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 9950e33..f5d5c37 100755
Binary files a/HappyFunTimes/libs/DeJson.dll and b/HappyFunTimes/libs/DeJson.dll differ
diff --git a/HappyFunTimesEditor/AssemblyInfo.cs b/HappyFunTimesEditor/AssemblyInfo.cs
new file mode 100644
index 0000000..cc4aa9a
--- /dev/null
+++ b/HappyFunTimesEditor/AssemblyInfo.cs
@@ -0,0 +1,27 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("HappyFunTimesEditor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Gregg Tavares")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
diff --git a/HappyFunTimesEditor/HFTWindow.cs b/HappyFunTimesEditor/HFTWindow.cs
new file mode 100644
index 0000000..42a4674
--- /dev/null
+++ b/HappyFunTimesEditor/HFTWindow.cs
@@ -0,0 +1,43 @@
+// ------------------------------------------------------------------------------
+//
+// 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