├── .gitignore ├── README.md ├── dist ├── SimpleJson.dll └── pomelo-dotnetClient.dll ├── lib └── SimpleJson.dll ├── pomelo-dotnetClient.sln ├── pomelo-dotnetClient ├── pomelo-dotnetClient.csproj └── src │ ├── client │ ├── EventManager.cs │ ├── PomeloClient.cs │ └── test │ │ └── ClientTest.cs │ ├── protobuf │ ├── Decoder.cs │ ├── Encoder.cs │ ├── MsgDecoder.cs │ ├── MsgEncoder.cs │ ├── Protobuf.cs │ ├── test │ │ ├── CodecTest.cs │ │ └── ProtobufTest.cs │ └── util │ │ └── Util.cs │ ├── protocol │ ├── HandShakeService.cs │ ├── HeartBeatService.cs │ ├── Message.cs │ ├── MessageProtocol.cs │ ├── MessageType.cs │ ├── Package.cs │ ├── PackageProtocol.cs │ ├── PackageTypes.cs │ ├── Protocol.cs │ └── ProtocolState.cs │ └── transport │ ├── TransportState.cs │ ├── Transporter.cs │ └── test │ └── TransportTest.cs └── test ├── Test.cs ├── json ├── msg.json ├── protos.json ├── rootMsg.json └── rootProtos.json └── test.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.log 5 | *.userprefs 6 | *.pdb 7 | 8 | # directories 9 | /pomelo-dotnetClient/obj 10 | /pomelo-dotnetClient/bin 11 | /test/obj 12 | /test/bin -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pomelo-unityclient-socket 2 | ============================= 3 | This is the pomelo dotnet client, support pomelo 0.3 and the new communicate protocol.It is based on native socket. 4 | The project is based on some libraries as follows: 5 | 6 | * [simple-json](https://github.com/facebook-csharp-sdk/simple-json) An open source json library 7 | 8 | ## Demo 9 | 10 | * [Unity3D demo](https://github.com/NetEase/pomelo-unitychat-socket) A pomelo-chat client use unity 3D. 11 | * [dotnet demo](https://github.com/NetEase/pomelo-dotnetchat-console) A pomelo-chat client use console and write by c#. 12 | 13 | ## How to use 14 | To use the dotnetClient, just include ./dist/*.dll in your project. 15 | 16 | ## API 17 | 18 | Initialize pomelo client 19 | 20 | ```c# 21 | using namespace Pomelo.DotNetClient 22 | string host="127.0.0.1";//(www.xxx.com/127.0.0.1/::1/localhost etc.) 23 | int port=3014; 24 | PomeloClient pclient = new PomeloClient(); 25 | 26 | //listen on network state changed event 27 | pclient.NetWorkStateChangedEvent += (state) => 28 | { 29 | Console.WriteLine(state); 30 | }; 31 | 32 | pclient.initClient(host, port, () => 33 | { 34 | //The user data is the handshake user params 35 | JsonObject user = new JsonObject(); 36 | pclient.connect(user, data => 37 | { 38 | //process handshake call back data 39 | }); 40 | }); 41 | 42 | ``` 43 | 44 | Use request and response 45 | ```c# 46 | pclient.request(route, message, (data)=>{ 47 | //process the data 48 | }); 49 | ``` 50 | 51 | Notify server without response 52 | 53 | ```c# 54 | pclient.notify(route, messge); 55 | ``` 56 | 57 | Add event listener, process broadcast message 58 | ```c# 59 | pclient.on(route, (data)=>{ 60 | //process the data 61 | }); 62 | ``` 63 | Disconnect the client. 64 | ```c# 65 | pclient.disconnect(); 66 | ``` 67 | 68 | protobuf surpported type:`uInt32, int32, sInt32, float, double, string.` 69 | 70 | The dotnet client support dictionary compress and protbuf encode. The use these function, just set the flag at server side. 71 | ##License 72 | (The MIT License) 73 | 74 | Copyright (c) 2012-2013 NetEase, Inc. and other contributors 75 | 76 | Permission is hereby granted, free of charge, to any person obtaining a 77 | copy of this software and associated documentation files (the 'Software'), 78 | to deal in the Software without restriction, including without limitation 79 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 80 | and/or sell copies of the Software, and to permit persons to whom the 81 | Software is furnished to do so, subject to the following conditions: 82 | 83 | The above copyright notice and this permission notice shall be included in 84 | all copies or substantial portions of the Software. 85 | 86 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 87 | -------------------------------------------------------------------------------- /dist/SimpleJson.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetEase/pomelo-unityclient-socket/e4a3b70593839204e91a280f27a3a092d1d18a2b/dist/SimpleJson.dll -------------------------------------------------------------------------------- /dist/pomelo-dotnetClient.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetEase/pomelo-unityclient-socket/e4a3b70593839204e91a280f27a3a092d1d18a2b/dist/pomelo-dotnetClient.dll -------------------------------------------------------------------------------- /lib/SimpleJson.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetEase/pomelo-unityclient-socket/e4a3b70593839204e91a280f27a3a092d1d18a2b/lib/SimpleJson.dll -------------------------------------------------------------------------------- /pomelo-dotnetClient.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pomelo-dotnetClient", "pomelo-dotnetClient\pomelo-dotnetClient.csproj", "{0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{0FFBA04C-F690-41DB-A795-BD6EC44C04AA}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|Mixed Platforms = Debug|Mixed Platforms 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|Mixed Platforms = Release|Mixed Platforms 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Debug|Any CPU.ActiveCfg = Debug|x86 21 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 22 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Debug|Mixed Platforms.Build.0 = Debug|x86 23 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Debug|x86.ActiveCfg = Debug|x86 24 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Debug|x86.Build.0 = Debug|x86 25 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Release|Any CPU.ActiveCfg = Release|x86 26 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Release|Mixed Platforms.ActiveCfg = Release|x86 27 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Release|Mixed Platforms.Build.0 = Release|x86 28 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Release|x86.ActiveCfg = Release|x86 29 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA}.Release|x86.Build.0 = Release|x86 30 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 33 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 34 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Debug|x86.ActiveCfg = Debug|Any CPU 35 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 38 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Release|Mixed Platforms.Build.0 = Release|Any CPU 39 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA}.Release|x86.ActiveCfg = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(MonoDevelopProperties) = preSolution 45 | StartupItem = pomelo-dotnetClient\pomelo-dotnetClient.csproj 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /pomelo-dotnetClient/pomelo-dotnetClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 10.0.0 7 | 2.0 8 | {0752F53D-46EF-44E2-AAA9-F2FAB9F0C4FA} 9 | Library 10 | pomelodotnetClient 11 | pomelo-dotnetClient 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug 18 | DEBUG; 19 | prompt 20 | 4 21 | x86 22 | false 23 | 24 | 25 | none 26 | true 27 | ..\dist\ 28 | prompt 29 | 4 30 | x86 31 | false 32 | 33 | 34 | 35 | 36 | ..\lib\SimpleJson.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/client/EventManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using SimpleJson; 5 | 6 | namespace Pomelo.DotNetClient 7 | { 8 | public class EventManager : IDisposable 9 | { 10 | private Dictionary> callBackMap; 11 | private Dictionary>> eventMap; 12 | 13 | public EventManager() 14 | { 15 | this.callBackMap = new Dictionary>(); 16 | this.eventMap = new Dictionary>>(); 17 | } 18 | 19 | //Adds callback to callBackMap by id. 20 | public void AddCallBack(uint id, Action callback) 21 | { 22 | if (id > 0 && callback != null) 23 | { 24 | this.callBackMap.Add(id, callback); 25 | } 26 | } 27 | 28 | /// 29 | /// Invoke the callback when the server return messge . 30 | /// 31 | /// 32 | /// Pomelo message. 33 | /// 34 | public void InvokeCallBack(uint id, JsonObject data) 35 | { 36 | if (!callBackMap.ContainsKey(id)) return; 37 | callBackMap[id].Invoke(data); 38 | } 39 | 40 | //Adds the event to eventMap by name. 41 | public void AddOnEvent(string eventName, Action callback) 42 | { 43 | List> list = null; 44 | if (this.eventMap.TryGetValue(eventName, out list)) 45 | { 46 | list.Add(callback); 47 | } 48 | else 49 | { 50 | list = new List>(); 51 | list.Add(callback); 52 | this.eventMap.Add(eventName, list); 53 | } 54 | } 55 | 56 | /// 57 | /// If the event exists,invoke the event when server return messge. 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | public void InvokeOnEvent(string route, JsonObject msg) 64 | { 65 | if (!this.eventMap.ContainsKey(route)) return; 66 | 67 | List> list = eventMap[route]; 68 | foreach (Action action in list) action.Invoke(msg); 69 | } 70 | 71 | // Dispose() calls Dispose(true) 72 | public void Dispose() 73 | { 74 | Dispose(true); 75 | GC.SuppressFinalize(this); 76 | } 77 | 78 | // The bulk of the clean-up code is implemented in Dispose(bool) 79 | protected void Dispose(bool disposing) 80 | { 81 | this.callBackMap.Clear(); 82 | this.eventMap.Clear(); 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/client/PomeloClient.cs: -------------------------------------------------------------------------------- 1 | using SimpleJson; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Threading; 7 | 8 | namespace Pomelo.DotNetClient 9 | { 10 | /// 11 | /// network state enum 12 | /// 13 | public enum NetWorkState 14 | { 15 | [Description("initial state")] 16 | CLOSED, 17 | 18 | [Description("connecting server")] 19 | CONNECTING, 20 | 21 | [Description("server connected")] 22 | CONNECTED, 23 | 24 | [Description("disconnected with server")] 25 | DISCONNECTED, 26 | 27 | [Description("connect timeout")] 28 | TIMEOUT, 29 | 30 | [Description("netwrok error")] 31 | ERROR 32 | } 33 | 34 | public class PomeloClient : IDisposable 35 | { 36 | /// 37 | /// netwrok changed event 38 | /// 39 | public event Action NetWorkStateChangedEvent; 40 | 41 | 42 | private NetWorkState netWorkState = NetWorkState.CLOSED; //current network state 43 | 44 | private EventManager eventManager; 45 | private Socket socket; 46 | private Protocol protocol; 47 | private bool disposed = false; 48 | private uint reqId = 1; 49 | 50 | private ManualResetEvent timeoutEvent = new ManualResetEvent(false); 51 | private int timeoutMSec = 8000; //connect timeout count in millisecond 52 | 53 | public PomeloClient() 54 | { 55 | } 56 | 57 | /// 58 | /// initialize pomelo client 59 | /// 60 | /// server name or server ip (www.xxx.com/127.0.0.1/::1/localhost etc.) 61 | /// server port 62 | /// socket successfully connected callback(in network thread) 63 | public void initClient(string host, int port, Action callback = null) 64 | { 65 | timeoutEvent.Reset(); 66 | eventManager = new EventManager(); 67 | NetWorkChanged(NetWorkState.CONNECTING); 68 | 69 | IPAddress ipAddress = null; 70 | 71 | try 72 | { 73 | IPAddress[] addresses = Dns.GetHostEntry(host).AddressList; 74 | foreach (var item in addresses) 75 | { 76 | if (item.AddressFamily == AddressFamily.InterNetwork) 77 | { 78 | ipAddress = item; 79 | break; 80 | } 81 | } 82 | } 83 | catch (Exception e) 84 | { 85 | NetWorkChanged(NetWorkState.ERROR); 86 | return; 87 | } 88 | 89 | if (ipAddress == null) 90 | { 91 | throw new Exception("can not parse host : " + host); 92 | } 93 | 94 | this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 95 | IPEndPoint ie = new IPEndPoint(ipAddress, port); 96 | 97 | socket.BeginConnect(ie, new AsyncCallback((result) => 98 | { 99 | try 100 | { 101 | this.socket.EndConnect(result); 102 | this.protocol = new Protocol(this, this.socket); 103 | NetWorkChanged(NetWorkState.CONNECTED); 104 | 105 | if (callback != null) 106 | { 107 | callback(); 108 | } 109 | } 110 | catch (SocketException e) 111 | { 112 | if (netWorkState != NetWorkState.TIMEOUT) 113 | { 114 | NetWorkChanged(NetWorkState.ERROR); 115 | } 116 | Dispose(); 117 | } 118 | finally 119 | { 120 | timeoutEvent.Set(); 121 | } 122 | }), this.socket); 123 | 124 | if (timeoutEvent.WaitOne(timeoutMSec, false)) 125 | { 126 | if (netWorkState != NetWorkState.CONNECTED && netWorkState != NetWorkState.ERROR) 127 | { 128 | NetWorkChanged(NetWorkState.TIMEOUT); 129 | Dispose(); 130 | } 131 | } 132 | } 133 | 134 | /// 135 | /// 网络状态变化 136 | /// 137 | /// 138 | private void NetWorkChanged(NetWorkState state) 139 | { 140 | netWorkState = state; 141 | 142 | if (NetWorkStateChangedEvent != null) 143 | { 144 | NetWorkStateChangedEvent(state); 145 | } 146 | } 147 | 148 | public void connect() 149 | { 150 | connect(null, null); 151 | } 152 | 153 | public void connect(JsonObject user) 154 | { 155 | connect(user, null); 156 | } 157 | 158 | public void connect(Action handshakeCallback) 159 | { 160 | connect(null, handshakeCallback); 161 | } 162 | 163 | public bool connect(JsonObject user, Action handshakeCallback) 164 | { 165 | try 166 | { 167 | protocol.start(user, handshakeCallback); 168 | return true; 169 | } 170 | catch (Exception e) 171 | { 172 | Console.WriteLine(e.ToString()); 173 | return false; 174 | } 175 | } 176 | 177 | private JsonObject emptyMsg = new JsonObject(); 178 | public void request(string route, Action action) 179 | { 180 | this.request(route, emptyMsg, action); 181 | } 182 | 183 | public void request(string route, JsonObject msg, Action action) 184 | { 185 | this.eventManager.AddCallBack(reqId, action); 186 | protocol.send(route, reqId, msg); 187 | 188 | reqId++; 189 | } 190 | 191 | public void notify(string route, JsonObject msg) 192 | { 193 | protocol.send(route, msg); 194 | } 195 | 196 | public void on(string eventName, Action action) 197 | { 198 | eventManager.AddOnEvent(eventName, action); 199 | } 200 | 201 | internal void processMessage(Message msg) 202 | { 203 | if (msg.type == MessageType.MSG_RESPONSE) 204 | { 205 | //msg.data["__route"] = msg.route; 206 | //msg.data["__type"] = "resp"; 207 | eventManager.InvokeCallBack(msg.id, msg.data); 208 | } 209 | else if (msg.type == MessageType.MSG_PUSH) 210 | { 211 | //msg.data["__route"] = msg.route; 212 | //msg.data["__type"] = "push"; 213 | eventManager.InvokeOnEvent(msg.route, msg.data); 214 | } 215 | } 216 | 217 | public void disconnect() 218 | { 219 | Dispose(); 220 | NetWorkChanged(NetWorkState.DISCONNECTED); 221 | } 222 | 223 | public void Dispose() 224 | { 225 | Dispose(true); 226 | GC.SuppressFinalize(this); 227 | } 228 | 229 | // The bulk of the clean-up code 230 | protected virtual void Dispose(bool disposing) 231 | { 232 | if (this.disposed) 233 | return; 234 | 235 | if (disposing) 236 | { 237 | // free managed resources 238 | if (this.protocol != null) 239 | { 240 | this.protocol.close(); 241 | } 242 | 243 | if (this.eventManager != null) 244 | { 245 | this.eventManager.Dispose(); 246 | } 247 | 248 | try 249 | { 250 | this.socket.Shutdown(SocketShutdown.Both); 251 | this.socket.Close(); 252 | this.socket = null; 253 | } 254 | catch (Exception) 255 | { 256 | //todo : 有待确定这里是否会出现异常,这里是参考之前官方github上pull request。emptyMsg 257 | } 258 | 259 | this.disposed = true; 260 | } 261 | } 262 | } 263 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/client/test/ClientTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleJson; 3 | 4 | namespace Pomelo.DotNetClient.Test 5 | { 6 | public class ClientTest 7 | { 8 | public static PomeloClient pc = null; 9 | 10 | public static void loginTest(string host, int port) 11 | { 12 | pc = new PomeloClient(); 13 | 14 | pc.NetWorkStateChangedEvent += (state) => 15 | { 16 | Console.WriteLine(state); 17 | }; 18 | 19 | 20 | pc.initClient(host, port, () => 21 | { 22 | pc.connect(null, data => 23 | { 24 | 25 | Console.WriteLine("on data back" + data.ToString()); 26 | JsonObject msg = new JsonObject(); 27 | msg["uid"] = 111; 28 | pc.request("gate.gateHandler.queryEntry", msg, OnQuery); 29 | }); 30 | }); 31 | } 32 | 33 | public static void OnQuery(JsonObject result) 34 | { 35 | if (Convert.ToInt32(result["code"]) == 200) 36 | { 37 | pc.disconnect(); 38 | 39 | string host = (string)result["host"]; 40 | int port = Convert.ToInt32(result["port"]); 41 | pc = new PomeloClient(); 42 | 43 | pc.NetWorkStateChangedEvent += (state) => 44 | { 45 | Console.WriteLine(state); 46 | }; 47 | 48 | pc.initClient(host, port, () => 49 | { 50 | pc.connect(null, (data) => 51 | { 52 | JsonObject userMessage = new JsonObject(); 53 | Console.WriteLine("on connect to connector!"); 54 | 55 | //Login 56 | JsonObject msg = new JsonObject(); 57 | msg["username"] = "test"; 58 | msg["rid"] = "pomelo"; 59 | 60 | pc.request("connector.entryHandler.enter", msg, OnEnter); 61 | }); 62 | }); 63 | } 64 | } 65 | 66 | public static void OnEnter(JsonObject result) 67 | { 68 | Console.WriteLine("on login " + result.ToString()); 69 | } 70 | 71 | public static void onDisconnect(JsonObject result) 72 | { 73 | Console.WriteLine("on sockect disconnected!"); 74 | } 75 | 76 | public static void Run() 77 | { 78 | string host = "192.168.0.156"; 79 | int port = 3014; 80 | 81 | loginTest(host, port); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/Decoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Pomelo.Protobuf 4 | { 5 | public class Decoder 6 | { 7 | /// 8 | /// Decodes the UInt32. 9 | /// 10 | public static uint decodeUInt32(int offset, byte[] bytes, out int length) 11 | { 12 | uint n = 0; 13 | length = 0; 14 | 15 | for (int i = offset; i < bytes.Length; i++) 16 | { 17 | length++; 18 | uint m = Convert.ToUInt32(bytes[i]); 19 | n = n + Convert.ToUInt32((m & 0x7f) * Math.Pow(2, (7 * (i - offset)))); 20 | if (m < 128) 21 | { 22 | break; 23 | } 24 | } 25 | 26 | return n; 27 | } 28 | 29 | public static uint decodeUInt32(byte[] bytes) 30 | { 31 | int length; 32 | return decodeUInt32(0, bytes, out length); 33 | } 34 | 35 | /// 36 | /// Decodes the SInt32. 37 | /// 38 | public static int decodeSInt32(byte[] bytes) 39 | { 40 | uint n = decodeUInt32(bytes); 41 | int flag = ((n % 2) == 1) ? -1 : 1; 42 | 43 | int result = Convert.ToInt32(((n % 2 + n) / 2) * flag); 44 | return result; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/Encoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace Pomelo.Protobuf 6 | { 7 | public class Encoder 8 | { 9 | 10 | //Encode the UInt32. 11 | public static byte[] encodeUInt32(string n) 12 | { 13 | return encodeUInt32(Convert.ToUInt32(n)); 14 | } 15 | 16 | /// 17 | /// Encode the UInt32. 18 | /// 19 | /// 20 | /// byte[] 21 | /// 22 | /// 23 | /// int 24 | /// 25 | public static byte[] encodeUInt32(uint n) 26 | { 27 | List byteList = new List(); 28 | do 29 | { 30 | uint tmp = n % 128; 31 | uint next = n >> 7; 32 | if (next != 0) 33 | { 34 | tmp = tmp + 128; 35 | } 36 | byteList.Add(Convert.ToByte(tmp)); 37 | n = next; 38 | } while (n != 0); 39 | 40 | return byteList.ToArray(); 41 | } 42 | 43 | //Encode SInt32 44 | public static byte[] encodeSInt32(string n) 45 | { 46 | return encodeSInt32(Convert.ToInt32(n)); 47 | } 48 | 49 | /// 50 | /// Encodes the SInt32. 51 | /// 52 | /// 53 | /// byte [] 54 | /// 55 | /// 56 | /// int 57 | /// 58 | public static byte[] encodeSInt32(int n) 59 | { 60 | UInt32 num = (uint)(n < 0 ? (Math.Abs(n) * 2 - 1) : n * 2); 61 | return encodeUInt32(num); 62 | } 63 | 64 | /// 65 | /// Encodes the float. 66 | /// 67 | /// 68 | /// byte [] 69 | /// 70 | /// 71 | /// float. 72 | /// 73 | public static byte[] encodeFloat(float n) 74 | { 75 | byte[] bytes = BitConverter.GetBytes(n); 76 | if (!BitConverter.IsLittleEndian) 77 | { 78 | Util.Reverse(bytes); 79 | } 80 | return bytes; 81 | } 82 | 83 | //Get the byte length of message. 84 | public static int byteLength(string msg) 85 | { 86 | return System.Text.Encoding.UTF8.GetBytes(msg).Length; 87 | } 88 | 89 | 90 | } 91 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/MsgDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SimpleJson; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | namespace Pomelo.Protobuf 8 | { 9 | public class MsgDecoder 10 | { 11 | private JsonObject protos { set; get; }//The message format(like .proto file) 12 | private int offset { set; get; } 13 | private byte[] buffer { set; get; }//The binary message from server. 14 | private Util util { set; get; } 15 | 16 | public MsgDecoder(JsonObject protos) 17 | { 18 | if (protos == null) protos = new JsonObject(); 19 | 20 | this.protos = protos; 21 | this.util = new Util(); 22 | } 23 | 24 | /// 25 | /// Decode message from server. 26 | /// 27 | /// 28 | /// Route. 29 | /// 30 | /// 31 | /// JsonObject. 32 | /// 33 | public JsonObject decode(string route, byte[] buf) 34 | { 35 | this.buffer = buf; 36 | this.offset = 0; 37 | object proto = null; 38 | if (this.protos.TryGetValue(route, out proto)) 39 | { 40 | JsonObject msg = new JsonObject(); 41 | return this.decodeMsg(msg, (JsonObject)proto, this.buffer.Length); 42 | } 43 | return null; 44 | } 45 | 46 | 47 | /// 48 | /// Decode the message. 49 | /// 50 | /// 51 | /// The message. 52 | /// 53 | /// 54 | /// JsonObject. 55 | /// 56 | /// 57 | /// JsonObject. 58 | /// 59 | /// 60 | /// int. 61 | /// 62 | private JsonObject decodeMsg(JsonObject msg, JsonObject proto, int length) 63 | { 64 | while (this.offset < length) 65 | { 66 | Dictionary head = this.getHead(); 67 | int tag; 68 | if (head.TryGetValue("tag", out tag)) 69 | { 70 | object _tags = null; 71 | if (proto.TryGetValue("__tags", out _tags)) 72 | { 73 | object name; 74 | if (((JsonObject)_tags).TryGetValue(tag.ToString(), out name)) 75 | { 76 | object value; 77 | if (proto.TryGetValue(name.ToString(), out value)) 78 | { 79 | object option; 80 | if (((JsonObject)(value)).TryGetValue("option", out option)) 81 | { 82 | switch (option.ToString()) 83 | { 84 | case "optional": 85 | case "required": 86 | object type; 87 | if (((JsonObject)(value)).TryGetValue("type", out type)) 88 | { 89 | msg.Add(name.ToString(), this.decodeProp(type.ToString(), proto)); 90 | } 91 | break; 92 | case "repeated": 93 | object _name; 94 | if (!msg.TryGetValue(name.ToString(), out _name)) 95 | { 96 | msg.Add(name.ToString(), new List()); 97 | } 98 | object value_type; 99 | if (msg.TryGetValue(name.ToString(), out _name) && ((JsonObject)(value)).TryGetValue("type", out value_type)) 100 | { 101 | decodeArray((List)_name, value_type.ToString(), proto); 102 | } 103 | break; 104 | } 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | return msg; 112 | } 113 | 114 | /// 115 | /// Decode array in message. 116 | /// 117 | private void decodeArray(List list, string type, JsonObject proto) 118 | { 119 | if (this.util.isSimpleType(type)) 120 | { 121 | int length = (int)Decoder.decodeUInt32(this.getBytes()); 122 | for (int i = 0; i < length; i++) 123 | { 124 | list.Add(this.decodeProp(type, null)); 125 | } 126 | } 127 | else 128 | { 129 | list.Add(this.decodeProp(type, proto)); 130 | } 131 | } 132 | 133 | /// 134 | /// Decode each simple type in message. 135 | /// 136 | private object decodeProp(string type, JsonObject proto) 137 | { 138 | switch (type) 139 | { 140 | case "uInt32": 141 | return Decoder.decodeUInt32(this.getBytes()); 142 | case "int32": 143 | case "sInt32": 144 | return Decoder.decodeSInt32(this.getBytes()); 145 | case "float": 146 | return this.decodeFloat(); 147 | case "double": 148 | return this.decodeDouble(); 149 | case "string": 150 | return this.decodeString(); 151 | default: 152 | return this.decodeObject(type, proto); 153 | } 154 | } 155 | 156 | //Decode the user-defined object type in message. 157 | private JsonObject decodeObject(string type, JsonObject proto) 158 | { 159 | if (proto != null) 160 | { 161 | object __messages; 162 | if (proto.TryGetValue("__messages", out __messages)) 163 | { 164 | object _type; 165 | if (((JsonObject)__messages).TryGetValue(type, out _type) || protos.TryGetValue("message " + type, out _type)) 166 | { 167 | int l = (int)Decoder.decodeUInt32(this.getBytes()); 168 | JsonObject msg = new JsonObject(); 169 | return this.decodeMsg(msg, (JsonObject)_type, this.offset + l); 170 | } 171 | } 172 | } 173 | return new JsonObject(); 174 | } 175 | 176 | //Decode string type. 177 | private string decodeString() 178 | { 179 | int length = (int)Decoder.decodeUInt32(this.getBytes()); 180 | string msg_string = Encoding.UTF8.GetString(this.buffer, this.offset, length); 181 | this.offset += length; 182 | return msg_string; 183 | } 184 | 185 | //Decode double type. 186 | private double decodeDouble() 187 | { 188 | double msg_double = BitConverter.Int64BitsToDouble((long)this.ReadRawLittleEndian64()); 189 | this.offset += 8; 190 | return msg_double; 191 | } 192 | 193 | //Decode float type 194 | private float decodeFloat() 195 | { 196 | float msg_float = BitConverter.ToSingle(this.buffer, this.offset); 197 | this.offset += 4; 198 | return msg_float; 199 | } 200 | 201 | //Read long in littleEndian 202 | private ulong ReadRawLittleEndian64() 203 | { 204 | ulong b1 = buffer[this.offset]; 205 | ulong b2 = buffer[this.offset + 1]; 206 | ulong b3 = buffer[this.offset + 2]; 207 | ulong b4 = buffer[this.offset + 3]; 208 | ulong b5 = buffer[this.offset + 4]; 209 | ulong b6 = buffer[this.offset + 5]; 210 | ulong b7 = buffer[this.offset + 6]; 211 | ulong b8 = buffer[this.offset + 7]; 212 | return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) 213 | | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); 214 | } 215 | 216 | //Get the type and tag. 217 | private Dictionary getHead() 218 | { 219 | int tag = (int)Decoder.decodeUInt32(this.getBytes()); 220 | Dictionary head = new Dictionary(); 221 | head.Add("type", tag & 0x7); 222 | head.Add("tag", tag >> 3); 223 | return head; 224 | } 225 | 226 | //Get bytes. 227 | private byte[] getBytes() 228 | { 229 | List arrayList = new List(); 230 | int pos = this.offset; 231 | byte b; 232 | do 233 | { 234 | b = this.buffer[pos]; 235 | arrayList.Add(b); 236 | pos++; 237 | } while (b >= 128); 238 | this.offset = pos; 239 | int length = arrayList.Count; 240 | byte[] bytes = new byte[length]; 241 | for (int i = 0; i < length; i++) 242 | { 243 | bytes[i] = arrayList[i]; 244 | } 245 | return bytes; 246 | } 247 | } 248 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/MsgEncoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SimpleJson; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | namespace Pomelo.Protobuf 8 | { 9 | public class MsgEncoder 10 | { 11 | private JsonObject protos { set; get; }//The message format(like .proto file) 12 | private Encoder encoder { set; get; } 13 | private Util util { set; get; } 14 | 15 | public MsgEncoder(JsonObject protos) 16 | { 17 | if (protos == null) protos = new JsonObject(); 18 | 19 | this.protos = protos; 20 | this.util = new Util(); 21 | } 22 | 23 | /// 24 | /// Encode the message from server. 25 | /// 26 | /// 27 | /// Route. 28 | /// 29 | /// 30 | /// Message. 31 | /// 32 | public byte[] encode(string route, JsonObject msg) 33 | { 34 | byte[] returnByte = null; 35 | object proto; 36 | if (this.protos.TryGetValue(route, out proto)) 37 | { 38 | if (!checkMsg(msg, (JsonObject)proto)) 39 | { 40 | return null; 41 | } 42 | int length = Encoder.byteLength(msg.ToString()) * 2; 43 | int offset = 0; 44 | byte[] buff = new byte[length]; 45 | offset = encodeMsg(buff, offset, (JsonObject)proto, msg); 46 | returnByte = new byte[offset]; 47 | for (int i = 0; i < offset; i++) 48 | { 49 | returnByte[i] = buff[i]; 50 | } 51 | } 52 | return returnByte; 53 | } 54 | 55 | /// 56 | /// Check the message. 57 | /// 58 | private bool checkMsg(JsonObject msg, JsonObject proto) 59 | { 60 | ICollection protoKeys = proto.Keys; 61 | foreach (string key in protoKeys) 62 | { 63 | JsonObject value = (JsonObject)proto[key]; 64 | object proto_option; 65 | if (value.TryGetValue("option", out proto_option)) 66 | { 67 | switch (proto_option.ToString()) 68 | { 69 | case "required": 70 | if (!msg.ContainsKey(key)) 71 | { 72 | return false; 73 | } 74 | else 75 | { 76 | 77 | } 78 | break; 79 | case "optional": 80 | object value_type; 81 | 82 | JsonObject messages = (JsonObject)proto["__messages"]; 83 | 84 | value_type = value["type"]; 85 | 86 | if (msg.ContainsKey(key)) 87 | { 88 | Object value_proto; 89 | 90 | if (messages.TryGetValue(value_type.ToString(), out value_proto) || protos.TryGetValue("message " + value_type.ToString(), out value_proto)) 91 | { 92 | checkMsg((JsonObject)msg[key], (JsonObject)value_proto); 93 | } 94 | } 95 | break; 96 | case "repeated": 97 | object msg_name; 98 | object msg_type; 99 | if (value.TryGetValue("type", out value_type) && msg.TryGetValue(key, out msg_name)) 100 | { 101 | if (((JsonObject)proto["__messages"]).TryGetValue(value_type.ToString(), out msg_type) || protos.TryGetValue("message " + value_type.ToString(), out msg_type)) 102 | { 103 | List o = (List)msg_name; 104 | foreach (object item in o) 105 | { 106 | if (!checkMsg((JsonObject)item, (JsonObject)msg_type)) 107 | { 108 | return false; 109 | } 110 | } 111 | } 112 | } 113 | break; 114 | } 115 | } 116 | } 117 | return true; 118 | } 119 | 120 | /// 121 | /// Encode the message. 122 | /// 123 | private int encodeMsg(byte[] buffer, int offset, JsonObject proto, JsonObject msg) 124 | { 125 | ICollection msgKeys = msg.Keys; 126 | foreach (string key in msgKeys) 127 | { 128 | object value; 129 | if (proto.TryGetValue(key, out value)) 130 | { 131 | object value_option; 132 | if (((JsonObject)value).TryGetValue("option", out value_option)) 133 | { 134 | switch (value_option.ToString()) 135 | { 136 | case "required": 137 | case "optional": 138 | object value_type, value_tag; 139 | if (((JsonObject)value).TryGetValue("type", out value_type) && ((JsonObject)value).TryGetValue("tag", out value_tag)) 140 | { 141 | offset = this.writeBytes(buffer, offset, this.encodeTag(value_type.ToString(), Convert.ToInt32(value_tag))); 142 | offset = this.encodeProp(msg[key], value_type.ToString(), offset, buffer, proto); 143 | } 144 | break; 145 | case "repeated": 146 | object msg_key; 147 | if (msg.TryGetValue(key, out msg_key)) 148 | { 149 | if (((List)msg_key).Count > 0) 150 | { 151 | offset = encodeArray((List)msg_key, (JsonObject)value, offset, buffer, proto); 152 | } 153 | } 154 | break; 155 | } 156 | } 157 | 158 | } 159 | } 160 | return offset; 161 | } 162 | 163 | /// 164 | /// Encode the array type. 165 | /// 166 | private int encodeArray(List msg, JsonObject value, int offset, byte[] buffer, JsonObject proto) 167 | { 168 | object value_type, value_tag; 169 | if (value.TryGetValue("type", out value_type) && value.TryGetValue("tag", out value_tag)) 170 | { 171 | if (this.util.isSimpleType(value_type.ToString())) 172 | { 173 | offset = this.writeBytes(buffer, offset, this.encodeTag(value_type.ToString(), Convert.ToInt32(value_tag))); 174 | offset = this.writeBytes(buffer, offset, Encoder.encodeUInt32((uint)msg.Count)); 175 | foreach (object item in msg) 176 | { 177 | offset = this.encodeProp(item, value_type.ToString(), offset, buffer, null); 178 | } 179 | } 180 | else 181 | { 182 | foreach (object item in msg) 183 | { 184 | offset = this.writeBytes(buffer, offset, this.encodeTag(value_type.ToString(), Convert.ToInt32(value_tag))); 185 | offset = this.encodeProp(item, value_type.ToString(), offset, buffer, proto); 186 | } 187 | } 188 | } 189 | return offset; 190 | } 191 | 192 | /// 193 | /// Encode each item in message. 194 | /// 195 | private int encodeProp(object value, string type, int offset, byte[] buffer, JsonObject proto) 196 | { 197 | switch (type) 198 | { 199 | case "uInt32": 200 | this.writeUInt32(buffer, ref offset, value); 201 | break; 202 | case "int32": 203 | case "sInt32": 204 | this.writeInt32(buffer, ref offset, value); 205 | break; 206 | case "float": 207 | this.writeFloat(buffer, ref offset, value); 208 | break; 209 | case "double": 210 | this.writeDouble(buffer, ref offset, value); 211 | break; 212 | case "string": 213 | this.writeString(buffer, ref offset, value); 214 | break; 215 | default: 216 | object __messages; 217 | object __message_type; 218 | 219 | if (proto.TryGetValue("__messages", out __messages)) 220 | { 221 | if (((JsonObject)__messages).TryGetValue(type, out __message_type) || protos.TryGetValue("message " + type, out __message_type)) 222 | { 223 | byte[] tembuff = new byte[Encoder.byteLength(value.ToString()) * 3]; 224 | int length = 0; 225 | length = this.encodeMsg(tembuff, length, (JsonObject)__message_type, (JsonObject)value); 226 | offset = writeBytes(buffer, offset, Encoder.encodeUInt32((uint)length)); 227 | for (int i = 0; i < length; i++) 228 | { 229 | buffer[offset] = tembuff[i]; 230 | offset++; 231 | } 232 | } 233 | } 234 | break; 235 | } 236 | return offset; 237 | } 238 | 239 | //Encode string. 240 | private void writeString(byte[] buffer, ref int offset, object value) 241 | { 242 | int le = Encoding.UTF8.GetByteCount(value.ToString()); 243 | offset = writeBytes(buffer, offset, Encoder.encodeUInt32((uint)le)); 244 | byte[] bytes = Encoding.UTF8.GetBytes(value.ToString()); 245 | this.writeBytes(buffer, offset, bytes); 246 | offset += le; 247 | } 248 | 249 | //Encode double. 250 | private void writeDouble(byte[] buffer, ref int offset, object value) 251 | { 252 | WriteRawLittleEndian64(buffer, offset, (ulong)BitConverter.DoubleToInt64Bits(double.Parse(value.ToString()))); 253 | offset += 8; 254 | } 255 | 256 | //Encode float. 257 | private void writeFloat(byte[] buffer, ref int offset, object value) 258 | { 259 | this.writeBytes(buffer, offset, Encoder.encodeFloat(float.Parse(value.ToString()))); 260 | offset += 4; 261 | } 262 | 263 | ////Encode UInt32. 264 | private void writeUInt32(byte[] buffer, ref int offset, object value) 265 | { 266 | offset = writeBytes(buffer, offset, Encoder.encodeUInt32(value.ToString())); 267 | } 268 | 269 | //Encode Int32 270 | private void writeInt32(byte[] buffer, ref int offset, object value) 271 | { 272 | offset = writeBytes(buffer, offset, Encoder.encodeSInt32(value.ToString())); 273 | } 274 | 275 | //Write bytes to buffer. 276 | private int writeBytes(byte[] buffer, int offset, byte[] bytes) 277 | { 278 | for (int i = 0; i < bytes.Length; i++) 279 | { 280 | buffer[offset] = bytes[i]; 281 | offset++; 282 | } 283 | return offset; 284 | } 285 | 286 | //Encode tag. 287 | private byte[] encodeTag(string type, int tag) 288 | { 289 | int flag = this.util.containType(type); 290 | return Encoder.encodeUInt32((uint)(tag << 3 | flag)); 291 | } 292 | 293 | 294 | private void WriteRawLittleEndian64(byte[] buffer, int offset, ulong value) 295 | { 296 | buffer[offset++] = ((byte)value); 297 | buffer[offset++] = ((byte)(value >> 8)); 298 | buffer[offset++] = ((byte)(value >> 16)); 299 | buffer[offset++] = ((byte)(value >> 24)); 300 | buffer[offset++] = ((byte)(value >> 32)); 301 | buffer[offset++] = ((byte)(value >> 40)); 302 | buffer[offset++] = ((byte)(value >> 48)); 303 | buffer[offset++] = ((byte)(value >> 56)); 304 | } 305 | } 306 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/Protobuf.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleJson; 3 | 4 | namespace Pomelo.Protobuf 5 | { 6 | public class Protobuf 7 | { 8 | private MsgDecoder decoder; 9 | private MsgEncoder encoder; 10 | 11 | public Protobuf(JsonObject encodeProtos, JsonObject decodeProtos) 12 | { 13 | this.encoder = new MsgEncoder(encodeProtos); 14 | this.decoder = new MsgDecoder(decodeProtos); 15 | } 16 | 17 | public byte[] encode(string route, JsonObject msg) 18 | { 19 | return encoder.encode(route, msg); 20 | } 21 | 22 | public JsonObject decode(string route, byte[] buffer) 23 | { 24 | return decoder.decode(route, buffer); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/test/CodecTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Pomelo.Protobuf; 4 | 5 | namespace Pomelo.Protobuf.Test 6 | { 7 | public class CodecTest 8 | { 9 | public static bool EncodeSInt32Test(int count) 10 | { 11 | Random random = new Random(); 12 | 13 | int flag = -1; 14 | for (int i = 0; i < count; i++) 15 | { 16 | flag *= -1; 17 | int num = random.Next(0, 0x7fffffff) * flag; 18 | byte[] bytes = Encoder.encodeSInt32(num); 19 | int result = Decoder.decodeSInt32(bytes); 20 | if (num != result) return false; 21 | } 22 | 23 | return true; 24 | } 25 | 26 | public static bool EncodeUInt32Test(int count) 27 | { 28 | Random random = new Random(); 29 | for (int i = 0; i < count; i++) 30 | { 31 | uint num = (uint)random.Next(0, 0x7fffffff); 32 | byte[] bytes = Encoder.encodeUInt32(num); 33 | uint result = Decoder.decodeUInt32(bytes); 34 | if (num != result) return false; 35 | } 36 | 37 | return true; 38 | } 39 | 40 | public static bool Run() 41 | { 42 | bool success = true, flag = false; 43 | DateTime start, end; 44 | 45 | start = DateTime.Now; 46 | flag = EncodeSInt32Test(10000); 47 | end = DateTime.Now; 48 | Console.WriteLine("Encode sint32 test finished , result is : {1}, cost time : {0}", end - start, flag); 49 | if (!flag) success = false; 50 | 51 | start = DateTime.Now; 52 | flag = EncodeUInt32Test(10000); 53 | end = DateTime.Now; 54 | Console.WriteLine("Encode uint32 test finished , result is : {1}, cost time : {0}", end - start, flag); 55 | if (!flag) success = false; 56 | 57 | return success; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/test/ProtobufTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using SimpleJson; 5 | using Pomelo.Protobuf; 6 | 7 | namespace Pomelo.Protobuf.Test 8 | { 9 | public class ProtobufTest 10 | { 11 | public static JsonObject read(string name) 12 | { 13 | StreamReader file = new StreamReader(name); 14 | 15 | String str = file.ReadToEnd(); 16 | 17 | return (JsonObject)SimpleJson.SimpleJson.DeserializeObject(str); 18 | } 19 | 20 | public static bool equal(JsonObject a, JsonObject b) 21 | { 22 | ICollection keys0 = a.Keys; 23 | ICollection keys1 = b.Keys; 24 | 25 | foreach (string key in keys0) 26 | { 27 | Console.WriteLine(a[key].GetType()); 28 | if (a[key].GetType().ToString() == "SimpleJson.JsonObject") 29 | { 30 | if (!equal((JsonObject)a[key], (JsonObject)b[key])) return false; 31 | } 32 | else if (a[key].GetType().ToString() == "SimpleJson.JsonArray") 33 | { 34 | continue; 35 | } 36 | else 37 | { 38 | if (!a[key].ToString().Equals(b[key].ToString())) return false; 39 | } 40 | } 41 | 42 | return true; 43 | } 44 | 45 | public static void Run() 46 | { 47 | JsonObject protos = read("../../json/rootProtos.json"); 48 | JsonObject msgs = read("../../json/rootMsg.json"); 49 | 50 | Protobuf protobuf = new Protobuf(protos, protos); 51 | 52 | ICollection keys = msgs.Keys; 53 | 54 | foreach (string key in keys) 55 | { 56 | JsonObject msg = (JsonObject)msgs[key]; 57 | byte[] bytes = protobuf.encode(key, msg); 58 | JsonObject result = protobuf.decode(key, bytes); 59 | if (!equal(msg, result)) 60 | { 61 | Console.WriteLine("protobuf test failed!"); 62 | return; 63 | } 64 | } 65 | 66 | Console.WriteLine("Protobuf test success!"); 67 | } 68 | 69 | private static void print(byte[] bytes, int offset, int length) 70 | { 71 | for (int i = offset; i < length; i++) 72 | Console.Write(Convert.ToString(bytes[i], 16) + " "); 73 | Console.WriteLine(); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protobuf/util/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace Pomelo.Protobuf 6 | { 7 | public class Util 8 | { 9 | //Simple type 10 | private string[] types; 11 | private Dictionary typeMap; 12 | 13 | public Util() 14 | { 15 | this.initTypeMap(); 16 | this.types = new string[] { "uInt32", "sInt32", "int32", "uInt64", "sInt64", "float", "double" }; 17 | } 18 | 19 | /// 20 | /// Check out the given type. If it is simple, return ture. 21 | /// 22 | /// 23 | /// The simple type. 24 | /// 25 | /// 26 | /// If set to true type. 27 | /// 28 | public bool isSimpleType(string type) 29 | { 30 | int length = types.Length; 31 | bool flag = false; 32 | for (int i = 0; i < length; i++) 33 | { 34 | if (type == types[i]) 35 | { 36 | flag = true; 37 | break; 38 | } 39 | } 40 | return flag; 41 | } 42 | 43 | /// 44 | /// Check out the given type. If the type exist in typeMap, return true. 45 | /// 46 | /// 47 | /// The type. 48 | /// 49 | /// 50 | /// Type. 51 | /// 52 | public int containType(string type) 53 | { 54 | int value, returnInt = 2; 55 | if (this.typeMap.TryGetValue(type, out value)) 56 | { 57 | returnInt = value; 58 | } 59 | return returnInt; 60 | } 61 | 62 | //Init the typeMap 63 | private void initTypeMap() 64 | { 65 | Dictionary dic = new Dictionary(); 66 | dic.Add("uInt32", 0); 67 | dic.Add("sInt32", 0); 68 | dic.Add("int32", 0); 69 | dic.Add("double", 1); 70 | dic.Add("string", 2); 71 | dic.Add("float", 5); 72 | dic.Add("message", 2); 73 | 74 | this.typeMap = dic; 75 | } 76 | 77 | /// 78 | /// Reverses the order of bytes in the array 79 | /// 80 | public static void Reverse(byte[] bytes) 81 | { 82 | byte temp; 83 | for (int first = 0, last = bytes.Length - 1; first < last; first++, last--) 84 | { 85 | temp = bytes[first]; 86 | bytes[first] = bytes[last]; 87 | bytes[last] = temp; 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/HandShakeService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SimpleJson; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | 7 | namespace Pomelo.DotNetClient 8 | { 9 | public class HandShakeService 10 | { 11 | private Protocol protocol; 12 | private Action callback; 13 | 14 | public const string Version = "0.3.0"; 15 | public const string Type = "unity-socket"; 16 | 17 | 18 | public HandShakeService(Protocol protocol) 19 | { 20 | this.protocol = protocol; 21 | } 22 | 23 | public void request(JsonObject user, Action callback) 24 | { 25 | byte[] body = Encoding.UTF8.GetBytes(buildMsg(user).ToString()); 26 | 27 | protocol.send(PackageType.PKG_HANDSHAKE, body); 28 | 29 | this.callback = callback; 30 | } 31 | 32 | internal void invokeCallback(JsonObject data) 33 | { 34 | //Invoke the handshake callback 35 | if (callback != null) callback.Invoke(data); 36 | } 37 | 38 | public void ack() 39 | { 40 | protocol.send(PackageType.PKG_HANDSHAKE_ACK, new byte[0]); 41 | } 42 | 43 | private JsonObject buildMsg(JsonObject user) 44 | { 45 | if (user == null) user = new JsonObject(); 46 | 47 | JsonObject msg = new JsonObject(); 48 | 49 | //Build sys option 50 | JsonObject sys = new JsonObject(); 51 | sys["version"] = Version; 52 | sys["type"] = Type; 53 | 54 | //Build handshake message 55 | msg["sys"] = sys; 56 | msg["user"] = user; 57 | 58 | return msg; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/HeartBeatService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Timers; 3 | 4 | namespace Pomelo.DotNetClient 5 | { 6 | public class HeartBeatService 7 | { 8 | int interval; 9 | public int timeout; 10 | Timer timer; 11 | DateTime lastTime; 12 | 13 | Protocol protocol; 14 | 15 | public HeartBeatService(int interval, Protocol protocol) 16 | { 17 | this.interval = interval * 1000; 18 | this.protocol = protocol; 19 | } 20 | 21 | internal void resetTimeout() 22 | { 23 | this.timeout = 0; 24 | lastTime = DateTime.Now; 25 | } 26 | 27 | public void sendHeartBeat(object source, ElapsedEventArgs e) 28 | { 29 | TimeSpan span = DateTime.Now - lastTime; 30 | timeout = (int)span.TotalMilliseconds; 31 | 32 | //check timeout 33 | if (timeout > interval * 2) 34 | { 35 | protocol.getPomeloClient().disconnect(); 36 | //stop(); 37 | return; 38 | } 39 | 40 | //Send heart beat 41 | protocol.send(PackageType.PKG_HEARTBEAT); 42 | } 43 | 44 | public void start() 45 | { 46 | if (interval < 1000) return; 47 | 48 | //start hearbeat 49 | this.timer = new Timer(); 50 | timer.Interval = interval; 51 | timer.Elapsed += new ElapsedEventHandler(sendHeartBeat); 52 | timer.Enabled = true; 53 | 54 | //Set timeout 55 | timeout = 0; 56 | lastTime = DateTime.Now; 57 | } 58 | 59 | public void stop() 60 | { 61 | if (this.timer != null) 62 | { 63 | this.timer.Enabled = false; 64 | this.timer.Dispose(); 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/Message.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleJson; 3 | 4 | namespace Pomelo.DotNetClient 5 | { 6 | public class Message 7 | { 8 | public MessageType type; 9 | public string route; 10 | public uint id; 11 | public JsonObject data; 12 | 13 | public Message(MessageType type, uint id, string route, JsonObject data) 14 | { 15 | this.type = type; 16 | this.id = id; 17 | this.route = route; 18 | this.data = data; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/MessageProtocol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using SimpleJson; 5 | using Pomelo.Protobuf; 6 | namespace Pomelo.DotNetClient 7 | { 8 | public class MessageProtocol 9 | { 10 | private Dictionary dict = new Dictionary(); 11 | private Dictionary abbrs = new Dictionary(); 12 | private JsonObject encodeProtos = new JsonObject(); 13 | private JsonObject decodeProtos = new JsonObject(); 14 | private Dictionary reqMap; 15 | private Protobuf.Protobuf protobuf; 16 | 17 | public const int MSG_Route_Limit = 255; 18 | public const int MSG_Route_Mask = 0x01; 19 | public const int MSG_Type_Mask = 0x07; 20 | 21 | public MessageProtocol(JsonObject dict, JsonObject serverProtos, JsonObject clientProtos) 22 | { 23 | ICollection keys = dict.Keys; 24 | 25 | foreach (string key in keys) 26 | { 27 | ushort value = Convert.ToUInt16(dict[key]); 28 | this.dict[key] = value; 29 | this.abbrs[value] = key; 30 | } 31 | 32 | protobuf = new Protobuf.Protobuf(clientProtos, serverProtos); 33 | this.encodeProtos = clientProtos; 34 | this.decodeProtos = serverProtos; 35 | 36 | this.reqMap = new Dictionary(); 37 | } 38 | 39 | public byte[] encode(string route, JsonObject msg) 40 | { 41 | return encode(route, 0, msg); 42 | } 43 | 44 | public byte[] encode(string route, uint id, JsonObject msg) 45 | { 46 | int routeLength = byteLength(route); 47 | if (routeLength > MSG_Route_Limit) 48 | { 49 | throw new Exception("Route is too long!"); 50 | } 51 | 52 | //Encode head 53 | //The maximus length of head is 1 byte flag + 4 bytes message id + route string length + 1byte 54 | byte[] head = new byte[routeLength + 6]; 55 | int offset = 1; 56 | byte flag = 0; 57 | 58 | if (id > 0) 59 | { 60 | byte[] bytes = Protobuf.Encoder.encodeUInt32(id); 61 | 62 | writeBytes(bytes, offset, head); 63 | flag |= ((byte)MessageType.MSG_REQUEST) << 1; 64 | offset += bytes.Length; 65 | } 66 | else 67 | { 68 | flag |= ((byte)MessageType.MSG_NOTIFY) << 1; 69 | } 70 | 71 | //Compress head 72 | if (dict.ContainsKey(route)) 73 | { 74 | ushort cmpRoute = dict[route]; 75 | writeShort(offset, cmpRoute, head); 76 | flag |= MSG_Route_Mask; 77 | offset += 2; 78 | } 79 | else 80 | { 81 | //Write route length 82 | head[offset++] = (byte)routeLength; 83 | 84 | //Write route 85 | writeBytes(Encoding.UTF8.GetBytes(route), offset, head); 86 | offset += routeLength; 87 | } 88 | 89 | head[0] = flag; 90 | 91 | //Encode body 92 | byte[] body; 93 | if (encodeProtos.ContainsKey(route)) 94 | { 95 | body = protobuf.encode(route, msg); 96 | } 97 | else 98 | { 99 | body = Encoding.UTF8.GetBytes(msg.ToString()); 100 | } 101 | 102 | //Construct the result 103 | byte[] result = new byte[offset + body.Length]; 104 | for (int i = 0; i < offset; i++) 105 | { 106 | result[i] = head[i]; 107 | } 108 | 109 | for (int i = 0; i < body.Length; i++) 110 | { 111 | result[offset + i] = body[i]; 112 | } 113 | 114 | //Add id to route map 115 | if (id > 0) reqMap.Add(id, route); 116 | 117 | return result; 118 | } 119 | 120 | public Message decode(byte[] buffer) 121 | { 122 | //Decode head 123 | //Get flag 124 | byte flag = buffer[0]; 125 | //Set offset to 1, for the 1st byte will always be the flag 126 | int offset = 1; 127 | 128 | //Get type from flag; 129 | MessageType type = (MessageType)((flag >> 1) & MSG_Type_Mask); 130 | uint id = 0; 131 | string route; 132 | 133 | if (type == MessageType.MSG_RESPONSE) 134 | { 135 | int length; 136 | id = (uint)Protobuf.Decoder.decodeUInt32(offset, buffer, out length); 137 | if (id <= 0 || !reqMap.ContainsKey(id)) 138 | { 139 | return null; 140 | } 141 | else 142 | { 143 | route = reqMap[id]; 144 | reqMap.Remove(id); 145 | } 146 | 147 | offset += length; 148 | } 149 | else if (type == MessageType.MSG_PUSH) 150 | { 151 | //Get route 152 | if ((flag & 0x01) == 1) 153 | { 154 | ushort routeId = readShort(offset, buffer); 155 | route = abbrs[routeId]; 156 | 157 | offset += 2; 158 | } 159 | else 160 | { 161 | byte length = buffer[offset]; 162 | offset += 1; 163 | 164 | route = Encoding.UTF8.GetString(buffer, offset, length); 165 | offset += length; 166 | } 167 | } 168 | else 169 | { 170 | return null; 171 | } 172 | 173 | //Decode body 174 | byte[] body = new byte[buffer.Length - offset]; 175 | for (int i = 0; i < body.Length; i++) 176 | { 177 | body[i] = buffer[i + offset]; 178 | } 179 | 180 | JsonObject msg; 181 | if (decodeProtos.ContainsKey(route)) 182 | { 183 | msg = protobuf.decode(route, body); 184 | } 185 | else 186 | { 187 | msg = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(Encoding.UTF8.GetString(body)); 188 | } 189 | 190 | //Construct the message 191 | return new Message(type, id, route, msg); 192 | } 193 | 194 | private void writeInt(int offset, uint value, byte[] bytes) 195 | { 196 | bytes[offset] = (byte)(value >> 24 & 0xff); 197 | bytes[offset + 1] = (byte)(value >> 16 & 0xff); 198 | bytes[offset + 2] = (byte)(value >> 8 & 0xff); 199 | bytes[offset + 3] = (byte)(value & 0xff); 200 | } 201 | 202 | private void writeShort(int offset, ushort value, byte[] bytes) 203 | { 204 | bytes[offset] = (byte)(value >> 8 & 0xff); 205 | bytes[offset + 1] = (byte)(value & 0xff); 206 | } 207 | 208 | private ushort readShort(int offset, byte[] bytes) 209 | { 210 | ushort result = 0; 211 | 212 | result += (ushort)(bytes[offset] << 8); 213 | result += (ushort)(bytes[offset + 1]); 214 | 215 | return result; 216 | } 217 | 218 | private int byteLength(string msg) 219 | { 220 | return Encoding.UTF8.GetBytes(msg).Length; 221 | } 222 | 223 | private void writeBytes(byte[] source, int offset, byte[] target) 224 | { 225 | for (int i = 0; i < source.Length; i++) 226 | { 227 | target[offset + i] = source[i]; 228 | } 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/MessageType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Pomelo.DotNetClient 4 | { 5 | public enum MessageType 6 | { 7 | MSG_REQUEST = 0, 8 | MSG_NOTIFY = 1, 9 | MSG_RESPONSE = 2, 10 | MSG_PUSH = 3 11 | } 12 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/Package.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Pomelo.DotNetClient 4 | { 5 | public class Package 6 | { 7 | public PackageType type; 8 | public int length; 9 | public byte[] body; 10 | 11 | public Package(PackageType type, byte[] body) 12 | { 13 | this.type = type; 14 | this.length = body.Length; 15 | this.body = body; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/PackageProtocol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Pomelo.DotNetClient 4 | { 5 | public class PackageProtocol 6 | { 7 | public const int HEADER_LENGTH = 4; 8 | 9 | public static byte[] encode(PackageType type) 10 | { 11 | return new byte[] { Convert.ToByte(type), 0, 0, 0 }; 12 | } 13 | 14 | public static byte[] encode(PackageType type, byte[] body) 15 | { 16 | int length = HEADER_LENGTH; 17 | 18 | if (body != null) length += body.Length; 19 | 20 | byte[] buf = new byte[length]; 21 | 22 | int index = 0; 23 | 24 | buf[index++] = Convert.ToByte(type); 25 | buf[index++] = Convert.ToByte(body.Length >> 16 & 0xFF); 26 | buf[index++] = Convert.ToByte(body.Length >> 8 & 0xFF); 27 | buf[index++] = Convert.ToByte(body.Length & 0xFF); 28 | 29 | while (index < length) 30 | { 31 | buf[index] = body[index - HEADER_LENGTH]; 32 | index++; 33 | } 34 | 35 | return buf; 36 | } 37 | 38 | public static Package decode(byte[] buf) 39 | { 40 | PackageType type = (PackageType)buf[0]; 41 | 42 | byte[] body = new byte[buf.Length - HEADER_LENGTH]; 43 | 44 | for (int i = 0; i < body.Length; i++) 45 | { 46 | body[i] = buf[i + HEADER_LENGTH]; 47 | } 48 | 49 | return new Package(type, body); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/PackageTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Pomelo.DotNetClient 4 | { 5 | public enum PackageType 6 | { 7 | PKG_HANDSHAKE = 1, 8 | PKG_HANDSHAKE_ACK = 2, 9 | PKG_HEARTBEAT = 3, 10 | PKG_DATA = 4, 11 | PKG_KICK = 5 12 | } 13 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/Protocol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleJson; 3 | using System.Text; 4 | 5 | namespace Pomelo.DotNetClient 6 | { 7 | public class Protocol 8 | { 9 | private MessageProtocol messageProtocol; 10 | private ProtocolState state; 11 | private Transporter transporter; 12 | private HandShakeService handshake; 13 | private HeartBeatService heartBeatService = null; 14 | private PomeloClient pc; 15 | 16 | public PomeloClient getPomeloClient() 17 | { 18 | return this.pc; 19 | } 20 | 21 | public Protocol(PomeloClient pc, System.Net.Sockets.Socket socket) 22 | { 23 | this.pc = pc; 24 | this.transporter = new Transporter(socket, this.processMessage); 25 | this.transporter.onDisconnect = onDisconnect; 26 | 27 | this.handshake = new HandShakeService(this); 28 | this.state = ProtocolState.start; 29 | } 30 | 31 | internal void start(JsonObject user, Action callback) 32 | { 33 | this.transporter.start(); 34 | this.handshake.request(user, callback); 35 | 36 | this.state = ProtocolState.handshaking; 37 | } 38 | 39 | //Send notify, do not need id 40 | internal void send(string route, JsonObject msg) 41 | { 42 | send(route, 0, msg); 43 | } 44 | 45 | //Send request, user request id 46 | internal void send(string route, uint id, JsonObject msg) 47 | { 48 | if (this.state != ProtocolState.working) return; 49 | 50 | byte[] body = messageProtocol.encode(route, id, msg); 51 | 52 | send(PackageType.PKG_DATA, body); 53 | } 54 | 55 | internal void send(PackageType type) 56 | { 57 | if (this.state == ProtocolState.closed) return; 58 | transporter.send(PackageProtocol.encode(type)); 59 | } 60 | 61 | //Send system message, these message do not use messageProtocol 62 | internal void send(PackageType type, JsonObject msg) 63 | { 64 | //This method only used to send system package 65 | if (type == PackageType.PKG_DATA) return; 66 | 67 | byte[] body = Encoding.UTF8.GetBytes(msg.ToString()); 68 | 69 | send(type, body); 70 | } 71 | 72 | //Send message use the transporter 73 | internal void send(PackageType type, byte[] body) 74 | { 75 | if (this.state == ProtocolState.closed) return; 76 | 77 | byte[] pkg = PackageProtocol.encode(type, body); 78 | 79 | transporter.send(pkg); 80 | } 81 | 82 | //Invoke by Transporter, process the message 83 | internal void processMessage(byte[] bytes) 84 | { 85 | Package pkg = PackageProtocol.decode(bytes); 86 | 87 | //Ignore all the message except handshading at handshake stage 88 | if (pkg.type == PackageType.PKG_HANDSHAKE && this.state == ProtocolState.handshaking) 89 | { 90 | 91 | //Ignore all the message except handshading 92 | JsonObject data = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(Encoding.UTF8.GetString(pkg.body)); 93 | 94 | processHandshakeData(data); 95 | 96 | this.state = ProtocolState.working; 97 | 98 | } 99 | else if (pkg.type == PackageType.PKG_HEARTBEAT && this.state == ProtocolState.working) 100 | { 101 | this.heartBeatService.resetTimeout(); 102 | } 103 | else if (pkg.type == PackageType.PKG_DATA && this.state == ProtocolState.working) 104 | { 105 | this.heartBeatService.resetTimeout(); 106 | pc.processMessage(messageProtocol.decode(pkg.body)); 107 | } 108 | else if (pkg.type == PackageType.PKG_KICK) 109 | { 110 | this.getPomeloClient().disconnect(); 111 | this.close(); 112 | } 113 | } 114 | 115 | private void processHandshakeData(JsonObject msg) 116 | { 117 | //Handshake error 118 | if (!msg.ContainsKey("code") || !msg.ContainsKey("sys") || Convert.ToInt32(msg["code"]) != 200) 119 | { 120 | throw new Exception("Handshake error! Please check your handshake config."); 121 | } 122 | 123 | //Set compress data 124 | JsonObject sys = (JsonObject)msg["sys"]; 125 | 126 | JsonObject dict = new JsonObject(); 127 | if (sys.ContainsKey("dict")) dict = (JsonObject)sys["dict"]; 128 | 129 | JsonObject protos = new JsonObject(); 130 | JsonObject serverProtos = new JsonObject(); 131 | JsonObject clientProtos = new JsonObject(); 132 | 133 | if (sys.ContainsKey("protos")) 134 | { 135 | protos = (JsonObject)sys["protos"]; 136 | serverProtos = (JsonObject)protos["server"]; 137 | clientProtos = (JsonObject)protos["client"]; 138 | } 139 | 140 | messageProtocol = new MessageProtocol(dict, serverProtos, clientProtos); 141 | 142 | //Init heartbeat service 143 | int interval = 0; 144 | if (sys.ContainsKey("heartbeat")) interval = Convert.ToInt32(sys["heartbeat"]); 145 | heartBeatService = new HeartBeatService(interval, this); 146 | 147 | if (interval > 0) 148 | { 149 | heartBeatService.start(); 150 | } 151 | 152 | //send ack and change protocol state 153 | handshake.ack(); 154 | this.state = ProtocolState.working; 155 | 156 | //Invoke handshake callback 157 | JsonObject user = new JsonObject(); 158 | if (msg.ContainsKey("user")) user = (JsonObject)msg["user"]; 159 | handshake.invokeCallback(user); 160 | } 161 | 162 | //The socket disconnect 163 | private void onDisconnect() 164 | { 165 | this.pc.disconnect(); 166 | } 167 | 168 | internal void close() 169 | { 170 | transporter.close(); 171 | 172 | if (heartBeatService != null) heartBeatService.stop(); 173 | 174 | this.state = ProtocolState.closed; 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/protocol/ProtocolState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Pomelo.DotNetClient 4 | { 5 | 6 | public enum ProtocolState 7 | { 8 | start = 1, // Just open, need to send handshaking 9 | handshaking = 2, // on handshaking process 10 | working = 3, // can receive and send data 11 | closed = 4, // on read body 12 | } 13 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/transport/TransportState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Pomelo.DotNetClient 4 | { 5 | 6 | public enum TransportState 7 | { 8 | readHead = 1, // on read head 9 | readBody = 2, // on read body 10 | closed = 3 // connection closed, will ignore all the message and wait for clean up 11 | } 12 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/transport/Transporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | 4 | namespace Pomelo.DotNetClient 5 | { 6 | class StateObject 7 | { 8 | public const int BufferSize = 1024; 9 | internal byte[] buffer = new byte[BufferSize]; 10 | } 11 | 12 | public class Transporter 13 | { 14 | public const int HeadLength = 4; 15 | 16 | private Socket socket; 17 | private Action messageProcesser; 18 | 19 | //Used for get message 20 | private StateObject stateObject = new StateObject(); 21 | private TransportState transportState; 22 | private IAsyncResult asyncReceive; 23 | private IAsyncResult asyncSend; 24 | private bool onSending = false; 25 | private bool onReceiving = false; 26 | private byte[] headBuffer = new byte[4]; 27 | private byte[] buffer; 28 | private int bufferOffset = 0; 29 | private int pkgLength = 0; 30 | internal Action onDisconnect = null; 31 | 32 | //private TransportQueue _receiveQueue = new TransportQueue(); 33 | private System.Object _lock = new System.Object(); 34 | 35 | public Transporter(Socket socket, Action processer) 36 | { 37 | this.socket = socket; 38 | this.messageProcesser = processer; 39 | transportState = TransportState.readHead; 40 | } 41 | 42 | public void start() 43 | { 44 | this.receive(); 45 | } 46 | 47 | public void send(byte[] buffer) 48 | { 49 | if (this.transportState != TransportState.closed) 50 | { 51 | //string str = ""; 52 | //foreach (byte code in buffer) 53 | //{ 54 | // str += code.ToString(); 55 | //} 56 | //Console.WriteLine("send:" + buffer.Length + " " + str.Length + " " + str); 57 | this.asyncSend = socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(sendCallback), socket); 58 | 59 | this.onSending = true; 60 | } 61 | } 62 | 63 | private void sendCallback(IAsyncResult asyncSend) 64 | { 65 | //UnityEngine.Debug.Log("sendCallback " + this.transportState); 66 | if (this.transportState == TransportState.closed) return; 67 | socket.EndSend(asyncSend); 68 | this.onSending = false; 69 | } 70 | 71 | public void receive() 72 | { 73 | //Console.WriteLine("receive state : {0}, {1}", this.transportState, socket.Available); 74 | this.asyncReceive = socket.BeginReceive(stateObject.buffer, 0, stateObject.buffer.Length, SocketFlags.None, new AsyncCallback(endReceive), stateObject); 75 | this.onReceiving = true; 76 | } 77 | 78 | internal void close() 79 | { 80 | this.transportState = TransportState.closed; 81 | /*try{ 82 | if(this.onReceiving) socket.EndReceive (this.asyncReceive); 83 | if(this.onSending) socket.EndSend(this.asyncSend); 84 | }catch (Exception e){ 85 | Console.WriteLine(e.Message); 86 | }*/ 87 | } 88 | 89 | private void endReceive(IAsyncResult asyncReceive) 90 | { 91 | if (this.transportState == TransportState.closed) 92 | return; 93 | StateObject state = (StateObject)asyncReceive.AsyncState; 94 | Socket socket = this.socket; 95 | 96 | try 97 | { 98 | int length = socket.EndReceive(asyncReceive); 99 | 100 | this.onReceiving = false; 101 | 102 | if (length > 0) 103 | { 104 | processBytes(state.buffer, 0, length); 105 | //Receive next message 106 | if (this.transportState != TransportState.closed) receive(); 107 | } 108 | else 109 | { 110 | if (this.onDisconnect != null) this.onDisconnect(); 111 | } 112 | 113 | } 114 | catch (System.Net.Sockets.SocketException) 115 | { 116 | if (this.onDisconnect != null) 117 | this.onDisconnect(); 118 | } 119 | } 120 | 121 | internal void processBytes(byte[] bytes, int offset, int limit) 122 | { 123 | if (this.transportState == TransportState.readHead) 124 | { 125 | readHead(bytes, offset, limit); 126 | } 127 | else if (this.transportState == TransportState.readBody) 128 | { 129 | readBody(bytes, offset, limit); 130 | } 131 | } 132 | 133 | private bool readHead(byte[] bytes, int offset, int limit) 134 | { 135 | int length = limit - offset; 136 | int headNum = HeadLength - bufferOffset; 137 | 138 | if (length >= headNum) 139 | { 140 | //Write head buffer 141 | writeBytes(bytes, offset, headNum, bufferOffset, headBuffer); 142 | //Get package length 143 | pkgLength = (headBuffer[1] << 16) + (headBuffer[2] << 8) + headBuffer[3]; 144 | 145 | //Init message buffer 146 | buffer = new byte[HeadLength + pkgLength]; 147 | writeBytes(headBuffer, 0, HeadLength, buffer); 148 | offset += headNum; 149 | bufferOffset = HeadLength; 150 | this.transportState = TransportState.readBody; 151 | 152 | if (offset <= limit) processBytes(bytes, offset, limit); 153 | return true; 154 | } 155 | else 156 | { 157 | writeBytes(bytes, offset, length, bufferOffset, headBuffer); 158 | bufferOffset += length; 159 | return false; 160 | } 161 | } 162 | 163 | private void readBody(byte[] bytes, int offset, int limit) 164 | { 165 | int length = pkgLength + HeadLength - bufferOffset; 166 | if ((offset + length) <= limit) 167 | { 168 | writeBytes(bytes, offset, length, bufferOffset, buffer); 169 | offset += length; 170 | 171 | //Invoke the protocol api to handle the message 172 | this.messageProcesser.Invoke(buffer); 173 | this.bufferOffset = 0; 174 | this.pkgLength = 0; 175 | 176 | if (this.transportState != TransportState.closed) 177 | this.transportState = TransportState.readHead; 178 | if (offset < limit) 179 | processBytes(bytes, offset, limit); 180 | } 181 | else 182 | { 183 | writeBytes(bytes, offset, limit - offset, bufferOffset, buffer); 184 | bufferOffset += limit - offset; 185 | this.transportState = TransportState.readBody; 186 | } 187 | } 188 | 189 | private void writeBytes(byte[] source, int start, int length, byte[] target) 190 | { 191 | writeBytes(source, start, length, 0, target); 192 | } 193 | 194 | private void writeBytes(byte[] source, int start, int length, int offset, byte[] target) 195 | { 196 | for (int i = 0; i < length; i++) 197 | { 198 | target[offset + i] = source[start + i]; 199 | } 200 | } 201 | 202 | private void print(byte[] bytes, int offset, int length) 203 | { 204 | for (int i = offset; i < length; i++) 205 | Console.Write(Convert.ToString(bytes[i], 16) + " "); 206 | Console.WriteLine(); 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /pomelo-dotnetClient/src/transport/test/TransportTest.cs: -------------------------------------------------------------------------------- 1 | using Pomelo.DotNetClient; 2 | using System; 3 | using System.Collections.Generic; 4 | using SimpleJson; 5 | 6 | namespace Pomelo.DotNetClient.Test 7 | { 8 | public class TransportTest 9 | { 10 | static List result = new List(); 11 | 12 | public static byte[] genBuffer(int count) 13 | { 14 | int size = count + 6; 15 | byte[] buffer = new byte[count + 10]; 16 | 17 | //Transporter tp = new Transporter(new Object(), protocol); 18 | buffer[0] = (byte)PackageType.PKG_DATA; 19 | buffer[1] = Convert.ToByte(size >> 16 & 0xFF); 20 | buffer[2] = Convert.ToByte(size >> 8 & 0xFF); 21 | buffer[3] = Convert.ToByte(size & 0xFF); 22 | 23 | //Generate message head 24 | //Response 25 | buffer[4] = 4; 26 | buffer[5] = 128; 27 | buffer[6] = 1; 28 | 29 | //Route length 30 | buffer[7] = 2; 31 | 32 | //Route 33 | buffer[8] = 62; 34 | buffer[9] = 72; 35 | 36 | Random random = new Random(); 37 | for (var i = 0; i < count; i++) buffer[10 + i] = (byte)random.Next(255); 38 | 39 | return buffer; 40 | } 41 | 42 | public static byte[] generateBuffers(int num, out List list) 43 | { 44 | int length = 100; 45 | int index = 0; 46 | byte[] result = new byte[(length + 10) * num]; 47 | list = new List(); 48 | 49 | for (int i = 0; i < num; i++) 50 | { 51 | byte[] bytes = genBuffer(length); 52 | list.Add(bytes); 53 | for (int j = 0; j < bytes.Length; j++) result[index++] = bytes[j]; 54 | } 55 | 56 | return result; 57 | } 58 | 59 | public static void Run() 60 | { 61 | int num = 10; 62 | int limit = 1000; 63 | 64 | Transporter tc = new Transporter(null, process); 65 | 66 | List list; 67 | 68 | byte[] buffer = generateBuffers(num, out list); 69 | 70 | int offset = 0; 71 | while (offset < buffer.Length) 72 | { 73 | int length = 200; 74 | length = (offset + length) > buffer.Length ? buffer.Length - offset : length; 75 | 76 | tc.processBytes(buffer, offset, offset + length); 77 | offset += length; 78 | } 79 | 80 | if (!check(list)) 81 | { 82 | Console.WriteLine("Transport test failed!"); 83 | } 84 | else 85 | { 86 | Console.WriteLine("Transport test success!"); 87 | } 88 | } 89 | 90 | public static void process(byte[] bytes) 91 | { 92 | result.Add(bytes); 93 | //Console.WriteLine("add bytes : {0}", result.Count); 94 | } 95 | 96 | public static void protocolProcess(byte[] bytes) 97 | { 98 | JsonObject dict = new JsonObject(); 99 | JsonObject serverProtos = new JsonObject(); 100 | JsonObject clientProtos = new JsonObject(); 101 | 102 | MessageProtocol messageProtocol = new MessageProtocol(dict, serverProtos, clientProtos); 103 | Package pkg = PackageProtocol.decode(bytes); 104 | 105 | messageProtocol.decode(pkg.body); 106 | } 107 | 108 | public static bool check(List list) 109 | { 110 | byte[][] origin = list.ToArray(); 111 | byte[][] target = result.ToArray(); 112 | 113 | if (origin.Length != target.Length) return false; 114 | 115 | for (int i = 0; i < origin.Length; i++) 116 | { 117 | byte[] o = origin[i]; 118 | byte[] t = result[i]; 119 | 120 | if (o.Length != t.Length) return false; 121 | for (int j = 0; j < o.Length; j++) 122 | { 123 | if (o[j] != t[j]) return false; 124 | } 125 | } 126 | 127 | return true; 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /test/Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Pomelo.Protobuf.Test; 3 | 4 | namespace Pomelo.DotNetClient.Test 5 | { 6 | public class Test 7 | { 8 | public static void Main(string[] args) 9 | { 10 | byte[] bytes = Pomelo.Protobuf.Encoder.encodeUInt32(112321); 11 | Console.WriteLine(Pomelo.Protobuf.Decoder.decodeUInt32(bytes)); 12 | CodecTest.Run(); 13 | ProtobufTest.Run(); 14 | TransportTest.Run(); 15 | ClientTest.Run(); 16 | Console.ReadKey(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /test/json/msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "area.playerHandler.enterScene": { 3 | "map": { 4 | "name": "a", 5 | "width": 10, 6 | "height": 10, 7 | "tileW": 5, 8 | "tileH": 5, 9 | "weightMap": [ 10 | { 11 | "collisions": [] 12 | }, 13 | { 14 | "collisions": [] 15 | }, 16 | { 17 | "collisions": [ 18 | { 19 | "start": 1, 20 | "length": 3 21 | }, 22 | { 23 | "start": 79, 24 | "length": 3 25 | } 26 | ] 27 | }, 28 | { 29 | "collisions": [ 30 | { 31 | "start": 27, 32 | "length": 2 33 | }, 34 | { 35 | "start": 78, 36 | "length": 4 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | }, 43 | "onMove": { 44 | "entityId": 14, 45 | "path": [ 46 | { 47 | "x": 10, 48 | "y": 90 49 | }, 50 | { 51 | "x": 10, 52 | "y": 90 53 | } 54 | ], 55 | "speed": 160 56 | }, 57 | "onUpgrade": { 58 | "id": 32726, 59 | "entityId": 48, 60 | "name": "super1", 61 | "kindId": 210, 62 | "kindName": "Angle", 63 | "type": "player", 64 | "x": 755, 65 | "y": 608, 66 | "hp": 352, 67 | "mp": 32, 68 | "maxHp": 352, 69 | "maxMp": 32, 70 | "level": 3, 71 | "experience": 2, 72 | "attackValue": 37, 73 | "defenceValue": 15, 74 | "walkSpeed": 240, 75 | "attackSpeed": 1.2, 76 | "areaId": 1, 77 | "hitRate": 90, 78 | "dodgeRate": 13, 79 | "nextLevelExp": 114, 80 | "skillPoint": 3 81 | } 82 | } -------------------------------------------------------------------------------- /test/json/protos.json: -------------------------------------------------------------------------------- 1 | { 2 | "onMove": { 3 | "entityId": { 4 | "option": "required", 5 | "type": "uInt32", 6 | "tag": 1 7 | }, 8 | "path": { 9 | "option": "repeated", 10 | "type": "Path", 11 | "tag": 2 12 | }, 13 | "speed": { 14 | "option": "required", 15 | "type": "uInt32", 16 | "tag": 3 17 | }, 18 | "__messages": { 19 | "Path": { 20 | "x": { 21 | "option": "required", 22 | "type": "uInt32", 23 | "tag": 1 24 | }, 25 | "y": { 26 | "option": "required", 27 | "type": "uInt32", 28 | "tag": 2 29 | }, 30 | "__messages": {}, 31 | "__tags": { 32 | "1": "x", 33 | "2": "y" 34 | } 35 | } 36 | }, 37 | "__tags": { 38 | "1": "entityId", 39 | "2": "path", 40 | "3": "speed" 41 | } 42 | }, 43 | "onAttack": { 44 | "attacker": { 45 | "option": "required", 46 | "type": "uInt32", 47 | "tag": 1 48 | }, 49 | "target": { 50 | "option": "required", 51 | "type": "uInt32", 52 | "tag": 2 53 | }, 54 | "result": { 55 | "option": "required", 56 | "type": "AttackResult", 57 | "tag": 3 58 | }, 59 | "exp": { 60 | "option": "optional", 61 | "type": "uInt32", 62 | "tag": 4 63 | }, 64 | "reviveTime": { 65 | "option": "optional", 66 | "type": "uInt32", 67 | "tag": 5 68 | }, 69 | "skillId": { 70 | "option": "required", 71 | "type": "uInt32", 72 | "tag": 6 73 | }, 74 | "__messages": { 75 | "AttackResult": { 76 | "result": { 77 | "option": "required", 78 | "type": "uInt32", 79 | "tag": 1 80 | }, 81 | "damage": { 82 | "option": "required", 83 | "type": "uInt32", 84 | "tag": 2 85 | }, 86 | "items": { 87 | "option": "repeated", 88 | "type": "Item", 89 | "tag": 4 90 | }, 91 | "__messages": { 92 | "Item": { 93 | "kindId": { 94 | "option": "required", 95 | "type": "uInt32", 96 | "tag": 1 97 | }, 98 | "x": { 99 | "option": "required", 100 | "type": "uInt32", 101 | "tag": 2 102 | }, 103 | "y": { 104 | "option": "required", 105 | "type": "uInt32", 106 | "tag": 3 107 | }, 108 | "entityId": { 109 | "option": "required", 110 | "type": "uInt32", 111 | "tag": 4 112 | }, 113 | "playerId": { 114 | "option": "required", 115 | "type": "uInt32", 116 | "tag": 5 117 | }, 118 | "type": { 119 | "option": "required", 120 | "type": "string", 121 | "tag": 6 122 | }, 123 | "__messages": {}, 124 | "__tags": { 125 | "1": "kindId", 126 | "2": "x", 127 | "3": "y", 128 | "4": "entityId", 129 | "5": "playerId", 130 | "6": "type" 131 | } 132 | } 133 | }, 134 | "__tags": { 135 | "1": "result", 136 | "2": "damage", 137 | "4": "items" 138 | } 139 | } 140 | }, 141 | "__tags": { 142 | "1": "attacker", 143 | "2": "target", 144 | "3": "result", 145 | "4": "exp", 146 | "5": "reviveTime", 147 | "6": "skillId" 148 | } 149 | }, 150 | "onUpgrade": { 151 | "entityId": { 152 | "option": "required", 153 | "type": "uInt32", 154 | "tag": 1 155 | }, 156 | "kindId": { 157 | "option": "required", 158 | "type": "uInt32", 159 | "tag": 2 160 | }, 161 | "x": { 162 | "option": "required", 163 | "type": "uInt32", 164 | "tag": 3 165 | }, 166 | "y": { 167 | "option": "required", 168 | "type": "uInt32", 169 | "tag": 4 170 | }, 171 | "level": { 172 | "option": "required", 173 | "type": "uInt32", 174 | "tag": 5 175 | }, 176 | "walkSpeed": { 177 | "option": "required", 178 | "type": "uInt32", 179 | "tag": 6 180 | }, 181 | "hp": { 182 | "option": "required", 183 | "type": "uInt32", 184 | "tag": 7 185 | }, 186 | "maxHp": { 187 | "option": "required", 188 | "type": "uInt32", 189 | "tag": 8 190 | }, 191 | "mp": { 192 | "option": "required", 193 | "type": "uInt32", 194 | "tag": 9 195 | }, 196 | "maxMp": { 197 | "option": "required", 198 | "type": "uInt32", 199 | "tag": 10 200 | }, 201 | "id": { 202 | "option": "required", 203 | "type": "uInt32", 204 | "tag": 11 205 | }, 206 | "name": { 207 | "option": "required", 208 | "type": "string", 209 | "tag": 12 210 | }, 211 | "experience": { 212 | "option": "required", 213 | "type": "uInt32", 214 | "tag": 13 215 | }, 216 | "attackValue": { 217 | "option": "required", 218 | "type": "uInt32", 219 | "tag": 14 220 | }, 221 | "defenceValue": { 222 | "option": "required", 223 | "type": "uInt32", 224 | "tag": 15 225 | }, 226 | "attackSpeed": { 227 | "option": "required", 228 | "type": "double", 229 | "tag": 16 230 | }, 231 | "areaId": { 232 | "option": "required", 233 | "type": "uInt32", 234 | "tag": 17 235 | }, 236 | "hitRate": { 237 | "option": "required", 238 | "type": "uInt32", 239 | "tag": 18 240 | }, 241 | "dodgeRate": { 242 | "option": "required", 243 | "type": "uInt32", 244 | "tag": 19 245 | }, 246 | "nextLevelExp": { 247 | "option": "required", 248 | "type": "uInt32", 249 | "tag": 20 250 | }, 251 | "skillPoint": { 252 | "option": "required", 253 | "type": "uInt32", 254 | "tag": 21 255 | }, 256 | "kindName": { 257 | "option": "required", 258 | "type": "string", 259 | "tag": 22 260 | }, 261 | "type": { 262 | "option": "required", 263 | "type": "string", 264 | "tag": 23 265 | }, 266 | "__messages": {}, 267 | "__tags": { 268 | "1": "entityId", 269 | "2": "kindId", 270 | "3": "x", 271 | "4": "y", 272 | "5": "level", 273 | "6": "walkSpeed", 274 | "7": "hp", 275 | "8": "maxHp", 276 | "9": "mp", 277 | "10": "maxMp", 278 | "11": "id", 279 | "12": "name", 280 | "13": "experience", 281 | "14": "attackValue", 282 | "15": "defenceValue", 283 | "16": "attackSpeed", 284 | "17": "areaId", 285 | "18": "hitRate", 286 | "19": "dodgeRate", 287 | "20": "nextLevelExp", 288 | "21": "skillPoint", 289 | "22": "kindName", 290 | "23": "type" 291 | } 292 | }, 293 | "onPickItem": { 294 | "player": { 295 | "option": "required", 296 | "type": "uInt32", 297 | "tag": 1 298 | }, 299 | "item": { 300 | "option": "required", 301 | "type": "uInt32", 302 | "tag": 2 303 | }, 304 | "index": { 305 | "option": "required", 306 | "type": "uInt32", 307 | "tag": 3 308 | }, 309 | "__messages": {}, 310 | "__tags": { 311 | "1": "player", 312 | "2": "item", 313 | "3": "index" 314 | } 315 | }, 316 | "onNPCTalk": { 317 | "npc": { 318 | "option": "required", 319 | "type": "uInt32", 320 | "tag": 1 321 | }, 322 | "npcword": { 323 | "option": "required", 324 | "type": "string", 325 | "tag": 2 326 | }, 327 | "myword": { 328 | "option": "required", 329 | "type": "string", 330 | "tag": 3 331 | }, 332 | "__messages": {}, 333 | "__tags": { 334 | "1": "npc", 335 | "2": "npcword", 336 | "3": "myword" 337 | } 338 | }, 339 | "onRevive": { 340 | "entityId": { 341 | "option": "required", 342 | "type": "uInt32", 343 | "tag": 1 344 | }, 345 | "x": { 346 | "option": "required", 347 | "type": "uInt32", 348 | "tag": 2 349 | }, 350 | "y": { 351 | "option": "required", 352 | "type": "uInt32", 353 | "tag": 3 354 | }, 355 | "hp": { 356 | "option": "required", 357 | "type": "uInt32", 358 | "tag": 4 359 | }, 360 | "__messages": {}, 361 | "__tags": { 362 | "1": "entityId", 363 | "2": "x", 364 | "3": "y", 365 | "4": "hp" 366 | } 367 | }, 368 | "onAddEntities": { 369 | "npc": { 370 | "option": "repeated", 371 | "type": "NPC", 372 | "tag": 1 373 | }, 374 | "mob": { 375 | "option": "repeated", 376 | "type": "Mob", 377 | "tag": 2 378 | }, 379 | "item": { 380 | "option": "repeated", 381 | "type": "Item", 382 | "tag": 3 383 | }, 384 | "euipment": { 385 | "option": "repeated", 386 | "type": "Equipment", 387 | "tag": 4 388 | }, 389 | "player": { 390 | "option": "repeated", 391 | "type": "Player", 392 | "tag": 5 393 | }, 394 | "__messages": { 395 | "NPC": { 396 | "entityId": { 397 | "option": "required", 398 | "type": "uInt32", 399 | "tag": 1 400 | }, 401 | "kindId": { 402 | "option": "required", 403 | "type": "uInt32", 404 | "tag": 2 405 | }, 406 | "x": { 407 | "option": "required", 408 | "type": "uInt32", 409 | "tag": 3 410 | }, 411 | "y": { 412 | "option": "required", 413 | "type": "uInt32", 414 | "tag": 4 415 | }, 416 | "__messages": {}, 417 | "__tags": { 418 | "1": "entityId", 419 | "2": "kindId", 420 | "3": "x", 421 | "4": "y" 422 | } 423 | }, 424 | "Mob": { 425 | "entityId": { 426 | "option": "required", 427 | "type": "uInt32", 428 | "tag": 1 429 | }, 430 | "kindId": { 431 | "option": "required", 432 | "type": "uInt32", 433 | "tag": 2 434 | }, 435 | "x": { 436 | "option": "required", 437 | "type": "uInt32", 438 | "tag": 3 439 | }, 440 | "y": { 441 | "option": "required", 442 | "type": "uInt32", 443 | "tag": 4 444 | }, 445 | "level": { 446 | "option": "required", 447 | "type": "uInt32", 448 | "tag": 5 449 | }, 450 | "walkSpeed": { 451 | "option": "required", 452 | "type": "uInt32", 453 | "tag": 6 454 | }, 455 | "hp": { 456 | "option": "required", 457 | "type": "uInt32", 458 | "tag": 7 459 | }, 460 | "maxHp": { 461 | "option": "required", 462 | "type": "uInt32", 463 | "tag": 8 464 | }, 465 | "__messages": {}, 466 | "__tags": { 467 | "1": "entityId", 468 | "2": "kindId", 469 | "3": "x", 470 | "4": "y", 471 | "5": "level", 472 | "6": "walkSpeed", 473 | "7": "hp", 474 | "8": "maxHp" 475 | } 476 | }, 477 | "Item": { 478 | "entityId": { 479 | "option": "required", 480 | "type": "uInt32", 481 | "tag": 1 482 | }, 483 | "kindId": { 484 | "option": "required", 485 | "type": "uInt32", 486 | "tag": 2 487 | }, 488 | "x": { 489 | "option": "required", 490 | "type": "uInt32", 491 | "tag": 3 492 | }, 493 | "y": { 494 | "option": "required", 495 | "type": "uInt32", 496 | "tag": 4 497 | }, 498 | "playerId": { 499 | "option": "required", 500 | "type": "uInt32", 501 | "tag": 5 502 | }, 503 | "__messages": {}, 504 | "__tags": { 505 | "1": "entityId", 506 | "2": "kindId", 507 | "3": "x", 508 | "4": "y", 509 | "5": "playerId" 510 | } 511 | }, 512 | "Equipment": { 513 | "entityId": { 514 | "option": "required", 515 | "type": "uInt32", 516 | "tag": 1 517 | }, 518 | "kindId": { 519 | "option": "required", 520 | "type": "uInt32", 521 | "tag": 2 522 | }, 523 | "x": { 524 | "option": "required", 525 | "type": "uInt32", 526 | "tag": 3 527 | }, 528 | "y": { 529 | "option": "required", 530 | "type": "uInt32", 531 | "tag": 4 532 | }, 533 | "playerId": { 534 | "option": "required", 535 | "type": "uInt32", 536 | "tag": 5 537 | }, 538 | "__messages": {}, 539 | "__tags": { 540 | "1": "entityId", 541 | "2": "kindId", 542 | "3": "x", 543 | "4": "y", 544 | "5": "playerId" 545 | } 546 | }, 547 | "Player": { 548 | "entityId": { 549 | "option": "required", 550 | "type": "uInt32", 551 | "tag": 1 552 | }, 553 | "kindId": { 554 | "option": "required", 555 | "type": "uInt32", 556 | "tag": 2 557 | }, 558 | "x": { 559 | "option": "required", 560 | "type": "uInt32", 561 | "tag": 3 562 | }, 563 | "y": { 564 | "option": "required", 565 | "type": "uInt32", 566 | "tag": 4 567 | }, 568 | "level": { 569 | "option": "required", 570 | "type": "uInt32", 571 | "tag": 5 572 | }, 573 | "walkSpeed": { 574 | "option": "required", 575 | "type": "uInt32", 576 | "tag": 6 577 | }, 578 | "hp": { 579 | "option": "required", 580 | "type": "uInt32", 581 | "tag": 7 582 | }, 583 | "maxHp": { 584 | "option": "required", 585 | "type": "uInt32", 586 | "tag": 8 587 | }, 588 | "mp": { 589 | "option": "required", 590 | "type": "uInt32", 591 | "tag": 9 592 | }, 593 | "maxMp": { 594 | "option": "required", 595 | "type": "uInt32", 596 | "tag": 10 597 | }, 598 | "id": { 599 | "option": "required", 600 | "type": "uInt32", 601 | "tag": 11 602 | }, 603 | "name": { 604 | "option": "required", 605 | "type": "string", 606 | "tag": 12 607 | }, 608 | "__messages": {}, 609 | "__tags": { 610 | "1": "entityId", 611 | "2": "kindId", 612 | "3": "x", 613 | "4": "y", 614 | "5": "level", 615 | "6": "walkSpeed", 616 | "7": "hp", 617 | "8": "maxHp", 618 | "9": "mp", 619 | "10": "maxMp", 620 | "11": "id", 621 | "12": "name" 622 | } 623 | } 624 | }, 625 | "__tags": { 626 | "1": "npc", 627 | "2": "mob", 628 | "3": "item", 629 | "4": "euipment", 630 | "5": "player" 631 | } 632 | }, 633 | "onRemoveEntities": { 634 | "entities": { 635 | "option": "repeated", 636 | "type": "uInt32", 637 | "tag": 1 638 | }, 639 | "__messages": {}, 640 | "__tags": { 641 | "1": "entities" 642 | } 643 | }, 644 | "onPathCheckOut": { 645 | "entityId": { 646 | "option": "required", 647 | "type": "uInt32", 648 | "tag": 1 649 | }, 650 | "position": { 651 | "option": "required", 652 | "type": "Position", 653 | "tag": 2 654 | }, 655 | "__messages": { 656 | "Position": { 657 | "x": { 658 | "option": "required", 659 | "type": "uInt32", 660 | "tag": 1 661 | }, 662 | "y": { 663 | "option": "required", 664 | "type": "uInt32", 665 | "tag": 2 666 | }, 667 | "__messages": {}, 668 | "__tags": { 669 | "1": "x", 670 | "2": "y" 671 | } 672 | } 673 | }, 674 | "__tags": { 675 | "1": "entityId", 676 | "2": "position" 677 | } 678 | }, 679 | "area.playerHandler.enterScene": { 680 | "entities": { 681 | "option": "optional", 682 | "type": "Entities", 683 | "tag": 1 684 | }, 685 | "curPlayer": { 686 | "option": "optional", 687 | "type": "Player", 688 | "tag": 2 689 | }, 690 | "map": { 691 | "option": "required", 692 | "type": "Map", 693 | "tag": 3 694 | }, 695 | "__messages": { 696 | "Entities": { 697 | "npc": { 698 | "option": "repeated", 699 | "type": "NPC", 700 | "tag": 1 701 | }, 702 | "mob": { 703 | "option": "repeated", 704 | "type": "Mob", 705 | "tag": 2 706 | }, 707 | "item": { 708 | "option": "repeated", 709 | "type": "Item", 710 | "tag": 3 711 | }, 712 | "euipment": { 713 | "option": "repeated", 714 | "type": "Equipment", 715 | "tag": 4 716 | }, 717 | "player": { 718 | "option": "repeated", 719 | "type": "Player", 720 | "tag": 5 721 | }, 722 | "__messages": { 723 | "NPC": { 724 | "entityId": { 725 | "option": "required", 726 | "type": "uInt32", 727 | "tag": 1 728 | }, 729 | "kindId": { 730 | "option": "required", 731 | "type": "uInt32", 732 | "tag": 2 733 | }, 734 | "x": { 735 | "option": "required", 736 | "type": "uInt32", 737 | "tag": 3 738 | }, 739 | "y": { 740 | "option": "required", 741 | "type": "uInt32", 742 | "tag": 4 743 | }, 744 | "__messages": {}, 745 | "__tags": { 746 | "1": "entityId", 747 | "2": "kindId", 748 | "3": "x", 749 | "4": "y" 750 | } 751 | }, 752 | "Mob": { 753 | "entityId": { 754 | "option": "required", 755 | "type": "uInt32", 756 | "tag": 1 757 | }, 758 | "kindId": { 759 | "option": "required", 760 | "type": "uInt32", 761 | "tag": 2 762 | }, 763 | "x": { 764 | "option": "required", 765 | "type": "uInt32", 766 | "tag": 3 767 | }, 768 | "y": { 769 | "option": "required", 770 | "type": "uInt32", 771 | "tag": 4 772 | }, 773 | "level": { 774 | "option": "required", 775 | "type": "uInt32", 776 | "tag": 5 777 | }, 778 | "walkSpeed": { 779 | "option": "required", 780 | "type": "uInt32", 781 | "tag": 6 782 | }, 783 | "hp": { 784 | "option": "required", 785 | "type": "uInt32", 786 | "tag": 7 787 | }, 788 | "maxHp": { 789 | "option": "required", 790 | "type": "uInt32", 791 | "tag": 8 792 | }, 793 | "__messages": {}, 794 | "__tags": { 795 | "1": "entityId", 796 | "2": "kindId", 797 | "3": "x", 798 | "4": "y", 799 | "5": "level", 800 | "6": "walkSpeed", 801 | "7": "hp", 802 | "8": "maxHp" 803 | } 804 | }, 805 | "Item": { 806 | "entityId": { 807 | "option": "required", 808 | "type": "uInt32", 809 | "tag": 1 810 | }, 811 | "kindId": { 812 | "option": "required", 813 | "type": "uInt32", 814 | "tag": 2 815 | }, 816 | "x": { 817 | "option": "required", 818 | "type": "uInt32", 819 | "tag": 3 820 | }, 821 | "y": { 822 | "option": "required", 823 | "type": "uInt32", 824 | "tag": 4 825 | }, 826 | "playerId": { 827 | "option": "required", 828 | "type": "uInt32", 829 | "tag": 5 830 | }, 831 | "__messages": {}, 832 | "__tags": { 833 | "1": "entityId", 834 | "2": "kindId", 835 | "3": "x", 836 | "4": "y", 837 | "5": "playerId" 838 | } 839 | }, 840 | "Equipment": { 841 | "entityId": { 842 | "option": "required", 843 | "type": "uInt32", 844 | "tag": 1 845 | }, 846 | "kindId": { 847 | "option": "required", 848 | "type": "uInt32", 849 | "tag": 2 850 | }, 851 | "x": { 852 | "option": "required", 853 | "type": "uInt32", 854 | "tag": 3 855 | }, 856 | "y": { 857 | "option": "required", 858 | "type": "uInt32", 859 | "tag": 4 860 | }, 861 | "playerId": { 862 | "option": "required", 863 | "type": "uInt32", 864 | "tag": 5 865 | }, 866 | "__messages": {}, 867 | "__tags": { 868 | "1": "entityId", 869 | "2": "kindId", 870 | "3": "x", 871 | "4": "y", 872 | "5": "playerId" 873 | } 874 | }, 875 | "Player": { 876 | "entityId": { 877 | "option": "required", 878 | "type": "uInt32", 879 | "tag": 1 880 | }, 881 | "kindId": { 882 | "option": "required", 883 | "type": "uInt32", 884 | "tag": 2 885 | }, 886 | "x": { 887 | "option": "required", 888 | "type": "uInt32", 889 | "tag": 3 890 | }, 891 | "y": { 892 | "option": "required", 893 | "type": "uInt32", 894 | "tag": 4 895 | }, 896 | "level": { 897 | "option": "required", 898 | "type": "uInt32", 899 | "tag": 5 900 | }, 901 | "walkSpeed": { 902 | "option": "required", 903 | "type": "uInt32", 904 | "tag": 6 905 | }, 906 | "hp": { 907 | "option": "required", 908 | "type": "uInt32", 909 | "tag": 7 910 | }, 911 | "maxHp": { 912 | "option": "required", 913 | "type": "uInt32", 914 | "tag": 8 915 | }, 916 | "mp": { 917 | "option": "required", 918 | "type": "uInt32", 919 | "tag": 9 920 | }, 921 | "maxMp": { 922 | "option": "required", 923 | "type": "uInt32", 924 | "tag": 10 925 | }, 926 | "id": { 927 | "option": "required", 928 | "type": "uInt32", 929 | "tag": 11 930 | }, 931 | "name": { 932 | "option": "required", 933 | "type": "string", 934 | "tag": 12 935 | }, 936 | "__messages": {}, 937 | "__tags": { 938 | "1": "entityId", 939 | "2": "kindId", 940 | "3": "x", 941 | "4": "y", 942 | "5": "level", 943 | "6": "walkSpeed", 944 | "7": "hp", 945 | "8": "maxHp", 946 | "9": "mp", 947 | "10": "maxMp", 948 | "11": "id", 949 | "12": "name" 950 | } 951 | } 952 | }, 953 | "__tags": { 954 | "1": "npc", 955 | "2": "mob", 956 | "3": "item", 957 | "4": "euipment", 958 | "5": "player" 959 | } 960 | }, 961 | "Player": { 962 | "entityId": { 963 | "option": "required", 964 | "type": "uInt32", 965 | "tag": 1 966 | }, 967 | "kindId": { 968 | "option": "required", 969 | "type": "uInt32", 970 | "tag": 2 971 | }, 972 | "x": { 973 | "option": "required", 974 | "type": "uInt32", 975 | "tag": 3 976 | }, 977 | "y": { 978 | "option": "required", 979 | "type": "uInt32", 980 | "tag": 4 981 | }, 982 | "level": { 983 | "option": "required", 984 | "type": "uInt32", 985 | "tag": 5 986 | }, 987 | "walkSpeed": { 988 | "option": "required", 989 | "type": "uInt32", 990 | "tag": 6 991 | }, 992 | "hp": { 993 | "option": "required", 994 | "type": "uInt32", 995 | "tag": 7 996 | }, 997 | "maxHp": { 998 | "option": "required", 999 | "type": "uInt32", 1000 | "tag": 8 1001 | }, 1002 | "mp": { 1003 | "option": "required", 1004 | "type": "uInt32", 1005 | "tag": 9 1006 | }, 1007 | "maxMp": { 1008 | "option": "required", 1009 | "type": "uInt32", 1010 | "tag": 10 1011 | }, 1012 | "id": { 1013 | "option": "required", 1014 | "type": "uInt32", 1015 | "tag": 11 1016 | }, 1017 | "name": { 1018 | "option": "required", 1019 | "type": "string", 1020 | "tag": 12 1021 | }, 1022 | "experience": { 1023 | "option": "required", 1024 | "type": "uInt32", 1025 | "tag": 13 1026 | }, 1027 | "attackValue": { 1028 | "option": "required", 1029 | "type": "uInt32", 1030 | "tag": 14 1031 | }, 1032 | "defenceValue": { 1033 | "option": "required", 1034 | "type": "uInt32", 1035 | "tag": 15 1036 | }, 1037 | "attackSpeed": { 1038 | "option": "required", 1039 | "type": "double", 1040 | "tag": 16 1041 | }, 1042 | "areaId": { 1043 | "option": "required", 1044 | "type": "uInt32", 1045 | "tag": 17 1046 | }, 1047 | "hitRate": { 1048 | "option": "required", 1049 | "type": "uInt32", 1050 | "tag": 18 1051 | }, 1052 | "dodgeRate": { 1053 | "option": "required", 1054 | "type": "uInt32", 1055 | "tag": 19 1056 | }, 1057 | "nextLevelExp": { 1058 | "option": "required", 1059 | "type": "uInt32", 1060 | "tag": 20 1061 | }, 1062 | "skillPoint": { 1063 | "option": "required", 1064 | "type": "uInt32", 1065 | "tag": 21 1066 | }, 1067 | "type": { 1068 | "option": "required", 1069 | "type": "string", 1070 | "tag": 22 1071 | }, 1072 | "bag": { 1073 | "option": "required", 1074 | "type": "Bag", 1075 | "tag": 23 1076 | }, 1077 | "equipments": { 1078 | "option": "required", 1079 | "type": "Equipments", 1080 | "tag": 24 1081 | }, 1082 | "fightSkills": { 1083 | "option": "repeated", 1084 | "type": "FightSkill", 1085 | "tag": 25 1086 | }, 1087 | "__messages": { 1088 | "Bag": { 1089 | "itemCount": { 1090 | "option": "required", 1091 | "type": "uInt32", 1092 | "tag": 1 1093 | }, 1094 | "items": { 1095 | "option": "repeated", 1096 | "type": "Item", 1097 | "tag": 2 1098 | }, 1099 | "__messages": { 1100 | "Item": { 1101 | "key": { 1102 | "option": "required", 1103 | "type": "uInt32", 1104 | "tag": 1 1105 | }, 1106 | "id": { 1107 | "option": "required", 1108 | "type": "uInt32", 1109 | "tag": 2 1110 | }, 1111 | "type": { 1112 | "option": "required", 1113 | "type": "string", 1114 | "tag": 3 1115 | }, 1116 | "__messages": {}, 1117 | "__tags": { 1118 | "1": "key", 1119 | "2": "id", 1120 | "3": "type" 1121 | } 1122 | } 1123 | }, 1124 | "__tags": { 1125 | "1": "itemCount", 1126 | "2": "items" 1127 | } 1128 | }, 1129 | "Equipments": { 1130 | "weapon": { 1131 | "option": "required", 1132 | "type": "uInt32", 1133 | "tag": 1 1134 | }, 1135 | "armor": { 1136 | "option": "required", 1137 | "type": "uInt32", 1138 | "tag": 2 1139 | }, 1140 | "helmet": { 1141 | "option": "required", 1142 | "type": "uInt32", 1143 | "tag": 3 1144 | }, 1145 | "necklace": { 1146 | "option": "required", 1147 | "type": "uInt32", 1148 | "tag": 4 1149 | }, 1150 | "ring": { 1151 | "option": "required", 1152 | "type": "uInt32", 1153 | "tag": 5 1154 | }, 1155 | "belt": { 1156 | "option": "required", 1157 | "type": "uInt32", 1158 | "tag": 6 1159 | }, 1160 | "shoes": { 1161 | "option": "required", 1162 | "type": "uInt32", 1163 | "tag": 7 1164 | }, 1165 | "legguard": { 1166 | "option": "required", 1167 | "type": "uInt32", 1168 | "tag": 8 1169 | }, 1170 | "amulet": { 1171 | "option": "required", 1172 | "type": "uInt32", 1173 | "tag": 9 1174 | }, 1175 | "__messages": {}, 1176 | "__tags": { 1177 | "1": "weapon", 1178 | "2": "armor", 1179 | "3": "helmet", 1180 | "4": "necklace", 1181 | "5": "ring", 1182 | "6": "belt", 1183 | "7": "shoes", 1184 | "8": "legguard", 1185 | "9": "amulet" 1186 | } 1187 | }, 1188 | "FightSkill": { 1189 | "id": { 1190 | "option": "required", 1191 | "type": "uInt32", 1192 | "tag": 1 1193 | }, 1194 | "level": { 1195 | "option": "required", 1196 | "type": "uInt32", 1197 | "tag": 2 1198 | }, 1199 | "__messages": {}, 1200 | "__tags": { 1201 | "1": "id", 1202 | "2": "level" 1203 | } 1204 | } 1205 | }, 1206 | "__tags": { 1207 | "1": "entityId", 1208 | "2": "kindId", 1209 | "3": "x", 1210 | "4": "y", 1211 | "5": "level", 1212 | "6": "walkSpeed", 1213 | "7": "hp", 1214 | "8": "maxHp", 1215 | "9": "mp", 1216 | "10": "maxMp", 1217 | "11": "id", 1218 | "12": "name", 1219 | "13": "experience", 1220 | "14": "attackValue", 1221 | "15": "defenceValue", 1222 | "16": "attackSpeed", 1223 | "17": "areaId", 1224 | "18": "hitRate", 1225 | "19": "dodgeRate", 1226 | "20": "nextLevelExp", 1227 | "21": "skillPoint", 1228 | "22": "type", 1229 | "23": "bag", 1230 | "24": "equipments", 1231 | "25": "fightSkills" 1232 | } 1233 | }, 1234 | "Map": { 1235 | "name": { 1236 | "option": "required", 1237 | "type": "string", 1238 | "tag": 1 1239 | }, 1240 | "width": { 1241 | "option": "required", 1242 | "type": "uInt32", 1243 | "tag": 2 1244 | }, 1245 | "height": { 1246 | "option": "required", 1247 | "type": "uInt32", 1248 | "tag": 3 1249 | }, 1250 | "tileW": { 1251 | "option": "required", 1252 | "type": "uInt32", 1253 | "tag": 4 1254 | }, 1255 | "tileH": { 1256 | "option": "required", 1257 | "type": "uInt32", 1258 | "tag": 5 1259 | }, 1260 | "weightMap": { 1261 | "option": "repeated", 1262 | "type": "Collisions", 1263 | "tag": 6 1264 | }, 1265 | "__messages": { 1266 | "Collisions": { 1267 | "collisions": { 1268 | "option": "repeated", 1269 | "type": "Collison", 1270 | "tag": 1 1271 | }, 1272 | "__messages": { 1273 | "Collison": { 1274 | "start": { 1275 | "option": "required", 1276 | "type": "uInt32", 1277 | "tag": 1 1278 | }, 1279 | "length": { 1280 | "option": "required", 1281 | "type": "uInt32", 1282 | "tag": 2 1283 | }, 1284 | "__messages": {}, 1285 | "__tags": { 1286 | "1": "start", 1287 | "2": "length" 1288 | } 1289 | } 1290 | }, 1291 | "__tags": { 1292 | "1": "collisions" 1293 | } 1294 | } 1295 | }, 1296 | "__tags": { 1297 | "1": "name", 1298 | "2": "width", 1299 | "3": "height", 1300 | "4": "tileW", 1301 | "5": "tileH", 1302 | "6": "weightMap" 1303 | } 1304 | } 1305 | }, 1306 | "__tags": { 1307 | "1": "entities", 1308 | "2": "curPlayer", 1309 | "3": "map" 1310 | } 1311 | } 1312 | } -------------------------------------------------------------------------------- /test/json/rootMsg.json: -------------------------------------------------------------------------------- 1 | { 2 | "onMove": { 3 | "entityId": 14, 4 | "path": [ 5 | { 6 | "x": 128, 7 | "y": 796 8 | }, 9 | { 10 | "x": 677, 11 | "y": 895 12 | } 13 | ], 14 | "speed": 160 15 | }, 16 | "area.playerHandler.enterScene": { 17 | "entities": { 18 | "item": [ 19 | { 20 | "entityId": 1, 21 | "kindId": 1001 22 | }, 23 | { 24 | "entityId": 2, 25 | "kindId": 1002 26 | } 27 | ], 28 | "equipment": [ 29 | { 30 | "entityId": 1, 31 | "kindId": 2001 32 | }, 33 | { 34 | "entityId": 2, 35 | "kindId": 2002 36 | } 37 | ] 38 | }, 39 | "curPlayer": { 40 | "entityId": 1, 41 | "kindId": 3001, 42 | "bag": { 43 | "items": [ 44 | { 45 | "id": 1, 46 | "type": "pomelo" 47 | }, 48 | { 49 | "id": 2, 50 | "type": "protobuf" 51 | } 52 | ] 53 | }, 54 | "equipments": [ 55 | { 56 | "entityId": 1, 57 | "kindId": 2001 58 | }, 59 | { 60 | "entityId": 2, 61 | "kindId": 2002 62 | } 63 | ] 64 | }, 65 | "map": { 66 | "name": "a", 67 | "width": 10, 68 | "height": 10, 69 | "tileW": 5, 70 | "tileH": 5, 71 | "weightMap": [ 72 | { 73 | "collisions": [] 74 | }, 75 | { 76 | "collisions": [] 77 | }, 78 | { 79 | "collisions": [ 80 | { 81 | "start": 1, 82 | "length": 3 83 | }, 84 | { 85 | "start": 79, 86 | "length": 3 87 | } 88 | ] 89 | }, 90 | { 91 | "collisions": [ 92 | { 93 | "start": 27, 94 | "length": 2 95 | }, 96 | { 97 | "start": 78, 98 | "length": 4 99 | } 100 | ] 101 | } 102 | ] 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /test/json/rootProtos.json: -------------------------------------------------------------------------------- 1 | { 2 | "message Path": { 3 | "x": { 4 | "option": "required", 5 | "type": "double", 6 | "tag": 1 7 | }, 8 | "y": { 9 | "option": "required", 10 | "type": "double", 11 | "tag": 2 12 | }, 13 | "__messages": {}, 14 | "__tags": { 15 | "1": "x", 16 | "2": "y" 17 | } 18 | }, 19 | "message Map": { 20 | "name": { 21 | "option": "required", 22 | "type": "string", 23 | "tag": 1 24 | }, 25 | "width": { 26 | "option": "required", 27 | "type": "uInt32", 28 | "tag": 2 29 | }, 30 | "height": { 31 | "option": "required", 32 | "type": "uInt32", 33 | "tag": 3 34 | }, 35 | "tileW": { 36 | "option": "required", 37 | "type": "uInt32", 38 | "tag": 4 39 | }, 40 | "tileH": { 41 | "option": "required", 42 | "type": "uInt32", 43 | "tag": 5 44 | }, 45 | "weightMap": { 46 | "option": "repeated", 47 | "type": "Collisions", 48 | "tag": 6 49 | }, 50 | "__messages": { 51 | "Collisions": { 52 | "collisions": { 53 | "option": "repeated", 54 | "type": "Collison", 55 | "tag": 1 56 | }, 57 | "__messages": { 58 | "Collison": { 59 | "start": { 60 | "option": "required", 61 | "type": "uInt32", 62 | "tag": 1 63 | }, 64 | "length": { 65 | "option": "required", 66 | "type": "uInt32", 67 | "tag": 2 68 | }, 69 | "__messages": {}, 70 | "__tags": { 71 | "1": "start", 72 | "2": "length" 73 | } 74 | } 75 | }, 76 | "__tags": { 77 | "1": "collisions" 78 | } 79 | } 80 | }, 81 | "__tags": { 82 | "1": "name", 83 | "2": "width", 84 | "3": "height", 85 | "4": "tileW", 86 | "5": "tileH", 87 | "6": "weightMap" 88 | } 89 | }, 90 | "message Item": { 91 | "entityId": { 92 | "option": "required", 93 | "type": "uInt32", 94 | "tag": 1 95 | }, 96 | "kindId": { 97 | "option": "required", 98 | "type": "uInt32", 99 | "tag": 2 100 | }, 101 | "__messages": {}, 102 | "__tags": { 103 | "1": "entityId", 104 | "2": "kindId" 105 | } 106 | }, 107 | "message Equipment": { 108 | "entityId": { 109 | "option": "required", 110 | "type": "uInt32", 111 | "tag": 1 112 | }, 113 | "kindId": { 114 | "option": "required", 115 | "type": "uInt32", 116 | "tag": 2 117 | }, 118 | "__messages": {}, 119 | "__tags": { 120 | "1": "entityId", 121 | "2": "kindId" 122 | } 123 | }, 124 | "message Entities": { 125 | "item": { 126 | "option": "repeated", 127 | "type": "Item", 128 | "tag": 1 129 | }, 130 | "equipment": { 131 | "option": "repeated", 132 | "type": "Equipment", 133 | "tag": 2 134 | }, 135 | "__messages": {}, 136 | "__tags": { 137 | "1": "item", 138 | "2": "equipment" 139 | } 140 | }, 141 | "onMove": { 142 | "entityId": { 143 | "option": "required", 144 | "type": "uInt32", 145 | "tag": 1 146 | }, 147 | "path": { 148 | "option": "repeated", 149 | "type": "Path", 150 | "tag": 2 151 | }, 152 | "speed": { 153 | "option": "required", 154 | "type": "float", 155 | "tag": 3 156 | }, 157 | "__messages": {}, 158 | "__tags": { 159 | "1": "entityId", 160 | "2": "path", 161 | "3": "speed" 162 | } 163 | }, 164 | "area.playerHandler.enterScene": { 165 | "entities": { 166 | "option": "optional", 167 | "type": "Entities", 168 | "tag": 1 169 | }, 170 | "curPlayer": { 171 | "option": "optional", 172 | "type": "Player", 173 | "tag": 2 174 | }, 175 | "map": { 176 | "option": "required", 177 | "type": "Map", 178 | "tag": 3 179 | }, 180 | "__messages": { 181 | "Player": { 182 | "entityId": { 183 | "option": "required", 184 | "type": "uInt32", 185 | "tag": 1 186 | }, 187 | "kindId": { 188 | "option": "required", 189 | "type": "uInt32", 190 | "tag": 2 191 | }, 192 | "bag": { 193 | "option": "required", 194 | "type": "Bag", 195 | "tag": 3 196 | }, 197 | "equipments": { 198 | "option": "repeated", 199 | "type": "Equipment", 200 | "tag": 4 201 | }, 202 | "__messages": { 203 | "Bag": { 204 | "items": { 205 | "option": "repeated", 206 | "type": "Item", 207 | "tag": 1 208 | }, 209 | "__messages": { 210 | "Item": { 211 | "id": { 212 | "option": "required", 213 | "type": "uInt32", 214 | "tag": 1 215 | }, 216 | "type": { 217 | "option": "optional", 218 | "type": "string", 219 | "tag": 2 220 | }, 221 | "__messages": {}, 222 | "__tags": { 223 | "1": "id", 224 | "2": "type" 225 | } 226 | } 227 | }, 228 | "__tags": { 229 | "1": "items" 230 | } 231 | } 232 | }, 233 | "__tags": { 234 | "1": "entityId", 235 | "2": "kindId", 236 | "3": "bag", 237 | "4": "equipments" 238 | } 239 | } 240 | }, 241 | "__tags": { 242 | "1": "entities", 243 | "2": "curPlayer", 244 | "3": "map" 245 | } 246 | } 247 | } -------------------------------------------------------------------------------- /test/test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0FFBA04C-F690-41DB-A795-BD6EC44C04AA} 8 | Exe 9 | Properties 10 | test 11 | test 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\lib\SimpleJson.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {0752f53d-46ef-44e2-aaa9-f2fab9f0c4fa} 52 | pomelo-dotnetClient 53 | 54 | 55 | 56 | 57 | 58 | 59 | 66 | --------------------------------------------------------------------------------