topical media & game development
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.