diff --git a/Assembly-CSharp-firstpass-vs.csproj b/Assembly-CSharp-firstpass-vs.csproj new file mode 100644 index 0000000..4d1b446 --- /dev/null +++ b/Assembly-CSharp-firstpass-vs.csproj @@ -0,0 +1,68 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF} + Library + Properties + + Assembly-CSharp-firstpass + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_5_5;UNITY_3_5;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_WEBCAM;ENABLE_MICROPHONE;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW;ENABLE_SUBSTANCE + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + + + + + + + + + + + + diff --git a/Assembly-CSharp-firstpass.csproj b/Assembly-CSharp-firstpass.csproj new file mode 100644 index 0000000..4d1b446 --- /dev/null +++ b/Assembly-CSharp-firstpass.csproj @@ -0,0 +1,68 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF} + Library + Properties + + Assembly-CSharp-firstpass + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_5_5;UNITY_3_5;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_WEBCAM;ENABLE_MICROPHONE;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW;ENABLE_SUBSTANCE + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + + + + + + + + + + + + diff --git a/Assembly-CSharp-firstpass.pidb b/Assembly-CSharp-firstpass.pidb new file mode 100644 index 0000000..0c7b9d0 Binary files /dev/null and b/Assembly-CSharp-firstpass.pidb differ diff --git a/Assembly-CSharp-vs.csproj b/Assembly-CSharp-vs.csproj new file mode 100644 index 0000000..e58cdf8 --- /dev/null +++ b/Assembly-CSharp-vs.csproj @@ -0,0 +1,70 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {05EC98B6-FB67-CA9B-8A26-88E22667BD72} + Library + Properties + + Assembly-CSharp + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_5_5;UNITY_3_5;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_WEBCAM;ENABLE_MICROPHONE;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW;ENABLE_SUBSTANCE + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + + + + + + + + + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF} Assembly-CSharp-firstpass-vs + + + + + diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj new file mode 100644 index 0000000..56462f5 --- /dev/null +++ b/Assembly-CSharp.csproj @@ -0,0 +1,70 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {05EC98B6-FB67-CA9B-8A26-88E22667BD72} + Library + Properties + + Assembly-CSharp + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_5_5;UNITY_3_5;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_WEBCAM;ENABLE_MICROPHONE;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW;ENABLE_SUBSTANCE + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + + + + + + + + + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF} Assembly-CSharp-firstpass + + + + + diff --git a/Assembly-CSharp.pidb b/Assembly-CSharp.pidb new file mode 100644 index 0000000..98f63f9 Binary files /dev/null and b/Assembly-CSharp.pidb differ diff --git a/Assembly-UnityScript-firstpass-vs.unityproj b/Assembly-UnityScript-firstpass-vs.unityproj new file mode 100644 index 0000000..8ee3c96 --- /dev/null +++ b/Assembly-UnityScript-firstpass-vs.unityproj @@ -0,0 +1,59 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {C996ED3F-7676-775A-01B8-71BA430EB67B} + Library + Properties + + Assembly-UnityScript-firstpass + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_4_2;UNITY_3_4;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + + + \ No newline at end of file diff --git a/Assembly-UnityScript-firstpass.pidb b/Assembly-UnityScript-firstpass.pidb new file mode 100644 index 0000000..d0841d0 Binary files /dev/null and b/Assembly-UnityScript-firstpass.pidb differ diff --git a/Assembly-UnityScript-firstpass.unityproj b/Assembly-UnityScript-firstpass.unityproj new file mode 100644 index 0000000..8ee3c96 --- /dev/null +++ b/Assembly-UnityScript-firstpass.unityproj @@ -0,0 +1,59 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {C996ED3F-7676-775A-01B8-71BA430EB67B} + Library + Properties + + Assembly-UnityScript-firstpass + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_4_2;UNITY_3_4;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + + + \ No newline at end of file diff --git a/Assembly-UnityScript-vs.unityproj b/Assembly-UnityScript-vs.unityproj new file mode 100644 index 0000000..aaaf420 --- /dev/null +++ b/Assembly-UnityScript-vs.unityproj @@ -0,0 +1,62 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {B871E12C-38CB-763F-3AD7-72A506D3A1DA} + Library + Properties + + Assembly-UnityScript + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_5_0;UNITY_3_5;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_WEBCAM;ENABLE_MICROPHONE;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW;ENABLE_SUBSTANCE + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + {08AEB838-7F73-4A1F-D970-1DA6C584F352} Assembly-CSharp-firstpass-vs + + + + + diff --git a/Assembly-UnityScript.pidb b/Assembly-UnityScript.pidb new file mode 100644 index 0000000..74bd82a Binary files /dev/null and b/Assembly-UnityScript.pidb differ diff --git a/Assembly-UnityScript.unityproj b/Assembly-UnityScript.unityproj new file mode 100644 index 0000000..099f5e7 --- /dev/null +++ b/Assembly-UnityScript.unityproj @@ -0,0 +1,62 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {B871E12C-38CB-763F-3AD7-72A506D3A1DA} + Library + Properties + + Assembly-UnityScript + v3.5 + 512 + + + true + full + false + Temp\bin\Debug\ + DEBUG;TRACE;UNITY_3_5_0;UNITY_3_5;UNITY_EDITOR;ENABLE_PROFILER;UNITY_STANDALONE_WIN;ENABLE_GENERICS;ENABLE_DUCK_TYPING;ENABLE_TERRAIN;ENABLE_MOVIES;ENABLE_WEBCAM;ENABLE_MICROPHONE;ENABLE_NETWORK;ENABLE_CLOTH;ENABLE_WWW;ENABLE_SUBSTANCE + prompt + 4 + 0169 + + + pdbonly + true + Temp\bin\Release\ + TRACE + prompt + 4 + 0169 + + + + + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEngine.dll + + + C:/Program Files (x86)/Unity/Editor/Data/Managed/UnityEditor.dll + + + + + + + + {08AEB838-7F73-4A1F-D970-1DA6C584F352} Assembly-CSharp-firstpass + + + + + diff --git a/Assets/Resources/Prefabs/PlayerShip.prefab b/Assets/Resources/Prefabs/PlayerShip.prefab new file mode 100644 index 0000000..d3ca2ce Binary files /dev/null and b/Assets/Resources/Prefabs/PlayerShip.prefab differ diff --git a/Assets/Resources/Prefabs/PlayerShipBullet.prefab b/Assets/Resources/Prefabs/PlayerShipBullet.prefab new file mode 100644 index 0000000..1c852c3 Binary files /dev/null and b/Assets/Resources/Prefabs/PlayerShipBullet.prefab differ diff --git a/Assets/Scenes/ConnectionFailureScene.unity b/Assets/Scenes/ConnectionFailureScene.unity new file mode 100644 index 0000000..c29b097 Binary files /dev/null and b/Assets/Scenes/ConnectionFailureScene.unity differ diff --git a/Assets/Scenes/Game.unity b/Assets/Scenes/Game.unity new file mode 100644 index 0000000..549b9a1 Binary files /dev/null and b/Assets/Scenes/Game.unity differ diff --git a/Assets/Scenes/Startup.unity b/Assets/Scenes/Startup.unity new file mode 100644 index 0000000..d2f0335 Binary files /dev/null and b/Assets/Scenes/Startup.unity differ diff --git a/Assets/Scripts/DebuggingAid.cs b/Assets/Scripts/DebuggingAid.cs new file mode 100644 index 0000000..e51624d --- /dev/null +++ b/Assets/Scripts/DebuggingAid.cs @@ -0,0 +1,83 @@ +// This is a temporary script used to get players to join automatically in another +// hosted game if it's on the LAN. + +using UnityEngine; +using System.Collections; + +public class DebuggingAid : MonoBehaviour +{ + MasterServerDirector masterServerDirector; + bool isSearchingForLANGames; + string serverIP = ""; + string playerName = ""; + + // Use this for initialization + void Start () + { + // Get the player name + playerName = ConfigurationDirector.GetPlayerName(); + + // Create a persistent player object. This, and the InputDirector object that is + // created in the process, will exist for the rest of the application's lifetime. + // We don't actually need it here, so just discard the return value. + Player.Create(); + + // Grab the master server director so we can listen for LAN games + masterServerDirector = InputDirector.Get().gameObject.GetComponent(); + } + + // Update is called once per frame + void Update () + { + // Connect to the first LAN game we find automatically + if (isSearchingForLANGames && masterServerDirector.LANGameCount > 0) + { + string strIP = masterServerDirector.GetLANGameByIndex(0).ipAddress; + Debug.Log("We found a server at " + strIP); + InputDirector inputDirector = InputDirector.Get(); + isSearchingForLANGames = false; + masterServerDirector.EnableLANGameSearch(false); + ConfigurationDirector.SetPlayerName(playerName); + inputDirector.ConnectToServer(strIP, ConfigurationDirector.GetServerPort(), ""); + } + } + + void OnGUI() + { + if (GUI.Button(new Rect(10,10,300,50), "Host a game")) + { + // Host a game + ConfigurationDirector.SetPlayerName(playerName); + isSearchingForLANGames = false; + masterServerDirector.EnableLANGameSearch(false); + InputDirector inputDirector = InputDirector.Get(); + inputDirector.HostServer( + ConfigurationDirector.GetMaxPlayerCount(), + ConfigurationDirector.GetServerPort(), + false, // Not a dedicated server + "", // No password + InputDirector.HostServerType.LAN, // LAN game (Don't submit to Unity's game listings) + VersionDirector.GetGameTypeName(), + "My Game", + "Woot" + ); + } + + serverIP = GUI.TextField(new Rect(350, 300, 300, 50), serverIP); + if (GUI.Button(new Rect(10,300,300,50), "Connect to IP")) + { + // Connect to a game + ConfigurationDirector.SetPlayerName(playerName); + isSearchingForLANGames = false; + masterServerDirector.EnableLANGameSearch(false); + InputDirector inputDirector = InputDirector.Get(); + inputDirector.ConnectToServer(serverIP, ConfigurationDirector.GetServerPort(), ""); + } + + GUI.Label(new Rect(600,10,100,50), "Name"); + playerName = GUI.TextField(new Rect(700, 10, 300, 50), playerName); + + isSearchingForLANGames = GUI.Toggle(new Rect(10,150,200,50), isSearchingForLANGames, "Search for LAN games"); + masterServerDirector.EnableLANGameSearch(isSearchingForLANGames); + } +} diff --git a/Assets/Scripts/Directors/GameDirector.cs b/Assets/Scripts/Directors/GameDirector.cs new file mode 100644 index 0000000..9f26a59 --- /dev/null +++ b/Assets/Scripts/Directors/GameDirector.cs @@ -0,0 +1,146 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class is responsible for dealing with getting a player set up to begin playing a round of the game. +/// +public class GameDirector : MonoBehaviour +{ + /// + /// The one and only input director where all game commands go through + /// + private InputDirector inputDirector; + + /// + /// The one and only GameRules script that applies to the current game + /// + private GameRules gameRules; + + /// + /// Gets the game rules + /// + /// + /// The game rules component + /// + public GameRules Rules + { + get { return gameRules; } + } + + /// + /// Get the current instance of the game director. It does not persist through the application's lifetime. + /// + static public GameDirector Get() + { + GameDirector gameDirector = (GameDirector)GameObject.FindObjectOfType(typeof(GameDirector)); + return gameDirector; + } + + #region Unity Events + + // Use this for initialization + void Start () + { + // If we're a dedicated server, don't render anything + if (inputDirector.IsDedicatedServer()) { + Camera.main.enabled = false; + } + + // Now start the game if we're playing alone + if (!inputDirector.IsNetworking()) + { + BeginGame(ConfigurationDirector.GetGameRules()); + } + } + + #endregion + + #region Client and Input Director events + + /// + /// This message is sent by the Client component in the InputDirector object after the level has been + /// loaded and all communications with the network game channels have been restored. This is code that + /// should NOT be occurring in Start() because network communications would still be down at that time. + /// + void OnNetworkLoadedLevel() + { + // First off, get our input director. This is in a separate object. + inputDirector = InputDirector.Get(); + + // In a network game, the server set up its instance of the rules, and starts its game here. + // The client will wait from a buffered message from the server with the game rules enumeration. + // Then it will start its game. + if (inputDirector.IsHosting()) + { + Debug.Log("Game is hot."); + string gameRules = ConfigurationDirector.GetGameRules(); + + // Inform the server and the clients of the game rules. It's important to do it now before + // any other buffered messages (like spawning AI cycles) happens, or else unpredictable things + // can happen. + inputDirector.BroadcastBufferedCommand("OnDefineGameRules", gameRules); + } + } + + /// + /// This message is sent from the Player component on clients. The originating message is a buffered + /// RPC message from the server to all clients right after OnNetworkLoadedlevel for the purpose of + /// informing clients what kind of game is being played. Hosts do not get this message. + /// + /// + /// Game rules. + /// + void OnGameRulesDefined(string gameRules) + { + // Now begin the game on our instance + BeginGame(gameRules); + } + + #endregion + + /// + /// This is called directly from a solo game or one where you are hosting; it is also called on + /// clients after the server tells them what the game rules are. This function will add a component + /// to the game director which controls how the game is run. Without calling this, the playfield + /// would be empty and nothing would ever happen. + /// + /// + /// The name of the game rules set. + /// + private void BeginGame(string rulesName) + { + Debug.Log("in BeginGame with rules: " + gameRules); + + if ("FreeForAll" == rulesName) + { + gameRules = (GameRules)gameObject.AddComponent(); + } + else + { + // This should never happen + Debug.LogError("Unhandled game rule type: " + gameRules + "!"); + return; + } + + // Send a message to the rules component to begin the game. This is where the scores are + // reset, players are spawned, etc. This is the part where this player, whether they be the + // host or a client who just joined, creates themselves on the playfield. + SendMessage("OnBeginGame"); + } + + /// + /// This message is sent by the UI of this component to terminate a game in progress. + /// + void OnShutdown() + { + if (inputDirector.IsHosting()) { + inputDirector.UnhostServer(); + } else { + inputDirector.DisconnectFromServer(); + } + // Return to the main menu + Application.LoadLevel("Startup"); + ConsoleDirector.Log("Game session terminated"); + } + +} diff --git a/Assets/Scripts/GameRules/GameRules.cs b/Assets/Scripts/GameRules/GameRules.cs new file mode 100644 index 0000000..8717b4b --- /dev/null +++ b/Assets/Scripts/GameRules/GameRules.cs @@ -0,0 +1,11 @@ +using UnityEngine; +using System.Collections; + +/// +/// The Game Rules class describes the rules for a game; such as: how do players +/// get points, how are winners chosen, and anything else that is specific to a +/// type of game. +/// +public abstract class GameRules : MonoBehaviour +{ +} diff --git a/Assets/Scripts/GameRules/GameRules_FFA.cs b/Assets/Scripts/GameRules/GameRules_FFA.cs new file mode 100644 index 0000000..e1bac76 --- /dev/null +++ b/Assets/Scripts/GameRules/GameRules_FFA.cs @@ -0,0 +1,56 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +/// +/// This class is responsible for setting up a free-for-all game +/// +public class GameRules_FFA : GameRules +{ + /// + /// The one and only input director where all game commands go through + /// + private InputDirector inputDirector; + + /// + /// Our player in the game + /// + private Player selfPlayer; + + #region Game Director Events + + /// + /// This message is sent by the GameDirector component to begin a game. This is where + /// the players need to spawn, dynamic obstacles get set up, etc. + /// + void OnBeginGame() + { + // Standard initialization for all game rules scripts. + inputDirector = InputDirector.Get(); + selfPlayer = Player.Get(); + + // If we're playing offline, then we need to do single-player setup stuff here. + if (!inputDirector.IsNetworking()) + { + selfPlayer.gameObject.SendMessage("OnSpawnSpaceship", new Vector3(Random.value * 250 - 500, 0, Random.value * 250 - 500)); + } + // If we're hosting a network game, then we need to decide now where all + // the players are going to spawn and spawn them. + else if (inputDirector.IsHosting()) + { + // Spawn our own spaceship + if (!inputDirector.IsDedicatedServer()) + { + selfPlayer.gameObject.SendMessage("OnSpawnSpaceship", new Vector3(Random.value * 250 - 500, 0, Random.value * 250 - 500)); + } + } + else + { + // If we get here, we're a client. Spawn our first spaceship in this scene. + selfPlayer.gameObject.SendMessage("OnSpawnSpaceship", new Vector3(Random.value * 250 - 500, 0, Random.value * 250 - 500)); + } + } + + #endregion + +} diff --git a/Assets/Scripts/GameView.cs b/Assets/Scripts/GameView.cs new file mode 100644 index 0000000..7623f50 --- /dev/null +++ b/Assets/Scripts/GameView.cs @@ -0,0 +1,68 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class is responsible for rendering the game's overlay. We should minimize the number +/// of OnGUI calls per scene. +/// +public class GameView : MonoBehaviour +{ + public Texture2D plainTex; + + private Server server; + + #region Unity Events + + void Start() + { + server = Server.Get(); + } + + void OnGUI() + { + // TODO: Maintain a spaceship list that only changes in response to messages + Spaceship[] spaceships = (Spaceship[])Object.FindObjectsOfType(typeof(Spaceship)); + foreach (Spaceship s in spaceships) + { + if (s.Initialized) + { + PlayerAttributes player = server.GetPlayer(s.playerID); + Vector3 screenPos = Camera.main.WorldToScreenPoint(s.transform.position); + screenPos.y = Screen.height - screenPos.y; + + // Draw the HP box frame + int width = 50; + int height = 8; + Rect rBox = new Rect(screenPos.x - width/2, screenPos.y - 20 - height/2, width, height); + GUI.color = Color.white; + GUI.DrawTexture(rBox, plainTex); + + // Draw the name + GUI.Label(new Rect(rBox.xMin, rBox.yMin-22, 300,20), player.PlayerName); + + GUI.color = Color.black; + rBox.xMin++; rBox.yMin++; + rBox.xMax--; rBox.yMax--; + GUI.DrawTexture(rBox, plainTex); + + // Now draw the HP + GUI.color = Color.green; + rBox.xMin++; rBox.yMin++; + rBox.xMax--; rBox.yMax--; + float w = (float)rBox.width * (float)s.CurrentEnergy / (float)s.maxEnergy; + rBox.xMax = rBox.xMin + w; + GUI.DrawTexture(rBox, plainTex); + } + } + + int buttonWidth = 200; + int buttonHeight = 50; + GUI.color = Color.white; + if (GUI.Button(new Rect(20, Screen.height - buttonHeight - 20, buttonWidth, buttonHeight), "Quit")) + { + SendMessage("OnShutdown"); + } + } + + #endregion +} diff --git a/Assets/Scripts/Player.cs b/Assets/Scripts/Player.cs new file mode 100644 index 0000000..667fcbb --- /dev/null +++ b/Assets/Scripts/Player.cs @@ -0,0 +1,184 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class represents the user directly interacting with this application. It has all the attributes +/// of the Client class, but also has game-specific traits. Player game elements are created from this +/// class as well. +/// +public class Player : Client +{ + /// + /// The one and only player (this is you). This will exist throughout the application's lifetime. + /// + static private Player _player; + + /// + /// Create the one and only instance of you in this game. + /// + static public Player Create() + { + if (null == _player) + { + InputDirector inputDirector = InputDirector.Create(); + _player = inputDirector.gameObject.AddComponent(); + _player.inputDirector = inputDirector; + } + return _player; + } + + /// + /// Get the only and only instance of you as a player in the game. + /// + static public Player Get() + { + return _player; + } + + /// + /// The input director cached for convenience + /// + InputDirector inputDirector; + /// + /// The self spaceship. + /// + Spaceship selfSpaceship; + /// + /// The reported game rules + /// + string serverGameRules; + + /// + /// Gets a value indicating whether this instance is registered with server. + /// + /// + /// true if this instance is registered with server; otherwise, false. + /// + bool IsRegisteredWithServer { get { return (null != selfID); } } + + #region Unity Events + + // Update is called once per frame + void Update () + { + if (null != selfSpaceship) + { + Camera.main.transform.localEulerAngles = new Vector3(90,-selfSpaceship.transform.localEulerAngles.y,0); + + if (Input.GetKeyDown(KeyCode.W)) { + selfSpaceship.acceleration = selfSpaceship.MaxAcceleration; + } + if (Input.GetKeyUp(KeyCode.W)) { + selfSpaceship.acceleration = 0; + } + if (Input.GetKeyDown(KeyCode.S)) { + selfSpaceship.acceleration = -selfSpaceship.MaxAcceleration; + } + if (Input.GetKeyUp(KeyCode.S)) { + selfSpaceship.acceleration = 0; + } + if (Input.GetKeyDown(KeyCode.A)) { + selfSpaceship.torque = -selfSpaceship.MaxTorque; + } + if (Input.GetKeyUp(KeyCode.A)) { + selfSpaceship.torque = 0; + } + if (Input.GetKeyDown(KeyCode.D)) { + selfSpaceship.torque = selfSpaceship.MaxTorque; + } + if (Input.GetKeyUp(KeyCode.D)) { + selfSpaceship.torque = 0; + } + if (Input.GetKeyDown(KeyCode.Space)) { + selfSpaceship.Fire(); + } + } + } + + #endregion + + #region Input Director and Application Events + + /// + /// This is called to spawn a spaceship in the game at the specified position + /// + void OnSpawnSpaceship(Vector3 pos) + { + Debug.Log("in OnSpawnSpaceship at " + pos.ToString() + " - selfID = " + selfID); + // Create the cycle and assign its name and color + GameObject spaceshipPrefab = (GameObject)Resources.Load("Prefabs/PlayerShip"); + GameObject selfSpaceshipObject = inputDirector.InstantiateObject(spaceshipPrefab, pos, spaceshipPrefab.transform.rotation, System.Convert.ToInt32(selfID)); + selfSpaceship = selfSpaceshipObject.GetComponent(); + selfSpaceshipObject.networkView.RPC("OnSetSpaceshipAttributes", RPCMode.AllBuffered, + selfID, ConfigurationDirector.GetPlayerName() + "'s ship"); + + // Now attach our camera to it. + Camera.main.transform.parent = selfSpaceshipObject.transform; + Camera.main.transform.localPosition = new Vector3(0,150,0); + } + + /// + /// Called on a client when the client is disconnected form the server + /// + /// + /// Disconnection mode. + /// + void OnDisconnectionFromServer(NetworkDisconnection disconnectionMode) + { + // Load the scene that shows the player why they can't connect + ConnectionFailureSceneDirector.Initialize("Lost connection to server", "Startup"); + } + + #endregion + + #region RPCs + + /// + /// This message is sent from a server to either itself or a client after they've registered + /// with the server. This is not a buffered call, and it's a one-time call per game instance. + /// + /// + /// The new player ID + /// + [RPC] + public override void OnRegisteredWithServer(string newID) + { + Debug.Log("OnRegisteredWithServer called. New player ID is " + newID + ". Rules are " + serverGameRules); + + // Assign our new ID + selfID = newID; + + // Now notify all players that we have successfully registered so that they know we exist. + inputDirector.BroadcastBufferedCommand("OnPlayerRegistered", newID, ConfigurationDirector.GetPlayerName()); + + // If this is the host, then we must enter the game now. + if (inputDirector.IsHosting()) + { + inputDirector.LoadScene("Game"); + } + else + { + // Don't tell the game director until we're registered + if (IsRegisteredWithServer && null != serverGameRules) + { + GameDirector.Get().SendMessage("OnGameRulesDefined", serverGameRules); + } + } + } + + // This is a buffered message sent from the server to all clients to tell them what + // kind of game is being played. This must always be the first buffered RPC to be sent + // after a level has been loaded so that the players know the "rules" of the game. + [RPC] + void OnDefineGameRules(string gameRules) + { + serverGameRules = gameRules; + // Don't tell the game director until we're registered + if (IsRegisteredWithServer && null != serverGameRules) + { + GameDirector.Get().SendMessage("OnGameRulesDefined", serverGameRules); + } + } + + #endregion +} diff --git a/Assets/Scripts/Projectile.cs b/Assets/Scripts/Projectile.cs new file mode 100644 index 0000000..b47cc3f --- /dev/null +++ b/Assets/Scripts/Projectile.cs @@ -0,0 +1,59 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class represents a projectile fired from a ship. +/// +public class Projectile : MonoBehaviour { + + public float lifeTime = 2.0f; + public float energyCost = 10.0f; + public float damage = 10.0f; + + InputDirector inputDirector; + + /// + /// The time this projectile was fired + /// + float startTime; + + #region Unity Events + + // Use this for initialization + void Start () + { + // Cache the input director + inputDirector = InputDirector.Get(); + // Set the start time + startTime = Time.time; + } + + // Update is called once per frame + void Update () + { + // Only hosts can destroy projectiles + if (inputDirector.IsHosting()) + { + if (Time.time > startTime + lifeTime) { + inputDirector.DestroyObject(gameObject); + } + } + } + + void OnTriggerEnter(Collider other) + { + if (inputDirector.IsHosting() // We're hosting the game + && other.tag == "Spaceship" // The other object is a spaceship + && networkView.group != other.networkView.group // The other object did not come fromm us + ) + { + // Tell the spaceship what happened + Spaceship s = other.GetComponent(); + s.OnHitByProjectile(this); + // We always destroy ourselves on contact + inputDirector.DestroyObject(gameObject); + } + } + + #endregion +} diff --git a/Assets/Scripts/Spaceship.cs b/Assets/Scripts/Spaceship.cs new file mode 100644 index 0000000..f2eca64 --- /dev/null +++ b/Assets/Scripts/Spaceship.cs @@ -0,0 +1,166 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +/// +/// This class represents a spaceship on the playfield. Although there is only one per player, +/// this is not to be confused with the Player component itself. +/// +public class Spaceship : MonoBehaviour +{ + [HideInInspector] + public float acceleration = 0; + [HideInInspector] + public float torque = 0; + + InputDirector inputDirector; + Transform t; + Rigidbody rb; + + /// + /// This is assigned by OnSetSpaceshipAttributes. We only use it for debugging + /// in Unity; by looking at this value in the inspector, we can be sure of the + /// owning player for this ship. + /// + public string playerID; + + public float maxAcceleration = 4000; + public float maxTorque = 1500; + + public float maxVelocity = 100; + public float sqrMaxVelocity = 100 * 100; + + public float maxEnergy = 1000; + public float energyRestoreRate = 50; + + public GameObject bulletPrefab; + + public Transform shipTransform; + + public bool Initialized { get { return (null != playerID); } } + + public float MaxAcceleration { get { return maxAcceleration; } } + public float MaxTorque { get { return maxTorque; } } + + public float currentEnergy = 1000; // Public for debugging only + public float CurrentEnergy { get { return currentEnergy; } } + + + #region Unity Events + + /// + /// Ensure that currentEnergy stays in sync with the other players. This is one + /// abstraction that cannot be hidden with an underlying script. + /// + /// + /// Stream. + /// + /// + /// Info. + /// + void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info) + { + if (stream.isWriting) { + float energy = currentEnergy; + stream.Serialize(ref energy); + } else { + float energy = 0; + stream.Serialize(ref energy); + currentEnergy = energy; + } + } + + // Use this for initialization + void Start () + { + inputDirector = InputDirector.Get(); + t = gameObject.transform; // Cache the transform + rb = gameObject.rigidbody; // Cache the rigidbody + rb.angularDrag = 5.0f; + } + + void Update () + { + float newEnergy = currentEnergy + energyRestoreRate * Time.deltaTime; + if (newEnergy > maxEnergy) { + newEnergy = maxEnergy; + } + if (currentEnergy < newEnergy) + { + currentEnergy = newEnergy; + } + } + + void FixedUpdate () + { + // Linear movement + if (acceleration != 0) { + rb.AddForce(shipTransform.forward * rigidbody.mass * acceleration * Time.fixedDeltaTime); + } + + // No drag...but in case we ever change our minds... + //float idealDrag = maxAcceleration / terminalVelocity; + //rigidbody.drag = idealDrag / ( idealDrag * Time.fixedDeltaTime + 1 ); + + // Angular movement + if (torque != 0) { + rigidbody.AddTorque(Vector3.up * rigidbody.mass * torque * Time.fixedDeltaTime); + } + + // Cap velocity + Vector3 vel = rb.velocity; + if (vel.sqrMagnitude > sqrMaxVelocity) { + rb.velocity = vel.normalized * maxVelocity; + } + } + + #endregion + + #region Events + + /// + /// This message is sent by a Projectile object when it collides with this object + /// + /// + /// The projectile + /// + public void OnHitByProjectile(Projectile p) + { + // Reduce our energy by the projectile damage amount + currentEnergy = currentEnergy - p.damage; + if (currentEnergy < 0) { + Debug.Log("Boom!"); + // TODO: Forward this event to the rules component for further game-rules-level processing + } + // TODO: Forward this event to the rules component for further game-rules-level processing + } + + #endregion + + #region RPCs + + [RPC] + void OnSetSpaceshipAttributes(string owningPlayerID, string name) + { + Debug.Log("in OnSetSpaceshipAttributes: " + owningPlayerID + " " + name); + playerID = owningPlayerID; + gameObject.name = name; + } + + #endregion + + /// + /// This function is called by the Player class to fire a projectile. + /// + public void Fire() + { + Projectile p = bulletPrefab.GetComponent(); + if (currentEnergy > p.energyCost) + { + GameObject bullet = (GameObject)inputDirector.InstantiateObject(bulletPrefab, t.position + shipTransform.forward * 3.0f, bulletPrefab.transform.rotation, networkView.group); + bullet.rigidbody.AddForce(shipTransform.forward * bullet.rigidbody.mass * 4000.0f); + currentEnergy -= p.energyCost; + } + } + +} diff --git a/Assets/Standard Assets/Scripts/Client.cs b/Assets/Standard Assets/Scripts/Client.cs new file mode 100644 index 0000000..72a193c --- /dev/null +++ b/Assets/Standard Assets/Scripts/Client.cs @@ -0,0 +1,175 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +/// +/// This class represents the user's existence in the application. All non-application-specific player management +/// happens here. This component must be a subclass of the actual application-specific user component. +/// +public abstract class Client : MonoBehaviour +{ + /// + /// The input director that we use to communicate with the world + /// + protected InputDirector inputDirector; + + /// + /// The server. Even if we are hosting, this object maintains the player list and + /// is responsible for server "registration" management; that is, players formally + /// requesting to join a game once connected. + /// + protected Server server; + + /// + /// Our own unique player ID + /// + protected string selfID; + + #region Properties + + /// + /// Returns your unique ID + /// + public string ID { get { return selfID; } } + + #endregion + + #region Abstracts + + /// + /// This message is sent from a server to either itself or a client after they've registered + /// with the server. This is not a buffered call, and it's a one-time call per game instance. + /// + /// + /// The new player ID + /// + [RPC] + public abstract void OnRegisteredWithServer(string newID); + + #endregion + + #region Unity Events + + // Use this for initialization + void Awake () + { + inputDirector = InputDirector.Get(); + server = Server.Get(); + } + + void OnLevelWasLoaded(int level) + { + // Allow receiving data again + Network.isMessageQueueRunning = true; + // Now the level has been loaded and we can start sending out data to clients + Network.SetSendingEnabled(0, true); + // Notify all objects that the level was loaded + foreach (GameObject o in FindObjectsOfType(typeof(GameObject))) + o.SendMessage("OnNetworkLoadedLevel", SendMessageOptions.DontRequireReceiver); + } + + #endregion + + #region Input Director and Application Events + + /// + /// This message is sent by the Input Director component if this application + /// has just put up a network host. + /// + /// + /// Error. + /// + void OnHostServerComplete(NetworkConnectionError error) + { + // Now handle player stuff + if (NetworkConnectionError.NoError == error) + { + // Reset our own ID + selfID = null; + // Register with ourselves + server.Register(); + // We know we're good to go, so lets just call this ourselves. + OnRegisteredWithServer(networkView.owner.ToString()); + } + } + + /// + /// This message is sent by the Input Director component if this application + /// has just connected to a server + /// + /// + /// Error. + /// + void OnConnectToServerComplete(NetworkConnectionError error) + { + // Now handle player stuff + if (NetworkConnectionError.NoError == error) + { + // Reset our own ID + selfID = null; + // Register with the server. This will send out an RPC call. + server.Register(); + } + } + + /// + /// Called on a client when the client is disconnected form the server + /// + /// + /// Disconnection mode. + /// + void OnDisconnectionFromServer(NetworkDisconnection disconnectionMode) + { + selfID = null; + } + + #endregion + + #region RPCs + + /// + /// This message is sent from a server to a client if they were rejected by the server + /// + /// + /// Reason. + /// + [RPC] + void OnServerRegistrationFailed(string reason) + { + // Disconnect + inputDirector.DisconnectFromServer(); + // Now load the scene that shows the player why they can't connect + ConnectionFailureSceneDirector.Initialize("Registration failed: " + reason, "Startup"); + } + + /// + /// This message is sent from a server to a client to load a level + /// + /// + /// Level. + /// + /// + /// Level prefix. + /// + [RPC] + void OnLoadNetworkLevel(string level, int levelPrefix) + { + // http://unity3d.com/support/documentation/Components/net-NetworkLevelLoad.html + Debug.Log("In OnLoadNetworkLevel"); + + // There is no reason to send any more data over the network on the default channel, + // because we are about to load the level, thus all those objects will get deleted anyway + Network.SetSendingEnabled(0, false); + + // We need to stop receiving because first the level must be loaded first. + // Once the level is loaded, rpc's and other state update attached to objects in the level are allowed to fire + Network.isMessageQueueRunning = false; + + // All network views loaded from a level will get a prefix into their NetworkViewID. + // This will prevent old updates from clients leaking into a newly created scene. + Network.SetLevelPrefix(levelPrefix); + Application.LoadLevel(level); + } + + #endregion +} diff --git a/Assets/Standard Assets/Scripts/ConfigurationDirector.cs b/Assets/Standard Assets/Scripts/ConfigurationDirector.cs new file mode 100644 index 0000000..2a6db3f --- /dev/null +++ b/Assets/Standard Assets/Scripts/ConfigurationDirector.cs @@ -0,0 +1,82 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class manages all configurable settings for a game. Everything from +/// the player profile to game hosting settings are handled here. All the current +/// values are hard coded for demonstration purposes; you would replace these with +/// PlayerPrefs function calls in the long term. +/// +static public class ConfigurationDirector +{ + /// + /// Gets the max player count. + /// + /// + /// The player count. + /// + static public int GetMaxPlayerCount() + { + return 32; + } + + /// + /// Gets the server port. + /// + /// + /// The server port. + /// + static public int GetServerPort() + { + // Host on this port + return 21182; + } + + /// + /// Gets the name of the player. + /// + /// + /// The player name. + /// + static public string GetPlayerName() + { + return PlayerPrefs.GetString("PlayerName", "Player"); + } + + /// + /// Sets the name of the player. + /// + /// + /// Value. + /// + static public void SetPlayerName(string value) + { + PlayerPrefs.SetString("PlayerName", value); + } + + /// + /// Gets the game rules. This is expressed as a simple named string, though you could + /// opt to make it an enumeration. + /// + /// + /// The game rules. + /// + static public string GetGameRules() + { + // The name "FreeForAll" has no special significance to this engine; it's simply + // the name chosen for this specific video game. + return "FreeForAll"; + } + + /// + /// Gets the multicast port for LAN game searching. + /// + /// + /// The multicast port. + /// + static public int GetMulticastPort() + { + // Search for games on this port using UDP protocol + return 22043; + } +} diff --git a/Assets/Standard Assets/Scripts/ConnectionFailureSceneDirector.cs b/Assets/Standard Assets/Scripts/ConnectionFailureSceneDirector.cs new file mode 100644 index 0000000..b6b6d2c --- /dev/null +++ b/Assets/Standard Assets/Scripts/ConnectionFailureSceneDirector.cs @@ -0,0 +1,52 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class manages what happens in the "ConnectionFailureScene" scene. +/// +public class ConnectionFailureSceneDirector : MonoBehaviour +{ + /// + /// The reason for the connection failure + /// + private string reason; + + /// + /// This is called from anywhere in the program when a connection to the host + /// has failed. + /// + /// + /// The reason for the failure. + /// + /// + /// The scene to go to after the user clicks the Back button. + /// + static public void Initialize(string reason, string subsequentScene) + { + PlayerPrefs.SetString("ConnectionFailureSceneReason", reason); + PlayerPrefs.SetString("ConnectionFailureSubsequentScene", subsequentScene); + Application.LoadLevel("ConnectionFailureScene"); + } + + // Use this for initialization + void Start () + { + // Cache the reason for the disconnection + reason = PlayerPrefs.GetString("ConnectionFailureSceneReason"); + } + + void OnGUI() + { + // Show the reason for the connection failure + GUI.Label(new Rect(0,0,Screen.width,Screen.height / 2), reason); + + // Back button takes the player back to the main menu + float sx = Screen.width; + float sy = Screen.height; + if (GUI.Button(new Rect(sx * 0.425f, sy * 0.55f, sx * 0.15f, sy * 0.1f), "Back")) + { + // Go go to the next scene + Application.LoadLevel(PlayerPrefs.GetString("ConnectionFailureSubsequentScene")); + } + } +} diff --git a/Assets/Standard Assets/Scripts/ConsoleDirector.cs b/Assets/Standard Assets/Scripts/ConsoleDirector.cs new file mode 100644 index 0000000..e48eb16 --- /dev/null +++ b/Assets/Standard Assets/Scripts/ConsoleDirector.cs @@ -0,0 +1,15 @@ +using UnityEngine; +using System.Collections; + +/// +/// This is the console director. Right now it's only good for logging messages. +/// Eventually you could use this to log messages in a global console. Consider +/// this class a very "under construction" class. +/// +static public class ConsoleDirector +{ + static public void Log (string value) + { + Debug.Log(value); + } +} diff --git a/Assets/Standard Assets/Scripts/InputDirector.cs b/Assets/Standard Assets/Scripts/InputDirector.cs new file mode 100644 index 0000000..ded6a11 --- /dev/null +++ b/Assets/Standard Assets/Scripts/InputDirector.cs @@ -0,0 +1,807 @@ +using UnityEngine; +using System.Collections; + +/// +/// The input director is an object that persists throughout the lifetime of this Unity application. +/// Its purpose is to hide the abstraction of the application being a local or a network game from +/// areas of the application which should not care: +/// +/// Benefits: +/// +/// - When creating or destroying objects, the application does not need to care about the particulars +/// of ensuring the action is propogated throughout the network. +/// +/// - When sending messages to objects, the application does not need to care about the particulars +/// of ensuring the action is propogated throughout the network. +/// +/// - MonoBehavior events like players connecting and disconnecting are all managed by this one object, +/// and can be rebroadcast to other scripts in the same game object that the InputDirector is attached to. +/// +/// +[RequireComponent (typeof (NetworkView))] +[RequireComponent (typeof (MasterServerDirector))] +[RequireComponent (typeof (Server))] +public class InputDirector : MonoBehaviour +{ + /// + /// This describes the state of the input director and the game. + /// + public enum InputTransportMode + { + /// + /// Nothing is going on. This is true when we're in the main menu. + /// + Idle, + + /// + /// A local instance of the game is in progress; no network activity + /// will be taking place. + /// + Local, + + /// + /// This instance of the game is acting as the host + /// + Server, + + /// + /// This instance of the game is connecting to a host + /// + Connecting, + + /// + /// This instance of the game is connected to a network host + /// + Client + + } + + /// + /// This describes the type of server being hosted + /// + public enum HostServerType + { + /// + /// The server is not being advertised to outside players who are looking for games. + /// + Private, + + /// + /// The server is being advertised, but only on the LAN. This should be used for + /// LAN parties or when all the players are on the same network. + /// + LAN, + + /// + /// This is a public game open to everyone on the Internet. + /// + Public + }; + + /// + /// The master server director which is responsible for acquiring online game listings + /// + private MasterServerDirector masterServerDirector; + /// + /// The current state of the input director + /// + private InputTransportMode mode = InputTransportMode.Idle; + /// + /// The prefix for the next level to load (prevents network messages from one + /// level trickling into another) + /// + private int nextLevelPrefix = 0; + + /// + /// True if our input transport mode is Server and we're not actually engaging in the game + /// (but we still need to run it) + /// + private bool isDedicatedServer; + /// + /// The state of how we interact with the master game-search server + /// + private HostServerType gameHostType; + + /// + /// The type name of the game; usually the title followed by version (e.g. Vengeance2.3) + /// + private string gameTypeName; + /// + /// The name of the game as it appears in game searches + /// + private string gameName; + /// + /// A description of the game as it appears in game searches + /// + private string gameComment; + + /// + /// The one and only input director. This will exist throughout the application's lifetime. + /// + static private InputDirector _inputDirector; + + /// + /// Returns the input director in the scene. If it does not exist, it is created. + /// The input director object persists through all scenes. + /// + static public InputDirector Create() + { + if (null == _inputDirector) { + GameObject o = new GameObject(); + o.name = "InputDirector"; + // Ensure this object is never destroyed + DontDestroyOnLoad(o); + // Cache the director + _inputDirector = o.AddComponent(); + } + return _inputDirector; + } + + /// + /// Returns the input director in the scene. If it does not exist, it is created. + /// The input director object persists through all scenes. + /// + static public InputDirector Get() + { + return _inputDirector; + } + + #region Properties + + /// + /// Determines whether this instance is hosting a game. + /// + /// + /// true if this instance is hosting a game; otherwise, false. + /// + public bool IsHosting() + { + bool result = false; + // If we are in a local one-player game (not on the internet), or are the server of an + // internet game, then we consider ourselves the host. + if (InputTransportMode.Local == mode || InputTransportMode.Server == mode) + { + result = true; + } + return result; + } + + /// + /// Determines whether this instance is network-enabled. + /// + /// + /// true if this instance is network-enabled; otherwise, false. + /// + public bool IsNetworking() + { + bool result = true; + // Simple test. We're always networking unless we're a local one-player game. + if (InputTransportMode.Local == mode) + { + result = false; + } + return result; + } + + /// + /// Determines whether the specified object belongs to our instance of the game. + /// + /// + /// true if this game object belongs to us for controlling. This only applies to objects with networkView components being used. + /// + /// + /// If set to true o. + /// + public bool IsOurs(GameObject o) + { + if (IsNetworking()) { + return o.networkView.isMine; + } else { + // If we're not networked, we must be hosting a one-player game...so the object o is always ours. + return true; + } + } + + /// + /// Determines whether this instance is running a dedicated server. + /// + /// + /// true if this instance is a dedicated server; otherwise, false. + /// + public bool IsDedicatedServer() + { + if (InputTransportMode.Server == mode) { + return isDedicatedServer; + } else { + // If we're not a server, we're not dedicated, period. + return false; + } + } + + /// + /// Gets the input transport mode. Refer to the enumeration for possible values. + /// + /// + /// The mode. + /// + public InputTransportMode GetMode() + { + return mode; + } + + // This function changes the mode of the input director + public void SetMode(InputTransportMode value) + { + // Unhost if we're currently hosting but the input mode is changing + if (InputTransportMode.Server == mode && value != mode) { + UnhostServer(); + } + + // TODO: Check the existing mode and throw exceptions if not supported. + // For example, going from Idle to Client without Connecting first shouldn't + // be possible. + SetModeInternal(value); + } + + /// + /// Gets the IP address from a given client ID + /// + /// + /// The IP address. + /// + /// + /// I. + /// + public string GetIPAddress(string ID) + { + foreach (NetworkPlayer p in Network.connections) { + if (p.ToString() == ID) { + return p.ipAddress; + } + } + return ""; + } + + #endregion + + #region Internal methods + + private void SetModeInternal(InputTransportMode value) + { + mode = value; + Debug.Log("InputDirector mode is now " + value.ToString()); + } + + #endregion + + #region Unity Events + + public void Start() + { + // First, add the ability for this object to communicate with a master game server + // to find other players online. + masterServerDirector = GetComponent(); + // Now turn off state synchronization because it does not apply to this object. + // See http://docs.unity3d.com/Documentation/Components/net-StateSynchronization.html for what + // it should apply to. + networkView.stateSynchronization = NetworkStateSynchronization.Off; + } + + /// + /// This is called by Unity when the network server has initialized. + /// + void OnServerInitialized() + { + Debug.Log("Server initialized for host type " + gameHostType.ToString() + "." ); + // Update our mode + SetModeInternal(InputTransportMode.Server); + // Register the game on Unity's master server + if (HostServerType.Private != gameHostType) { + masterServerDirector.RegisterHost(gameTypeName, gameName, gameComment, isDedicatedServer, (HostServerType.Public == gameHostType) ? true : false); + } + // Let all components know the server is initialized + gameObject.SendMessage("OnHostServerComplete", NetworkConnectionError.NoError, SendMessageOptions.DontRequireReceiver); + } + + /// + /// This is called by Unity when a player connects to a server. Only servers get this message. + /// + /// + /// The player who connected. + /// + void OnPlayerConnected(NetworkPlayer player) + { + // player.ToString() is the unique ID of the player. When printing NetworkPlayer.ToString() + // you will see a number, but we should not assume it will always be a number. + Debug.Log("Player " + player.ToString() + " connected from " + player.ipAddress + ":" + player.port + "."); + // Let all components know a player connected + gameObject.SendMessage("OnPlayerConnectedToServer", player, SendMessageOptions.DontRequireReceiver); + // TODO: Anything else? + } + + /// + /// This is called by Unity when a player disconnects from a server. Only servers get this message. + /// + /// + /// The player who disconnected. + /// + void OnPlayerDisconnected(NetworkPlayer player) + { + // player.ToString() is the unique ID of the player. When printing NetworkPlayer.ToString() + // you will see a number, but we should not assume it will always be a number. + Debug.Log("Player " + player.ToString() + " disconnected."); + + // Let all components know a player disconnected. + gameObject.SendMessage("OnPlayerDisconnectedFromServer", player, SendMessageOptions.DontRequireReceiver); + + // Delete all player objects and RPC buffer entries. This will also destroy them for all other players. + Network.RemoveRPCs(player); + Network.DestroyPlayerObjects(player); + + // TODO: Anything else? + } + + /// + /// This is called by Unity when we, a client, failed to connect to a remote server. + /// + /// + /// The error. + /// + void OnFailedToConnect(NetworkConnectionError error) + { + Debug.Log("Failed to connect to server!"); + SetModeInternal(InputTransportMode.Idle); + // Shut down our connection + DisconnectFromServer(); + // Let all components know what happened + SendMessage("OnConnectToServerComplete", error, SendMessageOptions.DontRequireReceiver); + } + + /// + /// This is called by Unity when we, a client, connected to the remote server. Only clients get this message. + /// + void OnConnectedToServer() + { + // Update our mode + SetModeInternal(InputTransportMode.Client); + // Let all components know what happened. We don't need to do anything here. + SendMessage("OnConnectToServerComplete", NetworkConnectionError.NoError, SendMessageOptions.DontRequireReceiver); + } + + /// + /// This is called by Unity when we, a client, were disconnected from the remote server. + /// It can also be called when the remote server is shut down. Only clients get this message. + /// + /// + /// Disconnection mode. + /// + void OnDisconnectedFromServer(NetworkDisconnection disconnectionMode) + { + Debug.Log("Disconnected from the server."); + SetModeInternal(InputTransportMode.Idle); + // TODO: Better handling per + // http://unity3d.com/support/documentation/ScriptReference/Network.OnDisconnectedFromServer.html + // Let all components know what happened + SendMessage("OnDisconnectionFromServer", disconnectionMode, SendMessageOptions.DontRequireReceiver); + } + + #endregion + + #region Connection methods + + /// + /// This function will host a server. The caller specifies the max number of players + /// and the listening port. This will notify the notification object when the game is + /// hosted. + /// + /// + /// The maximum number of connections allowed. This is typically the maximum player count. + /// + /// + /// Port we listen for connections on + /// + /// + /// True if this is a dedicated server. + /// + /// + /// Server password. + /// + /// + /// Host scope (see the enumeration for possible values) + /// + /// + /// The name of the type of game (typically the game's title followed by version number) + /// + /// + /// The name of the hosted game (e.g. "Ted's game") + /// + /// + /// A description of the game + /// + public void HostServer(int connections, int listenPort, bool dedicatedServer, + string password, HostServerType hostType, string typeName, string name, string comment) + { + if (InputTransportMode.Idle == mode) + { + bool useNat = !Network.HavePublicAddress(); + isDedicatedServer = dedicatedServer; + gameHostType = hostType; + gameTypeName = typeName; + gameName = name; + gameComment = comment; + // Although we're hosting, set our status to Connecting because doing anything else + // right now would be invalid. + SetModeInternal(InputTransportMode.Connecting); + Network.InitializeSecurity(); + Network.incomingPassword = password; + NetworkConnectionError result = Network.InitializeServer(connections, listenPort, useNat); + if (NetworkConnectionError.NoError == result) + { + // This might not mean the server is initialized; it could mean it's initializing. We want + // to wait for a OnServerInitialized message. + Debug.Log("Network.InitializeServer was successful"); + } + else { + // Unity doesn't send messages for failing to initialize the server, so we'll send one. + SendMessage("OnHostServerComplete", result); + // TODO: Throw an exception? + } + } + else { + // TODO: Throw an exception + } + } + + /// + /// This function will unhost a server + /// + public void UnhostServer() + { + if (InputTransportMode.Server == mode) { + // Remove from the Unity master server game list + masterServerDirector.UnregisterHost(); + // Now disconnect + Network.Disconnect(); + // Update our mode + SetModeInternal(InputTransportMode.Idle); + } + else { + // TODO: Throw an exception + } + } + + /// + /// This function will have the game instance attempt to connect to a server + /// + /// + /// The server's network IP address + /// + /// + /// The port of the server listening for connections + /// + /// + /// Server password. + /// + public void ConnectToServer(string IP, int remotePort, string password) + { + // Only do this if we're idle. If we were connected to a server, we should have disconnected by now. + if (InputTransportMode.Idle == mode) + { + SetModeInternal(InputTransportMode.Connecting); + NetworkConnectionError result = Network.Connect(IP, remotePort, password); + if (NetworkConnectionError.NoError == result) + { + // This might not mean the connection is established; it could mean it's initializing. We want + // to wait for a OnConnectedToServer message. + Debug.Log("Network.Connect was successful."); + } + else { + // TODO: Throw an exception? + } + } + else { + // TODO: Throw an exception? + } + } + + /// + /// This function will disconnect us from the server if we're still connected + /// + public void DisconnectFromServer() + { + if (InputTransportMode.Connecting == mode || InputTransportMode.Client == mode) + { + Network.Disconnect(); + // Update our mode + SetModeInternal(InputTransportMode.Idle); + } + } + + /// + /// This function is called by the server to disconnect a client + /// + /// + /// The client's ID. + /// + public void DisconnectClientFromServer(string ID) + { + for (var i=0; i < Network.connections.Length; i++) { + if (Network.connections[i].ToString() == ID) { + Network.CloseConnection(Network.connections[i],true); + return; + } + } + } + + #endregion + + #region Server and Client Methods + + /// + /// This function will create a new object in the game based on a prefab + /// + /// + /// The created object. + /// + /// + /// The prefab from which to create the object. + /// + /// + /// The position for the new object. + /// + /// + /// The rotation for the new object. + /// + /// + /// The network group to assign the object to. See the Unity documentation for more info. If you're + /// not sure what to put here or don't plan on grouping network objects, just leave this value 0. + /// + public GameObject InstantiateObject(GameObject prefab, Vector3 position, Quaternion rotation, int group) + { + GameObject result = null; + switch (mode) { + case InputTransportMode.Local: + // Regular instantiation + result = (GameObject)GameObject.Instantiate(prefab, position, rotation); + break; + case InputTransportMode.Server: + case InputTransportMode.Client: + // Network instantiation. This object will appear in everyone's world. + result = (GameObject)Network.Instantiate(prefab, position, rotation, group); + break; + default: + // Not supported + // TODO: Throw an exception + break; + } + return result; + } + + /// + /// This function will destroy a game object, regardless of whether this application owns the object. + /// + /// + /// Doomed object. + /// + public void DestroyObject(GameObject doomedObject) + { + switch (mode) { + case InputTransportMode.Local: + // Regular destruction + Destroy(doomedObject); + break; + case InputTransportMode.Server: + case InputTransportMode.Client: + // Network destruction + // TODO: Figure out why When when destroying client objects do we still get messages + // like "View ID AllocatedID: 50 not found during lookup. Strange behaviour may occur"? + // Probably because I pulled the rug from right under the client's feet and they're trying + // to send state information. I tried sending an RPC first, but Destroy seems to have a higher + // priority and completes before my RPC gets out. Need to confirm the right way to destroy + // objects that were instantiated by other players. + Network.Destroy(doomedObject); + break; + default: + // Not supported + // TODO: Throw an exception + break; + } + } + + /// + /// This function will send a buffered command to an object. In a local game, a message + /// will be sent. In a network game, an RPC call will be made. In a network game, + /// the object should have been created with InputDirector.InstantiateObject() + /// + /// + /// The receiving object. + /// + /// + /// The command string. + /// + /// + /// Additional arguments. + /// + public void SendBufferedCommand(GameObject receiver, string command, params object[] args) + { + switch (mode) { + case InputTransportMode.Local: + // Process the command locally. The receiver is required to handle the message. + if (args.Length == 1) { + receiver.SendMessage(command, args); + } else { + Debug.LogError("SendBufferedCommand called in a local session with multiple arguments!"); + } + break; + case InputTransportMode.Server: + case InputTransportMode.Client: + // Use an RPC command. This requires that the receiver has a network view. + receiver.networkView.RPC(command, RPCMode.AllBuffered, args); + break; + default: + // Not supported + // TODO: Throw an exception + break; + } + } + + /// + /// This function will send a non-buffered command to an object. In a local game, a message + /// will be sent. In a network game, an RPC call will be made. In a network game, + /// the object should have been created with InputDirector.InstantiateObject() + /// + /// + /// The receiving object. + /// + /// + /// The command string. + /// + /// + /// Additional arguments. + /// + public void SendCommand(GameObject receiver, string command, params object[] args) + { + switch (mode) { + case InputTransportMode.Local: + if (args.Length == 1) { + receiver.SendMessage(command, args[0], SendMessageOptions.DontRequireReceiver); + } else { + Debug.LogError("SendCommand called in a local session with multiple arguments!"); + } + break; + case InputTransportMode.Server: + case InputTransportMode.Client: + receiver.networkView.RPC(command, RPCMode.All, args); + break; + default: + // Not supported + break; + } + } + + /// + /// This function will broadcast a buffered command to all clients and the originator. + /// + /// + /// The command. + /// + /// + /// Command arguments. + /// + public void BroadcastBufferedCommand(string command, params object[] args) + { + switch (mode) { + case InputTransportMode.Server: + case InputTransportMode.Client: + networkView.RPC(command, RPCMode.AllBuffered, args); + break; + default: + // Not supported + break; + } + } + + /// + /// This function will broadcast a non-buffered command to all clients and the originator. + /// + /// + /// The command. + /// + /// + /// Command arguments. + /// + public void BroadcastCommand(string command, params object[] args) + { + switch (mode) { + case InputTransportMode.Server: + case InputTransportMode.Client: + networkView.RPC(command, RPCMode.All, args); + break; + default: + // Not supported + break; + } + } + + #endregion + + #region Server Methods + + /// + /// This function is called by us, the server, to change the current level during a network game + /// + /// + /// Level. + /// + public void LoadScene(string level) + { + if (InputTransportMode.Local == mode) + { + Application.LoadLevel(level); + } + else if (InputTransportMode.Server == mode) + { + // Send a buffered RPC to load the level so everyone who joins also gets the command + networkView.RPC("OnLoadNetworkLevel", RPCMode.AllBuffered, level, nextLevelPrefix++); + } + else { + // TODO: Throw an exception? + } + } + + /// + /// This function will send a non-buffered command to a single player. It is not + /// designed to be used by individual clients. + /// + /// + /// Network player ID (should originally have been acquired from NetworkPlayer.ToString()) + /// + /// + /// Command. + /// + /// + /// Arguments. + /// + public void SendCommand(string networkPlayerID, string command, params object[] args) + { + switch (mode) { + case InputTransportMode.Server: + foreach (NetworkPlayer p in Network.connections) { + if (p.ToString() == networkPlayerID) { + networkView.RPC(command, p, args); + return; + } + } + // TODO: Throw an exception? + break; + default: + break; + } + } + + + #endregion + + #region Client Methods + + /// + /// This function is called by us, the client, to send a non-buffered command to the server + /// + /// + /// The command string. + /// + public void SendCommandToServer(string command) + { + switch (mode) { + case InputTransportMode.Client: + networkView.RPC(command, RPCMode.Server); + break; + default: + // TODO: Throw an exception? + break; + } + } + + #endregion + +} diff --git a/Assets/Standard Assets/Scripts/LANBroadcastService.cs b/Assets/Standard Assets/Scripts/LANBroadcastService.cs new file mode 100644 index 0000000..4f4a492 --- /dev/null +++ b/Assets/Standard Assets/Scripts/LANBroadcastService.cs @@ -0,0 +1,281 @@ +// LAN UDP-Broadcast Service Script +// 12-11-2009 +// Made by Jordin Kee aka Jordos +// You may use and/or modify this script as you like. Crediting is welcome, but not required. +// Use this at your own risk, I do not guarantee it is bugfree. In fact I do not guarantee anything :) + +// This script can be used as a service to perform UDP Broadcasting over a LAN. This is usefull to search for servers on a LAN, without knowing their IP. +// Next, this script is designed for a situation where the player is not able to choose to either start a server, or join one. +// Instead, it is determined by the application (i.e. this script). This, because of the projet I made it for. +// If you don't like this, but still want to use the script, read on... +// This script uses the .Net UDPClient class to perform sending and receiving (http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.aspx) + +// How to use this script: +// This script must be seen as a service, therefore it must be controlled by another object, say "NetworkController". This script serves 2 goals: +// 1. Search for an existing server and determine out of the results, whether this player should join a server, or start one itself +// 2. Send out messages saying it has started a server and is ready to receive connections +// NetworkController calls 'StartSearchBroadcasting'. This function takes 2 delegates (WTF? Delegate? --> http://msdn.microsoft.com/en-us/library/900fyy8e(VS.71).aspx) +// One is called when the script has found a server. It passes the IP address of the server, so the NetworkController can join it. +// The other is called when there is no server found or it is determined that this player should start one. The NetworkController can create a server. +// In this last case service 2 should be used, namely StartAnnounceBroadcasting(). This will broadcast messages that this player has a server ready. +// When other players start a search, they will receive this message and stop searching immediately. +// Make sure NetworkController also calls StopBroadcasting(), as not doing this may result in crashes when the game is started again. + +// How the script works: +// When a search is started, the script begins recieving messages. These messages are stored in a list and this list is refreshed, so that old messages are deleted. +// What the script also does, is sending out messages that this player is willing to start a server, but has none ready at the moment. +// The search stops as soon as a 'I have server ready' message is received, send out by the StartAnnounceBroadcasting() of another player. +// After a specified amount of time, the search is ended. The received messages are scanned. If there are none, this player must start a server. +// If there are messages 'i am willing to start a server' from other players, this means that multiple players are searching, but non has started a server. +// In this case the script determines which of these players must start the server. This is based on the IP of the players (the highest willl be the server). +// All players agree on this, as they use the same script. The player that is choosen will call the 'MustStartServer' delegate, +// the others will continue searching, waiting for the message that a server is ready. + +// If you want to use this script, but want players to choose between creating/joining a server for themselves: +// When the player chooses to create a server, call the StartAnnounceBroadcasting() +// When the player chooses to join, call StartSearchBroadcasting(), but strip the part of sending out messages (see Update() method) + +using UnityEngine; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; + +public class LANBroadcastService : MonoBehaviour +{ + public delegate void delJoinServer(string strIP); // Definition of JoinServer Delegate, takes a string as argument that holds the ip of the server + public delegate void delStartServer(); // Definition of StartServer Delegate + public enum enuState { NotActive, Searching, Announcing }; // Definition of State Enumeration. + public struct ReceivedMessage { public float fTime; public string strIP; public bool bIsReady;} // Definition of a Received Message struct. This is the form in which we will store messages + + private string strMessage = ""; // A simple message string, that can be read by other objects (eg. NetworkController), to show what this object is doing. + private enuState currentState = enuState.NotActive; + private UdpClient objUDPClient; // The UDPClient we will use to send and receive messages + private List lstReceivedMessages; // The list we store all received messages in, when searching + private delJoinServer delWhenServerFound; // Reference to the delegate that will be called when a server is found, set by StartSearchBroadcasting() + private delStartServer delWhenServerMustStarted; // Reference to the delegate that will be called when a server must be created, set by StartSearchBroadcasting() + private string strServerNotReady = "wanttobeaserver"; // The actual content of the 'i am willing to start a server' message + private string strServerReady = "iamaserver"; // The actual content of the 'i have a server ready' message + private float fTimeLastMessageSent; + private float fIntervalMessageSending = 1f; // The interval in seconds between the sending of messages + private float fTimeMessagesLive = 3; // The time a message 'lives' in our list, before it gets deleted + private float fTimeToSearch = 5; // The time the script will search, before deciding what to do + private float fTimeSearchStarted; + private string myIPAddress; + private float lastUpdateTime; + + public string Message { get { return strMessage; } } // Property to read the strMessage + public enuState CurrentState { get { return currentState; } } + public List ReceivedMessages { get { return lstReceivedMessages; } } + + void Awake() + { + // Create our list + lstReceivedMessages = new List(); + // Cache our IP address + IPHostEntry host; + myIPAddress = "0.0.0.0"; + host = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress ip in host.AddressList) + { + if (ip.AddressFamily.ToString() == "InterNetwork") + { + myIPAddress = ip.ToString(); + } + } + Debug.Log("Local IP Address: " + myIPAddress); + } + + void Update() + { + // Check if we need to send messages and the interval has espired + if ((currentState == enuState.Searching || currentState == enuState.Announcing) + && Time.time > fTimeLastMessageSent + fIntervalMessageSending) + { + // Determine out of our current state what the content of the message will be + byte[] objByteMessageToSend = System.Text.Encoding.ASCII.GetBytes(currentState == enuState.Announcing ? strServerReady : strServerNotReady); + // Send out the message + objUDPClient.Send(objByteMessageToSend, objByteMessageToSend.Length, new IPEndPoint(IPAddress.Broadcast, ConfigurationDirector.GetMulticastPort())); + // Restart the timer + fTimeLastMessageSent = Time.time; + + // Refresh the list of received messages (remove old messages) + if (currentState == enuState.Searching) + { + // This rather complex piece of code is needed to be able to loop through a list while deleting members of that same list + for (int i=0; i < lstReceivedMessages.Count; i++) + { + if (Time.time > lstReceivedMessages[i].fTime + fTimeMessagesLive) + { + // If this message is too old, delete it and restart the foreach loop + lstReceivedMessages.RemoveAt(i--); + } + } + } + } + + if (currentState == enuState.Searching) + { + // Check the list of messages to see if there is any 'i have a server ready' message present + foreach (ReceivedMessage objMessage in lstReceivedMessages) + { + // If we have a server that is ready, call the right delegate and stop searching + if (objMessage.bIsReady) + { + StopSearching(); + strMessage = "We will join"; + if (null != delWhenServerFound) { + delWhenServerFound(objMessage.strIP); + } + break; + } + } + // Check if we're ready searching. + if (currentState == enuState.Searching && Time.time > fTimeSearchStarted + fTimeToSearch) + { + // We are. Now determine who's gonna be the server. + + // This string holds the ip of the new server. We will start off pointing ourselves as the new server + string strIPOfServer = myIPAddress; + // Next, we loop through the other messages, to see if there are other players that have more right to be the server (based on IP) + foreach (ReceivedMessage objMessage in lstReceivedMessages) + { + if (ScoreOfIP(objMessage.strIP) > ScoreOfIP(strIPOfServer)) + { + // The score of this received message is higher, so this will be our new server + strIPOfServer = objMessage.strIP; + } + } + // If after the loop the highest IP is still our own, call delegate to start a server and stop searching + if (strIPOfServer == myIPAddress) + { + StopSearching(); + strMessage = "We will start server."; + if (null != delWhenServerMustStarted) { + delWhenServerMustStarted(); + } + } + // If it's not, someone else must start the server. We will simply have to wait as the server is clearly not ready yet + else + { + strMessage = "Found server. Waiting for server to get ready..."; + // Clear the list and do the search again. + lstReceivedMessages.Clear(); + fTimeSearchStarted = Time.time; + } + } + } + lastUpdateTime = Time.time; + } + + // Method to start an Asynchronous receive procedure. The UDPClient is told to start receiving. + // When it received something, the UDPClient is told to call the EndAsyncReceive() method. + private void BeginAsyncReceive() + { + objUDPClient.BeginReceive(new AsyncCallback(EndAsyncReceive), null); + } + // Callback method from the UDPClient. + // This is called when the asynchronous receive procedure received a message + private void EndAsyncReceive(IAsyncResult objResult) + { + // Create an empty EndPoint, that will be filled by the UDPClient, holding information about the sender + IPEndPoint objSendersIPEndPoint = new IPEndPoint(IPAddress.Any, 0); + // Read the message + byte[] objByteMessage = objUDPClient.EndReceive(objResult, ref objSendersIPEndPoint); + // If the received message has content and it was not sent by ourselves... + if (objByteMessage.Length > 0 && + !objSendersIPEndPoint.Address.ToString().Equals(myIPAddress)) + { + // Translate message to string + string strReceivedMessage = System.Text.Encoding.ASCII.GetString(objByteMessage); + // Create a ReceivedMessage struct to store this message in the list + ReceivedMessage objReceivedMessage = new ReceivedMessage(); + objReceivedMessage.fTime = lastUpdateTime; + objReceivedMessage.strIP = objSendersIPEndPoint.Address.ToString(); + objReceivedMessage.bIsReady = strReceivedMessage == strServerReady ? true : false; + lstReceivedMessages.Add(objReceivedMessage); + } + // Check if we're still searching and if so, restart the receive procedure + if (currentState == enuState.Searching) BeginAsyncReceive(); + } + // Method to start this object announcing this is a server, used by the script itself + private void StartAnnouncing() + { + currentState = enuState.Announcing; + strMessage = "Announcing we are a server..."; + } + // Method to stop this object announcing this is a server, used by the script itself + private void StopAnnouncing() + { + currentState = enuState.NotActive; + strMessage = "Announcements stopped."; + } + // Method to start this object searching for LAN Broadcast messages sent by players, used by the script itself + private void StartSearching() + { + lstReceivedMessages.Clear(); + fTimeSearchStarted = Time.time; + BeginAsyncReceive(); + currentState = enuState.Searching; + strMessage = "Searching for other players..."; + } + // Method to stop this object searching for LAN Broadcast messages sent by players, used by the script itself + private void StopSearching() + { + currentState = enuState.NotActive; + strMessage = "Search stopped."; + } + + // Method to be called by some other object (eg. a NetworkController) to start a broadcast search + // It takes two delegates; the first for when this object finds a server that can be connected to, + // the second for when this player is determined to start a server itself. + public void StartSearchBroadCasting(delJoinServer connectToServer, delStartServer startServer) + { + // Set the delegate references, so other functions within this class can call it + delWhenServerFound = connectToServer; + delWhenServerMustStarted = startServer; + // Start a broadcasting session (this basically prepares the UDPClient) + StartBroadcastingSession(); + // Start a search + StartSearching(); + } + // Method to be called by some other object (eg. a NetworkController) to start a broadcast announcement. Announcement means; tell everyone you have a server. + public void StartAnnounceBroadCasting() + { + // Start a broadcasting session (this basically prepares the UDPClient) + StartBroadcastingSession(); + // Start an announcement + StartAnnouncing(); + } + // Method to start a general broadcast session. It prepares the object to do broadcasting work. Used by the script itself. + private void StartBroadcastingSession() + { + // If the previous broadcast session was for some reason not closed, close it now + if (currentState != enuState.NotActive) StopBroadCasting(); + // Create the client + objUDPClient = new UdpClient(ConfigurationDirector.GetMulticastPort()); + objUDPClient.EnableBroadcast = true; + // Reset sending timer + fTimeLastMessageSent = Time.time; + } + // Method to be called by some other object (eg. a NetworkController) to stop this object doing any broadcast work and free resources. + // Must be called before the game quits! + public void StopBroadCasting() + { + if (currentState == enuState.Searching) StopSearching(); + else if (currentState == enuState.Announcing) StopAnnouncing(); + if (objUDPClient != null) + { + objUDPClient.Close(); + objUDPClient = null; + } + } + // Method that calculates a 'score' out of an IP adress. This is used to determine which of multiple clients will be the server. Used by the script itself. + private long ScoreOfIP(string strIP) + { + long lReturn = 0; + string strCleanIP = strIP.Replace(".", ""); + lReturn = long.Parse(strCleanIP); + return lReturn; + } +} diff --git a/Assets/Standard Assets/Scripts/MasterServerDirector.cs b/Assets/Standard Assets/Scripts/MasterServerDirector.cs new file mode 100644 index 0000000..217effb --- /dev/null +++ b/Assets/Standard Assets/Scripts/MasterServerDirector.cs @@ -0,0 +1,266 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +/// +/// The "master server" is the ivory tower where all servers of this game report to let +/// everyone know that this game is available and looking for players. This class manages +/// this application's communication with the master server; including functionality for +/// both searching and hosting. +/// +[RequireComponent (typeof (LANBroadcastService))] +public class MasterServerDirector : MonoBehaviour +{ + /// + /// Describes a game that was found when a search was done. + /// + public class FoundGame + { + /// + /// The game's listening IP address. + /// + public string ipAddress; + /// + /// True if the game is a dedicated server + /// + public bool isDedicated; + /// + /// True if the game is on the LAN + /// + public bool isOnLAN; + /// + /// The number of players on the server + /// + public int playerCount; + /// + /// The max number of players allowed on the server + /// + public int maxPlayerCount; + /// + /// The ping object that measures latency + /// + public Ping ping; + } + + /// + /// This component enables LAN-based game searching + /// + LANBroadcastService lanBroadcastService; + /// + /// True if we are searching for LAN games + /// + bool isLANGameSearchEnabled; + + /// + /// The list of all found WAN games (we use Unity's framework to populate this) + /// + List foundWANGames; + /// + /// The list of all found LAN games (we use the LANBroadcastService component to populate this) + /// + List foundLANGames; + + public int WANGameCount { get { return foundWANGames.Count; } } + public int LANGameCount { get { return foundLANGames.Count; } } + public FoundGame GetWANGameByIndex(int index) + { + return foundWANGames[index]; + } + public FoundGame GetLANGameByIndex(int index) + { + return foundLANGames[index]; + } + + /// + /// When hosting a game, we need to include special data in the comment to find things + /// like whether the game is a dedicated server or not. This function, given the user + /// input comment, will return the data formatted comment to give to the master server + /// director. + /// + /// + /// The formatted game comment (e.g. 'HTed's 24/7 server!') + /// + /// + /// The original game comment (e.g. 'Ted's 24/7 server!') + /// + /// + /// Dedicated server. + /// + static string FormatGameComment(string comment, bool dedicatedServer) + { + return (dedicatedServer ? "D" : "H") + comment; + } + + /// + /// When hosting a game, we need to include special data in the comment to find things + /// like whether the game is a dedicated server or not. This function, given a comment + /// from a game search result from the master server director, will give us the comment + /// to display to the user. + /// + /// + /// The original game comment (e.g. 'Ted's 24/7 server!') + /// + /// + /// The formatted game comment (e.g. 'HTed's 24/7 server!') + /// + static string UnformatGameComment(string comment) + { + return comment.Substring(1,comment.Length-1); + } + + /// + /// Returns true if the game being hosted is a dedicated server + /// + /// + /// true if this instance is dedicated server; otherwise, false. + /// + /// + /// The game's formatted game comment. + /// + static bool IsDedicatedServerByComment(string comment) + { + return (comment.Length > 0 && comment[0] == 'D') ? true : false; + } + + static string IPFromStringArray(string[] value) + { + string result = ""; + foreach (string s in value) { + result = s + " "; + } + return result; + } + + #region Unity Events + + void Awake() + { + foundLANGames = new List(); + lanBroadcastService = gameObject.GetComponent(); + // Start looking for hosts on the LAN + Debug.Log("Polling for LAN games on port " + ConfigurationDirector.GetMulticastPort()); + } + + void Update() + { + // Check for any new listings from the Unity master server + HostData[] hostData = MasterServer.PollHostList(); + if (hostData.Length > 0) + { + foreach (HostData host in hostData) + { + FoundGame foundGame = new FoundGame(); + foundGame.ipAddress = IPFromStringArray(host.ip); + if (IsDedicatedServerByComment(host.comment)) { + foundGame.isDedicated = true; + foundGame.playerCount = host.connectedPlayers - 1; + foundGame.maxPlayerCount = host.playerLimit - 1; + } else { + foundGame.isDedicated = false; + foundGame.playerCount = host.connectedPlayers; + foundGame.maxPlayerCount = host.playerLimit; + } + foundGame.ping = new Ping(foundGame.ipAddress); + foundGame.isOnLAN = false; + foundWANGames.Add(foundGame); + } + MasterServer.ClearHostList(); + } + + // Update our LAN listings from the LAN broadcast service. This list has a list of all received + // UDP packets in the last five seconds. + foundLANGames.Clear(); + foreach (LANBroadcastService.ReceivedMessage m in lanBroadcastService.ReceivedMessages) + { + if (m.bIsReady) { + // This is a host announcing its presence + FoundGame foundGame = new FoundGame(); + foundGame.ipAddress = m.strIP; + foundGame.ping = new Ping(foundGame.ipAddress); + foundGame.isOnLAN = true; + foundLANGames.Add(foundGame); + } + } + } + + void OnFailedToConnectToMasterServer(NetworkConnectionError error) + { + // TODO: We should probably do stuff here someday + Debug.Log("Failed to connect to the master server! " + error.ToString()); + } + + void OnMasterServerEvent(MasterServerEvent msEvent) + { + // TODO: We should probably do stuff here someday + Debug.Log("In OnMasterServerEvent: " + msEvent.ToString()); + } + + #endregion + + /// + /// Enables the LAN game search. + /// + /// + /// Enable. + /// + public void EnableLANGameSearch(bool enable) + { + if (isLANGameSearchEnabled != enable) + { + isLANGameSearchEnabled = enable; + if (enable) { + lanBroadcastService.StartSearchBroadCasting(null,null); + } else { + lanBroadcastService.StopBroadCasting(); + } + } + } + + /// + /// Registers our game with Unity's master game server list and the LAN + /// + /// + /// Game type name. + /// + /// + /// Game name. + /// + /// + /// Comment. + /// + /// + /// Dedicated server. + /// + /// + /// Internet server. + /// + public void RegisterHost(string gameTypeName, string gameName, string comment, bool dedicatedServer, bool internetServer) + { + // TODO: Pass in server information (pass in the game type name, name, comment, and dedicated server flags) + lanBroadcastService.StopBroadCasting(); + lanBroadcastService.StartAnnounceBroadCasting(); + if (internetServer) { + MasterServer.RegisterHost(gameTypeName, gameName, FormatGameComment(comment, dedicatedServer)); + } + } + + /// + /// Removes our game from Unity's master game server list and the LAN + /// + public void UnregisterHost() + { + lanBroadcastService.StopBroadCasting(); + MasterServer.UnregisterHost(); // This is safe even if we aren't an internet host + } + + /// + /// Requests the master game server listing from Unity + /// + public void RequestHostList() + { + MasterServer.ClearHostList(); + foundWANGames = new List(); // Clear our known list + MasterServer.RequestHostList(VersionDirector.GetGameTypeName()); + } + +} diff --git a/Assets/Standard Assets/Scripts/PlayerAttributes.cs b/Assets/Standard Assets/Scripts/PlayerAttributes.cs new file mode 100644 index 0000000..f9171ff --- /dev/null +++ b/Assets/Standard Assets/Scripts/PlayerAttributes.cs @@ -0,0 +1,23 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class describes fundamental player attributes +/// +public class PlayerAttributes +{ + /// + /// This is the player's network view ID from the server's perspective + /// + public string ID; + + /// + /// The player's name + /// + public string PlayerName; + + /// + /// An object containing extended application-specific attributes + /// + public object Extended; +} diff --git a/Assets/Standard Assets/Scripts/Server.cs b/Assets/Standard Assets/Scripts/Server.cs new file mode 100644 index 0000000..2c699b3 --- /dev/null +++ b/Assets/Standard Assets/Scripts/Server.cs @@ -0,0 +1,202 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +/// +/// This component manages application-specific server work. When you host +/// a game, the input director doesn't care about who is already in the game +/// or banned IP addresses; but this component does. +/// +public class Server : MonoBehaviour +{ + #region Member Variables + + /// + /// The input director that we use to communicate with the world + /// + protected InputDirector inputDirector; + + /// + /// The list of players in the game + /// + public Dictionary playerList; // Public for debugging purposes + + /// + /// The list of banned IP addresses + /// + protected Dictionary bannedIPAddresses; + + #endregion + + #region Properties + + /// + /// Returns the number of players + /// + public int PlayerCount { get { return playerList.Count; } } + + /// + /// Gets the player. + /// + /// + /// The player. + /// + /// + /// Player ID. + /// + public PlayerAttributes GetPlayer(string playerID) { return playerList[playerID]; } + + /// + /// Determines whether a specified ID address is spanned. + /// + /// + /// true if the IP address is banned; otherwise, false. + /// + /// + /// The IP address. + /// + public bool IsBanned(string IPAddress) + { + return bannedIPAddresses.ContainsKey(IPAddress); + } + + #endregion + + /// + /// The one and only server. This will exist throughout the application's lifetime. + /// + static private Server _server; + + // Returns the one and only server component. This component is used by clients to register + // with game servers, and used by game servers to track player lists. This component is + // always attached to the input director. + static public Server Get() + { + if (null == _server) + { + InputDirector inputDirector = InputDirector.Get(); + if (null != inputDirector) + { + _server = InputDirector.Get().gameObject.GetComponent(); + } + } + return _server; + } + + // Use this for initialization + void Start() + { + playerList = new Dictionary(); + bannedIPAddresses = new Dictionary(); + inputDirector = GetComponent(); + } + + // This is called by clients and servers alike to register with the master player + // list, and to get an updated list from the main server. + public void Register() + { + if (inputDirector.GetMode() == InputDirector.InputTransportMode.Server) + { + // If this is called by the server, then it must mean the game is just starting + // and they are the only player in it. So, we need to clear the player list. The + // calling client component is responsible for acting like registration was successful. + playerList.Clear(); + } + else + { + // If we're a client, send a message to the server that we want to register. We will get our + // network view ID, as it is on the server, back in a response message. + networkView.RPC("OnServerRegister", RPCMode.Server, VersionDirector.GetVersion()); + } + } + + // This is called by the server when a player is disconnected and we want to + // unregister the player from the server list. + public void Unregister(NetworkPlayer p) + { + // Tell everyone, including ourselves, that the client left. We don't need to + // buffer this because when the client disconnected, we removed all its RPC + // buffer entries. New players that come in later will never know that this + // player existed. + networkView.RPC("OnPlayerUnregistered", RPCMode.All, p.ToString()); + } + + // This is called by the server to kick a player + public void Kick(string ID, bool ban) + { + if (inputDirector.IsHosting() && inputDirector.IsNetworking()) + { + string IPAddress = inputDirector.GetIPAddress(ID); + inputDirector.DisconnectClientFromServer(ID); + if (ban) { + bannedIPAddresses.Add(IPAddress, true); + ConsoleDirector.Log("Banned player " + ID + " with address " + IPAddress); + } else { + ConsoleDirector.Log("Kicked player " + ID); + } + } + } + + // Called on a server when a player disconnects from the server + void OnPlayerDisconnectedFromServer(NetworkPlayer player) + { + // Unregister the player with the server + Unregister(player); + } + + // This message is sent from a client to a server. This includes the version of the game + [RPC] + void OnServerRegister(string requestedVersion, NetworkMessageInfo info) + { + Debug.Log("OnServerRegister called from player " + info.sender.ToString() + " with version " + requestedVersion); + // This is where games can do per-game authentication with players before + // letting them actually play. Here, we: + // + // - Ensure the versions are consistent + // - Ensure the player is not banned + // + // If all is well, reply to the player with their new ID; make them responsible + // for broadcasting their presence to everyone so that it's their network + // view ID that gets put into the RPC buffer. + if (VersionDirector.GetVersion() != requestedVersion) { + networkView.RPC("OnServerRegistrationFailed", info.sender, "Version mismatch"); + } else if (IsBanned(info.sender.ipAddress)) { + networkView.RPC("OnServerRegistrationFailed", info.sender, "You are banned"); + } else { + // Inform the sender that the registration was successful + networkView.RPC("OnRegisteredWithServer", info.sender, info.sender.ToString()); + } + } + + // This message is sent from a client or server to everyone to make their presence known to + // all in the game. This is a buffered call so that incoming players can seamlessly get the + // player list. + [RPC] + void OnPlayerRegistered(string ID, string playerName) + { + Debug.Log("OnPlayerRegistered called. Adding " + playerName + " (" + ID + ") to the list"); + + // Add the player to our list + PlayerAttributes a = new PlayerAttributes(); + a.ID = ID; + a.PlayerName = playerName; + playerList.Add(ID, a); + + // Log the connection + ConsoleDirector.Log(playerName + " has joined the game."); + } + + // This message is sent from the server to everyone to make it known that a player has left + // the game. + [RPC] + void OnPlayerUnregistered(string ID) + { + // Remove the player from the list + PlayerAttributes e; + if (playerList.TryGetValue(ID, out e)) + { + ConsoleDirector.Log(e.PlayerName + " has left the game."); + playerList.Remove(ID); + } + } +} diff --git a/Assets/Standard Assets/Scripts/VersionDirector.cs b/Assets/Standard Assets/Scripts/VersionDirector.cs new file mode 100644 index 0000000..0cae4a6 --- /dev/null +++ b/Assets/Standard Assets/Scripts/VersionDirector.cs @@ -0,0 +1,31 @@ +using UnityEngine; +using System.Collections; + +/// +/// This class is responsible for maintaining the application version +/// and name for use with the master server. +/// +public class VersionDirector +{ + /// + /// Gets the version of the application. + /// + /// + /// The version. + /// + static public string GetVersion() + { + return "0.1"; + } + + /// + /// Gets the name of the game type. + /// + /// + /// The game type name. + /// + static public string GetGameTypeName() + { + return "Vengance " + GetVersion(); + } +} diff --git a/Assets/Textures/Materials/spaceship.mat b/Assets/Textures/Materials/spaceship.mat new file mode 100644 index 0000000..227c165 Binary files /dev/null and b/Assets/Textures/Materials/spaceship.mat differ diff --git a/Assets/Textures/Materials/spaceshipbullet.mat b/Assets/Textures/Materials/spaceshipbullet.mat new file mode 100644 index 0000000..ea3a6da Binary files /dev/null and b/Assets/Textures/Materials/spaceshipbullet.mat differ diff --git a/Assets/Textures/Materials/starfield.mat b/Assets/Textures/Materials/starfield.mat new file mode 100644 index 0000000..ba7a9a1 Binary files /dev/null and b/Assets/Textures/Materials/starfield.mat differ diff --git a/Assets/Textures/Spaceship.bmp b/Assets/Textures/Spaceship.bmp new file mode 100644 index 0000000..4422c5a Binary files /dev/null and b/Assets/Textures/Spaceship.bmp differ diff --git a/Assets/Textures/plain.bmp b/Assets/Textures/plain.bmp new file mode 100644 index 0000000..bf0bb26 Binary files /dev/null and b/Assets/Textures/plain.bmp differ diff --git a/Assets/instructions.txt b/Assets/instructions.txt new file mode 100644 index 0000000..c2976d4 --- /dev/null +++ b/Assets/instructions.txt @@ -0,0 +1,167 @@ +==================================================================================== +Gamieon Unity network wrapper +By Christopher Haag +Gamieon, Inc. +http://www.gamieon.com +==================================================================================== + +This is a simple project that has a number of scripts designed to be re-used in other Unity applications. +It is designed to offer a way to prevent all of your scripts from looking like: + + ... + if (networked) { do this; } + else { do that; } + ... + +By centralizing GameObject instantiation, destruction, message passing and player management into +a single persistent GameObject that has several scripts with functions you can use. + +For example, if you want to create a bullet, you wouldn't do: + + if (networked) { Network.Instantiate(...); } + else { GameObject.Instantiate(...); } + +You could instead just do: + + inputDirector.InstantiateObject(...); + +and it will get the job done. + +If your game is not single player or you just connect to yourself to play locally, then that is of little +value. However, these scripts also manage searching for instances of your game online (even on LAN's), +notifying players when other players join and leave in your game; and even support ban lists (in memory only). + +This project also provides a very simple example of how Unity networking mechanics work. A number of Debug.Log +messages are scattered throughout the code so you can observe them and better understand things. + + +=========================================================================================================== +Relevant files you shouldn't have to change +=========================================================================================================== +Client.cs - Represents the user's existence in the application. All non-application-specific player management +happens here. This component must be a subclass of the actual application-specific user component. + +InputDirector.cs - The main hub responsible for dispatching player events, hosting games and joining games. +There is only ever once instance of this component, and it persists throughout the application's lifetime. + +LANBroadcastService.cs - Lower level code used to find LAN players using UDP multicasting + +MasterServerDirector.cs - Used for players to find each other over both LANs and WANs + +PlayerAttributes.cs - Retains player ID's, names, and eventually custom fields like achievements and more. + +Server.cs - Whether you are a client or hosting the game, this acts as an interface to the list of players +and other authoritative operations. + + +=========================================================================================================== +Game-specific files used in this sample project +=========================================================================================================== + +ConfigurationDirector.cs - Controls server rules and player configuration + +ConnectionFailureSceneDirector.cs - Manages the scene you see when you fail to connect to a server + +ConsoleDirector.cs - Right now it simply forwards logging messages to the Unity console, but it is intended +to be the game's console controller where you can see an event history and also handle commands. + +GameDirector.cs - Responsible for dealing with getting a player set up to begin playing a round of the game. + +GameRules_FFA.cs - Defines how players set themselves up to play a free for all game, and the rules to win. + +Player.cs - Represents the user's existence in the application. There is only ever once instance of this component, +and it persists throughout the application's lifetime. It is inherited from the Client component. + +VersionDirector.cs - Simple application versioning properties + + + +=========================================================================================================== +The chain of events when hosting a game +=========================================================================================================== + +1. When the application is launched, Player.Create() is called. This creates a new GameObject in the initial scene +that encapsulates these components: InputDirector, LANBroadcastService, MasterServerDirector, Player (inherited +from Client). This GameObject will persist throughout the lifetime of the application. + +2. InputDirector.HostServer is called to launch the host. + +3. InputDirector sends OnHostServerComplete message to its game object. + +4. Client component catches OnHostServerComplete message and "registers" itself with the server by calling +Server.Register. Registering with the server is a level of authentication above Unity's engine; clients do +it as an RPC call to give the servers their name, version, any unique ID's, and IP address to check against +a ban list. + +5. OnRegisteredWithServer is called on the Player object, and it goes into the game by calling +InputDirector.LoadScene. A buffered RPC call is made to load the level; this way, new players know to go into that same level. + +6. Unity calls Client.OnLevelWasLoaded, and it notifies all game objects in the scene with a call to +OnNetworkLoadedLevel. + +7. The GameDirector object, which exists in the new scene, gets the OnNetworkLoadedLevel message. It checks +the application's configuration for what kind of game is playing (in this case, a free-for-all). It then +sends another buffered RPC call called "OnDefineGameRules" that informs all incoming clients of what kind +of game is being played. + +8. The Player object, which is in the same game object as the InputDirector component, will get the OnDefineGameRules +message and handle it by sending a OnGameRulesDefined message to the GameDirector object. + +9. The GameDirector object gets the OnGameRulesDefined message, and creates a GameRules-inherited component for its game object +based on what. The GameRules component dictates things like how to win a game, scoring rules, and so forth. The GameDirector +object then sends an OnHostBeginGame message to its game object. + +10. The GameRules_FFA component gets the OnHostBeginGame message, and sends a message back to the Player component's game object +of OnSpawnSpaceship to have a player spawn its spaceship. It's important to understand that the Player component and the Spaceship +components are two completely different things. + +11. The player instructs the input director to instantiate the spaceship. It will then keep a handle to its spaceship and +invoke a buffered RPC call to set the spaceship attributes. + +12. The created spaceship gets the OnSetSpaceshipAttributes message and has its attributes assigned to it. + + +=========================================================================================================== +The chain of events when connecting to a game +=========================================================================================================== + +1. When the application is launched, Player.Create() is called. This creates a new GameObject in the initial scene +that encapsulates these components: InputDirector, LANBroadcastService, MasterServerDirector, Player (inherited +from Client). This GameObject will persist throughout the lifetime of the application. + +2. InputDirector.ConnectToServer is calle to connect to the remote host. + +3. After Unity sends the OnConnectedToServer message to the InputDirector, the InputDirector will send a OnConnectToServerComplete +message that the Client component will get. + +4. The Client component will call Server.Register to "register" itself with the server. Registering with the server +is a level of authentication above Unity's engine; clients do it as an RPC call to give the servers their name, version, +any unique ID's, and IP address to check against a ban list. + +5. At this point, the server is sending buffered RPC's to you. The first buffered RPC message, OnLoadNetworkLevel, will +have you load the game scene. The next messages are OnPlayerRegistered messages that other players sent; server included, +when they joined the game. This is how you get a list of everyone in the game. You also get a OnDefineGameRules message +which contains information about the game you're playing. In this case, the rules are a simple "Free For All" string, which +tells the application that everyone spawns at will and it's just going to be a shootout. The Player component stores those +rules for later so we know how to spawn our spaceship after we've registered. + +6. The server approves your request to register, and you get a OnRegisteredWithServer message back in the Player component +that contains your new player ID. After getting this, you send a buffered OnPlayerRegistered message to everyone to "introduce" +yourself to everyone in the game; server included. + +7. Everyone, yourself included, gets the OnPlayerRegistered message and you show up in all the player lists. Now that you've +registered, and you know the game rules, you're ready to start playing. The Player component sends a OnGameRulesDefined message +to the GameDirector component over in another GameObject. + +8. The GameDirector component gets OnGameRulesDefined, creates a GameRules_FFA component, and sends an OnBeginGame message +to that component. + +9. The GameRules_FFA component sends a message to your InputDirector GameObject called OnSpawnSpaceship; which the Player +component picks up and uses InputDirector.instantiate to create your spaceship so that everyone in the game can see it. + +10. The player instructs the input director to instantiate the spaceship. It will then keep a handle to its spaceship and +invoke a buffered RPC call to set the spaceship attributes. + +11. The created spaceship gets the OnSetSpaceshipAttributes message and has its attributes assigned to it. + + diff --git a/Assets/starfield.jpg b/Assets/starfield.jpg new file mode 100644 index 0000000..e9ff18a Binary files /dev/null and b/Assets/starfield.jpg differ diff --git a/Library.zip b/Library.zip new file mode 100644 index 0000000..c36a875 Binary files /dev/null and b/Library.zip differ diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..9595c59 Binary files /dev/null and b/ProjectSettings/AudioManager.asset differ diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..b5f3165 Binary files /dev/null and b/ProjectSettings/DynamicsManager.asset differ diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..042b8fe Binary files /dev/null and b/ProjectSettings/EditorBuildSettings.asset differ diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..5cec1a9 Binary files /dev/null and b/ProjectSettings/EditorSettings.asset differ diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..9679976 Binary files /dev/null and b/ProjectSettings/InputManager.asset differ diff --git a/ProjectSettings/NavMeshLayers.asset b/ProjectSettings/NavMeshLayers.asset new file mode 100644 index 0000000..27d9e93 Binary files /dev/null and b/ProjectSettings/NavMeshLayers.asset differ diff --git a/ProjectSettings/NetworkManager.asset b/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..fbb4791 Binary files /dev/null and b/ProjectSettings/NetworkManager.asset differ diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..80b5754 Binary files /dev/null and b/ProjectSettings/ProjectSettings.asset differ diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..29e262b Binary files /dev/null and b/ProjectSettings/QualitySettings.asset differ diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..364caed Binary files /dev/null and b/ProjectSettings/TagManager.asset differ diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..ab817b9 Binary files /dev/null and b/ProjectSettings/TimeManager.asset differ diff --git a/UniNetShooter-csharp.sln b/UniNetShooter-csharp.sln new file mode 100644 index 0000000..88cf69d --- /dev/null +++ b/UniNetShooter-csharp.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 + +Project("{F2EAEDF2-2AEA-3F1C-5EEC-152ADA60C0D6}") = "UniNetShooter", "Assembly-CSharp-firstpass-vs.csproj", "{2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}" +EndProject +Project("{F2EAEDF2-2AEA-3F1C-5EEC-152ADA60C0D6}") = "UniNetShooter", "Assembly-CSharp-vs.csproj", "{05EC98B6-FB67-CA9B-8A26-88E22667BD72}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Release|Any CPU.Build.0 = Release|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Assembly-CSharp.csproj + Policies = $0 + $0.TextStylePolicy = $1 + $1.inheritsSet = null + $1.scope = text/x-csharp + $0.CSharpFormattingPolicy = $2 + $2.inheritsSet = Mono + $2.inheritsScope = text/x-csharp + $2.scope = text/x-csharp + $0.TextStylePolicy = $3 + $3.FileWidth = 120 + $3.TabWidth = 4 + $3.EolMarker = Unix + $3.inheritsSet = Mono + $3.inheritsScope = text/plain + $3.scope = text/plain + EndGlobalSection + +EndGlobal diff --git a/UniNetShooter.sln b/UniNetShooter.sln new file mode 100644 index 0000000..f6730d1 --- /dev/null +++ b/UniNetShooter.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 + +Project("{F2EAEDF2-2AEA-3F1C-5EEC-152ADA60C0D6}") = "UniNetShooter", "Assembly-CSharp-firstpass.csproj", "{2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}" +EndProject +Project("{F2EAEDF2-2AEA-3F1C-5EEC-152ADA60C0D6}") = "UniNetShooter", "Assembly-CSharp.csproj", "{05EC98B6-FB67-CA9B-8A26-88E22667BD72}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CA8DFC8-CE1D-F164-BA55-923E8768E9FF}.Release|Any CPU.Build.0 = Release|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05EC98B6-FB67-CA9B-8A26-88E22667BD72}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Assembly-CSharp.csproj + Policies = $0 + $0.TextStylePolicy = $1 + $1.inheritsSet = null + $1.scope = text/x-csharp + $0.CSharpFormattingPolicy = $2 + $2.inheritsSet = Mono + $2.inheritsScope = text/x-csharp + $2.scope = text/x-csharp + $0.TextStylePolicy = $3 + $3.FileWidth = 120 + $3.TabWidth = 4 + $3.EolMarker = Unix + $3.inheritsSet = Mono + $3.inheritsScope = text/plain + $3.scope = text/plain + EndGlobalSection + +EndGlobal diff --git a/UniNetShooter.userprefs b/UniNetShooter.userprefs new file mode 100644 index 0000000..c1bf329 --- /dev/null +++ b/UniNetShooter.userprefs @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file