Last active
December 29, 2017 04:55
-
-
Save robert-wallis/515efd60a5d17b8c02304a91d2ab04b4 to your computer and use it in GitHub Desktop.
"Lower-Level" UNet example refactored into UI and Networking classes. https://docs.unity3d.com/Manual/UnityMultiplayerIntegratingLowLevel.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (C) 2017 Robert A. Wallis, All Rights Reserved | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Networking.Match; | |
using UnityEngine.UI; | |
namespace Network { | |
public class NetUi : MonoBehaviour { | |
[SerializeField] public Button ButtonCreateRoom; | |
[SerializeField] public Button ButtonJoinFirstRoom; | |
[SerializeField] public Button ButtonListRooms; | |
[SerializeField] public InputField InputFieldMessage; | |
[SerializeField] public Button ButtonSendMessage; | |
[SerializeField] public Button ButtonDisconnect; | |
[SerializeField] public GameObject ScrollViewContent; | |
[SerializeField] public GameObject PrefabMatchButton; | |
[SerializeField] public GameObject PrefabNetworkText; | |
string _matchName; | |
UnityRelayNetwork _unityRelayNetwork; | |
void Awake() { | |
var networkMatch = gameObject.AddComponent<NetworkMatch>(); | |
_unityRelayNetwork = new UnityRelayNetwork(networkMatch, Debug.Log, Debug.LogError); | |
ButtonCreateRoom.onClick.AddListener(OnCreateRoomClicked); | |
ButtonJoinFirstRoom.onClick.AddListener(OnJoinFirstFoundMatchClicked); | |
ButtonListRooms.onClick.AddListener(OnListRoomsClicked); | |
InputFieldMessage.onEndEdit.AddListener(OnMessageEndEdit); | |
ButtonSendMessage.onClick.AddListener(OnMessageSendClicked); | |
ButtonDisconnect.onClick.AddListener(OnDisconnectClicked); | |
_unityRelayNetwork.OnMatchHosted += EventMatchHosted; | |
_unityRelayNetwork.OnConnectToMatch += EventConnectToMatch; | |
_unityRelayNetwork.OnMatchesListed += EventListMatches; | |
_unityRelayNetwork.OnMessage += EventMessage; | |
_unityRelayNetwork.OnDisconnected += EventDisconnected; | |
} | |
void Start() { | |
// While testing with multiple standalone players on one machine this will need to be enabled | |
Application.runInBackground = true; | |
_matchName = $"v{Application.version} Match"; | |
CleanList(); | |
CreateLogEntry("Cloud Project Id: " + Application.cloudProjectId); | |
} | |
void CleanList() { | |
var childCount = ScrollViewContent.transform.childCount; | |
var objs = new GameObject[childCount]; | |
for (var i = 0; i < childCount; i++) { | |
objs[i] = ScrollViewContent.transform.GetChild(i).gameObject; | |
} | |
foreach (var o in objs) { | |
Destroy(o); | |
} | |
} | |
void OnApplicationQuit() { | |
_unityRelayNetwork.ShutDown(); | |
} | |
void Update() { | |
_unityRelayNetwork.ProcessNetworkEvents(); | |
} | |
void OnCreateRoomClicked() { | |
_unityRelayNetwork.CreateMatch(_matchName); | |
} | |
void OnJoinFirstFoundMatchClicked() { | |
_unityRelayNetwork.JoinAnyMatch(); | |
} | |
void OnListRoomsClicked() { | |
_unityRelayNetwork.ListMatches(); | |
} | |
void OnDisconnectClicked() { | |
_unityRelayNetwork.Disconnect(); | |
} | |
void OnMessageSendClicked() { | |
_unityRelayNetwork.Send(InputFieldMessage.text); | |
} | |
void OnMessageEndEdit(string message) { | |
_unityRelayNetwork.Send(message); | |
} | |
void EventMatchHosted(MatchInfo matchInfo) { | |
CleanList(); | |
CreateLogEntry($"Created server: {matchInfo}"); | |
CreateLogEntry($"Relay Server: {matchInfo.address} :{matchInfo.port}"); | |
CreateLogEntry($"NetworkID: {matchInfo.networkId} NodeID: {matchInfo.nodeId}"); | |
} | |
void EventConnectToMatch(MatchInfo matchInfo) { | |
CleanList(); | |
CreateLogEntry($"Joined game: {matchInfo}"); | |
CreateLogEntry($"Relay Server: {matchInfo.address} :{matchInfo.port}"); | |
CreateLogEntry($"NetworkID: {matchInfo.networkId} NodeID: {matchInfo.nodeId}"); | |
} | |
void EventListMatches(List<MatchInfoSnapshot> matches) { | |
if (matches.Count == 0) | |
return; | |
CleanList(); | |
foreach (var match in matches) { | |
CreateMatchButton(match); | |
} | |
} | |
void EventMessage(string message) { | |
CreateLogEntry(message); | |
} | |
void EventDisconnected() { | |
CleanList(); | |
} | |
void CreateMatchButton(MatchInfoSnapshot match) { | |
var buttonGo = Instantiate(PrefabMatchButton, ScrollViewContent.transform); | |
var text = buttonGo.GetComponentInChildren<Text>(); | |
text.text = match.name; | |
var button = buttonGo.GetComponentInChildren<Button>(); | |
var netId = match.networkId; | |
button.onClick.AddListener(() => { | |
_unityRelayNetwork.JoinMatchById(netId); | |
}); | |
} | |
void CreateLogEntry(string message) { | |
var logGo = Instantiate(PrefabNetworkText, ScrollViewContent.transform); | |
var text = logGo.GetComponent<Text>(); | |
text.text = message; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (C) 2017 Robert A. Wallis, All Rights Reserved | |
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Networking; | |
using UnityEngine.Networking.Match; | |
using UnityEngine.Networking.Types; | |
namespace Network { | |
public class UnityRelayNetwork { | |
const int MAX_MESSAGE_SIZE = 65535; | |
const int MAX_DEFAULT_CONNECTIONS = 4; | |
const int SERVER_PORT = 25000; | |
readonly List<int> _connectionIds = new List<int>(); | |
readonly Action<string> _errorLogger; | |
readonly Action<string> _logger; | |
readonly NetworkMatch _networkMatch; | |
readonly NetworkWriter _networkWriter; | |
readonly byte[] _receiveBuffer; | |
int _hostId = -1; | |
MatchInfo _matchInfo; | |
List<MatchInfoSnapshot> _matchList = new List<MatchInfoSnapshot>(); | |
public event Action<List<MatchInfoSnapshot>> OnMatchesListed; | |
public event Action<MatchInfo> OnMatchHosted; | |
public event Action<MatchInfo> OnConnectToMatch; | |
public event Action OnDisconnected; | |
public event Action<string> OnMessage; | |
public UnityRelayNetwork(NetworkMatch networkMatch, Action<string> logger, Action<string> errorLogger) { | |
UnityBugFixBaseUriIos(networkMatch); | |
_networkMatch = networkMatch; | |
_logger = logger; | |
_errorLogger = errorLogger; | |
_receiveBuffer = new byte[MAX_MESSAGE_SIZE]; | |
_networkWriter = new NetworkWriter(); | |
} | |
public void CreateMatch(string roomName) { | |
_networkMatch.CreateMatch(roomName, 4, true, "", "", "", 0, 0, OnMatchCreate); | |
} | |
public void ListMatches() { | |
_networkMatch.ListMatches(0, 20, "", true, 0, 0, OnMatchList); | |
} | |
public void JoinAnyMatch() { | |
if (_matchList.Count > 0) { | |
JoinMatchById(_matchList[0].networkId); | |
return; | |
} | |
_networkMatch.ListMatches(0, 1, "", true, 0, 0, (success, info, matches) => { | |
if (success && matches.Count > 0) | |
JoinMatchById(matches[0].networkId); | |
}); | |
} | |
public void JoinMatchById(NetworkID networkId) { | |
_networkMatch.JoinMatch(networkId, "", "", "", 0, 0, OnMatchJoined); | |
} | |
public void Send(string message) { | |
_networkWriter.SeekZero(); | |
_networkWriter.Write(message); | |
foreach (var connectionId in _connectionIds) { | |
byte error; | |
NetworkTransport.Send(_hostId, | |
connectionId, 0, _networkWriter.AsArray(), _networkWriter.Position, out error); | |
if ((NetworkError) error != NetworkError.Ok) | |
_errorLogger("Failed to send message: " + (NetworkError) error); | |
} | |
} | |
public void ProcessNetworkEvents() { | |
if (_hostId == -1) | |
return; | |
ProcessRelayEvents(); | |
ProcessHostEvents(); | |
} | |
void ProcessHostEvents() { | |
NetworkEventType networkEvent; | |
do { | |
int connectionId; | |
int channelId; | |
int receivedSize; | |
byte error; | |
networkEvent = NetworkTransport.ReceiveFromHost( | |
_hostId, | |
out connectionId, out channelId, | |
_receiveBuffer, _receiveBuffer.Length, | |
out receivedSize, out error); | |
if ((NetworkError) error != NetworkError.Ok) | |
_errorLogger("Error while receiveing network message: " + (NetworkError) error); | |
switch (networkEvent) { | |
case NetworkEventType.ConnectEvent: { | |
EventConnected(connectionId, channelId); | |
break; | |
} | |
case NetworkEventType.DataEvent: { | |
EventData(connectionId, channelId, receivedSize); | |
break; | |
} | |
case NetworkEventType.DisconnectEvent: { | |
EventDisconnected(connectionId); | |
break; | |
} | |
case NetworkEventType.Nothing: | |
break; | |
case NetworkEventType.BroadcastEvent: | |
break; | |
} | |
} while (networkEvent != NetworkEventType.Nothing); | |
} | |
void ProcessRelayEvents() { | |
byte error; | |
var networkEvent = NetworkTransport.ReceiveRelayEventFromHost(_hostId, out error); | |
switch (networkEvent) { | |
case NetworkEventType.ConnectEvent: | |
_logger("Relay Event: Connected"); | |
break; | |
case NetworkEventType.DisconnectEvent: | |
_logger("Relay Event: Disconnected"); | |
break; | |
case NetworkEventType.DataEvent: | |
break; | |
case NetworkEventType.Nothing: | |
break; | |
case NetworkEventType.BroadcastEvent: | |
break; | |
} | |
} | |
public void Disconnect() { | |
if (_matchInfo == null) | |
return; | |
_networkMatch.DropConnection(_matchInfo.networkId, _matchInfo.nodeId, 0, OnConnectionDropped); | |
} | |
public void ShutDown() { | |
NetworkTransport.Shutdown(); | |
_hostId = -1; | |
_connectionIds.Clear(); | |
_matchInfo = null; | |
} | |
void StartServer(string relayIp, int relayPort, NetworkID networkId, NodeID nodeId) { | |
_logger("NetworkTransport.Init"); | |
NetworkTransport.Init(); | |
var topology = SetupTopology(); | |
_hostId = NetworkTransport.AddHost(topology, SERVER_PORT); | |
byte error; | |
_logger("StartServer ConnectAsNetworkHost"); | |
var sourceId = Utility.GetSourceID(); | |
NetworkTransport.ConnectAsNetworkHost(_hostId, relayIp, relayPort, networkId, sourceId, nodeId, out error); | |
} | |
void StartClient(string relayIp, int relayPort, NetworkID networkId, NodeID nodeId) { | |
_logger("NetworkTransport.Init"); | |
NetworkTransport.Init(); | |
var topology = SetupTopology(); | |
_hostId = NetworkTransport.AddHost(topology); | |
byte error; | |
_logger("StartClient ConnectToNetworkPeer"); | |
var sourceId = Utility.GetSourceID(); | |
NetworkTransport.ConnectToNetworkPeer(_hostId, relayIp, relayPort, 0, 0, networkId, sourceId, nodeId, | |
out error); | |
} | |
HostTopology SetupTopology() { | |
var config = new ConnectionConfig(); | |
config.AddChannel(QosType.Reliable); | |
config.AddChannel(QosType.Unreliable); | |
return new HostTopology(config, MAX_DEFAULT_CONNECTIONS); | |
} | |
void EventDisconnected(int connectionId) { | |
_logger("Event Disconnected on #" + connectionId); | |
} | |
void EventData(int connectionId, int channelId, int receivedSize) { | |
_logger("Event Data on #" + connectionId + " channel:" + channelId + " size:" + receivedSize); | |
var networkReader = new NetworkReader(_receiveBuffer); | |
var message = networkReader.ReadString(); | |
OnMessage?.Invoke(message); | |
} | |
void EventConnected(int connectionId, int channelId) { | |
_logger("Event Connect #:" + connectionId + " channel:" + channelId); | |
_connectionIds.Add(connectionId); | |
} | |
void OnConnectionDropped(bool success, string extendedInfo) { | |
_logger("Connection Dropped"); | |
ShutDown(); | |
OnDisconnected?.Invoke(); | |
} | |
void OnMatchCreate(bool success, string extendedInfo, MatchInfo matchInfo) { | |
if (!success) { | |
_errorLogger("Match Failed to Create: " + extendedInfo); | |
return; | |
} | |
_logger("Match Created"); | |
Utility.SetAccessTokenForNetwork(matchInfo.networkId, matchInfo.accessToken); | |
_matchInfo = matchInfo; | |
StartServer(matchInfo.address, matchInfo.port, matchInfo.networkId, matchInfo.nodeId); | |
OnMatchHosted?.Invoke(matchInfo); | |
} | |
void OnMatchList(bool success, string extendedInfo, List<MatchInfoSnapshot> matches) { | |
if (!success) { | |
_errorLogger("List match failed: " + extendedInfo); | |
return; | |
} | |
if (matches == null) | |
return; | |
_matchList = matches; | |
OnMatchesListed?.Invoke(matches); | |
} | |
void OnMatchJoined(bool success, string extendedInfo, MatchInfo matchInfo) { | |
if (!success) { | |
_errorLogger("Join match failed: " + extendedInfo); | |
return; | |
} | |
_logger("Join match succeeded"); | |
Utility.SetAccessTokenForNetwork(matchInfo.networkId, matchInfo.accessToken); | |
_matchInfo = matchInfo; | |
_logger("Connecting to Address:" + matchInfo.address + " Port:" + matchInfo.port + " NetworKID: " + | |
matchInfo.networkId + " NodeID: " + matchInfo.nodeId); | |
StartClient(matchInfo.address, matchInfo.port, matchInfo.networkId, matchInfo.nodeId); | |
OnConnectToMatch?.Invoke(matchInfo); | |
} | |
static void UnityBugFixBaseUriIos(NetworkMatch networkMatch) { | |
if (networkMatch.baseUri != null) | |
return; | |
var uri = new Uri("https://mm.unet.unity3d.com/"); | |
Debug.Log($"Unity Bug: baseUri was null, setting to {uri} still a bug in " + Application.unityVersion); | |
networkMatch.baseUri = uri; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment