Skip to content

Instantly share code, notes, and snippets.

@CodeSmile-0000011110110111
Last active September 21, 2022 18:16
Show Gist options
  • Save CodeSmile-0000011110110111/d37c7cca0baa3e6d22c6e7ea329ccbd4 to your computer and use it in GitHub Desktop.
Save CodeSmile-0000011110110111/d37c7cca0baa3e6d22c6e7ea329ccbd4 to your computer and use it in GitHub Desktop.
Improved Bootstrap example scripts for Unity Netcode for GameObjects, see: https://forum.unity.com/threads/testing-netcode-through-the-internet.1209205/#post-8456006
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
namespace Unity.Netcode.Samples
{
/// <summary>
/// Class to display helper buttons and status labels on the GUI, as well as buttons to start host/client/server.
/// Once a connection has been established to the server, the local player can be teleported to random positions via a GUI button.
/// </summary>
public class BootstrapManager : MonoBehaviour
{
private string _hostIpString = "<enter host IP or domain>";
private string _portString = "7777";
private ushort _port;
private string _resolvedHostIp = "";
public static string[] GetLocalIPv4(NetworkInterfaceType _type)
{
var ipAddrList = new List<string>();
foreach (var item in NetworkInterface.GetAllNetworkInterfaces())
{
if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
{
foreach (var ip in item.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
ipAddrList.Add(ip.Address.ToString());
}
}
}
return ipAddrList.ToArray();
}
public static IPAddress[] ResolveHostname(string hostName, bool ip6Wanted = false)
{
// is it already an IP?
if (IPAddress.TryParse(hostName, out var outIpAddress))
return new[] { outIpAddress };
try
{
var addresslist = Dns.GetHostAddresses(hostName);
if (addresslist == null || addresslist.Length == 0)
return new IPAddress[0];
if (ip6Wanted)
return addresslist.Where(o => o.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
return addresslist.Where(o => o.AddressFamily == AddressFamily.InterNetwork).ToArray();
}
catch {}
return null;
}
private void OnGUI()
{
GUILayout.BeginArea(new Rect(10, 10, 300, 300));
var networkManager = NetworkManager.Singleton;
var transport = networkManager.GetComponent<UnityTransport>();
_hostIpString = GUILayout.TextField(_hostIpString);
_portString = GUILayout.TextField(_portString);
var localIP = GetLocalIPv4(NetworkInterfaceType.Ethernet)?.FirstOrDefault();
if (!networkManager.IsClient && !networkManager.IsServer)
{
if (GUILayout.Button("Client"))
{
_resolvedHostIp = ResolveHostname(_hostIpString)?.FirstOrDefault()?.ToString();
ushort.TryParse(_portString, out _port);
print($"Connecting to: {_resolvedHostIp}:{_port}");
transport.SetConnectionData(_resolvedHostIp, _port);
networkManager.StartClient();
}
GUILayout.Space(40f);
var startHost = GUILayout.Button("Host");
var startServer = GUILayout.Button("Server");
if (startHost || startServer)
{
localIP = GetLocalIPv4(NetworkInterfaceType.Ethernet).FirstOrDefault();
ushort.TryParse(_portString, out _port);
print($"Serving on: {localIP}:{_port}");
transport.SetConnectionData(localIP, _port, "0.0.0.0");
if (startHost)
networkManager.StartHost();
if (startServer)
networkManager.StartServer();
}
}
else
{
var mode = networkManager.IsHost ? $"Host {localIP}:{_port}" :
networkManager.IsServer ? $"Server {localIP}:{_port}" : $"Client connected to {_resolvedHostIp}:{_port}";
GUILayout.Label($"Mode: {mode}");
// "Random Teleport" button will only be shown to clients
if (networkManager.IsClient)
{
if (GUILayout.Button("Random Teleport"))
{
if (networkManager.LocalClient != null)
{
// Get `BootstrapPlayer` component from the player's `PlayerObject`
if (networkManager.LocalClient.PlayerObject.TryGetComponent(out BootstrapPlayer bootstrapPlayer))
{
// Invoke a `ServerRpc` from client-side to teleport player to a random position on the server-side
bootstrapPlayer.RandomTeleportServerRpc();
}
}
}
}
}
GUILayout.EndArea();
}
}
}
using UnityEngine;
namespace Unity.Netcode.Samples
{
/// <summary>
/// Component attached to the "Player Prefab" on the `NetworkManager`.
/// </summary>
public class BootstrapPlayer : NetworkBehaviour
{
private static Vector3 GetRandomPositionOnXYPlane() => new(Random.Range(-5f, 5f), Random.Range(-5f, 5f), 0f);
private void Start()
{
GetComponent<MeshRenderer>().material.color = IsLocalPlayer ? Color.green : Color.red;
if (IsOwner)
RandomTeleportServerRpc();
}
/// <summary>
/// If this method is invoked on the client instance of this player, it will invoke a `ServerRpc` on the server-side.
/// If this method is invoked on the server instance of this player, it will teleport player to a random position.
/// </summary>
/// <remarks>
/// Since a `NetworkTransform` component is attached to this player, and the authority on that component is set to "Server",
/// this transform's position modification can only be performed on the server, where it will then be replicated down to all clients through `NetworkTransform`.
/// </remarks>
[ServerRpc]
public void RandomTeleportServerRpc()
{
var oldPosition = transform.position;
transform.position = GetRandomPositionOnXYPlane();
var newPosition = transform.position;
print(
$"{nameof(RandomTeleportServerRpc)}() -> {nameof(OwnerClientId)}: {OwnerClientId} --- {nameof(oldPosition)}: {oldPosition} --- {nameof(newPosition)}: {newPosition}");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment