Skip to content

fix: notification of missing networkobject on standalone networkbehaviour #1808

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Changed

### Fixed

- Fixed user never being notified in the editor that a NetworkBehaviour requires a NetworkObject to function properly. (#1808)
- Fixed PlayerObjects and dynamically spawned NetworkObjects not being added to the NetworkClient's OwnedObjects (#1801)
- Fixed issue where NetworkManager would continue starting even if the NetworkTransport selected failed. (#1780)
- Fixed issue when spawning new player if an already existing player exists it does not remove IsPlayer from the previous player (#1779)
Expand Down
74 changes: 74 additions & 0 deletions com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,5 +211,79 @@ public override void OnInspectorGUI()
serializedObject.ApplyModifiedProperties();
EditorGUI.EndChangeCheck();
}

/// <summary>
/// Invoked once when a NetworkBehaviour component is
/// displayed in the inspector view.
/// </summary>
private void OnEnable()
{
// When we first add a NetworkBehaviour this editor will be enabled
// so we go ahead and check for an already existing NetworkObject here
CheckForNetworkObject((target as NetworkBehaviour).gameObject);
}

internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist";

/// <summary>
/// Used to determine if a GameObject has one or more NetworkBehaviours but
/// does not already have a NetworkObject component. If not it will notify
/// the user that NetworkBehaviours require a NetworkObject.
/// </summary>
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
{
// If there are no NetworkBehaviours or no gameObject, then exit early
if (gameObject == null || gameObject.GetComponent<NetworkBehaviour>() == null && gameObject.GetComponentInChildren<NetworkBehaviour>() == null)
{
return;
}

// Otherwise, check to see if there is a NetworkObject and if not notify the user that NetworkBehaviours
// require that the relative GameObject has a NetworkObject component.
var networkObject = gameObject.GetComponent<NetworkObject>();
if (networkObject == null)
{
networkObject = gameObject.GetComponentInChildren<NetworkObject>();

if (networkObject == null)
{
// If we are removing a NetworkObject but there is still one or more NetworkBehaviour components
// and the user has already turned "Auto-Add NetworkObject" on when first notified about the requirement
// then just send a reminder to the user why the NetworkObject they just deleted seemingly "re-appeared"
// again.
if (networkObjectRemoved && EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists) && EditorPrefs.GetBool(AutoAddNetworkObjectIfNoneExists))
{
Debug.LogWarning($"{gameObject.name} still has {nameof(NetworkBehaviour)}s and Auto-Add NetworkObjects is enabled. A NetworkObject is being added back to {gameObject.name}.");
Debug.Log($"To reset Auto-Add NetworkObjects: Select the Netcode->General->Reset Auto-Add NetworkObject menu item.");
}

// Notify and provide the option to add it one time, always add a NetworkObject, or do nothing and let the user manually add it
if (EditorUtility.DisplayDialog($"{nameof(NetworkBehaviour)}s require a {nameof(NetworkObject)}",
$"{gameObject.name} does not have a {nameof(NetworkObject)} component. Would you like to add one now?", "Yes", "No (manually add it)",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like that this popup gives the option to automatically add it. I was actually coming into this expecting to bring up a concern about being annoyed by popups if I add the objects in the wrong order, and here you already thought of that and made a great solution. I didn't even have a solution in mind, I was just going to ask "do we care?" - great work!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though as a minor thing, the text of this popup disagrees with the comment... the comment suggests this dialogue will give the user the choice between "once", "always", or "never", but this text gives the choice between "yes", or "no".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Though I may be misreading that, given I don't know what DialogOptOutDecisionType.ForThisMachine does.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... this is a "Unity Thing" that basically gives the option to always/automatically select "Yes" for the scenario that invokes that dialog box (i.e. so a user can see it once, select the extended "Yes-Do not show this...", and then it will always automatically add a NetworkObject component if you first add a NetworkBehaviour component to a GameObject with no NetworkObject component.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, neat. Comment withdrawn then!

DialogOptOutDecisionType.ForThisMachine, AutoAddNetworkObjectIfNoneExists))
{
gameObject.AddComponent<NetworkObject>();
var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene);
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(activeScene);
}
}
}
}

/// <summary>
/// This allows users to reset the Auto-Add NetworkObject preference
/// so the next time they add a NetworkBehaviour to a GameObject without
/// a NetworkObject it will display the dialog box again and not
/// automatically add a NetworkObject.
/// </summary>
[MenuItem("Netcode/General/Reset Auto-Add NetworkObject", false, 1)]
private static void ResetMultiplayerToolsTipStatus()
{
if (EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists))
{
EditorPrefs.SetBool(AutoAddNetworkObjectIfNoneExists, false);
}
}
}
}
27 changes: 27 additions & 0 deletions com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,32 @@ public override void OnInspectorGUI()
GUI.enabled = guiEnabled;
}
}

// Saved for use in OnDestroy
private GameObject m_GameObject;

/// <summary>
/// Invoked once when a NetworkObject component is
/// displayed in the inspector view.
/// </summary>
private void OnEnable()
{
// We set the GameObject upon being enabled because when the
// NetworkObject component is removed (i.e. when OnDestroy is invoked)
// it is no longer valid/available.
m_GameObject = (target as NetworkObject).gameObject;
}

/// <summary>
/// Invoked once when a NetworkObject component is
/// no longer displayed in the inspector view.
/// </summary>
private void OnDestroy()
{
// Since this is also invoked when a NetworkObject component is removed
// from a GameObject, we go ahead and check for a NetworkObject when
// this custom editor is destroyed.
NetworkBehaviourEditor.CheckForNetworkObject(m_GameObject, true);
}
}
}