Add ultimate xr

This commit is contained in:
2024-08-06 21:58:35 +02:00
parent 864033bf10
commit 7165bacd9d
3952 changed files with 2162037 additions and 35 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ec5835b639bf45b4b95e91375e169613
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
{
"reference": "GUID:7c88a4a7926ee5145ad2dfa06f454c67"
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 05341feb6ff954c46826779cf33502be
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fe2525dafa0a5246ad16c39abb7544a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,348 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrFishNetAvatar.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UnityEngine;
#if ULTIMATEXR_USE_FISHNET_SDK
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 FishNet.Connection;
using FishNet.Object;
using UltimateXR.Core.Instantiation;
#endif
namespace UltimateXR.Networking.Integrations.Net.FishNet
{
#if ULTIMATEXR_USE_FISHNET_SDK
public class UxrFishNetAvatar : 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 OnStartClient()
{
base.OnStartClient();
Avatar = GetComponent<UxrAvatar>();
InitializeNetworkAvatar(Avatar, IsOwner, OwnerId.ToString(), $"Player {OwnerId} ({(IsOwner ? "Local" : "External")})");
if (IsOwner)
{
UxrManager.ComponentStateChanged += UxrManager_ComponentStateChanged;
}
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.NetworkingModule} {nameof(UxrFishNetAvatar)}.{nameof(OnStartClient)}: Is Local? {IsLocal}, Name: {AvatarName}. OwnerId: {OwnerId}, 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 OnStopClient()
{
base.OnStopClient();
if (Avatar && IsOwner)
{
UxrManager.ComponentStateChanged -= UxrManager_ComponentStateChanged;
}
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.NetworkingModule} {nameof(UxrFishNetAvatar)}.{nameof(OnStopClient)}: Is Local? {IsLocal}, Name: {AvatarName}");
}
if (UxrInstanceManager.HasInstance)
{
UxrInstanceManager.Instance.NotifyNetworkDespawn(Avatar.gameObject);
}
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="conn">Filled by FishNet with info</param>
[ServerRpc]
private void NewAvatarJoinedServerRpc(byte[] avatarState, NetworkConnection conn = null)
{
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
{
Debug.Log($"{UxrConstants.NetworkingModule} Received request for global state from client {conn.ClientId}.");
}
// 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 {conn.ClientId}. Broadcasting {avatarState.Length} bytes to sync new avatar.");
}
// Send global state to new user.
LoadGlobalStateTargetRpc(conn, serializedState);
// 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="networkObject">Object to get authority over</param>
/// <param name="conn">Filled by FishNet with info</param>
[ServerRpc]
private void RequestAuthorityServerRpc(NetworkObject networkObject, NetworkConnection conn = null)
{
if (UxrGlobalSettings.Instance.LogLevelNetworking >= UxrLogLevel.Verbose)
{
Debug.Log($"{UxrConstants.NetworkingModule} Granting authority to owner {OwnerId} over network object {networkObject.name}.");
}
NetworkObject[] networkObjects = networkObject.GetComponentsInChildren<NetworkObject>();
foreach (NetworkObject no in networkObjects)
{
no.GiveOwnership(conn);
}
}
/// <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>
[TargetRpc]
private void LoadGlobalStateTargetRpc(NetworkConnection conn, byte[] serializedStateData)
{
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>
[ObserversRpc]
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>
[ObserversRpc]
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 UxrFishNetAvatar : MonoBehaviour
{
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 230855043fe1c234c97a316567668d55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,417 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrFishNetNetwork.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_FISHNET_SDK && UNITY_EDITOR
using UnityEditor;
#endif
#if ULTIMATEXR_USE_FISHNET_SDK
using System.Linq;
using FishNet.Component.Spawning;
using FishNet.Component.Transforming;
using FishNet.Managing;
using FishNet.Managing.Object;
using FishNet.Managing.Observing;
using FishNet.Object;
using UltimateXR.Extensions.System.Collections;
using UltimateXR.Extensions.Unity;
using UltimateXR.Manipulation;
#endif
#pragma warning disable 414 // Disable warnings due to unused values
namespace UltimateXR.Networking.Integrations.Net.FishNet
{
/// <summary>
/// Implementation of networking support using FishNet.
/// </summary>
public class UxrFishNetNetwork : 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.SdkFishNet;
/// <inheritdoc />
public override bool IsServer
{
get
{
#if ULTIMATEXR_USE_FISHNET_SDK
return _networkManager != null && _networkManager.IsServer;
#else
return false;
#endif
}
}
/// <inheritdoc />
public override bool IsClient
{
get
{
#if ULTIMATEXR_USE_FISHNET_SDK
return _networkManager != null && _networkManager.IsClient;
#else
return false;
#endif
}
}
/// <inheritdoc />
public override UxrNetworkCapabilities Capabilities => UxrNetworkCapabilities.NetworkTransform | UxrNetworkCapabilities.NetworkRigidbody;
/// <inheritdoc />
//public override string NetworkRigidbodyWarning => $"{UxrConstants.SdkFishNet} does not use Rigidbodies";
/// <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_FISHNET_SDK && UNITY_EDITOR
GameObject networkManagerGo = new GameObject("FishNetNetworkManager");
Undo.RegisterCreatedObjectUndo(networkManagerGo, "Create FishNet Network Manager");
networkManagerGo.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
networkManagerGo.transform.SetSiblingIndex(networkManager.transform.GetSiblingIndex() + 1);
NetworkManager fishNetNetworkManager = networkManagerGo.GetOrAddComponent<NetworkManager>();
ObserverManager observerManager = networkManagerGo.GetOrAddComponent<ObserverManager>();
PlayerSpawner playerSpawner = networkManagerGo.GetOrAddComponent<PlayerSpawner>();
// Try to assign DefaultPrefabObjects asset to NetworkManager. It will still throw an unassigned error,
// adding the component triggers an internal check before we can assign anything.
string[] guids = AssetDatabase.FindAssets("t:DefaultPrefabObjects", new[] { "Assets/" });
if (guids.Any() && fishNetNetworkManager.SpawnablePrefabs == null)
{
fishNetNetworkManager.SpawnablePrefabs = AssetDatabase.LoadAssetAtPath<DefaultPrefabObjects>(AssetDatabase.GUIDToAssetPath(guids.First()));
}
Undo.RegisterFullObjectHierarchyUndo(networkManager.gameObject, "Setup FishNet NetworkManager");
newGameObjects.Add(networkManagerGo);
newComponents.Add(fishNetNetworkManager);
newComponents.Add(observerManager);
newComponents.Add(playerSpawner);
#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_FISHNET_SDK && UNITY_EDITOR
if (avatar == null)
{
return;
}
NetworkObject avatarNetworkIdentity = avatar.gameObject.GetOrAddComponent<NetworkObject>();
newComponents.Add(avatarNetworkIdentity);
UxrFishNetAvatar fishNetAvatar = avatar.GetOrAddComponent<UxrFishNetAvatar>();
newComponents.Add(fishNetAvatar);
IEnumerable<Behaviour> avatarComponents = SetupNetworkTransform(avatar.gameObject, true, UxrNetworkTransformFlags.ChildAll);
IEnumerable<Behaviour> cameraComponents = SetupNetworkTransform(avatar.CameraComponent.gameObject, true, UxrNetworkTransformFlags.ChildPositionAndRotation);
IEnumerable<Behaviour> leftHandComponents = SetupNetworkTransform(avatar.GetHand(UxrHandSide.Left).Wrist.gameObject, true, UxrNetworkTransformFlags.ChildTransform);
IEnumerable<Behaviour> rightHandComponents = SetupNetworkTransform(avatar.GetHand(UxrHandSide.Right).Wrist.gameObject, true, UxrNetworkTransformFlags.ChildTransform);
newComponents.AddRange(avatarComponents.ToList().Concat(cameraComponents).Concat(leftHandComponents).Concat(rightHandComponents));
Undo.RegisterFullObjectHierarchyUndo(avatar.gameObject, "Setup FishNet Avatar");
#endif
}
/// <inheritdoc />
public override void SetupPostProcess(IEnumerable<UxrAvatar> avatarPrefabs)
{
#if ULTIMATEXR_USE_FISHNET_SDK && UNITY_EDITOR
// Find the player spawner and assign the first avatar in the list as the spawnable avatar.
PlayerSpawner fishNetPlayerSpawner = FindObjectOfType<PlayerSpawner>();
if (fishNetPlayerSpawner != null)
{
SerializedObject so = new SerializedObject(fishNetPlayerSpawner);
if (so != null)
{
so.Update();
SerializedProperty sp = so.FindProperty("_playerPrefab");
if (sp != null && avatarPrefabs.Any() && sp.objectReferenceValue == null)
{
// If there is no avatar assigned and there is an avatar prefab available, assign the first.
sp.objectReferenceValue = avatarPrefabs.First().GetComponent<NetworkObject>();
so.ApplyModifiedProperties();
}
}
}
#endif
}
/// <inheritdoc />
public override IEnumerable<Behaviour> AddNetworkTransform(GameObject gameObject, bool worldSpace, UxrNetworkTransformFlags networkTransformFlags)
{
#if ULTIMATEXR_USE_FISHNET_SDK && UNITY_EDITOR
if (networkTransformFlags.HasFlag(UxrNetworkTransformFlags.ChildTransform) == false)
{
NetworkObject networkObject = gameObject.GetOrAddComponent<NetworkObject>();
yield return networkObject;
}
UxrFishNetworkTransform networkTransform = gameObject.GetOrAddComponent<UxrFishNetworkTransform>();
networkTransform.useWorldSpace = worldSpace;
yield return networkTransform;
#else
yield break;
#endif
}
/// <inheritdoc />
public override IEnumerable<Behaviour> AddNetworkRigidbody(GameObject gameObject, bool worldSpace, UxrNetworkRigidbodyFlags networkRigidbodyFlags)
{
// FishNet does not use NetworkRigidbody.
// We can just use the NetworkTransform if the Rigidbody is kinematic.
// Source: https://www.reddit.com/r/Unity3D/comments/vr4du5/comment/iettaaw/
return AddNetworkTransform(gameObject, worldSpace, UxrNetworkTransformFlags.All);
}
/// <inheritdoc />
public override void EnableNetworkTransform(GameObject gameObject, bool enable)
{
#if ULTIMATEXR_USE_FISHNET_SDK
UxrFishNetworkTransform[] networkTransforms = gameObject.GetComponentsInChildren<UxrFishNetworkTransform>();
networkTransforms.ForEach(nt => nt.SetEnabled(enable));
#endif
}
/// <inheritdoc />
public override void EnableNetworkRigidbody(GameObject gameObject, bool enable)
{
// FishNet does not use NetworkRigidbody.
// We can just use the NetworkTransform if the Rigidbody is kinematic.
// Source: https://www.reddit.com/r/Unity3D/comments/vr4du5/comment/iettaaw/
EnableNetworkTransform(gameObject, enable);
}
/// <inheritdoc />
public override bool HasAuthority(GameObject gameObject)
{
#if ULTIMATEXR_USE_FISHNET_SDK
NetworkObject networkObject = gameObject.GetComponent<NetworkObject>();
return networkObject != null && networkObject.IsOwner;
#else
return false;
#endif
}
/// <inheritdoc />
public override void RequestAuthority(GameObject gameObject)
{
#if ULTIMATEXR_USE_FISHNET_SDK
if (gameObject == null)
{
return;
}
UxrFishNetAvatar fishNetAvatar = UxrAvatar.LocalAvatar.GetComponentInChildren<UxrFishNetAvatar>();
NetworkObject networkObject = gameObject.GetComponent<NetworkObject>();
if (networkObject != null && fishNetAvatar != null)
{
fishNetAvatar.RequestAuthority(networkObject);
}
#endif
}
/// <inheritdoc />
public override void CheckReassignGrabAuthority(GameObject gameObject)
{
#if ULTIMATEXR_USE_FISHNET_SDK
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>().OwnerId == networkObject.OwnerId);
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_FISHNET_SDK
return gameObject.GetComponent<UxrFishNetworkTransform>() != null;
#else
return false;
#endif
}
#endregion
#if ULTIMATEXR_USE_FISHNET_SDK
#region Unity
/// <summary>
/// Unity OnGUI call that will draw the prototyping UI if it's enabled.
/// </summary>
private void OnGUI()
{
if (!_usePrototypingUI || !NetworkManager.Instances.First())
{
return;
}
PosY = 0;
GUI.Box(new Rect(0, 0, Screen.width, Screen.height), string.Empty);
GUI.Box(new Rect(0, PosY, ButtonWidth, ButtonHeight), "UltimateXR Fish Networking");
PosY += ButtonHeight;
if (_networkManager != null)
{
if (_networkManager.IsServer || _networkManager.IsClient)
{
if (_networkManager.IsServerOnly)
{
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Stop Server"))
{
_networkManager.ServerManager.StopConnection(true);
}
PosY += ButtonHeight;
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Start Client"))
{
_networkManager.ClientManager.StartConnection("localhost", NetworkPort);
}
}
else if (_networkManager.IsClientOnly)
{
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Disconnect Client"))
{
_networkManager.ClientManager.StopConnection();
}
}
else if (_networkManager.IsServer && _networkManager.IsClient)
{
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Stop Server"))
{
_networkManager.ServerManager.StopConnection(true);
}
PosY += ButtonHeight;
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Stop Client"))
{
_networkManager.ClientManager.StopConnection();
}
}
return;
}
}
else
{
_networkManager = NetworkManager.Instances.First();
}
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 (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Start Host"))
{
_networkManager.ServerManager.StartConnection(NetworkPort);
_networkManager.ClientManager.StartConnection(_networkAddress, NetworkPort);
}
PosY += ButtonHeight;
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Start Server"))
{
_networkManager.ServerManager.StartConnection(NetworkPort);
}
PosY += ButtonHeight;
if (GUI.Button(new Rect(0, PosY, ButtonWidth, ButtonHeight), "Start Client"))
{
_networkManager.ClientManager.StartConnection(_networkAddress, NetworkPort);
}
}
/// <summary>
/// Gets the network port specified by the user through the UI.
/// </summary>
private ushort NetworkPort => ushort.Parse(_networkPort);
#endregion
#region Private Types & Data
private int PosY { get; set; }
private const int LabelHeight = 25;
private const int ButtonWidth = 200;
private const int ButtonHeight = 40;
private NetworkManager _networkManager;
private string _networkAddress = "localhost";
private string _networkPort = "7777";
#endregion
#endif
}
}
#pragma warning restore 414

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 81f68be0652d69e4b82f09943363c604
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: