Created
July 18, 2016 08:10
-
-
Save arun02139/2bfd67e62c4c29d359b6434d1a5564c0 to your computer and use it in GitHub Desktop.
This file contains 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
using UnityEngine; | |
using UnityEngine.UI; | |
using UnityEngine.Assertions; | |
using UnityEngine.Networking; | |
using UnityEngine.Networking.NetworkSystem; | |
using UnityEngine.Networking.Types; | |
using UnityEngine.Networking.Match; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Reflection; | |
using UnityEngine.SceneManagement; | |
public class CustomNetworkManager : NetworkManager | |
{ | |
// convenient way to grab the NetworkManager from anywhere | |
static public CustomNetworkManager i; | |
//Client numPlayers from NetworkManager is always 0, so we count (throught connect/destroy in LobbyPlayer) the number | |
//of players, so that even client know how many player there is. | |
[HideInInspector] | |
public int _playerNumber = 0; | |
int _maxPlayers = 2; // HARDCODE | |
int _minPlayers = 2; // HARDCODE | |
//used to disconnect a client properly when exiting the matchmaker TODO: figure out exactly what this comment means | |
[HideInInspector] | |
public bool _isMatchmaking = false; | |
// can switch between hosting and searching modes | |
public bool hosting = true; | |
// for detemining when the auto-match process is complete | |
public bool matching = true; | |
public bool matchCreated = false; | |
public bool allPlayersReady = false; | |
protected bool _disconnectServer = false; | |
public ulong currentMatchNetworkId { get { return _currentMatchNetworkId; } } | |
protected ulong _currentMatchNetworkId; | |
[HideInInspector] | |
public string status; | |
// sub-coroutines to seperate asyc logic | |
IEnumerator _host; | |
IEnumerator _search; | |
// keep track of how many players are ready (copied from NetworkLobbyManager) | |
// struct PendingPlayer | |
// { | |
// public NetworkConnection conn; | |
// public GameObject lobbyPlayer; | |
// } | |
// List<PendingPlayer> _pendingPlayers = new List<PendingPlayer>(); | |
// static message objects to avoid runtime-allocations | |
static LobbyReadyToBeginMessage s_ReadyToBeginMessage = new LobbyReadyToBeginMessage(); | |
static IntegerMessage s_SceneLoadedMessage = new IntegerMessage(); | |
static LobbyReadyToBeginMessage s_LobbyReadyToBeginMessage = new LobbyReadyToBeginMessage(); | |
void Start() | |
{ | |
i = this; | |
_host = Host(); // = Host(2.5f to 5f); | |
_search = Search(); // = Search(2.5f); | |
base.maxConnections = 2; // HARDCODE | |
DontDestroyOnLoad(gameObject); | |
StartCoroutine(AutoMatch()); | |
} | |
public delegate void BackButtonDelegate(); | |
public BackButtonDelegate backDelegate; | |
public void GoBackButton() | |
{ | |
backDelegate(); | |
} | |
// ----------------- Server management | |
// NOTE: overrides NetworkManager.OnStartServer (empty virtual function) | |
public override void OnStartServer() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnStartServer{0}", "")); | |
Assert.IsNotNull (base.offlineScene); | |
Assert.IsNotNull (base.onlineScene); | |
// if (lobbySlots.Length == 0) | |
// { | |
// lobbySlots = new NetworkLobbyPlayer[maxPlayers]; | |
// } | |
NetworkServer.RegisterHandler(MsgType.LobbyReadyToBegin, OnServerReadyToBeginMessage); | |
NetworkServer.RegisterHandler(MsgType.LobbySceneLoaded, OnServerSceneLoadedMessage); | |
NetworkServer.RegisterHandler(MsgType.LobbyReturnToLobby, OnServerReturnToLobbyMessage); | |
} | |
public void AddLocalPlayer() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.AddLocalPlayer{0}", "")); | |
} | |
// public void RemovePlayer(LobbyPlayer player) | |
// { | |
// Debug.Log (string.Format ("CustomNetworkManager.RemovePlayer{0}", "")); | |
// } | |
public void SimpleBackClbk() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.SimpleBackClbk{0}", "")); | |
} | |
public void StopHostClbk() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.StopHostClbk{0}", "")); | |
if (_isMatchmaking) | |
{ | |
matchMaker.DestroyMatch((NetworkID)_currentMatchNetworkId, OnDestroyMatch); | |
_disconnectServer = true; | |
} | |
else | |
{ | |
StopHost(); | |
} | |
} | |
public void StopClientClbk() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.StopClientClbk{0}", "")); | |
StopClient(); | |
if (_isMatchmaking) | |
{ | |
StopMatchMaker(); | |
} | |
} | |
public void StopServerClbk() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.StopServerClbk{0}", "")); | |
StopServer(); | |
} | |
public void ServerReturnToLobby() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.ServerReturnToLobby: NetworkServer.active = {0}", NetworkServer.active)); | |
if (!NetworkServer.active) | |
{ | |
Debug.Log("ServerReturnToLobby called on client"); | |
return; | |
} | |
// ServerChangeScene(base.offlineScene); | |
} | |
//=================== | |
// NOTE: overrides NetworkManager.OnStartHost (empty virtual function) | |
// NOTE: base.StartHost() calls base.StartServer(), which will call base.ServerChangeScene(m_OnlineScene) | |
public override void OnStartHost() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnStartHost:", "")); | |
backDelegate = StopHostClbk; | |
} | |
// NOTE: overrides NetworkManager.OnMatchCreate | |
public override void OnMatchCreate(CreateMatchResponse createMatchResponse) | |
{ | |
if (!createMatchResponse.success) | |
{ | |
Debug.LogWarning (string.Format ("CustomNetworkManager.OnMatchCreate: createMatchResponse.success = {0}, aborting", | |
createMatchResponse.success)); | |
return; | |
} | |
MatchInfo matchInfo = new MatchInfo (createMatchResponse); | |
Debug.Log (string.Format ("CustomNetworkManager.OnMatchCreate: createMatchResponse =\n{0}\nmatchInfo = {1}\nbase.customConfig = {2}", | |
createMatchResponse.PrettyPrint(false, "", true), matchInfo, base.customConfig)); | |
// NOTE: calling base function below subsequently calls base.StartHost(new MatchInfo(createMatchResponse)).. | |
// ..which sets base.matchInfo and calls base.StartServer(matchInfo).. | |
// ..which calls base.StartServer(matchInfo, null, -1).. | |
// ..which does a bunch of checks on the configutation (none) and matchInfo.. | |
// ..and calls base.ServerChangeScene, which changes the scene (WHERE IS THE CHECK FOR MIN PLAYERS??) | |
base.OnMatchCreate(createMatchResponse); | |
matchCreated = true; | |
_currentMatchNetworkId = (ulong)createMatchResponse.networkId; | |
} | |
public void OnDestroyMatch(BasicResponse extendedInfo) | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnDestroyMatch{0}", "")); | |
if (_disconnectServer) | |
{ | |
StopMatchMaker(); | |
StopHost(); | |
} | |
} | |
// ----------------- Server callbacks ------------------ | |
// --- Countdown management | |
public IEnumerator ServerCountdownCoroutine() | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.ServerCountdownCoroutine: {0}", base.onlineScene)); | |
ServerChangeScene(base.onlineScene); | |
yield return null; | |
} | |
// ----------------- Client callbacks ------------------ | |
// NOTE: overrides NetworkManager.OnClientConnect | |
public override void OnClientConnect(NetworkConnection conn) | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnClientConnect: network connection = \n{0}", conn.PrettyPrint(false, "", true))); | |
base.OnClientConnect(conn); | |
//conn.RegisterHandler(MsgKicked, KickedMessageHandler); | |
if (!NetworkServer.active) | |
{ | |
//only to do on pure client (not self hosting client) | |
backDelegate = StopClientClbk; | |
} | |
} | |
// NOTE: overrides NetworkLobbyManager.OnClientDisconnect which overrides NetworkManager.OnClientDisconnect | |
public override void OnClientDisconnect(NetworkConnection conn) | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnClientDisconnect{0}", "")); | |
base.OnClientDisconnect(conn); | |
} | |
// NOTE: overrides NetworkManager.OnClientError (empty virtual function) | |
public override void OnClientError(NetworkConnection conn, int errorCode) | |
{ | |
Debug.LogError (string.Format ("CustomNetworkManager.OnClientError{0}", "")); | |
} | |
// NOTE: overrides overrides NetworkManager.OnServerError (empty virtuial function) | |
public override void OnServerError(NetworkConnection conn, int errorCode) | |
{ | |
Debug.LogError (string.Format ("CustomNetworkManager.OnServerError: error code = {0}, network connection =\n{1}", errorCode, conn.PrettyPrint())); | |
} | |
// auto-match logic | |
IEnumerator AutoMatch() | |
{ | |
// loops until a match is found *and started* | |
while (matching) | |
{ | |
if(hosting) | |
{ | |
// Debug.Log(string.Format("CustomNetworkManager.AutoMatch: starting 'host' coroutine","")); | |
yield return StartCoroutine(_host); | |
Debug.Log(string.Format("CustomNetworkManager.AutoMatch: 'host' coroutine returned (matching = {0})", matching)); | |
} | |
else | |
{ | |
Debug.Log(string.Format("CustomNetworkManager.AutoMatch: starting 'search' coroutine","")); | |
yield return StartCoroutine(_search); | |
Debug.Log(string.Format("CustomNetworkManager.AutoMatch: 'search' coroutine returned","")); | |
} | |
} | |
yield break; | |
} | |
IEnumerator Host() | |
{ | |
if(matchMaker == null) | |
{ | |
// Debug.Log (string.Format ("CustomNetworkManager.Host: _customNetManager.matchMaker was null, calling _customNetManager.StartMatchMaker ()")); | |
StartMatchMaker (); | |
} | |
Assert.IsNotNull(base.matchMaker); | |
if(!matchCreated) | |
{ | |
var _randomString = Util.GetUniqueString(8); | |
//Debug.Log(string.Format("CustomNetworkManager.Host: creating match {0}", _randomString)); | |
// our custom match type | |
// CreateMatchRequest request = CustomMatchRequest(_randomString); | |
// matchMaker.CreateMatch(request, OnMatchCreate); | |
matchMaker.CreateMatch(_randomString, (uint)2, true, "", OnMatchCreate); | |
} | |
while(!matchCreated) | |
{ | |
status = string.Format("waiting for OnMatchCreate ({0})", Time.realtimeSinceStartup.ToString("#.##")); | |
yield return new WaitForEndOfFrame(); | |
} | |
while (!allPlayersReady) | |
{ | |
status = string.Format("waiting for allPlayersReady to be true ({0})", Time.realtimeSinceStartup.ToString("#.##")); | |
yield return new WaitForEndOfFrame(); | |
} | |
matching = false; // TEMP | |
yield break; | |
} | |
IEnumerator Search() | |
{ | |
while(true) | |
{ | |
// NOTE: OnMatchList is a delegate function that will be called once the | |
// network responds to our match list request | |
matchMaker.ListMatches (0, 10, "", OnMatchList); | |
// wait 1 second between match list requests | |
yield return new WaitForSeconds (3f); // HARDCODE | |
} | |
} | |
public override void OnMatchList(ListMatchResponse response) | |
{ | |
// if we have already switched into hosting mode, just exit out | |
if (hosting) | |
{ | |
Debug.LogWarning (string.Format ( | |
"CustomNetworkManager.OnMatchList: fresh match list response recieved. There were {0} open " + | |
"matches, but auto-matcher is in 'hosting mode' (aborting function)", response.matches.Count)); | |
return; | |
} | |
// NOTE: sets public 'matches' variable in base class | |
base.OnMatchList(response); | |
// iterate through the matches and join the first on available! | |
for (int i = 0; i < matches.Count; i++) | |
{ | |
// make sure that we don't join a match we started! NOTE: this could happen because even | |
// though we destroy _customNetManager.currentMatchId when we start searching, that message | |
// could get lost or take longer than the response request for a fresh list of open matches | |
var openMatch = matches [i]; | |
if (openMatch.networkId == (NetworkID)currentMatchNetworkId) | |
{ | |
Debug.LogWarning (string.Format ("OpenGamesUI.OnMatchList: open match {0}/{1} has same " + | |
"network id as _customNetManager.currentMatchId ({2}), will not try to join", | |
i, matches.Count, openMatch.networkId)); | |
} else { | |
JoinMatch ((ulong)openMatch.networkId); | |
} | |
} | |
} | |
void JoinMatch(ulong networkId) | |
{ | |
matching = false; | |
Debug.Log (string.Format("CustomNetworkManager.JoinMatch: networkID = {0} (_customNetManager.currentMatchId = {1})", networkId, currentMatchNetworkId)); | |
// matchMaker.JoinMatch(networkID, "", OnMatchJoined); | |
// _customNetManager.backDelegate = () => { | |
// Debug.Log(string.Format("backDelegate (c)", "")); | |
// _customNetManager.StopClient (); | |
// _customNetManager.StopMatchMaker(); | |
// _customNetManager = null; | |
// // TODO: navigate back to 'battle' panel | |
// // SceneManager. | |
// }; | |
// we are done auto-matching (unless something unexpected happen) so set _matching flag false | |
// ready = true; | |
} | |
//allow to handle the (+) button to add/remove player | |
public void OnPlayersNumberModified(int count) | |
{ | |
_playerNumber += count; | |
Debug.Log (string.Format ("CustomNetworkManager.OnPlayersNumberModified: {0} players", _playerNumber)); | |
int localPlayerCount = 0; | |
foreach (PlayerController p in ClientScene.localPlayers) { | |
localPlayerCount += (p == null || p.playerControllerId == -1) ? 0 : 1; | |
} | |
} | |
public void CheckReadyToBegin() | |
{ | |
string loadedSceneName = SceneManager.GetSceneAt(0).name; | |
if (loadedSceneName != base.offlineScene) | |
{ | |
Debug.LogWarning (string.Format ("CustomNetworkManager.CheckReadyToBegin: expected (offline) scene {0}, " + | |
"but SceneManager.GetSceneAt(0).name = {1}. Aborting function.", base.offlineScene, loadedSceneName)); | |
return; | |
} | |
int readyCount = 0; | |
foreach (var conn in NetworkServer.connections) | |
{ | |
if (conn == null) | |
continue; | |
readyCount += CheckConnectionIsReadyToBegin(conn); | |
} | |
if (readyCount < _maxPlayers) // HARDCODE: | |
{ | |
// not enough players ready yet. | |
Debug.Log (string.Format ("CustomNetworkManager.CheckReadyToBegin: not enough players to start ({0}), aborting", readyCount)); | |
return; | |
} | |
//_pendingPlayers.Clear(); | |
//OnLobbyServerPlayersReady(); | |
} | |
void OnServerReadyToBeginMessage(NetworkMessage netMsg) | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnServerReadyToBeginMessage: {0}", "")); | |
netMsg.ReadMessage(s_ReadyToBeginMessage); | |
PlayerController lobbyController; | |
// original line: | |
// if (!netMsg.conn.GetPlayerController((short)s_ReadyToBeginMessage.slotId, out lobbyController)) | |
// using HACK-y extention method (see end of this file) | |
if (!netMsg.conn.GetPlayerControllerExtension((short)s_ReadyToBeginMessage.slotId, out lobbyController)) | |
{ | |
Debug.LogWarning (string.Format ("CustomNetworkManager.OnServerReadyToBeginMessage: invalid playerControllerId {0}", s_ReadyToBeginMessage.slotId)); | |
return; | |
} | |
// set this player ready | |
var lobbyPlayer = lobbyController.gameObject.GetComponent<NetworkLobbyPlayer>(); | |
lobbyPlayer.readyToBegin = s_ReadyToBeginMessage.readyState; | |
// tell every player that this player is ready | |
var outMsg = new LobbyReadyToBeginMessage(); | |
outMsg.slotId = lobbyPlayer.slot; | |
outMsg.readyState = s_ReadyToBeginMessage.readyState; | |
NetworkServer.SendToReady(null, MsgType.LobbyReadyToBegin, outMsg); | |
// maybe start the game | |
CheckReadyToBegin(); | |
} | |
void OnServerSceneLoadedMessage(NetworkMessage netMsg) | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnServerSceneLoadedMessage:{0}", "")); | |
} | |
void OnServerReturnToLobbyMessage(NetworkMessage netMsg) | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.OnServerReturnToLobbyMessage:{0}", "")); | |
} | |
void CreateMatchRequest (string name) | |
{ | |
Debug.Log (string.Format ("CustomNetworkManager.CreateMatchRequest: {0}", name)); | |
CreateMatchRequest request = new CreateMatchRequest (); | |
request.name = name; | |
request.size = 2; // max number of clients that may join the match (QUESTION: how is this different from base.m_MaxConnections?) | |
request.advertise = true; | |
request.password = string.Empty; | |
request.matchAttributes = new Dictionary<string, long> () { | |
{ "a", 1 }, | |
{ "b", 2 } | |
}; | |
} | |
// copied form NetworkLobbyManager | |
static int CheckConnectionIsReadyToBegin(NetworkConnection conn) | |
{ | |
int countPlayers = 0; | |
foreach (var player in conn.playerControllers) | |
{ | |
if (player.IsValid) | |
{ | |
var lobbyPlayer = player.gameObject.GetComponent<NetworkLobbyPlayer>(); | |
if (lobbyPlayer.readyToBegin) | |
{ | |
countPlayers += 1; | |
} | |
} | |
} | |
return countPlayers; | |
} | |
// copied from NetworkLobbyManager | |
public void TryToAddPlayer() | |
{ | |
if (NetworkClient.active) | |
{ | |
short controllerId = -1; | |
var controllers = NetworkClient.allClients[0].connection.playerControllers; | |
if (controllers.Count < _maxPlayers) | |
{ | |
controllerId = (short)controllers.Count; | |
} | |
else | |
{ | |
for (short i = 0; i < _maxPlayers; i++) | |
{ | |
if (!controllers[i].IsValid) | |
{ | |
controllerId = i; | |
break; | |
} | |
} | |
} | |
Debug.Log(string.Format("CustomNetworkManager.TryToAddPlayer: controllerId = {0}, ClientScene.ready = {1}", controllerId, ClientScene.ready)); | |
if (controllerId == -1) | |
{ | |
Debug.Log(string.Format("CustomNetworkManager.TryToAddPlayer: no space! (controllerId == -1)","")); | |
return; | |
} | |
if (ClientScene.ready) | |
{ | |
ClientScene.AddPlayer(controllerId); | |
} | |
else | |
{ | |
ClientScene.AddPlayer(NetworkClient.allClients[0].connection, controllerId); | |
} | |
} | |
else | |
{ | |
Debug.Log(string.Format("CustomNetworkManager.TryToAddPlayer: NetworkClient not active!","")); | |
} | |
} | |
// update delegate stored in net manager for going 'back' (NOTE: from the user's perspective, | |
// hitting back transitions back to a main scene but for the network manager to shutdown | |
// gracefully we need different logic based on if the unit was host vs client | |
// _customNetManager.backDelegate = () => | |
// { | |
// Debug.Log(string.Format("backDelegate (a)", "")); | |
// _customNetManager.working = false; | |
// _customNetManager.StopClient (); | |
// _customNetManager.StopMatchMaker(); | |
// _customNetManager = null; | |
// }; | |
// | |
// _customNetManager.matchMaker.DestroyMatch ((NetworkID)_customNetManager.currentMatchNetworkId, OnDestroyMatch); | |
// _customNetManager.hosting = false; | |
// | |
// | |
// // update delegate stored in net manager for going 'back' (NOTE: from the user's perspective, | |
// // hitting back transitions back to a main scene but for the network manager to shutdown | |
// // gracefully we need different logic based on if the unit was host vs client | |
// _customNetManager.backDelegate = () => | |
// { | |
// Debug.Log(string.Format("backDelegate (a)", "")); | |
// _customNetManager.working = false; | |
// _customNetManager.StopClient (); | |
// _customNetManager.StopMatchMaker(); | |
// _customNetManager = null; | |
// }; | |
} | |
// some neccessary hacks, reimplementing seemingly missing functionality from the normal networking namespaces (LINK: https://goo.gl/8O50ea) | |
class LobbyReadyToBeginMessage : MessageBase | |
{ | |
public byte slotId; | |
public bool readyState; | |
public override void Deserialize(NetworkReader reader) | |
{ | |
slotId = reader.ReadByte(); | |
readyState = reader.ReadBoolean(); | |
} | |
public override void Serialize(NetworkWriter writer) | |
{ | |
writer.Write(slotId); | |
writer.Write(readyState); | |
} | |
} | |
// NOTE, WARNING: relys on reflection | |
public static class NetworkConnectionExtensionMethods { | |
public static bool GetPlayerControllerExtension(this NetworkConnection value, short playerControllerId, out PlayerController playerController){ | |
MethodInfo dynMethod = value.GetType().GetMethod("GetPlayerController", | |
BindingFlags.NonPublic | BindingFlags.Instance); | |
object[] parameters = new object[]{ playerControllerId, null }; | |
object result = dynMethod.Invoke(value, new object[] { parameters }); | |
bool blResult = (bool)result; | |
playerController = blResult ? (PlayerController)parameters[1] : null; | |
return blResult; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment