├── .gitignore
├── .jshintrc
├── .project
├── LICENSE
├── README.md
├── component.json
├── lib
├── client
│ └── protobuf.js
├── codec.js
├── constant.js
├── decoder.js
├── encoder.js
├── parser.js
├── protobuf.js
└── util.js
├── package.json
└── test
├── client
├── encoderTest.js
├── protobufTest.js
└── rootMsgTest.js
├── codecTest.js
├── example.json
├── msg.json
├── protobufTest.js
├── protos.json
├── rootMsg.json
├── rootMsgTC.js
├── rootMsgTest.js
├── rootProtos.json
├── stringBufferTest.js
├── testMsg.js
└── writeProtos.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | *.log
3 | .DS_Store
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "describe",
4 | "it",
5 | "before",
6 | "after",
7 | "window",
8 | "__resources__"
9 | ],
10 | "es5": true,
11 | "node": true,
12 | "eqeqeq": true,
13 | "undef": true,
14 | "curly": true,
15 | "bitwise": true,
16 | "immed": false,
17 | "newcap": true,
18 | "nonew": true,
19 | "white": false,
20 | "smarttabs": true,
21 | "strict": false
22 | }
23 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | pomelo-protobuf
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2012 Netease, Inc. and other pomelo contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Pomelo-protobuf
2 | Protobuf protocol is a high efficient binary protocol for data encode, this module implement the protobuf protocol, and used in [pomelo](https://github.com/NetEase/pomelo) for data transfer.
3 | Of course, pomelo-protobuf can also be used independently in other projects.
4 | ##Architecture
5 | Unlike the google protobuf, we provide a universal encoder and decoder in pomelo-protobuf. We use protos file as meta data to encode/decode messages, so you do not need to add any code to your project, instead , what you need is to add a protos.json (or two for different encoder and decoder messages) files to define the message need to encode by protobuf.The architecture of pomelo-protobuf is as follow:
6 |
7 | 
8 |
9 | ##Usage
10 | ###Define protos
11 | To use pomelo-protobuf, you need to write a JSON file to define the message format. The syntax of the file is as the same as the .proto file in protobuf, but in JSON format, here is the example protos.json:
12 |
13 | ```
14 | {
15 | "onMove" : {
16 | "required uInt32 entityId" : 1,
17 | "message Path": {
18 | "required uInt32 x" : 1,
19 | "required uInt32 y" : 2
20 | },
21 | "repeated Path path" : 2,
22 | "required uInt32 speed" : 3
23 | },
24 | "onAttack" : {
25 | "required uInt32 attacker" : 1,
26 | "required uInt32 target" : 2,
27 | "message AttackResult" : {
28 | "required uInt32 result" : 1,
29 | "required uInt32 damage" : 2,
30 | "optional uInt32 exp" : 3
31 | },
32 | "required AttackResult result" : 3
33 | }
34 | }
35 | ```
36 |
37 | Unlike the google protobuf, we write all the protos in the same file, with a unique key to define the message.
38 |
39 | To use the protos, we use a parser to parse the protos file into more machine friendly format, which is also a json format, then you can use the result to decode/encode messages.
40 |
41 | ###RootMessage support
42 | you can write rootMessage in protos for global usage
43 | ```
44 | {
45 | "message Path": {
46 | "required double x" : 1,
47 | "required double y" : 2
48 | },
49 | "message Equipment" : {
50 | "required uInt32 entityId" : 1,
51 | "required uInt32 kindId" : 2
52 | },
53 | "onMove" : {
54 | "required uInt32 entityId" : 1,
55 | "repeated Path path" : 2,
56 | "required float speed" : 3
57 | },
58 | "area.playerHandler.enterScene" : {
59 | "message Player" : {
60 | "message Bag" : {
61 | "message Item" : {
62 | "required uInt32 id" : 1,
63 | "optional string type" : 2
64 | },
65 | "repeated Item items" : 1
66 | },
67 | "required uInt32 entityId" : 1,
68 | "required uInt32 kindId" : 2,
69 | "required Bag bag" : 3,
70 | "repeated Equipment equipments" : 4
71 | },
72 | "optional Player curPlayer" : 2
73 | }
74 | }
75 | ```
76 |
77 | ###Server side and Client side
78 | Pomelo-protobuf has server code and client code for js.
79 |
80 | - The server code run in Node.JS environment, use Buffer to represent the binary data.
81 | - The client side code run on browser, use ByteArray to represent the binary data.
82 |
83 | On average, the encode/decode speed of Server version is 60% faster than client version, with less memory usage. So we highly recommend that use the server code on Node.JS for better performance.
84 |
85 | ### Example message
86 |
87 | ```
88 | var key = 'onMove';
89 | var msg = {
90 | entityId : 14,
91 | path : [{x : 128,y : 796},{x : 677,y : 895}],
92 | speed : 160
93 | };
94 |
95 | ```
96 |
97 | ### Server side encode/decode
98 |
99 | ```
100 | //Require proto buf module
101 | var protobuf = require('protobuf');
102 |
103 | //Set encode protos and decode protos
104 | var protos = protobuf.parse(require('./protos.json'));
105 | protobuf.init({encoderProtos:protos, decoderProtos:protos});
106 |
107 | //Encode msg to binary Buffer
108 | var buffer = protobuf.encode(key, msg);
109 |
110 | //Decode a msg from binary buffer
111 | var decodeMsg = protobuf.decode(key, buffer);
112 |
113 | ```
114 | At server side, the encode result will be a Buffer.
115 | The encoderProtos and decodeProtos can be different, in this case we use the same protos for encoder and decoder.
116 |
117 | ### Client side encode/decode
118 | To use the protbuf as browser, you need to include the /client/protobuf.js in your html.
119 |
120 | ```
121 | //Require proto buf
122 | var protobuf = require('protobuf');
123 |
124 | //Get parsed protos from server
125 | var protos = getProtos();
126 |
127 | //Init protobuf
128 | protobuf.init({encoderProtos:protos, decoderProtos:protos});
129 |
130 | //Encode msg to binary Buffer
131 | var buffer = protobuf.encode(key, msg);
132 |
133 | //Decode a msg from binary buffer
134 | var decodeMsg = protobuf.decode(key, buffer);
135 |
136 | ```
137 |
138 | The protobuf will be a global variable, and you need to get the parsed protos from server.
139 | The others are the same as in server side, except the encoder result will by a ByteArray instead of Buffer.
140 |
141 | ###Compatibility
142 | For the same message and proto, the encode results are **the same** for **pomelo-protobuf** and **google protobuf** .This means you can exchange binary data with google-protobuf.
143 |
144 | Some how we has some changes in the proto file, and there are some features we do not support, there are the different:
145 |
146 | - **package** : The array with simple content (integer, float) are packaged by default.And the complex content(message, string) are not packaged.
147 |
148 | - **long** : Pomelo protocol do not support long type, because there are no long int in javascript.All the integer bigger than 32 bits will be translate to a 64bit float, which has only has 52 bits significant figure. It will lost presion for any integer has more than 52 bits significant figures.
149 |
150 | - **default** : Pomelo-protobuf do not support default keyword, for the default value is only used to initialized the element at the decoder side, which can be done by the constructor.
151 |
152 | - **enum** : Pomelo-protobuf do not support the enum keyword.
153 |
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pomelo-protobuf",
3 | "description": "pomelo-protobuf",
4 | "keywords": [
5 | "pomelo",
6 | "protobuf"
7 | ],
8 | "version": "0.2.0",
9 | "main": "lib/client/protobuf.js",
10 | "scripts": [
11 | "lib/client/protobuf.js"
12 | ],
13 | "repo": "https://github.com/pomelonode/pomelo-protobuf"
14 | }
--------------------------------------------------------------------------------
/lib/client/protobuf.js:
--------------------------------------------------------------------------------
1 | /* ProtocolBuffer client 0.1.0*/
2 |
3 | /**
4 | * pomelo-protobuf
5 | * @author
6 | */
7 |
8 | /**
9 | * Protocol buffer root
10 | * In browser, it will be window.protbuf
11 | */
12 | (function (exports, global){
13 | var Protobuf = exports;
14 |
15 | Protobuf.init = function(opts){
16 | //On the serverside, use serverProtos to encode messages send to client
17 | Protobuf.encoder.init(opts.encoderProtos);
18 |
19 | //On the serverside, user clientProtos to decode messages receive from clients
20 | Protobuf.decoder.init(opts.decoderProtos);
21 | };
22 |
23 | Protobuf.encode = function(key, msg){
24 | return Protobuf.encoder.encode(key, msg);
25 | };
26 |
27 | Protobuf.decode = function(key, msg){
28 | return Protobuf.decoder.decode(key, msg);
29 | };
30 |
31 | // exports to support for components
32 | module.exports = Protobuf;
33 | if(typeof(window) != "undefined") {
34 | window.protobuf = Protobuf;
35 | }
36 |
37 | })(typeof(window) == "undefined" ? module.exports : (this.protobuf = {}), this);
38 |
39 | /**
40 | * constants
41 | */
42 | (function (exports, global){
43 | var constants = exports.constants = {};
44 |
45 | constants.TYPES = {
46 | uInt32 : 0,
47 | sInt32 : 0,
48 | int32 : 0,
49 | double : 1,
50 | string : 2,
51 | message : 2,
52 | float : 5
53 | };
54 |
55 | })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
56 |
57 | /**
58 | * util module
59 | */
60 | (function (exports, global){
61 |
62 | var Util = exports.util = {};
63 |
64 | Util.isSimpleType = function(type){
65 | return ( type === 'uInt32' ||
66 | type === 'sInt32' ||
67 | type === 'int32' ||
68 | type === 'uInt64' ||
69 | type === 'sInt64' ||
70 | type === 'float' ||
71 | type === 'double' );
72 | };
73 |
74 | })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
75 |
76 | /**
77 | * codec module
78 | */
79 | (function (exports, global){
80 |
81 | var Codec = exports.codec = {};
82 |
83 | var buffer = new ArrayBuffer(8);
84 | var float32Array = new Float32Array(buffer);
85 | var float64Array = new Float64Array(buffer);
86 | var uInt8Array = new Uint8Array(buffer);
87 |
88 | Codec.encodeUInt32 = function(n){
89 | var n = parseInt(n);
90 | if(isNaN(n) || n < 0){
91 | return null;
92 | }
93 |
94 | var result = [];
95 | do{
96 | var tmp = n % 128;
97 | var next = Math.floor(n/128);
98 |
99 | if(next !== 0){
100 | tmp = tmp + 128;
101 | }
102 | result.push(tmp);
103 | n = next;
104 | }while(n !== 0);
105 |
106 | return result;
107 | };
108 |
109 | Codec.encodeSInt32 = function(n){
110 | var n = parseInt(n);
111 | if(isNaN(n)){
112 | return null;
113 | }
114 | n = n<0?(Math.abs(n)*2-1):n*2;
115 |
116 | return Codec.encodeUInt32(n);
117 | };
118 |
119 | Codec.decodeUInt32 = function(bytes){
120 | var n = 0;
121 |
122 | for(var i = 0; i < bytes.length; i++){
123 | var m = parseInt(bytes[i]);
124 | n = n + ((m & 0x7f) * Math.pow(2,(7*i)));
125 | if(m < 128){
126 | return n;
127 | }
128 | }
129 |
130 | return n;
131 | };
132 |
133 | Codec.decodeSInt32 = function(bytes){
134 | var n = this.decodeUInt32(bytes);
135 | var flag = ((n%2) === 1)?-1:1;
136 |
137 | n = ((n%2 + n)/2)*flag;
138 |
139 | return n;
140 | };
141 |
142 | Codec.encodeFloat = function(float){
143 | float32Array[0] = float;
144 | return uInt8Array;
145 | };
146 |
147 | Codec.decodeFloat = function(bytes, offset){
148 | if(!bytes || bytes.length < (offset + 4)){
149 | return null;
150 | }
151 |
152 | for(var i = 0; i < 4; i++){
153 | uInt8Array[i] = bytes[offset + i];
154 | }
155 |
156 | return float32Array[0];
157 | };
158 |
159 | Codec.encodeDouble = function(double){
160 | float64Array[0] = double;
161 | return uInt8Array.subarray(0, 8);
162 | };
163 |
164 | Codec.decodeDouble = function(bytes, offset){
165 | if(!bytes || bytes.length < (offset + 8)){
166 | return null;
167 | }
168 |
169 | for(var i = 0; i < 8; i++){
170 | uInt8Array[i] = bytes[offset + i];
171 | }
172 |
173 | return float64Array[0];
174 | };
175 |
176 | Codec.encodeStr = function(bytes, offset, str){
177 | for(var i = 0; i < str.length; i++){
178 | var code = str.charCodeAt(i);
179 | var codes = encode2UTF8(code);
180 |
181 | for(var j = 0; j < codes.length; j++){
182 | bytes[offset] = codes[j];
183 | offset++;
184 | }
185 | }
186 |
187 | return offset;
188 | };
189 |
190 | /**
191 | * Decode string from utf8 bytes
192 | */
193 | Codec.decodeStr = function(bytes, offset, length){
194 | var array = [];
195 | var end = offset + length;
196 |
197 | while(offset < end){
198 | var code = 0;
199 |
200 | if(bytes[offset] < 128){
201 | code = bytes[offset];
202 |
203 | offset += 1;
204 | }else if(bytes[offset] < 224){
205 | code = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);
206 | offset += 2;
207 | }else{
208 | code = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
209 | offset += 3;
210 | }
211 |
212 | array.push(code);
213 |
214 | }
215 |
216 | var str = '';
217 | for(var i = 0; i < array.length;){
218 | str += String.fromCharCode.apply(null, array.slice(i, i + 10000));
219 | i += 10000;
220 | }
221 |
222 | return str;
223 | };
224 |
225 | /**
226 | * Return the byte length of the str use utf8
227 | */
228 | Codec.byteLength = function(str){
229 | if(typeof(str) !== 'string'){
230 | return -1;
231 | }
232 |
233 | var length = 0;
234 |
235 | for(var i = 0; i < str.length; i++){
236 | var code = str.charCodeAt(i);
237 | length += codeLength(code);
238 | }
239 |
240 | return length;
241 | };
242 |
243 | /**
244 | * Encode a unicode16 char code to utf8 bytes
245 | */
246 | function encode2UTF8(charCode){
247 | if(charCode <= 0x7f){
248 | return [charCode];
249 | }else if(charCode <= 0x7ff){
250 | return [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
251 | }else{
252 | return [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
253 | }
254 | }
255 |
256 | function codeLength(code){
257 | if(code <= 0x7f){
258 | return 1;
259 | }else if(code <= 0x7ff){
260 | return 2;
261 | }else{
262 | return 3;
263 | }
264 | }
265 | })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
266 |
267 | /**
268 | * encoder module
269 | */
270 | (function (exports, global){
271 |
272 | var protobuf = exports;
273 | var MsgEncoder = exports.encoder = {};
274 |
275 | var codec = protobuf.codec;
276 | var constant = protobuf.constants;
277 | var util = protobuf.util;
278 |
279 | MsgEncoder.init = function(protos){
280 | this.protos = protos || {};
281 | };
282 |
283 | MsgEncoder.encode = function(route, msg){
284 | //Get protos from protos map use the route as key
285 | var protos = this.protos[route];
286 |
287 | //Check msg
288 | if(!checkMsg(msg, protos)){
289 | return null;
290 | }
291 |
292 | //Set the length of the buffer 2 times bigger to prevent overflow
293 | var length = codec.byteLength(JSON.stringify(msg));
294 |
295 | //Init buffer and offset
296 | var buffer = new ArrayBuffer(length);
297 | var uInt8Array = new Uint8Array(buffer);
298 | var offset = 0;
299 |
300 | if(!!protos){
301 | offset = encodeMsg(uInt8Array, offset, protos, msg);
302 | if(offset > 0){
303 | return uInt8Array.subarray(0, offset);
304 | }
305 | }
306 |
307 | return null;
308 | };
309 |
310 | /**
311 | * Check if the msg follow the defination in the protos
312 | */
313 | function checkMsg(msg, protos){
314 | if(!protos){
315 | return false;
316 | }
317 |
318 | for(var name in protos){
319 | var proto = protos[name];
320 |
321 | //All required element must exist
322 | switch(proto.option){
323 | case 'required' :
324 | if(typeof(msg[name]) === 'undefined'){
325 | console.warn('no property exist for required! name: %j, proto: %j, msg: %j', name, proto, msg);
326 | return false;
327 | }
328 | case 'optional' :
329 | if(typeof(msg[name]) !== 'undefined'){
330 | var message = protos.__messages[proto.type] || MsgEncoder.protos['message ' + proto.type];
331 | if(!!message && !checkMsg(msg[name], message)){
332 | console.warn('inner proto error! name: %j, proto: %j, msg: %j', name, proto, msg);
333 | return false;
334 | }
335 | }
336 | break;
337 | case 'repeated' :
338 | //Check nest message in repeated elements
339 | var message = protos.__messages[proto.type] || MsgEncoder.protos['message ' + proto.type];
340 | if(!!msg[name] && !!message){
341 | for(var i = 0; i < msg[name].length; i++){
342 | if(!checkMsg(msg[name][i], message)){
343 | return false;
344 | }
345 | }
346 | }
347 | break;
348 | }
349 | }
350 |
351 | return true;
352 | }
353 |
354 | function encodeMsg(buffer, offset, protos, msg){
355 | for(var name in msg){
356 | if(!!protos[name]){
357 | var proto = protos[name];
358 |
359 | switch(proto.option){
360 | case 'required' :
361 | case 'optional' :
362 | offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
363 | offset = encodeProp(msg[name], proto.type, offset, buffer, protos);
364 | break;
365 | case 'repeated' :
366 | if(msg[name].length > 0){
367 | offset = encodeArray(msg[name], proto, offset, buffer, protos);
368 | }
369 | break;
370 | }
371 | }
372 | }
373 |
374 | return offset;
375 | }
376 |
377 | function encodeProp(value, type, offset, buffer, protos){
378 | switch(type){
379 | case 'uInt32':
380 | offset = writeBytes(buffer, offset, codec.encodeUInt32(value));
381 | break;
382 | case 'int32' :
383 | case 'sInt32':
384 | offset = writeBytes(buffer, offset, codec.encodeSInt32(value));
385 | break;
386 | case 'float':
387 | writeBytes(buffer, offset, codec.encodeFloat(value));
388 | offset += 4;
389 | break;
390 | case 'double':
391 | writeBytes(buffer, offset, codec.encodeDouble(value));
392 | offset += 8;
393 | break;
394 | case 'string':
395 | var length = codec.byteLength(value);
396 |
397 | //Encode length
398 | offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
399 | //write string
400 | codec.encodeStr(buffer, offset, value);
401 | offset += length;
402 | break;
403 | default :
404 | var message = protos.__messages[type] || MsgEncoder.protos['message ' + type];
405 | if(!!message){
406 | //Use a tmp buffer to build an internal msg
407 | var tmpBuffer = new ArrayBuffer(codec.byteLength(JSON.stringify(value))*2);
408 | var length = 0;
409 |
410 | length = encodeMsg(tmpBuffer, length, message, value);
411 | //Encode length
412 | offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
413 | //contact the object
414 | for(var i = 0; i < length; i++){
415 | buffer[offset] = tmpBuffer[i];
416 | offset++;
417 | }
418 | }
419 | break;
420 | }
421 |
422 | return offset;
423 | }
424 |
425 | /**
426 | * Encode reapeated properties, simple msg and object are decode differented
427 | */
428 | function encodeArray(array, proto, offset, buffer, protos){
429 | var i = 0;
430 |
431 | if(util.isSimpleType(proto.type)){
432 | offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
433 | offset = writeBytes(buffer, offset, codec.encodeUInt32(array.length));
434 | for(i = 0; i < array.length; i++){
435 | offset = encodeProp(array[i], proto.type, offset, buffer);
436 | }
437 | }else{
438 | for(i = 0; i < array.length; i++){
439 | offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
440 | offset = encodeProp(array[i], proto.type, offset, buffer, protos);
441 | }
442 | }
443 |
444 | return offset;
445 | }
446 |
447 | function writeBytes(buffer, offset, bytes){
448 | for(var i = 0; i < bytes.length; i++, offset++){
449 | buffer[offset] = bytes[i];
450 | }
451 |
452 | return offset;
453 | }
454 |
455 | function encodeTag(type, tag){
456 | var value = constant.TYPES[type]||2;
457 |
458 | return codec.encodeUInt32((tag<<3)|value);
459 | }
460 | })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
461 |
462 | /**
463 | * decoder module
464 | */
465 | (function (exports, global){
466 | var protobuf = exports;
467 | var MsgDecoder = exports.decoder = {};
468 |
469 | var codec = protobuf.codec;
470 | var util = protobuf.util;
471 |
472 | var buffer;
473 | var offset = 0;
474 |
475 | MsgDecoder.init = function(protos){
476 | this.protos = protos || {};
477 | };
478 |
479 | MsgDecoder.setProtos = function(protos){
480 | if(!!protos){
481 | this.protos = protos;
482 | }
483 | };
484 |
485 | MsgDecoder.decode = function(route, buf){
486 | var protos = this.protos[route];
487 |
488 | buffer = buf;
489 | offset = 0;
490 |
491 | if(!!protos){
492 | return decodeMsg({}, protos, buffer.length);
493 | }
494 |
495 | return null;
496 | };
497 |
498 | function decodeMsg(msg, protos, length){
499 | while(offset>3
537 | };
538 | }
539 |
540 | /**
541 | * Get tag head without move the offset
542 | */
543 | function peekHead(){
544 | var tag = codec.decodeUInt32(peekBytes());
545 |
546 | return {
547 | type : tag&0x7,
548 | tag : tag>>3
549 | };
550 | }
551 |
552 | function decodeProp(type, protos){
553 | switch(type){
554 | case 'uInt32':
555 | return codec.decodeUInt32(getBytes());
556 | case 'int32' :
557 | case 'sInt32' :
558 | return codec.decodeSInt32(getBytes());
559 | case 'float' :
560 | var float = codec.decodeFloat(buffer, offset);
561 | offset += 4;
562 | return float;
563 | case 'double' :
564 | var double = codec.decodeDouble(buffer, offset);
565 | offset += 8;
566 | return double;
567 | case 'string' :
568 | var length = codec.decodeUInt32(getBytes());
569 |
570 | var str = codec.decodeStr(buffer, offset, length);
571 | offset += length;
572 |
573 | return str;
574 | default :
575 | var message = protos && (protos.__messages[type] || MsgDecoder.protos['message ' + type]);
576 | if(!!message){
577 | var length = codec.decodeUInt32(getBytes());
578 | var msg = {};
579 | decodeMsg(msg, message, offset+length);
580 | return msg;
581 | }
582 | break;
583 | }
584 | }
585 |
586 | function decodeArray(array, type, protos){
587 | if(util.isSimpleType(type)){
588 | var length = codec.decodeUInt32(getBytes());
589 |
590 | for(var i = 0; i < length; i++){
591 | array.push(decodeProp(type));
592 | }
593 | }else{
594 | array.push(decodeProp(type, protos));
595 | }
596 | }
597 |
598 | function getBytes(flag){
599 | var bytes = [];
600 | var pos = offset;
601 | flag = flag || false;
602 |
603 | var b;
604 |
605 | do{
606 | b = buffer[pos];
607 | bytes.push(b);
608 | pos++;
609 | }while(b >= 128);
610 |
611 | if(!flag){
612 | offset = pos;
613 | }
614 | return bytes;
615 | }
616 |
617 | function peekBytes(){
618 | return getBytes(true);
619 | }
620 |
621 | })('undefined' !== typeof protobuf ? protobuf : module.exports, this);
622 |
623 |
--------------------------------------------------------------------------------
/lib/codec.js:
--------------------------------------------------------------------------------
1 | var Encoder = module.exports;
2 |
3 | /**
4 | * [encode an uInt32, return a array of bytes]
5 | * @param {[integer]} num
6 | * @return {[array]}
7 | */
8 | Encoder.encodeUInt32 = function(num){
9 | var n = parseInt(num);
10 | if(isNaN(n) || n < 0){
11 | console.log(n);
12 | return null;
13 | }
14 |
15 | var result = [];
16 | do{
17 | var tmp = n % 128;
18 | var next = Math.floor(n/128);
19 |
20 | if(next !== 0){
21 | tmp = tmp + 128;
22 | }
23 | result.push(tmp);
24 | n = next;
25 | } while(n !== 0);
26 |
27 | return result;
28 | };
29 |
30 | /**
31 | * [encode a sInt32, return a byte array]
32 | * @param {[sInt32]} num The sInt32 need to encode
33 | * @return {[array]} A byte array represent the integer
34 | */
35 | Encoder.encodeSInt32 = function(num){
36 | var n = parseInt(num);
37 | if(isNaN(n)){
38 | return null;
39 | }
40 | n = n<0?(Math.abs(n)*2-1):n*2;
41 |
42 | return Encoder.encodeUInt32(n);
43 | };
44 |
45 | Encoder.decodeUInt32 = function(bytes){
46 | var n = 0;
47 |
48 | for(var i = 0; i < bytes.length; i++){
49 | var m = parseInt(bytes[i]);
50 | n = n + ((m & 0x7f) * Math.pow(2,(7*i)));
51 | if(m < 128){
52 | return n;
53 | }
54 | }
55 |
56 | return n;
57 | };
58 |
59 |
60 | Encoder.decodeSInt32 = function(bytes){
61 | var n = this.decodeUInt32(bytes);
62 | var flag = ((n%2) === 1)?-1:1;
63 |
64 | n = ((n%2 + n)/2)*flag;
65 |
66 | return n;
67 | };
68 |
--------------------------------------------------------------------------------
/lib/constant.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | TYPES : {
3 | uInt32 : 0,
4 | sInt32 : 0,
5 | int32 : 0,
6 | double : 1,
7 | string : 2,
8 | message : 2,
9 | float : 5
10 | }
11 | }
--------------------------------------------------------------------------------
/lib/decoder.js:
--------------------------------------------------------------------------------
1 | var codec = require('./codec');
2 | var util = require('./util');
3 |
4 | var Decoder = module.exports;
5 |
6 | var buffer;
7 | var offset = 0;
8 |
9 | Decoder.init = function(protos){
10 | this.protos = protos || {};
11 | };
12 |
13 | Decoder.setProtos = function(protos){
14 | if(!!protos){
15 | this.protos = protos;
16 | }
17 | };
18 |
19 | Decoder.decode = function(route, buf){
20 | var protos = this.protos[route];
21 |
22 | buffer = buf;
23 | offset = 0;
24 |
25 | if(!!protos){
26 | return decodeMsg({}, protos, buffer.length);
27 | }
28 |
29 | return null;
30 | };
31 |
32 | function decodeMsg(msg, protos, length){
33 | while(offset>3
71 | };
72 | }
73 |
74 | /**
75 | * Get tag head without move the offset
76 | */
77 | function peekHead(){
78 | var tag = codec.decodeUInt32(peekBytes());
79 |
80 | return {
81 | type : tag&0x7,
82 | tag : tag>>3
83 | };
84 | }
85 |
86 | function decodeProp(type, protos){
87 | switch(type){
88 | case 'uInt32':
89 | return codec.decodeUInt32(getBytes());
90 | case 'int32' :
91 | case 'sInt32' :
92 | return codec.decodeSInt32(getBytes());
93 | case 'float' :
94 | var float = buffer.readFloatLE(offset);
95 | offset += 4;
96 | return float;
97 | case 'double' :
98 | var double = buffer.readDoubleLE(offset);
99 | offset += 8;
100 | return double;
101 | case 'string' :
102 | var length = codec.decodeUInt32(getBytes());
103 |
104 | var str = buffer.toString('utf8', offset, offset+length);
105 | offset += length;
106 |
107 | return str;
108 | default :
109 | var message = protos && (protos.__messages[type] || Decoder.protos['message ' + type]);
110 | if(message){
111 | var length = codec.decodeUInt32(getBytes());
112 | var msg = {};
113 | decodeMsg(msg, message, offset+length);
114 | return msg;
115 | }
116 | break;
117 | }
118 | }
119 |
120 | function decodeArray(array, type, protos){
121 | if(util.isSimpleType(type)){
122 | var length = codec.decodeUInt32(getBytes());
123 |
124 | for(var i = 0; i < length; i++){
125 | array.push(decodeProp(type));
126 | }
127 | }else{
128 | array.push(decodeProp(type, protos));
129 | }
130 | }
131 |
132 | function getBytes(flag){
133 | var bytes = [];
134 | var pos = offset;
135 | flag = flag || false;
136 |
137 | var b;
138 | do{
139 | var b = buffer.readUInt8(pos);
140 | bytes.push(b);
141 | pos++;
142 | }while(b >= 128);
143 |
144 | if(!flag){
145 | offset = pos;
146 | }
147 | return bytes;
148 | }
149 |
150 | function peekBytes(){
151 | return getBytes(true);
152 | }
--------------------------------------------------------------------------------
/lib/encoder.js:
--------------------------------------------------------------------------------
1 | var codec = require('./codec');
2 | var constant = require('./constant');
3 | var util = require('./util');
4 |
5 | var Encoder = module.exports;
6 |
7 | Encoder.init = function(protos){
8 | this.protos = protos || {};
9 | };
10 |
11 | Encoder.encode = function(route, msg){
12 | if(!route || !msg){
13 | console.warn('Route or msg can not be null! route : %j, msg %j', route, msg);
14 | return null;
15 | }
16 |
17 | //Get protos from protos map use the route as key
18 | var protos = this.protos[route];
19 |
20 | //Check msg
21 | if(!checkMsg(msg, protos)){
22 | console.warn('check msg failed! msg : %j, proto : %j', msg, protos);
23 | return null;
24 | }
25 |
26 | //Set the length of the buffer 2 times bigger to prevent overflow
27 | var length = Buffer.byteLength(JSON.stringify(msg))*2;
28 |
29 | //Init buffer and offset
30 | var buffer = new Buffer(length);
31 | var offset = 0;
32 |
33 | if(!!protos){
34 | offset = encodeMsg(buffer, offset, protos, msg);
35 | if(offset > 0){
36 | return buffer.slice(0, offset);
37 | }
38 | }
39 |
40 | return null;
41 | };
42 |
43 | /**
44 | * Check if the msg follow the defination in the protos
45 | */
46 | function checkMsg(msg, protos){
47 | if(!protos || !msg){
48 | console.warn('no protos or msg exist! msg : %j, protos : %j', msg, protos);
49 | return false;
50 | }
51 |
52 | for(var name in protos){
53 | var proto = protos[name];
54 |
55 | //All required element must exist
56 | switch(proto.option){
57 | case 'required' :
58 | if(typeof(msg[name]) === 'undefined'){
59 | console.warn('no property exist for required! name: %j, proto: %j, msg: %j', name, proto, msg);
60 | return false;
61 | }
62 | case 'optional' :
63 | if(typeof(msg[name]) !== 'undefined'){
64 | var message = protos.__messages[proto.type] || Encoder.protos['message ' + proto.type];
65 | if(!!message && !checkMsg(msg[name], message)){
66 | console.warn('inner proto error! name: %j, proto: %j, msg: %j', name, proto, msg);
67 | return false;
68 | }
69 | }
70 | break;
71 | case 'repeated' :
72 | //Check nest message in repeated elements
73 | var message = protos.__messages[proto.type] || Encoder.protos['message ' + proto.type];
74 | if(!!msg[name] && !!message){
75 | for(var i = 0; i < msg[name].length; i++){
76 | if(!checkMsg(msg[name][i], message)){
77 | return false;
78 | }
79 | }
80 | }
81 | break;
82 | }
83 | }
84 |
85 | return true;
86 | }
87 |
88 | function encodeMsg(buffer, offset, protos, msg){
89 | for(var name in msg){
90 | if(!!protos[name]){
91 | var proto = protos[name];
92 |
93 | switch(proto.option){
94 | case 'required' :
95 | case 'optional' :
96 | offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
97 | offset = encodeProp(msg[name], proto.type, offset, buffer, protos);
98 | break;
99 | case 'repeated' :
100 | if(!!msg[name] && msg[name].length > 0){
101 | offset = encodeArray(msg[name], proto, offset, buffer, protos);
102 | }
103 | break;
104 | }
105 | }
106 | }
107 |
108 | return offset;
109 | }
110 |
111 | function encodeProp(value, type, offset, buffer, protos){
112 | var length = 0;
113 |
114 | switch(type){
115 | case 'uInt32':
116 | offset = writeBytes(buffer, offset, codec.encodeUInt32(value));
117 | break;
118 | case 'int32' :
119 | case 'sInt32':
120 | offset = writeBytes(buffer, offset, codec.encodeSInt32(value));
121 | break;
122 | case 'float':
123 | buffer.writeFloatLE(value, offset);
124 | offset += 4;
125 | break;
126 | case 'double':
127 | buffer.writeDoubleLE(value, offset);
128 | offset += 8;
129 | break;
130 | case 'string':
131 | length = Buffer.byteLength(value);
132 |
133 | //Encode length
134 | offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
135 | //write string
136 | buffer.write(value, offset, length);
137 | offset += length;
138 | break;
139 | default :
140 | var message = protos.__messages[type] || Encoder.protos['message ' + type];
141 | if(!!message){
142 | //Use a tmp buffer to build an internal msg
143 | var tmpBuffer = new Buffer(Buffer.byteLength(JSON.stringify(value))*2);
144 | length = 0;
145 |
146 | length = encodeMsg(tmpBuffer, length, message, value);
147 | //Encode length
148 | offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
149 | //contact the object
150 | tmpBuffer.copy(buffer, offset, 0, length);
151 |
152 | offset += length;
153 | }
154 | break;
155 | }
156 |
157 | return offset;
158 | }
159 |
160 | /**
161 | * Encode reapeated properties, simple msg and object are decode differented
162 | */
163 | function encodeArray(array, proto, offset, buffer, protos){
164 | var i = 0;
165 | if(util.isSimpleType(proto.type)){
166 | offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
167 | offset = writeBytes(buffer, offset, codec.encodeUInt32(array.length));
168 | for(i = 0; i < array.length; i++){
169 | offset = encodeProp(array[i], proto.type, offset, buffer);
170 | }
171 | }else{
172 | for(i = 0; i < array.length; i++){
173 | offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
174 | offset = encodeProp(array[i], proto.type, offset, buffer, protos);
175 | }
176 | }
177 |
178 | return offset;
179 | }
180 |
181 | function writeBytes(buffer, offset, bytes){
182 | for(var i = 0; i < bytes.length; i++){
183 | buffer.writeUInt8(bytes[i], offset);
184 | offset++;
185 | }
186 |
187 | return offset;
188 | }
189 |
190 | function encodeTag(type, tag){
191 | var value = constant.TYPES[type];
192 |
193 | if(value === undefined) value = 2;
194 |
195 | return codec.encodeUInt32((tag<<3)|value);
196 | }
197 |
--------------------------------------------------------------------------------
/lib/parser.js:
--------------------------------------------------------------------------------
1 | var Parser = module.exports;
2 |
3 | /**
4 | * [parse the original protos, give the paresed result can be used by protobuf encode/decode.]
5 | * @param {[Object]} protos Original protos, in a js map.
6 | * @return {[Object]} The presed result, a js object represent all the meta data of the given protos.
7 | */
8 | Parser.parse = function(protos){
9 | var maps = {};
10 | for(var key in protos){
11 | maps[key] = parseObject(protos[key]);
12 | }
13 |
14 | return maps;
15 | };
16 |
17 | /**
18 | * [parse a single protos, return a object represent the result. The method can be invocked recursively.]
19 | * @param {[Object]} obj The origin proto need to parse.
20 | * @return {[Object]} The parsed result, a js object.
21 | */
22 | function parseObject(obj){
23 | var proto = {};
24 | var nestProtos = {};
25 | var tags = {};
26 |
27 | for(var name in obj){
28 | var tag = obj[name];
29 | var params = name.split(' ');
30 |
31 | switch(params[0]){
32 | case 'message':
33 | if(params.length !== 2){
34 | continue;
35 | }
36 | nestProtos[params[1]] = parseObject(tag);
37 | continue;
38 | case 'required':
39 | case 'optional':
40 | case 'repeated':{
41 | //params length should be 3 and tag can't be duplicated
42 | if(params.length !== 3 || !!tags[tag]){
43 | continue;
44 | }
45 | proto[params[2]] = {
46 | option : params[0],
47 | type : params[1],
48 | tag : tag
49 | };
50 | tags[tag] = params[2];
51 | }
52 | }
53 | }
54 |
55 | proto.__messages = nestProtos;
56 | proto.__tags = tags;
57 | return proto;
58 | }
--------------------------------------------------------------------------------
/lib/protobuf.js:
--------------------------------------------------------------------------------
1 | var encoder = require('./encoder');
2 | var decoder = require('./decoder');
3 | var parser = require('./parser');
4 |
5 | var Protobuf = module.exports;
6 |
7 | /**
8 | * [encode the given message, return a Buffer represent the message encoded by protobuf]
9 | * @param {[type]} key The key to identify the message type.
10 | * @param {[type]} msg The message body, a js object.
11 | * @return {[type]} The binary encode result in a Buffer.
12 | */
13 | Protobuf.encode = function(key, msg){
14 | return encoder.encode(key, msg);
15 | };
16 |
17 | Protobuf.encode2Bytes = function(key, msg){
18 | var buffer = this.encode(key, msg);
19 | if(!buffer || !buffer.length){
20 | console.warn('encode msg failed! key : %j, msg : %j', key, msg);
21 | return null;
22 | }
23 | var bytes = new Uint8Array(buffer.length);
24 | for(var offset = 0; offset < buffer.length; offset++){
25 | bytes[offset] = buffer.readUInt8(offset);
26 | }
27 |
28 | return bytes;
29 | };
30 |
31 | Protobuf.encodeStr = function(key, msg, code){
32 | code = code || 'base64';
33 | var buffer = Protobuf.encode(key, msg);
34 | return !!buffer?buffer.toString(code):buffer;
35 | };
36 |
37 | Protobuf.decode = function(key, msg){
38 | return decoder.decode(key, msg);
39 | };
40 |
41 | Protobuf.decodeStr = function(key, str, code){
42 | code = code || 'base64';
43 | var buffer = new Buffer(str, code);
44 |
45 | return !!buffer?Protobuf.decode(key, buffer):buffer;
46 | };
47 |
48 | Protobuf.parse = function(json){
49 | return parser.parse(json);
50 | };
51 |
52 | Protobuf.setEncoderProtos = function(protos){
53 | encoder.init(protos);
54 | };
55 |
56 | Protobuf.setDecoderProtos = function(protos){
57 | decoder.init(protos);
58 | };
59 |
60 | Protobuf.init = function(opts){
61 | //On the serverside, use serverProtos to encode messages send to client
62 | encoder.init(opts.encoderProtos);
63 |
64 | //On the serverside, user clientProtos to decode messages receive from clients
65 | decoder.init(opts.decoderProtos);
66 |
67 | };
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 | var util = module.exports;
2 |
3 | util.isSimpleType = function(type){
4 | return ( type === 'uInt32' ||
5 | type === 'sInt32' ||
6 | type === 'int32' ||
7 | type === 'uInt64' ||
8 | type === 'sInt64' ||
9 | type === 'float' ||
10 | type === 'double');
11 | };
12 |
13 | util.equal = function(obj0, obj1){
14 | for(var key in obj0){
15 | var m = obj0[key];
16 | var n = obj1[key];
17 |
18 | if(typeof(m) === 'object'){
19 | if(!util.equal(m, n)){
20 | return false;
21 | }
22 | }else if(m !== n){
23 | return false;
24 | }
25 | }
26 |
27 | return true;
28 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pomelo-protobuf",
3 | "version": "0.4.0",
4 | "main": "./lib/protobuf",
5 | "dependencies": {},
6 | "author": {
7 | "name": "XiaogangZhang",
8 | "email": "zhang0925@gmail.com"
9 | },
10 | "devDependencies": {
11 | "should": ">=0.0.1",
12 | "mocha": ">=0.0.1"
13 | }
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/test/client/encoderTest.js:
--------------------------------------------------------------------------------
1 | var should = require('should');
2 | var encoder = require('../../lib/client/protobuf').codec;
3 |
4 | describe('client encoder test', function(){
5 | describe('float test for 10000 times', function(){
6 | for(var i = 0; i < 10000; i++){
7 | var float = Math.random();
8 |
9 | var bytes = encoder.encodeFloat(float);
10 | var result = encoder.decodeFloat(bytes, 0);
11 |
12 | var diff = Math.abs(float-result);
13 | //console.log('float : %j, result : %j, diff : %j', float, result, diff);
14 | diff.should.below(0.0000001);
15 |
16 | }
17 | });
18 |
19 | describe('double test for 10000 times', function(){
20 | for(var i = 0; i < 10000; i++){
21 | var double = Math.random();
22 |
23 | var bytes = encoder.encodeDouble(double);
24 | var result = encoder.decodeDouble(bytes, 0);
25 |
26 | double.should.equal(result);
27 | }
28 | });
29 |
30 | describe('utf8 encode & decode test, use 1000 * 1000 test case', function(){
31 | var num = 1000;
32 | var limit = 1000;
33 | for(var i = 0; i < num; i++){
34 | var strLength = Math.ceil(Math.random()*limit);
35 | var arr = [];
36 | for(var j = 0; j < strLength; j++){
37 | arr.push(Math.floor(Math.random()*65536));
38 | }
39 | //arr = [ 58452, 127, 38641, 25796, 20652, 19237 ];
40 |
41 | var str = String.fromCharCode.apply(null, arr);
42 |
43 | //console.log('old arr : %j', arr);
44 |
45 | var length = encoder.byteLength(str);
46 | var buffer = new ArrayBuffer(length);
47 | var bytes = new Uint8Array(buffer);
48 |
49 | var offset = encoder.encodeStr(bytes, 0, str);
50 | //console.log('encode over, offset : %j, length : %j, str length : %j', offset, length, str.length);
51 | //console.log(bytes);
52 | length.should.equal.offset;
53 |
54 | var result = encoder.decodeStr(bytes, 0, length);
55 |
56 |
57 | str.length.should.equal(result.length);
58 | var flag = true;
59 | for(var m = 0; m < str.length; m++){
60 | if(str.charCodeAt(m) != result.charCodeAt(m)){
61 | console.log('error ! origin : %j, result : %j, code : %j, code 1 : %j', str, result, str.charCodeAt(m), result.charCodeAt(m));
62 | console.log(arr);
63 | flag = false;
64 | }
65 | }
66 |
67 | if(!flag)return;
68 | //console.log('str : %j, bytes : %j, result : %j', str, bytes, result);
69 | }
70 | });
71 |
72 | describe('string decode speed test', function(){
73 | var array = [];
74 | var length = 100000;
75 | for(var i = 0; i < length; i++,array.push(0));
76 | var start = Date.now();
77 | var str = '';
78 | for(var j = 0; j < length; ){
79 | str += String.fromCharCode.apply(null, array.slice(j, j+10000));
80 | j += 10000;
81 | }
82 | //var str = String.fromCharCode.apply(null, array);
83 | var end = Date.now();
84 |
85 | console.log('cost time with fromCharCode method : %j, length : %j', end-start, str.length);
86 |
87 | start = Date.now();
88 | str = '';
89 | for(var i = 0; i < length; i++){
90 | str += array[i];
91 | }
92 | end = Date.now();
93 |
94 | console.log('cost time by add string: %j, length : %j', end-start, str.length);
95 | });
96 | });
--------------------------------------------------------------------------------
/test/client/protobufTest.js:
--------------------------------------------------------------------------------
1 | var protobuf = require('../../lib/client/protobuf');
2 | var protobufServer = require('../../lib/protobuf');
3 | var encoder = protobuf.encoder;
4 | var decoder = protobuf.decoder;
5 | var codec = protobuf.codec;
6 | var parser = require('../../lib/parser');
7 | var util = require('../../lib/util');
8 | var should = require('should');
9 | var tc = require('../testMsg');
10 |
11 | describe('msgEncoderTest', function(){
12 |
13 | var protos = parser.parse(require('../example.json'));
14 |
15 | protobuf.init({encoderProtos:protos, decoderProtos:protos});
16 | protobufServer.init({encoderProtos:protos, decoderProtos:protos});
17 |
18 | describe('protobufTest', function(){
19 | for(var route in tc){
20 | var msg = tc[route];
21 | var buffer = protobuf.encode(route, msg);
22 |
23 | var decodeMsg = protobuf.decode(route, buffer);
24 |
25 | util.equal(msg, decodeMsg).should.equal(true);
26 | }
27 | });
28 | });
29 |
30 | function toBuffer(arr){
31 | var buffer = new Buffer(arr.length);
32 |
33 | for(var i = 0; i < arr.length; i++){
34 | buffer.writeUInt8(arr[i], i);
35 | }
36 |
37 | return buffer;
38 | }
--------------------------------------------------------------------------------
/test/client/rootMsgTest.js:
--------------------------------------------------------------------------------
1 | var protobuf = require('../../lib/client/protobuf');
2 | var encoder = protobuf.encoder;
3 | var decoder = protobuf.decoder;
4 | var codec = protobuf.codec;
5 | var parser = require('../../lib/parser');
6 | var util = require('../../lib/util');
7 | var should = require('should');
8 | var tc = require('../rootMsgTC');
9 |
10 | describe('msgEncoderTest', function(){
11 |
12 | var protos = parser.parse(require('../rootMsg.json'));
13 |
14 | protobuf.init({encoderProtos:protos, decoderProtos:protos});
15 |
16 | describe('protobufTest', function(){
17 | for(var route in tc){
18 | var msg = tc[route];
19 |
20 | console.log('====================');
21 | console.log(route);
22 |
23 | var buffer = protobuf.encode(route, msg);
24 |
25 | console.log(msg);
26 | console.log(buffer.length);
27 |
28 | var decodeMsg = protobuf.decode(route, buffer);
29 |
30 | console.log(decodeMsg);
31 | console.log('====================');
32 |
33 | util.equal(msg, decodeMsg).should.equal(true);
34 | }
35 | });
36 | });
--------------------------------------------------------------------------------
/test/codecTest.js:
--------------------------------------------------------------------------------
1 | var encoder = require('../lib/codec');
2 | var should = require('should');
3 |
4 | describe('encoder test', function(){
5 | describe('uInt32 and uInt64 test, for encode and decode 10000 random number', function(){
6 | var limit = 0x7fffffffffffffff;
7 |
8 | var count = 10000;
9 | for(var i = 0; i < count; i++){
10 | var number = Math.ceil(Math.random()*limit);
11 | var result = encoder.decodeUInt32(encoder.encodeUInt32(number));
12 | should.equal(number, result);
13 | }
14 | });
15 |
16 | describe('sInt32 adn sInt64 test, for encode and decode 10000 random number', function(){
17 | var limit = 0xfffffffffffff;
18 |
19 | for(var i = 0; i < 10000; i++){
20 | var flag = Math.random>0.5?1:-1;
21 | var number = Math.ceil(Math.random()*limit)*flag;
22 |
23 | var result = encoder.decodeSInt32(encoder.encodeSInt32(number));
24 |
25 | should.equal(number, result);
26 | }
27 | });
28 | });
--------------------------------------------------------------------------------
/test/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "onMove" : {
3 | "required uInt32 entityId" : 1,
4 | "message Path": {
5 | "required double x" : 1,
6 | "required double y" : 2
7 | },
8 | "repeated Path path" : 2,
9 | "required float speed" : 3
10 | },
11 | "onAttack" : {
12 | "required uInt32 attacker" : 1,
13 | "required uInt32 target" : 2,
14 | "message AttackResult" : {
15 | "required uInt32 result" : 1,
16 | "required uInt32 damage" : 2,
17 | "repeated Item items" : 4,
18 | "message Item" : {
19 | "required uInt32 kindId" : 1,
20 | "required uInt32 x" : 2,
21 | "required uInt32 y" : 3,
22 | "required uInt32 entityId" : 4,
23 | "required uInt32 playerId" : 5,
24 | "required string type" : 6
25 | }
26 | },
27 | "required AttackResult result" : 3,
28 | "optional uInt32 exp" : 4,
29 | "optional uInt32 reviveTime" : 5,
30 | "required uInt32 skillId" : 6
31 | },
32 | "onUpgrade" : {
33 | "required uInt32 entityId" : 1,
34 | "required uInt32 kindId" : 2,
35 | "required uInt32 x" : 3,
36 | "required uInt32 y" : 4,
37 | "required uInt32 level" : 5,
38 | "required uInt32 walkSpeed" : 6,
39 | "required uInt32 hp" : 7,
40 | "required uInt32 maxHp" : 8,
41 | "required uInt32 mp" : 9,
42 | "required uInt32 maxMp" : 10,
43 | "required uInt32 id" : 11,
44 | "required string name" : 12,
45 | "required uInt32 experience" : 13,
46 | "required uInt32 attackValue" : 14,
47 | "required uInt32 defenceValue" : 15,
48 | "required double attackSpeed" : 16,
49 | "required uInt32 areaId" : 17,
50 | "required uInt32 hitRate" : 18,
51 | "required uInt32 dodgeRate" : 19,
52 | "required uInt32 nextLevelExp" : 20,
53 | "required uInt32 skillPoint" : 21,
54 | "required string kindName" : 22,
55 | "required string type" : 23
56 | },
57 | "onPickItem" : {
58 | "required uInt32 player" : 1,
59 | "required uInt32 item" : 2,
60 | "required uInt32 index" : 3
61 | },
62 | "onNPCTalk" : {
63 | "required uInt32 npc" : 1,
64 | "required string npcword" : 2,
65 | "required string myword" : 3
66 | },
67 | "onRevive" : {
68 | "required uInt32 entityId" : 1,
69 | "required uInt32 x" : 2,
70 | "required uInt32 y" : 3,
71 | "required uInt32 hp" : 4
72 | },
73 | "onAddEntities" : {
74 | "message NPC" : {
75 | "required uInt32 entityId" : 1,
76 | "required uInt32 kindId" : 2,
77 | "required uInt32 x" : 3,
78 | "required uInt32 y" : 4
79 | },
80 | "message Mob" : {
81 | "required uInt32 entityId" : 1,
82 | "required uInt32 kindId" : 2,
83 | "required uInt32 x" : 3,
84 | "required uInt32 y" : 4,
85 | "required uInt32 level" : 5,
86 | "required uInt32 walkSpeed" : 6,
87 | "required uInt32 hp" : 7,
88 | "required uInt32 maxHp" : 8
89 | },
90 | "message Item" : {
91 | "required uInt32 entityId" : 1,
92 | "required uInt32 kindId" : 2,
93 | "required uInt32 x" : 3,
94 | "required uInt32 y" : 4,
95 | "required uInt32 playerId" : 5
96 | },
97 | "message Equipment" : {
98 | "required uInt32 entityId" : 1,
99 | "required uInt32 kindId" : 2,
100 | "required uInt32 x" : 3,
101 | "required uInt32 y" : 4,
102 | "required uInt32 playerId" : 5
103 | },
104 | "message Player" : {
105 | "required uInt32 entityId" : 1,
106 | "required uInt32 kindId" : 2,
107 | "required uInt32 x" : 3,
108 | "required uInt32 y" : 4,
109 | "required uInt32 level" : 5,
110 | "required uInt32 walkSpeed" : 6,
111 | "required uInt32 hp" : 7,
112 | "required uInt32 maxHp" : 8,
113 | "required uInt32 mp" : 9,
114 | "required uInt32 maxMp" : 10,
115 | "required uInt32 id" : 11,
116 | "required string name" : 12
117 | },
118 | "repeated NPC npc" : 1,
119 | "repeated Mob mob" : 2,
120 | "repeated Item item" : 3,
121 | "repeated Equipment euipment" : 4,
122 | "repeated Player player" : 5
123 | },
124 | "onRemoveEntities" : {
125 | "repeated uInt32 entities" : 1
126 | },
127 | "onPathCheckOut" : {
128 | "required uInt32 entityId" : 1,
129 | "message Position" : {
130 | "required uInt32 x" : 1,
131 | "required uInt32 y" : 2
132 | },
133 | "required Position position" : 2
134 | },
135 | "area.playerHandler.enterScene" : {
136 | "message Entities" : {
137 | "message NPC" : {
138 | "required uInt32 entityId" : 1,
139 | "required uInt32 kindId" : 2,
140 | "required uInt32 x" : 3,
141 | "required uInt32 y" : 4
142 | },
143 | "message Mob" : {
144 | "required uInt32 entityId" : 1,
145 | "required uInt32 kindId" : 2,
146 | "required uInt32 x" : 3,
147 | "required uInt32 y" : 4,
148 | "required uInt32 level" : 5,
149 | "required uInt32 walkSpeed" : 6,
150 | "required uInt32 hp" : 7,
151 | "required uInt32 maxHp" : 8
152 | },
153 | "message Item" : {
154 | "required uInt32 entityId" : 1,
155 | "required uInt32 kindId" : 2,
156 | "required uInt32 x" : 3,
157 | "required uInt32 y" : 4,
158 | "required uInt32 playerId" : 5
159 | },
160 | "message Equipment" : {
161 | "required uInt32 entityId" : 1,
162 | "required uInt32 kindId" : 2,
163 | "required uInt32 x" : 3,
164 | "required uInt32 y" : 4,
165 | "required uInt32 playerId" : 5
166 | },
167 | "message Player" : {
168 | "required uInt32 entityId" : 1,
169 | "required uInt32 kindId" : 2,
170 | "required uInt32 x" : 3,
171 | "required uInt32 y" : 4,
172 | "required uInt32 level" : 5,
173 | "required uInt32 walkSpeed" : 6,
174 | "required uInt32 hp" : 7,
175 | "required uInt32 maxHp" : 8,
176 | "required uInt32 mp" : 9,
177 | "required uInt32 maxMp" : 10,
178 | "required uInt32 id" : 11,
179 | "required string name" : 12
180 | },
181 | "repeated NPC npc" : 1,
182 | "repeated Mob mob" : 2,
183 | "repeated Item item" : 3,
184 | "repeated Equipment euipment" : 4,
185 | "repeated Player player" : 5
186 | },
187 | "message Player" : {
188 | "message Bag" : {
189 | "message Item" : {
190 | "required uInt32 key" : 1,
191 | "required uInt32 id" : 2,
192 | "required string type" : 3
193 | },
194 | "required uInt32 itemCount" : 1,
195 | "repeated Item items" : 2
196 | },
197 | "message Equipments" : {
198 | "required uInt32 weapon" : 1,
199 | "required uInt32 armor" : 2,
200 | "required uInt32 helmet" : 3,
201 | "required uInt32 necklace" : 4,
202 | "required uInt32 ring" : 5,
203 | "required uInt32 belt" : 6,
204 | "required uInt32 shoes" : 7,
205 | "required uInt32 legguard" : 8,
206 | "required uInt32 amulet" : 9
207 | },
208 | "message FightSkill" : {
209 | "required uInt32 id" : 1,
210 | "required uInt32 level" : 2
211 | },
212 | "required uInt32 entityId" : 1,
213 | "required uInt32 kindId" : 2,
214 | "required uInt32 x" : 3,
215 | "required uInt32 y" : 4,
216 | "required uInt32 level" : 5,
217 | "required uInt32 walkSpeed" : 6,
218 | "required uInt32 hp" : 7,
219 | "required uInt32 maxHp" : 8,
220 | "required uInt32 mp" : 9,
221 | "required uInt32 maxMp" : 10,
222 | "required uInt32 id" : 11,
223 | "required string name" : 12,
224 | "required uInt32 experience" : 13,
225 | "required uInt32 attackValue" : 14,
226 | "required uInt32 defenceValue" : 15,
227 | "required double attackSpeed" : 16,
228 | "required uInt32 areaId" : 17,
229 | "required uInt32 hitRate" : 18,
230 | "required uInt32 dodgeRate" : 19,
231 | "required uInt32 nextLevelExp" : 20,
232 | "required uInt32 skillPoint" : 21,
233 | "required string type" : 22,
234 | "required Bag bag" : 23,
235 | "required Equipments equipments" : 24,
236 | "repeated FightSkill fightSkills" : 25
237 | },
238 | "optional Entities entities" : 1,
239 | "optional Player curPlayer" : 2,
240 | "message Map" : {
241 | "message Collisions" : {
242 | "message Collison" : {
243 | "required uInt32 start" : 1,
244 | "required uInt32 length" : 2
245 | },
246 | "repeated Collison collisions" : 1
247 | },
248 | "required string name" : 1,
249 | "required uInt32 width" : 2,
250 | "required uInt32 height" : 3,
251 | "required uInt32 tileW" : 4,
252 | "required uInt32 tileH" : 5,
253 | "repeated Collisions weightMap" : 6
254 | },
255 | "required Map map" : 3
256 | }
257 | }
--------------------------------------------------------------------------------
/test/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.4,
48 | "y": 90.9
49 | },
50 | {
51 | "x": 10.4,
52 | "y": 90.1
53 | }
54 | ],
55 | "speed": 160.0
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/protobufTest.js:
--------------------------------------------------------------------------------
1 | var protobuf = require('../lib/protobuf');
2 | var util = require('../lib/util');
3 | var should = require('should');
4 | var tc = require('./testMsg');
5 |
6 |
7 | describe('msgEncoderTest', function(){
8 | var protos = protobuf.parse(require('./example.json'));
9 | protobuf.init({encoderProtos:protos, decoderProtos:protos});
10 |
11 | describe('encodeTest', function(){
12 | for(var route in tc){
13 | var msg = tc[route];
14 | var buffer = protobuf.encode(route, msg);
15 |
16 | console.log(msg);
17 | console.log(buffer.length);
18 | console.log(buffer)
19 |
20 | var decodeMsg = protobuf.decode(route, buffer);
21 |
22 | console.log(decodeMsg);
23 |
24 | util.equal(msg, decodeMsg).should.equal(true);
25 | }
26 | });
27 | });
--------------------------------------------------------------------------------
/test/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/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/rootMsgTC.js:
--------------------------------------------------------------------------------
1 | var tc = module.exports;
2 |
3 | tc.onMove = {
4 | 'entityId':14,
5 | 'path' : [{'x':128,'y':796},{'x':677,'y':895}],
6 | 'speed':160
7 | };
8 |
9 | tc['area.playerHandler.enterScene'] = {
10 | entities : {
11 | item : [
12 | {
13 | entityId : 1,
14 | kindId : 1001
15 | },
16 | {
17 | entityId : 2,
18 | kindId : 1002
19 | }
20 | ],
21 | equipment : [
22 | {
23 | entityId : 1,
24 | kindId : 2001
25 | },
26 | {
27 | entityId : 2,
28 | kindId : 2002
29 | }
30 | ]
31 | },
32 | curPlayer : {
33 | entityId : 1,
34 | kindId : 3001,
35 | bag : {
36 | items : [
37 | {
38 | id : 1,
39 | type : 'pomelo'
40 | },
41 | {
42 | id : 2,
43 | type : 'protobuf'
44 | }
45 | ]
46 | },
47 | equipments : [
48 | {
49 | entityId : 1,
50 | kindId : 2001
51 | },
52 | {
53 | entityId : 2,
54 | kindId : 2002
55 | }
56 | ]
57 | },
58 | map : {
59 | name : 'a',
60 | width : 10,
61 | height : 10,
62 | tileW : 5,
63 | tileH : 5,
64 | weightMap:[
65 | {'collisions':[]},
66 | {'collisions':[]},
67 | {'collisions':[
68 | {'start':1,'length':3},
69 | {'start':79,'length':3}
70 | ]},
71 | {'collisions':[
72 | {'start':27,'length':2},
73 | {'start':78,'length':4}
74 | ]}
75 | ]
76 | }
77 | };
--------------------------------------------------------------------------------
/test/rootMsgTest.js:
--------------------------------------------------------------------------------
1 | var protobuf = require('../lib/protobuf');
2 | var util = require('../lib/util');
3 | var should = require('should');
4 | var tc = require('./rootMsgTC');
5 |
6 | describe('msgEncoderTest', function(){
7 | var protos = protobuf.parse(require('./rootMsg.json'));
8 | // console.log(protos);
9 |
10 | protobuf.init({encoderProtos:protos, decoderProtos:protos});
11 |
12 | describe('encodeTest', function(){
13 | // console.log('%j', tc);
14 |
15 | for(var route in tc){
16 | var msg = tc[route];
17 |
18 | console.log('====================');
19 | console.log(route);
20 | var buffer = protobuf.encode(route, msg);
21 |
22 | console.log(msg);
23 | console.log(buffer.length);
24 | // console.log(buffer);
25 |
26 | var decodeMsg = protobuf.decode(route, buffer);
27 |
28 | console.log(decodeMsg);
29 | console.log('====================');
30 |
31 | util.equal(msg, decodeMsg).should.equal(true);
32 | }
33 | });
34 | });
--------------------------------------------------------------------------------
/test/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/stringBufferTest.js:
--------------------------------------------------------------------------------
1 |
2 | function joinTest(num){
3 |
4 | var arr = [];
5 | for(var i = 0; i < num; i++)
6 | arr.push(i + '');
7 |
8 | var start = Date.now();
9 | var str = '';
10 |
11 | for(var i = 0; i < num; i++){
12 | str += arr[i];
13 | }
14 |
15 | var end = Date.now();
16 | var time1 = end - start;
17 |
18 | start = Date.now();
19 | var arr = [];
20 | for(var i = 0; i < num; i++){
21 | arr.push(arr[i]);
22 | }
23 | var str1 = arr.join();
24 | end = Date.now();
25 | var time2 = end - start;
26 |
27 | console.log('test count : %j, \ncost 1 : %j, \ncost 2 : %j', num, time1, time2);
28 | }
29 |
30 | joinTest(100);
31 | joinTest(50000);
32 | joinTest(100000);
33 | joinTest(200000);
34 | //joinTest(500000);
--------------------------------------------------------------------------------
/test/testMsg.js:
--------------------------------------------------------------------------------
1 | var tc = module.exports;
2 |
3 | tc['area.playerHandler.enterScene'] = {
4 | map : {
5 | name : 'a',
6 | width : 10,
7 | height : 10,
8 | tileW : 5,
9 | tileH : 5,
10 | weightMap:[
11 | {'collisions':[]},
12 | {'collisions':[]},
13 | {'collisions':[
14 | {'start':1,'length':3},
15 | {'start':79,'length':3}
16 | ]},
17 | {'collisions':[
18 | {'start':27,'length':2},
19 | {'start':78,'length':4}
20 | ]}
21 | ]
22 | }
23 | };
24 |
25 | tc.onMove = {
26 | 'entityId':14,
27 | 'path' : [{'x':128,'y':796},{'x':677,'y':895}],
28 | 'speed':160
29 | };
30 |
31 | tc.onUpgrade = {
32 | id:32726,
33 | entityId:48,
34 | name:'super1',
35 | kindId:210,
36 | kindName:'Angle',
37 | type:'player',
38 | x:755,
39 | y:608,
40 | hp:352,
41 | mp:32,
42 | maxHp:352,
43 | maxMp:32,
44 | level:3,
45 | experience:2,
46 | attackValue:37,
47 | defenceValue:15,
48 | walkSpeed:240,
49 | attackSpeed:1.2,
50 | areaId:1,
51 | hitRate:90,
52 | dodgeRate:13,
53 | nextLevelExp:114,
54 | skillPoint:3
55 | };
56 |
--------------------------------------------------------------------------------
/test/writeProtos.js:
--------------------------------------------------------------------------------
1 | var protobuf = require('../lib/protobuf');
2 | var fs = require('fs');
3 |
4 | var protoFile = "./rootMsg.json";
5 | var protoTarget = "./rootProtos.json";
6 | var msgFile = "./rootMsgTC";
7 | var msgTarget = "./rootMsg.json";
8 |
9 | var protos = protobuf.parse(require(protoFile));
10 |
11 | console.log(protos);
12 | fs.writeFile(protoTarget, JSON.stringify(protos, null ,2));
13 |
14 | fs.writeFile(msgTarget, JSON.stringify(require(msgFile), null ,2));
15 |
16 |
--------------------------------------------------------------------------------