Add ultimate xr
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrClientNetworkTransform.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
using Unity.Netcode.Components;
|
||||
#else
|
||||
using UnityEngine;
|
||||
#endif
|
||||
|
||||
namespace UltimateXR.Networking.Integrations.Net.UnityNetCode
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
public class UxrClientNetworkTransform : NetworkTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Behave like a NetworkTransform but authority is the owner, not the server.
|
||||
/// </summary>
|
||||
protected override bool OnIsServerAuthoritative()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class UxrClientNetworkTransform : MonoBehaviour
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cdb9d15b9979084aa96d70e62a54c0a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,351 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrUnityNetCodeAvatar.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Core.StateSave;
|
||||
using UltimateXR.Core.StateSync;
|
||||
using UltimateXR.Extensions.System;
|
||||
using UltimateXR.Extensions.System.Collections;
|
||||
using UltimateXR.Core.Instantiation;
|
||||
using Unity.Netcode;
|
||||
#endif
|
||||
|
||||
namespace UltimateXR.Networking.Integrations.Net.UnityNetCode
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
public class UxrUnityNetCodeAvatar : NetworkBehaviour, IUxrNetworkAvatar
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[Tooltip("List of objects that will be disabled when the avatar is in local mode, to avoid intersections with the camera for example")] [SerializeField] private List<GameObject> _localDisabledGameObjects;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrNetworkAvatar
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<GameObject> LocalDisabledGameObjects => _localDisabledGameObjects;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsLocal { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public UxrAvatar Avatar { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string AvatarName
|
||||
{
|
||||
get => _avatarName;
|
||||
set
|
||||
{
|
||||
_avatarName = value;
|
||||
|
||||
if (Avatar != null)
|
||||
{
|
||||
Avatar.name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action AvatarSpawned;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action AvatarDespawned;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void InitializeNetworkAvatar(UxrAvatar avatar, bool isLocal, string uniqueId, string avatarName)
|
||||
{
|
||||
IsLocal = isLocal;
|
||||
AvatarName = avatarName;
|
||||
avatar.AvatarMode = isLocal ? UxrAvatarMode.Local : UxrAvatarMode.UpdateExternally;
|
||||
|
||||
if (isLocal)
|
||||
{
|
||||
LocalDisabledGameObjects.ForEach(o => o.SetActive(false));
|
||||
}
|
||||
|
||||
avatar.CombineUniqueId(uniqueId.GetGuid(), true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Request authority of the local avatar over an object.
|
||||
/// </summary>
|
||||
/// <param name="networkObject">The object to get authority over</param>
|
||||
public void RequestAuthority(NetworkObject networkObject)
|
||||
{
|
||||
RequestAuthorityServerRpc(networkObject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when a component in UltimateXR had a state change.
|
||||
/// </summary>
|
||||
/// <param name="component">Component</param>
|
||||
/// <param name="eventArgs">Event parameters</param>
|
||||
private void UxrManager_ComponentStateChanged(IUxrStateSync component, UxrSyncEventArgs eventArgs)
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventArgs.Options.HasFlag(UxrStateSyncOptions.Network))
|
||||
{
|
||||
byte[] serializedEvent = eventArgs.SerializeEventBinary(component);
|
||||
|
||||
if (serializedEvent != null)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} Sending {serializedEvent.Length} bytes from {component.Component.name} ({component.UniqueId}) {eventArgs}");
|
||||
}
|
||||
|
||||
ComponentStateChangedServerRpc(serializedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
Avatar = GetComponent<UxrAvatar>();
|
||||
|
||||
InitializeNetworkAvatar(Avatar, IsOwner, OwnerClientId.ToString(), $"Player {OwnerClientId} ({(IsOwner ? "Local" : "External")})");
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
UxrManager.ComponentStateChanged += UxrManager_ComponentStateChanged;
|
||||
}
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} {nameof(UxrUnityNetCodeAvatar)}.{nameof(OnNetworkSpawn)}: Is Local? {IsLocal}, Name: {AvatarName}. OwnerClientId: {OwnerClientId}, UniqueId: {Avatar.UniqueId}.");
|
||||
}
|
||||
|
||||
AvatarSpawned?.Invoke();
|
||||
|
||||
if (UxrInstanceManager.HasInstance)
|
||||
{
|
||||
UxrInstanceManager.Instance.NotifyNetworkSpawn(Avatar.gameObject);
|
||||
}
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
if (!IsServer)
|
||||
{
|
||||
byte[] localAvatarState = UxrManager.Instance.SaveStateChanges(new List<GameObject> { Avatar.gameObject }, null, UxrStateSaveLevel.ChangesSinceBeginning, UxrGlobalSettings.Instance.NetFormatInitialState);
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} Requesting global state and sending local avatar state in {localAvatarState.Length} bytes.");
|
||||
}
|
||||
|
||||
// Send the initial avatar state to the server and request the current scene state.
|
||||
// Call after AvatarSpawned() in case any event handler changes the avatar state.
|
||||
NewAvatarJoinedServerRpc(localAvatarState);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Server creates the session and doesn't need to send the initial state.
|
||||
s_initialStateLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (Avatar && IsOwner)
|
||||
{
|
||||
UxrManager.ComponentStateChanged -= UxrManager_ComponentStateChanged;
|
||||
}
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} {nameof(UxrUnityNetCodeAvatar)}.{nameof(OnNetworkDespawn)}: Is Local? {IsLocal}, Name: {AvatarName}");
|
||||
}
|
||||
|
||||
AvatarDespawned?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Server RPC to request the current global state upon joining.
|
||||
/// </summary>
|
||||
/// <param name="avatarState">The initial state of the avatar that joined</param>
|
||||
/// <param name="serverRpcParams">Filled by NetCode with info</param>
|
||||
[ServerRpc]
|
||||
private void NewAvatarJoinedServerRpc(byte[] avatarState, ServerRpcParams serverRpcParams = default)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} Received request for global state from client {serverRpcParams.Receive.SenderClientId}.");
|
||||
}
|
||||
|
||||
// First load the avatar state
|
||||
UxrManager.Instance.LoadStateChanges(avatarState);
|
||||
|
||||
// Now export the scenario state, except for the new avatar, and send it back
|
||||
byte[] serializedState = UxrManager.Instance.SaveStateChanges(null, new List<GameObject> { gameObject }, UxrStateSaveLevel.ChangesSinceBeginning, UxrGlobalSettings.Instance.NetFormatInitialState);
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} Sending global state in {serializedState.Length} bytes to client {serverRpcParams.Receive.SenderClientId}. Broadcasting {avatarState.Length} bytes to sync new avatar.");
|
||||
}
|
||||
|
||||
// Send global state to new user.
|
||||
|
||||
ClientRpcParams clientRpcParams = new ClientRpcParams
|
||||
{
|
||||
Send = new ClientRpcSendParams
|
||||
{
|
||||
TargetClientIds = new[] { serverRpcParams.Receive.SenderClientId }
|
||||
}
|
||||
};
|
||||
LoadGlobalStateClientRpc(serializedState, clientRpcParams);
|
||||
|
||||
// Broadcast initial state of new avatar.
|
||||
LoadAvatarStateClientRpc(avatarState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server RPC call to propagate state change events to all other clients.
|
||||
/// </summary>
|
||||
/// <param name="serializedEventData">The serialized state change data</param>
|
||||
[ServerRpc]
|
||||
private void ComponentStateChangedServerRpc(byte[] serializedEventData)
|
||||
{
|
||||
ComponentStateChangedClientRpc(serializedEventData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server RPC requesting authority over an object.
|
||||
/// </summary>
|
||||
/// <param name="networkObjectReference">Object to get authority over</param>
|
||||
/// <param name="serverRpcParams">Filled by NetCode with info</param>
|
||||
[ServerRpc]
|
||||
private void RequestAuthorityServerRpc(NetworkObjectReference networkObjectReference, ServerRpcParams serverRpcParams = default)
|
||||
{
|
||||
if (networkObjectReference.TryGet(out NetworkObject networkObject))
|
||||
{
|
||||
NetworkManager networkManager = UxrNetworkManager.Instance.GetComponent<NetworkManager>();
|
||||
|
||||
if (networkManager != null)
|
||||
{
|
||||
networkObject.ChangeOwnership(serverRpcParams.Receive.SenderClientId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Warnings)
|
||||
{
|
||||
Debug.LogWarning($"{UxrConstants.NetworkingModule} {nameof(UxrUnityNetCodeAvatar)}.{nameof(RequestAuthorityServerRpc)}() Cannot find target network object.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Targeted client RPC to client that joined to sync to the current state.
|
||||
/// </summary>
|
||||
/// <param name="serializedStateData">The serialized state data</param>
|
||||
/// <param name="clientRpcParams">Target of the RPC</param>
|
||||
[ClientRpc]
|
||||
private void LoadGlobalStateClientRpc(byte[] serializedStateData, ClientRpcParams clientRpcParams = default)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} Receiving {serializedStateData.Length} bytes of global state data.");
|
||||
}
|
||||
|
||||
UxrManager.Instance.LoadStateChanges(serializedStateData);
|
||||
s_initialStateLoaded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client RPC to sync the state of a new avatar that joined.
|
||||
/// </summary>
|
||||
/// <param name="serializedStateData">The serialized state data</param>
|
||||
[ClientRpc]
|
||||
private void LoadAvatarStateClientRpc(byte[] serializedStateData)
|
||||
{
|
||||
if (IsOwner)
|
||||
{
|
||||
// Don't execute on the source of the event, we don't want to load our own avatar data.
|
||||
return;
|
||||
}
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} Receiving {serializedStateData.Length} bytes of avatar state data.");
|
||||
}
|
||||
|
||||
UxrManager.Instance.LoadStateChanges(serializedStateData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client RPC call to execute a state change event. It will execute on all clients except the one that generated it,
|
||||
/// which can be identified because it's the one with ownership.
|
||||
/// </summary>
|
||||
/// <param name="serializedEventData">The serialized state change data</param>
|
||||
[ClientRpc]
|
||||
private void ComponentStateChangedClientRpc(byte[] serializedEventData)
|
||||
{
|
||||
if (IsOwner)
|
||||
{
|
||||
// Don't execute on the source of the event.
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_initialStateLoaded == false)
|
||||
{
|
||||
// Ignore sync events until the initial state is sent, to make sure the syncs are only processed after the initial state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.NetworkingModule} Receiving {serializedEventData.Length} bytes of data. Base64: {Convert.ToBase64String(serializedEventData)}");
|
||||
}
|
||||
|
||||
UxrStateSyncResult result = UxrManager.Instance.ExecuteStateSyncEvent(serializedEventData);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private static bool s_initialStateLoaded;
|
||||
|
||||
private string _avatarName;
|
||||
|
||||
#endregion
|
||||
}
|
||||
#else
|
||||
public class UxrPhotonFusionAvatar : MonoBehaviour
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 331b06803190dac428d6de4f688107e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,449 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrUnityNetCodeNetwork.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UnityEngine;
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE && UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
using System.Linq;
|
||||
using UltimateXR.Extensions.System.Collections;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UltimateXR.Manipulation;
|
||||
using Unity.Netcode;
|
||||
using Unity.Netcode.Transports.UTP;
|
||||
using NetworkObject = Unity.Netcode.NetworkObject;
|
||||
using NetworkRigidbody = Unity.Netcode.Components.NetworkRigidbody;
|
||||
using NetworkTransform = Unity.Netcode.Components.NetworkTransform;
|
||||
#endif
|
||||
|
||||
#pragma warning disable 414 // Disable warnings due to unused values
|
||||
|
||||
namespace UltimateXR.Networking.Integrations.Net.UnityNetCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of networking support using Unity NetCode.
|
||||
/// </summary>
|
||||
public class UxrUnityNetCodeNetwork : UxrNetworkImplementation
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[Tooltip("Show a UI during play mode with connection options to quickly prototype networking functionality")] [SerializeField] private bool _usePrototypingUI = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides UxrNetworkImplementation
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SdkName => UxrConstants.SdkUnityNetCode;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsServer
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
return NetworkManager.Singleton != null && NetworkManager.Singleton.IsServer;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsClient
|
||||
{
|
||||
get
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
return NetworkManager.Singleton != null && NetworkManager.Singleton.IsClient;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override UxrNetworkCapabilities Capabilities => UxrNetworkCapabilities.NetworkTransform; // The following is momentarily disabled until we get a workaround for not allowing re-parenting during startup | UxrNetworkCapabilities.NetworkRigidbody;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string NetworkRigidbodyWarning => $"{UxrConstants.SdkUnityNetCode} does not allow re-parenting NetworkIdentity GameObjects during startup. Until there is a workaround, NetworkRigidbody support here will be disabled. Don't worry! UltimateXR will still synchronize grabbable physics-driven rigidbodies using RPC calls to try to keep the same position/velocity on all users.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetupGlobal(UxrNetworkManager networkManager, out List<GameObject> newGameObjects, out List<Component> newComponents)
|
||||
{
|
||||
newGameObjects = new List<GameObject>();
|
||||
newComponents = new List<Component>();
|
||||
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE && UNITY_EDITOR
|
||||
GameObject networkManagerGo = new GameObject("NetCodeNetworkManager");
|
||||
Undo.RegisterCreatedObjectUndo(networkManagerGo, "Create NetCode Network Manager");
|
||||
networkManagerGo.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
networkManagerGo.transform.SetSiblingIndex(networkManager.transform.GetSiblingIndex() + 1);
|
||||
|
||||
NetworkManager netCodeNetworkManager = networkManagerGo.GetOrAddComponent<NetworkManager>();
|
||||
UnityTransport unityTransport = networkManagerGo.GetOrAddComponent<UnityTransport>();
|
||||
netCodeNetworkManager.NetworkConfig.NetworkTransport = unityTransport;
|
||||
Undo.RegisterFullObjectHierarchyUndo(networkManager.gameObject, "Setup NetCode NetworkManager");
|
||||
|
||||
newComponents.Add(unityTransport);
|
||||
newComponents.Add(netCodeNetworkManager);
|
||||
newGameObjects.Add(networkManagerGo);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetupAvatar(UxrAvatar avatar, out List<GameObject> newGameObjects, out List<Component> newComponents)
|
||||
{
|
||||
newGameObjects = new List<GameObject>();
|
||||
newComponents = new List<Component>();
|
||||
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE && UNITY_EDITOR
|
||||
if (avatar == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UxrUnityNetCodeAvatar netCodeAvatar = avatar.GetOrAddComponent<UxrUnityNetCodeAvatar>();
|
||||
newComponents.Add(netCodeAvatar);
|
||||
|
||||
IEnumerable<Behaviour> avatarComponents = SetupClientNetworkTransform(avatar.gameObject, true, UxrNetworkTransformFlags.All);
|
||||
IEnumerable<Behaviour> cameraComponents = SetupClientNetworkTransform(avatar.CameraComponent.gameObject, true, UxrNetworkTransformFlags.ChildPositionAndRotation);
|
||||
IEnumerable<Behaviour> leftHandComponents = SetupClientNetworkTransform(avatar.GetHand(UxrHandSide.Left).Wrist.gameObject, true, UxrNetworkTransformFlags.ChildPositionAndRotation);
|
||||
IEnumerable<Behaviour> rightHandComponents = SetupClientNetworkTransform(avatar.GetHand(UxrHandSide.Right).Wrist.gameObject, true, UxrNetworkTransformFlags.ChildPositionAndRotation);
|
||||
|
||||
newComponents.AddRange(avatarComponents.ToList().Concat(cameraComponents).Concat(leftHandComponents).Concat(rightHandComponents));
|
||||
Undo.RegisterFullObjectHierarchyUndo(avatar.gameObject, "Setup NetCode Avatar");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetupPostProcess(IEnumerable<UxrAvatar> avatarPrefabs)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE && UNITY_EDITOR
|
||||
NetworkManager netCodeNetworkManager = FindObjectOfType<NetworkManager>();
|
||||
|
||||
if (netCodeNetworkManager != null && netCodeNetworkManager.NetworkConfig.PlayerPrefab == null && avatarPrefabs.Any())
|
||||
{
|
||||
netCodeNetworkManager.NetworkConfig.PlayerPrefab = avatarPrefabs.First().gameObject;
|
||||
Undo.RegisterCompleteObjectUndo(netCodeNetworkManager, "Setup NetCode Avatar");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Behaviour> AddNetworkTransform(GameObject gameObject, bool worldSpace, UxrNetworkTransformFlags networkTransformFlags)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE && UNITY_EDITOR
|
||||
if (networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ChildTransform) == false)
|
||||
{
|
||||
NetworkObject networkObject = gameObject.GetOrAddComponent<NetworkObject>();
|
||||
yield return networkObject;
|
||||
}
|
||||
|
||||
NetworkTransform networkTransform = gameObject.GetOrAddComponent<NetworkTransform>();
|
||||
networkTransform.InLocalSpace = !worldSpace;
|
||||
networkTransform.SyncPositionX = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.PositionX);
|
||||
networkTransform.SyncPositionY = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.PositionY);
|
||||
networkTransform.SyncPositionZ = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.PositionZ);
|
||||
networkTransform.SyncRotAngleX = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.RotationX);
|
||||
networkTransform.SyncRotAngleY = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.RotationY);
|
||||
networkTransform.SyncRotAngleZ = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.RotationZ);
|
||||
networkTransform.SyncScaleX = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ScaleX);
|
||||
networkTransform.SyncScaleY = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ScaleY);
|
||||
networkTransform.SyncScaleZ = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ScaleZ);
|
||||
yield return networkTransform;
|
||||
|
||||
#else
|
||||
yield break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Behaviour> AddNetworkRigidbody(GameObject gameObject, bool worldSpace, UxrNetworkRigidbodyFlags networkRigidbodyFlags)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE && UNITY_EDITOR
|
||||
// Building list forces evaluation of AddNetworkTransform IEnumerable and creates the components
|
||||
List<Behaviour> networkTransformComponents = new List<Behaviour>(AddNetworkTransform(gameObject, worldSpace, UxrNetworkTransformFlags.All));
|
||||
|
||||
NetworkRigidbody networkRigidbody = gameObject.GetOrAddComponent<NetworkRigidbody>();
|
||||
yield return networkRigidbody;
|
||||
|
||||
// Return transform components after, so that when removing the components the NetworkRigidbody is removed before the identity. Otherwise Mirror will complain.
|
||||
|
||||
foreach (Behaviour newBehaviour in networkTransformComponents)
|
||||
{
|
||||
yield return newBehaviour;
|
||||
}
|
||||
|
||||
#else
|
||||
yield break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void EnableNetworkTransform(GameObject gameObject, bool enable)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
NetworkTransform[] networkTransforms = gameObject.GetComponentsInChildren<NetworkTransform>();
|
||||
networkTransforms.ForEach(nt => nt.SetEnabled(enable));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void EnableNetworkRigidbody(GameObject gameObject, bool enable)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
EnableNetworkTransform(gameObject, enabled);
|
||||
|
||||
NetworkRigidbody[] networkRigidbodies = gameObject.GetComponentsInChildren<NetworkRigidbody>();
|
||||
networkRigidbodies.ForEach(nrb => nrb.SetEnabled(enable));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasAuthority(GameObject gameObject)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
NetworkObject networkObject = gameObject.GetComponent<NetworkObject>();
|
||||
|
||||
if (networkObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return networkObject.IsOwner;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void RequestAuthority(GameObject gameObject)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
if (gameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UxrUnityNetCodeAvatar netCodeAvatar = UxrAvatar.LocalAvatar.GetComponentInChildren<UxrUnityNetCodeAvatar>();
|
||||
NetworkObject networkObject = gameObject.GetComponent<NetworkObject>();
|
||||
|
||||
if (netCodeAvatar != null && networkObject != null)
|
||||
{
|
||||
netCodeAvatar.RequestAuthority(networkObject);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void CheckReassignGrabAuthority(GameObject gameObject)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
UxrGrabbableObject grabbableObject = gameObject.GetComponent<UxrGrabbableObject>();
|
||||
NetworkObject networkObject = gameObject.GetComponent<NetworkObject>();
|
||||
|
||||
if (networkObject != null && grabbableObject != null)
|
||||
{
|
||||
UxrAvatar avatarAuthority = UxrAvatar.EnabledComponents.FirstOrDefault(a => a.GetComponent<NetworkObject>() != null && a.GetComponent<NetworkObject>().OwnerClientId == networkObject.OwnerClientId);
|
||||
|
||||
if (avatarAuthority == null || !UxrGrabManager.Instance.IsBeingGrabbedBy(grabbableObject, avatarAuthority))
|
||||
{
|
||||
// No avatar has authority or the avatar that grabbed it doesn't have it anymore. Change authority to first one.
|
||||
|
||||
UxrAvatar firstAvatar = UxrGrabManager.Instance.GetGrabbingHands(grabbableObject).First().Avatar;
|
||||
|
||||
if (firstAvatar == UxrAvatar.LocalAvatar)
|
||||
{
|
||||
UxrNetworkManager.Instance.RequestAuthority(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasNetworkTransformSyncComponents(GameObject gameObject)
|
||||
{
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
return gameObject.GetComponent<NetworkTransform>() != null || gameObject.GetComponent<NetworkRigidbody>() != null;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#if ULTIMATEXR_USE_UNITY_NETCODE
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (NetworkManager.Singleton != null && NetworkManager.Singleton.NetworkConfig.NetworkTransport is UnityTransport unityTransport)
|
||||
{
|
||||
_networkAddress = unityTransport.ConnectionData.Address;
|
||||
_networkPort = unityTransport.ConnectionData.Port.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the connection UI if its enabled.
|
||||
/// </summary>
|
||||
private void OnGUI()
|
||||
{
|
||||
if (!_usePrototypingUI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PosY = 0;
|
||||
GUI.Box(new Rect(0, 0, Screen.width, Screen.height), string.Empty);
|
||||
|
||||
GUI.Box(new Rect(0, PosY, ButtonWidth, ButtonHeight), "UltimateXR Unity NetCode");
|
||||
PosY += ButtonHeight;
|
||||
|
||||
if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer)
|
||||
{
|
||||
if (NetworkManager.Singleton.ShutdownInProgress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkManager.Singleton.IsHost)
|
||||
{
|
||||
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Stop Host"))
|
||||
{
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
}
|
||||
}
|
||||
else if (NetworkManager.Singleton.IsServer)
|
||||
{
|
||||
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Stop Server"))
|
||||
{
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
}
|
||||
}
|
||||
else if (NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Disconnect Client"))
|
||||
{
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GUI.Box(new Rect(0, PosY, ButtonWidth, LabelHeight), "Network Address:");
|
||||
GUI.Box(new Rect(ButtonWidth + 10, PosY, ButtonWidth / 2, LabelHeight), "Port:");
|
||||
|
||||
PosY += LabelHeight;
|
||||
|
||||
_networkAddress = GUI.TextField(new Rect(0, PosY, ButtonWidth, LabelHeight), _networkAddress);
|
||||
_networkPort = GUI.TextField(new Rect(ButtonWidth + 10, PosY, ButtonWidth / 2, LabelHeight), _networkPort);
|
||||
PosY += ButtonHeight;
|
||||
|
||||
if (NetworkManager.Singleton != null && NetworkManager.Singleton.NetworkConfig.NetworkTransport is UnityTransport unityTransport)
|
||||
{
|
||||
ushort.TryParse(_networkPort, out ushort port);
|
||||
unityTransport.SetConnectionData(string.IsNullOrEmpty(_networkAddress) ? DefaultNetworkAddress : _networkAddress, port);
|
||||
}
|
||||
|
||||
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Start Host"))
|
||||
{
|
||||
NetworkManager.Singleton.StartHost();
|
||||
}
|
||||
|
||||
PosY += ButtonHeight;
|
||||
|
||||
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Start Server"))
|
||||
{
|
||||
NetworkManager.Singleton.StartServer();
|
||||
}
|
||||
|
||||
PosY += ButtonHeight;
|
||||
|
||||
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Start Client"))
|
||||
{
|
||||
NetworkManager.Singleton.StartClient();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to set up UxrClientNetworkTransform components for a given object.
|
||||
/// </summary>
|
||||
/// <param name="go">The GameObject to set up</param>
|
||||
/// <param name="worldSpace">Whether to use world-space coordinates or local-space coordinates</param>
|
||||
/// <param name="flags">Option flags</param>
|
||||
/// <returns>List of components that were added: an UxrClientNetworkTransform and NetworkObject</returns>
|
||||
private IEnumerable<Behaviour> SetupClientNetworkTransform(GameObject go, bool worldSpace, UxrNetworkTransformFlags networkTransformFlags)
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
if (networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ChildTransform) == false)
|
||||
{
|
||||
NetworkObject networkObject = go.GetOrAddComponent<NetworkObject>();
|
||||
yield return networkObject;
|
||||
}
|
||||
|
||||
UxrClientNetworkTransform clientNetworkTransform = go.GetOrAddComponent<UxrClientNetworkTransform>();
|
||||
clientNetworkTransform.InLocalSpace = !worldSpace;
|
||||
clientNetworkTransform.SyncPositionX = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.PositionX);
|
||||
clientNetworkTransform.SyncPositionY = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.PositionY);
|
||||
clientNetworkTransform.SyncPositionZ = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.PositionZ);
|
||||
clientNetworkTransform.SyncRotAngleX = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.RotationX);
|
||||
clientNetworkTransform.SyncRotAngleY = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.RotationY);
|
||||
clientNetworkTransform.SyncRotAngleZ = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.RotationZ);
|
||||
clientNetworkTransform.SyncScaleX = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ScaleX);
|
||||
clientNetworkTransform.SyncScaleY = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ScaleY);
|
||||
clientNetworkTransform.SyncScaleZ = networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ScaleZ);
|
||||
yield return clientNetworkTransform;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Data
|
||||
|
||||
private const string DefaultNetworkAddress = "127.0.0.1";
|
||||
private const ushort DefaultNetworkPort = 7777;
|
||||
private const int LabelHeight = 25;
|
||||
private const int ButtonWidth = 200;
|
||||
private const int ButtonHeight = 40;
|
||||
|
||||
private int PosY { get; set; }
|
||||
|
||||
private string _networkAddress = DefaultNetworkAddress;
|
||||
private string _networkPort = DefaultNetworkPort.ToString();
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 414
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2538d188e76399b42afad64b10e0d936
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user