topical media & game development

talk show tell print

lib-unity-tutorial-network-m2h-Assets-Example2-multiplayerScript.js / js



  /* 
  *  This file is part of the Unity networking tutorial by M2H (http://www.M2H.nl)
  *  The original author of this code is Mike Hergaarden, even though some small parts 
  *  are copied from the Unity tutorials/manuals.
  *  Feel free to use this code for your own projects, drop us a line if you made something exciting! 
  */
  #pragma strict
  
  public var gameName = "Example2";
  public var serverPort =  35001;
  
  public var hostData : HostData[];
  
  private var natCapable : ConnectionTesterStatus = ConnectionTesterStatus.Undetermined;
  public var filterNATHosts = false;
  private var probingPublicIP = false;
  private var doneTestingNAT = false;
  private var timer : float = 0.0;
  
  private var hideTest = false;
  private var testMessage = "Undetermined NAT capabilities";
  
  private var tryingToConnectPlayNow : boolean = false;
  public var tryingToConnectPlayNowNumber : int = 0;
  
  private var remotePort : int[] = new int[3];
  private var remoteIP : String[] = new String[3];
  public var connectionInfo : String = "";
  
  public var lastMSConnectionAttemptForcedNat : boolean= false;
  private var NAToptionWasSwitchedForTesting : boolean = false;
  private var officialNATstatus : boolean = Network.useNat;
  public var errorMessage : String = "";
  private var lastPlayNowConnectionTime : float;
  
  public var nowConnecting : boolean = false;
  
  function Awake ()
  {
          sortedHostList = new Array ();
          
          // Start connection test
          natCapable = Network.TestConnection();
                  
          // What kind of IP does this machine have? TestConnection also indicates this in the
          // test results
          if (Network.HavePublicAddress()){
                  Debug.Log("This machine has a public IP address");
          }else{
                  Debug.Log("This machine has a private IP address");
          }        
          
          /* //If you dont want to use the Unity masterserver..
          Network.natFacilitatorIP = myMasterServerIP;
          Network.natFacilitatorPort = 11111;//Change this
          MasterServer.ipAddress = myMasterServerIP;
          MasterServer.port = 22222;//Change this
          Network.connectionTesterIP = myMasterServerIP;
          Network.connectionTesterPort = 33333;//Change this
  	*/
          
          
  }
  
  function Start(){//must be in start because of coroutine
                  
          yield WaitForSeconds(0.5);
          var tries : int=0;
          while(tries<=10){                
                  if(hostData && hostData.length>0){
                          //Waiting for hostData
                  }else{
                          FetchHostList(true);
                  }
                  yield WaitForSeconds(0.5);
                  tries++;
          }
  }
  
  function OnFailedToConnectToMasterServer(info: NetworkConnectionError)
  {
          //Yikes
  }
  
  function OnFailedToConnect(info: NetworkConnectionError)
  {
          Debug.Log("FailedToConnect info:"+info);
          FailedConnRetry(info);                
  }
  
  function Connect(ip : String, port : int, usenat : boolean){
          // Enable NAT functionality based on what the hosts if configured to do
          Network.useNat = usenat;
          lastMSConnectionAttemptForcedNat = usenat;
          
          Debug.Log("Connecting to "+ip+":"+port+" NAT:"+usenat);
          Network.Connect(ip, port);                
          nowConnecting=true;        
  }
  
  //This second definition of Connect can handle the ip string[] passed by the masterserver
  function Connect(ip : String[], port : int, usenat : boolean){
          // Enable NAT functionality based on what the hosts if configured to do
          Network.useNat = usenat;
          lastMSConnectionAttemptForcedNat = usenat;
          
          Debug.Log("Connecting to "+ip[0]+":"+port+" NAT:"+usenat);
          Network.Connect(ip, port);                
          nowConnecting=true;        
  }
  
  function StartHost(players : int, port : int){
          if(players<=1){
                  players=1;
          }
          //Network.InitializeSecurity();
          Network.InitializeServer(players, port);
  }
  
  function OnConnectedToServer(){
          //Stop communication until in the game
          Network.isMessageQueueRunning = false;
  
          //Save these details so we can use it in the next scene
          PlayerPrefs.SetString("connectIP", Network.connections[0].ipAddress);
          PlayerPrefs.SetInt("connectPort", Network.connections[0].port);
  }
  
  function FailedConnRetry(info: NetworkConnectionError){
          if(info == NetworkConnectionError.InvalidPassword){
                  mayRetry=false;
          }
          
          
          nowConnecting=false;
          
          //Quickplay
          if(tryingToConnectPlayNow){
                  //Try again without NAT if we used NAT
                  if(mayRetry && Network.useNat && lastMSConnectionAttemptForcedNat){
                          Debug.Log("Failed connect 1A: retry without NAT");
                  
                          remotePort[0]=serverPort;//Fall back to default server port
                          Connect(remoteIP, remotePort[0], false);
                          lastPlayNowConnectionTime=Time.time;
                  }else{
                          //We didn't use NAT and failed
                          Debug.Log("Failed connect 1B: Don't retry");
                  
                          //Reset NAT to org. value
                          Network.useNat=officialNATstatus;
                                                  
                          //Connect to next playnow/quickplay host
                          tryingToConnectPlayNowNumber++;
                          tryingToConnectPlayNow=false;
                  }
          }else{
                  //Direct connect or via host list manually
                  connectionInfo="Failed to connect!";
                  
                  if(mayRetry && Network.useNat && lastMSConnectionAttemptForcedNat){
                          //Since the last connect forced NAT usage,
                          // let's try again without NAT.                
                          Network.useNat=false;
                          Network.Connect(remoteIP, remotePort[0]);
                          nowConnecting=true;
                          lastPlayNowConnectionTime=Time.time;
                          
                  }else{
                          Debug.Log("Failed 2b");
                  
                          if(info == NetworkConnectionError.InvalidPassword){
                                  errorMessage="Failed to connect: Wrong password supplied";
                          }else if(info == NetworkConnectionError.TooManyConnectedPlayers){
                                  errorMessage="Failed to connect: Server is full";
                          }else{
                                  errorMessage="Failed to connect";
                          }
                          
                          //reset to default port
                          remotePort[0]=serverPort;
                          
                          //Reset nat to tested value
                          Network.useNat=officialNATstatus;
                          
                  }
          }        
  }
  
  public var CONNECT_TIMEOUT : float = 0.75;
  public var CONNECT_NAT_TIMEOUT : float = 5.00;
  
  //Our quickplay function: Go trough the gameslist and try to connect to all games
  function PlayNow(timeStarted : float ){
          
                  var i : int=0;
                  
                  for (var myElement in sortedHostList)
                  {
                          var element=hostData[myElement];
                  
                          // Do not try NAT enabled games if we cannot do NAT punchthrough
                          // Do not try connecting to password protected games
                          if ( !(filterNATHosts && element.useNat) && !element.passwordProtected  )
                          {
                                  aHost=1;
                                                                  
                                  if(element.connectedPlayers<element.playerLimit)
                                  {                                        
                                          if(tryingToConnectPlayNow){
                                                  var natText;
                                                  if(Network.useNat){
                                                          natText=" with option 1/2";
                                                  }else{
                                                          natText=" with option 2/2";
                                                  }
                                                  if((!Network.useNat && lastPlayNowConnectionTime+CONNECT_TIMEOUT<=Time.time) || (Network.useNat  && lastPlayNowConnectionTime+CONNECT_NAT_TIMEOUT<=Time.time)){
                                                          Debug.Log("Interrupted by timer, NAT:"+Network.useNat);
                                                          FailedConnRetry(NetworkConnectionError.ConnectionFailed);                                                        
                                                  }
                                                  return "Trying to connect to host "+(tryingToConnectPlayNowNumber+1)+"/"+sortedHostList.length+" "+natText;        
                                          }                
                                          if(!tryingToConnectPlayNow && tryingToConnectPlayNowNumber<=i){
                                                  Debug.Log("Trying to connect to game NR "+i+" & "+tryingToConnectPlayNowNumber);
                                                  tryingToConnectPlayNow=true;
                                                  tryingToConnectPlayNowNumber=i;
                                                  
                                                  // Enable NAT functionality based on what the hosts if configured to do
                                                  lastMSConnectionAttemptForcedNat=element.useNat;
                                                  Network.useNat = element.useNat;
                                                  var connectPort : int=element.port;
                                                  if (Network.useNat){
                                                          print("Using Nat punchthrough to connect");
                                                  }else{
                                                          //connectPort=serverPort; //bad idea!
                                                          print("Connecting directly to host");
                                                  }
                                                  Debug.Log("connecting to "+element.gameName+" "+element.ip+":"+connectPort);
                                                  Network.Connect(element.ip, connectPort);        
                                                  lastPlayNowConnectionTime=Time.time;
                                          }
                                          i++;                
                                  }
                          }                        
                  }
                  
                  //If we reach this point then either we've parsed the whole list OR the list is still empty
                  
                  //Dont give up yet: Give MS 7 seconds to feed the list
                  if(Time.time<timeStarted+7){
                          FetchHostList(true);        
                          return "Waiting for masterserver..."+Mathf.Ceil((timeStarted+7)-Time.time);        
                  }
                  
                  if(!tryingToConnectPlayNow){
                          return "failed";
                  }
                  
          
  }
  
  function Update() {
          // If network test is undetermined, keep running
          if (!doneTestingNAT) {
                  TestConnection();
          }
  }
  
  function TestConnection() {
          // Start/Poll the connection test, report the results in a label and react to the results accordingly
          natCapable = Network.TestConnection();
          switch (natCapable) {
                  case ConnectionTesterStatus.Error: 
                          testMessage = "Problem determining NAT capabilities";
                          doneTestingNAT = true;
                          break;
                          
                  case ConnectionTesterStatus.Undetermined: 
                          testMessage = "Undetermined NAT capabilities";
                          doneTestingNAT = false;
                          break;
                          
                  case ConnectionTesterStatus.PrivateIPNoNATPunchthrough: 
                          testMessage = "Cannot do NAT punchthrough, filtering NAT enabled hosts for client connections, local LAN games only.";
                          filterNATHosts = true;
                          Network.useNat = true;
                          doneTestingNAT = true;
                          break;
                          
                  case ConnectionTesterStatus.PrivateIPHasNATPunchThrough:
                          if (probingPublicIP)
                                  testMessage = "Non-connectable public IP address (port "+ serverPort +" blocked), NAT punchthrough can circumvent the firewall.";
                          else
                                  testMessage = "NAT punchthrough capable. Enabling NAT punchthrough functionality.";
                          // NAT functionality is enabled in case a server is started,
                          // clients should enable this based on if the host requires it
                          Network.useNat = true;
                          doneTestingNAT = true;
                          break;
                          
                  case ConnectionTesterStatus.PublicIPIsConnectable:
                          testMessage = "Directly connectable public IP address.";
                          Network.useNat = false;
                          doneTestingNAT = true;
                          break;
                          
                  // This case is a bit special as we now need to check if we can 
                  // cicrumvent the blocking by using NAT punchthrough
                  case ConnectionTesterStatus.PublicIPPortBlocked:
                          testMessage = "Non-connectble public IP address (port " + serverPort +" blocked), running a server is impossible.";
                          Network.useNat = false;
                          // If no NAT punchthrough test has been performed on this public IP, force a test
                          if (!probingPublicIP)
                          {
                                  Debug.Log("Testing if firewall can be circumnvented");
                                  natCapable = Network.TestConnectionNAT();
                                  probingPublicIP = true;
                                  timer = Time.time + 10;
                          }
                          // NAT punchthrough test was performed but we still get blocked
                          else if (Time.time > timer)
                          {
                                  probingPublicIP = false;                 // reset
                                  Network.useNat = true;
                                  doneTestingNAT = true;
                          }
                          break;
                  case ConnectionTesterStatus.PublicIPNoServerStarted:
                          testMessage = "Public IP address but server not initialized, it must be started to check server accessibility. Restart connection test when ready.";
                          break;
                  default: 
                          testMessage = "Error in test routine, got " + natCapable;
          }
          officialNATstatus=Network.useNat;
          if(doneTestingNAT){
                  Debug.Log("TestConn:"+testMessage);
                  Debug.Log("TestConn:"+natCapable + " " + probingPublicIP + " " + doneTestingNAT);
          }
  }
  
  private var lastHostListRequest : float = 0;
  
  //Request the host limit, but we limit these requests
  //Max once every two minutes automatically, max once every 5 seconds when manually requested
  function FetchHostList(manual : boolean){
          var timeout : int = 120;
          if(manual){
                  timeout=5;
          }
          
          if(lastHostListRequest==0 || Time.realtimeSinceStartup > lastHostListRequest + timeout){
                          lastHostListRequest = Time.realtimeSinceStartup;
                          MasterServer.RequestHostList (gameName);                        
                          yield WaitForSeconds(1);//We gotta wait :/                        
                          hostData = MasterServer.PollHostList();
                          yield WaitForSeconds(1);//We gotta wait :/        
                          CreateSortedArray();
                          Debug.Log("Requested new host list, got: "+hostData.length);
          }
          
          
                  
  }
  
  //The code below is all about sorting the game list by playeramount
  
  public var sortedHostList : Array;
  
  function CreateSortedArray(){
          
          sortedHostList = new Array();        
          
          var i : int=0;
          var data : HostData[] = hostData;
          for (var element in data)
          {
                  AddToArray(i);
                  i++;                
          }                        
  }
  
  function AddToArray(nr : int){
          sortedHostList.Add (nr);        
          SortLastItem();
  }
  
  function SortLastItem(){
          if(sortedHostList.length<=1){
                  return;
          }
          for(var i=sortedHostList.length-1;i>0;i--){
          var value1 : int= hostData[sortedHostList[i-1]].connectedPlayers;
          var value2 : int = hostData[sortedHostList[i]].connectedPlayers;
                  if(value1<value2){
                          SwapArrayItem((i-1), i);
                  }else{
                          //Sorted!
                          return;
                  }
          }
  }
  
  function SwapArrayItem(nr1, nr2){
          var tmp=sortedHostList[nr1];
          sortedHostList[nr1]=sortedHostList[nr2];
          sortedHostList[nr2]=tmp;
  }


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.