diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c55a84 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +node_modules +bin +*.userprefs +.DS_Store +*~ +*.swp +*.bat +*.bak +temp +.mayaSwatches +.git +.svn +.hg +*.pyc +*.orig +.project +*.meta +*.vpj +*.vpw +*.vpwhistu +*.vtg + diff --git a/HappyFunTimes.sln b/HappyFunTimes.sln new file mode 100644 index 0000000..31b7a22 --- /dev/null +++ b/HappyFunTimes.sln @@ -0,0 +1,20 @@ + +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 +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {554A0027-C03D-4D04-946D-2A34E43CC586}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = HappyFunTimes\HappyFunTimes.csproj + EndGlobalSection +EndGlobal diff --git a/HappyFunTimes/AssemblyInfo.cs b/HappyFunTimes/AssemblyInfo.cs new file mode 100644 index 0000000..43853d9 --- /dev/null +++ b/HappyFunTimes/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("HappyFunTimes")] +[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/HappyFunTimes/ClientMessageArgs.cs b/HappyFunTimes/ClientMessageArgs.cs new file mode 100644 index 0000000..50af9c1 --- /dev/null +++ b/HappyFunTimes/ClientMessageArgs.cs @@ -0,0 +1,41 @@ +/* + * 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; + +namespace HappyFunTimes { + +public class ClientMessageArgs : EventArgs { + public MessageCmdData cmdData; +}; + +} + diff --git a/HappyFunTimes/EventProcessor.cs b/HappyFunTimes/EventProcessor.cs new file mode 100644 index 0000000..0e32874 --- /dev/null +++ b/HappyFunTimes/EventProcessor.cs @@ -0,0 +1,83 @@ +/* + * 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 UnityEngine; +using System; +using System.Collections.Generic; + +namespace HappyFunTimes { + +// This is needed to send the events to on the correct thread. +public class EventProcessor : MonoBehaviour { + + public void QueueEvent(Action action) { + lock(m_queueLock) { + m_queuedEvents.Add(action); + } + } + + void Start() { + m_queuedEvents = new List(); + m_executingEvents = new List(); + } + + void Update() { + MoveQueuedEventsToExecuting(); + + if (m_executingEvents != null) { + while (m_executingEvents.Count > 0) { + Action e = m_executingEvents[0]; + m_executingEvents.RemoveAt(0); + e(); + } + } + } + + private void MoveQueuedEventsToExecuting() { + lock(m_queueLock) { + if (m_executingEvents != null) { + while (m_queuedEvents.Count > 0) { + Action e = m_queuedEvents[0]; + m_executingEvents.Add(e); + m_queuedEvents.RemoveAt(0); + } + } + } + } + + private System.Object m_queueLock = new System.Object(); + private List m_queuedEvents; + private List m_executingEvents; +} + + +} // namespace HappyFunTimes + diff --git a/HappyFunTimes/GameServer.cs b/HappyFunTimes/GameServer.cs new file mode 100644 index 0000000..31cabb0 --- /dev/null +++ b/HappyFunTimes/GameServer.cs @@ -0,0 +1,239 @@ +/* + * 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 UnityEngine; +using DeJson; +using System; +using System.Collections.Generic; +using WebSocketSharp; + +namespace HappyFunTimes { + +public class GameServer { + + public class Options { + public string gameId; + public string controllerUrl; // not used! + public bool showMessages; + }; + + public GameServer(Options options, GameObject gameObject) { + m_options = options; + m_gameObject = gameObject; + m_players = new Dictionary(); + m_sendQueue = new List(); + m_deserializer = new Deserializer(); + m_gameSystem = new GameSystem(this); + + m_eventProcessor = m_gameObject.AddComponent(); + } + + public void Init() { + Init("ws://localhost:8080"); + } + + public void Init(string url/* = "ws://localhost:8080" */) { + + if (m_socket == null) { + m_socket = new WebSocket(url); + m_socket.OnOpen += SocketOpened; + m_socket.OnMessage += SocketMessage; + m_socket.OnClose += SocketClosed; + m_socket.OnError += SocketError; + + m_socket.Connect (); + } + + } + + public void Close() { + if (m_connected) { + m_connected = false; + m_socket.Close(); + m_socket = null; + } + } + + public void QueueEvent(Action action) { + m_eventProcessor.QueueEvent(action); + } + + public event EventHandler OnPlayerConnect; + public event EventHandler OnConnect; + public event EventHandler OnDisconnect; + + private Options m_options; + private bool m_connected = false; + private int m_totalPlayerCount = 0; + private WebSocket m_socket; + private Dictionary m_players; + private List m_sendQueue; + private Deserializer m_deserializer; + private GameObject m_gameObject; + private EventProcessor m_eventProcessor; + private GameSystem m_gameSystem; + + public class MessageToClient { + public string cmd; // command 'server', 'update' + public int id; // id of client + public Dictionary data; + }; + + private class RelayServerCmd { + public RelayServerCmd(string _cmd, int _id, System.Object _data) { + cmd = _cmd; + id = _id; + data = _data; + } + + public string cmd; + public int id; + public System.Object data; + } + + private void SocketOpened(object sender, System.EventArgs e) { + //invoke when socket opened + Debug.Log("socket opened"); + m_connected = true; + + List.Enumerator i = m_sendQueue.GetEnumerator(); + while (i.MoveNext()) { + m_socket.Send(i.Current); + } + m_sendQueue.Clear(); + + // Inform the relayserver we're a server + SendCmd("server", -1, m_options); + + OnConnect.Emit(this, new EventArgs()); + } + + private void SocketClosed(object sender, CloseEventArgs e) { + //invoke when socket closed + Debug.Log("connection closed"); + m_connected = false; + + OnDisconnect.Emit(this, new EventArgs()); + while (m_players.Count > 0) { + Dictionary.Enumerator i = m_players.GetEnumerator(); + i.MoveNext(); + RemovePlayer(i.Current.Key); + } + } + + private void SocketMessage(object sender, MessageEventArgs e) { + //invoke when socket message + if ( e!= null && e.Type == Opcode.Text) { + try { + MessageToClient m = m_deserializer.Deserialize(e.Data); + // TODO: make this a dict to callback + if (m.cmd.Equals("start")) { + StartPlayer(m.id, ""); + } 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 { + Debug.LogError("unknown client message: " + m.cmd); + } + } catch (Exception ex) { + Debug.LogException(ex); // TODO: Add object if possible + return; + } + } + } + + private void SocketError(object sender, ErrorEventArgs e) { + //invoke when socket error + Debug.Log("socket error: " + e.Message); + m_connected = false; + } + + private void StartPlayer(int id, string name) { + if (m_players.ContainsKey(id)) { + return; + } + + if (string.IsNullOrEmpty(name)) { + name = "Player" + (++m_totalPlayerCount); + } + + NetPlayer player = new NetPlayer(this, id); + m_players[id] = player; + m_eventProcessor.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)); + }); + } + + private void DoSysCommand(Dictionary cmd) { + m_gameSystem.HandleUnparsedCommand(cmd); + } + + private void UpdatePlayer(int id, Dictionary cmd) { + NetPlayer player; + if (!m_players.TryGetValue(id, out player)) { + return; + } + player.SendUnparsedEvent(cmd); + } + + private void RemovePlayer(int id) { + NetPlayer player; + if (!m_players.TryGetValue(id, out player)) { + return; + } + m_eventProcessor.QueueEvent(delegate() { + player.Disconnect(); + }); + m_players.Remove(id); + } + + private void Send(string msg) { + if (m_connected) { + m_socket.Send(msg); + } else { + m_sendQueue.Add(msg); + } + } + + // Only NetPlayer should call this. + public void SendCmd(string cmd, int id, System.Object data) { + var msg = new RelayServerCmd(cmd, id, data); + string json = Serializer.Serialize(msg); + Send(json); + } +}; + +} diff --git a/HappyFunTimes/GameSystem.cs b/HappyFunTimes/GameSystem.cs new file mode 100644 index 0000000..90daf21 --- /dev/null +++ b/HappyFunTimes/GameSystem.cs @@ -0,0 +1,60 @@ +/* + * 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; +using UnityEngine; + +namespace HappyFunTimes { + +public class GameSystem { + + [CmdName("exit")] + private class MessageExit : MessageCmdData { + }; + + private NetPlayer m_netPlayer; + + public GameSystem(GameServer server) { + m_netPlayer = new NetPlayer(server, -1); + + m_netPlayer.RegisterCmdHandler(OnExit); + } + + private void OnExit(MessageExit data) { + Application.Quit(); + } + + public void HandleUnparsedCommand(Dictionary cmd) { + m_netPlayer.SendUnparsedEvent(cmd); + } + +} + +} // namespace HappyFunTimes diff --git a/HappyFunTimes/HappyFunTimes.csproj b/HappyFunTimes/HappyFunTimes.csproj new file mode 100644 index 0000000..3abb84c --- /dev/null +++ b/HappyFunTimes/HappyFunTimes.csproj @@ -0,0 +1,57 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {554A0027-C03D-4D04-946D-2A34E43CC586} + Library + HappyFunTimes + HappyFunTimes + v3.5 + + + 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 + + + libs\DeJson.dll + + + libs\websocket-sharp.dll + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HappyFunTimes/MessageCmd.cs b/HappyFunTimes/MessageCmd.cs new file mode 100644 index 0000000..6259f6a --- /dev/null +++ b/HappyFunTimes/MessageCmd.cs @@ -0,0 +1,179 @@ +/* + * 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; +using System.Collections.Generic; +using DeJson; + +namespace HappyFunTimes { + +public class MessageCmd { + public MessageCmd() { + cmd = ""; + data = null; + } + + public MessageCmd(string _cmd, MessageCmdData _data) { + cmd = _cmd; + data = _data; + } + + public string cmd; // command to emit + public MessageCmdData data; // data for command +}; + +/// +/// Base class for all message data. Be sure to set the CmdName attribute +/// +/// +/// +/// +/// [CmdName("playerHit")] +/// public class PlayerHit : MessageCmdData +/// public int numHitPoints; +/// } +/// +/// +public class MessageCmdData { + + public static string GetCmdName(System.Type type) { + //Querying Class Attributes + foreach (Attribute attr in type.GetCustomAttributes(true)) { + CmdNameAttribute cmdNameAttr = attr as CmdNameAttribute; + if (cmdNameAttr != null) { + return cmdNameAttr.CmdName; + } + } + return null; + } + +} + +// This is just to cache the command names since iterating over fields seems like it +// would be slow. Probably a pre-mature optimization. +public class MessageCmdDataNameDB { + + public static string GetCmdName(System.Type type) { + string name; + if (!m_typeToCommandName.TryGetValue(type, out name)) { + name = MessageCmdData.GetCmdName(type); + m_typeToCommandName[type] = name; + } + return name; + } + + private static Dictionary m_typeToCommandName = new Dictionary(); +} + +/// +/// Attibute used to associate a command name with a class. +/// In C# land we could just use the name of the class through reflection +/// but these names originate in JavaScript where there is no class name. +/// +[AttributeUsage(AttributeTargets.Class)] +public class CmdNameAttribute : System.Attribute +{ + public readonly string CmdName; + + public CmdNameAttribute(string cmdName) + { + this.CmdName = cmdName; + } +} + +// Deserialize from base MessageCmdData into a concrete derived type. +public class MessageCmdDataCreator : Deserializer.CustomCreator { + + // base class that goes in dictionary of names -> creators + public abstract class Creator { + public abstract object Create(); + } + + // concrete creators + public class TypedCreator : Creator where T : new() { + public override object Create() { + return new T(); + } + } + + public class TypeBasedCreator : Creator { + public TypeBasedCreator(System.Type type) { + m_type = type; + } + + public override object Create() { + return Activator.CreateInstance(m_type); + } + + private System.Type m_type; + } + + public MessageCmdDataCreator() { + m_creators = new Dictionary(); + } + + public void RegisterCreator() where T : new() { + string name = MessageCmdData.GetCmdName(typeof(T)); + if (name == null) { + System.InvalidOperationException ex = new System.InvalidOperationException("missing CmdNameAttribute"); + throw ex; + } + + m_creators[name] = new TypedCreator(); + } + + public void RegisterCreator(System.Type type) { + string name = MessageCmdData.GetCmdName(type); + if (name == null) { + System.InvalidOperationException ex = new System.InvalidOperationException("missing CmdNameAttribute"); + throw ex; + } + m_creators[name] = new TypeBasedCreator(type); + } + + public override object Create(Dictionary src, Dictionary parentSrc) { + string typeName = (string)parentSrc["cmd"]; + Creator creator; + if (m_creators.TryGetValue(typeName, out creator)) { + return creator.Create(); + } + return null; + } + + public override System.Type TypeToCreate() { + return typeof(MessageCmdData); + } + + Dictionary m_creators; +}; + + +} // namespace HappyFunTimes diff --git a/HappyFunTimes/NetPlayer.cs b/HappyFunTimes/NetPlayer.cs new file mode 100644 index 0000000..b26b148 --- /dev/null +++ b/HappyFunTimes/NetPlayer.cs @@ -0,0 +1,131 @@ +/* + * 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 UnityEngine; +using DeJson; +using System; +using System.Collections.Generic; + +namespace HappyFunTimes { + +public class NetPlayer { + + public delegate void TypedCmdEventHandler(T eventArgs) where T : MessageCmdData; + + private class CmdConverter where T : MessageCmdData + { + public CmdConverter(TypedCmdEventHandler handler) { + m_handler = handler; + } + + public void Callback(GameServer server, MessageCmdData data) { + server.QueueEvent(delegate() { + m_handler((T)data); + }); + } + + TypedCmdEventHandler m_handler; + } + + public NetPlayer(GameServer server, int id) { + m_server = server; + m_id = id; + m_handlers = new Dictionary(); + m_deserializer = new Deserializer(); + m_mcdc = new MessageCmdDataCreator(); + m_deserializer.RegisterCreator(m_mcdc); + } + + 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); + } + CmdConverter converter = new CmdConverter(callback); + m_handlers[name] = converter.Callback; + m_mcdc.RegisterCreator(typeof(T)); + } + + + /// 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); + + 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); + } + + public void SendUnparsedEvent(Dictionary data) { + // 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 + // on another thread. But, before that event has triggered other messages might + // come through. So, if there are no handlers then we add an event to run the + // command later. It's the same queue that will birth the object that needs the + // message. + if (m_handlers.Count == 0) { + m_server.QueueEvent(delegate() { + SendUnparsedEvent(data); + }); + return; + } + + try { + MessageCmd cmd = m_deserializer.Deserialize(data); + CmdEventHandler handler; + if (!m_handlers.TryGetValue(cmd.cmd, out handler)) { + Debug.LogError("unhandled NetPlayer cmd: " + cmd.cmd); + return; + } + handler(m_server, cmd.data); + } catch (Exception ex) { + Debug.LogException(ex); + } + + } + + public void Disconnect() { + OnDisconnect(this, new EventArgs()); + } + + public event EventHandler OnDisconnect; + + private GameServer m_server; + private int m_id; + 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 new file mode 100644 index 0000000..7c7bf6f --- /dev/null +++ b/HappyFunTimes/PlayerConnectMessageArgs.cs @@ -0,0 +1,45 @@ +/* + * 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; + +namespace HappyFunTimes { + +public class PlayerConnectMessageArgs : EventArgs { + public PlayerConnectMessageArgs(NetPlayer _netPlayer) { + netPlayer = _netPlayer; + } + + public NetPlayer netPlayer; +}; + +} + diff --git a/HappyFunTimes/PlayerDisconnectMessageArgs.cs b/HappyFunTimes/PlayerDisconnectMessageArgs.cs new file mode 100644 index 0000000..b1420d6 --- /dev/null +++ b/HappyFunTimes/PlayerDisconnectMessageArgs.cs @@ -0,0 +1,44 @@ +/* + * 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; + +namespace HappyFunTimes { + +public class PlayerDisconnectMessageArgs : EventArgs { + public PlayerDisconnectMessageArgs(NetPlayer _netPlayer) { + netPlayer = _netPlayer; + } + public NetPlayer netPlayer; +}; + +} + diff --git a/HappyFunTimes/PlayerSpawner.cs b/HappyFunTimes/PlayerSpawner.cs new file mode 100644 index 0000000..9f4b7cd --- /dev/null +++ b/HappyFunTimes/PlayerSpawner.cs @@ -0,0 +1,76 @@ +using UnityEngine; +using System; +using System.Collections; +using System.Collections.Generic; +using HappyFunTimes; + +namespace HappyFunTimes { + +public class SpawnInfo { + public NetPlayer netPlayer; + public string name; +}; + +[AddComponentMenu("HappyFunTimes/PlayerSpawner")] +public class PlayerSpawner : MonoBehaviour +{ + public GameObject prefabToSpawnForPlayer; + public string gameId = ""; + public bool showMessages = false; + + void StartConnection() { + GameServer.Options options = new GameServer.Options(); + options.gameId = gameId; + + m_server = new GameServer(options, gameObject); + m_server.Init(); + + m_server.OnPlayerConnect += StartNewPlayer; + m_server.OnConnect += Connected; + m_server.OnDisconnect += Disconnected; + } + + void StartNewPlayer(object sender, PlayerConnectMessageArgs e) + { + GameObject gameObject = (GameObject)Instantiate(prefabToSpawnForPlayer); + SpawnInfo spawnInfo = new SpawnInfo(); + spawnInfo.netPlayer = e.netPlayer; + spawnInfo.name = "Player" + (++m_count); + gameObject.SendMessage("InitalizeNetPlayer", spawnInfo); + } + + void Start () + { + StartConnection(); + } + + void Connected(object sender, EventArgs e) + { + } + + void Disconnected(object sender, EventArgs e) + { + } + + void Cleanup() + { + if (m_server != null) { + m_server.Close(); + } + } + + void OnDestroy() + { + Cleanup(); + } + + void OnApplicationExit() + { + Cleanup(); + } + + private GameServer m_server; + private int m_count; +}; + +} // namespace HappyFunTimes diff --git a/HappyFunTimes/libs/DeJson.dll b/HappyFunTimes/libs/DeJson.dll new file mode 100755 index 0000000..9950e33 Binary files /dev/null and b/HappyFunTimes/libs/DeJson.dll differ diff --git a/HappyFunTimes/libs/websocket-sharp.dll b/HappyFunTimes/libs/websocket-sharp.dll new file mode 100755 index 0000000..58545e2 Binary files /dev/null and b/HappyFunTimes/libs/websocket-sharp.dll differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..83c7330 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +HappyFunTimes-Unity3D +===================== + +Unity3D libraries for HappyFunTimes. + +It's probably easist to download this from the asset store but otherwise you can build it from source +here. + +