From 02acf5a7e76ecee2b8e000dd955c0db35da458fd Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Mon, 1 Dec 2014 23:51:49 -0800 Subject: [PATCH] Lots of stuff --- HappyFunTimes.sln | 7 + HappyFunTimes/ArgParser.cs | 133 ++++++ HappyFunTimes/AssemblyInfo.cs | 6 +- HappyFunTimes/GameServer.cs | 387 ++++++++++++++++-- HappyFunTimes/GameSystem.cs | 2 +- HappyFunTimes/HappyFunTimes.csproj | 2 + HappyFunTimes/MessageCmd.cs | 4 + HappyFunTimes/NetPlayer.cs | 170 +++++++- HappyFunTimes/PlayerConnectMessageArgs.cs | 7 +- HappyFunTimes/PlayerSpawner.cs | 21 + HappyFunTimes/libs/DeJson.dll | Bin 12800 -> 14848 bytes HappyFunTimesEditor/AssemblyInfo.cs | 27 ++ HappyFunTimesEditor/HFTWindow.cs | 43 ++ .../HappyFunTimesEditor.csproj | 50 +++ 14 files changed, 814 insertions(+), 45 deletions(-) create mode 100644 HappyFunTimes/ArgParser.cs create mode 100644 HappyFunTimesEditor/AssemblyInfo.cs create mode 100644 HappyFunTimesEditor/HFTWindow.cs create mode 100644 HappyFunTimesEditor/HappyFunTimesEditor.csproj diff --git a/HappyFunTimes.sln b/HappyFunTimes.sln index 31b7a22..6c8a1bb 100644 --- a/HappyFunTimes.sln +++ b/HappyFunTimes.sln @@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HappyFunTimes", "HappyFunTimes\HappyFunTimes.csproj", "{554A0027-C03D-4D04-946D-2A34E43CC586}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HappyFunTimesEditor", "HappyFunTimesEditor\HappyFunTimesEditor.csproj", "{97F1A61A-1B41-4878-BA21-9A029AA49791}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -13,8 +15,13 @@ Global {554A0027-C03D-4D04-946D-2A34E43CC586}.Debug|Any CPU.Build.0 = Debug|Any CPU {554A0027-C03D-4D04-946D-2A34E43CC586}.Release|Any CPU.ActiveCfg = Release|Any CPU {554A0027-C03D-4D04-946D-2A34E43CC586}.Release|Any CPU.Build.0 = Release|Any CPU + {97F1A61A-1B41-4878-BA21-9A029AA49791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97F1A61A-1B41-4878-BA21-9A029AA49791}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97F1A61A-1B41-4878-BA21-9A029AA49791}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97F1A61A-1B41-4878-BA21-9A029AA49791}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = HappyFunTimes\HappyFunTimes.csproj + version = 0.2 EndGlobalSection EndGlobal diff --git a/HappyFunTimes/ArgParser.cs b/HappyFunTimes/ArgParser.cs new file mode 100644 index 0000000..9727e3d --- /dev/null +++ b/HappyFunTimes/ArgParser.cs @@ -0,0 +1,133 @@ +/* + * Copyright 2014, Gregg Tavares. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Gregg Tavares. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System.Collections.Generic; + +namespace HappyFunTimes { + +/// +/// A very simple commandline argument parser. +/// +/// It just looks arguments that start with "--". For any +/// argument that starts with "--" it will then look for the +/// first "=" sign. If found it will set remember the value +/// for that option. If no equals is found it uses the value +/// "true" +/// +/// +/// Example: Assume your command line is +/// +/// myprogram --name=gregg --count=123 --debug +/// +/// You can then read those options with +/// +/// using HappyFunTimes; +/// +/// ... +/// ArgParser p = new ArgParser(); +/// +/// // Set Defaults +/// string name = "Someone"; +/// int count = 1; +/// bool debug = false; +/// +/// p.TryGet("name", name); +/// p.TryGet("count", count); +/// if (p.Contains("debug")) { +/// debug = true; +/// } +/// +/// +/// +public class ArgParser { + + /// + /// Constructor for ArgParser that parses command line arguments + /// + public ArgParser() + { + Init(System.Environment.GetCommandLineArgs()); + } + + /// + /// Constructor for ArgParser which you can pass your own array of strings. + /// + /// Array of command line like argument strings + public ArgParser(string[] args) + { + Init(args); + } + + /// + /// Check if a switch exists. + /// + /// name of switch + /// true if switch was contained in arguments + public bool Contains(string id) + { + return m_switches.ContainsKey(id); + } + + /// + /// Gets the value of a switch if it exists + /// + /// id of switch + /// variable to receive the value + /// true if the value exists. False if it does not. If false value has not been affected. + public bool TryGet(string id, ref T value) { + string v; + bool found = m_switches.TryGetValue(id, out v); + if (found) { + value = (T)System.Convert.ChangeType(v, typeof(T)); + } + return found; + } + + private void Init(string[] arguments) + { + m_switches = new Dictionary(); + foreach (string arg in arguments) { + if (arg.StartsWith("--")) { + int equalsNdx = arg.IndexOf('='); + if (equalsNdx >= 0) { + m_switches[arg.Substring(2, equalsNdx - 2)] = arg.Substring(equalsNdx + 1); + } else { + m_switches[arg.Substring(2)] = "true"; + } + } + } + } + + private Dictionary m_switches; +}; + + +} // namespace HappyFunTimes + diff --git a/HappyFunTimes/AssemblyInfo.cs b/HappyFunTimes/AssemblyInfo.cs index 43853d9..e720bed 100644 --- a/HappyFunTimes/AssemblyInfo.cs +++ b/HappyFunTimes/AssemblyInfo.cs @@ -5,10 +5,10 @@ // Change them to the values specific to your project. [assembly: AssemblyTitle("HappyFunTimes")] -[assembly: AssemblyDescription("")] +[assembly: AssemblyDescription(".NET Support for HappyFunTimes")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] +[assembly: AssemblyCompany("Greggman")] +[assembly: AssemblyProduct("HappyFunTimes")] [assembly: AssemblyCopyright("Gregg Tavares")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/HappyFunTimes/GameServer.cs b/HappyFunTimes/GameServer.cs index 789b15a..25d46f1 100644 --- a/HappyFunTimes/GameServer.cs +++ b/HappyFunTimes/GameServer.cs @@ -39,34 +39,153 @@ namespace HappyFunTimes { public class GameServer { - public class Options { - public Options() { + public delegate void UntypedCmdEventHandler(Dictionary data, string id); + public delegate void TypedCmdEventHandler(T eventArgs, string id) where T : MessageCmdData; + + private class CmdConverter where T : MessageCmdData + { + public CmdConverter(TypedCmdEventHandler handler) { + m_handler = handler; + } + + public void Callback(GameServer server, MessageCmdData data, Dictionary dict, string id) { + server.QueueEvent(delegate() { + m_handler((T)data, id); + }); + } + + TypedCmdEventHandler m_handler; + } + + private class UntypedCmdConverter { + public UntypedCmdConverter(UntypedCmdEventHandler handler) { + m_handler = handler; + } + + public void Callback(GameServer server, MessageCmdData data, Dictionary dict, string id) { + server.QueueEvent(delegate() { + object mcd = null; + // dict is the MessageCmd. We want dict for the MessageCmdData inside the MessageCmd + // It might not exist + dict.TryGetValue("data", out mcd); + m_handler((Dictionary)mcd, id); + }); + } + + UntypedCmdEventHandler m_handler; + } + + /// + /// Options for the GameServer + /// + public class Options + { + public Options() + { cwd = Application.dataPath; disconnectPlayersIfGameDisconnects = true; + url = "ws://localhost:18679"; + + // Prefix all HFT arguments with "hft-" so user can filter them out + ArgParser p = new ArgParser(); + p.TryGet("hft-id", ref id); + p.TryGet("hft-url", ref url); + master = p.Contains("hft-master"); } + + /// + /// there's generally no need to set this. + /// public string gameId; - public string controllerUrl; // not used! left over so things don't break - public bool showMessages; + + /// + /// id used for multi-player games. Can be set from command line with --hft=id=someid + /// + public string id; + + /// + ///Deprecated and not used. + /// + public string controllerUrl; + + /// + ///true allows multiple games to run as the same id. Default: false + /// + ///normally when a second game connects the first game will be disconnected + ///as it's assumed the first game probably crashed or for whatever reason did + ///not disconnect and this game is taking over. Setting this to true doesn't + ///disconnect the old game. This is needed for multi-machine games. + /// + public bool allowMultipleGames; // allow multiple games + + /// + ///For a multiple machine game designates this game as the game where players start. + ///Default: false + ///Can be set from command line with --hft-master + /// + public bool master; + + /// + ///The URL of HappyFunTimes + /// + ///Normally you don't need to set this as HappyFunTimes is running on the same machine + ///as the game. But, for multi-machine games you'd need to tell each machine the address + ///of the machine running HappyFunTimes. Example: "ws://192.168.2.9:18679". + /// + ///Can be set from the command line with --hft-url=someurl + /// + public string url; + + /// + ///Normally if a game disconnets all players are also disconnected. This means + ///they'll auto join the next game running. + ///Default: true + /// public bool disconnectPlayersIfGameDisconnects; - public string cwd; // don't set this. it will be set automatically + + /// + ///Prints all the messages in and out to the console. + /// + public bool showMessages; + + /// + /// don't set this. it will be set automatically + /// + public string cwd; }; + /// + /// Constructor for GameServer + /// + /// The objects + /// gameObject that will process messages from HappyFunTimes public GameServer(Options options, GameObject gameObject) { m_options = options; m_gameObject = gameObject; - m_players = new Dictionary(); + m_players = new Dictionary(); m_sendQueue = new List(); m_deserializer = new Deserializer(); + m_mcdc = new MessageCmdDataCreator(); + m_deserializer.RegisterCreator(m_mcdc); + m_handlers = new Dictionary(); + m_gameSystem = new GameSystem(this); m_eventProcessor = m_gameObject.AddComponent(); m_eventProcessor.Init(this); } + /// + /// Starts the connection to HappyFunTimes. + /// public void Init() { - Init("ws://localhost:18679"); + Init(m_options.url); } + /// + /// Deperacated + /// + /// public void Init(string url/* = "ws://localhost:18679" */) { if (m_socket == null) { @@ -93,38 +212,177 @@ public void QueueEvent(Action action) { m_eventProcessor.QueueEvent(action); } + /// + /// Lets you register a command to be called when the game is directly sent a message. + /// + /// The callback you register must have a CmdName attribute. That attibute will determine + /// which event the callback gets dispatched for. + /// + /// + /// + /// [CmdName("color")] + /// private class MessageColor : MessageCmdData { + /// public string color = ""; // in CSS format rgb(r,g,b) + /// }; + /// + /// ... + /// gameServer.RegisterCmdHandler(OnColor); + /// ... + /// void OnColor(MessageColor) { + /// Debug.Log("received msg with color: " + color); + /// } + /// + /// + /// Typed callback + public void RegisterCmdHandler(TypedCmdEventHandler callback) where T : MessageCmdData { + string name = MessageCmdDataNameDB.GetCmdName(typeof(T)); + if (name == null) { + throw new System.InvalidOperationException("no CmdNameAttribute on " + typeof(T).Name); + } + RegisterCmdHandler(name, callback); + } + + /// + /// Lets you register a command to be called when the game is directly sent a message. + /// + /// + /// + /// private class MessageColor : MessageCmdData { + /// public string color = ""; // in CSS format rgb(r,g,b) + /// }; + /// + /// ... + /// gameServer.RegisterCmdHandler("color", OnColor); + /// ... + /// void OnColor(MessageColor) { + /// Debug.Log("received msg with color: " + color); + /// } + /// + /// + /// command to call callback for + /// Typed callback + public void RegisterCmdHandler(string name, TypedCmdEventHandler callback) where T : MessageCmdData { + CmdConverter converter = new CmdConverter(callback); + m_handlers[name] = converter.Callback; + m_mcdc.RegisterCreator(typeof(T)); + } + + + /// + /// Lets you register a command to be called when the game is directly sent a message. + /// + /// This the most low-level basic version of RegisterCmdHandler. The function registered + /// will be called with a Dictionary with whatever data is passed in. + /// You can either pull the data out directly OR you can call Deserializer.Deserilize + /// with a type and have the data extracted for you. + /// + /// + /// + /// ... + /// gameServer.RegisterCmdHandler("color", OnColor); + /// ... + /// void OnColor(Dictionary data) { + /// Debug.Log("received msg with color: " + data["color"]); + /// } + /// + /// or + /// + /// private class MessageColor : MessageCmdData { + /// public string color = ""; // in CSS format rgb(r,g,b) + /// }; + /// ... + /// gameServer.RegisterCmdHandler("color", OnColor); + /// ... + /// void OnColor(Dictionary data) { + /// Deserializer d = new Deserializer(); + /// MessageColor msg = d.Deserialize(data); + /// Debug.Log("received msg with color: " + msg.color); + /// } + /// + /// + /// command to call callback for + /// Typed callback + public void RegisterCmdHandler(string name, UntypedCmdEventHandler callback) { + UntypedCmdConverter converter = new UntypedCmdConverter(callback); + m_handlers[name] = converter.Callback; + m_mcdc.RegisterCreator(name, typeof(Dictionary)); + } + + /// This needs the server because messages need to be queued as they need to be delivered on anther thread. + private delegate void CmdEventHandler(GameServer server, MessageCmdData cmdData, Dictionary dict, string id); + public event EventHandler OnPlayerConnect; public event EventHandler OnConnect; public event EventHandler OnDisconnect; + /// + /// Id of the machine assigned by HappyFunTimes. + /// + /// If you're running a multi-machine you can pass an id in the GameServer construtor options. + /// If you don't pass an id you'll be assigned one. This is the assigned one. + /// + /// Note: It is invalid to read this property before the game has connected. + /// + /// + /// + /// ... + /// gameServer.OnConnect += OnConnect; + /// ... + /// void OnConnect(EventArgs e) { + /// Debug.Log(gameServer.Id); + /// } + /// + /// + /// id of machine assigned by HappyFunTimes + public string Id { + get { + if (m_id == null) { + Debug.LogError("you can NOT read id before the game has connected"); + } + return m_id; + } + private set { + } + } + private Options m_options; private bool m_connected = false; private int m_totalPlayerCount = 0; + private int m_recvCount = 0; + private int m_sendCount = 0; + private int m_queueCount = 0; private bool m_gotMessages = false; private WebSocket m_socket; - private Dictionary m_players; + private Dictionary m_players; private List m_sendQueue; private Deserializer m_deserializer; private GameObject m_gameObject; private EventProcessor m_eventProcessor; private GameSystem m_gameSystem; + private Dictionary m_handlers; // handlers by command name + private MessageCmdDataCreator m_mcdc; + private string m_id = null; public class MessageToClient { public string cmd; // command 'server', 'update' - public int id; // id of client + public string id; // id of client public Dictionary data; }; + private class MessageGameStart { + public string id = ""; + }; + private class RelayServerCmd { - public RelayServerCmd(string _cmd, int _id, System.Object _data) { + public RelayServerCmd(string _cmd, string _id, object _data) { cmd = _cmd; id = _id; data = _data; } public string cmd; - public int id; - public System.Object data; + public string id; + public object data; } private void SocketOpened(object sender, System.EventArgs e) { @@ -139,11 +397,11 @@ private void SocketOpened(object sender, System.EventArgs e) { m_sendQueue.Clear(); // Inform the relayserver we're a server - SendCmd("server", -1, m_options); - - QueueEvent(delegate() { - OnConnect.Emit(this, new EventArgs()); - }); + try { + SendSysCmd("server", "-1", m_options); + } catch (Exception ex) { + Debug.LogException(ex); + } } private void SocketClosed(object sender, CloseEventArgs e) { @@ -162,14 +420,21 @@ private void SocketMessage(object sender, MessageEventArgs e) { Send("P"); return; } + if (m_options.showMessages) { + Debug.Log("r[" + (m_recvCount++) + "] " + e.Data); + } MessageToClient m = m_deserializer.Deserialize(e.Data); // TODO: make this a dict to callback - if (m.cmd.Equals("start")) { - StartPlayer(m.id, ""); + if (m.cmd.Equals("update")) { + UpdatePlayer(m.id, m.data); + } else if (m.cmd.Equals("upgame")) { + UpdateGame(m.id, m.data); + } else if (m.cmd.Equals("start")) { + StartPlayer(m.id, "", m.data); + } else if (m.cmd.Equals("gamestart")) { + StartGame(m.id, m.data); } else if (m.cmd.Equals("remove")) { RemovePlayer(m.id); - } else if (m.cmd.Equals("update")) { - UpdatePlayer(m.id, m.data); } else if (m.cmd.Equals("system")) { DoSysCommand(m.data); } else { @@ -201,13 +466,13 @@ private void Cleanup() }); while (m_players.Count > 0) { - Dictionary.Enumerator i = m_players.GetEnumerator(); + Dictionary.Enumerator i = m_players.GetEnumerator(); i.MoveNext(); RemovePlayer(i.Current.Key); } } - private void StartPlayer(int id, string name) { + private void StartPlayer(string id, string name, Dictionary data) { if (m_players.ContainsKey(id)) { return; } @@ -220,8 +485,8 @@ private void StartPlayer(int id, string name) { m_players[id] = player; QueueEvent(delegate() { // UGH! This is not thread safe because someone might add handler to OnPlayerConnect - // Odds or low though. - OnPlayerConnect.Emit(this, new PlayerConnectMessageArgs(player)); + // Odds are low though? + OnPlayerConnect.Emit(this, new PlayerConnectMessageArgs(player, name, data)); }); } @@ -229,7 +494,7 @@ private void DoSysCommand(Dictionary cmd) { m_gameSystem.HandleUnparsedCommand(cmd); } - private void UpdatePlayer(int id, Dictionary cmd) { + private void UpdatePlayer(string id, Dictionary cmd) { NetPlayer player; if (!m_players.TryGetValue(id, out player)) { return; @@ -237,7 +502,30 @@ private void UpdatePlayer(int id, Dictionary cmd) { player.SendUnparsedEvent(cmd); } - private void RemovePlayer(int id) { + private void StartGame(string id, Dictionary cmd) { + MessageGameStart data = m_deserializer.Deserialize(cmd); + m_id = data.id; + + QueueEvent(delegate() { + OnConnect.Emit(this, new EventArgs()); + }); + } + + private void UpdateGame(string id, Dictionary data) { + try { + MessageCmd cmd = m_deserializer.Deserialize(data); + CmdEventHandler handler; + if (!m_handlers.TryGetValue(cmd.cmd, out handler)) { + Debug.LogError("unhandled GameServer cmd: " + cmd.cmd); + return; + } + handler(this, cmd.data, data, id); + } catch (Exception ex) { + Debug.LogException(ex); + } + } + + private void RemovePlayer(string id) { NetPlayer player; if (!m_players.TryGetValue(id, out player)) { return; @@ -250,18 +538,59 @@ private void RemovePlayer(int id) { private void Send(string msg) { if (m_connected) { + if (m_options.showMessages) { + Debug.Log("q[" + (m_queueCount++) + "] " + msg); + } m_socket.Send(msg); } else { + if (m_options.showMessages) { + Debug.Log("s[" + (m_sendCount++) + "] " + msg); + } m_sendQueue.Add(msg); } } - // Only NetPlayer should call this. - public void SendCmd(string cmd, int id, System.Object data) { + public void SendSysCmd(string cmd, string id, object data) { var msg = new RelayServerCmd(cmd, id, data); string json = Serializer.Serialize(msg); Send(json); } + + // Only NetPlayer should call this. + public void SendCmd(string cmd, string name, MessageCmdData data, string id = "-1") { + MessageCmd msgCmd = new MessageCmd(name, data); + SendSysCmd(cmd, id, msgCmd); + } + + // Only NetPlayer should call this. + public void SendCmd(string cmd, MessageCmdData data, string id = "-1") { + string name = MessageCmdDataNameDB.GetCmdName(data.GetType()); + SendCmd(cmd, name, data, id); + } + + public void BroadcastCmd(string cmd, MessageCmdData data) { + SendCmd("broadcast", cmd, data); + } + + public void BroadcastCmd(MessageCmdData data) { + SendCmd("broadcast", data); + } + + public void SendCmdToGame(string id, string cmd, MessageCmdData data) { + SendCmd("peer", cmd, data, id); + } + + public void SendCmdToGame(string id, MessageCmdData data) { + SendCmd("peer", data, id); + } + + public void BroadcastCmdToGames(string cmd, MessageCmdData data) { + SendCmd("bcastToGames", cmd, data); + } + + public void BroadcastCmdToGames(MessageCmdData data) { + SendCmd("bcastToGames", data); + } }; } diff --git a/HappyFunTimes/GameSystem.cs b/HappyFunTimes/GameSystem.cs index 590d8bc..476a92d 100644 --- a/HappyFunTimes/GameSystem.cs +++ b/HappyFunTimes/GameSystem.cs @@ -42,7 +42,7 @@ private class MessageExit : MessageCmdData { private NetPlayer m_netPlayer; public GameSystem(GameServer server) { - m_netPlayer = new NetPlayer(server, -1); + m_netPlayer = new NetPlayer(server, "-1"); m_netPlayer.RegisterCmdHandler(OnExit); } diff --git a/HappyFunTimes/HappyFunTimes.csproj b/HappyFunTimes/HappyFunTimes.csproj index e470725..0265e2e 100644 --- a/HappyFunTimes/HappyFunTimes.csproj +++ b/HappyFunTimes/HappyFunTimes.csproj @@ -10,6 +10,7 @@ HappyFunTimes HappyFunTimes v3.5 + 0.2 True @@ -52,6 +53,7 @@ + \ No newline at end of file diff --git a/HappyFunTimes/MessageCmd.cs b/HappyFunTimes/MessageCmd.cs index 6259f6a..a348683 100644 --- a/HappyFunTimes/MessageCmd.cs +++ b/HappyFunTimes/MessageCmd.cs @@ -159,6 +159,10 @@ public void RegisterCreator(System.Type type) { m_creators[name] = new TypeBasedCreator(type); } + public void RegisterCreator(string name, System.Type type) { + m_creators[name] = new TypeBasedCreator(type); + } + public override object Create(Dictionary src, Dictionary parentSrc) { string typeName = (string)parentSrc["cmd"]; Creator creator; diff --git a/HappyFunTimes/NetPlayer.cs b/HappyFunTimes/NetPlayer.cs index b26b148..c50c3bc 100644 --- a/HappyFunTimes/NetPlayer.cs +++ b/HappyFunTimes/NetPlayer.cs @@ -36,8 +36,12 @@ namespace HappyFunTimes { +/// +/// This object represent the connections between a player's phone and this game. +/// public class NetPlayer { + public delegate void UntypedCmdEventHandler(Dictionary data); public delegate void TypedCmdEventHandler(T eventArgs) where T : MessageCmdData; private class CmdConverter where T : MessageCmdData @@ -46,7 +50,7 @@ public CmdConverter(TypedCmdEventHandler handler) { m_handler = handler; } - public void Callback(GameServer server, MessageCmdData data) { + public void Callback(GameServer server, MessageCmdData data, Dictionary dict) { server.QueueEvent(delegate() { m_handler((T)data); }); @@ -55,8 +59,27 @@ public void Callback(GameServer server, MessageCmdData data) { TypedCmdEventHandler m_handler; } - public NetPlayer(GameServer server, int id) { + private class UntypedCmdConverter { + public UntypedCmdConverter(UntypedCmdEventHandler handler) { + m_handler = handler; + } + + public void Callback(GameServer server, MessageCmdData data, Dictionary dict) { + server.QueueEvent(delegate() { + object mcd = null; + // dict is the MessageCmd. We want dict for the MessageCmdData inside the MessageCmd + // It might not exist + dict.TryGetValue("data", out mcd); + m_handler((Dictionary)mcd); + }); + } + + UntypedCmdEventHandler m_handler; + } + + public NetPlayer(GameServer server, string id) { m_server = server; + m_connected = true; m_id = id; m_handlers = new Dictionary(); m_deserializer = new Deserializer(); @@ -64,27 +87,132 @@ public NetPlayer(GameServer server, int id) { m_deserializer.RegisterCreator(m_mcdc); } + /// + /// Lets you register a command to be called when message comes in from this player. + /// + /// The callback you register must have a CmdName attribute. That attibute will determine + /// which event the callback gets dispatched for. + /// + /// + /// + /// [CmdName("color")] + /// private class MessageColor : MessageCmdData { + /// public string color = ""; // in CSS format rgb(r,g,b) + /// }; + /// + /// ... + /// netPlayer.RegisterCmdHandler(OnColor); + /// ... + /// void OnColor(MessageColor) { + /// Debug.Log("received msg with color: " + color); + /// } + /// + /// + /// Typed callback public void RegisterCmdHandler(TypedCmdEventHandler callback) where T : MessageCmdData { string name = MessageCmdDataNameDB.GetCmdName(typeof(T)); if (name == null) { throw new System.InvalidOperationException("no CmdNameAttribute on " + typeof(T).Name); } + RegisterCmdHandler(name, callback); + } + + /// + /// Lets you register a command to be called when a message comes in from this player. + /// + /// + /// + /// private class MessageColor : MessageCmdData { + /// public string color = ""; // in CSS format rgb(r,g,b) + /// }; + /// + /// ... + /// newPlayer.RegisterCmdHandler("color", OnColor); + /// ... + /// void OnColor(MessageColor) { + /// Debug.Log("received msg with color: " + color); + /// } + /// + /// + /// command to call callback for + /// Typed callback + public void RegisterCmdHandler(string name, TypedCmdEventHandler callback) where T : MessageCmdData { CmdConverter converter = new CmdConverter(callback); m_handlers[name] = converter.Callback; - m_mcdc.RegisterCreator(typeof(T)); + m_mcdc.RegisterCreator(name, typeof(T)); } + /// + /// Lets you register a command to be called when a message is sent from this player. + /// + /// This the most low-level basic version of RegisterCmdHandler. The function registered + /// will be called with a Dictionary with whatever data is passed in. + /// You can either pull the data out directly OR you can call Deserializer.Deserilize + /// with a type and have the data extracted for you. + /// + /// + /// + /// ... + /// netPlayer.RegisterCmdHandler("color", OnColor); + /// ... + /// void OnColor(Dictionary data) { + /// Debug.Log("received msg with color: " + data["color"]); + /// } + /// + /// or + /// + /// private class MessageColor : MessageCmdData { + /// public string color = ""; // in CSS format rgb(r,g,b) + /// }; + /// ... + /// gameServer.RegisterCmdHandler("color", OnColor); + /// ... + /// void OnColor(Dictionary data) { + /// Deserializer d = new Deserializer(); + /// MessageColor msg = d.Deserialize(data); + /// Debug.Log("received msg with color: " + msg.color); + /// } + /// + /// + /// command to call callback for + /// Typed callback + public void RegisterCmdHandler(string name, UntypedCmdEventHandler callback) { + UntypedCmdConverter converter = new UntypedCmdConverter(callback); + m_handlers[name] = converter.Callback; + m_mcdc.RegisterCreator(name, typeof(Dictionary)); + } /// This needs the server because messages need to be queued as they need to be delivered on anther thread. - private delegate void CmdEventHandler(GameServer server, MessageCmdData cmdData); + private delegate void CmdEventHandler(GameServer server, MessageCmdData cmdData, Dictionary dict); - public void SendCmd(MessageCmdData data) { // Make it Ob's name is the message. - string name = MessageCmdDataNameDB.GetCmdName(data.GetType()); - MessageCmd msgCmd = new MessageCmd(name, data); - m_server.SendCmd("client", m_id, msgCmd); + private void SendCmd(string cmd, MessageCmdData data) + { + if (m_connected) + { + m_server.SendCmd(cmd, data, m_id); + } } - public void SendUnparsedEvent(Dictionary data) { + /// + /// Sends a message to this player's phone + /// + /// The message. It must be derived from MessageCmdData and must have a + /// CmdName attribute + /// + /// + /// + /// + public void SendCmd(MessageCmdData data) { + SendCmd("client", data); + } + + public void SendUnparsedEvent(Dictionary data) + { + if (!m_connected) + { + return; + } + // If there are no handlers registered then the object using this NetPlayer // has not been instantiated yet. The issue is the GameSever makes a NetPlayer. // It then has to queue an event to start that player so that it can be started @@ -106,7 +234,7 @@ public void SendUnparsedEvent(Dictionary data) { Debug.LogError("unhandled NetPlayer cmd: " + cmd.cmd); return; } - handler(m_server, cmd.data); + handler(m_server, cmd.data, data); } catch (Exception ex) { Debug.LogException(ex); } @@ -117,10 +245,30 @@ public void Disconnect() { OnDisconnect(this, new EventArgs()); } + private class MessageSwitchGame : MessageCmdData { + public string gameId; + public MessageCmdData data; + + public MessageSwitchGame(string id, MessageCmdData d) { + gameId = id; + data = d; + } + } + + public void SwitchGame(string gameId, MessageCmdData data) + { + if (m_connected) + { + m_server.SendSysCmd("switchGame", m_id, new MessageSwitchGame(gameId, data)); + m_connected = false; + } + } + public event EventHandler OnDisconnect; private GameServer m_server; - private int m_id; + private string m_id; + private bool m_connected; private Dictionary m_handlers; // handlers by command name private Deserializer m_deserializer; private MessageCmdDataCreator m_mcdc; diff --git a/HappyFunTimes/PlayerConnectMessageArgs.cs b/HappyFunTimes/PlayerConnectMessageArgs.cs index 7c7bf6f..80e5e5f 100644 --- a/HappyFunTimes/PlayerConnectMessageArgs.cs +++ b/HappyFunTimes/PlayerConnectMessageArgs.cs @@ -30,15 +30,20 @@ */ using System; +using System.Collections.Generic; namespace HappyFunTimes { public class PlayerConnectMessageArgs : EventArgs { - public PlayerConnectMessageArgs(NetPlayer _netPlayer) { + public PlayerConnectMessageArgs(NetPlayer _netPlayer, string _name, Dictionary _data) { netPlayer = _netPlayer; + name = _name; + data = _data; } public NetPlayer netPlayer; + public string name; + public Dictionary data; }; } diff --git a/HappyFunTimes/PlayerSpawner.cs b/HappyFunTimes/PlayerSpawner.cs index 2aba94f..e2d80cd 100644 --- a/HappyFunTimes/PlayerSpawner.cs +++ b/HappyFunTimes/PlayerSpawner.cs @@ -9,6 +9,7 @@ namespace HappyFunTimes { public class SpawnInfo { public NetPlayer netPlayer; public string name; + public Dictionary data; }; [AddComponentMenu("HappyFunTimes/PlayerSpawner")] @@ -17,10 +18,21 @@ public class PlayerSpawner : MonoBehaviour public GameObject prefabToSpawnForPlayer; public string gameId = ""; public bool showMessages = false; + public bool allowMultipleGames; + + public GameServer server + { + get + { + return m_server; + } + } void StartConnection() { GameServer.Options options = new GameServer.Options(); options.gameId = gameId; + options.allowMultipleGames = allowMultipleGames; + options.showMessages = showMessages; m_server = new GameServer(options, gameObject); @@ -37,6 +49,7 @@ void StartNewPlayer(object sender, PlayerConnectMessageArgs e) SpawnInfo spawnInfo = new SpawnInfo(); spawnInfo.netPlayer = e.netPlayer; spawnInfo.name = "Player" + (++m_count); + spawnInfo.data = e.data; gameObject.SendMessage("InitializeNetPlayer", spawnInfo); } @@ -72,6 +85,14 @@ void OnApplicationExit() Cleanup(); } + public GameServer GameServer { + get { + return m_server; + } + private set { + } + } + private GameServer m_server; private int m_count; }; diff --git a/HappyFunTimes/libs/DeJson.dll b/HappyFunTimes/libs/DeJson.dll index 9950e33a958c69551fde93bcb3578b1fd1581d5f..f5d5c377f5001195d67a8972ca2819f8a22ba03e 100755 GIT binary patch literal 14848 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⁢erTT6x{hTmk+oq;|}&9bZ&d<6jc3)wGJP09`)YqCfvu zGAN@Sz{qOu!Q2>AD^)$0c332DY>dBB;-KPt6>)4aucwE<0p%GI?;HMbFBr$aC+hOW P{SVj7|F`Ua#{>TjgClmE literal 12800 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 +// This code was generated by a tool. +// Mono Runtime Version: 4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +using System; +using UnityEditor; +using UnityEngine; + +namespace HappyFunTimesEditor +{ + + public class HFTWindow : EditorWindow + { + string myString = "Hello World"; + bool groupEnabled; + bool myBool = true; + float myFloat = 1.23f; + + [MenuItem("Window/HappyFunTimes")] + public static void ShowWindow() + { + //Show existing window instance. If one doesn't exist, make one. + EditorWindow.GetWindow(typeof(HFTWindow)); + } + + void OnGUI() + { + GUILayout.Label ("Base Settings", EditorStyles.boldLabel); + myString = EditorGUILayout.TextField ("Text Field", myString); + + groupEnabled = EditorGUILayout.BeginToggleGroup ("Optional Settings", groupEnabled); + myBool = EditorGUILayout.Toggle ("Toggle", myBool); + myFloat = EditorGUILayout.Slider ("Slider", myFloat, -3, 3); + EditorGUILayout.EndToggleGroup (); + } + } +} + diff --git a/HappyFunTimesEditor/HappyFunTimesEditor.csproj b/HappyFunTimesEditor/HappyFunTimesEditor.csproj new file mode 100644 index 0000000..a7c4724 --- /dev/null +++ b/HappyFunTimesEditor/HappyFunTimesEditor.csproj @@ -0,0 +1,50 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {97F1A61A-1B41-4878-BA21-9A029AA49791} + Library + HappyFunTimesEditor + HappyFunTimesEditor + v3.5 + 0.2 + + + True + full + False + bin\Debug + DEBUG; + prompt + 4 + False + + + none + True + bin\Release + prompt + 4 + False + + + + + ..\..\..\..\..\Applications\Unity\Unity.app\Contents\Frameworks\Managed\UnityEngine.dll + + + ..\HappyFunTimes\libs\DeJson.dll + + + ..\..\..\..\..\Applications\Unity\Unity.app\Contents\Frameworks\Managed\UnityEditor.dll + + + + + + + + \ No newline at end of file