New Example, auto LAN connect + client acting as controller for server. (#3863)

This commit is contained in:
JesusLuvsYooh 2024-07-17 08:58:44 +01:00 committed by GitHub
parent ae2fe3834e
commit 02e9b4fc0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 4089 additions and 0 deletions

View File

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

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8e092fa854dbe4a24b7489ecd5b5b9fe
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,51 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2448974773247019605
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2051395529432152427}
- component: {fileID: 4941989115671398543}
m_Layer: 0
m_Name: PlayerController
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2051395529432152427
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2448974773247019605}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4941989115671398543
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2448974773247019605}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3}
m_Name:
m_EditorClassIdentifier:
sceneId: 0
_assetId: 0
serverOnly: 0
visibility: 0
hasSpawned: 0

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2b6efbce53c9140b88da2ec384c5a120
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,98 @@
using System;
using System.Net;
using UnityEngine;
using UnityEngine.Events;
using Mirror.Discovery;
//[Serializable]
//public class ServerFoundUnityEvent<TResponseType> : UnityEvent<TResponseType> { };
namespace Mirror.Examples.AutoLANClientController
{
[DisallowMultipleComponent]
[AddComponentMenu("Network/Network Discovery")]
public class AutoLANNetworkDiscovery : NetworkDiscoveryBase<ServerRequest, ServerResponse>
{
#region Server
public CanvasHUD canvasHUD;
/// <summary>
/// Process the request from a client
/// </summary>
/// <remarks>
/// Override if you wish to provide more information to the clients
/// such as the name of the host player
/// </remarks>
/// <param name="request">Request coming from client</param>
/// <param name="endpoint">Address of the client that sent the request</param>
/// <returns>The message to be sent back to the client or null</returns>
protected override ServerResponse ProcessRequest(ServerRequest request, IPEndPoint endpoint)
{
// In this case we don't do anything with the request
// but other discovery implementations might want to use the data
// in there, This way the client can ask for
// specific game mode or something
try
{
// this is an example reply message, return your own
// to include whatever is relevant for your game
return new ServerResponse
{
serverId = ServerId,
uri = transport.ServerUri()
};
}
catch (NotImplementedException)
{
Debug.LogError($"Transport {transport} does not support network discovery");
throw;
}
}
#endregion
#region Client
/// <summary>
/// Create a message that will be broadcasted on the network to discover servers
/// </summary>
/// <remarks>
/// Override if you wish to include additional data in the discovery message
/// such as desired game mode, language, difficulty, etc... </remarks>
/// <returns>An instance of ServerRequest with data to be broadcasted</returns>
protected override ServerRequest GetRequest() => new ServerRequest();
/// <summary>
/// Process the answer from a server
/// </summary>
/// <remarks>
/// A client receives a reply from a server, this method processes the
/// reply and raises an event
/// </remarks>
/// <param name="response">Response that came from the server</param>
/// <param name="endpoint">Address of the server that replied</param>
protected override void ProcessResponse(ServerResponse response, IPEndPoint endpoint)
{
// we received a message from the remote endpoint
response.EndPoint = endpoint;
// although we got a supposedly valid url, we may not be able to resolve
// the provided host
// However we know the real ip address of the server because we just
// received a packet from it, so use that as host.
UriBuilder realUri = new UriBuilder(response.uri)
{
Host = response.EndPoint.Address.ToString()
};
response.uri = realUri.Uri;
//OnServerFound.Invoke(response);
if (canvasHUD == null)
{ canvasHUD = GameObject.FindObjectOfType<CanvasHUD>(); }
canvasHUD.OnDiscoveredServer(response);
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,198 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using UnityEngine.UI;
using Mirror.Discovery;
using UnityEngine.SceneManagement;
namespace Mirror.Examples.AutoLANClientController
{
public class CanvasHUD : MonoBehaviour
{
// this will check for games to join, if non, start host.
public bool alwaysAutoStart = false;
public AutoLANNetworkDiscovery networkDiscovery;
readonly Dictionary<long, ServerResponse> discoveredServers = new Dictionary<long, ServerResponse>();
public bool runAsPlayerHost = false;
// UI
public GameObject PanelStart, PanelStop;
public Button buttonHost, buttonServer, buttonClient, buttonStop, buttonAuto;
public Text infoText;
// legacy inputfield interaction does not auto bring up a keyboard on headset builds, use tmp.
public InputField inputFieldAddress;
private void Start()
{
//Make sure to attach these Buttons in the Inspector
buttonHost.onClick.AddListener(ButtonHost);
buttonServer.onClick.AddListener(ButtonServer);
buttonClient.onClick.AddListener(ButtonClient);
buttonStop.onClick.AddListener(ButtonStop);
buttonAuto.onClick.AddListener(ButtonAuto);
//Update the canvas text if you have manually changed network managers address from the game object before starting the game scene
inputFieldAddress.text = NetworkManager.singleton.networkAddress;
//Adds a listener to the input field and invokes a method when the value changes.
inputFieldAddress.onValueChanged.AddListener(delegate { OnValueChangedAddress(); });
if (networkDiscovery == null)
{ networkDiscovery = GameObject.FindObjectOfType<AutoLANNetworkDiscovery>(); }
// skips waiting for users to press ui button
if (alwaysAutoStart)
{
StartCoroutine(Waiter());
}
}
public IEnumerator Waiter()
{
infoText.text = "Discovering servers..";
discoveredServers.Clear();
networkDiscovery.StartDiscovery();
// we have set this as 3.1 seconds, default discovery scan is 3 seconds, allows some time if host and client are started at same time
yield return new WaitForSeconds(3.1f);
if (discoveredServers == null || discoveredServers.Count <= 0)
{
if (runAsPlayerHost == true)
{
infoText.text = "No Servers found, starting as Host.";
}
else
{
infoText.text = "No Servers found, starting as Server.";
}
yield return new WaitForSeconds(1.0f);
discoveredServers.Clear();
// NetworkManager.singleton.onlineScene = SceneManager.GetActiveScene().name;
if (runAsPlayerHost == true)
{
NetworkManager.singleton.StartHost();
}
else
{
NetworkManager.singleton.StartServer();
}
networkDiscovery.AdvertiseServer();
}
}
void Connect(ServerResponse info)
{
infoText.text = "Connecting to: " + info.serverId;
networkDiscovery.StopDiscovery();
NetworkManager.singleton.StartClient(info.uri);
}
public void OnDiscoveredServer(ServerResponse info)
{
discoveredServers[info.serverId] = info;
Connect(info);
}
public void ButtonHost()
{
SetupInfoText("Starting as host");
discoveredServers.Clear();
//NetworkManager.singleton.onlineScene = SceneManager.GetActiveScene().name;
NetworkManager.singleton.StartHost();
networkDiscovery.AdvertiseServer();
}
public void ButtonServer()
{
SetupInfoText("Starting as server.");
discoveredServers.Clear();
// NetworkManager.singleton.onlineScene = SceneManager.GetActiveScene().name;
NetworkManager.singleton.StartServer();
networkDiscovery.AdvertiseServer();
}
public void ButtonClient()
{
SetupInfoText("Starting as client.");
discoveredServers.Clear();
networkDiscovery.StartDiscovery();
}
public void ButtonStop()
{
SetupInfoText("Stopping.");
// stop host if host mode
if (NetworkServer.active && NetworkClient.isConnected)
{
NetworkManager.singleton.StopHost();
}
// stop client if client-only
else if (NetworkClient.isConnected)
{
NetworkManager.singleton.StopClient();
}
// stop server if server-only
else if (NetworkServer.active)
{
NetworkManager.singleton.StopServer();
}
networkDiscovery.StopDiscovery();
// we need to call setup canvas a second time in this function for it to update the abovee changes
SetupCanvas();
}
public void ButtonAuto()
{
SetupInfoText("Auto Starting.");
StartCoroutine(Waiter());
}
// manually call canvas changes for performance, can lazily be done via Update()
public void SetupCanvas()
{
// Here we will dump majority of the canvas UI
if (NetworkManager.singleton == null)
{
SetupInfoText("NetworkManager null");
return;
}
// check network status, and show required UI
if (!NetworkClient.isConnected && !NetworkServer.active)
{
if (NetworkClient.active)
{
PanelStart.SetActive(false);
PanelStop.SetActive(true);
}
else
{
PanelStart.SetActive(true);
PanelStop.SetActive(false);
}
}
else
{
PanelStart.SetActive(false);
PanelStop.SetActive(true);
}
}
// useful status info to display on screen
public void SetupInfoText(string _info)
{
infoText.text = _info;
SetupCanvas();
}
// Invoked when the value of the text field changes.
public void OnValueChangedAddress()
{
NetworkManager.singleton.networkAddress = inputFieldAddress.text;
}
}
}

View File

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

View File

@ -0,0 +1,329 @@
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
using Mirror;
using System.Linq;
/*
Documentation: https://mirror-networking.gitbook.io/docs/components/network-manager
API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkManager.html
*/
namespace Mirror.Examples.AutoLANClientController
{
public class AutoLANNetworkManager : NetworkManager
{
// Overrides the base singleton so we don't
// have to cast to this type everywhere.
public static new AutoLANNetworkManager singleton { get; private set; }
public CanvasHUD canvasHUD;
private NetworkIdentity[] copyOfOwnedObjects;
/// <summary>
/// Runs on both Server and Client
/// Networking is NOT initialized when this fires
/// </summary>
public override void Awake()
{
base.Awake();
singleton = this;
}
#region Unity Callbacks
public override void OnValidate()
{
base.OnValidate();
}
/// <summary>
/// Runs on both Server and Client
/// Networking is NOT initialized when this fires
/// </summary>
public override void Start()
{
base.Start();
if (canvasHUD == null)
{ canvasHUD = GameObject.FindObjectOfType<CanvasHUD>(); }
}
/// <summary>
/// Runs on both Server and Client
/// </summary>
public override void LateUpdate()
{
base.LateUpdate();
}
/// <summary>
/// Runs on both Server and Client
/// </summary>
public override void OnDestroy()
{
base.OnDestroy();
//UnityEngine.Debug.Log("OnDestroy");
}
#endregion
#region Start & Stop
/// <summary>
/// Set the frame rate for a headless server.
/// <para>Override if you wish to disable the behavior or set your own tick rate.</para>
/// </summary>
public override void ConfigureHeadlessFrameRate()
{
base.ConfigureHeadlessFrameRate();
}
/// <summary>
/// called when quitting the application by closing the window / pressing stop in the editor
/// </summary>
public override void OnApplicationQuit()
{
base.OnApplicationQuit();
//UnityEngine.Debug.Log("OnApplicationQuit");
}
#endregion
#region Scene Management
/// <summary>
/// This causes the server to switch scenes and sets the networkSceneName.
/// <para>Clients that connect to this server will automatically switch to this scene. This is called automatically if onlineScene or offlineScene are set, but it can be called from user code to switch scenes again while the game is in progress. This automatically sets clients to be not-ready. The clients must call NetworkClient.Ready() again to participate in the new scene.</para>
/// </summary>
/// <param name="newSceneName"></param>
public override void ServerChangeScene(string newSceneName)
{
base.ServerChangeScene(newSceneName);
//UnityEngine.Debug.Log("ServerChangeScene");
}
/// <summary>
/// Called from ServerChangeScene immediately before SceneManager.LoadSceneAsync is executed
/// <para>This allows server to do work / cleanup / prep before the scene changes.</para>
/// </summary>
/// <param name="newSceneName">Name of the scene that's about to be loaded</param>
public override void OnServerChangeScene(string newSceneName)
{
//UnityEngine.Debug.Log("OnServerChangeScene");
}
/// <summary>
/// Called on the server when a scene is completed loaded, when the scene load was initiated by the server with ServerChangeScene().
/// </summary>
/// <param name="sceneName">The name of the new scene.</param>
public override void OnServerSceneChanged(string sceneName)
{
//UnityEngine.Debug.Log("OnServerSceneChanged");
}
/// <summary>
/// Called from ClientChangeScene immediately before SceneManager.LoadSceneAsync is executed
/// <para>This allows client to do work / cleanup / prep before the scene changes.</para>
/// </summary>
/// <param name="newSceneName">Name of the scene that's about to be loaded</param>
/// <param name="sceneOperation">Scene operation that's about to happen</param>
/// <param name="customHandling">true to indicate that scene loading will be handled through overrides</param>
public override void OnClientChangeScene(string newSceneName, SceneOperation sceneOperation, bool customHandling)
{
//UnityEngine.Debug.Log("OnClientChangeScene");
}
/// <summary>
/// Called on clients when a scene has completed loaded, when the scene load was initiated by the server.
/// <para>Scene changes can cause player objects to be destroyed. The default implementation of OnClientSceneChanged in the NetworkManager is to add a player object for the connection if no player object exists.</para>
/// </summary>
public override void OnClientSceneChanged()
{
base.OnClientSceneChanged();
// UnityEngine.Debug.Log("OnClientSceneChanged");
}
#endregion
#region Server System Callbacks
/// <summary>
/// Called on the server when a new client connects.
/// <para>Unity calls this on the Server when a Client connects to the Server. Use an override to tell the NetworkManager what to do when a client connects to the server.</para>
/// </summary>
/// <param name="conn">Connection from client.</param>
public override void OnServerConnect(NetworkConnectionToClient conn)
{
//UnityEngine.Debug.Log("OnServerConnect");
canvasHUD.SetupInfoText("A client connected.");
}
/// <summary>
/// Called on the server when a client is ready.
/// <para>The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process.</para>
/// </summary>
/// <param name="conn">Connection from client.</param>
public override void OnServerReady(NetworkConnectionToClient conn)
{
base.OnServerReady(conn);
//UnityEngine.Debug.Log("OnServerReady");
}
/// <summary>
/// Called on the server when a client adds a new player with ClientScene.AddPlayer.
/// <para>The default implementation for this function creates a new player object from the playerPrefab.</para>
/// </summary>
/// <param name="conn">Connection from client.</param>
public override void OnServerAddPlayer(NetworkConnectionToClient conn)
{
base.OnServerAddPlayer(conn);
//UnityEngine.Debug.Log("OnServerAddPlayer");
}
/// <summary>
/// Called on the server when a client disconnects.
/// <para>This is called on the Server when a Client disconnects from the Server. Use an override to decide what should happen when a disconnection is detected.</para>
/// </summary>
/// <param name="conn">Connection from client.</param>
public override void OnServerDisconnect(NetworkConnectionToClient conn)
{
// this code is to reset any objects belonging to disconnected clients
// make a copy because the original collection will change in the loop
copyOfOwnedObjects = conn.owned.ToArray();
// Loop the copy, skipping the player object.
// RemoveClientAuthority on everything else
foreach (NetworkIdentity identity in copyOfOwnedObjects)
{
if (identity != conn.identity)
identity.RemoveClientAuthority();
}
base.OnServerDisconnect(conn);
//UnityEngine.Debug.Log("OnServerDisconnect");
canvasHUD.SetupInfoText("A client disconnected.");
}
/// <summary>
/// Called on server when transport raises an error.
/// <para>NetworkConnection may be null.</para>
/// </summary>
/// <param name="conn">Connection of the client...may be null</param>
/// <param name="transportError">TransportError enum</param>
/// <param name="message">String message of the error.</param>
public override void OnServerError(NetworkConnectionToClient conn, TransportError transportError, string message)
{
UnityEngine.Debug.Log("OnServerError");
canvasHUD.SetupInfoText("OnServerError: " + message);
}
#endregion
#region Client System Callbacks
/// <summary>
/// Called on the client when connected to a server.
/// <para>The default implementation of this function sets the client as ready and adds a player. Override the function to dictate what happens when the client connects.</para>
/// </summary>
public override void OnClientConnect()
{
base.OnClientConnect();
//UnityEngine.Debug.Log("OnClientConnect");
canvasHUD.SetupInfoText("Connected to server.");
}
/// <summary>
/// Called on clients when disconnected from a server.
/// <para>This is called on the client when it disconnects from the server. Override this function to decide what happens when the client disconnects.</para>
/// </summary>
public override void OnClientDisconnect()
{
//UnityEngine.Debug.Log("OnClientDisconnect");
canvasHUD.SetupInfoText("Disconnected from server.");
}
/// <summary>
/// Called on clients when a servers tells the client it is no longer ready.
/// <para>This is commonly used when switching scenes.</para>
/// </summary>
public override void OnClientNotReady()
{
//UnityEngine.Debug.Log("OnClientNotReady");
}
/// <summary>
/// Called on client when transport raises an error.</summary>
/// </summary>
/// <param name="transportError">TransportError enum.</param>
/// <param name="message">String message of the error.</param>
public override void OnClientError(TransportError transportError, string message)
{
UnityEngine.Debug.Log("OnClientError");
canvasHUD.SetupInfoText("OnClientError: " + message);
}
#endregion
#region Start & Stop Callbacks
// Since there are multiple versions of StartServer, StartClient and StartHost, to reliably customize
// their functionality, users would need override all the versions. Instead these callbacks are invoked
// from all versions, so users only need to implement this one case.
/// <summary>
/// This is invoked when a host is started.
/// <para>StartHost has multiple signatures, but they all cause this hook to be called.</para>
/// </summary>
public override void OnStartHost()
{
//UnityEngine.Debug.Log("OnStartHost");
canvasHUD.SetupInfoText("Started Hosting.");
}
/// <summary>
/// This is invoked when a server is started - including when a host is started.
/// <para>StartServer has multiple signatures, but they all cause this hook to be called.</para>
/// </summary>
public override void OnStartServer()
{
//UnityEngine.Debug.Log("OnStartServer");
canvasHUD.SetupInfoText("Started server.");
}
/// <summary>
/// This is invoked when the client is started.
/// </summary>
public override void OnStartClient()
{
//UnityEngine.Debug.Log("OnStartClient");
canvasHUD.SetupInfoText("Client started.");
}
/// <summary>
/// This is called when a host is stopped.
/// </summary>
public override void OnStopHost()
{
//UnityEngine.Debug.Log("OnStopHost");
canvasHUD.SetupInfoText("Hosting stopped.");
}
/// <summary>
/// This is called when a server is stopped - including when a host is stopped.
/// </summary>
public override void OnStopServer()
{
//UnityEngine.Debug.Log("OnStopServer");
canvasHUD.SetupInfoText("Server stopped.");
}
/// <summary>
/// This is called when a client is stopped.
/// </summary>
public override void OnStopClient()
{
//UnityEngine.Debug.Log("OnStopClient");
canvasHUD.SetupInfoText("Client stopped.");
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,48 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using UnityEngine.UI;
namespace Mirror.Examples.AutoLANClientController
{
public class NetworkSceneScript : NetworkBehaviour
{
public Button clientButton;
public Text textResult;
public GameObject panelClient, panelServer;
void Start()
{
clientButton.onClick.AddListener(ClientButton);
panelServer.SetActive(false);
panelClient.SetActive(false);
if (isServer)
{
panelServer.SetActive(true);
}
if (isClient)
{
panelClient.SetActive(true);
}
}
void ClientButton()
{
if (isClient)
{
CmdSendText("Text: " + Random.Range(0, 999));
}
}
[Command(requiresAuthority = false)]
public void CmdSendText(string _value)
{
textResult.text = _value;
}
}
}

View File

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