// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Avatar;
using UltimateXR.Core;
using UltimateXR.Core.Components.Singleton;
using UltimateXR.Core.Settings;
using UltimateXR.Extensions.Unity;
using UltimateXR.Manipulation;
using UnityEngine;
#pragma warning disable 414 // Unused values
namespace UltimateXR.Networking
{
///
/// Network Manager. This singleton will take care of all communication between the different users to keep them in
/// sync.
///
public partial class UxrNetworkManager : UxrSingleton
{
#region Inspector Properties/Serialized Fields
[SerializeField] private UxrNetworkImplementation _networkImplementation;
[SerializeField] private UxrNetworkVoiceImplementation _networkVoiceImplementation;
[SerializeField] private bool _useSameSdkVoice = true;
[SerializeField] private List _createdGlobalGameObjects = new List();
[SerializeField] private List _createdGlobalComponents = new List();
[SerializeField] private List _createdGlobalVoiceGameObjects = new List();
[SerializeField] private List _createdGlobalVoiceComponents = new List();
[SerializeField] private List _createdGlobalGameObjectPaths = new List();
[SerializeField] private List _createdGlobalComponentPaths = new List();
[SerializeField] private List _createdGlobalVoiceGameObjectPaths = new List();
[SerializeField] private List _createdGlobalVoiceComponentPaths = new List();
[SerializeField] private List _registeredAvatars = new List();
[SerializeField] private bool _grabbablePhysicsAddProjectScenes;
[SerializeField] private bool _grabbablePhysicsAddPathPrefabs;
[SerializeField] private string _grabbablePhysicsPathRoot;
[SerializeField] private bool _grabbablePhysicsOnlyLog;
[SerializeField] private GrabbablePhysicsSetup _grabbablePhysicsSetupInfo = new GrabbablePhysicsSetup();
#endregion
#region Public Types & Data
///
/// Event called right after the authority of the local user over a GameObject was requested.
///
public event Action LocalAuthorityRequested;
///
/// Event called right after a NetworkTransform component was enabled or disabled.
///
public event Action NetworkTransformEnabled;
///
/// Event called right after a NetworkRigidbody component was enabled or disabled.
///
public event Action NetworkRigidbodyEnabled;
///
/// Gets whether there is a network session active.
///
public static bool IsSessionActive => IsServer || IsClient;
///
/// Gets whether the current user is owner of the session. This can be either because there is no multiplayer session
/// active or because the local user is the server.
///
public static bool NoSessionOrSessionOwner => Instance == null || Instance._networkImplementation == null || Instance._networkImplementation.IsServer;
///
/// Gets whether there is a network session active and the local user is the host (client and server at the same time).
///
public static bool IsHost => IsServer && IsClient;
///
/// Gets whether there is a network session active and the local user is the server.
///
public static bool IsServer => HasInstance && Instance._networkImplementation != null && Instance._networkImplementation.IsServer;
///
/// Gets whether there is a network session active and the local user is the server.
///
public static bool IsClient => HasInstance && Instance._networkImplementation != null && Instance._networkImplementation.IsClient;
///
/// Gets the network implementation.
///
public UxrNetworkImplementation NetworkImplementation => _networkImplementation;
///
/// Gets the network voice implementation.
///
public UxrNetworkVoiceImplementation NetworkVoiceImplementation => _networkVoiceImplementation;
///
/// Gets the global GameObjects created to add support for the given network SDK.
///
public IEnumerable CreatedGlobalGameObjects => _createdGlobalGameObjects;
///
/// Gets the global Components created to add support for the given network SDK.
///
public IEnumerable CreatedGlobalComponents => _createdGlobalComponents;
///
/// Gets the global GameObjects created to add support for the given network voice SDK.
///
public IEnumerable CreatedGlobalVoiceGameObjects => _createdGlobalVoiceGameObjects;
///
/// Gets the global Components created to add support for the given network voice SDK.
///
public IEnumerable CreatedGlobalVoiceComponents => _createdGlobalVoiceComponents;
///
/// Gets the registered avatar prefabs.
///
public IEnumerable RegisteredAvatarPrefabs
{
get
{
foreach (var avatar in _registeredAvatars)
{
yield return avatar.AvatarPrefab;
}
}
}
#endregion
#region Public Methods
///
/// Requests authority of the local avatar over the given target object. The target object should have a valid network
/// component using any given SDK, such as NetworkObject.
///
/// GameObject with networking component
public void RequestAuthority(GameObject gameObject)
{
NetworkImplementation.RequestAuthority(gameObject);
OnAuthorityRequested(gameObject);
}
///
/// Enables or disables the NetworkTransform on the given object and all its children.
///
/// Target
/// Enabled state
public void SetNetworkTransformEnabled(GameObject target, bool enabled)
{
NetworkImplementation.EnableNetworkTransform(gameObject, enabled);
OnNetworkTransformEnabled(gameObject, enabled);
}
///
/// Enables or disables the NetworkRigidbody on the given object and all its children.
///
/// Target
/// Enabled state
public void SetNetworkRigidbodyEnabled(GameObject target, bool enabled)
{
NetworkImplementation.EnableNetworkRigidbody(target, enabled);
OnNetworkRigidbodyEnabled(target, enabled);
}
#endregion
#region Unity
///
/// Subscribes to events.
///
protected override void OnEnable()
{
base.OnEnable();
if (NetworkImplementation)
{
UxrGrabManager.Instance.ObjectGrabbed += GrabManager_ObjectGrabbed;
UxrGrabManager.Instance.ObjectReleased += GrabManager_ObjectReleased;
UxrGrabManager.Instance.ObjectPlaced += GrabManager_ObjectPlaced;
}
}
///
/// Unsubscribes from events.
///
protected override void OnDisable()
{
base.OnDisable();
if (NetworkImplementation)
{
UxrGrabManager.Instance.ObjectGrabbed -= GrabManager_ObjectGrabbed;
UxrGrabManager.Instance.ObjectReleased -= GrabManager_ObjectReleased;
UxrGrabManager.Instance.ObjectPlaced -= GrabManager_ObjectPlaced;
}
}
#endregion
#region Event Handling Methods
///
/// Called when an object was grabbed. Checks if the grabbed object's authority or a NetworkRigidbody enabled
/// state need to change. A free object that was grabbed will now belong to the grabbing avatar and have its
/// NetworkRigidbody disabled because it is not driven by physics anymore, but by the avatar's tracked hand.
///
/// Event sender
/// Event parameters
private void GrabManager_ObjectGrabbed(object sender, UxrManipulationEventArgs e)
{
if (e.IsGrabbedStateChanged && e.GrabbableObject.RigidBodySource != null && e.GrabbableObject.RigidBodyDynamicOnRelease && e.GrabbableObject.CanUseRigidBody)
{
// From not grabbed to grabbed. Switch authority to local avatar if it's the one that grabbed it.
if (e.Grabber != null && e.Grabber.Avatar == UxrAvatar.LocalAvatar && !NetworkImplementation.HasAuthority(e.GrabbableObject.gameObject))
{
RequestAuthority(e.GrabbableObject.gameObject);
}
// Disable network rigidbody because avatar will drive the grabbed object's transform.
SetNetworkRigidbodyEnabled(e.GrabbableObject.gameObject, false);
}
}
///
/// Called when an object was released. Checks if the avatar that had the authority needs to give it to another avatar
/// that keeps the grab or if the object was thrown and the NetworkRigidbody needs to be enabled.
///
/// Event sender
/// Event parameters
private void GrabManager_ObjectReleased(object sender, UxrManipulationEventArgs e)
{
if (e.GrabbableObject.RigidBodySource != null && e.GrabbableObject.RigidBodyDynamicOnRelease && e.GrabbableObject.CanUseRigidBody)
{
if (e.IsGrabbedStateChanged)
{
// From grabbed to released. Enable network rigidbody.
SetNetworkRigidbodyEnabled(e.GrabbableObject.gameObject, true);
}
else if (e.Grabber != null && !UxrGrabManager.Instance.IsBeingGrabbedBy(e.GrabbableObject, e.Grabber.Avatar))
{
// An avatar released its last grip, check if we need to reassign the network authority
NetworkImplementation.CheckReassignGrabAuthority(e.GrabbableObject.gameObject);
}
}
}
///
/// Called when an object was placed to disable the NetworkRigidbody. Checks if the place call comes from a user Place
/// instead of a manipulation event, because manipulation events come from a grab where the rigidbody was already
/// disabled at the time of the grab.
///
/// Event sender
/// Event parameters
private void GrabManager_ObjectPlaced(object sender, UxrManipulationEventArgs e)
{
if (e.GrabbableObject.RigidBodySource != null && e.GrabbableObject.RigidBodyDynamicOnRelease && e.GrabbableObject.CanUseRigidBody)
{
if (e.Grabber == null)
{
// Source is a "manual" place
SetNetworkRigidbodyEnabled(e.GrabbableObject.gameObject, false);
}
}
}
#endregion
#region Event Trigger Methods
///
/// Event trigger for .
///
/// The GameObject with the NetworkRigidbody
/// Whether the component was enabled or disabled
private void OnNetworkRigidbodyEnabled(GameObject gameObject, bool enabled)
{
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.NetworkingModule} NetworkRigidbody {(enabled ? "enabled" : "disabled")} on GameObject {gameObject.GetPathUnderScene()}.");
}
NetworkRigidbodyEnabled?.Invoke(gameObject, enabled);
}
///
/// Event trigger for .
///
/// The GameObject with the NetworkRigidbody
/// Whether the component was enabled or disabled
private void OnNetworkTransformEnabled(GameObject gameObject, bool enabled)
{
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.NetworkingModule} NetworkTransform {(enabled ? "enabled" : "disabled")} on GameObject {gameObject.GetPathUnderScene()}.");
}
NetworkTransformEnabled?.Invoke(gameObject, enabled);
}
///
/// Event trigger for .
///
/// The GameObject to get the authority for
private void OnAuthorityRequested(GameObject gameObject)
{
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.NetworkingModule} Authority requested of local avatar over GameObject {gameObject.GetPathUnderScene()}.");
}
LocalAuthorityRequested?.Invoke(gameObject);
}
#endregion
}
}
#pragma warning restore 414