Skip to content

Instantly share code, notes, and snippets.

@arun02139
Created July 18, 2016 08:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arun02139/2bfd67e62c4c29d359b6434d1a5564c0 to your computer and use it in GitHub Desktop.
Save arun02139/2bfd67e62c4c29d359b6434d1a5564c0 to your computer and use it in GitHub Desktop.
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