├── client ├── run ├── run2 ├── print_r.lua └── client.lua ├── 3rd └── lua-cjson │ ├── tests │ ├── types.json │ ├── octets-escaped.dat │ ├── README │ ├── numbers.json │ ├── example2.json │ ├── rfc-example1.json │ ├── rfc-example2.json │ ├── example3.json │ ├── genutf8.pl │ ├── example1.json │ ├── example5.json │ ├── example4.json │ ├── bench.lua │ └── test.lua │ ├── THANKS │ ├── lua │ ├── json2lua.lua │ ├── lua2json.lua │ └── cjson │ │ └── util.lua │ ├── fpconv.h │ ├── LICENSE │ ├── lua-cjson-2.1.0-1.rockspec │ ├── NEWS │ ├── dtoa_config.h │ ├── runtests.sh │ ├── lua-cjson.spec │ ├── CMakeLists.txt │ ├── g_fmt.c │ ├── performance.txt │ ├── Makefile │ ├── strbuf.h │ ├── fpconv.c │ ├── strbuf.c │ ├── performance.html │ └── rfc4627.txt ├── server ├── kill ├── test_run ├── run ├── lualib │ ├── config │ │ ├── gameserver.lua │ │ ├── loginserver.lua │ │ ├── system.lua │ │ └── database.lua │ ├── db │ │ ├── packer.lua │ │ ├── account.lua │ │ └── character.lua │ ├── agent │ │ ├── map_handler.lua │ │ ├── move_handler.lua │ │ ├── handler.lua │ │ ├── combat_handler.lua │ │ ├── character_handler.lua │ │ └── aoi_handler.lua │ ├── base │ │ ├── protoloader.lua │ │ ├── uuid.lua │ │ └── syslog.lua │ ├── map │ │ ├── quadtree.lua │ │ └── aoi.lua │ └── gameserver │ │ ├── gameserver.lua │ │ └── gateserver.lua ├── test │ ├── main.lua │ ├── test_aes.lua │ ├── config │ └── test_srp.lua ├── service │ ├── protod.lua │ ├── gdd.lua │ ├── main.lua │ ├── world.lua │ ├── database.lua │ ├── loginserver.lua │ ├── gamed.lua │ ├── map.lua │ ├── loginslave.lua │ └── agent.lua ├── lualib-src │ ├── lua-uuid.c │ ├── lua-aes.c │ └── lua-srp.c ├── config ├── service-src │ └── service_syslog.c └── Makefile ├── common ├── constant.lua ├── gddata │ ├── class.lua │ ├── gdd.lua │ ├── race.lua │ ├── map.lua │ └── attribute.lua └── proto │ ├── login_proto.lua │ └── game_proto.lua ├── tool └── setup │ ├── script │ ├── redis-shutdown │ ├── redis-start │ └── redis-reset │ ├── setup │ └── redis │ └── setup ├── .gitmodules ├── .gitignore ├── LICENSE └── README.md /client/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lua client.lua 4 | 5 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/types.json: -------------------------------------------------------------------------------- 1 | { "array": [ 10, true, null ] } 2 | -------------------------------------------------------------------------------- /client/run2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lua client.lua asdfasdf 4 | 5 | -------------------------------------------------------------------------------- /server/kill: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kill `cat *.pid` 4 | rm *.pid 5 | 6 | -------------------------------------------------------------------------------- /server/test_run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ../3rd/skynet/skynet ./test/config 4 | -------------------------------------------------------------------------------- /server/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SERVER_NAME=smmo 4 | export OS=$(uname -s) 5 | 6 | ../3rd/skynet/skynet config 7 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/octets-escaped.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jintiao/some-mmorpg/HEAD/3rd/lua-cjson/tests/octets-escaped.dat -------------------------------------------------------------------------------- /common/constant.lua: -------------------------------------------------------------------------------- 1 | 2 | local constant = { 3 | 4 | default_password = "123abcDef$%^", 5 | 6 | } 7 | 8 | 9 | return constant 10 | -------------------------------------------------------------------------------- /common/gddata/class.lua: -------------------------------------------------------------------------------- 1 | local class = { 2 | ["warrior"] = { name = "warrior" }, 3 | ["mage"] = { name = "mage" }, 4 | } 5 | 6 | return class 7 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/README: -------------------------------------------------------------------------------- 1 | These JSON examples were taken from the JSON website 2 | (http://json.org/example.html) and RFC 4627. 3 | 4 | Used with permission. 5 | -------------------------------------------------------------------------------- /server/lualib/config/gameserver.lua: -------------------------------------------------------------------------------- 1 | local config = { 2 | name = "gameserver", 3 | port = 9555, 4 | maxclient = 64, 5 | pool = 2, 6 | } 7 | 8 | return config 9 | -------------------------------------------------------------------------------- /server/test/main.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | skynet.start(function() 4 | skynet.newservice ("test_srp") 5 | skynet.newservice ("test_aes") 6 | end) 7 | -------------------------------------------------------------------------------- /server/service/protod.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local protoloader = require "protoloader" 4 | 5 | skynet.start (function () 6 | protoloader.init () 7 | end) 8 | -------------------------------------------------------------------------------- /tool/setup/script/redis-shutdown: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for i in `seq 0 32` 4 | do 5 | PORT=$(($i + 6379)) 6 | redis-cli -p ${PORT} shutdown 7 | cd .. 8 | done 9 | 10 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/numbers.json: -------------------------------------------------------------------------------- 1 | [ 0.110001, 2 | 0.12345678910111, 3 | 0.412454033640, 4 | 2.6651441426902, 5 | 2.718281828459, 6 | 3.1415926535898, 7 | 2.1406926327793 ] 8 | -------------------------------------------------------------------------------- /server/lualib/config/loginserver.lua: -------------------------------------------------------------------------------- 1 | local config = { 2 | port = 9777, 3 | slave = 8, 4 | auth_timeout = 10, -- seconds 5 | session_expire_time = 30 * 60, -- seconds 6 | } 7 | 8 | return config 9 | -------------------------------------------------------------------------------- /server/service/gdd.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local sharedata = require "sharedata" 3 | local gdd = require "gddata.gdd" 4 | 5 | skynet.start (function () 6 | sharedata.new ("gdd", gdd) 7 | end) 8 | -------------------------------------------------------------------------------- /common/gddata/gdd.lua: -------------------------------------------------------------------------------- 1 | 2 | local gdd = { 3 | race = require "gddata.race", 4 | class = require "gddata.class", 5 | map = require "gddata.map", 6 | attribute = require "gddata.attribute", 7 | } 8 | 9 | return gdd 10 | -------------------------------------------------------------------------------- /tool/setup/script/redis-start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR="${PWD}/$(dirname $0)" 4 | 5 | for i in `seq 0 32` 6 | do 7 | NAME="redis${i}" 8 | DIR="${BASEDIR}/${NAME}" 9 | cd ${DIR} 10 | redis-server ${NAME}.conf 11 | done 12 | 13 | -------------------------------------------------------------------------------- /tool/setup/script/redis-reset: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR="${PWD}/$(dirname $0)" 4 | 5 | ${BASEDIR}/redis-shutdown 6 | 7 | for i in `seq 0 32` 8 | do 9 | NAME="redis${i}" 10 | DIR="${BASEDIR}/${NAME}" 11 | rm ${DIR}/${NAME}.rdb 12 | done 13 | 14 | -------------------------------------------------------------------------------- /3rd/lua-cjson/THANKS: -------------------------------------------------------------------------------- 1 | The following people have helped with bug reports, testing and/or 2 | suggestions: 3 | 4 | - Louis-Philippe Perron (@loopole) 5 | - Ondřej Jirman 6 | - Steve Donovan 7 | - Zhang "agentzh" Yichun 8 | 9 | Thanks! 10 | -------------------------------------------------------------------------------- /server/lualib/db/packer.lua: -------------------------------------------------------------------------------- 1 | local cjson = require "cjson" 2 | cjson.encode_sparse_array(true, 1, 1) 3 | 4 | local packer = {} 5 | 6 | function packer.pack (v) 7 | return cjson.encode (v) 8 | end 9 | 10 | function packer.unpack (v) 11 | return cjson.decode (v) 12 | end 13 | 14 | return packer 15 | -------------------------------------------------------------------------------- /server/lualib/config/system.lua: -------------------------------------------------------------------------------- 1 | local config = {} 2 | 3 | config.log_level = 1 -- 1:debug 2:info 3:notice 4:warning 5:error 4 | 5 | config.debug_port = 9333 6 | 7 | config.gamed = { 8 | name = "gameserver", 9 | port = 9555, 10 | maxclient = 64, 11 | pool = 32, 12 | } 13 | 14 | return config 15 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/example2.json: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "id": "file", 3 | "value": "File", 4 | "popup": { 5 | "menuitem": [ 6 | {"value": "New", "onclick": "CreateNewDoc()"}, 7 | {"value": "Open", "onclick": "OpenDoc()"}, 8 | {"value": "Close", "onclick": "CloseDoc()"} 9 | ] 10 | } 11 | }} 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rd/skynet"] 2 | path = 3rd/skynet 3 | url = git@github.com:cloudwu/skynet.git 4 | branch = lua53 5 | [submodule "3rd/redis"] 6 | path = 3rd/redis 7 | url = git@github.com:antirez/redis.git 8 | branch = 2.8 9 | [submodule "3rd/openssl"] 10 | path = 3rd/openssl 11 | url = git@github.com:openssl/openssl.git 12 | branch = 1.0.2-aead 13 | -------------------------------------------------------------------------------- /tool/setup/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR=$(dirname $0) 4 | TARGET="${BASEDIR}/../../var" 5 | 6 | echo "running setup..." 7 | echo "target folder : ${TARGET}" 8 | 9 | if [ ! -d "${TARGET}" ] 10 | then 11 | mkdir $TARGET 12 | fi 13 | 14 | ${BASEDIR}/redis/setup ${TARGET} 15 | 16 | cp ${BASEDIR}/script/* ${TARGET} 17 | 18 | echo "setup finished." 19 | 20 | -------------------------------------------------------------------------------- /common/gddata/race.lua: -------------------------------------------------------------------------------- 1 | local race = { 2 | ["human"] = { 3 | name = "human", 4 | home = "Stormwind City", 5 | pos_x = 100, 6 | pos_y = 10, 7 | pos_z = 100, 8 | pos_o = 180, 9 | }, 10 | ["orc"] = { 11 | name = "orc", 12 | home = "Orgrimmar", 13 | pos_x = 900, 14 | pos_y = 10, 15 | pos_z = 900, 16 | pos_o = 0, 17 | }, 18 | } 19 | 20 | return race 21 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/rfc-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "Image": { 3 | "Width": 800, 4 | "Height": 600, 5 | "Title": "View from 15th Floor", 6 | "Thumbnail": { 7 | "Url": "http://www.example.com/image/481989943", 8 | "Height": 125, 9 | "Width": "100" 10 | }, 11 | "IDs": [116, 943, 234, 38793] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /3rd/lua-cjson/lua/json2lua.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- usage: json2lua.lua [json_file] 4 | -- 5 | -- Eg: 6 | -- echo '[ "testing" ]' | ./json2lua.lua 7 | -- ./json2lua.lua test.json 8 | 9 | local json = require "cjson" 10 | local util = require "cjson.util" 11 | 12 | local json_text = util.file_load(arg[1]) 13 | local t = json.decode(json_text) 14 | print(util.serialise_value(t)) 15 | -------------------------------------------------------------------------------- /server/lualib/config/database.lua: -------------------------------------------------------------------------------- 1 | local host = "127.0.0.1" 2 | local port = 6379 3 | local db = 0 4 | 5 | local center = { 6 | host = host, 7 | port = port, 8 | db = db, 9 | } 10 | 11 | local ngroup = 1 12 | local group = {} 13 | for i = 1, ngroup do 14 | table.insert (group, { host = host, port = port + i, db = db }) 15 | end 16 | 17 | local database_config = { center = center, group = group } 18 | 19 | return database_config 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | *.swp 31 | *.pid 32 | 33 | # other files 34 | var/ 35 | client/anonymous 36 | -------------------------------------------------------------------------------- /common/gddata/map.lua: -------------------------------------------------------------------------------- 1 | local map = { 2 | ["Stormwind City"] = { 3 | name = "Stormwind City", 4 | bbox = { 5 | left = 0, 6 | top = 0, 7 | right = 1000, 8 | bottom = 1000, 9 | }, 10 | radius = 20, 11 | }, 12 | ["Orgrimmar"] = { 13 | name = "Orgrimmar", 14 | bbox = { 15 | left = 0, 16 | top = 0, 17 | right = 1000, 18 | bottom = 1000, 19 | }, 20 | radius = 25, 21 | }, 22 | } 23 | 24 | return map 25 | -------------------------------------------------------------------------------- /server/lualib-src/lua-uuid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static uint32_t sid; 5 | 6 | static int 7 | lsid (lua_State *L) { 8 | if (sid >= 0xffff) 9 | return 0; 10 | sid++; 11 | lua_pushinteger (L, sid); 12 | return 1; 13 | } 14 | 15 | int 16 | luaopen_uuid_core (lua_State *L) { 17 | luaL_checkversion (L); 18 | luaL_Reg l[] = { 19 | { "sid", lsid }, 20 | { NULL, NULL }, 21 | }; 22 | luaL_newlib (L,l); 23 | sid = 0; 24 | return 1; 25 | } 26 | -------------------------------------------------------------------------------- /server/lualib/agent/map_handler.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local syslog = require "syslog" 4 | local handler = require "agent.handler" 5 | 6 | 7 | local REQUEST = {} 8 | local user 9 | handler = handler.new (REQUEST) 10 | 11 | handler:init (function (u) 12 | user = u 13 | end) 14 | 15 | function REQUEST.map_ready () 16 | local ok = skynet.call (user.map, "lua", "character_ready", user.character.movement.pos) or error () 17 | end 18 | 19 | return handler 20 | -------------------------------------------------------------------------------- /3rd/lua-cjson/lua/lua2json.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- usage: lua2json.lua [lua_file] 4 | -- 5 | -- Eg: 6 | -- echo '{ "testing" }' | ./lua2json.lua 7 | -- ./lua2json.lua test.lua 8 | 9 | local json = require "cjson" 10 | local util = require "cjson.util" 11 | 12 | local env = { 13 | json = { null = json.null }, 14 | null = json.null 15 | } 16 | 17 | local t = util.run_script("data = " .. util.file_load(arg[1]), env) 18 | print(json.encode(t.data)) 19 | 20 | -- vi:ai et sw=4 ts=4: 21 | -------------------------------------------------------------------------------- /server/test/test_aes.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local aes = require "aes" 3 | 4 | skynet.start (function () 5 | local text = "hello aes" 6 | local key = "key" 7 | local okey = "other key" 8 | 9 | local a = aes.encrypt (text, key) 10 | local b = aes.decrypt (a, key) 11 | local c = aes.decrypt (a, okey) 12 | 13 | if not (assert (text == b)) then 14 | print ("aes test failed") 15 | end 16 | 17 | if not (assert (text ~= c)) then 18 | print ("aes test failed") 19 | end 20 | 21 | print ("aes test passed") 22 | end) 23 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/rfc-example2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "precision": "zip", 4 | "Latitude": 37.7668, 5 | "Longitude": -122.3959, 6 | "Address": "", 7 | "City": "SAN FRANCISCO", 8 | "State": "CA", 9 | "Zip": "94107", 10 | "Country": "US" 11 | }, 12 | { 13 | "precision": "zip", 14 | "Latitude": 37.371991, 15 | "Longitude": -122.026020, 16 | "Address": "", 17 | "City": "SUNNYVALE", 18 | "State": "CA", 19 | "Zip": "94085", 20 | "Country": "US" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /server/test/config: -------------------------------------------------------------------------------- 1 | skynet_root = "../3rd/skynet/" 2 | root = "./" 3 | common = "../common/" 4 | thread = 8 5 | logger = nil 6 | harbor = 0 7 | start = "main" -- main script 8 | bootstrap = "snlua bootstrap" -- The service for bootstrap 9 | 10 | lua_path = skynet_root.."lualib/?.lua;"..root.."lualib/?.lua;"..root.."lualib/base/?.lua;"..common.."?.lua" 11 | lua_cpath = skynet_root.."luaclib/?.so;"..root.."luaclib/?.so" 12 | cpath = skynet_root.."cservice/?.so" 13 | luaservice = skynet_root.."service/?.lua;"..root.."test/?.lua" 14 | lualoader = skynet_root.."lualib/loader.lua" 15 | snax = skynet_root.."?.lua;" 16 | -------------------------------------------------------------------------------- /server/service/main.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local config = require "config.system" 4 | local login_config = require "config.loginserver" 5 | local game_config = require "config.gameserver" 6 | 7 | skynet.start(function() 8 | skynet.newservice ("debug_console", config.debug_port) 9 | skynet.newservice ("protod") 10 | skynet.uniqueservice ("database") 11 | 12 | local loginserver = skynet.newservice ("loginserver") 13 | skynet.call (loginserver, "lua", "open", login_config) 14 | 15 | local gamed = skynet.newservice ("gamed", loginserver) 16 | skynet.call (gamed, "lua", "open", game_config) 17 | end) 18 | -------------------------------------------------------------------------------- /3rd/lua-cjson/fpconv.h: -------------------------------------------------------------------------------- 1 | /* Lua CJSON floating point conversion routines */ 2 | 3 | /* Buffer required to store the largest string representation of a double. 4 | * 5 | * Longest double printed with %.14g is 21 characters long: 6 | * -1.7976931348623e+308 */ 7 | # define FPCONV_G_FMT_BUFSIZE 32 8 | 9 | #ifdef USE_INTERNAL_FPCONV 10 | static inline void fpconv_init() 11 | { 12 | /* Do nothing - not required */ 13 | } 14 | #else 15 | extern inline void fpconv_init(); 16 | #endif 17 | 18 | extern int fpconv_g_fmt(char*, double, int); 19 | extern double fpconv_strtod(const char*, char**); 20 | 21 | /* vi:ai et sw=4 ts=4: 22 | */ 23 | -------------------------------------------------------------------------------- /common/gddata/attribute.lua: -------------------------------------------------------------------------------- 1 | local health_max = { 2 | warrior = { 3 | [1] = 100, 4 | [2] = 300, 5 | [3] = 500, 6 | }, 7 | 8 | mage = { 9 | [1] = 80, 10 | [2] = 160, 11 | [3] = 240, 12 | }, 13 | } 14 | 15 | local strength = { 16 | human = { 17 | [1] = 22, 18 | [2] = 24, 19 | [3] = 26, 20 | }, 21 | orc = { 22 | [1] = 24, 23 | [2] = 27, 24 | [3] = 30, 25 | }, 26 | } 27 | 28 | local stamina = { 29 | human = { 30 | [1] = 21, 31 | [2] = 23, 32 | [3] = 25, 33 | }, 34 | orc = { 35 | [1] = 23, 36 | [2] = 26, 37 | [3] = 29, 38 | }, 39 | } 40 | 41 | local attribute = { 42 | health_max = health_max, 43 | strength = strength, 44 | stamina = stamina, 45 | } 46 | 47 | 48 | return attribute 49 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/example3.json: -------------------------------------------------------------------------------- 1 | {"widget": { 2 | "debug": "on", 3 | "window": { 4 | "title": "Sample Konfabulator Widget", 5 | "name": "main_window", 6 | "width": 500, 7 | "height": 500 8 | }, 9 | "image": { 10 | "src": "Images/Sun.png", 11 | "name": "sun1", 12 | "hOffset": 250, 13 | "vOffset": 250, 14 | "alignment": "center" 15 | }, 16 | "text": { 17 | "data": "Click Here", 18 | "size": 36, 19 | "style": "bold", 20 | "name": "text1", 21 | "hOffset": 250, 22 | "vOffset": 100, 23 | "alignment": "center", 24 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 25 | } 26 | }} 27 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/genutf8.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Create test comparison data using a different UTF-8 implementation. 4 | 5 | # The generated utf8.dat file must have the following MD5 sum: 6 | # cff03b039d850f370a7362f3313e5268 7 | 8 | use strict; 9 | 10 | # 0xD800 - 0xDFFF are used to encode supplementary codepoints 11 | # 0x10000 - 0x10FFFF are supplementary codepoints 12 | my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF); 13 | 14 | my $utf8 = pack("U*", @codepoints); 15 | defined($utf8) or die "Unable create UTF-8 string\n"; 16 | 17 | open(FH, ">:utf8", "utf8.dat") 18 | or die "Unable to open utf8.dat: $!\n"; 19 | print FH $utf8 20 | or die "Unable to write utf8.dat\n"; 21 | close(FH); 22 | 23 | # vi:ai et sw=4 ts=4: 24 | -------------------------------------------------------------------------------- /server/config: -------------------------------------------------------------------------------- 1 | skynet_root = "../3rd/skynet/" 2 | root = "./" 3 | common = "../common/" 4 | thread = 8 5 | -- logger = "$SERVER_NAME" 6 | -- logservice = "syslog" 7 | harbor = 0 8 | start = "main" -- main script 9 | bootstrap = "snlua bootstrap" -- The service for bootstrap 10 | 11 | lua_path = skynet_root.."lualib/?.lua;"..root.."lualib/?.lua;"..root.."lualib/base/?.lua;"..common.."?.lua" 12 | lua_cpath = skynet_root.."luaclib/?.so;"..root.."luaclib/?.so" 13 | cpath = skynet_root.."cservice/?.so;"..root.."cservice/?.so" 14 | luaservice = skynet_root.."service/?.lua;"..root.."service/?.lua" 15 | lualoader = skynet_root.."lualib/loader.lua" 16 | snax = skynet_root.."?.lua;" 17 | 18 | if "$OS" == "Linux" then 19 | -- daemon = root .. "$SERVER_NAME" .. ".pid" 20 | end 21 | 22 | -------------------------------------------------------------------------------- /server/lualib/agent/move_handler.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local syslog = require "syslog" 4 | local handler = require "agent.handler" 5 | local aoi_handler = require "agent.aoi_handler" 6 | 7 | 8 | local REQUEST = {} 9 | local user 10 | handler = handler.new (REQUEST) 11 | 12 | handler:init (function (u) 13 | user = u 14 | end) 15 | 16 | function REQUEST.move (args) 17 | assert (args and args.pos) 18 | 19 | local npos = args.pos 20 | local opos = user.character.movement.pos 21 | for k, v in pairs (opos) do 22 | if not npos[k] then 23 | npos[k] = v 24 | end 25 | end 26 | user.character.movement.pos = npos 27 | 28 | local ok = skynet.call (user.map, "lua", "move_blink", npos) 29 | if not ok then 30 | user.character.movement.pos = opos 31 | error () 32 | end 33 | 34 | return { pos = npos } 35 | end 36 | 37 | return handler 38 | -------------------------------------------------------------------------------- /tool/setup/redis/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "redis.setup" 4 | 5 | BASEDIR=$(dirname $0) 6 | TEMPLATE="${BASEDIR}/redis.conf" 7 | 8 | if [[ $# -eq 0 ]] 9 | then 10 | TARGET="${BASEDIR}/var" 11 | else 12 | TARGET=$1 13 | fi 14 | 15 | if [ ! -d "${TARGET}" ] 16 | then 17 | mkdir $TARGET 18 | fi 19 | 20 | BASEPORT=6379 21 | 22 | for i in `seq 0 32` 23 | do 24 | NAME="redis${i}" 25 | DIR="${TARGET}/${NAME}" 26 | if [ ! -d "${DIR}" ] 27 | then 28 | mkdir $DIR 29 | fi 30 | 31 | PORT=$(($i + $BASEPORT)) 32 | PIDFILE="${NAME}.pid" 33 | LOGFILE="${NAME}.log" 34 | DBFILE="${NAME}.rdb" 35 | 36 | cat ${TEMPLATE} | 37 | sed "s@logfile \"\"@logfile ${LOGFILE}@g" | 38 | sed "s@port 6379@port ${PORT}@g" | 39 | sed "s@pidfile /var/run/redis.pid@pidfile ${PIDFILE}@g" | 40 | sed "s@dbfilename dump.rdb@dbfilename ${DBFILE}@g" > ${DIR}/${NAME}.conf 41 | done 42 | 43 | -------------------------------------------------------------------------------- /server/lualib/base/protoloader.lua: -------------------------------------------------------------------------------- 1 | local sprotoloader = require "sprotoloader" 2 | 3 | local loginp = require "proto.login_proto" 4 | local gamep = require "proto.game_proto" 5 | 6 | local loader = { 7 | GAME_TYPES = 0, 8 | 9 | LOGIN = 1, 10 | LOGIN_C2S = 1, 11 | LOGIN_S2C = 2, 12 | 13 | GAME = 3, 14 | GAME_C2S = 3, 15 | GAME_S2C = 4, 16 | } 17 | 18 | function loader.init () 19 | sprotoloader.save (gamep.types, loader.GAME_TYPES) 20 | 21 | sprotoloader.save (loginp.c2s, loader.LOGIN_C2S) 22 | sprotoloader.save (loginp.s2c, loader.LOGIN_S2C) 23 | 24 | sprotoloader.save (gamep.c2s, loader.GAME_C2S) 25 | sprotoloader.save (gamep.s2c, loader.GAME_S2C) 26 | end 27 | 28 | function loader.load (index) 29 | local host = sprotoloader.load (index):host "package" 30 | local request = host:attach (sprotoloader.load (index + 1)) 31 | return host, request 32 | end 33 | 34 | return loader 35 | -------------------------------------------------------------------------------- /client/print_r.lua: -------------------------------------------------------------------------------- 1 | local print = print 2 | local tconcat = table.concat 3 | local tinsert = table.insert 4 | local srep = string.rep 5 | local type = type 6 | local pairs = pairs 7 | local tostring = tostring 8 | local next = next 9 | 10 | local function print_r(root) 11 | local cache = { [root] = "." } 12 | local function _dump(t,space,name) 13 | local temp = {} 14 | for k,v in pairs(t) do 15 | local key = tostring(k) 16 | if cache[v] then 17 | tinsert(temp,"+" .. key .. " {" .. cache[v].."}") 18 | elseif type(v) == "table" then 19 | local new_key = name .. "." .. key 20 | cache[v] = new_key 21 | tinsert(temp,"+" .. key .. _dump(v,space .. (next(t,k) and "|" or " " ).. srep(" ",#key),new_key)) 22 | else 23 | tinsert(temp,"+" .. key .. " [" .. tostring(v).."]") 24 | end 25 | end 26 | return tconcat(temp,"\n"..space) 27 | end 28 | print(_dump(root, "","")) 29 | end 30 | 31 | return print_r -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "glossary": { 3 | "title": "example glossary", 4 | "GlossDiv": { 5 | "title": "S", 6 | "GlossList": { 7 | "GlossEntry": { 8 | "ID": "SGML", 9 | "SortAs": "SGML", 10 | "GlossTerm": "Standard Generalized Mark up Language", 11 | "Acronym": "SGML", 12 | "Abbrev": "ISO 8879:1986", 13 | "GlossDef": { 14 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 15 | "GlossSeeAlso": ["GML", "XML"] 16 | }, 17 | "GlossSee": "markup" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/example5.json: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "header": "SVG Viewer", 3 | "items": [ 4 | {"id": "Open"}, 5 | {"id": "OpenNew", "label": "Open New"}, 6 | null, 7 | {"id": "ZoomIn", "label": "Zoom In"}, 8 | {"id": "ZoomOut", "label": "Zoom Out"}, 9 | {"id": "OriginalView", "label": "Original View"}, 10 | null, 11 | {"id": "Quality"}, 12 | {"id": "Pause"}, 13 | {"id": "Mute"}, 14 | null, 15 | {"id": "Find", "label": "Find..."}, 16 | {"id": "FindAgain", "label": "Find Again"}, 17 | {"id": "Copy"}, 18 | {"id": "CopyAgain", "label": "Copy Again"}, 19 | {"id": "CopySVG", "label": "Copy SVG"}, 20 | {"id": "ViewSVG", "label": "View SVG"}, 21 | {"id": "ViewSource", "label": "View Source"}, 22 | {"id": "SaveAs", "label": "Save As"}, 23 | null, 24 | {"id": "Help"}, 25 | {"id": "About", "label": "About Adobe CVG Viewer..."} 26 | ] 27 | }} 28 | -------------------------------------------------------------------------------- /server/lualib/agent/handler.lua: -------------------------------------------------------------------------------- 1 | local handler = {} 2 | local mt = { __index = handler } 3 | 4 | function handler.new (request, response, cmd) 5 | return setmetatable ({ 6 | init_func = {}, 7 | request = request, 8 | response = response, 9 | cmd = cmd, 10 | }, mt) 11 | end 12 | 13 | function handler:init (f) 14 | table.insert (self.init_func, f) 15 | end 16 | 17 | local function merge (dest, t) 18 | if not dest or not t then return end 19 | for k, v in pairs (t) do 20 | dest[k] = v 21 | end 22 | end 23 | 24 | function handler:register (user) 25 | for _, f in pairs (self.init_func) do 26 | f (user) 27 | end 28 | 29 | merge (user.REQUEST, self.request) 30 | merge (user.RESPONSE, self.response) 31 | merge (user.CMD, self.cmd) 32 | end 33 | 34 | local function clean (dest, t) 35 | if not dest or not t then return end 36 | for k, _ in pairs (t) do 37 | dest[k] = nil 38 | end 39 | end 40 | 41 | function handler:unregister (user) 42 | clean (user.REQUEST, self.request) 43 | clean (user.RESPONSE, self.response) 44 | clean (user.CMD, self.cmd) 45 | end 46 | 47 | return handler 48 | -------------------------------------------------------------------------------- /server/lualib/base/uuid.lua: -------------------------------------------------------------------------------- 1 | local core = require "uuid.core" 2 | local skynet = require "skynet" 3 | local skynet_timeout = skynet.timeout 4 | 5 | 6 | -- [[uuid format : (33bits timestamp)(6bits harbor)(15bits service)(10bits sequence)]] 7 | local uuid = {} 8 | 9 | 10 | local timestamp 11 | local service 12 | local sequence 13 | function uuid.gen () 14 | if not service then 15 | local sid = core.sid () or error ("init uuid failed") 16 | local harbor = skynet.harbor (skynet.self ()) 17 | service = ((harbor & 0x3f) << 25) | ((sid & 0xffff) << 10) 18 | end 19 | 20 | if not timestamp then 21 | timestamp = (os.time () << 31) | service 22 | sequence = 0 23 | skynet_timeout (100, function () 24 | timestamp = nil 25 | end) 26 | end 27 | 28 | sequence = sequence + 1 29 | assert (sequence < 1024) 30 | 31 | return (timestamp | sequence) 32 | end 33 | 34 | function uuid.split (id) 35 | local timestamp = id >> 31 36 | local harbor = (id & 0x7fffffff) >> 25 37 | local service = (id & 0x1ffffff) >> 10 38 | local sequence = id & 0x3ff 39 | return timestamp, harbor, service, sequence 40 | end 41 | 42 | return uuid 43 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 jintiao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /3rd/lua-cjson/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2012 Mark Pulford 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /server/lualib/agent/combat_handler.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local syslog = require "syslog" 4 | local handler = require "agent.handler" 5 | local aoi_handler = require "agent.aoi_handler" 6 | 7 | 8 | local REQUEST = {} 9 | local CMD = {} 10 | local user 11 | handler = handler.new (REQUEST, nil, CMD) 12 | 13 | handler:init (function (u) 14 | user = u 15 | end) 16 | 17 | 18 | function REQUEST.combat (args) 19 | assert (args and args.target) 20 | 21 | local tid = args.target 22 | local agent = aoi_handler.find (tid) or error () 23 | 24 | local damage = user.character.attribute.attack_power 25 | damage = skynet.call (agent, "lua", "combat_melee_damage", user.character.id, damage) 26 | 27 | return { target = tid, damage = damage } 28 | end 29 | 30 | function CMD.combat_melee_damage (attacker, damage) 31 | damage = math.floor (damage * 0.75) 32 | 33 | hp = user.character.attribute.health - damage 34 | if hp <= 0 then 35 | damage = damage + hp 36 | hp = user.character.attribute.health_max 37 | end 38 | user.character.attribute.health = hp 39 | 40 | aoi_handler.boardcast ("attribute") 41 | return damage 42 | end 43 | 44 | return handler 45 | -------------------------------------------------------------------------------- /server/service-src/service_syslog.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "skynet.h" 6 | 7 | 8 | static const char * 9 | check_msg (const char *msg, size_t sz, int *priority) { 10 | if (sz < 2 || msg[1] != '|') 11 | return msg; 12 | switch (msg[0]) { 13 | case 'D': 14 | *priority = LOG_DEBUG; 15 | break; 16 | case 'I': 17 | *priority = LOG_INFO; 18 | break; 19 | case 'N': 20 | *priority = LOG_NOTICE; 21 | break; 22 | case 'W': 23 | *priority = LOG_WARNING; 24 | break; 25 | case 'E': 26 | *priority = LOG_ERR; 27 | break; 28 | default: 29 | return msg; 30 | } 31 | return &msg[2]; 32 | } 33 | 34 | static int 35 | cb (struct skynet_context *ctx, void *ud, int type, int session, uint32_t source, const void *msg, size_t sz) { 36 | int priority = LOG_WARNING; 37 | const char *str = check_msg ((const char *)msg, sz, &priority); 38 | syslog (priority, "[:%08x] %s", source, str); 39 | return 0; 40 | } 41 | 42 | int 43 | syslog_init (void *inst, struct skynet_context *ctx, const char *ident) { 44 | openlog (ident, LOG_PID, LOG_LOCAL3); 45 | skynet_callback (ctx, NULL, cb); 46 | skynet_command (ctx, "REG", ".logger"); 47 | return 0; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /server/test/test_srp.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local srp = require "srp" 3 | 4 | local function protect (...) 5 | local ok = pcall (...) 6 | assert (ok == false) 7 | end 8 | 9 | skynet.start (function () 10 | local I = "jintiao" 11 | local p = "123abc*()DEF" 12 | 13 | -- call at server side when user register new user 14 | protect (srp.create_verifier) 15 | protect (srp.create_verifier, I) 16 | local s, v = srp.create_verifier (I, p) 17 | assert (s and v) 18 | 19 | -- call at client side when user try to login 20 | local a, A = srp.create_client_key () 21 | 22 | -- call at server side. A is send from client to server 23 | protect (srp.create_server_session_key) 24 | protect (srp.create_server_session_key, v) 25 | local Ks, b, B = srp.create_server_session_key (v, A) 26 | 27 | -- call at client side. s, B is send from server to client 28 | protect (srp.create_client_session_key) 29 | protect (srp.create_client_session_key, I) 30 | local Kc = srp.create_client_session_key (I, p, s, a, A, B) 31 | 32 | -- we should not use this in real world, K must not expose to network 33 | -- use this key to encrypt something then verify it on other side is more reasonable 34 | if assert (Ks == Kc) then 35 | print ("srp test passed") 36 | else 37 | print ("srp test failed") 38 | end 39 | end) 40 | -------------------------------------------------------------------------------- /server/lualib/db/account.lua: -------------------------------------------------------------------------------- 1 | local constant = require "constant" 2 | local srp = require "srp" 3 | 4 | 5 | local account = {} 6 | local connection_handler 7 | 8 | function account.init (ch) 9 | connection_handler = ch 10 | end 11 | 12 | local function make_key (name) 13 | return connection_handler (name), string.format ("user:%s", name) 14 | end 15 | 16 | function account.load (name) 17 | assert (name) 18 | 19 | local acc = { name = name } 20 | 21 | local connection, key = make_key (name) 22 | if connection:exists (key) then 23 | acc.id = connection:hget (key, "account") 24 | acc.salt = connection:hget (key, "salt") 25 | acc.verifier = connection:hget (key, "verifier") 26 | else 27 | acc.salt, acc.verifier = srp.create_verifier (name, constant.default_password) 28 | end 29 | 30 | return acc 31 | end 32 | 33 | function account.create (id, name, password) 34 | assert (id and name and #name < 24 and password and #password < 24, "invalid argument") 35 | 36 | local connection, key = make_key (name) 37 | assert (connection:hsetnx (key, "account", id) ~= 0, "create account failed") 38 | 39 | local salt, verifier = srp.create_verifier (name, password) 40 | assert (connection:hmset (key, "salt", salt, "verifier", verifier) ~= 0, "save account verifier failed") 41 | 42 | return id 43 | end 44 | 45 | return account 46 | -------------------------------------------------------------------------------- /server/lualib/base/syslog.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local config = require "config.system" 4 | 5 | local syslog = { 6 | prefix = { 7 | "D|", 8 | "I|", 9 | "N|", 10 | "W|", 11 | "E|", 12 | }, 13 | } 14 | 15 | local level 16 | function syslog.level (lv) 17 | level = lv 18 | end 19 | 20 | local function write (priority, fmt, ...) 21 | if priority >= level then 22 | skynet.error (syslog.prefix[priority] .. fmt, ...) 23 | end 24 | end 25 | 26 | local function writef (priority, ...) 27 | if priority >= level then 28 | skynet.error (syslog.prefix[priority] .. string.format (...)) 29 | end 30 | end 31 | 32 | function syslog.debug (...) 33 | write (1, ...) 34 | end 35 | 36 | function syslog.debugf (...) 37 | writef (1, ...) 38 | end 39 | 40 | function syslog.info (...) 41 | write (2, ...) 42 | end 43 | 44 | function syslog.infof (...) 45 | writef (2, ...) 46 | end 47 | 48 | function syslog.notice (...) 49 | write (3, ...) 50 | end 51 | 52 | function syslog.noticef (...) 53 | writef (3, ...) 54 | end 55 | 56 | function syslog.warning (...) 57 | write (4, ...) 58 | end 59 | 60 | function syslog.warningf (...) 61 | writef (4, ...) 62 | end 63 | 64 | function syslog.err (...) 65 | write (5, ...) 66 | end 67 | 68 | function syslog.errf (...) 69 | writef (5, ...) 70 | end 71 | 72 | 73 | 74 | syslog.level (tonumber (config.log_level) or 3) 75 | 76 | return syslog 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 学习[skynet](https://github.com/cloudwu/skynet)时撸出来的简单服务器,欢迎吐槽。 4 | 5 | 完成度很低,已实现功能有: 6 | * 登陆 7 | * 角色创建 8 | * 地图内移动 9 | * 攻击 10 | * aoi 11 | 12 | # 编译及安装 13 | 14 | ## 编译依赖 15 | 16 | ### 3rd/openssl 17 | 程序中用到了openssl中的crypto库,请将crypto编译成**静态库**。 [参考文档](https://wiki.openssl.org/index.php/Compilation_and_Installation)。 18 | 对于64linux系统,可尝试用命令 "./Configure linux-x86_64 no-shared -fPIC; make"进行编译。 19 | 如果编译出错,请自行google解决。 20 | 21 | ### 3rd/skynet 22 | [参考文档](https://github.com/cloudwu/skynet)。 23 | 24 | ## 安装 25 | 1. 先安装redis 26 | 2. 执行 tool/setup/setup 脚本,它会在项目根目录下创建一个var目录,创建好数据库需要的文件和管理脚本。 27 | 28 | ## 运行 29 | 1. 运行 var/redis-start 脚本,启动好对应的数据库实例。 30 | 2. 运行 server/run 脚本,启动服务器程序。 31 | 32 | # 客户端 33 | 34 | 供测试用的客户端在 client 目录通过命令 “lua client.lua”或者脚本 “./run” 运行。 35 | 36 | client.lua 接受用户名、密码作为命令行参数 “lua client.lua username password”,如果留空,则由程序自动生成一个用户名,保存在本地 anonymous 文件中。 37 | 38 | client.lua 会自动完成登陆相关的流程,然后等待用户输入。 39 | 40 | 用户输入以回车结束,输入内容将打包发送至服务器。 41 | 输入的格式为 “命令 参数”,全部命令请参考 common/proto/game_proto.lua 文件中的 game_proto.c2s 42 | 43 | 一个常见的client命令流程是这样的: 44 | 45 | ```lua 46 | cd client 47 | ./run 48 | character_create character = { name = “hello”, race = “human”, class = “warrior” } 49 | character_list 50 | character_pick id = 4 51 | map_ready 52 | move pos = { x = 123, z = 321 } 53 | combat target = 7 54 | ``` 55 | 56 | # 其他 57 | 58 | 详细说明请移步 [wiki](https://github.com/jintiao/some-mmorpg/wiki) 59 | -------------------------------------------------------------------------------- /server/lualib/db/character.lua: -------------------------------------------------------------------------------- 1 | local syslog = require "syslog" 2 | local packer = require "db.packer" 3 | 4 | local character = {} 5 | local connection_handler 6 | 7 | function character.init (ch) 8 | connection_handler = ch 9 | end 10 | 11 | local function make_list_key (account) 12 | local major = account // 100 13 | local minor = account % 100 14 | return connection_handler (account), string.format ("char-list:%d", major), minor 15 | end 16 | 17 | local function make_character_key (id) 18 | local major = id // 100 19 | local minor = id % 100 20 | return connection_handler (id), string.format ("character:%d", major), minor 21 | end 22 | 23 | local function make_name_key (name) 24 | return connection_handler (name), "char-name", name 25 | end 26 | 27 | function character.reserve (id, name) 28 | local connection, key, field = make_name_key (name) 29 | assert (connection:hsetnx (key, field, id) ~= 0) 30 | return id 31 | end 32 | 33 | function character.save (id, data) 34 | connection, key, field = make_character_key (id) 35 | connection:hset (key, field, data) 36 | end 37 | 38 | function character.load (id) 39 | connection, key, field = make_character_key (id) 40 | local data = connection:hget (key, field) or error () 41 | return data 42 | end 43 | 44 | function character.list (account) 45 | local connection, key, field = make_list_key (account) 46 | local v = connection:hget (key, field) or error () 47 | return v 48 | end 49 | 50 | function character.savelist (id, data) 51 | connection, key, field = make_list_key (id) 52 | connection:hset (key, field, data) 53 | end 54 | 55 | return character 56 | 57 | -------------------------------------------------------------------------------- /common/proto/login_proto.lua: -------------------------------------------------------------------------------- 1 | local sparser = require "sprotoparser" 2 | 3 | local login_proto = {} 4 | 5 | login_proto.c2s = sparser.parse [[ 6 | .package { 7 | type 0 : integer 8 | session 1 : integer 9 | } 10 | 11 | handshake 1 { 12 | request { 13 | name 0 : string # username 14 | client_pub 1 : string # srp argument, client public key, known as 'A' 15 | } 16 | response { 17 | user_exists 0 : boolean # 'true' if username is already used 18 | salt 1 : string # srp argument, salt, known as 's' 19 | server_pub 2 : string # srp argument, server public key, known as 'B' 20 | challenge 3 : string # session challenge 21 | } 22 | } 23 | 24 | auth 2 { 25 | request { 26 | challenge 0 : string # encrypted challenge 27 | password 1 : string # encrypted password. send this ONLY IF you're registrying new account 28 | } 29 | response { 30 | session 0 : integer # login session id, needed for further use 31 | expire 1 : integer # session expire time, in second 32 | challenge 2 : string # token request challenge 33 | } 34 | } 35 | 36 | challenge 3 { 37 | request { 38 | session 0 : integer # login session id 39 | challenge 1 : string # encryped challenge 40 | } 41 | response { 42 | token 0 : string # login token 43 | challenge 1 : string # next token challenge 44 | } 45 | } 46 | 47 | login 4 { 48 | request { 49 | session 0 : integer # login session id 50 | token 1 : string # encryped token 51 | } 52 | } 53 | 54 | ]] 55 | 56 | login_proto.s2c = sparser.parse [[ 57 | .package { 58 | type 0 : integer 59 | session 1 : integer 60 | } 61 | ]] 62 | 63 | return login_proto 64 | -------------------------------------------------------------------------------- /server/service/world.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local sharedata = require "sharedata" 3 | local syslog = require "syslog" 4 | local mapdata = require "gddata.map" 5 | 6 | local CMD = {} 7 | local map_instance = {} 8 | local online_character = {} 9 | 10 | function CMD.kick (character) 11 | local a = online_character[character] 12 | if a then 13 | skynet.call (a, "lua", "kick") 14 | online_character[character] = nil 15 | end 16 | end 17 | 18 | function CMD.character_enter (agent, character) 19 | if online_character[character] ~= nil then 20 | syslog.notice (string.format ("multiple login detected, character %d", character)) 21 | CMD.kick (character) 22 | end 23 | 24 | online_character[character] = agent 25 | syslog.notice (string.format ("character(%d) enter world", character)) 26 | local map, pos = skynet.call (agent, "lua", "world_enter", skynet.self ()) 27 | 28 | local m = map_instance[map] 29 | if not m then 30 | CMD.kick (character) 31 | return 32 | end 33 | 34 | skynet.call (m, "lua", "character_enter", agent, character, pos) 35 | end 36 | 37 | function CMD.character_leave (agent, character) 38 | syslog.notice (string.format ("character(%d) leave world", character)) 39 | online_character[character] = nil 40 | end 41 | 42 | skynet.start (function () 43 | local self = skynet.self () 44 | for _, conf in pairs (mapdata) do 45 | local name = conf.name 46 | local s = skynet.newservice ("map", self) 47 | skynet.call (s, "lua", "init", conf) 48 | map_instance[name] = s 49 | end 50 | 51 | skynet.dispatch ("lua", function (_, source, command, ...) 52 | local f = assert (CMD[command]) 53 | skynet.retpack (f (source, ...)) 54 | end) 55 | end) 56 | -------------------------------------------------------------------------------- /server/service/database.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local redis = require "redis" 3 | 4 | local config = require "config.database" 5 | local account = require "db.account" 6 | local character = require "db.character" 7 | 8 | local center 9 | local group = {} 10 | local ngroup 11 | 12 | local function hash_str (str) 13 | local hash = 0 14 | string.gsub (str, "(%w)", function (c) 15 | hash = hash + string.byte (c) 16 | end) 17 | return hash 18 | end 19 | 20 | local function hash_num (num) 21 | local hash = num << 8 22 | return hash 23 | end 24 | 25 | function connection_handler (key) 26 | local hash 27 | local t = type (key) 28 | if t == "string" then 29 | hash = hash_str (key) 30 | else 31 | hash = hash_num (assert (tonumber (key))) 32 | end 33 | 34 | return group[hash % ngroup + 1] 35 | end 36 | 37 | 38 | local MODULE = {} 39 | local function module_init (name, mod) 40 | MODULE[name] = mod 41 | mod.init (connection_handler) 42 | end 43 | 44 | local traceback = debug.traceback 45 | 46 | skynet.start (function () 47 | module_init ("account", account) 48 | module_init ("character", character) 49 | 50 | center = redis.connect (config.center) 51 | ngroup = #config.group 52 | for _, c in ipairs (config.group) do 53 | table.insert (group, redis.connect (c)) 54 | end 55 | 56 | skynet.dispatch ("lua", function (_, _, mod, cmd, ...) 57 | local m = MODULE[mod] 58 | if not m then 59 | return skynet.ret () 60 | end 61 | local f = m[cmd] 62 | if not f then 63 | return skynet.ret () 64 | end 65 | 66 | local function ret (ok, ...) 67 | if not ok then 68 | skynet.ret () 69 | else 70 | skynet.retpack (...) 71 | end 72 | 73 | end 74 | ret (xpcall (f, traceback, ...)) 75 | end) 76 | end) 77 | -------------------------------------------------------------------------------- /server/service/loginserver.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local socket = require "socket" 3 | 4 | local syslog = require "syslog" 5 | local config = require "config.system" 6 | 7 | 8 | local session_id = 1 9 | local slave = {} 10 | local nslave 11 | local gameserver = {} 12 | 13 | local CMD = {} 14 | 15 | function CMD.open (conf) 16 | for i = 1, conf.slave do 17 | local s = skynet.newservice ("loginslave") 18 | skynet.call (s, "lua", "init", skynet.self (), i, conf) 19 | table.insert (slave, s) 20 | end 21 | nslave = #slave 22 | 23 | local host = conf.host or "0.0.0.0" 24 | local port = assert (tonumber (conf.port)) 25 | local sock = socket.listen (host, port) 26 | 27 | syslog.noticef ("listen on %s:%d", host, port) 28 | 29 | local balance = 1 30 | socket.start (sock, function (fd, addr) 31 | local s = slave[balance] 32 | balance = balance + 1 33 | if balance > nslave then balance = 1 end 34 | 35 | skynet.call (s, "lua", "auth", fd, addr) 36 | end) 37 | end 38 | 39 | function CMD.save_session (account, key, challenge) 40 | session = session_id 41 | session_id = session_id + 1 42 | 43 | s = slave[(session % nslave) + 1] 44 | skynet.call (s, "lua", "save_session", session, account, key, challenge) 45 | return session 46 | end 47 | 48 | function CMD.challenge (session, challenge) 49 | s = slave[(session % nslave) + 1] 50 | return skynet.call (s, "lua", "challenge", session, challenge) 51 | end 52 | 53 | function CMD.verify (session, token) 54 | local s = slave[(session % nslave) + 1] 55 | return skynet.call (s, "lua", "verify", session, token) 56 | end 57 | 58 | skynet.start (function () 59 | skynet.dispatch ("lua", function (_, _, command, ...) 60 | local f = assert (CMD[command]) 61 | skynet.retpack (f (...)) 62 | end) 63 | end) 64 | -------------------------------------------------------------------------------- /3rd/lua-cjson/lua-cjson-2.1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-cjson" 2 | version = "2.1.0-1" 3 | 4 | source = { 5 | url = "http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1.0.zip", 6 | } 7 | 8 | description = { 9 | summary = "A fast JSON encoding/parsing module", 10 | detailed = [[ 11 | The Lua CJSON module provides JSON support for Lua. It features: 12 | - Fast, standards compliant encoding/parsing routines 13 | - Full support for JSON with UTF-8, including decoding surrogate pairs 14 | - Optional run-time support for common exceptions to the JSON specification 15 | (infinity, NaN,..) 16 | - No dependencies on other libraries 17 | ]], 18 | homepage = "http://www.kyne.com.au/~mark/software/lua-cjson.php", 19 | license = "MIT" 20 | } 21 | 22 | dependencies = { 23 | "lua >= 5.1" 24 | } 25 | 26 | build = { 27 | type = "builtin", 28 | modules = { 29 | cjson = { 30 | sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" }, 31 | defines = { 32 | -- LuaRocks does not support platform specific configuration for Solaris. 33 | -- Uncomment the line below on Solaris platforms if required. 34 | -- "USE_INTERNAL_ISINF" 35 | } 36 | } 37 | }, 38 | install = { 39 | lua = { 40 | ["cjson.util"] = "lua/cjson/util.lua" 41 | }, 42 | bin = { 43 | json2lua = "lua/json2lua.lua", 44 | lua2json = "lua/lua2json.lua" 45 | } 46 | }, 47 | -- Override default build options (per platform) 48 | platforms = { 49 | win32 = { modules = { cjson = { defines = { 50 | "DISABLE_INVALID_NUMBERS" 51 | } } } } 52 | }, 53 | copy_directories = { "tests" } 54 | } 55 | 56 | -- vi:ai et sw=4 ts=4: 57 | -------------------------------------------------------------------------------- /server/Makefile: -------------------------------------------------------------------------------- 1 | 2 | THIRD_LIB_ROOT ?= ../3rd/ 3 | 4 | SKYNET_ROOT ?= $(THIRD_LIB_ROOT)skynet/ 5 | include $(SKYNET_ROOT)platform.mk 6 | 7 | CJSON_ROOT ?= $(THIRD_LIB_ROOT)lua-cjson 8 | CJSON_INC ?= ../skynet/3rd/lua 9 | 10 | OPENSSL_ROOT ?= $(THIRD_LIB_ROOT)openssl 11 | OPENSSL_FLAG ?= -I$(OPENSSL_ROOT)/include -L$(OPENSSL_ROOT) -lcrypto 12 | 13 | 14 | SKYNET_BUILD_PATH ?= . 15 | LUA_CLIB_PATH ?= luaclib 16 | MCSERVICE_PATH ?= cservice 17 | 18 | CFLAGS = -g -O2 -Wall -I$(LUA_INC) $(MYCFLAGS) 19 | 20 | # lua 21 | 22 | LUA_STATICLIB := $(SKYNET_ROOT)3rd/lua/liblua.a 23 | LUA_LIB ?= $(LUA_STATICLIB) 24 | LUA_INC ?= $(SKYNET_ROOT)3rd/lua 25 | 26 | # skynet 27 | 28 | MCSERVICE = syslog 29 | LUA_CLIB = srp aes cjson uuid 30 | 31 | all : \ 32 | $(foreach v, $(MCSERVICE), $(MCSERVICE_PATH)/$(v).so) \ 33 | $(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so) 34 | 35 | $(MCSERVICE_PATH) : 36 | mkdir $(MCSERVICE_PATH) 37 | 38 | $(LUA_CLIB_PATH) : 39 | mkdir $(LUA_CLIB_PATH) 40 | 41 | $(MCSERVICE_PATH)/syslog.so : service-src/service_syslog.c | $(MCSERVICE_PATH) 42 | $(CC) $(CFLAGS) $(SHARED) $< -o $@ -I$(SKYNET_ROOT)skynet-src 43 | 44 | $(LUA_CLIB_PATH)/srp.so : lualib-src/lua-srp.c | $(LUA_CLIB_PATH) 45 | $(CC) $(CFLAGS) $(SHARED) $^ $(OPENSSL_FLAG) -o $@ 46 | 47 | $(LUA_CLIB_PATH)/aes.so : lualib-src/lua-aes.c | $(LUA_CLIB_PATH) 48 | $(CC) $(CFLAGS) $(SHARED) $^ $(OPENSSL_FLAG) -o $@ 49 | 50 | $(LUA_CLIB_PATH)/uuid.so : lualib-src/lua-uuid.c | $(LUA_CLIB_PATH) 51 | $(CC) $(CFLAGS) $(SHARED) $^ $(OPENSSL_FLAG) -o $@ 52 | 53 | $(LUA_CLIB_PATH)/cjson.so : | $(LUA_CLIB_PATH) 54 | cd $(CJSON_ROOT) && $(MAKE) LUA_INCLUDE_DIR=$(CJSON_INC) CC=$(CC) CJSON_LDFLAGS="$(SHARED)" && cd - && cp $(CJSON_ROOT)/cjson.so $@ 55 | 56 | clean : 57 | rm -f $(MCSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so 58 | 59 | cleanall: clean 60 | 61 | -------------------------------------------------------------------------------- /server/service/gamed.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local gameserver = require "gameserver.gameserver" 4 | local syslog = require "syslog" 5 | 6 | 7 | local logind = tonumber (...) 8 | 9 | local gamed = {} 10 | 11 | local pending_agent = {} 12 | local pool = {} 13 | 14 | local online_account = {} 15 | 16 | function gamed.open (config) 17 | syslog.notice ("gamed opened") 18 | 19 | local self = skynet.self () 20 | local n = config.pool or 0 21 | for i = 1, n do 22 | table.insert (pool, skynet.newservice ("agent", self)) 23 | end 24 | 25 | skynet.uniqueservice ("gdd") 26 | skynet.uniqueservice ("world") 27 | end 28 | 29 | function gamed.command_handler (cmd, ...) 30 | local CMD = {} 31 | 32 | function CMD.close (agent, account) 33 | syslog.debugf ("agent %d recycled", agent) 34 | 35 | online_account[account] = nil 36 | table.insert (pool, agent) 37 | end 38 | 39 | function CMD.kick (agent, fd) 40 | gameserver.kick (fd) 41 | end 42 | 43 | local f = assert (CMD[cmd]) 44 | return f (...) 45 | end 46 | 47 | function gamed.auth_handler (session, token) 48 | return skynet.call (logind, "lua", "verify", session, token) 49 | end 50 | 51 | function gamed.login_handler (fd, account) 52 | local agent = online_account[account] 53 | if agent then 54 | syslog.warningf ("multiple login detected for account %d", account) 55 | skynet.call (agent, "lua", "kick", account) 56 | end 57 | 58 | if #pool == 0 then 59 | agent = skynet.newservice ("agent", skynet.self ()) 60 | syslog.noticef ("pool is empty, new agent(%d) created", agent) 61 | else 62 | agent = table.remove (pool, 1) 63 | syslog.debugf ("agent(%d) assigned, %d remain in pool", agent, #pool) 64 | end 65 | online_account[account] = agent 66 | 67 | skynet.call (agent, "lua", "open", fd, account) 68 | gameserver.forward (fd, agent) 69 | return agent 70 | end 71 | 72 | gameserver.start (gamed) 73 | -------------------------------------------------------------------------------- /3rd/lua-cjson/NEWS: -------------------------------------------------------------------------------- 1 | Version 2.1.0 (Mar 1 2012) 2 | * Added cjson.safe module interface which returns nil after an error 3 | * Improved Makefile compatibility with Solaris make 4 | 5 | Version 2.0.0 (Jan 22 2012) 6 | * Improved platform compatibility for strtod/sprintf locale workaround 7 | * Added option to build with David Gay's dtoa.c for improved performance 8 | * Added support for Lua 5.2 9 | * Added option to encode infinity/NaN as JSON null 10 | * Fixed encode bug with a raised default limit and deeply nested tables 11 | * Updated Makefile for compatibility with non-GNU make implementations 12 | * Added CMake build support 13 | * Added HTML manual 14 | * Increased default nesting limit to 1000 15 | * Added support for re-entrant use of encode and decode 16 | * Added support for installing lua2json and json2lua utilities 17 | * Added encode_invalid_numbers() and decode_invalid_numbers() 18 | * Added decode_max_depth() 19 | * Removed registration of global cjson module table 20 | * Removed refuse_invalid_numbers() 21 | 22 | Version 1.0.4 (Nov 30 2011) 23 | * Fixed numeric conversion under locales with a comma decimal separator 24 | 25 | Version 1.0.3 (Sep 15 2011) 26 | * Fixed detection of objects with numeric string keys 27 | * Provided work around for missing isinf() on Solaris 28 | 29 | Version 1.0.2 (May 30 2011) 30 | * Portability improvements for Windows 31 | - No longer links with -lm 32 | - Use "socket" instead of "posix" for sub-second timing 33 | * Removed UTF-8 test dependency on Perl Text::Iconv 34 | * Added simple CLI commands for testing Lua <-> JSON conversions 35 | * Added cjson.encode_number_precision() 36 | 37 | Version 1.0.1 (May 10 2011) 38 | * Added build support for OSX 39 | * Removed unnecessary whitespace from JSON output 40 | * Added cjson.encode_keep_buffer() 41 | * Fixed memory leak on Lua stack overflow exception 42 | 43 | Version 1.0 (May 9 2011) 44 | * Initial release 45 | -------------------------------------------------------------------------------- /server/service/map.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local syslog = require "syslog" 4 | local aoi = require "map.aoi" 5 | 6 | 7 | local world 8 | local conf 9 | 10 | local pending_character = {} 11 | local online_character = {} 12 | local CMD = {} 13 | 14 | function CMD.init (w, c) 15 | world = w 16 | conf = c 17 | aoi.init (conf.bbox, conf.radius) 18 | end 19 | 20 | function CMD.character_enter (_, agent, character) 21 | syslog.noticef ("character(%d) loading map", character) 22 | 23 | pending_character[agent] = character 24 | skynet.call (agent, "lua", "map_enter", skynet.self ()) 25 | end 26 | 27 | function CMD.character_leave (agent) 28 | local character = online_character[agent] or pending_character[agent] 29 | if character ~= nil then 30 | syslog.noticef ("character(%d) leave map", character) 31 | local ok, list = aoi.remove (agent) 32 | if ok then 33 | skynet.call (agent, "lua", "aoi_manage", nil, list) 34 | end 35 | end 36 | online_character[agent] = nil 37 | pending_character[agent] = nil 38 | end 39 | 40 | function CMD.character_ready (agent, pos) 41 | if pending_character[agent] == nil then return false end 42 | online_character[agent] = pending_character[agent] 43 | pending_character[agent] = nil 44 | 45 | syslog.noticef ("character(%d) enter map", online_character[agent]) 46 | 47 | local ok, list = aoi.insert (agent, pos) 48 | if not ok then return false end 49 | 50 | skynet.call (agent, "lua", "aoi_manage", list) 51 | return true 52 | end 53 | 54 | function CMD.move_blink (agent, pos) 55 | local ok, add, update, remove = aoi.update (agent, pos) 56 | if not ok then return end 57 | skynet.call (agent, "lua", "aoi_manage", add, remove, update, "move") 58 | return true 59 | end 60 | 61 | skynet.start (function () 62 | skynet.dispatch ("lua", function (_, source, command, ...) 63 | local f = assert (CMD[command]) 64 | skynet.retpack (f (source, ...)) 65 | end) 66 | end) 67 | -------------------------------------------------------------------------------- /server/lualib-src/lua-aes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define AES_KEY_LENGTH 16 9 | 10 | static int 11 | lencrypt (lua_State *L) { 12 | size_t len = 0; 13 | const char *text = lua_tolstring (L, 1, &len); 14 | const char *key = lua_tostring (L, 2); 15 | 16 | EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); 17 | unsigned char iv[16] = { 0 }; 18 | char *output = malloc (len + AES_BLOCK_SIZE); 19 | int olen1; 20 | int olen2; 21 | 22 | EVP_EncryptInit (ctx, EVP_aes_128_cbc(), (unsigned char *)key, iv); 23 | EVP_EncryptUpdate (ctx, (unsigned char *)output, &olen1, (const unsigned char *)text, len); 24 | int ok = EVP_EncryptFinal (ctx, (unsigned char *)output + olen1, &olen2); 25 | if (!ok) { 26 | free (output); 27 | EVP_CIPHER_CTX_free (ctx); 28 | return 0; 29 | } 30 | 31 | lua_pushlstring (L, output, olen1 + olen2); 32 | free (output); 33 | EVP_CIPHER_CTX_free (ctx); 34 | return 1; 35 | } 36 | 37 | static int 38 | ldecrypt (lua_State *L) { 39 | size_t len = 0; 40 | const char *text = lua_tolstring (L, 1, &len); 41 | const char *key = lua_tostring (L, 2); 42 | 43 | EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); 44 | unsigned char iv[16] = { 0 }; 45 | char *output = malloc (len); 46 | int olen1; 47 | int olen2; 48 | 49 | EVP_DecryptInit (ctx, EVP_aes_128_cbc(), (unsigned char *)key, iv); 50 | EVP_DecryptUpdate (ctx, (unsigned char *)output, &olen1, (const unsigned char *)text, len); 51 | int ok = EVP_DecryptFinal (ctx, (unsigned char *)(output + olen1), &olen2); 52 | if (!ok) { 53 | free (output); 54 | EVP_CIPHER_CTX_free (ctx); 55 | return 0; 56 | } 57 | 58 | lua_pushlstring (L, output, olen1 + olen2); 59 | free (output); 60 | EVP_CIPHER_CTX_free (ctx); 61 | return 1; 62 | } 63 | 64 | int 65 | luaopen_aes (lua_State *L) { 66 | luaL_checkversion (L); 67 | luaL_Reg l[] = { 68 | { "encrypt", lencrypt }, 69 | { "decrypt", ldecrypt }, 70 | { NULL, NULL }, 71 | }; 72 | luaL_newlib (L,l); 73 | return 1; 74 | } 75 | -------------------------------------------------------------------------------- /3rd/lua-cjson/dtoa_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _DTOA_CONFIG_H 2 | #define _DTOA_CONFIG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale 9 | * aware conversion routines. */ 10 | #undef USE_LOCALE 11 | 12 | /* dtoa.c should not touch errno, Lua CJSON does not use it, and it 13 | * may not be threadsafe */ 14 | #define NO_ERRNO 15 | 16 | #define Long int32_t 17 | #define ULong uint32_t 18 | #define Llong int64_t 19 | #define ULLong uint64_t 20 | 21 | #ifdef IEEE_BIG_ENDIAN 22 | #define IEEE_MC68k 23 | #else 24 | #define IEEE_8087 25 | #endif 26 | 27 | #define MALLOC(n) xmalloc(n) 28 | 29 | static void *xmalloc(size_t size) 30 | { 31 | void *p; 32 | 33 | p = malloc(size); 34 | if (!p) { 35 | fprintf(stderr, "Out of memory"); 36 | abort(); 37 | } 38 | 39 | return p; 40 | } 41 | 42 | #ifdef MULTIPLE_THREADS 43 | 44 | /* Enable locking to support multi-threaded applications */ 45 | 46 | #include 47 | 48 | static pthread_mutex_t private_dtoa_lock[2] = { 49 | PTHREAD_MUTEX_INITIALIZER, 50 | PTHREAD_MUTEX_INITIALIZER 51 | }; 52 | 53 | #define ACQUIRE_DTOA_LOCK(n) do { \ 54 | int r = pthread_mutex_lock(&private_dtoa_lock[n]); \ 55 | if (r) { \ 56 | fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \ 57 | abort(); \ 58 | } \ 59 | } while (0) 60 | 61 | #define FREE_DTOA_LOCK(n) do { \ 62 | int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \ 63 | if (r) { \ 64 | fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\ 65 | abort(); \ 66 | } \ 67 | } while (0) 68 | 69 | #endif /* MULTIPLE_THREADS */ 70 | 71 | #endif /* _DTOA_CONFIG_H */ 72 | 73 | /* vi:ai et sw=4 ts=4: 74 | */ 75 | -------------------------------------------------------------------------------- /3rd/lua-cjson/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PLATFORM="`uname -s`" 4 | [ "$1" ] && VERSION="$1" || VERSION="2.1.0" 5 | 6 | set -e 7 | 8 | # Portable "ggrep -A" replacement. 9 | # Work around Solaris awk record limit of 2559 bytes. 10 | # contextgrep PATTERN POST_MATCH_LINES 11 | contextgrep() { 12 | cut -c -2500 | awk "/$1/ { count = ($2 + 1) } count > 0 { count--; print }" 13 | } 14 | 15 | do_tests() { 16 | echo 17 | cd tests 18 | lua -e 'print("Testing Lua CJSON version " .. require("cjson")._VERSION)' 19 | ./test.lua | contextgrep 'FAIL|Summary' 3 | grep -v PASS | cut -c -150 20 | cd .. 21 | } 22 | 23 | echo "===== Setting LuaRocks PATH =====" 24 | eval "`luarocks path`" 25 | 26 | echo "===== Building UTF-8 test data =====" 27 | ( cd tests && ./genutf8.pl; ) 28 | 29 | echo "===== Cleaning old build data =====" 30 | make clean 31 | rm -f tests/cjson.so 32 | 33 | echo "===== Verifying cjson.so is not installed =====" 34 | 35 | cd tests 36 | if lua -e 'require "cjson"' 2>/dev/null 37 | then 38 | cat < "$LOG" 82 | RPM="`awk '/^Wrote: / && ! /debuginfo/ { print $2}' < "$LOG"`" 83 | sudo -- rpm -Uvh \"$RPM\" 84 | do_tests 85 | sudo -- rpm -e lua-cjson 86 | rm -f "$LOG" 87 | else 88 | echo "==> skipping, $TGZ not found" 89 | fi 90 | fi 91 | 92 | # vi:ai et sw=4 ts=4: 93 | -------------------------------------------------------------------------------- /server/lualib/map/quadtree.lua: -------------------------------------------------------------------------------- 1 | local quadtree = {} 2 | local mt = { __index = quadtree } 3 | 4 | function quadtree.new (l, t, r, b) 5 | return setmetatable ({ 6 | left = l, 7 | top = t, 8 | right = r, 9 | bottom = b, 10 | object = {}, 11 | }, mt) 12 | end 13 | 14 | function quadtree:insert (id, x, y) 15 | if x < self.left or x > self.right or y < self.top or y > self.bottom then return end 16 | 17 | if self.children then 18 | local t 19 | for _, v in pairs (self.children) do 20 | t = v:insert (id, x, y) 21 | if t then return t end 22 | end 23 | else 24 | self.object[id] = { x = x, y = y } 25 | 26 | if #self.object >= 2 then 27 | return self:subdivide (id) 28 | end 29 | 30 | return self 31 | end 32 | end 33 | 34 | function quadtree:subdevide (last) 35 | local left, top, right, bottom = self.left, self.top, self.right, self.bottom 36 | local centerx = (left + right) // 2 37 | local centery = (top + bottom) // 2 38 | 39 | self.children = { 40 | quadtree.new (left, top, centerx, centery), 41 | quadtree.new (centerx, top, right, centery), 42 | quadtree.new (left, centery, centerx, bottom), 43 | quadtree.new (centerx, centery, right, bottom), 44 | } 45 | 46 | local ret 47 | local t 48 | for k, v in pairs (self.object) do 49 | for _, c in self.children do 50 | t = c:insert (k, v.x, v.y) 51 | if t then 52 | if k == last then 53 | ret = t 54 | end 55 | break 56 | end 57 | end 58 | end 59 | self.object = nil 60 | 61 | return ret 62 | end 63 | 64 | function quadtree:remove (id) 65 | if self.object then 66 | if self.object[id] ~= nil then 67 | self.object[id] = nil 68 | return true 69 | end 70 | elseif self.children then 71 | for _, v in pairs (self.children) do 72 | if v:remove (id) then return true end 73 | end 74 | end 75 | end 76 | 77 | function quadtree:query (id, left, top, right, bottom, result) 78 | if left > self.right or right < self.left or top > self.bottom or bottom < self.top then return end 79 | 80 | if self.children then 81 | for _, v in pairs (self.children) do 82 | v:query (id, left, top, right, bottom, result) 83 | end 84 | else 85 | for k, v in pairs (self.object) do 86 | if k ~= id and v.x > left and v.x < right and v.y > top and v.y < bottom then 87 | table.insert (result, k) 88 | end 89 | end 90 | end 91 | end 92 | 93 | return quadtree 94 | -------------------------------------------------------------------------------- /server/lualib/map/aoi.lua: -------------------------------------------------------------------------------- 1 | local quadtree = require "map.quadtree" 2 | 3 | local aoi = {} 4 | 5 | local object = {} 6 | local qtree 7 | local radius 8 | 9 | function aoi.init (bbox, r) 10 | qtree = quadtree.new (bbox.left, bbox.top, bbox.right, bbox.bottom) 11 | radius = r 12 | end 13 | 14 | function aoi.insert (id, pos) 15 | if object[id] then return end 16 | 17 | local tree = qtree:insert (id, pos.x, pos.z) 18 | if not tree then return end 19 | 20 | local result = {} 21 | qtree:query (id, pos.x - radius, pos.z - radius, pos.x + radius, pos.z + radius, result) 22 | 23 | local list = {} 24 | for i = 1, #result do 25 | local cid = result[i] 26 | local c = object[cid] 27 | if c then 28 | list[cid] = cid 29 | c.list[id] = id 30 | end 31 | end 32 | 33 | object[id] = { id = id, pos = pos, qtree = tree, list = list } 34 | 35 | return true, list 36 | end 37 | 38 | function aoi.remove (id) 39 | local c = object[id] 40 | if not c then return end 41 | 42 | if c.qtree then 43 | c.qtree:remove (id) 44 | else 45 | qtree:remove (id) 46 | end 47 | 48 | for _, v in pairs (c.list) do 49 | local t = object[v] 50 | if t then 51 | t.list[id] = nil 52 | end 53 | end 54 | object[id] = nil 55 | 56 | return true, c.list 57 | end 58 | 59 | function aoi.update (id, pos) 60 | local c = object[id] 61 | if not c then return end 62 | 63 | if c.qtree then 64 | c.qtree:remove (id) 65 | else 66 | qtree:remove (id) 67 | end 68 | 69 | local olist = c.list 70 | 71 | local tree = qtree:insert (id, pos.x, pos.z) 72 | if not tree then return end 73 | 74 | c.pos = pos 75 | 76 | local result = {} 77 | qtree:query (id, pos.x - radius, pos.z - radius, pos.x + radius, pos.z + radius, result) 78 | 79 | local nlist = {} 80 | for i = 1, #result do 81 | local cid = result[i] 82 | nlist[cid] = cid 83 | end 84 | 85 | local ulist = {} 86 | for _, a in pairs (nlist) do 87 | local k = olist[a] 88 | if k then 89 | ulist[a] = a 90 | olist[a] = nil 91 | end 92 | end 93 | 94 | for _, a in pairs (ulist) do 95 | nlist[a] = nil 96 | end 97 | 98 | c.list = {} 99 | for _, v in pairs (nlist) do 100 | c.list[v] = v 101 | end 102 | for _, v in pairs (ulist) do 103 | c.list[v] = v 104 | end 105 | 106 | return true, nlist, ulist, olist 107 | end 108 | 109 | return aoi 110 | -------------------------------------------------------------------------------- /3rd/lua-cjson/lua-cjson.spec: -------------------------------------------------------------------------------- 1 | %define luaver 5.1 2 | %define lualibdir %{_libdir}/lua/%{luaver} 3 | %define luadatadir %{_datadir}/lua/%{luaver} 4 | 5 | Name: lua-cjson 6 | Version: 2.1.0 7 | Release: 1%{?dist} 8 | Summary: A fast JSON encoding/parsing module for Lua 9 | 10 | Group: Development/Libraries 11 | License: MIT 12 | URL: http://www.kyne.com.au/~mark/software/lua-cjson/ 13 | Source0: http://www.kyne.com.au/~mark/software/lua-cjson/download/lua-cjson-%{version}.tar.gz 14 | BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) 15 | 16 | BuildRequires: lua >= %{luaver}, lua-devel >= %{luaver} 17 | Requires: lua >= %{luaver} 18 | 19 | %description 20 | The Lua CJSON module provides JSON support for Lua. It features: 21 | - Fast, standards compliant encoding/parsing routines 22 | - Full support for JSON with UTF-8, including decoding surrogate pairs 23 | - Optional run-time support for common exceptions to the JSON specification 24 | (infinity, NaN,..) 25 | - No dependencies on other libraries 26 | 27 | 28 | %prep 29 | %setup -q 30 | 31 | 32 | %build 33 | make %{?_smp_mflags} CFLAGS="%{optflags}" LUA_INCLUDE_DIR="%{_includedir}" 34 | 35 | 36 | %install 37 | rm -rf "$RPM_BUILD_ROOT" 38 | make install DESTDIR="$RPM_BUILD_ROOT" LUA_CMODULE_DIR="%{lualibdir}" 39 | make install-extra DESTDIR="$RPM_BUILD_ROOT" LUA_MODULE_DIR="%{luadatadir}" \ 40 | LUA_BIN_DIR="%{_bindir}" 41 | 42 | 43 | %clean 44 | rm -rf "$RPM_BUILD_ROOT" 45 | 46 | 47 | %preun 48 | /bin/rm -f "%{luadatadir}/cjson/tests/utf8.dat" 49 | 50 | 51 | %files 52 | %defattr(-,root,root,-) 53 | %doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS 54 | %{lualibdir}/* 55 | %{luadatadir}/* 56 | %{_bindir}/* 57 | 58 | 59 | %changelog 60 | * Thu Mar 1 2012 Mark Pulford - 2.1.0-1 61 | - Update for 2.1.0 62 | 63 | * Sun Jan 22 2012 Mark Pulford - 2.0.0-1 64 | - Update for 2.0.0 65 | - Install lua2json / json2lua utilities 66 | 67 | * Wed Nov 27 2011 Mark Pulford - 1.0.4-1 68 | - Update for 1.0.4 69 | 70 | * Wed Sep 15 2011 Mark Pulford - 1.0.3-1 71 | - Update for 1.0.3 72 | 73 | * Sun May 29 2011 Mark Pulford - 1.0.2-1 74 | - Update for 1.0.2 75 | 76 | * Sun May 10 2011 Mark Pulford - 1.0.1-1 77 | - Update for 1.0.1 78 | 79 | * Sun May 1 2011 Mark Pulford - 1.0-1 80 | - Initial package 81 | -------------------------------------------------------------------------------- /3rd/lua-cjson/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # If Lua is installed in a non-standard location, please set the LUA_DIR 2 | # environment variable to point to prefix for the install. Eg: 3 | # Unix: export LUA_DIR=/home/user/pkg 4 | # Windows: set LUA_DIR=c:\lua51 5 | 6 | project(lua-cjson C) 7 | cmake_minimum_required(VERSION 2.6) 8 | 9 | option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance") 10 | option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON) 11 | 12 | if(NOT CMAKE_BUILD_TYPE) 13 | set(CMAKE_BUILD_TYPE Release CACHE STRING 14 | "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." 15 | FORCE) 16 | endif() 17 | 18 | find_package(Lua51 REQUIRED) 19 | include_directories(${LUA_INCLUDE_DIR}) 20 | 21 | if(NOT USE_INTERNAL_FPCONV) 22 | # Use libc number conversion routines (strtod(), sprintf()) 23 | set(FPCONV_SOURCES fpconv.c) 24 | else() 25 | # Use internal number conversion routines 26 | add_definitions(-DUSE_INTERNAL_FPCONV) 27 | set(FPCONV_SOURCES g_fmt.c dtoa.c) 28 | 29 | include(TestBigEndian) 30 | TEST_BIG_ENDIAN(IEEE_BIG_ENDIAN) 31 | if(IEEE_BIG_ENDIAN) 32 | add_definitions(-DIEEE_BIG_ENDIAN) 33 | endif() 34 | 35 | if(MULTIPLE_THREADS) 36 | set(CMAKE_THREAD_PREFER_PTHREAD TRUE) 37 | find_package(Threads REQUIRED) 38 | if(NOT CMAKE_USE_PTHREADS_INIT) 39 | message(FATAL_ERROR 40 | "Pthreads not found - required by MULTIPLE_THREADS option") 41 | endif() 42 | add_definitions(-DMULTIPLE_THREADS) 43 | endif() 44 | endif() 45 | 46 | # Handle platforms missing isinf() macro (Eg, some Solaris systems). 47 | include(CheckSymbolExists) 48 | CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) 49 | if(NOT HAVE_ISINF) 50 | add_definitions(-DUSE_INTERNAL_ISINF) 51 | endif() 52 | 53 | set(_MODULE_LINK "${CMAKE_THREAD_LIBS_INIT}") 54 | get_filename_component(_lua_lib_dir ${LUA_LIBRARY} PATH) 55 | 56 | if(APPLE) 57 | set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS 58 | "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") 59 | endif() 60 | 61 | if(WIN32) 62 | # Win32 modules need to be linked to the Lua library. 63 | set(_MODULE_LINK ${LUA_LIBRARY} ${_MODULE_LINK}) 64 | set(_lua_module_dir "${_lua_lib_dir}") 65 | # Windows sprintf()/strtod() handle NaN/inf differently. Not supported. 66 | add_definitions(-DDISABLE_INVALID_NUMBERS) 67 | else() 68 | set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") 69 | endif() 70 | 71 | add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES}) 72 | set_target_properties(cjson PROPERTIES PREFIX "") 73 | target_link_libraries(cjson ${_MODULE_LINK}) 74 | install(TARGETS cjson DESTINATION "${_lua_module_dir}") 75 | 76 | # vi:ai et sw=4 ts=4: 77 | -------------------------------------------------------------------------------- /server/lualib/gameserver/gameserver.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local gateserver = require "gameserver.gateserver" 4 | local syslog = require "syslog" 5 | local protoloader = require "protoloader" 6 | 7 | 8 | local gameserver = {} 9 | local pending_msg = {} 10 | 11 | function gameserver.forward (fd, agent) 12 | gateserver.forward (fd, agent) 13 | end 14 | 15 | function gameserver.kick (fd) 16 | gateserver.close_client (fd) 17 | end 18 | 19 | function gameserver.start (gamed) 20 | local handler = {} 21 | 22 | local host, send_request = protoloader.load (protoloader.LOGIN) 23 | 24 | function handler.open (source, conf) 25 | return gamed.open (conf) 26 | end 27 | 28 | function handler.connect (fd, addr) 29 | syslog.noticef ("connect from %s (fd = %d)", addr, fd) 30 | gateserver.open_client (fd) 31 | end 32 | 33 | function handler.disconnect (fd) 34 | syslog.noticef ("fd (%d) disconnected", fd) 35 | end 36 | 37 | local function do_login (fd, msg, sz) 38 | local type, name, args, response = host:dispatch (msg, sz) 39 | assert (type == "REQUEST") 40 | assert (name == "login") 41 | assert (args.session and args.token) 42 | local session = tonumber (args.session) or error () 43 | local account = gamed.auth_handler (session, args.token) or error () 44 | assert (account) 45 | return account 46 | end 47 | 48 | local traceback = debug.traceback 49 | function handler.message (fd, msg, sz) 50 | local queue = pending_msg[fd] 51 | if queue then 52 | table.insert (queue, { msg = msg, sz = sz }) 53 | else 54 | pending_msg[fd] = {} 55 | 56 | local ok, account = xpcall (do_login, traceback, fd, msg, sz) 57 | if ok then 58 | syslog.noticef ("account %d login success", account) 59 | local agent = gamed.login_handler (fd, account) 60 | queue = pending_msg[fd] 61 | for _, t in pairs (queue) do 62 | syslog.noticef ("forward pending message to agent %d", agent) 63 | skynet.rawcall(agent, "client", t.msg, t.sz) 64 | end 65 | else 66 | syslog.warningf ("%s login failed : %s", addr, account) 67 | gateserver.close_client (fd) 68 | end 69 | 70 | pending_msg[fd] = nil 71 | end 72 | end 73 | 74 | local CMD = {} 75 | 76 | function CMD.token (id, secret) 77 | local id = tonumber (id) 78 | login_token[id] = secret 79 | skynet.timeout (10 * 100, function () 80 | if login_token[id] == secret then 81 | syslog.noticef ("account %d token timeout", id) 82 | login_token[id] = nil 83 | end 84 | end) 85 | end 86 | 87 | function handler.command (cmd, ...) 88 | local f = CMD[cmd] 89 | if f then 90 | return f (...) 91 | else 92 | return gamed.command_handler (cmd, ...) 93 | end 94 | end 95 | 96 | return gateserver.start (handler) 97 | end 98 | 99 | return gameserver 100 | -------------------------------------------------------------------------------- /3rd/lua-cjson/g_fmt.c: -------------------------------------------------------------------------------- 1 | /**************************************************************** 2 | * 3 | * The author of this software is David M. Gay. 4 | * 5 | * Copyright (c) 1991, 1996 by Lucent Technologies. 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose without fee is hereby granted, provided that this entire notice 9 | * is included in all copies of any software which is or includes a copy 10 | * or modification of this software and in all copies of the supporting 11 | * documentation for such software. 12 | * 13 | * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 14 | * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY 15 | * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY 16 | * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. 17 | * 18 | ***************************************************************/ 19 | 20 | /* g_fmt(buf,x) stores the closest decimal approximation to x in buf; 21 | * it suffices to declare buf 22 | * char buf[32]; 23 | */ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | extern char *dtoa(double, int, int, int *, int *, char **); 29 | extern int g_fmt(char *, double, int); 30 | extern void freedtoa(char*); 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | int 36 | fpconv_g_fmt(char *b, double x, int precision) 37 | { 38 | register int i, k; 39 | register char *s; 40 | int decpt, j, sign; 41 | char *b0, *s0, *se; 42 | 43 | b0 = b; 44 | #ifdef IGNORE_ZERO_SIGN 45 | if (!x) { 46 | *b++ = '0'; 47 | *b = 0; 48 | goto done; 49 | } 50 | #endif 51 | s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se); 52 | if (sign) 53 | *b++ = '-'; 54 | if (decpt == 9999) /* Infinity or Nan */ { 55 | while((*b++ = *s++)); 56 | /* "b" is used to calculate the return length. Decrement to exclude the 57 | * Null terminator from the length */ 58 | b--; 59 | goto done0; 60 | } 61 | if (decpt <= -4 || decpt > precision) { 62 | *b++ = *s++; 63 | if (*s) { 64 | *b++ = '.'; 65 | while((*b = *s++)) 66 | b++; 67 | } 68 | *b++ = 'e'; 69 | /* sprintf(b, "%+.2d", decpt - 1); */ 70 | if (--decpt < 0) { 71 | *b++ = '-'; 72 | decpt = -decpt; 73 | } 74 | else 75 | *b++ = '+'; 76 | for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); 77 | for(;;) { 78 | i = decpt / k; 79 | *b++ = i + '0'; 80 | if (--j <= 0) 81 | break; 82 | decpt -= i*k; 83 | decpt *= 10; 84 | } 85 | *b = 0; 86 | } 87 | else if (decpt <= 0) { 88 | *b++ = '0'; 89 | *b++ = '.'; 90 | for(; decpt < 0; decpt++) 91 | *b++ = '0'; 92 | while((*b++ = *s++)); 93 | b--; 94 | } 95 | else { 96 | while((*b = *s++)) { 97 | b++; 98 | if (--decpt == 0 && *s) 99 | *b++ = '.'; 100 | } 101 | for(; decpt > 0; decpt--) 102 | *b++ = '0'; 103 | *b = 0; 104 | } 105 | done0: 106 | freedtoa(s0); 107 | #ifdef IGNORE_ZERO_SIGN 108 | done: 109 | #endif 110 | return b - b0; 111 | } 112 | -------------------------------------------------------------------------------- /common/proto/game_proto.lua: -------------------------------------------------------------------------------- 1 | local sparser = require "sprotoparser" 2 | 3 | local game_proto = {} 4 | 5 | local types = [[ 6 | 7 | .package { 8 | type 0 : integer 9 | session 1 : integer 10 | } 11 | 12 | .general { 13 | name 0 : string 14 | race 1 : string 15 | class 2 : string 16 | map 3 : string 17 | } 18 | 19 | .position { 20 | x 0 : integer 21 | y 1 : integer 22 | z 2 : integer 23 | o 3 : integer 24 | } 25 | 26 | .movement { 27 | pos 0 : position 28 | } 29 | 30 | .attribute { 31 | health 0 : integer 32 | level 1 : integer 33 | exp 2 : integer 34 | health_max 3 : integer 35 | strength 4 : integer 36 | stamina 5 : integer 37 | attack_power 6 : integer 38 | } 39 | 40 | .attribute_overview { 41 | level 0 : integer 42 | } 43 | 44 | .attribute_aoi { 45 | level 0 : integer 46 | health 1 : integer 47 | health_max 2 : integer 48 | } 49 | 50 | .character { 51 | id 0 : integer 52 | general 1 : general 53 | attribute 2 : attribute 54 | movement 3 : movement 55 | } 56 | 57 | .character_agent { 58 | id 0 : integer 59 | general 1 : general 60 | attribute 2 : attribute 61 | movement 3 : movement 62 | } 63 | 64 | .character_overview { 65 | id 0 : integer 66 | general 1 : general 67 | attribute 2 : attribute_overview 68 | } 69 | 70 | .character_aoi { 71 | id 0 : integer 72 | general 1 : general 73 | attribute 2 : attribute_aoi 74 | movement 3 : movement 75 | } 76 | 77 | .character_aoi_move { 78 | id 0 : integer 79 | movement 1 : movement 80 | } 81 | 82 | .character_aoi_attribute { 83 | id 0 : integer 84 | attribute 1 : attribute_aoi 85 | } 86 | ]] 87 | 88 | local c2s = [[ 89 | character_list 0 { 90 | response { 91 | character 0 : *character_overview(id) 92 | } 93 | } 94 | 95 | character_create 1 { 96 | request { 97 | character 0 : general 98 | } 99 | 100 | response { 101 | character 0 : character_overview 102 | errno 1 : integer 103 | } 104 | } 105 | 106 | character_pick 2 { 107 | request { 108 | id 0 : integer 109 | } 110 | 111 | response { 112 | character 0 : character 113 | errno 1 : integer 114 | } 115 | } 116 | 117 | map_ready 100 { 118 | } 119 | 120 | move 200 { 121 | request { 122 | pos 0 : position 123 | } 124 | response { 125 | pos 0 : position 126 | } 127 | } 128 | 129 | combat 400 { 130 | request { 131 | target 0 : integer 132 | } 133 | response { 134 | target 0 : integer 135 | damage 1 : integer 136 | } 137 | } 138 | 139 | ]] 140 | 141 | local s2c = [[ 142 | aoi_add 0 { 143 | request { 144 | character 0 : character_aoi 145 | } 146 | response { 147 | wantmore 0 : boolean 148 | } 149 | } 150 | 151 | aoi_remove 1 { 152 | request { 153 | character 0 : integer 154 | } 155 | } 156 | 157 | aoi_update_move 2 { 158 | request { 159 | character 0 : character_aoi_move 160 | } 161 | response { 162 | wantmore 0 : boolean 163 | } 164 | } 165 | 166 | aoi_update_attribute 3 { 167 | request { 168 | character 0 : character_aoi_attribute 169 | } 170 | response { 171 | wantmore 0 : boolean 172 | } 173 | } 174 | ]] 175 | 176 | game_proto.types = sparser.parse (types) 177 | game_proto.c2s = sparser.parse (types .. c2s) 178 | game_proto.s2c = sparser.parse (types .. s2c) 179 | 180 | return game_proto 181 | -------------------------------------------------------------------------------- /server/lualib/gameserver/gateserver.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local netpack = require "netpack" 3 | local socketdriver = require "socketdriver" 4 | 5 | local syslog = require "syslog" 6 | 7 | 8 | local gateserver = {} 9 | 10 | local socket 11 | local queue 12 | local maxclient 13 | local nclient = 0 14 | local CMD = setmetatable ({}, { __gc = function () netpack.clear (queue) end }) 15 | 16 | skynet.register_protocol { 17 | name = "client", 18 | id = skynet.PTYPE_CLIENT, 19 | } 20 | 21 | local connection = {} 22 | 23 | function gateserver.open_client (fd) 24 | if connection[fd] then 25 | socketdriver.start (fd) 26 | end 27 | end 28 | 29 | function gateserver.close_client (fd) 30 | local c = connection[fd] 31 | if c then 32 | socketdriver.close (fd) 33 | end 34 | end 35 | 36 | function gateserver.forward (fd, agent) 37 | local c = connection[fd] 38 | if c then 39 | c.agent = agent 40 | syslog.debugf ("start forward fd(%d) to agent(%d)", fd, agent) 41 | end 42 | end 43 | 44 | function gateserver.start (handler) 45 | 46 | function CMD.open (source, conf) 47 | local addr = conf.address or "0.0.0.0" 48 | local port = assert (tonumber (conf.port)) 49 | maxclient = conf.maxclient or 64 50 | 51 | syslog.noticef ("listen on %s:%d", addr, port) 52 | socket = socketdriver.listen (addr, port) 53 | socketdriver.start (socket) 54 | 55 | if handler.open then 56 | return handler.open (source, conf) 57 | end 58 | end 59 | 60 | local MSG = {} 61 | 62 | function MSG.open (fd, addr) 63 | if nclient >= maxclient then 64 | return socketdriver.close (fd) 65 | end 66 | 67 | local c = { 68 | fd = fd, 69 | addr = addr, 70 | } 71 | connection[fd] = c 72 | nclient = nclient + 1 73 | 74 | handler.connect (fd, addr) 75 | end 76 | 77 | local function close_fd (fd) 78 | local c = connection[fd] 79 | if c then 80 | local agent = c.agent 81 | if agent then 82 | syslog.noticef ("fd(%d) disconnected, closing agent(%d)", fd, agent) 83 | skynet.call (agent, "lua", "close") 84 | c.agent = nil 85 | else 86 | if handler.disconnect then 87 | handler.disconnect (fd) 88 | end 89 | end 90 | 91 | connection[fd] = nil 92 | nclient = nclient - 1 93 | end 94 | end 95 | 96 | function MSG.close (fd) 97 | close_fd (fd) 98 | end 99 | 100 | function MSG.error (fd, msg) 101 | close_fd (fd) 102 | end 103 | 104 | local function dispatch_msg (fd, msg, sz) 105 | local c = connection[fd] 106 | local agent = c.agent 107 | if agent then 108 | skynet.redirect (agent, 0, "client", 0, msg, sz) 109 | else 110 | handler.message (fd, msg, sz) 111 | end 112 | end 113 | 114 | MSG.data = dispatch_msg 115 | 116 | local function dispatch_queue () 117 | local fd, msg, sz = netpack.pop (queue) 118 | if fd then 119 | skynet.fork (dispatch_queue) 120 | dispatch_msg (fd, msg, sz) 121 | 122 | for fd, msg, sz in netpack.pop, queue do 123 | dispatch_msg (fd, msg, sz) 124 | end 125 | end 126 | end 127 | 128 | MSG.more = dispatch_queue 129 | 130 | skynet.register_protocol { 131 | name = "socket", 132 | id = skynet.PTYPE_SOCKET, 133 | unpack = function (msg, sz) 134 | return netpack.filter (queue, msg, sz) 135 | end, 136 | dispatch = function (_, _, q, type, ...) 137 | queue = q 138 | if type then 139 | return MSG[type] (...) 140 | end 141 | end, 142 | } 143 | 144 | skynet.start (function () 145 | skynet.dispatch ("lua", function (_, address, cmd, ...) 146 | local f = CMD[cmd] 147 | if f then 148 | skynet.retpack (f(address, ...)) 149 | else 150 | skynet.retpack (handler.command (cmd, ...)) 151 | end 152 | end) 153 | end) 154 | end 155 | 156 | return gateserver 157 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/example4.json: -------------------------------------------------------------------------------- 1 | {"web-app": { 2 | "servlet": [ 3 | { 4 | "servlet-name": "cofaxCDS", 5 | "servlet-class": "org.cofax.cds.CDSServlet", 6 | "init-param": { 7 | "configGlossary:installationAt": "Philadelphia, PA", 8 | "configGlossary:adminEmail": "ksm@pobox.com", 9 | "configGlossary:poweredBy": "Cofax", 10 | "configGlossary:poweredByIcon": "/images/cofax.gif", 11 | "configGlossary:staticPath": "/content/static", 12 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 13 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 14 | "templatePath": "templates", 15 | "templateOverridePath": "", 16 | "defaultListTemplate": "listTemplate.htm", 17 | "defaultFileTemplate": "articleTemplate.htm", 18 | "useJSP": false, 19 | "jspListTemplate": "listTemplate.jsp", 20 | "jspFileTemplate": "articleTemplate.jsp", 21 | "cachePackageTagsTrack": 200, 22 | "cachePackageTagsStore": 200, 23 | "cachePackageTagsRefresh": 60, 24 | "cacheTemplatesTrack": 100, 25 | "cacheTemplatesStore": 50, 26 | "cacheTemplatesRefresh": 15, 27 | "cachePagesTrack": 200, 28 | "cachePagesStore": 100, 29 | "cachePagesRefresh": 10, 30 | "cachePagesDirtyRead": 10, 31 | "searchEngineListTemplate": "forSearchEnginesList.htm", 32 | "searchEngineFileTemplate": "forSearchEngines.htm", 33 | "searchEngineRobotsDb": "WEB-INF/robots.db", 34 | "useDataStore": true, 35 | "dataStoreClass": "org.cofax.SqlDataStore", 36 | "redirectionClass": "org.cofax.SqlRedirection", 37 | "dataStoreName": "cofax", 38 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 39 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 40 | "dataStoreUser": "sa", 41 | "dataStorePassword": "dataStoreTestQuery", 42 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 43 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 44 | "dataStoreInitConns": 10, 45 | "dataStoreMaxConns": 100, 46 | "dataStoreConnUsageLimit": 100, 47 | "dataStoreLogLevel": "debug", 48 | "maxUrlLength": 500}}, 49 | { 50 | "servlet-name": "cofaxEmail", 51 | "servlet-class": "org.cofax.cds.EmailServlet", 52 | "init-param": { 53 | "mailHost": "mail1", 54 | "mailHostOverride": "mail2"}}, 55 | { 56 | "servlet-name": "cofaxAdmin", 57 | "servlet-class": "org.cofax.cds.AdminServlet"}, 58 | 59 | { 60 | "servlet-name": "fileServlet", 61 | "servlet-class": "org.cofax.cds.FileServlet"}, 62 | { 63 | "servlet-name": "cofaxTools", 64 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 65 | "init-param": { 66 | "templatePath": "toolstemplates/", 67 | "log": 1, 68 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 69 | "logMaxSize": "", 70 | "dataLog": 1, 71 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 72 | "dataLogMaxSize": "", 73 | "removePageCache": "/content/admin/remove?cache=pages&id=", 74 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 75 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 76 | "lookInContext": 1, 77 | "adminGroupID": 4, 78 | "betaServer": true}}], 79 | "servlet-mapping": { 80 | "cofaxCDS": "/", 81 | "cofaxEmail": "/cofaxutil/aemail/*", 82 | "cofaxAdmin": "/admin/*", 83 | "fileServlet": "/static/*", 84 | "cofaxTools": "/tools/*"}, 85 | 86 | "taglib": { 87 | "taglib-uri": "cofax.tld", 88 | "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} 89 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/bench.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- This benchmark script measures wall clock time and should be 4 | -- run on an unloaded system. 5 | -- 6 | -- Your Mileage May Vary. 7 | -- 8 | -- Mark Pulford 9 | 10 | local json_module = os.getenv("JSON_MODULE") or "cjson" 11 | 12 | require "socket" 13 | local json = require(json_module) 14 | local util = require "cjson.util" 15 | 16 | local function find_func(mod, funcnames) 17 | for _, v in ipairs(funcnames) do 18 | if mod[v] then 19 | return mod[v] 20 | end 21 | end 22 | 23 | return nil 24 | end 25 | 26 | local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" }) 27 | local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" }) 28 | 29 | local function average(t) 30 | local total = 0 31 | for _, v in ipairs(t) do 32 | total = total + v 33 | end 34 | return total / #t 35 | end 36 | 37 | function benchmark(tests, seconds, rep) 38 | local function bench(func, iter) 39 | -- Use socket.gettime() to measure microsecond resolution 40 | -- wall clock time. 41 | local t = socket.gettime() 42 | for i = 1, iter do 43 | func(i) 44 | end 45 | t = socket.gettime() - t 46 | 47 | -- Don't trust any results when the run lasted for less than a 48 | -- millisecond - return nil. 49 | if t < 0.001 then 50 | return nil 51 | end 52 | 53 | return (iter / t) 54 | end 55 | 56 | -- Roughly calculate the number of interations required 57 | -- to obtain a particular time period. 58 | local function calc_iter(func, seconds) 59 | local iter = 1 60 | local rate 61 | -- Warm up the bench function first. 62 | func() 63 | while not rate do 64 | rate = bench(func, iter) 65 | iter = iter * 10 66 | end 67 | return math.ceil(seconds * rate) 68 | end 69 | 70 | local test_results = {} 71 | for name, func in pairs(tests) do 72 | -- k(number), v(string) 73 | -- k(string), v(function) 74 | -- k(number), v(function) 75 | if type(func) == "string" then 76 | name = func 77 | func = _G[name] 78 | end 79 | 80 | local iter = calc_iter(func, seconds) 81 | 82 | local result = {} 83 | for i = 1, rep do 84 | result[i] = bench(func, iter) 85 | end 86 | 87 | -- Remove the slowest half (round down) of the result set 88 | table.sort(result) 89 | for i = 1, math.floor(#result / 2) do 90 | table.remove(result, 1) 91 | end 92 | 93 | test_results[name] = average(result) 94 | end 95 | 96 | return test_results 97 | end 98 | 99 | function bench_file(filename) 100 | local data_json = util.file_load(filename) 101 | local data_obj = json_decode(data_json) 102 | 103 | local function test_encode() 104 | json_encode(data_obj) 105 | end 106 | local function test_decode() 107 | json_decode(data_json) 108 | end 109 | 110 | local tests = {} 111 | if json_encode then tests.encode = test_encode end 112 | if json_decode then tests.decode = test_decode end 113 | 114 | return benchmark(tests, 0.1, 5) 115 | end 116 | 117 | -- Optionally load any custom configuration required for this module 118 | local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module)) 119 | if success then 120 | util.run_script(data, _G) 121 | configure(json) 122 | end 123 | 124 | for i = 1, #arg do 125 | local results = bench_file(arg[i]) 126 | for k, v in pairs(results) do 127 | print(("%s\t%s\t%d"):format(arg[i], k, v)) 128 | end 129 | end 130 | 131 | -- vi:ai et sw=4 ts=4: 132 | -------------------------------------------------------------------------------- /3rd/lua-cjson/performance.txt: -------------------------------------------------------------------------------- 1 | JSON module performance comparison under Lua 2 | ============================================ 3 | Mark Pulford 4 | :revdate: January 22, 2012 5 | 6 | This performance comparison aims to provide a guide of relative 7 | performance between several fast and popular JSON modules. 8 | 9 | The examples used in this comparison were mostly sourced from the 10 | http://json.org[JSON website] and 11 | http://tools.ietf.org/html/rfc4627[RFC 4627]. 12 | 13 | Performance will vary widely between platforms and data sets. These 14 | results should only be used as an approximation. 15 | 16 | 17 | Modules 18 | ------- 19 | 20 | The following JSON modules for Lua were tested: 21 | 22 | http://chiselapp.com/user/dhkolf/repository/dkjson/[DKJSON 2.1]:: 23 | - Lua implementation with no dependencies on other libraries 24 | - Supports LPeg to improve decode performance 25 | 26 | https://github.com/brimworks/lua-yajl[Lua YAJL 2.0]:: 27 | - C wrapper for the YAJL library 28 | 29 | http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CSJON 2.0.0]:: 30 | - C implementation with no dependencies on other libraries 31 | 32 | 33 | Summary 34 | ------- 35 | 36 | All modules were built and tested as follows: 37 | 38 | DKJSON:: Tested with/without LPeg 10.2. 39 | Lua YAJL:: Tested with YAJL 2.0.4. 40 | Lua CJSON:: Tested with Libc and internal floating point conversion 41 | routines. 42 | 43 | The following Lua implementations were used for this comparison: 44 | 45 | - http://www.lua.org[Lua 5.1.4] (_Lua_) 46 | - http://www.luajit.org[LuaJIT 2.0.0-beta9] (_JIT_) 47 | 48 | These results show the number of JSON operations per second sustained by 49 | each module. All results have been normalised against the pure Lua 50 | DKJSON implementation. 51 | 52 | .Decoding performance 53 | ............................................................................ 54 | | DKJSON | Lua YAJL | Lua CJSON 55 | | No LPeg With LPeg | | Libc Internal 56 | | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT 57 | example1 | 1x 2x 2.6x 3.4x | 7.1x 10x | 14x 20x 14x 20x 58 | example2 | 1x 2.2x 2.9x 4.4x | 6.7x 9.9x | 14x 22x 14x 22x 59 | example3 | 1x 2.1x 3x 4.3x | 6.9x 9.3x | 14x 21x 15x 22x 60 | example4 | 1x 2x 2.5x 3.7x | 7.3x 10x | 12x 19x 12x 20x 61 | example5 | 1x 2.2x 3x 4.5x | 7.8x 11x | 16x 24x 16x 24x 62 | numbers | 1x 2.2x 2.3x 4x | 4.6x 5.5x | 8.9x 10x 13x 17x 63 | rfc-example1 | 1x 2.1x 2.8x 4.3x | 6.1x 8.1x | 13x 19x 14x 21x 64 | rfc-example2 | 1x 2.1x 3.1x 4.2x | 7.1x 9.2x | 15x 21x 17x 24x 65 | types | 1x 2.2x 2.6x 4.3x | 5.3x 7.4x | 12x 20x 13x 21x 66 | -------------|-------------------------|------------|----------------------- 67 | = Average => | 1x 2.1x 2.7x 4.1x | 6.5x 9x | 13x 20x 14x 21x 68 | ............................................................................ 69 | 70 | .Encoding performance 71 | ............................................................................. 72 | | DKJSON | Lua YAJL | Lua CJSON 73 | | No LPeg With LPeg | | Libc Internal 74 | | Lua JIT Lua JIT | Lua JIT | Lua JIT Lua JIT 75 | example1 | 1x 1.8x 0.97x 1.6x | 3.1x 5.2x | 23x 29x 23x 29x 76 | example2 | 1x 2x 0.97x 1.7x | 2.6x 4.3x | 22x 28x 22x 28x 77 | example3 | 1x 1.9x 0.98x 1.6x | 2.8x 4.3x | 13x 15x 16x 18x 78 | example4 | 1x 1.7x 0.96x 1.3x | 3.9x 6.1x | 15x 19x 17x 21x 79 | example5 | 1x 2x 0.98x 1.7x | 2.7x 4.5x | 20x 23x 20x 23x 80 | numbers | 1x 2.3x 1x 2.2x | 1.3x 1.9x | 3.8x 4.1x 4.2x 4.6x 81 | rfc-example1 | 1x 1.9x 0.97x 1.6x | 2.2x 3.2x | 8.5x 9.3x 11x 12x 82 | rfc-example2 | 1x 1.9x 0.98x 1.6x | 2.6x 3.9x | 10x 11x 17x 19x 83 | types | 1x 2.2x 0.97x 2x | 1.2x 1.9x | 11x 13x 12x 14x 84 | -------------|-------------------------|------------|----------------------- 85 | = Average => | 1x 1.9x 0.98x 1.7x | 2.5x 3.9x | 14x 17x 16x 19x 86 | ............................................................................. 87 | 88 | 89 | // vi:ft=asciidoc tw=72: 90 | -------------------------------------------------------------------------------- /3rd/lua-cjson/Makefile: -------------------------------------------------------------------------------- 1 | ##### Available defines for CJSON_CFLAGS ##### 2 | ## 3 | ## USE_INTERNAL_ISINF: Workaround for Solaris platforms missing isinf(). 4 | ## DISABLE_INVALID_NUMBERS: Permanently disable invalid JSON numbers: 5 | ## NaN, Infinity, hex. 6 | ## 7 | ## Optional built-in number conversion uses the following defines: 8 | ## USE_INTERNAL_FPCONV: Use builtin strtod/dtoa for numeric conversions. 9 | ## IEEE_BIG_ENDIAN: Required on big endian architectures. 10 | ## MULTIPLE_THREADS: Must be set when Lua CJSON may be used in a 11 | ## multi-threaded application. Requries _pthreads_. 12 | 13 | ##### Build defaults ##### 14 | LUA_VERSION = 5.1 15 | TARGET = cjson.so 16 | PREFIX = /usr/local 17 | #CFLAGS = -g -Wall -pedantic -fno-inline 18 | CFLAGS = -O3 -Wall -pedantic -DNDEBUG 19 | CJSON_CFLAGS = -fpic 20 | CJSON_LDFLAGS = -shared 21 | LUA_INCLUDE_DIR = $(PREFIX)/include 22 | LUA_CMODULE_DIR = $(PREFIX)/lib/lua/$(LUA_VERSION) 23 | LUA_MODULE_DIR = $(PREFIX)/share/lua/$(LUA_VERSION) 24 | LUA_BIN_DIR = $(PREFIX)/bin 25 | 26 | ##### Platform overrides ##### 27 | ## 28 | ## Tweak one of the platform sections below to suit your situation. 29 | ## 30 | ## See http://lua-users.org/wiki/BuildingModules for further platform 31 | ## specific details. 32 | 33 | ## Linux 34 | 35 | ## FreeBSD 36 | #LUA_INCLUDE_DIR = $(PREFIX)/include/lua51 37 | 38 | ## MacOSX (Macports) 39 | #PREFIX = /opt/local 40 | #CJSON_LDFLAGS = -bundle -undefined dynamic_lookup 41 | 42 | ## Solaris 43 | #CC = gcc 44 | #CJSON_CFLAGS = -fpic -DUSE_INTERNAL_ISINF 45 | 46 | ## Windows (MinGW) 47 | #TARGET = cjson.dll 48 | #PREFIX = /home/user/opt 49 | #CJSON_CFLAGS = -DDISABLE_INVALID_NUMBERS 50 | #CJSON_LDFLAGS = -shared -L$(PREFIX)/lib -llua51 51 | #LUA_BIN_SUFFIX = .lua 52 | 53 | ##### Number conversion configuration ##### 54 | 55 | ## Use Libc support for number conversion (default) 56 | FPCONV_OBJS = fpconv.o 57 | 58 | ## Use built in number conversion 59 | #FPCONV_OBJS = g_fmt.o dtoa.o 60 | #CJSON_CFLAGS += -DUSE_INTERNAL_FPCONV 61 | 62 | ## Compile built in number conversion for big endian architectures 63 | #CJSON_CFLAGS += -DIEEE_BIG_ENDIAN 64 | 65 | ## Compile built in number conversion to support multi-threaded 66 | ## applications (recommended) 67 | #CJSON_CFLAGS += -pthread -DMULTIPLE_THREADS 68 | #CJSON_LDFLAGS += -pthread 69 | 70 | ##### End customisable sections ##### 71 | 72 | TEST_FILES = README bench.lua genutf8.pl test.lua octets-escaped.dat \ 73 | example1.json example2.json example3.json example4.json \ 74 | example5.json numbers.json rfc-example1.json \ 75 | rfc-example2.json types.json 76 | DATAPERM = 644 77 | EXECPERM = 755 78 | 79 | ASCIIDOC = asciidoc 80 | 81 | BUILD_CFLAGS = -I$(LUA_INCLUDE_DIR) $(CJSON_CFLAGS) 82 | OBJS = lua_cjson.o strbuf.o $(FPCONV_OBJS) 83 | 84 | .PHONY: all clean install install-extra doc 85 | 86 | .SUFFIXES: .html .txt 87 | 88 | .c.o: 89 | $(CC) -c $(CFLAGS) $(CPPFLAGS) $(BUILD_CFLAGS) -o $@ $< 90 | 91 | .txt.html: 92 | $(ASCIIDOC) -n -a toc $< 93 | 94 | all: $(TARGET) 95 | 96 | doc: manual.html performance.html 97 | 98 | $(TARGET): $(OBJS) 99 | $(CC) $(LDFLAGS) $(CJSON_LDFLAGS) -o $@ $(OBJS) 100 | 101 | install: $(TARGET) 102 | mkdir -p $(DESTDIR)/$(LUA_CMODULE_DIR) 103 | cp $(TARGET) $(DESTDIR)/$(LUA_CMODULE_DIR) 104 | chmod $(EXECPERM) $(DESTDIR)/$(LUA_CMODULE_DIR)/$(TARGET) 105 | 106 | install-extra: 107 | mkdir -p $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/tests \ 108 | $(DESTDIR)/$(LUA_BIN_DIR) 109 | cp lua/cjson/util.lua $(DESTDIR)/$(LUA_MODULE_DIR)/cjson 110 | chmod $(DATAPERM) $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/util.lua 111 | cp lua/lua2json.lua $(DESTDIR)/$(LUA_BIN_DIR)/lua2json$(LUA_BIN_SUFFIX) 112 | chmod $(EXECPERM) $(DESTDIR)/$(LUA_BIN_DIR)/lua2json$(LUA_BIN_SUFFIX) 113 | cp lua/json2lua.lua $(DESTDIR)/$(LUA_BIN_DIR)/json2lua$(LUA_BIN_SUFFIX) 114 | chmod $(EXECPERM) $(DESTDIR)/$(LUA_BIN_DIR)/json2lua$(LUA_BIN_SUFFIX) 115 | cd tests; cp $(TEST_FILES) $(DESTDIR)/$(LUA_MODULE_DIR)/cjson/tests 116 | cd tests; chmod $(DATAPERM) $(TEST_FILES); chmod $(EXECPERM) *.lua *.pl 117 | 118 | clean: 119 | rm -f *.o $(TARGET) 120 | -------------------------------------------------------------------------------- /server/lualib-src/lua-srp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define KEY_SIZE 32 // 32 bytes == 256 bits 8 | 9 | static BIGNUM * 10 | random_key () { 11 | unsigned char tmp[KEY_SIZE]; 12 | RAND_bytes (tmp, sizeof (tmp)); 13 | return BN_bin2bn (tmp, sizeof (tmp), NULL); 14 | } 15 | 16 | static void 17 | push_bn (lua_State *L, BIGNUM *bn) { 18 | char buf[1024]; 19 | int len = BN_bn2bin (bn, (unsigned char *)buf); 20 | assert (len < sizeof (buf)); 21 | lua_pushlstring (L, buf, len); 22 | } 23 | 24 | static BIGNUM * 25 | lua_tobn (lua_State *L, int index) { 26 | size_t len = 0; 27 | const char *s = lua_tolstring (L, index, &len); 28 | if (s == NULL) 29 | lua_error (L); 30 | return BN_bin2bn ((unsigned char *)s, len, NULL); 31 | } 32 | /* 33 | static void 34 | dump (const char *name, BIGNUM *bn) { 35 | printf ("%s dump\n", name); 36 | unsigned char buf[1024]; 37 | int len = BN_bn2bin (bn, buf); 38 | int i = 1; 39 | for (; i <= len; i++) { 40 | printf ("%02x", buf[i - 1]); 41 | if ((i % 16) == 0) 42 | printf ("\n"); 43 | else if ((i % 8) == 0) 44 | printf (" - "); 45 | else 46 | printf (" "); 47 | } 48 | printf ("\n"); 49 | 50 | } 51 | */ 52 | static int 53 | lcreate_verifier (lua_State *L) { 54 | SRP_gN *GN = SRP_get_default_gN("1024"); 55 | BIGNUM *s = NULL; 56 | BIGNUM *v = NULL; 57 | 58 | const char * I = luaL_checkstring (L, 1); 59 | const char * p = luaL_checkstring (L, 2); 60 | 61 | if (!SRP_create_verifier_BN(I, p, &s, &v, GN->N, GN->g)) { 62 | return 0; 63 | } 64 | 65 | push_bn (L, s); 66 | push_bn (L, v); 67 | 68 | BN_free(s); 69 | BN_clear_free(v); 70 | return 2; 71 | } 72 | 73 | static int 74 | lcreate_client_key (lua_State *L) { 75 | SRP_gN *GN = SRP_get_default_gN("1024"); 76 | BIGNUM *a = NULL; 77 | BIGNUM *A = NULL; 78 | 79 | while (1) { 80 | a = random_key (); 81 | A = SRP_Calc_A (a, GN->N, GN->g); 82 | 83 | if (!SRP_Verify_A_mod_N (A, GN->N)) { 84 | BN_clear_free (a); 85 | BN_free (A); 86 | } 87 | else { 88 | break; 89 | } 90 | } 91 | 92 | push_bn (L, a); 93 | push_bn (L, A); 94 | 95 | BN_clear_free (a); 96 | BN_free (A); 97 | return 2; 98 | } 99 | 100 | static int 101 | lcreate_server_session_key (lua_State *L) { 102 | SRP_gN *GN = SRP_get_default_gN("1024"); 103 | BIGNUM *v = lua_tobn (L, 1); 104 | BIGNUM *A = lua_tobn (L, 2); 105 | 106 | BIGNUM *b = NULL; 107 | BIGNUM *B = NULL; 108 | 109 | while (1) { 110 | b = random_key (); 111 | B = SRP_Calc_B (b, GN->N, GN->g, v); 112 | 113 | if (!SRP_Verify_B_mod_N (B, GN->N)) { 114 | BN_clear_free (b); 115 | BN_free (B); 116 | } 117 | else { 118 | break; 119 | } 120 | } 121 | 122 | BIGNUM *u = SRP_Calc_u (A, B, GN->N); 123 | BIGNUM *K = SRP_Calc_server_key (A, v, u, b, GN->N); 124 | 125 | push_bn (L, K); 126 | push_bn (L, b); 127 | push_bn (L, B); 128 | 129 | BN_clear_free (b); 130 | BN_free (B); 131 | BN_clear_free (v); 132 | BN_free (A); 133 | BN_clear_free (u); 134 | BN_clear_free (K); 135 | return 3; 136 | } 137 | 138 | static int 139 | lcreate_client_session_key (lua_State *L) { 140 | SRP_gN *GN = SRP_get_default_gN("1024"); 141 | const char *I = luaL_checkstring (L, 1); 142 | const char *p = luaL_checkstring (L, 2); 143 | BIGNUM *s = lua_tobn (L, 3); 144 | BIGNUM *a = lua_tobn (L, 4); 145 | BIGNUM *A = lua_tobn (L, 5); 146 | BIGNUM *B = lua_tobn (L, 6); 147 | 148 | BIGNUM *u = SRP_Calc_u (A, B, GN->N); 149 | BIGNUM *x = SRP_Calc_x (s, I, p); 150 | BIGNUM *K = SRP_Calc_client_key (GN->N, B, GN->g, x, a, u); 151 | 152 | push_bn (L, K); 153 | 154 | BN_free (s); 155 | BN_clear_free (a); 156 | BN_free (A); 157 | BN_free (B); 158 | BN_clear_free (u); 159 | BN_clear_free (x); 160 | BN_clear_free (K); 161 | return 1; 162 | } 163 | 164 | static int 165 | lrandom (lua_State *L) { 166 | BIGNUM *r = random_key (); 167 | push_bn (L, r); 168 | BN_free (r); 169 | return 1; 170 | } 171 | 172 | int 173 | luaopen_srp (lua_State *L) { 174 | luaL_checkversion (L); 175 | luaL_Reg l[] = { 176 | { "create_verifier", lcreate_verifier }, 177 | { "create_client_key", lcreate_client_key }, 178 | { "create_server_session_key", lcreate_server_session_key }, 179 | { "create_client_session_key", lcreate_client_session_key }, 180 | { "random", lrandom }, 181 | { NULL, NULL }, 182 | }; 183 | luaL_newlib (L,l); 184 | return 1; 185 | } 186 | -------------------------------------------------------------------------------- /server/lualib/agent/character_handler.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local sharedata = require "sharedata" 3 | 4 | local syslog = require "syslog" 5 | local dbpacker = require "db.packer" 6 | local handler = require "agent.handler" 7 | local uuid = require "uuid" 8 | 9 | 10 | local REQUEST = {} 11 | handler = handler.new (REQUEST) 12 | 13 | local user 14 | local database 15 | local gdd 16 | 17 | handler:init (function (u) 18 | user = u 19 | database = skynet.uniqueservice ("database") 20 | gdd = sharedata.query "gdd" 21 | end) 22 | 23 | local function load_list (account) 24 | local list = skynet.call (database, "lua", "character", "list", account) 25 | if list then 26 | list = dbpacker.unpack (list) 27 | else 28 | list = {} 29 | end 30 | return list 31 | end 32 | 33 | local function check_character (account, id) 34 | print ("check_character", account, id) 35 | local list = load_list (account) 36 | for _, v in pairs (list) do 37 | print ("list", v) 38 | if v == id then return true end 39 | end 40 | return false 41 | end 42 | 43 | function REQUEST.character_list () 44 | local list = load_list (user.account) 45 | local character = {} 46 | for _, id in pairs (list) do 47 | local c = skynet.call (database, "lua", "character", "load", id) 48 | if c then 49 | character[id] = dbpacker.unpack (c) 50 | end 51 | end 52 | return { character = character } 53 | end 54 | 55 | local function create (name, race, class) 56 | assert (name and race and class) 57 | assert (#name > 2 and #name < 24) 58 | assert (gdd.class[class]) 59 | 60 | local r = gdd.race[race] or error () 61 | 62 | local character = { 63 | general = { 64 | name = name, 65 | race = race, 66 | class = class, 67 | map = r.home, 68 | }, 69 | attribute = { 70 | level = 1, 71 | exp = 0, 72 | }, 73 | movement = { 74 | mode = 0, 75 | pos = { x = r.pos_x, y = r.pos_y, z = r.pos_z, o = r.pos_o }, 76 | }, 77 | } 78 | return character 79 | end 80 | 81 | function REQUEST.character_create (args) 82 | for k, v in pairs (args) do print (k, v) end 83 | local c = args.character or error ("invalid argument") 84 | 85 | local character = create (c.name, c.race, c.class) 86 | local id = skynet.call (database, "lua", "character", "reserve", uuid.gen (), c.name) 87 | if not id then return {} end 88 | 89 | character.id = id 90 | local json = dbpacker.pack (character) 91 | skynet.call (database, "lua", "character", "save", id, json) 92 | 93 | local list = load_list (user.account) 94 | table.insert (list, id) 95 | json = dbpacker.pack (list) 96 | skynet.call (database, "lua", "character", "savelist", user.account, json) 97 | 98 | return { character = character } 99 | end 100 | 101 | function REQUEST.character_pick (args) 102 | local id = args.id or error () 103 | assert (check_character (user.account, id)) 104 | 105 | local c = skynet.call (database, "lua", "character", "load", id) or error () 106 | local character = dbpacker.unpack (c) 107 | user.character = character 108 | 109 | local world = skynet.uniqueservice ("world") 110 | skynet.call (world, "lua", "character_enter", id) 111 | 112 | return { character = character } 113 | end 114 | 115 | attribute_string = { 116 | "health", 117 | "strength", 118 | "stamina", 119 | } 120 | 121 | function handler.init (character) 122 | local temp_attribute = { 123 | [1] = {}, 124 | [2] = {}, 125 | } 126 | local attribute_count = #temp_attribute 127 | 128 | character.runtime = { 129 | temp_attribute = temp_attribute, 130 | attribute = temp_attribute[attribute_count], 131 | } 132 | 133 | local class = character.general.class 134 | local race = character.general.race 135 | local level = character.attribute.level 136 | 137 | local gda = gdd.attribute 138 | 139 | local base = temp_attribute[1] 140 | base.health_max = gda.health_max[class][level] 141 | base.strength = gda.strength[race][level] 142 | base.stamina = gda.stamina[race][level] 143 | base.attack_power = 0 144 | 145 | local last = temp_attribute[attribute_count - 1] 146 | local final = temp_attribute[attribute_count] 147 | 148 | if last.stamina >= 20 then 149 | final.health_max = last.health_max + 20 + (last.stamina - 20) * 10 150 | else 151 | final.health_max = last.health_max + last.stamina 152 | end 153 | final.strength = last.strength 154 | final.stamina = last.stamina 155 | final.attack_power = last.attack_power + final.strength 156 | 157 | local attribute = setmetatable (character.attribute, { __index = character.runtime.attribute }) 158 | 159 | local health = attribute.health 160 | if not health or health > attribute.health_max then 161 | attribute.health = attribute.health_max 162 | end 163 | end 164 | 165 | function handler.save (character) 166 | if not character then return end 167 | 168 | local runtime = character.runtime 169 | character.runtime = nil 170 | local data = dbpacker.pack (character) 171 | character.runtime = runtime 172 | skynet.call (database, "lua", "character", "save", character.id, data) 173 | end 174 | 175 | return handler 176 | 177 | -------------------------------------------------------------------------------- /3rd/lua-cjson/strbuf.h: -------------------------------------------------------------------------------- 1 | /* strbuf - String buffer routines 2 | * 3 | * Copyright (c) 2010-2012 Mark Pulford 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 | */ 24 | 25 | #include 26 | #include 27 | 28 | /* Size: Total bytes allocated to *buf 29 | * Length: String length, excluding optional NULL terminator. 30 | * Increment: Allocation increments when resizing the string buffer. 31 | * Dynamic: True if created via strbuf_new() 32 | */ 33 | 34 | typedef struct { 35 | char *buf; 36 | int size; 37 | int length; 38 | int increment; 39 | int dynamic; 40 | int reallocs; 41 | int debug; 42 | } strbuf_t; 43 | 44 | #ifndef STRBUF_DEFAULT_SIZE 45 | #define STRBUF_DEFAULT_SIZE 1023 46 | #endif 47 | #ifndef STRBUF_DEFAULT_INCREMENT 48 | #define STRBUF_DEFAULT_INCREMENT -2 49 | #endif 50 | 51 | /* Initialise */ 52 | extern strbuf_t *strbuf_new(int len); 53 | extern void strbuf_init(strbuf_t *s, int len); 54 | extern void strbuf_set_increment(strbuf_t *s, int increment); 55 | 56 | /* Release */ 57 | extern void strbuf_free(strbuf_t *s); 58 | extern char *strbuf_free_to_string(strbuf_t *s, int *len); 59 | 60 | /* Management */ 61 | extern void strbuf_resize(strbuf_t *s, int len); 62 | static int strbuf_empty_length(strbuf_t *s); 63 | static int strbuf_length(strbuf_t *s); 64 | static char *strbuf_string(strbuf_t *s, int *len); 65 | static void strbuf_ensure_empty_length(strbuf_t *s, int len); 66 | static char *strbuf_empty_ptr(strbuf_t *s); 67 | static void strbuf_extend_length(strbuf_t *s, int len); 68 | 69 | /* Update */ 70 | extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); 71 | extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); 72 | static void strbuf_append_mem(strbuf_t *s, const char *c, int len); 73 | extern void strbuf_append_string(strbuf_t *s, const char *str); 74 | static void strbuf_append_char(strbuf_t *s, const char c); 75 | static void strbuf_ensure_null(strbuf_t *s); 76 | 77 | /* Reset string for before use */ 78 | static inline void strbuf_reset(strbuf_t *s) 79 | { 80 | s->length = 0; 81 | } 82 | 83 | static inline int strbuf_allocated(strbuf_t *s) 84 | { 85 | return s->buf != NULL; 86 | } 87 | 88 | /* Return bytes remaining in the string buffer 89 | * Ensure there is space for a NULL terminator. */ 90 | static inline int strbuf_empty_length(strbuf_t *s) 91 | { 92 | return s->size - s->length - 1; 93 | } 94 | 95 | static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) 96 | { 97 | if (len > strbuf_empty_length(s)) 98 | strbuf_resize(s, s->length + len); 99 | } 100 | 101 | static inline char *strbuf_empty_ptr(strbuf_t *s) 102 | { 103 | return s->buf + s->length; 104 | } 105 | 106 | static inline void strbuf_extend_length(strbuf_t *s, int len) 107 | { 108 | s->length += len; 109 | } 110 | 111 | static inline int strbuf_length(strbuf_t *s) 112 | { 113 | return s->length; 114 | } 115 | 116 | static inline void strbuf_append_char(strbuf_t *s, const char c) 117 | { 118 | strbuf_ensure_empty_length(s, 1); 119 | s->buf[s->length++] = c; 120 | } 121 | 122 | static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) 123 | { 124 | s->buf[s->length++] = c; 125 | } 126 | 127 | static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) 128 | { 129 | strbuf_ensure_empty_length(s, len); 130 | memcpy(s->buf + s->length, c, len); 131 | s->length += len; 132 | } 133 | 134 | static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) 135 | { 136 | memcpy(s->buf + s->length, c, len); 137 | s->length += len; 138 | } 139 | 140 | static inline void strbuf_ensure_null(strbuf_t *s) 141 | { 142 | s->buf[s->length] = 0; 143 | } 144 | 145 | static inline char *strbuf_string(strbuf_t *s, int *len) 146 | { 147 | if (len) 148 | *len = s->length; 149 | 150 | return s->buf; 151 | } 152 | 153 | /* vi:ai et sw=4 ts=4: 154 | */ 155 | -------------------------------------------------------------------------------- /server/service/loginslave.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local socket = require "socket" 3 | 4 | local syslog = require "syslog" 5 | local protoloader = require "protoloader" 6 | local srp = require "srp" 7 | local aes = require "aes" 8 | local uuid = require "uuid" 9 | 10 | local traceback = debug.traceback 11 | 12 | 13 | local master 14 | local database 15 | local host 16 | local auth_timeout 17 | local session_expire_time 18 | local session_expire_time_in_second 19 | local connection = {} 20 | local saved_session = {} 21 | 22 | local slaved = {} 23 | 24 | local CMD = {} 25 | 26 | function CMD.init (m, id, conf) 27 | master = m 28 | database = skynet.uniqueservice ("database") 29 | host = protoloader.load (protoloader.LOGIN) 30 | auth_timeout = conf.auth_timeout * 100 31 | session_expire_time = conf.session_expire_time * 100 32 | session_expire_time_in_second = conf.session_expire_time 33 | end 34 | 35 | local function close (fd) 36 | if connection[fd] then 37 | socket.close (fd) 38 | connection[fd] = nil 39 | end 40 | end 41 | 42 | local function read (fd, size) 43 | return socket.read (fd, size) or error () 44 | end 45 | 46 | local function read_msg (fd) 47 | local s = read (fd, 2) 48 | local size = s:byte(1) * 256 + s:byte(2) 49 | local msg = read (fd, size) 50 | return host:dispatch (msg, size) 51 | end 52 | 53 | local function send_msg (fd, msg) 54 | local package = string.pack (">s2", msg) 55 | socket.write (fd, package) 56 | end 57 | 58 | function CMD.auth (fd, addr) 59 | connection[fd] = addr 60 | skynet.timeout (auth_timeout, function () 61 | if connection[fd] == addr then 62 | syslog.warningf ("connection %d from %s auth timeout!", fd, addr) 63 | close (fd) 64 | end 65 | end) 66 | 67 | socket.start (fd) 68 | socket.limit (fd, 8192) 69 | 70 | local type, name, args, response = read_msg (fd) 71 | assert (type == "REQUEST") 72 | 73 | if name == "handshake" then 74 | assert (args and args.name and args.client_pub, "invalid handshake request") 75 | 76 | local account = skynet.call (database, "lua", "account", "load", args.name) or error ("load account " .. args.name .. " failed") 77 | 78 | local session_key, _, pkey = srp.create_server_session_key (account.verifier, args.client_pub) 79 | local challenge = srp.random () 80 | local msg = response { 81 | user_exists = (account.id ~= nil), 82 | salt = account.salt, 83 | server_pub = pkey, 84 | challenge = challenge, 85 | } 86 | send_msg (fd, msg) 87 | 88 | type, name, args, response = read_msg (fd) 89 | assert (type == "REQUEST" and name == "auth" and args and args.challenge, "invalid auth request") 90 | 91 | local text = aes.decrypt (args.challenge, session_key) 92 | assert (challenge == text, "auth challenge failed") 93 | 94 | local id = tonumber (account.id) 95 | if not id then 96 | assert (args.password) 97 | id = uuid.gen () 98 | local password = aes.decrypt (args.password, session_key) 99 | account.id = skynet.call (database, "lua", "account", "create", id, account.name, password) or error (string.format ("create account %s/%d failed", args.name, id)) 100 | end 101 | 102 | challenge = srp.random () 103 | local session = skynet.call (master, "lua", "save_session", id, session_key, challenge) 104 | 105 | msg = response { 106 | session = session, 107 | expire = session_expire_time_in_second, 108 | challenge = challenge, 109 | } 110 | send_msg (fd, msg) 111 | 112 | type, name, args, response = read_msg (fd) 113 | assert (type == "REQUEST") 114 | end 115 | 116 | assert (name == "challenge") 117 | assert (args and args.session and args.challenge) 118 | 119 | local token, challenge = skynet.call (master, "lua", "challenge", args.session, args.challenge) 120 | assert (token and challenge) 121 | 122 | local msg = response { 123 | token = token, 124 | challenge = challenge, 125 | } 126 | send_msg (fd, msg) 127 | 128 | close (fd) 129 | end 130 | 131 | function CMD.save_session (session, account, key, challenge) 132 | saved_session[session] = { account = account, key = key, challenge = challenge } 133 | skynet.timeout (session_expire_time, function () 134 | local t = saved_session[session] 135 | if t and t.key == key then 136 | saved_session[session] = nil 137 | end 138 | end) 139 | end 140 | 141 | function CMD.challenge (session, secret) 142 | local t = saved_session[session] or error () 143 | 144 | local text = aes.decrypt (secret, t.key) or error () 145 | assert (text == t.challenge) 146 | 147 | t.token = srp.random () 148 | t.challenge = srp.random () 149 | 150 | return t.token, t.challenge 151 | end 152 | 153 | function CMD.verify (session, secret) 154 | local t = saved_session[session] or error () 155 | 156 | local text = aes.decrypt (secret, t.key) or error () 157 | assert (text == t.token) 158 | t.token = nil 159 | 160 | return t.account 161 | end 162 | 163 | skynet.start (function () 164 | skynet.dispatch ("lua", function (_, _, command, ...) 165 | local function pret (ok, ...) 166 | if not ok then 167 | syslog.warningf (...) 168 | skynet.ret () 169 | else 170 | skynet.retpack (...) 171 | end 172 | end 173 | 174 | local f = assert (CMD[command]) 175 | pret (xpcall (f, traceback, ...)) 176 | end) 177 | end) 178 | 179 | -------------------------------------------------------------------------------- /server/service/agent.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local queue = require "skynet.queue" 3 | local sharemap = require "sharemap" 4 | local socket = require "socket" 5 | 6 | local syslog = require "syslog" 7 | local protoloader = require "protoloader" 8 | local character_handler = require "agent.character_handler" 9 | local map_handler = require "agent.map_handler" 10 | local aoi_handler = require "agent.aoi_handler" 11 | local move_handler = require "agent.move_handler" 12 | local combat_handler = require "agent.combat_handler" 13 | 14 | 15 | local gamed = tonumber (...) 16 | local database 17 | 18 | local host, proto_request = protoloader.load (protoloader.GAME) 19 | 20 | --[[ 21 | .user { 22 | fd : integer 23 | account : integer 24 | 25 | character : character 26 | world : integer 27 | map : integer 28 | } 29 | ]] 30 | 31 | local user 32 | 33 | local function send_msg (fd, msg) 34 | local package = string.pack (">s2", msg) 35 | socket.write (fd, package) 36 | end 37 | 38 | local user_fd 39 | local session = {} 40 | local session_id = 0 41 | local function send_request (name, args) 42 | session_id = session_id + 1 43 | local str = proto_request (name, args, session_id) 44 | send_msg (user_fd, str) 45 | session[session_id] = { name = name, args = args } 46 | end 47 | 48 | local function kick_self () 49 | skynet.call (gamed, "lua", "kick", skynet.self (), user_fd) 50 | end 51 | 52 | local last_heartbeat_time 53 | local HEARTBEAT_TIME_MAX = 0 -- 60 * 100 54 | local function heartbeat_check () 55 | if HEARTBEAT_TIME_MAX <= 0 or not user_fd then return end 56 | 57 | local t = last_heartbeat_time + HEARTBEAT_TIME_MAX - skynet.now () 58 | if t <= 0 then 59 | syslog.warning ("heatbeat check failed") 60 | kick_self () 61 | else 62 | skynet.timeout (t, heartbeat_check) 63 | end 64 | end 65 | 66 | local traceback = debug.traceback 67 | local REQUEST 68 | local function handle_request (name, args, response) 69 | local f = REQUEST[name] 70 | if f then 71 | local ok, ret = xpcall (f, traceback, args) 72 | if not ok then 73 | syslog.warningf ("handle message(%s) failed : %s", name, ret) 74 | kick_self () 75 | else 76 | last_heartbeat_time = skynet.now () 77 | if response and ret then 78 | send_msg (user_fd, response (ret)) 79 | end 80 | end 81 | else 82 | syslog.warningf ("unhandled message : %s", name) 83 | kick_self () 84 | end 85 | end 86 | 87 | local RESPONSE 88 | local function handle_response (id, args) 89 | local s = session[id] 90 | if not s then 91 | syslog.warningf ("session %d not found", id) 92 | kick_self () 93 | return 94 | end 95 | 96 | local f = RESPONSE[s.name] 97 | if not f then 98 | syslog.warningf ("unhandled response : %s", s.name) 99 | kick_self () 100 | return 101 | end 102 | 103 | local ok, ret = xpcall (f, traceback, s.args, args) 104 | if not ok then 105 | syslog.warningf ("handle response(%d-%s) failed : %s", id, s.name, ret) 106 | kick_self () 107 | end 108 | end 109 | 110 | skynet.register_protocol { 111 | name = "client", 112 | id = skynet.PTYPE_CLIENT, 113 | unpack = function (msg, sz) 114 | return host:dispatch (msg, sz) 115 | end, 116 | dispatch = function (_, _, type, ...) 117 | if type == "REQUEST" then 118 | handle_request (...) 119 | elseif type == "RESPONSE" then 120 | handle_response (...) 121 | else 122 | syslog.warningf ("invalid message type : %s", type) 123 | kick_self () 124 | end 125 | end, 126 | } 127 | 128 | local CMD = {} 129 | 130 | function CMD.open (fd, account) 131 | local name = string.format ("agent:%d", account) 132 | syslog.debug ("agent opened") 133 | 134 | user = { 135 | fd = fd, 136 | account = account, 137 | REQUEST = {}, 138 | RESPONSE = {}, 139 | CMD = CMD, 140 | send_request = send_request, 141 | } 142 | user_fd = user.fd 143 | REQUEST = user.REQUEST 144 | RESPONSE = user.RESPONSE 145 | 146 | character_handler:register (user) 147 | 148 | last_heartbeat_time = skynet.now () 149 | heartbeat_check () 150 | end 151 | 152 | function CMD.close () 153 | syslog.debug ("agent closed") 154 | 155 | local account 156 | if user then 157 | account = user.account 158 | 159 | if user.map then 160 | skynet.call (user.map, "lua", "character_leave") 161 | user.map = nil 162 | map_handler:unregister (user) 163 | aoi_handler:unregister (user) 164 | move_handler:unregister (user) 165 | combat_handler:unregister (user) 166 | end 167 | 168 | if user.world then 169 | skynet.call (user.world, "lua", "character_leave", user.character.id) 170 | user.world = nil 171 | end 172 | 173 | character_handler.save (user.character) 174 | 175 | user = nil 176 | user_fd = nil 177 | REQUEST = nil 178 | end 179 | 180 | skynet.call (gamed, "lua", "close", skynet.self (), account) 181 | end 182 | 183 | function CMD.kick () 184 | error () 185 | syslog.debug ("agent kicked") 186 | skynet.call (gamed, "lua", "kick", skynet.self (), user_fd) 187 | end 188 | 189 | function CMD.world_enter (world) 190 | local name = string.format ("agent:%d:%s", user.character.id, user.character.general.name) 191 | 192 | character_handler.init (user.character) 193 | 194 | user.world = world 195 | character_handler:unregister (user) 196 | 197 | return user.character.general.map, user.character.movement.pos 198 | end 199 | 200 | function CMD.map_enter (map) 201 | user.map = map 202 | 203 | map_handler:register (user) 204 | aoi_handler:register (user) 205 | move_handler:register (user) 206 | combat_handler:register (user) 207 | end 208 | 209 | skynet.start (function () 210 | skynet.dispatch ("lua", function (_, _, command, ...) 211 | local f = CMD[command] 212 | if not f then 213 | syslog.warningf ("unhandled message(%s)", command) 214 | return skynet.ret () 215 | end 216 | 217 | local ok, ret = xpcall (f, traceback, ...) 218 | if not ok then 219 | syslog.warningf ("handle message(%s) failed : %s", command, ret) 220 | kick_self () 221 | return skynet.ret () 222 | end 223 | skynet.retpack (ret) 224 | end) 225 | end) 226 | 227 | -------------------------------------------------------------------------------- /server/lualib/agent/aoi_handler.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local sharemap = require "sharemap" 3 | 4 | local syslog = require "syslog" 5 | local handler = require "agent.handler" 6 | 7 | 8 | local RESPONSE = {} 9 | local CMD = {} 10 | handler = handler.new (nil, RESPONSE, CMD) 11 | 12 | local subscribe_character 13 | local subscribe_agent 14 | local user 15 | local character_writer 16 | local self_id 17 | local self_flag 18 | local scope2proto = { 19 | ["move"] = "aoi_update_move", 20 | ["attribute"] = "aoi_update_attribute", 21 | } 22 | 23 | handler:init (function (u) 24 | user = u 25 | character_writer = nil 26 | subscribe_character = {} 27 | subscribe_agent = {} 28 | 29 | self_id = user.character.id 30 | self_flag = {} 31 | for k, _ in pairs (scope2proto) do 32 | self_flag[k] = { dirty = false, wantmore = true } 33 | end 34 | end) 35 | 36 | local function send_self (scope) 37 | local flag = self_flag[scope] 38 | if flag.dirty and flag.wantmore then 39 | flag.dirty = false 40 | flag.wantmore = false 41 | user.send_request (scope2proto[scope], { character = user.character }) 42 | end 43 | end 44 | 45 | local function agent2id (agent) 46 | local t = subscribe_agent[agent] 47 | if not t then return end 48 | return t.character.id 49 | end 50 | 51 | local function mark_flag (character, scope, field, value) 52 | local t = subscribe_character[character] 53 | if not t then return end 54 | 55 | t = t.flag[scope] 56 | if not t then return end 57 | 58 | if value == nil then value = true end 59 | t[field] = value 60 | end 61 | 62 | local function create_reader () 63 | syslog.debug ("aoi_handler create_reader") 64 | if not character_writer then 65 | character_writer = sharemap.writer ("character", user.character) 66 | end 67 | return character_writer:copy () 68 | end 69 | 70 | local function subscribe (agent, reader) 71 | syslog.debugf ("aoi_handler aoi_subscribe agent(%d) reader(%s)", agent, reader) 72 | local c = sharemap.reader ("character", reader) 73 | 74 | local flag = {} 75 | for k, _ in pairs (scope2proto) do 76 | flag[k] = { dirty = false, wantmore = false } 77 | end 78 | 79 | local t = { 80 | character = c, 81 | agent = agent, 82 | flag = flag, 83 | } 84 | subscribe_character[c.id] = t 85 | subscribe_agent[agent] = t 86 | 87 | user.send_request ("aoi_add", { character = c }) 88 | end 89 | 90 | local function refresh_aoi (id, scope) 91 | syslog.debugf ("refresh_aoi character(%d) scope(%s)", id, scope) 92 | 93 | local t = subscribe_character[id] 94 | if not t then return end 95 | local c = t.character 96 | 97 | t = t.flag[scope] 98 | if not t then return end 99 | 100 | syslog.debugf ("dirty(%s) wantmore(%s)", t.dirty, t.wantmore) 101 | 102 | if t.dirty and t.wantmore then 103 | c:update () 104 | 105 | user.send_request (scope2proto[scope], { character = c }) 106 | t.wantmore = false 107 | t.dirty = false 108 | end 109 | end 110 | 111 | local function aoi_update_response (id, scope) 112 | if id == self_id then 113 | self_flag[scope].wantmore = true 114 | send_self (scope) 115 | return 116 | end 117 | 118 | mark_flag (id, scope, "wantmore", true) 119 | refresh_aoi (id, scope) 120 | end 121 | 122 | local function aoi_add (list) 123 | if not list then return end 124 | 125 | local self = skynet.self () 126 | for _, target in pairs (list) do 127 | skynet.fork (function () 128 | local reader = skynet.call (target, "lua", "aoi_subscribe", self, create_reader ()) 129 | subscribe (target, reader) 130 | end) 131 | end 132 | end 133 | 134 | local function aoi_update (list, scope) 135 | if not list then return end 136 | 137 | self_flag[scope].dirty = true 138 | send_self (scope) 139 | 140 | local self = skynet.self () 141 | for _, target in pairs (list) do 142 | skynet.fork (function () 143 | skynet.call (target, "lua", "aoi_send", self, scope) 144 | end) 145 | end 146 | end 147 | 148 | local function aoi_remove (list) 149 | if not list then return end 150 | 151 | local self = skynet.self () 152 | for _, agent in pairs (list) do 153 | skynet.fork (function () 154 | local t = subscribe_agent[agent] 155 | if t then 156 | local id = t.character.id 157 | subscribe_agent[agent] = nil 158 | subscribe_character[id] = nil 159 | user.send_request ("aoi_remove", { character = id }) 160 | skynet.call (agent, "lua", "aoi_unsubscribe", self) 161 | end 162 | end) 163 | end 164 | end 165 | 166 | function CMD.aoi_subscribe (agent, reader) 167 | syslog.debugf ("aoi_subscribe agent(%d) reader(%s)", agent, reader) 168 | subscribe (agent, reader) 169 | return create_reader () 170 | end 171 | 172 | function CMD.aoi_unsubscribe (agent) 173 | syslog.debugf ("aoi_unsubscribe agent(%d)", agent) 174 | local t = subscribe_agent[agent] 175 | if t then 176 | local id = t.character.id 177 | subscribe_agent[agent] = nil 178 | subscribe_character[id] = nil 179 | user.send_request ("aoi_remove", { character = id }) 180 | end 181 | end 182 | 183 | function CMD.aoi_manage (alist, rlist, ulist, scope) 184 | if (alist or ulist) and character_writer then 185 | character_writer:commit () 186 | end 187 | 188 | aoi_add (alist) 189 | aoi_remove (rlist) 190 | aoi_update (ulist, scope) 191 | end 192 | 193 | function CMD.aoi_send (agent, scope) 194 | local t = subscribe_agent[agent] 195 | if not t then return end 196 | local id = t.character.id 197 | 198 | mark_flag (id, scope, "dirty", true) 199 | refresh_aoi (id, scope) 200 | end 201 | 202 | function RESPONSE.aoi_add (request, response) 203 | if not response or not response.wantmore then return end 204 | 205 | local id = request.character.id 206 | for k, _ in pairs (scope2proto) do 207 | mark_flag (id, k, "wantmore", true) 208 | refresh_aoi (id, k) 209 | end 210 | end 211 | 212 | function RESPONSE.aoi_update_move (request, response) 213 | if not response or not response.wantmore then return end 214 | aoi_update_response (request.character.id, "move") 215 | end 216 | 217 | function RESPONSE.aoi_update_attribute (request, response) 218 | if not response or not response.wantmore then return end 219 | aoi_update_response (request.character.id, "attribute") 220 | end 221 | 222 | function handler.find (id) 223 | local t = subscribe_character[id] 224 | if t then 225 | return t.agent 226 | end 227 | end 228 | 229 | function handler.boardcast (scope) 230 | if not character_writer then return end 231 | character_writer:commit () 232 | 233 | self_flag[scope].dirty = true 234 | send_self (scope) 235 | 236 | local self = skynet.self () 237 | for a, _ in pairs (subscribe_agent) do 238 | skynet.fork (function () 239 | skynet.call (a, "lua", "aoi_send", self, scope) 240 | end) 241 | end 242 | end 243 | 244 | return handler 245 | -------------------------------------------------------------------------------- /3rd/lua-cjson/fpconv.c: -------------------------------------------------------------------------------- 1 | /* fpconv - Floating point conversion routines 2 | * 3 | * Copyright (c) 2011-2012 Mark Pulford 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 | */ 24 | 25 | /* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries 26 | * with locale support will break when the decimal separator is a comma. 27 | * 28 | * fpconv_* will around these issues with a translation buffer if required. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "fpconv.h" 37 | 38 | /* Lua CJSON assumes the locale is the same for all threads within a 39 | * process and doesn't change after initialisation. 40 | * 41 | * This avoids the need for per thread storage or expensive checks 42 | * for call. */ 43 | static char locale_decimal_point = '.'; 44 | 45 | /* In theory multibyte decimal_points are possible, but 46 | * Lua CJSON only supports UTF-8 and known locales only have 47 | * single byte decimal points ([.,]). 48 | * 49 | * localconv() may not be thread safe (=>crash), and nl_langinfo() is 50 | * not supported on some platforms. Use sprintf() instead - if the 51 | * locale does change, at least Lua CJSON won't crash. */ 52 | static void fpconv_update_locale() 53 | { 54 | char buf[8]; 55 | 56 | snprintf(buf, sizeof(buf), "%g", 0.5); 57 | 58 | /* Failing this test might imply the platform has a buggy dtoa 59 | * implementation or wide characters */ 60 | if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { 61 | fprintf(stderr, "Error: wide characters found or printf() bug."); 62 | abort(); 63 | } 64 | 65 | locale_decimal_point = buf[1]; 66 | } 67 | 68 | /* Check for a valid number character: [-+0-9a-yA-Y.] 69 | * Eg: -0.6e+5, infinity, 0xF0.F0pF0 70 | * 71 | * Used to find the probable end of a number. It doesn't matter if 72 | * invalid characters are counted - strtod() will find the valid 73 | * number if it exists. The risk is that slightly more memory might 74 | * be allocated before a parse error occurs. */ 75 | static inline int valid_number_character(char ch) 76 | { 77 | char lower_ch; 78 | 79 | if ('0' <= ch && ch <= '9') 80 | return 1; 81 | if (ch == '-' || ch == '+' || ch == '.') 82 | return 1; 83 | 84 | /* Hex digits, exponent (e), base (p), "infinity",.. */ 85 | lower_ch = ch | 0x20; 86 | if ('a' <= lower_ch && lower_ch <= 'y') 87 | return 1; 88 | 89 | return 0; 90 | } 91 | 92 | /* Calculate the size of the buffer required for a strtod locale 93 | * conversion. */ 94 | static int strtod_buffer_size(const char *s) 95 | { 96 | const char *p = s; 97 | 98 | while (valid_number_character(*p)) 99 | p++; 100 | 101 | return p - s; 102 | } 103 | 104 | /* Similar to strtod(), but must be passed the current locale's decimal point 105 | * character. Guaranteed to be called at the start of any valid number in a string */ 106 | double fpconv_strtod(const char *nptr, char **endptr) 107 | { 108 | char localbuf[FPCONV_G_FMT_BUFSIZE]; 109 | char *buf, *endbuf, *dp; 110 | int buflen; 111 | double value; 112 | 113 | /* System strtod() is fine when decimal point is '.' */ 114 | if (locale_decimal_point == '.') 115 | return strtod(nptr, endptr); 116 | 117 | buflen = strtod_buffer_size(nptr); 118 | if (!buflen) { 119 | /* No valid characters found, standard strtod() return */ 120 | *endptr = (char *)nptr; 121 | return 0; 122 | } 123 | 124 | /* Duplicate number into buffer */ 125 | if (buflen >= FPCONV_G_FMT_BUFSIZE) { 126 | /* Handle unusually large numbers */ 127 | buf = malloc(buflen + 1); 128 | if (!buf) { 129 | fprintf(stderr, "Out of memory"); 130 | abort(); 131 | } 132 | } else { 133 | /* This is the common case.. */ 134 | buf = localbuf; 135 | } 136 | memcpy(buf, nptr, buflen); 137 | buf[buflen] = 0; 138 | 139 | /* Update decimal point character if found */ 140 | dp = strchr(buf, '.'); 141 | if (dp) 142 | *dp = locale_decimal_point; 143 | 144 | value = strtod(buf, &endbuf); 145 | *endptr = (char *)&nptr[endbuf - buf]; 146 | if (buflen >= FPCONV_G_FMT_BUFSIZE) 147 | free(buf); 148 | 149 | return value; 150 | } 151 | 152 | /* "fmt" must point to a buffer of at least 6 characters */ 153 | static void set_number_format(char *fmt, int precision) 154 | { 155 | int d1, d2, i; 156 | 157 | assert(1 <= precision && precision <= 14); 158 | 159 | /* Create printf format (%.14g) from precision */ 160 | d1 = precision / 10; 161 | d2 = precision % 10; 162 | fmt[0] = '%'; 163 | fmt[1] = '.'; 164 | i = 2; 165 | if (d1) { 166 | fmt[i++] = '0' + d1; 167 | } 168 | fmt[i++] = '0' + d2; 169 | fmt[i++] = 'g'; 170 | fmt[i] = 0; 171 | } 172 | 173 | /* Assumes there is always at least 32 characters available in the target buffer */ 174 | int fpconv_g_fmt(char *str, double num, int precision) 175 | { 176 | char buf[FPCONV_G_FMT_BUFSIZE]; 177 | char fmt[6]; 178 | int len; 179 | char *b; 180 | 181 | set_number_format(fmt, precision); 182 | 183 | /* Pass through when decimal point character is dot. */ 184 | if (locale_decimal_point == '.') 185 | return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num); 186 | 187 | /* snprintf() to a buffer then translate for other decimal point characters */ 188 | len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); 189 | 190 | /* Copy into target location. Translate decimal point if required */ 191 | b = buf; 192 | do { 193 | *str++ = (*b == locale_decimal_point ? '.' : *b); 194 | } while(*b++); 195 | 196 | return len; 197 | } 198 | 199 | void fpconv_init() 200 | { 201 | fpconv_update_locale(); 202 | } 203 | 204 | /* vi:ai et sw=4 ts=4: 205 | */ 206 | -------------------------------------------------------------------------------- /client/client.lua: -------------------------------------------------------------------------------- 1 | package.cpath = package.cpath .. ";../3rd/skynet/luaclib/?.so;../server/luaclib/?.so" 2 | package.path = package.path .. ";../3rd/skynet/lualib/?.lua;../common/?.lua" 3 | 4 | local print_r = require "print_r" 5 | local socket = require "clientsocket" 6 | local sproto = require "sproto" 7 | local srp = require "srp" 8 | local aes = require "aes" 9 | local login_proto = require "proto.login_proto" 10 | local game_proto = require "proto.game_proto" 11 | local constant = require "constant" 12 | 13 | local username = arg[1] 14 | local password = arg[2] 15 | 16 | local user = { name = arg[1], password = arg[2] } 17 | 18 | if not user.name then 19 | local f = io.open ("anonymous", "r") 20 | if not f then 21 | f = io.open ("anonymous", "w") 22 | local name = "" 23 | math.randomseed (os.time ()) 24 | for i = 1, 16 do 25 | name = name .. string.char (math.random (127)) 26 | end 27 | 28 | user.name = name 29 | f:write (name) 30 | f:flush () 31 | f:close () 32 | else 33 | user.name = f:read ("a") 34 | f:close () 35 | end 36 | end 37 | 38 | if not user.password then 39 | user.password = constant.default_password 40 | end 41 | 42 | local server = "127.0.0.1" 43 | local login_port = 9777 44 | local game_port = 9555 45 | local gameserver = { 46 | addr = "127.0.0.1", 47 | port = 9555, 48 | name = "gameserver", 49 | } 50 | 51 | local host = sproto.new (login_proto.s2c):host "package" 52 | local request = host:attach (sproto.new (login_proto.c2s)) 53 | local fd 54 | local game_fd 55 | 56 | local function send_message (fd, msg) 57 | local package = string.pack (">s2", msg) 58 | socket.send (fd, package) 59 | end 60 | 61 | local session = {} 62 | local session_id = 0 63 | local function send_request (name, args) 64 | print ("send_request", name) 65 | session_id = session_id + 1 66 | local str = request (name, args, session_id) 67 | send_message (fd, str) 68 | session[session_id] = { name = name, args = args } 69 | end 70 | 71 | local function unpack (text) 72 | local size = #text 73 | if size < 2 then 74 | return nil, text 75 | end 76 | local s = text:byte (1) * 256 + text:byte (2) 77 | if size < s + 2 then 78 | return nil, text 79 | end 80 | 81 | return text:sub (3, 2 + s), text:sub (3 + s) 82 | end 83 | 84 | local function recv (last) 85 | local result 86 | result, last = unpack (last) 87 | if result then 88 | return result, last 89 | end 90 | local r = socket.recv (fd) 91 | if not r then 92 | return nil, last 93 | end 94 | if r == "" then 95 | error (string.format ("socket %d closed", fd)) 96 | end 97 | 98 | return unpack (last .. r) 99 | end 100 | 101 | local rr = { wantmore = true } 102 | local function handle_request (name, args, response) 103 | print ("request", name) 104 | if args then 105 | print_r (args) 106 | else 107 | print "empty argument" 108 | end 109 | 110 | if name:sub (1, 3) == "aoi" and name ~= "aoi_remove" then 111 | if response then 112 | send_message (fd, response (rr)) 113 | end 114 | end 115 | end 116 | 117 | local RESPONSE = {} 118 | 119 | function RESPONSE:handshake (args) 120 | print ("RESPONSE.handshake") 121 | local name = self.name 122 | assert (name == user.name) 123 | 124 | if args.user_exists then 125 | local key = srp.create_client_session_key (name, user.password, args.salt, user.private_key, user.public_key, args.server_pub) 126 | user.session_key = key 127 | local ret = { challenge = aes.encrypt (args.challenge, key) } 128 | send_request ("auth", ret) 129 | else 130 | print (name, constant.default_password) 131 | local key = srp.create_client_session_key (name, constant.default_password, args.salt, user.private_key, user.public_key, args.server_pub) 132 | user.session_key = key 133 | local ret = { challenge = aes.encrypt (args.challenge, key), password = aes.encrypt (user.password, key) } 134 | send_request ("auth", ret) 135 | end 136 | end 137 | 138 | function RESPONSE:auth (args) 139 | print ("RESPONSE.auth") 140 | 141 | user.session = args.session 142 | local challenge = aes.encrypt (args.challenge, user.session_key) 143 | send_request ("challenge", { session = args.session, challenge = challenge }) 144 | end 145 | 146 | function RESPONSE:challenge (args) 147 | print ("RESPONSE.challenge") 148 | 149 | local token = aes.encrypt (args.token, user.session_key) 150 | 151 | fd = assert (socket.connect (gameserver.addr, gameserver.port)) 152 | print (string.format ("game server connected, fd = %d", fd)) 153 | send_request ("login", { session = user.session, token = token }) 154 | 155 | host = sproto.new (game_proto.s2c):host "package" 156 | request = host:attach (sproto.new (game_proto.c2s)) 157 | 158 | send_request ("character_list") 159 | end 160 | 161 | local function handle_response (id, args) 162 | local s = assert (session[id]) 163 | session[id] = nil 164 | local f = RESPONSE[s.name] 165 | print ("response type : " .. type (args)) 166 | if f then 167 | f (s.args, args) 168 | else 169 | print "response" 170 | print_r (args) 171 | end 172 | end 173 | 174 | local function handle_message (t, ...) 175 | if t == "REQUEST" then 176 | handle_request (...) 177 | else 178 | handle_response (...) 179 | end 180 | end 181 | 182 | local last = "" 183 | local function dispatch_message () 184 | while true do 185 | local v 186 | v, last = recv (last) 187 | if not v then 188 | break 189 | end 190 | 191 | handle_message (host:dispatch (v)) 192 | end 193 | end 194 | 195 | local private_key, public_key = srp.create_client_key () 196 | user.private_key = private_key 197 | user.public_key = public_key 198 | fd = assert (socket.connect (server, login_port)) 199 | print (string.format ("login server connected, fd = %d", fd)) 200 | send_request ("handshake", { name = user.name, client_pub = public_key }) 201 | 202 | local HELP = {} 203 | 204 | local function handle_cmd (line) 205 | local cmd 206 | local p = string.gsub (line, "([%w-_]+)", function (s) 207 | cmd = s 208 | return "" 209 | end, 1) 210 | print (cmd, "====", p) 211 | 212 | if string.lower (cmd) == "help" then 213 | for k, v in pairs (HELP) do 214 | print (string.format ("command:\n\t%s\nparameter:\n%s", k, v())) 215 | end 216 | return 217 | end 218 | 219 | local t = {} 220 | local f, err = load (p, "=(load)" , "t", t) 221 | 222 | if not f then error (err) end 223 | f () 224 | 225 | print ("cmd", cmd) 226 | if t then 227 | print_r (t) 228 | else 229 | print ("null argument") 230 | end 231 | 232 | if not next (t) then t = nil end 233 | 234 | if cmd then 235 | local ok, err = pcall (send_request, cmd, t) 236 | if not ok then 237 | print (string.format ("invalid command (%s), error (%s)", cmd, err)) 238 | end 239 | end 240 | end 241 | 242 | function HELP.character_create () 243 | return [[ 244 | name: your nickname in game 245 | race: 1(human)/2(orc) 246 | class: 1(warrior)/2(mage) 247 | ]] 248 | end 249 | 250 | print ('type "help" to see all available command.') 251 | while true do 252 | dispatch_message () 253 | local cmd = socket.readstdin () 254 | if cmd then 255 | handle_cmd (cmd) 256 | else 257 | socket.usleep (100) 258 | end 259 | end 260 | 261 | -------------------------------------------------------------------------------- /3rd/lua-cjson/strbuf.c: -------------------------------------------------------------------------------- 1 | /* strbuf - String buffer routines 2 | * 3 | * Copyright (c) 2010-2012 Mark Pulford 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 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "strbuf.h" 31 | 32 | static void die(const char *fmt, ...) 33 | { 34 | va_list arg; 35 | 36 | va_start(arg, fmt); 37 | vfprintf(stderr, fmt, arg); 38 | va_end(arg); 39 | fprintf(stderr, "\n"); 40 | 41 | exit(-1); 42 | } 43 | 44 | void strbuf_init(strbuf_t *s, int len) 45 | { 46 | int size; 47 | 48 | if (len <= 0) 49 | size = STRBUF_DEFAULT_SIZE; 50 | else 51 | size = len + 1; /* \0 terminator */ 52 | 53 | s->buf = NULL; 54 | s->size = size; 55 | s->length = 0; 56 | s->increment = STRBUF_DEFAULT_INCREMENT; 57 | s->dynamic = 0; 58 | s->reallocs = 0; 59 | s->debug = 0; 60 | 61 | s->buf = malloc(size); 62 | if (!s->buf) 63 | die("Out of memory"); 64 | 65 | strbuf_ensure_null(s); 66 | } 67 | 68 | strbuf_t *strbuf_new(int len) 69 | { 70 | strbuf_t *s; 71 | 72 | s = malloc(sizeof(strbuf_t)); 73 | if (!s) 74 | die("Out of memory"); 75 | 76 | strbuf_init(s, len); 77 | 78 | /* Dynamic strbuf allocation / deallocation */ 79 | s->dynamic = 1; 80 | 81 | return s; 82 | } 83 | 84 | void strbuf_set_increment(strbuf_t *s, int increment) 85 | { 86 | /* Increment > 0: Linear buffer growth rate 87 | * Increment < -1: Exponential buffer growth rate */ 88 | if (increment == 0 || increment == -1) 89 | die("BUG: Invalid string increment"); 90 | 91 | s->increment = increment; 92 | } 93 | 94 | static inline void debug_stats(strbuf_t *s) 95 | { 96 | if (s->debug) { 97 | fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", 98 | (long)s, s->reallocs, s->length, s->size); 99 | } 100 | } 101 | 102 | /* If strbuf_t has not been dynamically allocated, strbuf_free() can 103 | * be called any number of times strbuf_init() */ 104 | void strbuf_free(strbuf_t *s) 105 | { 106 | debug_stats(s); 107 | 108 | if (s->buf) { 109 | free(s->buf); 110 | s->buf = NULL; 111 | } 112 | if (s->dynamic) 113 | free(s); 114 | } 115 | 116 | char *strbuf_free_to_string(strbuf_t *s, int *len) 117 | { 118 | char *buf; 119 | 120 | debug_stats(s); 121 | 122 | strbuf_ensure_null(s); 123 | 124 | buf = s->buf; 125 | if (len) 126 | *len = s->length; 127 | 128 | if (s->dynamic) 129 | free(s); 130 | 131 | return buf; 132 | } 133 | 134 | static int calculate_new_size(strbuf_t *s, int len) 135 | { 136 | int reqsize, newsize; 137 | 138 | if (len <= 0) 139 | die("BUG: Invalid strbuf length requested"); 140 | 141 | /* Ensure there is room for optional NULL termination */ 142 | reqsize = len + 1; 143 | 144 | /* If the user has requested to shrink the buffer, do it exactly */ 145 | if (s->size > reqsize) 146 | return reqsize; 147 | 148 | newsize = s->size; 149 | if (s->increment < 0) { 150 | /* Exponential sizing */ 151 | while (newsize < reqsize) 152 | newsize *= -s->increment; 153 | } else { 154 | /* Linear sizing */ 155 | newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; 156 | } 157 | 158 | return newsize; 159 | } 160 | 161 | 162 | /* Ensure strbuf can handle a string length bytes long (ignoring NULL 163 | * optional termination). */ 164 | void strbuf_resize(strbuf_t *s, int len) 165 | { 166 | int newsize; 167 | 168 | newsize = calculate_new_size(s, len); 169 | 170 | if (s->debug > 1) { 171 | fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", 172 | (long)s, s->size, newsize); 173 | } 174 | 175 | s->size = newsize; 176 | s->buf = realloc(s->buf, s->size); 177 | if (!s->buf) 178 | die("Out of memory"); 179 | s->reallocs++; 180 | } 181 | 182 | void strbuf_append_string(strbuf_t *s, const char *str) 183 | { 184 | int space, i; 185 | 186 | space = strbuf_empty_length(s); 187 | 188 | for (i = 0; str[i]; i++) { 189 | if (space < 1) { 190 | strbuf_resize(s, s->length + 1); 191 | space = strbuf_empty_length(s); 192 | } 193 | 194 | s->buf[s->length] = str[i]; 195 | s->length++; 196 | space--; 197 | } 198 | } 199 | 200 | /* strbuf_append_fmt() should only be used when an upper bound 201 | * is known for the output string. */ 202 | void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) 203 | { 204 | va_list arg; 205 | int fmt_len; 206 | 207 | strbuf_ensure_empty_length(s, len); 208 | 209 | va_start(arg, fmt); 210 | fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); 211 | va_end(arg); 212 | 213 | if (fmt_len < 0) 214 | die("BUG: Unable to convert number"); /* This should never happen.. */ 215 | 216 | s->length += fmt_len; 217 | } 218 | 219 | /* strbuf_append_fmt_retry() can be used when the there is no known 220 | * upper bound for the output string. */ 221 | void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) 222 | { 223 | va_list arg; 224 | int fmt_len, try; 225 | int empty_len; 226 | 227 | /* If the first attempt to append fails, resize the buffer appropriately 228 | * and try again */ 229 | for (try = 0; ; try++) { 230 | va_start(arg, fmt); 231 | /* Append the new formatted string */ 232 | /* fmt_len is the length of the string required, excluding the 233 | * trailing NULL */ 234 | empty_len = strbuf_empty_length(s); 235 | /* Add 1 since there is also space to store the terminating NULL. */ 236 | fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); 237 | va_end(arg); 238 | 239 | if (fmt_len <= empty_len) 240 | break; /* SUCCESS */ 241 | if (try > 0) 242 | die("BUG: length of formatted string changed"); 243 | 244 | strbuf_resize(s, s->length + fmt_len); 245 | } 246 | 247 | s->length += fmt_len; 248 | } 249 | 250 | /* vi:ai et sw=4 ts=4: 251 | */ 252 | -------------------------------------------------------------------------------- /3rd/lua-cjson/lua/cjson/util.lua: -------------------------------------------------------------------------------- 1 | local json = require "cjson" 2 | 3 | -- Various common routines used by the Lua CJSON package 4 | -- 5 | -- Mark Pulford 6 | 7 | -- Determine with a Lua table can be treated as an array. 8 | -- Explicitly returns "not an array" for very sparse arrays. 9 | -- Returns: 10 | -- -1 Not an array 11 | -- 0 Empty table 12 | -- >0 Highest index in the array 13 | local function is_array(table) 14 | local max = 0 15 | local count = 0 16 | for k, v in pairs(table) do 17 | if type(k) == "number" then 18 | if k > max then max = k end 19 | count = count + 1 20 | else 21 | return -1 22 | end 23 | end 24 | if max > count * 2 then 25 | return -1 26 | end 27 | 28 | return max 29 | end 30 | 31 | local serialise_value 32 | 33 | local function serialise_table(value, indent, depth) 34 | local spacing, spacing2, indent2 35 | if indent then 36 | spacing = "\n" .. indent 37 | spacing2 = spacing .. " " 38 | indent2 = indent .. " " 39 | else 40 | spacing, spacing2, indent2 = " ", " ", false 41 | end 42 | depth = depth + 1 43 | if depth > 50 then 44 | return "Cannot serialise any further: too many nested tables" 45 | end 46 | 47 | local max = is_array(value) 48 | 49 | local comma = false 50 | local fragment = { "{" .. spacing2 } 51 | if max > 0 then 52 | -- Serialise array 53 | for i = 1, max do 54 | if comma then 55 | table.insert(fragment, "," .. spacing2) 56 | end 57 | table.insert(fragment, serialise_value(value[i], indent2, depth)) 58 | comma = true 59 | end 60 | elseif max < 0 then 61 | -- Serialise table 62 | for k, v in pairs(value) do 63 | if comma then 64 | table.insert(fragment, "," .. spacing2) 65 | end 66 | table.insert(fragment, 67 | ("[%s] = %s"):format(serialise_value(k, indent2, depth), 68 | serialise_value(v, indent2, depth))) 69 | comma = true 70 | end 71 | end 72 | table.insert(fragment, spacing .. "}") 73 | 74 | return table.concat(fragment) 75 | end 76 | 77 | function serialise_value(value, indent, depth) 78 | if indent == nil then indent = "" end 79 | if depth == nil then depth = 0 end 80 | 81 | if value == json.null then 82 | return "json.null" 83 | elseif type(value) == "string" then 84 | return ("%q"):format(value) 85 | elseif type(value) == "nil" or type(value) == "number" or 86 | type(value) == "boolean" then 87 | return tostring(value) 88 | elseif type(value) == "table" then 89 | return serialise_table(value, indent, depth) 90 | else 91 | return "\"<" .. type(value) .. ">\"" 92 | end 93 | end 94 | 95 | local function file_load(filename) 96 | local file 97 | if filename == nil then 98 | file = io.stdin 99 | else 100 | local err 101 | file, err = io.open(filename, "rb") 102 | if file == nil then 103 | error(("Unable to read '%s': %s"):format(filename, err)) 104 | end 105 | end 106 | local data = file:read("*a") 107 | 108 | if filename ~= nil then 109 | file:close() 110 | end 111 | 112 | if data == nil then 113 | error("Failed to read " .. filename) 114 | end 115 | 116 | return data 117 | end 118 | 119 | local function file_save(filename, data) 120 | local file 121 | if filename == nil then 122 | file = io.stdout 123 | else 124 | local err 125 | file, err = io.open(filename, "wb") 126 | if file == nil then 127 | error(("Unable to write '%s': %s"):format(filename, err)) 128 | end 129 | end 130 | file:write(data) 131 | if filename ~= nil then 132 | file:close() 133 | end 134 | end 135 | 136 | local function compare_values(val1, val2) 137 | local type1 = type(val1) 138 | local type2 = type(val2) 139 | if type1 ~= type2 then 140 | return false 141 | end 142 | 143 | -- Check for NaN 144 | if type1 == "number" and val1 ~= val1 and val2 ~= val2 then 145 | return true 146 | end 147 | 148 | if type1 ~= "table" then 149 | return val1 == val2 150 | end 151 | 152 | -- check_keys stores all the keys that must be checked in val2 153 | local check_keys = {} 154 | for k, _ in pairs(val1) do 155 | check_keys[k] = true 156 | end 157 | 158 | for k, v in pairs(val2) do 159 | if not check_keys[k] then 160 | return false 161 | end 162 | 163 | if not compare_values(val1[k], val2[k]) then 164 | return false 165 | end 166 | 167 | check_keys[k] = nil 168 | end 169 | for k, _ in pairs(check_keys) do 170 | -- Not the same if any keys from val1 were not found in val2 171 | return false 172 | end 173 | return true 174 | end 175 | 176 | local test_count_pass = 0 177 | local test_count_total = 0 178 | 179 | local function run_test_summary() 180 | return test_count_pass, test_count_total 181 | end 182 | 183 | local function run_test(testname, func, input, should_work, output) 184 | local function status_line(name, status, value) 185 | local statusmap = { [true] = ":success", [false] = ":error" } 186 | if status ~= nil then 187 | name = name .. statusmap[status] 188 | end 189 | print(("[%s] %s"):format(name, serialise_value(value, false))) 190 | end 191 | 192 | local result = { pcall(func, unpack(input)) } 193 | local success = table.remove(result, 1) 194 | 195 | local correct = false 196 | if success == should_work and compare_values(result, output) then 197 | correct = true 198 | test_count_pass = test_count_pass + 1 199 | end 200 | test_count_total = test_count_total + 1 201 | 202 | local teststatus = { [true] = "PASS", [false] = "FAIL" } 203 | print(("==> Test [%d] %s: %s"):format(test_count_total, testname, 204 | teststatus[correct])) 205 | 206 | status_line("Input", nil, input) 207 | if not correct then 208 | status_line("Expected", should_work, output) 209 | end 210 | status_line("Received", success, result) 211 | print() 212 | 213 | return correct, result 214 | end 215 | 216 | local function run_test_group(tests) 217 | local function run_helper(name, func, input) 218 | if type(name) == "string" and #name > 0 then 219 | print("==> " .. name) 220 | end 221 | -- Not a protected call, these functions should never generate errors. 222 | func(unpack(input or {})) 223 | print() 224 | end 225 | 226 | for _, v in ipairs(tests) do 227 | -- Run the helper if "should_work" is missing 228 | if v[4] == nil then 229 | run_helper(unpack(v)) 230 | else 231 | run_test(unpack(v)) 232 | end 233 | end 234 | end 235 | 236 | -- Run a Lua script in a separate environment 237 | local function run_script(script, env) 238 | local env = env or {} 239 | local func 240 | 241 | -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists 242 | if _G.setfenv then 243 | func = loadstring(script) 244 | if func then 245 | setfenv(func, env) 246 | end 247 | else 248 | func = load(script, nil, nil, env) 249 | end 250 | 251 | if func == nil then 252 | error("Invalid syntax.") 253 | end 254 | func() 255 | 256 | return env 257 | end 258 | 259 | -- Export functions 260 | return { 261 | serialise_value = serialise_value, 262 | file_load = file_load, 263 | file_save = file_save, 264 | compare_values = compare_values, 265 | run_test_summary = run_test_summary, 266 | run_test = run_test, 267 | run_test_group = run_test_group, 268 | run_script = run_script 269 | } 270 | 271 | -- vi:ai et sw=4 ts=4: 272 | -------------------------------------------------------------------------------- /3rd/lua-cjson/performance.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | JSON module performance comparison under Lua 8 | 384 | 458 | 459 | 460 | 470 |
471 |
472 |

This performance comparison aims to provide a guide of relative 473 | performance between several fast and popular JSON modules.

474 |

The examples used in this comparison were mostly sourced from the 475 | JSON website and 476 | RFC 4627.

477 |

Performance will vary widely between platforms and data sets. These 478 | results should only be used as an approximation.

479 |
480 |
481 |

1. Modules

482 |
483 |

The following JSON modules for Lua were tested:

484 |
485 |
486 | DKJSON 2.1 487 |
488 |
489 |
    490 |
  • 491 |

    492 | Lua implementation with no dependencies on other libraries 493 |

    494 |
  • 495 |
  • 496 |

    497 | Supports LPeg to improve decode performance 498 |

    499 |
  • 500 |
501 |
502 |
503 | Lua YAJL 2.0 504 |
505 |
506 |
    507 |
  • 508 |

    509 | C wrapper for the YAJL library 510 |

    511 |
  • 512 |
513 |
514 |
515 | Lua CSJON 2.0.0 516 |
517 |
518 |
    519 |
  • 520 |

    521 | C implementation with no dependencies on other libraries 522 |

    523 |
  • 524 |
525 |
526 |
527 |
528 |

2. Summary

529 |
530 |

All modules were built and tested as follows:

531 |
532 |
533 | DKJSON 534 |
535 |
536 |

537 | Tested with/without LPeg 10.2. 538 |

539 |
540 |
541 | Lua YAJL 542 |
543 |
544 |

545 | Tested with YAJL 2.0.4. 546 |

547 |
548 |
549 | Lua CJSON 550 |
551 |
552 |

553 | Tested with Libc and internal floating point conversion 554 | routines. 555 |

556 |
557 |
558 |

The following Lua implementations were used for this comparison:

559 |
571 |

These results show the number of JSON operations per second sustained by 572 | each module. All results have been normalised against the pure Lua 573 | DKJSON implementation.

574 |
575 |
Decoding performance
576 |
577 |
             | DKJSON                  | Lua YAJL   | Lua CJSON
578 |              | No LPeg     With LPeg   |            | Libc         Internal
579 |              | Lua  JIT    Lua    JIT  | Lua   JIT  | Lua   JIT    Lua   JIT
580 | example1     | 1x   2x     2.6x   3.4x | 7.1x  10x  | 14x   20x    14x   20x
581 | example2     | 1x   2.2x   2.9x   4.4x | 6.7x  9.9x | 14x   22x    14x   22x
582 | example3     | 1x   2.1x   3x     4.3x | 6.9x  9.3x | 14x   21x    15x   22x
583 | example4     | 1x   2x     2.5x   3.7x | 7.3x  10x  | 12x   19x    12x   20x
584 | example5     | 1x   2.2x   3x     4.5x | 7.8x  11x  | 16x   24x    16x   24x
585 | numbers      | 1x   2.2x   2.3x   4x   | 4.6x  5.5x | 8.9x  10x    13x   17x
586 | rfc-example1 | 1x   2.1x   2.8x   4.3x | 6.1x  8.1x | 13x   19x    14x   21x
587 | rfc-example2 | 1x   2.1x   3.1x   4.2x | 7.1x  9.2x | 15x   21x    17x   24x
588 | types        | 1x   2.2x   2.6x   4.3x | 5.3x  7.4x | 12x   20x    13x   21x
589 | -------------|-------------------------|------------|-----------------------
590 | = Average => | 1x   2.1x   2.7x   4.1x | 6.5x  9x   | 13x   20x    14x   21x
591 |
592 |
593 |
Encoding performance
594 |
595 |
             | DKJSON                  | Lua YAJL   | Lua CJSON
596 |              | No LPeg     With LPeg   |            | Libc         Internal
597 |              | Lua  JIT    Lua    JIT  | Lua   JIT  | Lua   JIT    Lua   JIT
598 | example1     | 1x   1.8x   0.97x  1.6x | 3.1x  5.2x | 23x   29x    23x   29x
599 | example2     | 1x   2x     0.97x  1.7x | 2.6x  4.3x | 22x   28x    22x   28x
600 | example3     | 1x   1.9x   0.98x  1.6x | 2.8x  4.3x | 13x   15x    16x   18x
601 | example4     | 1x   1.7x   0.96x  1.3x | 3.9x  6.1x | 15x   19x    17x   21x
602 | example5     | 1x   2x     0.98x  1.7x | 2.7x  4.5x | 20x   23x    20x   23x
603 | numbers      | 1x   2.3x   1x     2.2x | 1.3x  1.9x | 3.8x  4.1x   4.2x  4.6x
604 | rfc-example1 | 1x   1.9x   0.97x  1.6x | 2.2x  3.2x | 8.5x  9.3x   11x   12x
605 | rfc-example2 | 1x   1.9x   0.98x  1.6x | 2.6x  3.9x | 10x   11x    17x   19x
606 | types        | 1x   2.2x   0.97x  2x   | 1.2x  1.9x | 11x   13x    12x   14x
607 | -------------|-------------------------|------------|-----------------------
608 | = Average => | 1x   1.9x   0.98x  1.7x | 2.5x  3.9x | 14x   17x    16x   19x
609 |
610 |
611 | 616 | 617 | 618 | -------------------------------------------------------------------------------- /3rd/lua-cjson/rfc4627.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group D. Crockford 8 | Request for Comments: 4627 JSON.org 9 | Category: Informational July 2006 10 | 11 | 12 | The application/json Media Type for JavaScript Object Notation (JSON) 13 | 14 | Status of This Memo 15 | 16 | This memo provides information for the Internet community. It does 17 | not specify an Internet standard of any kind. Distribution of this 18 | memo is unlimited. 19 | 20 | Copyright Notice 21 | 22 | Copyright (C) The Internet Society (2006). 23 | 24 | Abstract 25 | 26 | JavaScript Object Notation (JSON) is a lightweight, text-based, 27 | language-independent data interchange format. It was derived from 28 | the ECMAScript Programming Language Standard. JSON defines a small 29 | set of formatting rules for the portable representation of structured 30 | data. 31 | 32 | 1. Introduction 33 | 34 | JavaScript Object Notation (JSON) is a text format for the 35 | serialization of structured data. It is derived from the object 36 | literals of JavaScript, as defined in the ECMAScript Programming 37 | Language Standard, Third Edition [ECMA]. 38 | 39 | JSON can represent four primitive types (strings, numbers, booleans, 40 | and null) and two structured types (objects and arrays). 41 | 42 | A string is a sequence of zero or more Unicode characters [UNICODE]. 43 | 44 | An object is an unordered collection of zero or more name/value 45 | pairs, where a name is a string and a value is a string, number, 46 | boolean, null, object, or array. 47 | 48 | An array is an ordered sequence of zero or more values. 49 | 50 | The terms "object" and "array" come from the conventions of 51 | JavaScript. 52 | 53 | JSON's design goals were for it to be minimal, portable, textual, and 54 | a subset of JavaScript. 55 | 56 | 57 | 58 | Crockford Informational [Page 1] 59 | 60 | RFC 4627 JSON July 2006 61 | 62 | 63 | 1.1. Conventions Used in This Document 64 | 65 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 66 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 67 | document are to be interpreted as described in [RFC2119]. 68 | 69 | The grammatical rules in this document are to be interpreted as 70 | described in [RFC4234]. 71 | 72 | 2. JSON Grammar 73 | 74 | A JSON text is a sequence of tokens. The set of tokens includes six 75 | structural characters, strings, numbers, and three literal names. 76 | 77 | A JSON text is a serialized object or array. 78 | 79 | JSON-text = object / array 80 | 81 | These are the six structural characters: 82 | 83 | begin-array = ws %x5B ws ; [ left square bracket 84 | 85 | begin-object = ws %x7B ws ; { left curly bracket 86 | 87 | end-array = ws %x5D ws ; ] right square bracket 88 | 89 | end-object = ws %x7D ws ; } right curly bracket 90 | 91 | name-separator = ws %x3A ws ; : colon 92 | 93 | value-separator = ws %x2C ws ; , comma 94 | 95 | Insignificant whitespace is allowed before or after any of the six 96 | structural characters. 97 | 98 | ws = *( 99 | %x20 / ; Space 100 | %x09 / ; Horizontal tab 101 | %x0A / ; Line feed or New line 102 | %x0D ; Carriage return 103 | ) 104 | 105 | 2.1. Values 106 | 107 | A JSON value MUST be an object, array, number, or string, or one of 108 | the following three literal names: 109 | 110 | false null true 111 | 112 | 113 | 114 | Crockford Informational [Page 2] 115 | 116 | RFC 4627 JSON July 2006 117 | 118 | 119 | The literal names MUST be lowercase. No other literal names are 120 | allowed. 121 | 122 | value = false / null / true / object / array / number / string 123 | 124 | false = %x66.61.6c.73.65 ; false 125 | 126 | null = %x6e.75.6c.6c ; null 127 | 128 | true = %x74.72.75.65 ; true 129 | 130 | 2.2. Objects 131 | 132 | An object structure is represented as a pair of curly brackets 133 | surrounding zero or more name/value pairs (or members). A name is a 134 | string. A single colon comes after each name, separating the name 135 | from the value. A single comma separates a value from a following 136 | name. The names within an object SHOULD be unique. 137 | 138 | object = begin-object [ member *( value-separator member ) ] 139 | end-object 140 | 141 | member = string name-separator value 142 | 143 | 2.3. Arrays 144 | 145 | An array structure is represented as square brackets surrounding zero 146 | or more values (or elements). Elements are separated by commas. 147 | 148 | array = begin-array [ value *( value-separator value ) ] end-array 149 | 150 | 2.4. Numbers 151 | 152 | The representation of numbers is similar to that used in most 153 | programming languages. A number contains an integer component that 154 | may be prefixed with an optional minus sign, which may be followed by 155 | a fraction part and/or an exponent part. 156 | 157 | Octal and hex forms are not allowed. Leading zeros are not allowed. 158 | 159 | A fraction part is a decimal point followed by one or more digits. 160 | 161 | An exponent part begins with the letter E in upper or lowercase, 162 | which may be followed by a plus or minus sign. The E and optional 163 | sign are followed by one or more digits. 164 | 165 | Numeric values that cannot be represented as sequences of digits 166 | (such as Infinity and NaN) are not permitted. 167 | 168 | 169 | 170 | Crockford Informational [Page 3] 171 | 172 | RFC 4627 JSON July 2006 173 | 174 | 175 | number = [ minus ] int [ frac ] [ exp ] 176 | 177 | decimal-point = %x2E ; . 178 | 179 | digit1-9 = %x31-39 ; 1-9 180 | 181 | e = %x65 / %x45 ; e E 182 | 183 | exp = e [ minus / plus ] 1*DIGIT 184 | 185 | frac = decimal-point 1*DIGIT 186 | 187 | int = zero / ( digit1-9 *DIGIT ) 188 | 189 | minus = %x2D ; - 190 | 191 | plus = %x2B ; + 192 | 193 | zero = %x30 ; 0 194 | 195 | 2.5. Strings 196 | 197 | The representation of strings is similar to conventions used in the C 198 | family of programming languages. A string begins and ends with 199 | quotation marks. All Unicode characters may be placed within the 200 | quotation marks except for the characters that must be escaped: 201 | quotation mark, reverse solidus, and the control characters (U+0000 202 | through U+001F). 203 | 204 | Any character may be escaped. If the character is in the Basic 205 | Multilingual Plane (U+0000 through U+FFFF), then it may be 206 | represented as a six-character sequence: a reverse solidus, followed 207 | by the lowercase letter u, followed by four hexadecimal digits that 208 | encode the character's code point. The hexadecimal letters A though 209 | F can be upper or lowercase. So, for example, a string containing 210 | only a single reverse solidus character may be represented as 211 | "\u005C". 212 | 213 | Alternatively, there are two-character sequence escape 214 | representations of some popular characters. So, for example, a 215 | string containing only a single reverse solidus character may be 216 | represented more compactly as "\\". 217 | 218 | To escape an extended character that is not in the Basic Multilingual 219 | Plane, the character is represented as a twelve-character sequence, 220 | encoding the UTF-16 surrogate pair. So, for example, a string 221 | containing only the G clef character (U+1D11E) may be represented as 222 | "\uD834\uDD1E". 223 | 224 | 225 | 226 | Crockford Informational [Page 4] 227 | 228 | RFC 4627 JSON July 2006 229 | 230 | 231 | string = quotation-mark *char quotation-mark 232 | 233 | char = unescaped / 234 | escape ( 235 | %x22 / ; " quotation mark U+0022 236 | %x5C / ; \ reverse solidus U+005C 237 | %x2F / ; / solidus U+002F 238 | %x62 / ; b backspace U+0008 239 | %x66 / ; f form feed U+000C 240 | %x6E / ; n line feed U+000A 241 | %x72 / ; r carriage return U+000D 242 | %x74 / ; t tab U+0009 243 | %x75 4HEXDIG ) ; uXXXX U+XXXX 244 | 245 | escape = %x5C ; \ 246 | 247 | quotation-mark = %x22 ; " 248 | 249 | unescaped = %x20-21 / %x23-5B / %x5D-10FFFF 250 | 251 | 3. Encoding 252 | 253 | JSON text SHALL be encoded in Unicode. The default encoding is 254 | UTF-8. 255 | 256 | Since the first two characters of a JSON text will always be ASCII 257 | characters [RFC0020], it is possible to determine whether an octet 258 | stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking 259 | at the pattern of nulls in the first four octets. 260 | 261 | 00 00 00 xx UTF-32BE 262 | 00 xx 00 xx UTF-16BE 263 | xx 00 00 00 UTF-32LE 264 | xx 00 xx 00 UTF-16LE 265 | xx xx xx xx UTF-8 266 | 267 | 4. Parsers 268 | 269 | A JSON parser transforms a JSON text into another representation. A 270 | JSON parser MUST accept all texts that conform to the JSON grammar. 271 | A JSON parser MAY accept non-JSON forms or extensions. 272 | 273 | An implementation may set limits on the size of texts that it 274 | accepts. An implementation may set limits on the maximum depth of 275 | nesting. An implementation may set limits on the range of numbers. 276 | An implementation may set limits on the length and character contents 277 | of strings. 278 | 279 | 280 | 281 | 282 | Crockford Informational [Page 5] 283 | 284 | RFC 4627 JSON July 2006 285 | 286 | 287 | 5. Generators 288 | 289 | A JSON generator produces JSON text. The resulting text MUST 290 | strictly conform to the JSON grammar. 291 | 292 | 6. IANA Considerations 293 | 294 | The MIME media type for JSON text is application/json. 295 | 296 | Type name: application 297 | 298 | Subtype name: json 299 | 300 | Required parameters: n/a 301 | 302 | Optional parameters: n/a 303 | 304 | Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32 305 | 306 | JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON 307 | is written in UTF-8, JSON is 8bit compatible. When JSON is 308 | written in UTF-16 or UTF-32, the binary content-transfer-encoding 309 | must be used. 310 | 311 | Security considerations: 312 | 313 | Generally there are security issues with scripting languages. JSON 314 | is a subset of JavaScript, but it is a safe subset that excludes 315 | assignment and invocation. 316 | 317 | A JSON text can be safely passed into JavaScript's eval() function 318 | (which compiles and executes a string) if all the characters not 319 | enclosed in strings are in the set of characters that form JSON 320 | tokens. This can be quickly determined in JavaScript with two 321 | regular expressions and calls to the test and replace methods. 322 | 323 | var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( 324 | text.replace(/"(\\.|[^"\\])*"/g, ''))) && 325 | eval('(' + text + ')'); 326 | 327 | Interoperability considerations: n/a 328 | 329 | Published specification: RFC 4627 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | Crockford Informational [Page 6] 339 | 340 | RFC 4627 JSON July 2006 341 | 342 | 343 | Applications that use this media type: 344 | 345 | JSON has been used to exchange data between applications written 346 | in all of these programming languages: ActionScript, C, C#, 347 | ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua, 348 | Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme. 349 | 350 | Additional information: 351 | 352 | Magic number(s): n/a 353 | File extension(s): .json 354 | Macintosh file type code(s): TEXT 355 | 356 | Person & email address to contact for further information: 357 | Douglas Crockford 358 | douglas@crockford.com 359 | 360 | Intended usage: COMMON 361 | 362 | Restrictions on usage: none 363 | 364 | Author: 365 | Douglas Crockford 366 | douglas@crockford.com 367 | 368 | Change controller: 369 | Douglas Crockford 370 | douglas@crockford.com 371 | 372 | 7. Security Considerations 373 | 374 | See Security Considerations in Section 6. 375 | 376 | 8. Examples 377 | 378 | This is a JSON object: 379 | 380 | { 381 | "Image": { 382 | "Width": 800, 383 | "Height": 600, 384 | "Title": "View from 15th Floor", 385 | "Thumbnail": { 386 | "Url": "http://www.example.com/image/481989943", 387 | "Height": 125, 388 | "Width": "100" 389 | }, 390 | "IDs": [116, 943, 234, 38793] 391 | 392 | 393 | 394 | Crockford Informational [Page 7] 395 | 396 | RFC 4627 JSON July 2006 397 | 398 | 399 | } 400 | } 401 | 402 | Its Image member is an object whose Thumbnail member is an object 403 | and whose IDs member is an array of numbers. 404 | 405 | This is a JSON array containing two objects: 406 | 407 | [ 408 | { 409 | "precision": "zip", 410 | "Latitude": 37.7668, 411 | "Longitude": -122.3959, 412 | "Address": "", 413 | "City": "SAN FRANCISCO", 414 | "State": "CA", 415 | "Zip": "94107", 416 | "Country": "US" 417 | }, 418 | { 419 | "precision": "zip", 420 | "Latitude": 37.371991, 421 | "Longitude": -122.026020, 422 | "Address": "", 423 | "City": "SUNNYVALE", 424 | "State": "CA", 425 | "Zip": "94085", 426 | "Country": "US" 427 | } 428 | ] 429 | 430 | 9. References 431 | 432 | 9.1. Normative References 433 | 434 | [ECMA] European Computer Manufacturers Association, "ECMAScript 435 | Language Specification 3rd Edition", December 1999, 436 | . 438 | 439 | [RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20, 440 | October 1969. 441 | 442 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 443 | Requirement Levels", BCP 14, RFC 2119, March 1997. 444 | 445 | [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax 446 | Specifications: ABNF", RFC 4234, October 2005. 447 | 448 | 449 | 450 | Crockford Informational [Page 8] 451 | 452 | RFC 4627 JSON July 2006 453 | 454 | 455 | [UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0", 456 | 2003, . 457 | 458 | Author's Address 459 | 460 | Douglas Crockford 461 | JSON.org 462 | EMail: douglas@crockford.com 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | Crockford Informational [Page 9] 507 | 508 | RFC 4627 JSON July 2006 509 | 510 | 511 | Full Copyright Statement 512 | 513 | Copyright (C) The Internet Society (2006). 514 | 515 | This document is subject to the rights, licenses and restrictions 516 | contained in BCP 78, and except as set forth therein, the authors 517 | retain all their rights. 518 | 519 | This document and the information contained herein are provided on an 520 | "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS 521 | OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET 522 | ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, 523 | INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE 524 | INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED 525 | WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 526 | 527 | Intellectual Property 528 | 529 | The IETF takes no position regarding the validity or scope of any 530 | Intellectual Property Rights or other rights that might be claimed to 531 | pertain to the implementation or use of the technology described in 532 | this document or the extent to which any license under such rights 533 | might or might not be available; nor does it represent that it has 534 | made any independent effort to identify any such rights. Information 535 | on the procedures with respect to rights in RFC documents can be 536 | found in BCP 78 and BCP 79. 537 | 538 | Copies of IPR disclosures made to the IETF Secretariat and any 539 | assurances of licenses to be made available, or the result of an 540 | attempt made to obtain a general license or permission for the use of 541 | such proprietary rights by implementers or users of this 542 | specification can be obtained from the IETF on-line IPR repository at 543 | http://www.ietf.org/ipr. 544 | 545 | The IETF invites any interested party to bring to its attention any 546 | copyrights, patents or patent applications, or other proprietary 547 | rights that may cover technology that may be required to implement 548 | this standard. Please address the information to the IETF at 549 | ietf-ipr@ietf.org. 550 | 551 | Acknowledgement 552 | 553 | Funding for the RFC Editor function is provided by the IETF 554 | Administrative Support Activity (IASA). 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | Crockford Informational [Page 10] 563 | 564 | -------------------------------------------------------------------------------- /3rd/lua-cjson/tests/test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- Lua CJSON tests 4 | -- 5 | -- Mark Pulford 6 | -- 7 | -- Note: The output of this script is easier to read with "less -S" 8 | 9 | local json = require "cjson" 10 | local json_safe = require "cjson.safe" 11 | local util = require "cjson.util" 12 | 13 | local function gen_raw_octets() 14 | local chars = {} 15 | for i = 0, 255 do chars[i + 1] = string.char(i) end 16 | return table.concat(chars) 17 | end 18 | 19 | -- Generate every UTF-16 codepoint, including supplementary codes 20 | local function gen_utf16_escaped() 21 | -- Create raw table escapes 22 | local utf16_escaped = {} 23 | local count = 0 24 | 25 | local function append_escape(code) 26 | local esc = ('\\u%04X'):format(code) 27 | table.insert(utf16_escaped, esc) 28 | end 29 | 30 | table.insert(utf16_escaped, '"') 31 | for i = 0, 0xD7FF do 32 | append_escape(i) 33 | end 34 | -- Skip 0xD800 - 0xDFFF since they are used to encode supplementary 35 | -- codepoints 36 | for i = 0xE000, 0xFFFF do 37 | append_escape(i) 38 | end 39 | -- Append surrogate pair for each supplementary codepoint 40 | for high = 0xD800, 0xDBFF do 41 | for low = 0xDC00, 0xDFFF do 42 | append_escape(high) 43 | append_escape(low) 44 | end 45 | end 46 | table.insert(utf16_escaped, '"') 47 | 48 | return table.concat(utf16_escaped) 49 | end 50 | 51 | function load_testdata() 52 | local data = {} 53 | 54 | -- Data for 8bit raw <-> escaped octets tests 55 | data.octets_raw = gen_raw_octets() 56 | data.octets_escaped = util.file_load("octets-escaped.dat") 57 | 58 | -- Data for \uXXXX -> UTF-8 test 59 | data.utf16_escaped = gen_utf16_escaped() 60 | 61 | -- Load matching data for utf16_escaped 62 | local utf8_loaded 63 | utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat") 64 | if not utf8_loaded then 65 | data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl" 66 | end 67 | 68 | data.table_cycle = {} 69 | data.table_cycle[1] = data.table_cycle 70 | 71 | local big = {} 72 | for i = 1, 1100 do 73 | big = { { 10, false, true, json.null }, "string", a = big } 74 | end 75 | data.deeply_nested_data = big 76 | 77 | return data 78 | end 79 | 80 | function test_decode_cycle(filename) 81 | local obj1 = json.decode(util.file_load(filename)) 82 | local obj2 = json.decode(json.encode(obj1)) 83 | return util.compare_values(obj1, obj2) 84 | end 85 | 86 | -- Set up data used in tests 87 | local Inf = math.huge; 88 | local NaN = math.huge * 0; 89 | 90 | local testdata = load_testdata() 91 | 92 | local cjson_tests = { 93 | -- Test API variables 94 | { "Check module name, version", 95 | function () return json._NAME, json._VERSION end, { }, 96 | true, { "cjson", "2.1.0" } }, 97 | 98 | -- Test decoding simple types 99 | { "Decode string", 100 | json.decode, { '"test string"' }, true, { "test string" } }, 101 | { "Decode numbers", 102 | json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' }, 103 | true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } }, 104 | { "Decode null", 105 | json.decode, { 'null' }, true, { json.null } }, 106 | { "Decode true", 107 | json.decode, { 'true' }, true, { true } }, 108 | { "Decode false", 109 | json.decode, { 'false' }, true, { false } }, 110 | { "Decode object with numeric keys", 111 | json.decode, { '{ "1": "one", "3": "three" }' }, 112 | true, { { ["1"] = "one", ["3"] = "three" } } }, 113 | { "Decode object with string keys", 114 | json.decode, { '{ "a": "a", "b": "b" }' }, 115 | true, { { a = "a", b = "b" } } }, 116 | { "Decode array", 117 | json.decode, { '[ "one", null, "three" ]' }, 118 | true, { { "one", json.null, "three" } } }, 119 | 120 | -- Test decoding errors 121 | { "Decode UTF-16BE [throw error]", 122 | json.decode, { '\0"\0"' }, 123 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 124 | { "Decode UTF-16LE [throw error]", 125 | json.decode, { '"\0"\0' }, 126 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 127 | { "Decode UTF-32BE [throw error]", 128 | json.decode, { '\0\0\0"' }, 129 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 130 | { "Decode UTF-32LE [throw error]", 131 | json.decode, { '"\0\0\0' }, 132 | false, { "JSON parser does not support UTF-16 or UTF-32" } }, 133 | { "Decode partial JSON [throw error]", 134 | json.decode, { '{ "unexpected eof": ' }, 135 | false, { "Expected value but found T_END at character 21" } }, 136 | { "Decode with extra comma [throw error]", 137 | json.decode, { '{ "extra data": true }, false' }, 138 | false, { "Expected the end but found T_COMMA at character 23" } }, 139 | { "Decode invalid escape code [throw error]", 140 | json.decode, { [[ { "bad escape \q code" } ]] }, 141 | false, { "Expected object key string but found invalid escape code at character 16" } }, 142 | { "Decode invalid unicode escape [throw error]", 143 | json.decode, { [[ { "bad unicode \u0f6 escape" } ]] }, 144 | false, { "Expected object key string but found invalid unicode escape code at character 17" } }, 145 | { "Decode invalid keyword [throw error]", 146 | json.decode, { ' [ "bad barewood", test ] ' }, 147 | false, { "Expected value but found invalid token at character 20" } }, 148 | { "Decode invalid number #1 [throw error]", 149 | json.decode, { '[ -+12 ]' }, 150 | false, { "Expected value but found invalid number at character 3" } }, 151 | { "Decode invalid number #2 [throw error]", 152 | json.decode, { '-v' }, 153 | false, { "Expected value but found invalid number at character 1" } }, 154 | { "Decode invalid number exponent [throw error]", 155 | json.decode, { '[ 0.4eg10 ]' }, 156 | false, { "Expected comma or array end but found invalid token at character 6" } }, 157 | 158 | -- Test decoding nested arrays / objects 159 | { "Set decode_max_depth(5)", 160 | json.decode_max_depth, { 5 }, true, { 5 } }, 161 | { "Decode array at nested limit", 162 | json.decode, { '[[[[[ "nested" ]]]]]' }, 163 | true, { {{{{{ "nested" }}}}} } }, 164 | { "Decode array over nested limit [throw error]", 165 | json.decode, { '[[[[[[ "nested" ]]]]]]' }, 166 | false, { "Found too many nested data structures (6) at character 6" } }, 167 | { "Decode object at nested limit", 168 | json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' }, 169 | true, { {a={b={c={d={e="nested"}}}}} } }, 170 | { "Decode object over nested limit [throw error]", 171 | json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' }, 172 | false, { "Found too many nested data structures (6) at character 26" } }, 173 | { "Set decode_max_depth(1000)", 174 | json.decode_max_depth, { 1000 }, true, { 1000 } }, 175 | { "Decode deeply nested array [throw error]", 176 | json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)}, 177 | false, { "Found too many nested data structures (1001) at character 1001" } }, 178 | 179 | -- Test encoding nested tables 180 | { "Set encode_max_depth(5)", 181 | json.encode_max_depth, { 5 }, true, { 5 } }, 182 | { "Encode nested table as array at nested limit", 183 | json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } }, 184 | { "Encode nested table as array after nested limit [throw error]", 185 | json.encode, { { {{{{{"nested"}}}}} } }, 186 | false, { "Cannot serialise, excessive nesting (6)" } }, 187 | { "Encode nested table as object at nested limit", 188 | json.encode, { {a={b={c={d={e="nested"}}}}} }, 189 | true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } }, 190 | { "Encode nested table as object over nested limit [throw error]", 191 | json.encode, { {a={b={c={d={e={f="nested"}}}}}} }, 192 | false, { "Cannot serialise, excessive nesting (6)" } }, 193 | { "Encode table with cycle [throw error]", 194 | json.encode, { testdata.table_cycle }, 195 | false, { "Cannot serialise, excessive nesting (6)" } }, 196 | { "Set encode_max_depth(1000)", 197 | json.encode_max_depth, { 1000 }, true, { 1000 } }, 198 | { "Encode deeply nested data [throw error]", 199 | json.encode, { testdata.deeply_nested_data }, 200 | false, { "Cannot serialise, excessive nesting (1001)" } }, 201 | 202 | -- Test encoding simple types 203 | { "Encode null", 204 | json.encode, { json.null }, true, { 'null' } }, 205 | { "Encode true", 206 | json.encode, { true }, true, { 'true' } }, 207 | { "Encode false", 208 | json.encode, { false }, true, { 'false' } }, 209 | { "Encode empty object", 210 | json.encode, { { } }, true, { '{}' } }, 211 | { "Encode integer", 212 | json.encode, { 10 }, true, { '10' } }, 213 | { "Encode string", 214 | json.encode, { "hello" }, true, { '"hello"' } }, 215 | { "Encode Lua function [throw error]", 216 | json.encode, { function () end }, 217 | false, { "Cannot serialise function: type not supported" } }, 218 | 219 | -- Test decoding invalid numbers 220 | { "Set decode_invalid_numbers(true)", 221 | json.decode_invalid_numbers, { true }, true, { true } }, 222 | { "Decode hexadecimal", 223 | json.decode, { '0x6.ffp1' }, true, { 13.9921875 } }, 224 | { "Decode numbers with leading zero", 225 | json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } }, 226 | { "Decode +-Inf", 227 | json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } }, 228 | { "Decode +-Infinity", 229 | json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, 230 | true, { { Inf, Inf, -Inf } } }, 231 | { "Decode +-NaN", 232 | json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } }, 233 | { "Decode Infrared (not infinity) [throw error]", 234 | json.decode, { 'Infrared' }, 235 | false, { "Expected the end but found invalid token at character 4" } }, 236 | { "Decode Noodle (not NaN) [throw error]", 237 | json.decode, { 'Noodle' }, 238 | false, { "Expected value but found invalid token at character 1" } }, 239 | { "Set decode_invalid_numbers(false)", 240 | json.decode_invalid_numbers, { false }, true, { false } }, 241 | { "Decode hexadecimal [throw error]", 242 | json.decode, { '0x6' }, 243 | false, { "Expected value but found invalid number at character 1" } }, 244 | { "Decode numbers with leading zero [throw error]", 245 | json.decode, { '[ 0123, 00.33 ]' }, 246 | false, { "Expected value but found invalid number at character 3" } }, 247 | { "Decode +-Inf [throw error]", 248 | json.decode, { '[ +Inf, Inf, -Inf ]' }, 249 | false, { "Expected value but found invalid token at character 3" } }, 250 | { "Decode +-Infinity [throw error]", 251 | json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, 252 | false, { "Expected value but found invalid token at character 3" } }, 253 | { "Decode +-NaN [throw error]", 254 | json.decode, { '[ +NaN, NaN, -NaN ]' }, 255 | false, { "Expected value but found invalid token at character 3" } }, 256 | { 'Set decode_invalid_numbers("on")', 257 | json.decode_invalid_numbers, { "on" }, true, { true } }, 258 | 259 | -- Test encoding invalid numbers 260 | { "Set encode_invalid_numbers(false)", 261 | json.encode_invalid_numbers, { false }, true, { false } }, 262 | { "Encode NaN [throw error]", 263 | json.encode, { NaN }, 264 | false, { "Cannot serialise number: must not be NaN or Inf" } }, 265 | { "Encode Infinity [throw error]", 266 | json.encode, { Inf }, 267 | false, { "Cannot serialise number: must not be NaN or Inf" } }, 268 | { "Set encode_invalid_numbers(\"null\")", 269 | json.encode_invalid_numbers, { "null" }, true, { "null" } }, 270 | { "Encode NaN as null", 271 | json.encode, { NaN }, true, { "null" } }, 272 | { "Encode Infinity as null", 273 | json.encode, { Inf }, true, { "null" } }, 274 | { "Set encode_invalid_numbers(true)", 275 | json.encode_invalid_numbers, { true }, true, { true } }, 276 | { "Encode NaN", 277 | json.encode, { NaN }, true, { "nan" } }, 278 | { "Encode Infinity", 279 | json.encode, { Inf }, true, { "inf" } }, 280 | { 'Set encode_invalid_numbers("off")', 281 | json.encode_invalid_numbers, { "off" }, true, { false } }, 282 | 283 | -- Test encoding tables 284 | { "Set encode_sparse_array(true, 2, 3)", 285 | json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } }, 286 | { "Encode sparse table as array #1", 287 | json.encode, { { [3] = "sparse test" } }, 288 | true, { '[null,null,"sparse test"]' } }, 289 | { "Encode sparse table as array #2", 290 | json.encode, { { [1] = "one", [4] = "sparse test" } }, 291 | true, { '["one",null,null,"sparse test"]' } }, 292 | { "Encode sparse array as object", 293 | json.encode, { { [1] = "one", [5] = "sparse test" } }, 294 | true, { '{"1":"one","5":"sparse test"}' } }, 295 | { "Encode table with numeric string key as object", 296 | json.encode, { { ["2"] = "numeric string key test" } }, 297 | true, { '{"2":"numeric string key test"}' } }, 298 | { "Set encode_sparse_array(false)", 299 | json.encode_sparse_array, { false }, true, { false, 2, 3 } }, 300 | { "Encode table with incompatible key [throw error]", 301 | json.encode, { { [false] = "wrong" } }, 302 | false, { "Cannot serialise boolean: table key must be a number or string" } }, 303 | 304 | -- Test escaping 305 | { "Encode all octets (8-bit clean)", 306 | json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } }, 307 | { "Decode all escaped octets", 308 | json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } }, 309 | { "Decode single UTF-16 escape", 310 | json.decode, { [["\uF800"]] }, true, { "\239\160\128" } }, 311 | { "Decode all UTF-16 escapes (including surrogate combinations)", 312 | json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } }, 313 | { "Decode swapped surrogate pair [throw error]", 314 | json.decode, { [["\uDC00\uD800"]] }, 315 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 316 | { "Decode duplicate high surrogate [throw error]", 317 | json.decode, { [["\uDB00\uDB00"]] }, 318 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 319 | { "Decode duplicate low surrogate [throw error]", 320 | json.decode, { [["\uDB00\uDB00"]] }, 321 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 322 | { "Decode missing low surrogate [throw error]", 323 | json.decode, { [["\uDB00"]] }, 324 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 325 | { "Decode invalid low surrogate [throw error]", 326 | json.decode, { [["\uDB00\uD"]] }, 327 | false, { "Expected value but found invalid unicode escape code at character 2" } }, 328 | 329 | -- Test locale support 330 | -- 331 | -- The standard Lua interpreter is ANSI C online doesn't support locales 332 | -- by default. Force a known problematic locale to test strtod()/sprintf(). 333 | { "Set locale to cs_CZ (comma separator)", function () 334 | os.setlocale("cs_CZ") 335 | json.new() 336 | end }, 337 | { "Encode number under comma locale", 338 | json.encode, { 1.5 }, true, { '1.5' } }, 339 | { "Decode number in array under comma locale", 340 | json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } }, 341 | { "Revert locale to POSIX", function () 342 | os.setlocale("C") 343 | json.new() 344 | end }, 345 | 346 | -- Test encode_keep_buffer() and enable_number_precision() 347 | { "Set encode_keep_buffer(false)", 348 | json.encode_keep_buffer, { false }, true, { false } }, 349 | { "Set encode_number_precision(3)", 350 | json.encode_number_precision, { 3 }, true, { 3 } }, 351 | { "Encode number with precision 3", 352 | json.encode, { 1/3 }, true, { "0.333" } }, 353 | { "Set encode_number_precision(14)", 354 | json.encode_number_precision, { 14 }, true, { 14 } }, 355 | { "Set encode_keep_buffer(true)", 356 | json.encode_keep_buffer, { true }, true, { true } }, 357 | 358 | -- Test config API errors 359 | -- Function is listed as '?' due to pcall 360 | { "Set encode_number_precision(0) [throw error]", 361 | json.encode_number_precision, { 0 }, 362 | false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } }, 363 | { "Set encode_number_precision(\"five\") [throw error]", 364 | json.encode_number_precision, { "five" }, 365 | false, { "bad argument #1 to '?' (number expected, got string)" } }, 366 | { "Set encode_keep_buffer(nil, true) [throw error]", 367 | json.encode_keep_buffer, { nil, true }, 368 | false, { "bad argument #2 to '?' (found too many arguments)" } }, 369 | { "Set encode_max_depth(\"wrong\") [throw error]", 370 | json.encode_max_depth, { "wrong" }, 371 | false, { "bad argument #1 to '?' (number expected, got string)" } }, 372 | { "Set decode_max_depth(0) [throw error]", 373 | json.decode_max_depth, { "0" }, 374 | false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } }, 375 | { "Set encode_invalid_numbers(-2) [throw error]", 376 | json.encode_invalid_numbers, { -2 }, 377 | false, { "bad argument #1 to '?' (invalid option '-2')" } }, 378 | { "Set decode_invalid_numbers(true, false) [throw error]", 379 | json.decode_invalid_numbers, { true, false }, 380 | false, { "bad argument #2 to '?' (found too many arguments)" } }, 381 | { "Set encode_sparse_array(\"not quite on\") [throw error]", 382 | json.encode_sparse_array, { "not quite on" }, 383 | false, { "bad argument #1 to '?' (invalid option 'not quite on')" } }, 384 | 385 | { "Reset Lua CJSON configuration", function () json = json.new() end }, 386 | -- Wrap in a function to ensure the table returned by json.new() is used 387 | { "Check encode_sparse_array()", 388 | function (...) return json.encode_sparse_array(...) end, { }, 389 | true, { false, 2, 10 } }, 390 | 391 | { "Encode (safe) simple value", 392 | json_safe.encode, { true }, 393 | true, { "true" } }, 394 | { "Encode (safe) argument validation [throw error]", 395 | json_safe.encode, { "arg1", "arg2" }, 396 | false, { "bad argument #1 to '?' (expected 1 argument)" } }, 397 | { "Decode (safe) error generation", 398 | json_safe.decode, { "Oops" }, 399 | true, { nil, "Expected value but found invalid token at character 1" } }, 400 | { "Decode (safe) error generation after new()", 401 | function(...) return json_safe.new().decode(...) end, { "Oops" }, 402 | true, { nil, "Expected value but found invalid token at character 1" } }, 403 | } 404 | 405 | print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION)) 406 | 407 | util.run_test_group(cjson_tests) 408 | 409 | for _, filename in ipairs(arg) do 410 | util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename }, 411 | true, { true }) 412 | end 413 | 414 | local pass, total = util.run_test_summary() 415 | 416 | if pass == total then 417 | print("==> Summary: all tests succeeded") 418 | else 419 | print(("==> Summary: %d/%d tests failed"):format(total - pass, total)) 420 | os.exit(1) 421 | end 422 | 423 | -- vi:ai et sw=4 ts=4: 424 | --------------------------------------------------------------------------------