├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cluster └── clustername.lua ├── common ├── datacenter │ ├── accountdc.lua │ ├── roomdc.lua │ └── userdc.lua ├── dbmgr.lua ├── dbsync.lua ├── entity │ ├── d_account.lua │ ├── d_room.lua │ ├── d_user.lua │ ├── d_user_custom.lua │ ├── s_config.lua │ └── s_roleinit.lua ├── entitybase │ ├── CommonEntity.lua │ ├── ConfigEntity.lua │ ├── Entity.lua │ ├── EntityFactory.lua │ ├── UserEntity.lua │ ├── UserMultiEntity.lua │ └── UserSingleEntity.lua ├── log.lua ├── mysqlpool.lua └── redispool.lua ├── doc ├── README.md ├── TIPS.md ├── TODO.md ├── general.md ├── guide.md ├── hall-with-game.md ├── login-hall.md ├── login.md ├── persist.md ├── piggy.md └── protocol.md ├── etc ├── config.game ├── config.hall ├── config.login └── gamelet.lua ├── game ├── agent.lua ├── gamelet │ └── chat │ │ └── chat.lua ├── gated.lua ├── main.lua ├── room.lua ├── roommanager.lua ├── rpc │ └── game.lua └── watchdog.lua ├── global ├── errno.lua ├── globaldef.lua ├── luaext.lua ├── preload.lua └── util.lua ├── hall ├── gated.lua ├── main.lua ├── msgagent.lua ├── roomproxy.lua └── rpc │ └── hall.lua ├── login ├── logind.lua └── main.lua ├── lualib-src ├── lua-log.c ├── lua-zlib.c └── pbc │ ├── .gitignore │ ├── .travis.yml │ ├── Android.mk │ ├── Makefile │ ├── README.md │ ├── binding │ ├── lua │ │ ├── Makefile │ │ ├── README.md │ │ ├── parser.lua │ │ ├── pbc-lua.c │ │ ├── protobuf.lua │ │ ├── test.lua │ │ ├── test2.lua │ │ └── testparser.lua │ └── lua53 │ │ ├── Makefile │ │ ├── pbc-lua53.c │ │ ├── protobuf.lua │ │ ├── protobuf.so │ │ └── test.lua │ ├── license.txt │ ├── pbc.h │ ├── pbc.sln │ ├── pbc.vcxproj │ ├── src │ ├── alloc.c │ ├── alloc.h │ ├── array.c │ ├── array.h │ ├── bootstrap.c │ ├── bootstrap.h │ ├── context.c │ ├── context.h │ ├── decode.c │ ├── descriptor.pbc.h │ ├── map.c │ ├── map.h │ ├── pattern.c │ ├── pattern.h │ ├── proto.c │ ├── proto.h │ ├── register.c │ ├── rmessage.c │ ├── stringpool.c │ ├── stringpool.h │ ├── varint.c │ ├── varint.h │ └── wmessage.c │ ├── test │ ├── addressbook.c │ ├── addressbook.proto │ ├── array.c │ ├── decode.c │ ├── descriptor.proto │ ├── float.c │ ├── float.proto │ ├── map.c │ ├── pattern.c │ ├── pbc.c │ ├── readfile.h │ ├── test.c │ ├── test.proto │ └── varint.c │ └── tool │ └── dump.c ├── lualib ├── loginserverx.lua ├── message.lua ├── message_map.lua ├── messageid.lua ├── msgserver0.lua ├── protobuf.lua ├── random.lua └── uuid.lua ├── protocol ├── game.pb ├── game.proto ├── hall.pb ├── hall.proto ├── netmsg.pb ├── netmsg.proto ├── protoc └── protogenpb.sh ├── redis ├── redis-server ├── redis1.conf ├── redis2.conf ├── redis3.conf ├── redis4.conf ├── redis5.conf ├── redis6.conf ├── redis7.conf └── redis8.conf ├── sh ├── game.sh ├── hall.sh ├── login.sh ├── pigy.sql └── redis.sh └── test ├── c_game.lua ├── c_hall.lua ├── c_login.lua ├── client_util.lua └── i_client.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | PLAT ?= linux 4 | SHARED := -fPIC --shared 5 | LUA_CLIB_PATH ?= luaclib 6 | 7 | CFLAGS = -g -O2 -Wall -Iskynet/3rd/lua/ 8 | 9 | LUA_CLIB = protobuf log 10 | 11 | all : \ 12 | $(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so) 13 | 14 | $(LUA_CLIB_PATH) : 15 | mkdir $(LUA_CLIB_PATH) 16 | 17 | $(LUA_CLIB_PATH)/protobuf.so : | $(LUA_CLIB_PATH) 18 | cd lualib-src/pbc && $(MAKE) lib && cd binding/lua53 && $(MAKE) && cd ../../../.. && cp lualib-src/pbc/binding/lua53/protobuf.so $@ 19 | 20 | $(LUA_CLIB_PATH)/log.so : lualib-src/lua-log.c | $(LUA_CLIB_PATH) 21 | $(CC) $(CFLAGS) $(SHARED) $^ -o $@ 22 | 23 | clean : 24 | cd skynet && $(MAKE) clean 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pigy 2 | 3 | see BLOG [post](http://codemacro.com/2017/12/12/chess-gameserver/) for details. 4 | -------------------------------------------------------------------------------- /cluster/clustername.lua: -------------------------------------------------------------------------------- 1 | login = "127.0.0.1:5000" 2 | hall = "127.0.0.1:5003" 3 | game1 = "127.0.0.1:6000" 4 | -------------------------------------------------------------------------------- /common/datacenter/accountdc.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local snax = require "skynet.snax" 3 | local EntityFactory = require "EntityFactory" 4 | 5 | local entAccount 6 | 7 | function init(...) 8 | entAccount = EntityFactory.get("d_account") 9 | entAccount:init() 10 | entAccount:load() 11 | end 12 | 13 | function exit(...) 14 | 15 | end 16 | 17 | function response.add(row) 18 | return entAccount:add(row) 19 | end 20 | 21 | function response.delete(row) 22 | return entAccount:delete(row) 23 | end 24 | 25 | function response.get(sdkid, pid) 26 | return entAccount:get(sdkid, pid) 27 | end 28 | 29 | function response.update(row) 30 | return entAccount:update(row) 31 | end 32 | 33 | function response.get_nextid() 34 | return entAccount:getNextId() 35 | end 36 | -------------------------------------------------------------------------------- /common/datacenter/roomdc.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local snax = require "skynet.snax" 3 | local EntityFactory = require "EntityFactory" 4 | 5 | local ent_room 6 | 7 | function init(...) 8 | ent_room = EntityFactory.get("d_room") 9 | ent_room:init() 10 | ent_room:load() 11 | end 12 | 13 | function exit(...) 14 | end 15 | 16 | function response.get_nextid() 17 | return ent_room:getNextId() 18 | end 19 | 20 | function response.add(row) 21 | return ent_room:add(row) 22 | end 23 | 24 | function response.delete(row) 25 | return ent_room:delete(row) 26 | end 27 | 28 | function response.get_all() 29 | return ent_room:getAll() 30 | end 31 | 32 | function response.get(rid) 33 | return ent_room:get(rid) 34 | end 35 | 36 | -------------------------------------------------------------------------------- /common/datacenter/userdc.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local snax = require "skynet.snax" 3 | local EntityFactory = require "EntityFactory" 4 | 5 | local entUser 6 | 7 | function init(...) 8 | entUser = EntityFactory.get("d_user") 9 | entUser:init() 10 | end 11 | 12 | function exit(...) 13 | end 14 | 15 | function response.load(uid) 16 | entUser:load(uid) 17 | end 18 | 19 | function response.ensure(uid, nick) 20 | entUser:load(uid) 21 | entUser:add({uid = uid, nick = nick}) 22 | end 23 | 24 | function response.getvalue(uid, key) 25 | return entUser:getValue(uid, key) 26 | end 27 | 28 | function response.setvalue(uid, key, value) 29 | return entUser:setValue(uid, key, value) 30 | end 31 | 32 | function response.add(row) 33 | return entUser:add(row) 34 | end 35 | 36 | function response.delete(row) 37 | return entUser:delete(row) 38 | end 39 | 40 | function response.get(uid) 41 | return entUser:get(uid) 42 | end 43 | -------------------------------------------------------------------------------- /common/dbsync.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | require "skynet.manager" 3 | 4 | local queue = {} 5 | 6 | local CMD = {} 7 | 8 | function CMD.start() 9 | end 10 | 11 | function CMD.stop() 12 | end 13 | 14 | function CMD.sync(sql) 15 | table.insert(queue, sql) 16 | end 17 | 18 | local function sync_impl() 19 | while true do 20 | for k, sql in pairs(queue) do 21 | LOG_DEBUG('execute sql:%s', sql) 22 | skynet.call("mysqlpool", "lua", "execute", sql) 23 | queue[k] = nil 24 | end 25 | skynet.sleep(500) 26 | end 27 | end 28 | 29 | skynet.start(function() 30 | skynet.dispatch("lua", function(session, source, cmd, ...) 31 | local f = assert(CMD[cmd], cmd .. "not found") 32 | skynet.retpack(f(...)) 33 | end) 34 | skynet.fork(sync_impl) 35 | skynet.register(SERVICE_NAME) 36 | end) 37 | 38 | -------------------------------------------------------------------------------- /common/entity/d_account.lua: -------------------------------------------------------------------------------- 1 | local CommonEntity = require "CommonEntity" 2 | 3 | local EntityType = class("d_account", CommonEntity) 4 | 5 | function EntityType:ctor() 6 | EntityType.super.ctor(self) 7 | self.tbname = "d_account" 8 | end 9 | 10 | return EntityType.new() 11 | -------------------------------------------------------------------------------- /common/entity/d_room.lua: -------------------------------------------------------------------------------- 1 | local CommonEntity = require "CommonEntity" 2 | 3 | local EntityType = class("d_room", CommonEntity) 4 | 5 | function EntityType:ctor() 6 | EntityType.super.ctor(self) 7 | self.tbname = "d_room" 8 | end 9 | 10 | return EntityType.new() 11 | -------------------------------------------------------------------------------- /common/entity/d_user.lua: -------------------------------------------------------------------------------- 1 | local UserSingleEntity = require "UserSingleEntity" 2 | 3 | local EntityType = class("d_user", UserSingleEntity) 4 | 5 | function EntityType:ctor() 6 | EntityType.super.ctor(self) 7 | self.tbname = "d_user" 8 | end 9 | 10 | return EntityType.new() 11 | -------------------------------------------------------------------------------- /common/entity/d_user_custom.lua: -------------------------------------------------------------------------------- 1 | local CommonEntity = require "CommonEntity" 2 | 3 | local EntityType = class("d_user_custom", CommonEntity) 4 | 5 | function EntityType:ctor() 6 | EntityType.super.ctor(self) 7 | self.tbname = "d_user" 8 | end 9 | 10 | return EntityType.new() 11 | -------------------------------------------------------------------------------- /common/entity/s_config.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinlynx/pigy/08755bae537f6e3fd32de3bf64237babf445e536/common/entity/s_config.lua -------------------------------------------------------------------------------- /common/entity/s_roleinit.lua: -------------------------------------------------------------------------------- 1 | local ConfigEntity = require "ConfigEntity" 2 | 3 | local EntityType = class("s_roleinit", ConfigEntity) 4 | 5 | function EntityType:ctor() 6 | EntityType.super.ctor(self) 7 | self.tbname = "s_roleinit" 8 | end 9 | 10 | return EntityType.new() 11 | -------------------------------------------------------------------------------- /common/entitybase/CommonEntity.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local Entity = require "Entity" 3 | 4 | -- CommonEntity 5 | local CommonEntity = class("CommonEntity", Entity) 6 | 7 | function CommonEntity:ctor() 8 | CommonEntity.super.ctor(self) 9 | self.type = 3 10 | end 11 | 12 | function CommonEntity:load() 13 | if table.empty(self.recordset) then 14 | local rs = skynet.call("dbmgr", "lua", "get_common", self.tbname) 15 | if rs then 16 | self.recordset = rs 17 | end 18 | end 19 | 20 | end 21 | 22 | function CommonEntity:add(row, nosync) 23 | local key = self:getKey(row) 24 | if self.recordset[key] then return end 25 | 26 | local id = row[self.pk] 27 | if not id or id == 0 then 28 | id = self:getNextId() 29 | row[self.pk] = id 30 | end 31 | 32 | local ret = skynet.call("dbmgr", "lua", "add", self.tbname, row, self.type, nosync) 33 | 34 | if ret then 35 | self.recordset[key] = row 36 | end 37 | 38 | return true 39 | end 40 | 41 | function CommonEntity:delete(row, nosync) 42 | local key = self:getKey(row) 43 | if not self.recordset[key] then return end 44 | 45 | local ret = skynet.call("dbmgr", "lua", "delete", self.tbname, row, self.type, nosync) 46 | 47 | if ret then 48 | self.recordset[key] = nil 49 | end 50 | 51 | return true 52 | end 53 | 54 | function CommonEntity:remove(row) 55 | local key = self:getKey(row) 56 | self.recordset[key] = nil 57 | return true 58 | end 59 | 60 | function CommonEntity:update(row, nosync) 61 | local key = self:getKey(row) 62 | if not self.recordset[key] then return end 63 | 64 | local ret = skynet.call("dbmgr", "lua", "update", self.tbname, row, self.type, nosync) 65 | 66 | if ret then 67 | for k, v in pairs(row) do 68 | self.recordset[key][k] = v 69 | end 70 | end 71 | 72 | return true 73 | end 74 | 75 | function CommonEntity:get(...) 76 | local t = { ... } 77 | assert(#t > 0) 78 | local key 79 | if #t == 1 then 80 | key = t[1] 81 | else 82 | key = "" 83 | for i = 1, #t do 84 | if i > 1 then 85 | key = key .. ":" 86 | end 87 | key = key .. tostring(t[i]) 88 | end 89 | end 90 | 91 | return self.recordset[key] or {} 92 | end 93 | 94 | function CommonEntity:getValue(id, field) 95 | local record = self:get(id) 96 | if record then 97 | return record[field] 98 | end 99 | end 100 | 101 | function CommonEntity:setValue(id, field, data) 102 | local record = {} 103 | record[self.pkfield] = id 104 | record[field] = data 105 | self:update(record) 106 | end 107 | 108 | function CommonEntity:getKey(row) 109 | local fields = string.split(self.key, ",") 110 | local key 111 | for i=1, #fields do 112 | if i == 1 then 113 | key = row[fields[i]] 114 | else 115 | key = key .. ":" .. row[fields[i]] 116 | end 117 | end 118 | 119 | return tonumber(key) or key 120 | end 121 | 122 | function CommonEntity:getAll( ) 123 | return self.recordset 124 | end 125 | 126 | return CommonEntity 127 | -------------------------------------------------------------------------------- /common/entitybase/ConfigEntity.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local Entity = require "Entity" 3 | 4 | -- 定义ConfigEntity类型 5 | local ConfigEntity = class("ConfigEntity", Entity) 6 | 7 | function ConfigEntity:ctor() 8 | ConfigEntity.super.ctor(self) 9 | self.type = 1 10 | end 11 | 12 | function ConfigEntity:init() 13 | 14 | end 15 | 16 | function ConfigEntity:load() 17 | --if table.empty(self.recordset) then 18 | local rs = skynet.call("dbmgr", "lua", "get_config", self.tbname) 19 | if rs then 20 | self.recordset = rs 21 | end 22 | --end 23 | end 24 | 25 | function ConfigEntity:get(...) 26 | local t = { ... } 27 | assert(#t > 0) 28 | local key 29 | if #t == 1 then 30 | key = t[1] 31 | else 32 | key = "" 33 | for i = 1, #t do 34 | if i > 1 then 35 | key = key .. ":" 36 | end 37 | key = key .. tostring(t[i]) 38 | end 39 | end 40 | 41 | return self.recordset[key] or {} 42 | end 43 | 44 | 45 | function ConfigEntity:getAll( ) 46 | return self.recordset 47 | end 48 | 49 | return ConfigEntity 50 | -------------------------------------------------------------------------------- /common/entitybase/Entity.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | -- 定义Entity类型 4 | local Entity = class("Entity") 5 | 6 | function Entity:ctor() 7 | self.recordset = {} -- 存放记录集 8 | setmetatable(self.recordset, { __mode = "k" }) 9 | self.tbname = "" -- 表名 10 | --self.sql = "" -- sql语句 11 | --self.tbschema = {} -- 表结构 12 | self.pk = "" -- 主键字段 13 | self.key = "" -- key 14 | self.indexkey = "" -- indexkey 15 | self.type = 0 -- 表类型:1、config,2、user,3、common 16 | end 17 | 18 | -- 获取redis下一个编号 19 | function Entity:getNextId() 20 | return do_redis({ "incr", self.tbname .. ":" .. self.pk }) 21 | end 22 | 23 | function Entity:init() 24 | self.pk, self.key, self.indexkey = skynet.call("dbmgr", "lua", "get_table_key", self.tbname, self.type) 25 | --self.tbschema = skynet.call("dbmgr", "lua", "get_schema", self.tbname) 26 | end 27 | 28 | return Entity 29 | -------------------------------------------------------------------------------- /common/entitybase/EntityFactory.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | local entities = {} -- 保存实体对象 3 | 4 | -- 工厂方法,获取具体对象,name为表名 5 | function M.get(name) 6 | if entities[name] then 7 | return entities[name] 8 | end 9 | 10 | local ent = require(name) 11 | entities[name] = ent 12 | return ent 13 | end 14 | 15 | return M 16 | -------------------------------------------------------------------------------- /common/entitybase/UserEntity.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local Entity = require "Entity" 3 | 4 | -- 定义UserEntity类型 5 | local UserEntity = class("UserEntity", Entity) 6 | 7 | function UserEntity:ctor() 8 | UserEntity.super.ctor(self) 9 | self.ismulti = false -- 是否多行记录 10 | self.type = 2 11 | end 12 | 13 | return UserEntity 14 | -------------------------------------------------------------------------------- /common/entitybase/UserMultiEntity.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local UserEntity = require "UserEntity" 3 | 4 | -- UserMultiEntity 5 | local UserMultiEntity = class("UserMultiEntity", UserEntity) 6 | 7 | -- self.recordset格式如下: 8 | --[[ 9 | { 10 | [uid1] = 11 | { 12 | [id1] = { field1 = 1, field2 = 2 } 13 | [id2] = { field1 = 1, field2 = 2 } 14 | }, 15 | [uid2] = 16 | { 17 | [id1] = { field1 = 1, field2 = 2 } 18 | [id2] = { field1 = 1, field2 = 2 } 19 | }, 20 | } 21 | --]] 22 | 23 | 24 | function UserMultiEntity:ctor() 25 | UserMultiEntity.super.ctor(self) 26 | self.ismulti = true -- 是否多行记录 27 | end 28 | 29 | -- 加载玩家数据 30 | function UserMultiEntity:load(uid) 31 | 32 | if not self.recordset[uid] then 33 | local rs = skynet.call("dbmgr", "lua", "get_user_multi", self.tbname, uid) 34 | if rs then 35 | self.recordset[uid] = rs 36 | end 37 | end 38 | end 39 | 40 | -- 将内存中的数据先同步回redis,再从redis加载到内存(该方法要不要待定) 41 | function UserMultiEntity:reload(uid) 42 | 43 | end 44 | 45 | -- 卸载玩家数据 46 | function UserMultiEntity:unload(uid) 47 | local rs = self.recordset[uid] 48 | if rs then 49 | for k, v in pairs(rs) do 50 | rs[k] = nil 51 | end 52 | 53 | self.recordset[uid] = nil 54 | 55 | -- 是否需要移除待定 56 | -- 从redis删除,但不删除mysql中的数据 57 | end 58 | end 59 | 60 | -- record,record,v形式table 61 | -- 内存中不存在,则添加,并同步到redis 62 | function UserMultiEntity:add(record, nosync) 63 | 64 | if not record.uid then return end 65 | 66 | local id = record[self.pk] 67 | if self.recordset[record.uid] and self.recordset[record.uid][id] then return end -- 记录已经存在,返回 68 | 69 | if not id or id == 0 then 70 | id = self:getNextId() 71 | record[self.pk] = id 72 | end 73 | 74 | local ret = skynet.call("dbmgr", "lua", "add", self.tbname, record, self.type, nosync) 75 | if ret then 76 | if not self.recordset[record.uid] then 77 | self.recordset[record.uid] = {} 78 | setmetatable(self.recordset[record.uid], { __mode = "k" }) 79 | end 80 | self.recordset[record.uid][id] = record 81 | end 82 | return ret,id 83 | end 84 | 85 | -- record中包含uid字段,record为k,v形式table 86 | -- 从内存中删除,并同步到redis 87 | function UserMultiEntity:delete(record, nosync) 88 | if not record.uid then return end 89 | 90 | local id = record[self.pk] 91 | if not self.recordset[record.uid] or not self.recordset[record.uid][id] then return end -- 记录不存在,返回 92 | 93 | local ret = skynet.call("dbmgr", "lua", "delete", self.tbname, record, self.type, nosync) 94 | 95 | if ret then 96 | self.recordset[record.uid][id] = nil 97 | end 98 | 99 | return ret 100 | end 101 | 102 | -- record中包含uid字段,record为k,v形式table 103 | -- 仅从内存中移除,但不同步到redis 104 | function UserMultiEntity:remove(record) 105 | if not record.uid then return end 106 | 107 | local id = record[self.pk] 108 | if not self.recordset[record.uid] or not self.recordset[record.uid][id] then return end -- 记录不存在,返回 109 | 110 | self.recordset[record.uid][id] = nil 111 | 112 | return true 113 | end 114 | 115 | -- record中包含uid字段,record为k,v形式table 116 | function UserMultiEntity:update(record, nosync) 117 | if not record.uid then return end 118 | 119 | local id = record[self.pk] 120 | 121 | if not self.recordset[record.uid] or not self.recordset[record.uid][id] then return end -- 记录不存在,返回 122 | 123 | local ret = skynet.call("dbmgr", "lua", "update", self.tbname, record, self.type, nosync) 124 | if ret then 125 | for k, v in pairs(record) do 126 | self.recordset[record.uid][id][k] = v 127 | end 128 | end 129 | 130 | return ret 131 | end 132 | 133 | -- 从内存中获取,如果不存在,说明是其他的离线玩家数据,则加载数据到redis 134 | function UserMultiEntity:get(uid, id, field) 135 | if not id and not field then 136 | return self:getMulti(uid) 137 | end 138 | 139 | local record 140 | if self.recordset[uid] then 141 | if not field then 142 | record = self.recordset[uid][id] or {} 143 | elseif type(field) == "string" then 144 | if not self.recordset[uid][id] then return end 145 | if self.recordset[uid][id] then 146 | record = self.recordset[uid][id][field] 147 | end 148 | elseif type(field) == "table" then 149 | local t = self.recordset[uid][id] 150 | if t then 151 | record = {} 152 | for i=1, #field do 153 | record[field[i]] = t[field[i]] 154 | end 155 | end 156 | end 157 | 158 | if record then return record end 159 | end 160 | 161 | -- 从redis获取,如果redis不存在,从mysql加载 162 | local orifield = field 163 | if type(field) == "string" then 164 | field = { field } 165 | end 166 | record = skynet.call("dbmgr", "lua", "get_user_multi", self.tbname, uid, id) 167 | if not table.empty(record) then 168 | if not self.recordset[uid] then 169 | self.recordset[uid] = {} 170 | setmetatable(self.recordset[uid], { __mode = "k" }) 171 | end 172 | self.recordset[uid][id] = record 173 | end 174 | 175 | if type(orifield) == "string" then 176 | return record[orifield] 177 | end 178 | 179 | return record 180 | end 181 | 182 | -- 获取单个字段的值,field为string,获取多个字段的值,field为table 183 | function UserMultiEntity:getValue(uid, id, field) 184 | local record = self:get(uid, id, field) 185 | if record then 186 | return record 187 | end 188 | end 189 | 190 | -- 成功返回true,失败返回false 191 | -- 设置单个字段的值,field为string,data为值,设置多个字段的值,field为key,value形式table,data为nil 192 | function UserMultiEntity:setValue(uid, id, field, value) 193 | local record = {} 194 | id = id or uid 195 | record["uid"] = uid 196 | record[self.pk] = id 197 | if value then 198 | record[field] = value 199 | else 200 | for k, v in pairs(field) do 201 | record[k] = v 202 | end 203 | end 204 | return self:update(record) 205 | end 206 | 207 | 208 | -- 内部接口 209 | -- 多行记录,根据uid返回所有行 210 | function UserMultiEntity:getMulti(uid) 211 | local rs = self.recordset[uid] 212 | 213 | if not rs then 214 | -- 从redis获取,如果redis不存在,从mysql加载 215 | rs = skynet.call("dbmgr", "lua", "get_user_multi", self.tbname, uid) 216 | 217 | self.recordset[uid] = rs 218 | end 219 | return rs 220 | end 221 | 222 | return UserMultiEntity 223 | -------------------------------------------------------------------------------- /common/entitybase/UserSingleEntity.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local UserEntity = require "UserEntity" 3 | 4 | -- UserSingleEntity 5 | local UserSingleEntity = class("UserSingleEntity", UserEntity) 6 | 7 | -- self.recordset格式如下: 8 | --[[ 9 | { 10 | [uid1] = { field1 = 1, field2 = 2 }, 11 | [uid2] = { field1 = 1, field2 = 2 } 12 | } 13 | --]] 14 | 15 | function UserSingleEntity:ctor() 16 | UserSingleEntity.super.ctor(self) 17 | self.ismulti = false -- 是否多行记录 18 | end 19 | 20 | -- 加载玩家数据 21 | function UserSingleEntity:load(uid) 22 | if not self.recordset[uid] then 23 | local record = skynet.call("dbmgr", "lua", "get_user_single", self.tbname, uid) 24 | if not table.empty(record) then 25 | self.recordset[uid] = record 26 | end 27 | end 28 | 29 | end 30 | 31 | -- 将内存中的数据先同步回redis,再从redis加载到内存(该方法要不要待定) 32 | function UserSingleEntity:reLoad(uid) 33 | 34 | end 35 | 36 | -- 卸载玩家数据 37 | function UserSingleEntity:unload(uid) 38 | local rs = self.recordset[uid] 39 | if rs then 40 | for k, v in pairs(rs) do 41 | rs[k] = nil 42 | end 43 | 44 | self.recordset[uid] = nil 45 | 46 | -- 是否需要移除待定 47 | -- 从redis删除,但不删除mysql中的数据 48 | end 49 | end 50 | 51 | -- record中包含uid字段(如果表主键是uid字段,不需要包含),record为k,v形式table 52 | -- 内存中不存在,则添加,并同步到redis 53 | function UserSingleEntity:add(record, nosync) 54 | if record.uid and self.recordset[record.uid] then return end -- 记录已经存在,返回 55 | 56 | local id = record[self.pk] 57 | if not id or id == 0 then 58 | id = self:getNextId() 59 | record[self.pk] = id 60 | end 61 | 62 | local ret = skynet.call("dbmgr", "lua", "add", self.tbname, record, self.type, nosync) 63 | if ret then 64 | self.recordset[record.uid] = record 65 | end 66 | 67 | return ret, record 68 | end 69 | 70 | -- record中包含uid字段,record为k,v形式table 71 | -- 从内存中删除,并同步到redis 72 | function UserSingleEntity:delete(record, nosync) 73 | if not record.uid then return end 74 | 75 | local ret = skynet.call("dbmgr", "lua", "delete", self.tbname, record, self.type, nosync) 76 | if ret then 77 | self.recordset[record.uid] = nil 78 | end 79 | 80 | return ret 81 | end 82 | 83 | -- record中包含uid字段,record为k,v形式table 84 | -- 仅从内存中移除,但不同步到redis 85 | function UserSingleEntity:remove(record) 86 | if not record.uid or not self.recordset[record.uid] then return end -- 记录不存在,返回 87 | self.recordset[record.uid] = nil 88 | 89 | return true 90 | end 91 | 92 | -- record中包含uid字段,record为k,v形式table 93 | function UserSingleEntity:update(record, nosync) 94 | if not record.uid or not self.recordset[record.uid] then return end -- 记录不存在,返回 95 | 96 | local ret = skynet.call("dbmgr", "lua", "update", self.tbname, record, self.type, nosync) 97 | if ret then 98 | for k, v in pairs(record) do 99 | self.recordset[record.uid][k] = v 100 | end 101 | end 102 | 103 | return ret 104 | end 105 | 106 | -- 从内存中获取,如果不存在,说明是其他的离线玩家数据,则加载数据到redis 107 | -- field为空,获取整行记录,返回k,v形式table 108 | -- field为字符串表示获取单个字段的值,如果字段不存在,返回nil 109 | -- field为一个数组形式table,表示获取数组中指定的字段的值,返回k,v形式table 110 | function UserSingleEntity:get(uid, field) 111 | -- 内存中存在 112 | local record 113 | 114 | if self.recordset[uid] then 115 | if not field then 116 | record = self.recordset[uid] 117 | elseif type(field) == "string" then 118 | record = self.recordset[uid][field] 119 | elseif type(field) == "table" then 120 | record = {} 121 | for i=1, #field do 122 | local t = self.recordset[uid] 123 | record[field[i]] = t[field[i]] 124 | end 125 | end 126 | return record 127 | end 128 | 129 | -- 从redis获取,如果redis不存在,从mysql加载 130 | local orifield = field 131 | if type(field) == "string" then 132 | field = { field } 133 | end 134 | record = skynet.call("dbmgr", "lua", "get_user_single", self.tbname, uid) --不存在返回空的table {} 135 | if not table.empty(record) then 136 | self.recordset[uid] = record 137 | 138 | if type(orifield) == "string" then 139 | return record[orifield] 140 | end 141 | end 142 | 143 | if table.empty(record) and type(orifield) == "string" then return end 144 | 145 | return record 146 | end 147 | 148 | -- field为字符串表示获取单个字段的值 149 | -- field为一个数组形式table,表示获取数组中指定的字段的值,返回k,v形式table 150 | function UserSingleEntity:getValue(uid, field) 151 | if not field then return end 152 | local record = self:get(uid, field) 153 | if record then 154 | return record 155 | end 156 | end 157 | 158 | -- 成功返回true,失败返回false或nil 159 | -- 设置单个字段的值,field为字符串,value为值 160 | -- 设置多个字段的值,field为k,v形式table,value为空 161 | function UserSingleEntity:setValue(uid, field, value) 162 | local record = {} 163 | record["uid"] = uid 164 | if value then 165 | record[field] = value 166 | else 167 | for k, v in pairs(field) do 168 | record[k] = v 169 | end 170 | end 171 | 172 | return self:update(record) 173 | end 174 | 175 | return UserSingleEntity 176 | -------------------------------------------------------------------------------- /common/log.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | require "skynet.manager" 3 | local logger = require "log.core" 4 | 5 | local CMD = {} 6 | 7 | function CMD.start() 8 | logger.init(tonumber(skynet.getenv("log_level")) or 0, 9 | tonumber(skynet.getenv("log_rollsize")) or 1024, 10 | tonumber(skynet.getenv("log_flushinterval")) or 5, 11 | skynet.getenv("log_dirname") or "log", 12 | skynet.getenv("log_basename") or "test") 13 | end 14 | 15 | function CMD.stop( ) 16 | logger.exit() 17 | end 18 | 19 | function CMD.debug(name, msg) 20 | logger.debug(string.format("%s [%s] %s",os.date("%Y-%m-%d %H:%M:%S"), name, msg)) 21 | end 22 | 23 | function CMD.info(name, msg) 24 | logger.info(string.format("%s [%s] %s",os.date("%Y-%m-%d %H:%M:%S"), name, msg)) 25 | end 26 | 27 | function CMD.warning(name, msg) 28 | logger.warning(string.format("%s [%s] %s",os.date("%Y-%m-%d %H:%M:%S"), name, msg)) 29 | end 30 | 31 | function CMD.error(name, msg) 32 | logger.error(string.format("%s [%s] %s",os.date("%Y-%m-%d %H:%M:%S"), name, msg)) 33 | end 34 | 35 | function CMD.fatal(name, msg) 36 | logger.fatal(string.format("%s [%s] %s",os.date("%Y-%m-%d %H:%M:%S"), name, msg)) 37 | end 38 | 39 | skynet.start(function() 40 | skynet.dispatch("lua", function(session, source, cmd, ...) 41 | local f = assert(CMD[cmd], cmd .. "not found") 42 | if cmd == "start" or cmd == "stop" then 43 | skynet.retpack(f(...)) 44 | else 45 | f(...) 46 | end 47 | end) 48 | 49 | skynet.register(SERVICE_NAME) 50 | end) 51 | -------------------------------------------------------------------------------- /common/mysqlpool.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | require "skynet.manager" 3 | local mysql = require "skynet.db.mysql" 4 | 5 | local CMD = {} 6 | local pool = {} 7 | 8 | local maxconn 9 | local index = 2 10 | local function getconn(sync) 11 | local db 12 | if sync then 13 | db = pool[1] 14 | else 15 | db = pool[index] 16 | assert(db) 17 | index = index + 1 18 | if index > maxconn then 19 | index = 2 20 | end 21 | end 22 | return db 23 | end 24 | 25 | function CMD.start() 26 | maxconn = tonumber(skynet.getenv("mysql_maxconn")) or 10 27 | assert(maxconn >= 2) 28 | for i = 1, maxconn do 29 | local db = mysql.connect{ 30 | host = skynet.getenv("mysql_host"), 31 | port = tonumber(skynet.getenv("mysql_port")), 32 | database = skynet.getenv("mysql_db"), 33 | user = skynet.getenv("mysql_user"), 34 | password = skynet.getenv("mysql_pwd"), 35 | max_packet_size = 1024 * 1024 36 | } 37 | if db then 38 | table.insert(pool, db) 39 | db:query("set charset utf8") 40 | else 41 | skynet.error("mysql connect error") 42 | end 43 | end 44 | end 45 | 46 | -- sync为false或者nil,sql为读操作,如果sync为true用于数据变动时同步数据到mysql,sql为写操作 47 | -- 写操作取连接池中的第一个连接进行操作 48 | function CMD.execute(sql, sync) 49 | local db = getconn(sync) 50 | return db:query(sql) 51 | end 52 | 53 | function CMD.stop() 54 | for _, db in pairs(pool) do 55 | db:disconnect() 56 | end 57 | pool = {} 58 | end 59 | 60 | skynet.start(function() 61 | skynet.dispatch("lua", function(session, source, cmd, ...) 62 | local f = assert(CMD[cmd], cmd .. "not found") 63 | skynet.retpack(f(...)) 64 | end) 65 | 66 | skynet.register(SERVICE_NAME) 67 | end) 68 | -------------------------------------------------------------------------------- /common/redispool.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | require "skynet.manager" 3 | local redis = require "skynet.db.redis" 4 | 5 | local CMD = {} 6 | local pool = {} 7 | 8 | local maxconn 9 | local function getconn(uid) 10 | local db 11 | if not uid or maxconn == 1 then 12 | db = pool[1] 13 | else 14 | db = pool[uid % (maxconn - 1) + 2] 15 | end 16 | 17 | return db 18 | end 19 | 20 | function CMD.start() 21 | maxconn = tonumber(skynet.getenv("redis_maxinst")) or 2 22 | for i = 1, maxconn do 23 | local db = redis.connect{ 24 | host = skynet.getenv("redis_host" .. i), 25 | port = skynet.getenv("redis_port" .. i), 26 | db = 0, 27 | auth = skynet.getenv("redis_auth" .. i), 28 | } 29 | 30 | if db then 31 | db:flushdb() --测试期,清理redis数据 32 | table.insert(pool, db) 33 | else 34 | skynet.error("redis connect error") 35 | end 36 | end 37 | end 38 | 39 | function CMD.set(uid, key, value) 40 | local db = getconn(uid) 41 | local retsult = db:set(key,value) 42 | 43 | return retsult 44 | end 45 | 46 | function CMD.get(uid, key) 47 | local db = getconn(uid) 48 | local retsult = db:get(key) 49 | 50 | return retsult 51 | end 52 | 53 | function CMD.hmset(uid, key, t) 54 | local data = {} 55 | for k, v in pairs(t) do 56 | table.insert(data, k) 57 | table.insert(data, v) 58 | end 59 | 60 | local db = getconn(uid) 61 | local result = db:hmset(key, table.unpack(data)) 62 | 63 | return result 64 | end 65 | 66 | function CMD.hmget(uid, key, ...) 67 | if not key then return end 68 | 69 | local db = getconn(uid) 70 | local result = db:hmget(key, ...) 71 | 72 | return result 73 | end 74 | 75 | function CMD.hset(uid, key, filed, value) 76 | local db = getconn(uid) 77 | local result = db:hset(key,filed,value) 78 | 79 | return result 80 | end 81 | 82 | function CMD.hget(uid, key, filed) 83 | local db = getconn(uid) 84 | local result = db:hget(key, filed) 85 | 86 | return result 87 | end 88 | 89 | function CMD.hgetall(uid, key) 90 | local db = getconn(uid) 91 | local result = db:hgetall(key) 92 | 93 | return result 94 | end 95 | 96 | function CMD.zadd(uid, key, score, member) 97 | local db = getconn(uid) 98 | local result = db:zadd(key, score, member) 99 | 100 | return result 101 | end 102 | 103 | function CMD.zrem(uid, key, member) 104 | local db = getconn(uid) 105 | local result = db:zrem(key, member) 106 | return result 107 | end 108 | 109 | function CMD.keys(uid, key) 110 | local db = getconn(uid) 111 | local result = db:keys(key) 112 | 113 | return result 114 | 115 | end 116 | 117 | function CMD.zrange(uid, key, from, to) 118 | local db = getconn(uid) 119 | local result = db:zrange(key, from, to) 120 | 121 | return result 122 | end 123 | 124 | function CMD.zrevrange(uid, key, from, to ,scores) 125 | local result 126 | local db = getconn(uid) 127 | if not scores then 128 | result = db:zrevrange(key,from,to) 129 | else 130 | result = db:zrevrange(key,from,to,scores) 131 | end 132 | 133 | return result 134 | end 135 | 136 | function CMD.zrank(uid, key, member) 137 | local db = getconn(uid) 138 | local result = db:zrank(key,member) 139 | 140 | return result 141 | end 142 | 143 | function CMD.zrevrank(uid, key, member) 144 | local db = getconn(uid) 145 | local result = db:zrevrank(key,member) 146 | 147 | return result 148 | end 149 | 150 | function CMD.zscore(uid, key, score) 151 | local db = getconn(uid) 152 | local result = db:zscore(key,score) 153 | 154 | return result 155 | end 156 | 157 | function CMD.zcount(uid, key, from, to) 158 | local db = getconn(uid) 159 | local result = db:zcount(key,from,to) 160 | 161 | return result 162 | end 163 | 164 | function CMD.zcard(uid, key) 165 | local db = getconn(uid) 166 | local result = db:zcard(key) 167 | 168 | return result 169 | end 170 | 171 | function CMD.incr(uid, key) 172 | local db = getconn(uid) 173 | local result = db:incr(key) 174 | 175 | return result 176 | end 177 | 178 | function CMD.del(uid, key) 179 | local db = getconn(uid) 180 | local result = db:del(key) 181 | 182 | return result 183 | end 184 | 185 | skynet.start(function() 186 | skynet.dispatch("lua", function(session, source, cmd, ...) 187 | local f = assert(CMD[cmd], cmd .. " not found") 188 | skynet.retpack(f(...)) 189 | end) 190 | 191 | skynet.register(SERVICE_NAME) 192 | end) 193 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # 如何编写文档 2 | 3 | * 文档应尽量简洁、简单,甚至简陋也行。设计文档很容易面临变化,简单的文档方便维护。 4 | * 文档最好只表达框架或指引性质的信息,用以方便阅读者通过代码做细节的探索 5 | * 讨论性质的东西可以作为草稿文档,草稿文档没有参考价值,仅做历史记录 6 | * 讨论性质的文档可以考虑用issue表达 7 | * 最好的文档是代码 8 | -------------------------------------------------------------------------------- /doc/TIPS.md: -------------------------------------------------------------------------------- 1 | * msgserver 中的cache在session相同时,可能导致消息错乱 2 | * skynet中call一个不存在服务会导致阻塞 3 | * snax.newservice name 传错也会导致阻塞 4 | -------------------------------------------------------------------------------- /doc/TODO.md: -------------------------------------------------------------------------------- 1 | * logout 2 | * ~~login/hall互为备份recover用户登录状态~~ 3 | * ~~client是否只需要配置一个login地址,不需要hall地址;支持多login/hall~~ 4 | * 梳理所有服务重入的地方,可能由于服务重入引入bug 5 | * ~~load balancing to select a game server to create room~~ 6 | * ~~load balancing to select a hall server~~ 7 | * ~~destroy room~~ 8 | * ~~leave room~~ 9 | * game server ping 10 | * ~~进入hall后获取用户数据,包括可能已经进入的房间信息。如果有房间信息则重新进入~~ 11 | * ~~data persist~~ 12 | * game framework 13 | * 全服广播通知 14 | * benchmark 15 | * ~~重新进入game server房间~~ 16 | * 断线重连问题 17 | * msgserver本身带了基于session的缓存,client进程退出后,应该重走登陆流程,而不是直接与hall重连,因此此时session号重置会导致收到很多缓存回应;client因为网络不稳定时才直接与hall重连,此时可以理解为hall将之前可能丢失的消息重发给client,但这也是基于session,这还要求client能记录哪些session需要重新请求 18 | * gameserver 重连问题,是否也需要基于断线重连协议 19 | * ~~select room~~ 20 | * ~~目前对游戏的定义是有问题的,玩家进入game应该是创建了房间,房间内可以开启若干次具体游戏~~ 21 | * ~~game server 中使用fd去代表玩家,关联游戏是有问题的,无法处理断线重连的问题~~ 22 | * ~~目前的网络协议依靠字符串来自动route到rpc服务,有点浪费网络带宽~~ 23 | * 交互式客户端,测试全流程 24 | * ~~persist login state and relogin~~ 25 | * 各个server挂掉后的异常测试 26 | * login重启后由于hall有登陆记录导致无法登陆 27 | * hall挂掉后: a) client自动重连失败;b) client重启登陆正常 28 | * server状态梳理,确认是否无状态 29 | * login主要是logind中的online user 30 | * hall主要是msgserver中的online user及msgagent 31 | * reconnect to game server 32 | * 异常流程测试 33 | * 玩家重复登录 34 | 35 | -------------------------------------------------------------------------------- /doc/general.md: -------------------------------------------------------------------------------- 1 | # servers 2 | 3 | * login server,包装skynet loginserver为独立进程 4 | * hall server,大厅服务,负责大厅所有逻辑,负责分配玩家到具体游戏服务器 5 | * game server,具体的游戏服务 6 | * redis,充当全局配置管理,及缓存角色。有了全局配置管理,login/hall可以实现为无状态服务 7 | 8 | ## 连接关系 9 | 10 | * hall 启动注册到redis,hall有唯一配置名字 11 | * login 在处理登陆时负载均衡玩家到某个hall 12 | * login 主要保存了uid到hall的映射,该数据缓存到redis,即使服务挂掉重启亦可恢复,且支持多instance 13 | * hall 需要缓存管理的user列表,在挂掉恢复时重新载入该列表并登记到skynet msgserver,当玩家自主重登时可以正确处理 14 | * login & hall 基于skynet loginserver/msgserver,以支持玩家断线重连 15 | * game 启动时写入redis 游戏id及桌子列表 16 | * hall 在接收玩家选择游戏桌号时找到对应的game server,准备移动玩家进游戏 17 | * 玩家进入game后与hall断开连接,整个系统并不基于连接,玩家状态的清除由玩家显示登出驱动 18 | * 每一局游戏有一个guid,玩家持久化该guid,游戏guid关联game server信息,重连时如果无法正确进入game则回到hall 19 | * 如果需要考虑guid的非法截获,就需要将玩家登陆session信息与guid关联 20 | 21 | ## 框架与游戏 22 | 23 | 交互消息: 24 | 25 | * 进入桌子,坐下 26 | 27 | 框架接口: 28 | 29 | * 获取某座位玩家信息 30 | * 向指定座位玩家发送消息 31 | * 广播房间内消息 32 | * 持久化玩家数据 33 | * 定时器 34 | 35 | ## 缓存设计 36 | 37 | ## 持久化数据结构设计 38 | 39 | ## 断线重连 40 | 41 | * 游戏过程中断线 42 | * 大厅里断线 43 | 44 | game上保存有所有玩家,client经过hall进入game前,hall会传递玩家当前的secret(联系msgserver的handshake)过去。game上用msgserver生成的username(目前是uid+server+subid)作为玩家标识。client连接game时,需要先如同连接hall时发送handshake。game以handshake作为合法性校验。 45 | 46 | 如果client与game断线,由于一般也会与hall断线,所以统一重连hall。hall需要持久化当前玩家所在的游戏及server信息。hall查询该信息通知client重连game。连接game无论重连还是首次连接处理相同,发送handshake。如果与hall并未断开连接,此时可以主动断开。如果client进程退出,重新开启后亦可以之前的handshake进入hall,相继进入game。 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /doc/guide.md: -------------------------------------------------------------------------------- 1 | ## startup 2 | 3 | ``` 4 | ./sh/redis.sh # start 1 redis instance 5 | ./sh/login.sh 6 | ./sh/hall.sh 7 | ./sh/game.sh 8 | ``` 9 | 10 | to run the test client: 11 | 12 | ``` 13 | ./skynet/3rd/lua/lua test/i_client.lua 14 | ``` 15 | 16 | ## hall与game 17 | 18 | config.hall中新增配置 19 | 20 | ``` 21 | gameservers = 'game1;game2' 22 | ``` 23 | 24 | 表示hall会与该配置中所有game进行交互,轮询其上的游戏列表,并负载均衡选择game创建房间。game名字对应skynet cluster map name。 25 | 26 | ## login与hall 27 | 28 | config.login中配置hall列表: 29 | 30 | ``` 31 | hallservers='hall1;hall2' 32 | ``` 33 | 34 | ## redis 35 | 36 | reids instance 1用于存放通用数据。其他redis目前会根据uid hash,分别存放用户的数据。 37 | 38 | -------------------------------------------------------------------------------- /doc/hall-with-game.md: -------------------------------------------------------------------------------- 1 | # Hall与Game的交互 2 | 3 | ## 定义 4 | 5 | * 房间;单个game可以静态或动态配置房间列表。房间有属性,如:人数、关联的游戏类型、金额统计等。房间可以由GM或玩家创建。房间需要持久化 6 | * 游戏:依附于房间内 7 | 8 | ## 游戏列表获取 9 | 10 | Game需配置自己支持的游戏列表。Game在启动时会载入所有自己关联的房间列表,如果某房间关联游戏不在该Game内,该房间本次载入失败。游戏列表配置: 11 | 12 | ``` 13 | [ 14 | {name: 'chat', id: 101} 15 | ] 16 | ``` 17 | 18 | Hall向Game请求所有房间列表,并以游戏id聚合,client就可以按游戏展现房间列表。 19 | 20 | ``` 21 | [ 22 | { 23 | 'GameID': 101, 24 | 'Rooms': [ 25 | { 26 | 'ID': 1, 27 | 'State': 0, 28 | 'PlayerCount': 3, 29 | 'Extra': '' 30 | } 31 | ] 32 | } 33 | ] 34 | ``` 35 | 36 | `Extra` 扩展信息一般是客户端与Game的协商结果。Hall端在一轮询问结束后更新本地的游戏状态,该状态也会同步到客户端。 37 | 38 | ## 进入房间 39 | 40 | 流程: 41 | 42 | * 玩家选择游戏,Client展示房间列表 43 | * 玩家创建房间或选择房间进入 44 | 45 | 系统: 46 | 47 | * Client -> Hall: 创建房间并进入;选择房间进入 48 | * [创建时] Hall -> Game: 创建房间,返回房间id 49 | * Hall -> Game: 玩家登记,关联Room与玩家 50 | * Client -> Game: handshake,Game验证Client 51 | * Client -> Game: 进入房间 52 | 53 | ## 断线重连 54 | 55 | 进程未结束,网络异常: 56 | 57 | * Client -> Hall: handshake,基于msgserver重连进入 58 | * Hall: 检查玩家是否有关联的房间,有则进入 59 | * Client -> Game: handshake 60 | * Client -> Game: 进入房间 61 | 62 | 进程结束: 63 | 64 | * 走完整登陆流程 65 | 66 | ## 房间生命周期 67 | 68 | * room id以数据库主键为准,全局唯一 69 | * hall通知game创建 70 | * 用户选择房间进入,并关联房间id持久化 71 | * owner client可以发消息结束房间 72 | * game有接口由游戏逻辑决定结束房间 73 | * room结束需广播消息给所有用户 74 | * room结束后需从持久化数据中移除,可放置到历史room表中 75 | * user离开room需要更新user关联的room 76 | -------------------------------------------------------------------------------- /doc/login-hall.md: -------------------------------------------------------------------------------- 1 | ## client 2 | 3 | 在不引入gate的情况下,login可以对外暴露并绑定域名。client解析域名获取login列表,随机选择login或按三方登陆token hash login。 4 | 5 | client配置hall地址列表是不对的,会面临难以更新的窘境。 6 | 7 | ## login/hall error 8 | 9 | login/hall针对在线用户状态,互为备份。login可以定时ping hall。当发现hall上的数据丢失时,意味着hall发生了重启,此时对login记录的所有用户发起hall上的登陆操作。这个ping操作同样可用于login从hall恢复login自身的在线用户数据。 10 | 11 | 另一种容灾方式就是用redis,将状态数据同步到redis上,在重启后从redis恢复。 12 | -------------------------------------------------------------------------------- /doc/login.md: -------------------------------------------------------------------------------- 1 | ## 登陆 2 | 3 | login实现了3个接口: 4 | 5 | * register,用于添加账号 6 | * login,登陆验证 7 | * bind,绑定游客账号 8 | 9 | 对应到3种账号体系: 10 | 11 | * 第三方平台认证,直接尝试登陆,失败则以三方平台token自动注册 12 | * 自有账号体系,客户端引导用户注册 13 | * 游客账号,直接尝试登陆,失败则以机器标识注册。 14 | * 可以绑定三方平台token到游客账号 15 | 16 | 账号数据库至少包含信息: 17 | 18 | * uid 19 | * pid, 三方平台token; 注册账号; 机器标识 20 | * sdkid, 账号类型:1: 自有账号;2: 游客;; 10+: 三方平台 21 | * password, 除了自有账号外都为空 22 | 23 | 24 | 客户端登陆验证: 25 | 26 | ``` 27 | h = desencode(username, password) || 28 | desencode(3rd_sdk_token, '') || 29 | desencode(machine_id, '') 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /doc/persist.md: -------------------------------------------------------------------------------- 1 | 2 | ## 功能设计 3 | 4 | 需要持久化数据: 5 | 6 | * room 7 | * user 8 | * account 9 | 10 | 数据持久化在目前的memory+redis+mysql三层结构中,mysql持久化基本上只是数据的备份,在服务器挂掉重启后载入恢复使用。 11 | 12 | ### Room 13 | 14 | room表仅存储当前使用中的房间。已结束的房间可存储到历史表中。hall启动时载入所有room到redis,并去game上ensure该room存在。room需要有扩展状态,根据不同游戏状态内容不一样。 15 | 16 | ## 框架相关 17 | 18 | * Entity (recordset, table) 19 | * UserEntity / UserSingleEntity / d_user 20 | * CommEntity / d_account 21 | * datacenter (service) 22 | * accountdc 23 | * userdc 24 | * dbmgr (load from redis first) 25 | 26 | ``` 27 | recordset[k] = v 28 | k = ','.join(map(lambda k: row[key], config.key.split(','))) 29 | 相当于取一行记录中的关键值作为key 30 | ``` 31 | 32 | ``` 33 | UserSingleEntity:load(uid) 34 | call dbmgr to load a user by uid, store it in self.recordset 35 | ``` 36 | 37 | * config 38 | * columns (db table columns) 39 | * name (db table name) 40 | * key (for redis) 41 | * indexkey (for redis) 42 | 43 | ``` 44 | uid用作redis分partition 45 | 46 | insert row: 47 | rediskey = tbname..row[key] 48 | hmset(rediskey, row): HMSET rediskey k1 v1 k2 v2 # 一行记录对应redis一个哈希表 49 | zadd(tbname..':index:'..row[indexkey], rediskey) # 对所有rediskey做索引 50 | ``` 51 | 52 | 以上,由于表的每一行都作为redis中的一个独立hashmap,当尝试载入整张表数据时,由于并不知道整张表有多少数据,所以`zadd`添加的有序集合,实际就表示了该表里所有行记录的key,基于该key可以进一步从redis中查出完整的db行数据。 53 | 54 | 表类型: 55 | 56 | * config,应该是一些特别轻量的表,类似配置,数据很少 57 | * common,相对数据更多,但也可以全量载入redis,例如账号 58 | * user,不能全量载入,在使用时根据uid载入,其中根据uid是否重复分为单行和多行 59 | 60 | dbsync 服务其实只是一个sql语句缓存执行队列,可以让sql操作异步化。 61 | 62 | 整体上结构是3级缓存:本地内存、redis、mysql 63 | 64 | -------------------------------------------------------------------------------- /doc/piggy.md: -------------------------------------------------------------------------------- 1 | # 全局 2 | - 1.交互协议,除开登录开始的几个特殊场合用文本交互协议外,其余均采用2bytes+protobuf 3 | - 2.直面客户端的只有gaeway和login。 4 | - 3.服务器内部消息通讯,服务器内部采用lua消息?客户端的protobuf在gaeway解码后直接call hall或者game,还是直接转发这个probobuf,支持直接转发吗? 5 | - 4.数据,包含两部分数据,一部分是永久性数据,如玩家账号信息,玩家金币、各个游戏数据,这部分需要及时存储到mysql, 6 | 另外一部分就是房间内的数据,这部分需要支持断线重连,放入redis,不必存入数据库,如果宕机掉了就掉了。 7 | - 5.游戏内部如何表示用户+gateway+tcp连接,并以此来进行(msgserver?)?game或者hall需要有一个具体的玩家对象,玩家登录时记录。 8 | 9 | # 网关服务器 (gateway,拟采用skynet的gateserver,或者msgserver?) 10 | - 1.和客户端直连,作为客户端的登陆点,由登录服负载选择gateway给客户端 11 | - 2.负责包转发、协议加解密等 12 | - 3.管理大量客户端连接 13 | 14 | # 登录服 (login) 15 | - 1.注册(需要支持游客账户) 16 | - 2.绑定(将一个游客账号绑定到一个特定的账号,可以是自定义用户名密码,也可以根据第三方的token生成用户名密码) 17 | - 3.登录(沿用skynet的loginserver,这里要做真实用户名密码的验证,加在token里即可) 18 | 19 | # 大厅服务器 (hall) 20 | - 1.具体游戏管理,考虑每个game启动时向大厅报告自己的游戏信息,比如游戏类型、房间编号。 21 | - 2.游戏房间管理,房间可用游戏id+游戏房间号组成全局唯一id 22 | - 3.玩家游戏数据管理,登录时拉取到,进入game时到大厅询问,game及时同步到大厅,大厅定时保存到mysql。 23 | - 4.gm命令支持(有可能需要后台改玩家的某些概率之类的内容) 24 | 25 | # 游戏服务器(game) 26 | - 1.负责具体游戏逻辑 27 | - 2.向大厅及时同步每个房间的信息,可能直接存到redis,大厅拉即可 28 | - 3.某些游戏数据记录,考虑直接存mysql(可能需要记录每一局游戏各个玩家的输赢啊等等作为历史记录,以供查询分析) 29 | 30 | # TODO 31 | - 1.具体协议梳理文档,以便客户端沟通 32 | - 2.登录和gateway完善 -------------------------------------------------------------------------------- /doc/protocol.md: -------------------------------------------------------------------------------- 1 | # 说明 2 | 3 | 目前client与server的协议已经基于数字,通过一个中间层来映射数字到rpc。参考`messageid.lua`及`protocol/*.proto` 4 | 5 | # 具体协议 6 | 7 | 主要列举与客户端的协议 8 | 9 | ## 进入hall 10 | 11 | * C2H, handshake 12 | * C2H, get user 13 | * if user.has_room then 14 | * C2H, reconn room 15 | * C2G, 参考进入Game 16 | * C2H, get room&game list 17 | 18 | ## 选择房间 19 | 20 | * C2H, select room 21 | * C2G, 参考进入Game 22 | 23 | ## 创建房间 24 | 25 | * C2H, create room 26 | * C2G, 参考进入Game 27 | 28 | ## 离开房间 29 | 30 | * C2H, leave room 31 | 32 | ## 销毁房间 33 | 34 | * C2H, destroy room 35 | 36 | ## 进入Game 37 | 38 | * C2G, handshake 39 | * C2G, enter game 40 | 41 | ## 重连 42 | 43 | * 进程退出, 完整登陆流程 44 | * 大厅内断线,基于msgserver handshake hall重连。hall重发缓存消息 45 | * 游戏内断线 46 | * 全量重连:正常进入游戏逻辑,获取游戏全量状态 47 | * 增量重连:需要game支持断线重连协议,补发部分消息 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /etc/config.game: -------------------------------------------------------------------------------- 1 | skynetroot = "./skynet/" 2 | thread = 8 3 | logger = nil 4 | logpath = "." 5 | harbor = 0 6 | start = "main" -- main script 7 | bootstrap = "snlua bootstrap" -- The service for bootstrap 8 | 9 | debug_port = 8004 10 | 11 | cluster = "./cluster/clustername.lua" 12 | nodename = "game1" 13 | 14 | log_dirname = "log" 15 | log_basename = "game" 16 | 17 | gameservice = "./game/?.lua;./game/rpc/?.lua;" .. 18 | "./common/?.lua;" .. 19 | "./common/datacenter/?.lua" 20 | 21 | luaservice = skynetroot .. "service/?.lua;" .. gameservice 22 | snax = gameservice .. ';./game/gamelet/chat/?.lua;' 23 | 24 | gamelet = './etc/gamelet.lua' 25 | 26 | lualoader = skynetroot .. "lualib/loader.lua" 27 | preload = "./global/preload.lua" -- run preload.lua before every lua service run 28 | 29 | cpath = skynetroot .. "cservice/?.so" 30 | 31 | lua_path = skynetroot .. "lualib/?.lua;" .. 32 | "./lualib/?.lua;" .. 33 | "./global/?.lua;" .. 34 | "./common/entitybase/?.lua;" .. 35 | "./common/entity/?.lua" 36 | 37 | lua_cpath = skynetroot .. "luaclib/?.so;" .. "./luaclib/?.so" 38 | 39 | --daemon = "./game.pid" 40 | 41 | ap_ip = '10.101.83.237' 42 | port = 5190 43 | maxclient = 5000 44 | 45 | mysql_maxconn = 10 46 | mysql_host = "$PIGY_MYSQL_HOST" 47 | mysql_port = $PIGY_MYSQL_PORT 48 | mysql_db = "$PIGY_MYSQL_DB" 49 | mysql_user = "$PIGY_MYSQL_USER" 50 | mysql_pwd = "$PIGY_MYSQL_PWD" 51 | 52 | redis_maxinst = 1 53 | 54 | redis_host1 = "127.0.0.1" 55 | redis_port1 = 6379 56 | redis_auth1 = "123456" 57 | -------------------------------------------------------------------------------- /etc/config.hall: -------------------------------------------------------------------------------- 1 | skynetroot = "./skynet/" 2 | thread = 8 3 | logger = nil 4 | logpath = "." 5 | harbor = 0 6 | start = "main" -- main script 7 | bootstrap = "snlua bootstrap" -- The service for bootstrap 8 | 9 | debug_port = 8003 10 | 11 | cluster = "./cluster/clustername.lua" 12 | nodename = "hall" 13 | 14 | log_dirname = "log" 15 | log_basename = "hall" 16 | 17 | hallservice = "./hall/?.lua;./hall/rpc/?.lua;" .. 18 | "./common/?.lua;" .. 19 | "./common/datacenter/?.lua" 20 | 21 | luaservice = skynetroot .. "service/?.lua;" .. hallservice 22 | snax = hallservice 23 | 24 | lualoader = skynetroot .. "lualib/loader.lua" 25 | preload = "./global/preload.lua" -- run preload.lua before every lua service run 26 | 27 | cpath = skynetroot .. "cservice/?.so" 28 | 29 | lua_path = skynetroot .. "lualib/?.lua;" .. 30 | "./lualib/?.lua;" .. 31 | "./global/?.lua;" .. 32 | "./common/entitybase/?.lua;" .. 33 | "./common/entity/?.lua" 34 | 35 | lua_cpath = skynetroot .. "luaclib/?.so;" .. "./luaclib/?.so" 36 | 37 | gameservers = 'game1' 38 | 39 | --daemon = "./hall.pid" 40 | 41 | ap_ip = '127.0.0.1' 42 | port = 5189 43 | maxclient = 5000 44 | 45 | mysql_maxconn = 10 46 | mysql_host = "$PIGY_MYSQL_HOST" 47 | mysql_port = $PIGY_MYSQL_PORT 48 | mysql_db = "$PIGY_MYSQL_DB" 49 | mysql_user = "$PIGY_MYSQL_USER" 50 | mysql_pwd = "$PIGY_MYSQL_PWD" 51 | 52 | redis_maxinst = 1 53 | 54 | redis_host1 = "127.0.0.1" 55 | redis_port1 = 6379 56 | redis_auth1 = "123456" 57 | -------------------------------------------------------------------------------- /etc/config.login: -------------------------------------------------------------------------------- 1 | skynetroot = "./skynet/" 2 | thread = 8 3 | logger = nil 4 | logpath = "." 5 | harbor = 0 6 | start = "main" -- main script 7 | bootstrap = "snlua bootstrap" -- The service for bootstrap 8 | 9 | cluster = "./cluster/clustername.lua" 10 | hallservers = "hall" 11 | 12 | log_dirname = "log" 13 | log_basename = "login" 14 | 15 | loginservice = "./login/?.lua;" .. 16 | "./common/?.lua;" .. 17 | "./common/datacenter/?.lua" 18 | 19 | luaservice = skynetroot .. "service/?.lua;" .. loginservice 20 | -- snax standard services 21 | snax = loginservice 22 | 23 | lualoader = skynetroot .. "lualib/loader.lua" 24 | preload = "./global/preload.lua" -- run preload.lua before every lua service run 25 | 26 | cpath = skynetroot .. "cservice/?.so" 27 | 28 | lua_path = skynetroot .. "lualib/?.lua;" .. 29 | "./lualib/?.lua;" .. 30 | "./global/?.lua;" .. 31 | "./common/entitybase/?.lua;" .. 32 | "./common/entity/?.lua" 33 | 34 | lua_cpath = skynetroot .. "luaclib/?.so;" .. "./luaclib/?.so" 35 | 36 | --daemon = "./login.pid" 37 | 38 | port = $PIGY_PORT 39 | 40 | mysql_maxconn = 10 41 | mysql_host = "$PIGY_MYSQL_HOST" 42 | mysql_port = $PIGY_MYSQL_PORT 43 | mysql_db = "$PIGY_MYSQL_DB" 44 | mysql_user = "$PIGY_MYSQL_USER" 45 | mysql_pwd = "$PIGY_MYSQL_PWD" 46 | 47 | redis_maxinst = 1 48 | 49 | redis_host1 = "127.0.0.1" 50 | redis_port1 = 6379 51 | redis_auth1 = "123456" 52 | 53 | -------------------------------------------------------------------------------- /etc/gamelet.lua: -------------------------------------------------------------------------------- 1 | return { 2 | {id = 1, name = 'chat'}, 3 | } 4 | -------------------------------------------------------------------------------- /game/agent.lua: -------------------------------------------------------------------------------- 1 | -- agent.lua 2 | local skynet = require "skynet" 3 | local message = require 'message' 4 | local protobuf = require 'protobuf' 5 | local socketdriver = require 'skynet.socketdriver' 6 | local netpack = require 'skynet.netpack' 7 | local crypt = require 'skynet.crypt' 8 | 9 | local client_fd 10 | local WATCHDOG 11 | local user 12 | local handshake 13 | 14 | local CMD = {} 15 | 16 | local function unpack_message(msg, sz) 17 | if handshake then 18 | return message.unpack(msg, sz) 19 | end 20 | handshake = true 21 | local data = skynet.tostring(msg, sz) 22 | local secret, username = data:match "([^@]*)@(.*)" 23 | return {secret = secret, username = username}, true 24 | end 25 | 26 | local function auth(username, secret) 27 | local u 28 | if not username or not secret then 29 | err = '400 Bad Request' 30 | else 31 | secret = crypt.base64decode(secret) 32 | u = skynet.call('roommanager', 'lua', 'get_user', username) 33 | if not u then 34 | err = '404 User Not Found' 35 | elseif u.secret ~= secret then 36 | err = '401 Unauthorized' 37 | end 38 | end 39 | if err then 40 | socketdriver.send(client_fd, netpack.pack(err)) 41 | skynet.call(WATCHDOG, 'lua', 'close', client_fd) 42 | return 43 | end 44 | user = u 45 | user.fd = client_fd 46 | socketdriver.send(client_fd, netpack.pack('200 OK')) 47 | LOG_INFO('user [%s:%d] handshake success', user.username, user.userid) 48 | end 49 | 50 | skynet.register_protocol { 51 | name = "client", 52 | id = skynet.PTYPE_CLIENT, 53 | unpack = unpack_message, 54 | -- NOTE: skynet will dispatch in more than 1 coroutine ? 55 | dispatch = function (_, _, netmsg, t) 56 | if t then 57 | return auth(netmsg.username, netmsg.secret) 58 | end 59 | message.dispatch_game(netmsg, user) 60 | end 61 | } 62 | 63 | function CMD.start(conf) 64 | local fd = conf.client 65 | local gate = conf.gate 66 | WATCHDOG = conf.watchdog 67 | client_fd = fd 68 | skynet.call(gate, "lua", "forward", fd) 69 | end 70 | 71 | function CMD.disconnect() 72 | -- todo: do something before exit 73 | skynet.exit() 74 | end 75 | 76 | skynet.start(function() 77 | message.mapping('game') 78 | skynet.dispatch("lua", function(_,_, command, ...) 79 | local f = CMD[command] 80 | skynet.ret(skynet.pack(f(...))) 81 | end) 82 | protobuf.register_file("./protocol/netmsg.pb") 83 | end) 84 | -------------------------------------------------------------------------------- /game/gamelet/chat/chat.lua: -------------------------------------------------------------------------------- 1 | -- chat.lua 2 | -- a simple test chat game 3 | local socket = require 'skynet.socket' 4 | require 'skynet.netpack' 5 | local table = require 'table' 6 | 7 | local room 8 | 9 | function init(source) 10 | room = source 11 | LOG_INFO('game chat associated to room %d', room) 12 | end 13 | 14 | function exit(...) 15 | end 16 | 17 | local function send_msg(fd, resp) 18 | local pack = string.pack('>I2', #resp) .. resp 19 | socket.write(fd, pack) 20 | end 21 | 22 | -- when user enter this game 23 | function accept.enter(user) 24 | LOG_INFO('user %d enter chat', user.userid) 25 | send_msg(user.fd, 'welcome to chat game: ' .. tostring(user.userid)) 26 | end 27 | 28 | function accept.leave(user) 29 | LOG_INFO('user %d leave chat', user.userid) 30 | end 31 | 32 | -- recv client game message 33 | function accept.play(user, detail) 34 | LOG_INFO('user %d play chat: %s', user.userid, detail) 35 | local resp = 'hello:' .. detail 36 | send_msg(user.fd, resp) 37 | end 38 | 39 | -------------------------------------------------------------------------------- /game/gated.lua: -------------------------------------------------------------------------------- 1 | -- gated.lua 2 | local msgserver = require "snax.msgserver" 3 | local crypt = require "skynet.crypt" 4 | local skynet = require "skynet" 5 | 6 | local loginservice = tonumber(...) 7 | 8 | local server = {} 9 | local users = {} 10 | local username_map = {} 11 | local internal_id = 0 12 | 13 | -- login server disallow multi login, so login_handler never be reentry 14 | -- call by login server 15 | function server.login_handler(uid, secret) 16 | if users[uid] then 17 | error(string.format("%s is already login", uid)) 18 | end 19 | 20 | internal_id = internal_id + 1 21 | local id = internal_id -- don't use internal_id directly 22 | local username = msgserver.username(uid, id, servername) 23 | 24 | -- you can use a pool to alloc new agent 25 | local agent = skynet.newservice "msgagent" 26 | local u = { 27 | username = username, 28 | agent = agent, 29 | uid = uid, 30 | subid = id, 31 | } 32 | 33 | -- trash subid (no used) 34 | skynet.call(agent, "lua", "login", uid, id, secret) 35 | 36 | users[uid] = u 37 | username_map[username] = u 38 | 39 | msgserver.login(username, secret) 40 | 41 | -- you should return unique subid 42 | return id 43 | end 44 | 45 | -- call by agent 46 | function server.logout_handler(uid, subid) 47 | local u = users[uid] 48 | if u then 49 | local username = msgserver.username(uid, subid, servername) 50 | assert(u.username == username) 51 | msgserver.logout(u.username) 52 | users[uid] = nil 53 | username_map[u.username] = nil 54 | skynet.call(loginservice, "lua", "logout",uid, subid) 55 | end 56 | end 57 | 58 | -- call by login server 59 | function server.kick_handler(uid, subid) 60 | local u = users[uid] 61 | if u then 62 | local username = msgserver.username(uid, subid, servername) 63 | assert(u.username == username) 64 | -- NOTICE: logout may call skynet.exit, so you should use pcall. 65 | pcall(skynet.call, u.agent, "lua", "logout") 66 | end 67 | end 68 | 69 | -- call by self (when socket disconnect) 70 | function server.disconnect_handler(username) 71 | local u = username_map[username] 72 | if u then 73 | skynet.call(u.agent, "lua", "afk") 74 | end 75 | end 76 | 77 | -- call by self (when recv a request from client) 78 | function server.request_handler(username, msg) 79 | local u = username_map[username] 80 | return skynet.tostring(skynet.rawcall(u.agent, "client", msg)) 81 | end 82 | 83 | -- call by self (when gate open) 84 | function server.register_handler(name) 85 | servername = name 86 | skynet.call(loginservice, "lua", "register_gate", servername, skynet.self()) 87 | end 88 | 89 | msgserver.start(server) 90 | 91 | -------------------------------------------------------------------------------- /game/main.lua: -------------------------------------------------------------------------------- 1 | -- main.lua 2 | local skynet = require "skynet" 3 | local snax = require "skynet.snax" 4 | local cluster = require "skynet.cluster" 5 | 6 | skynet.start(function() 7 | local log = skynet.uniqueservice("log") 8 | skynet.call(log, "lua", "start") 9 | 10 | skynet.newservice("debug_console", tonumber(skynet.getenv("debug_port"))) 11 | 12 | local gm = skynet.uniqueservice('roommanager') 13 | skynet.call(gm, 'lua', 'start') 14 | 15 | local gate = skynet.uniqueservice("watchdog") 16 | skynet.call(gate, "lua", "start" , { 17 | port = tonumber(skynet.getenv("port")) or 8888, 18 | maxclient = tonumber(skynet.getenv("maxclient")) or 1024, 19 | nodelay = true, 20 | }) 21 | cluster.open(NODE_NAME) 22 | end) 23 | -------------------------------------------------------------------------------- /game/room.lua: -------------------------------------------------------------------------------- 1 | -- room.lua 2 | local skynet = require 'skynet' 3 | local snax = require 'skynet.snax' 4 | 5 | local gid, game 6 | local users = {} 7 | local roomid 8 | 9 | local command = {} 10 | 11 | function command.start(id, gamelet) 12 | roomid = id 13 | gid = gamelet.id 14 | LOG_INFO('start room %d for game %d:%s', id, gid, gamelet.name) 15 | local ok, obj = pcall(snax.newservice, gamelet.name, skynet.self()) 16 | game = obj 17 | end 18 | 19 | function command.user_enter(user) 20 | LOG_INFO('user %d enter room %d', user.userid, roomid) 21 | users[user.username] = user 22 | game.post.enter(user) 23 | end 24 | 25 | function command.user_leave(user) 26 | game.post.leave(user) 27 | users[user.username] = nil 28 | end 29 | 30 | function command.get_users() 31 | return users 32 | end 33 | 34 | function command.destroyable() 35 | return table.empty(users) 36 | end 37 | 38 | function command.destroy() 39 | -- TODO: if destroyed by force, it should broadcast a destroyed messge to all users 40 | skynet.exit() 41 | end 42 | 43 | function command.get_info() 44 | return {id = roomid, gid = gid, user_count = table.size(users)} 45 | end 46 | 47 | function command.play_game(user, detail) 48 | game.post.play(user, detail) 49 | end 50 | 51 | skynet.start(function() 52 | skynet.dispatch("lua", function(session, address, cmd, ...) 53 | local f = command[cmd] 54 | if f then 55 | skynet.ret(skynet.pack(f(...))) 56 | else 57 | error(string.format("Unknown command %s", tostring(cmd))) 58 | end 59 | end) 60 | end) 61 | -------------------------------------------------------------------------------- /game/roommanager.lua: -------------------------------------------------------------------------------- 1 | -- roommanager.lua 2 | -- manage room list 3 | local skynet = require 'skynet' 4 | local snax = require 'skynet.snax' 5 | require 'skynet.manager' 6 | local table = require 'table' 7 | 8 | local gamelets = {} 9 | local room_map = {} -- room_id: room 10 | local user2rooms = {} 11 | local username_map = {} -- usernmae: user 12 | 13 | local command = {} 14 | 15 | function command.start() 16 | local fn, err = loadfile(skynet.getenv('gamelet')) 17 | if not fn then 18 | error('load gamelet config failed:' .. err) 19 | end 20 | local conf = fn() 21 | for _, g in ipairs(conf) do 22 | gamelets[g.id] = g 23 | LOG_INFO('register game %d:%s', g.id, g.name) 24 | end 25 | end 26 | 27 | -- called by hall 28 | function command.get() 29 | --[[ 30 | {gid: [{id: room_id, sname: server_name}]} 31 | --]] 32 | -- TODO: this IP can be used with a gate server ? 33 | local server = {ip = skynet.getenv('ap_ip'), port = skynet.getenv('port')} 34 | local game_infos = {} 35 | for id, _ in pairs(gamelets) do game_infos[id] = {} end 36 | for _, room in pairs(room_map) do 37 | local info = skynet.call(room, 'lua', 'get_info') 38 | table.insert(game_infos[info.gid], info) 39 | end 40 | return {game_infos = game_infos, server = server} 41 | end 42 | 43 | local function user_checkin(user) 44 | LOG_INFO('user checkin server: %d:%s:%s', user.userid, user.username, user.secret) 45 | username_map[user.username] = user 46 | end 47 | 48 | command.user_checkin = user_checkin 49 | 50 | function command.get_user(username) 51 | return username_map[username] 52 | end 53 | 54 | local function new_room(rid, gid) 55 | local room = skynet.newservice('room') 56 | skynet.call(room, 'lua', 'start', rid, {id = gid, name = gamelets[gid].name}) 57 | room_map[rid] = room 58 | return true 59 | end 60 | 61 | -- called by hall 62 | function command.create_room(id, user, rid) 63 | user_checkin(user) 64 | new_room(rid, id) 65 | LOG_INFO('create a new room for game %d by user %d', id, user.userid) 66 | return rid 67 | end 68 | 69 | -- called by hall 70 | function command.destroy_room(id, user) 71 | local room = room_map[id] 72 | if not room then 73 | return 404 74 | end 75 | LOG_INFO('destroy a room %d by user %d', id, user.userid) 76 | local f = skynet.call(room, 'lua', 'destroyable') 77 | if not f then 78 | return 403 79 | end 80 | room_map[id] = nil 81 | skynet.send(room, 'lua', 'destroy') 82 | return 200 83 | end 84 | 85 | -- called by hall 86 | function command.select_room(id, user) 87 | if room_map[id] == nil then 88 | return 404 89 | end 90 | user_checkin(user) 91 | return 200 92 | end 93 | 94 | -- called by hall, usually recover from db 95 | function command.ensure_room(row) 96 | local id = row.id 97 | if room_map[id] ~= nil then 98 | return true 99 | end 100 | new_room(row.id, row.game) 101 | LOG_INFO('ensure room %d for game %d', id, row.game) 102 | return true 103 | end 104 | 105 | -- called by client 106 | function command.enter_room(user, rid) 107 | local room = room_map[rid] 108 | if not room then 109 | LOG_WARNING('not found room:%d', rid) 110 | return 111 | end 112 | user2rooms[user.userid] = rid 113 | skynet.call(room, 'lua', 'user_enter', user) 114 | end 115 | 116 | -- called by hall 117 | function command.leave_room(user) 118 | local rid = user2rooms[user.userid] 119 | if not rid then 120 | LOG_WARNING('not found room for user %d', user.userid) 121 | return 404 122 | end 123 | LOG_INFO('user leave room, uid %d, rid %d', user.userid, rid) 124 | local room = room_map[rid] 125 | assert(room) 126 | skynet.call(room, 'lua', 'user_leave', user) 127 | user2rooms[user.userid] = nil 128 | username_map[user.username] = nil 129 | return 200 130 | end 131 | 132 | function command.play_game(user, detail) 133 | local rid = user2rooms[user.userid] 134 | local room = room_map[rid] 135 | skynet.send(room, 'lua', 'play_game', user, detail) 136 | end 137 | 138 | skynet.start(function() 139 | skynet.dispatch("lua", function(session, address, cmd, ...) 140 | local f = command[cmd] 141 | if f then 142 | skynet.ret(skynet.pack(f(...))) 143 | else 144 | error(string.format("Unknown command %s", tostring(cmd))) 145 | end 146 | end) 147 | skynet.register('roommanager') 148 | end) 149 | 150 | -------------------------------------------------------------------------------- /game/rpc/game.lua: -------------------------------------------------------------------------------- 1 | -- rpc/game.lua 2 | local skynet = require 'skynet' 3 | local protobuf = require "protobuf" 4 | local message = require 'message' 5 | 6 | function init(...) 7 | protobuf.register_file("./protocol/netmsg.pb") 8 | protobuf.register_file("./protocol/game.pb") 9 | end 10 | 11 | function exit(...) 12 | end 13 | 14 | function accept.EnterRoom(data, user) 15 | LOG_DEBUG('rpc recv EnterRoom from user %d', user.userid) 16 | local args = pb_decode(data) 17 | local rid = args.rid 18 | skynet.send('roommanager', 'lua', 'enter_room', user, rid) 19 | end 20 | 21 | function accept.PlayGame(data, user) 22 | local args = pb_decode(data) 23 | skynet.send('roommanager', 'lua', 'play_game', user, args.detail) 24 | end 25 | 26 | -------------------------------------------------------------------------------- /game/watchdog.lua: -------------------------------------------------------------------------------- 1 | -- watchdog.lua 2 | local skynet = require "skynet" 3 | 4 | local CMD = {} 5 | local SOCKET = {} 6 | local gate 7 | local agent = {} 8 | 9 | function SOCKET.open(fd, addr) 10 | LOG_INFO("New client from : " .. addr) 11 | agent[fd] = skynet.newservice("agent") 12 | skynet.call(agent[fd], "lua", "start", { gate = gate, client = fd, watchdog = skynet.self() }) 13 | end 14 | 15 | local function close_agent(fd) 16 | local a = agent[fd] 17 | agent[fd] = nil 18 | if a then 19 | skynet.call(gate, "lua", "kick", fd) 20 | -- disconnect never return 21 | skynet.send(a, "lua", "disconnect") 22 | end 23 | end 24 | 25 | function SOCKET.close(fd) 26 | LOG_INFO("socket close %d", fd) 27 | close_agent(fd) 28 | end 29 | 30 | function SOCKET.error(fd, msg) 31 | LOG_WARNING("socket error %d %s", fd, msg) 32 | close_agent(fd) 33 | end 34 | 35 | function SOCKET.warning(fd, size) 36 | -- size K bytes havn't send out in fd 37 | print("socket warning", fd, size) 38 | end 39 | 40 | function SOCKET.data(fd, msg) 41 | end 42 | 43 | function CMD.start(conf) 44 | skynet.call(gate, "lua", "open" , conf) 45 | end 46 | 47 | function CMD.close(fd) 48 | close_agent(fd) 49 | end 50 | 51 | skynet.start(function() 52 | skynet.dispatch("lua", function(session, source, cmd, subcmd, ...) 53 | if cmd == "socket" then 54 | local f = SOCKET[subcmd] 55 | f(...) 56 | -- socket api don't need return 57 | else 58 | local f = assert(CMD[cmd]) 59 | skynet.ret(skynet.pack(f(subcmd, ...))) 60 | end 61 | end) 62 | 63 | gate = skynet.newservice("gate") 64 | end) 65 | -------------------------------------------------------------------------------- /global/errno.lua: -------------------------------------------------------------------------------- 1 | ErrorCode = { 2 | E_SUCCESS = 0, -- 成功 3 | E_ROLE_EXISTS = 1, -- 玩家角色已存在 4 | E_DB_ERROR = 2, -- 数据库操作失败 5 | E_ROLE_NOT_EXISTS = 3, -- 玩家角色不存在 6 | } 7 | -------------------------------------------------------------------------------- /global/globaldef.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | NODE_NAME = skynet.getenv("nodename") or "noname" 4 | -------------------------------------------------------------------------------- /global/luaext.lua: -------------------------------------------------------------------------------- 1 | -- lua扩展 2 | 3 | -- table扩展 4 | 5 | -- 返回table大小 6 | table.size = function(t) 7 | local count = 0 8 | for _ in pairs(t) do 9 | count = count + 1 10 | end 11 | return count 12 | end 13 | 14 | -- 判断table是否为空 15 | table.empty = function(t) 16 | return not next(t) 17 | end 18 | 19 | -- 返回table索引列表 20 | table.indices = function(t) 21 | local result = {} 22 | for k, v in pairs(t) do 23 | table.insert(result, k) 24 | end 25 | return result 26 | end 27 | 28 | -- 返回table值列表 29 | table.values = function(t) 30 | local result = {} 31 | for k, v in pairs(t) do 32 | table.insert(result, v) 33 | end 34 | return result 35 | end 36 | 37 | -- 浅拷贝 38 | table.clone = function(t, nometa) 39 | local result = {} 40 | if not nometa then 41 | setmetatable(result, getmetatable(t)) 42 | end 43 | for k, v in pairs (t) do 44 | result[k] = v 45 | end 46 | return result 47 | end 48 | 49 | -- 深拷贝 50 | table.copy = function(t, nometa) 51 | local result = {} 52 | 53 | if not nometa then 54 | setmetatable(result, getmetatable(t)) 55 | end 56 | 57 | for k, v in pairs(t) do 58 | if type(v) == "table" then 59 | result[k] = copy(v) 60 | else 61 | result[k] = v 62 | end 63 | end 64 | return result 65 | end 66 | 67 | table.merge = function(dest, src) 68 | for k, v in pairs(src) do 69 | dest[k] = v 70 | end 71 | end 72 | 73 | -- string扩展 74 | 75 | -- 下标运算 76 | do 77 | local mt = getmetatable("") 78 | local _index = mt.__index 79 | 80 | mt.__index = function (s, ...) 81 | local k = ... 82 | if "number" == type(k) then 83 | return _index.sub(s, k, k) 84 | else 85 | return _index[k] 86 | end 87 | end 88 | end 89 | 90 | string.split = function(s, delim) 91 | local split = {} 92 | local pattern = "[^" .. delim .. "]+" 93 | string.gsub(s, pattern, function(v) table.insert(split, v) end) 94 | return split 95 | end 96 | 97 | string.ltrim = function(s, c) 98 | local pattern = "^" .. (c or "%s") .. "+" 99 | return (string.gsub(s, pattern, "")) 100 | end 101 | 102 | string.rtrim = function(s, c) 103 | local pattern = (c or "%s") .. "+" .. "$" 104 | return (string.gsub(s, pattern, "")) 105 | end 106 | 107 | string.trim = function(s, c) 108 | return string.rtrim(string.ltrim(s, c), c) 109 | end 110 | 111 | local function dump(obj) 112 | local getIndent, quoteStr, wrapKey, wrapVal, dumpObj 113 | getIndent = function(level) 114 | return string.rep("\t", level) 115 | end 116 | quoteStr = function(str) 117 | return '"' .. string.gsub(str, '"', '\\"') .. '"' 118 | end 119 | wrapKey = function(val) 120 | if type(val) == "number" then 121 | return "[" .. val .. "]" 122 | elseif type(val) == "string" then 123 | return "[" .. quoteStr(val) .. "]" 124 | else 125 | return "[" .. tostring(val) .. "]" 126 | end 127 | end 128 | wrapVal = function(val, level) 129 | if type(val) == "table" then 130 | return dumpObj(val, level) 131 | elseif type(val) == "number" then 132 | return val 133 | elseif type(val) == "string" then 134 | return quoteStr(val) 135 | else 136 | return tostring(val) 137 | end 138 | end 139 | dumpObj = function(obj, level) 140 | if type(obj) ~= "table" then 141 | return wrapVal(obj) 142 | end 143 | level = level + 1 144 | local tokens = {} 145 | tokens[#tokens + 1] = "{" 146 | for k, v in pairs(obj) do 147 | tokens[#tokens + 1] = getIndent(level) .. wrapKey(k) .. " = " .. wrapVal(v, level) .. "," 148 | end 149 | tokens[#tokens + 1] = getIndent(level - 1) .. "}" 150 | return table.concat(tokens, "\n") 151 | end 152 | return dumpObj(obj, 0) 153 | end 154 | 155 | do 156 | local _tostring = tostring 157 | tostring = function(v) 158 | if type(v) == 'table' then 159 | return dump(v) 160 | else 161 | return _tostring(v) 162 | end 163 | end 164 | end 165 | 166 | -- math扩展 167 | do 168 | local _floor = math.floor 169 | math.floor = function(n, p) 170 | if p and p ~= 0 then 171 | local e = 10 ^ p 172 | return _floor(n * e) / e 173 | else 174 | return _floor(n) 175 | end 176 | end 177 | end 178 | 179 | math.round = function(n, p) 180 | local e = 10 ^ (p or 0) 181 | return math.floor(n * e + 0.5) / e 182 | end 183 | 184 | 185 | -- lua面向对象扩展 186 | function class(classname, super) 187 | local superType = type(super) 188 | local cls 189 | 190 | if superType ~= "function" and superType ~= "table" then 191 | superType = nil 192 | super = nil 193 | end 194 | 195 | if superType == "function" or (super and super.__ctype == 1) then 196 | -- inherited from native C++ Object 197 | cls = {} 198 | 199 | if superType == "table" then 200 | -- copy fields from super 201 | for k,v in pairs(super) do cls[k] = v end 202 | cls.__create = super.__create 203 | cls.super = super 204 | else 205 | cls.__create = super 206 | cls.ctor = function() end 207 | end 208 | 209 | cls.__cname = classname 210 | cls.__ctype = 1 211 | 212 | function cls.new(...) 213 | local instance = cls.__create(...) 214 | -- copy fields from class to native object 215 | for k,v in pairs(cls) do instance[k] = v end 216 | instance.class = cls 217 | instance:ctor(...) 218 | return instance 219 | end 220 | 221 | else 222 | -- inherited from Lua Object 223 | if super then 224 | cls = {} 225 | setmetatable(cls, {__index = super}) 226 | cls.super = super 227 | else 228 | cls = {ctor = function() end} 229 | end 230 | 231 | cls.__cname = classname 232 | cls.__ctype = 2 -- lua 233 | cls.__index = cls 234 | 235 | function cls.new(...) 236 | local instance = setmetatable({}, cls) 237 | instance.class = cls 238 | instance:ctor(...) 239 | return instance 240 | end 241 | end 242 | 243 | return cls 244 | end 245 | 246 | function iskindof(obj, classname) 247 | local t = type(obj) 248 | local mt 249 | if t == "table" then 250 | mt = getmetatable(obj) 251 | elseif t == "userdata" then 252 | mt = tolua.getpeer(obj) 253 | end 254 | 255 | while mt do 256 | if mt.__cname == classname then 257 | return true 258 | end 259 | mt = mt.super 260 | end 261 | 262 | return false 263 | end 264 | -------------------------------------------------------------------------------- /global/preload.lua: -------------------------------------------------------------------------------- 1 | require "luaext" 2 | require "util" 3 | require "globaldef" 4 | require "errno" -------------------------------------------------------------------------------- /global/util.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local protobuf = require "protobuf" 3 | local socketdriver = require "skynet.socketdriver" 4 | 5 | function do_redis(args, uid) 6 | local cmd = assert(args[1]) 7 | args[1] = uid 8 | return skynet.call("redispool", "lua", cmd, table.unpack(args)) 9 | end 10 | 11 | function LOG_DEBUG(fmt, ...) 12 | local msg = string.format(fmt, ...) 13 | local info = debug.getinfo(2) 14 | if info then 15 | msg = string.format("[%s:%d] %s", info.short_src, info.currentline, msg) 16 | end 17 | skynet.send("log", "lua", "debug", SERVICE_NAME, msg) 18 | return msg 19 | end 20 | 21 | function LOG_INFO(fmt, ...) 22 | local msg = string.format(fmt, ...) 23 | local info = debug.getinfo(2) 24 | if info then 25 | msg = string.format("[%s:%d] %s", info.short_src, info.currentline, msg) 26 | end 27 | skynet.send("log", "lua", "info", SERVICE_NAME, msg) 28 | return msg 29 | end 30 | 31 | function LOG_WARNING(fmt, ...) 32 | local msg = string.format(fmt, ...) 33 | local info = debug.getinfo(2) 34 | if info then 35 | msg = string.format("[%s:%d] %s", info.short_src, info.currentline, msg) 36 | end 37 | skynet.send("log", "lua", "warning", SERVICE_NAME, msg) 38 | return msg 39 | end 40 | 41 | function LOG_ERROR(fmt, ...) 42 | local msg = string.format(fmt, ...) 43 | local info = debug.getinfo(2) 44 | if info then 45 | msg = string.format("[%s:%d] %s", info.short_src, info.currentline, msg) 46 | end 47 | skynet.send("log", "lua", "error", SERVICE_NAME, msg) 48 | return msg 49 | end 50 | 51 | function LOG_FATAL(fmt, ...) 52 | local msg = string.format(fmt, ...) 53 | local info = debug.getinfo(2) 54 | if info then 55 | msg = string.format("[%s:%d] %s", info.short_src, info.currentline, msg) 56 | end 57 | skynet.send("log", "lua", "fatal", SERVICE_NAME, msg) 58 | return msg 59 | end 60 | 61 | function pb_encode(name, msg) 62 | local data = protobuf.encode(name, msg) 63 | if not data then 64 | LOG_ERROR("pb_encode error") 65 | end 66 | return data 67 | end 68 | 69 | function pb_decode(data) 70 | local msg = protobuf.decode(data.name, data.payload) 71 | if not msg then 72 | LOG_ERROR("pb_decode error") 73 | return 74 | end 75 | if data.uid then 76 | msg.uid = data.uid 77 | end 78 | return msg 79 | end 80 | 81 | function check( ... ) 82 | -- body 83 | end 84 | 85 | function check_user_online(uid) 86 | return skynet.call("gated", "lua", "is_online", uid) 87 | end 88 | 89 | function send_client(fd, proto, data) 90 | local payload = pb_encode(proto, data) 91 | local msg = protobuf.encode("netmsg.NetMsg", { name = proto, payload = payload }) 92 | if not msg then 93 | error("protobuf.encode error") 94 | end 95 | 96 | msg = msg .. string.pack(">BI4", 1, 9) 97 | msg = string.pack(">s2", msg) 98 | socketdriver.send(fd, msg) 99 | end 100 | -------------------------------------------------------------------------------- /hall/gated.lua: -------------------------------------------------------------------------------- 1 | -- gated.lua 2 | local msgserver = require "msgserver0" 3 | require 'skynet.manager' 4 | local crypt = require "skynet.crypt" 5 | local cluster = require "skynet.cluster" 6 | local skynet = require "skynet" 7 | 8 | local users = {} -- uid -> u 9 | local username_map = {} -- username -> u 10 | local internal_id = 0 -- disable multi login 11 | local starttm = skynet.time() 12 | 13 | local server = {} 14 | local CMD = {} 15 | 16 | local function do_login(uid, secret) 17 | local username = msgserver.username(uid, internal_id, NODE_NAME) 18 | local agent = skynet.newservice "msgagent" 19 | 20 | LOG_INFO('user (uid:%d) logined subid: %d, username: %s', uid, internal_id, username) 21 | 22 | local u = { 23 | username = username, 24 | agent = agent, 25 | uid = uid, 26 | subid = internal_id, 27 | secret = secret, 28 | } 29 | 30 | -- trash subid (no used) 31 | skynet.call(agent, "lua", "login", username, uid, internal_id, secret) 32 | 33 | users[uid] = u 34 | username_map[username] = u 35 | msgserver.login(username, secret) 36 | end 37 | 38 | -- login server disallow multi login, so login_handler never be reentry 39 | -- call by login server 40 | function server.login_handler(uid, secret) 41 | if users[uid] then 42 | local errmsg = string.format("%d is already login", uid) 43 | LOG_ERROR(errmsg) 44 | error(errmsg) 45 | end 46 | do_login(uid, secret) 47 | -- you should return unique subid 48 | return internal_id 49 | end 50 | 51 | -- call by agent 52 | function server.logout_handler(uid, subid) 53 | local u = users[uid] 54 | if u then 55 | local username = msgserver.username(uid, subid, NODE_NAME) 56 | assert(u.username == username) 57 | msgserver.logout(u.username) 58 | users[uid] = nil 59 | username_map[u.username] = nil 60 | pcall(cluster.call, "login", ".login_master", "logout", uid, subid) 61 | end 62 | end 63 | 64 | -- call by login server 65 | function server.kick_handler(uid, subid) 66 | local u = users[uid] 67 | if u then 68 | local username = msgserver.username(uid, subid, NODE_NAME) 69 | assert(u.username == username) 70 | -- NOTICE: logout may call skynet.exit, so you should use pcall. 71 | pcall(skynet.call, u.agent, "lua", "logout") 72 | else 73 | -- in case this server crashed and login server still keep the user 74 | pcall(cluster.call, "login", ".login_master", "logout", uid, subid) 75 | end 76 | end 77 | 78 | -- call by self (when socket disconnect) 79 | function server.disconnect_handler(username) 80 | local u = username_map[username] 81 | if u then 82 | skynet.call(u.agent, "lua", "afk") 83 | end 84 | end 85 | 86 | -- call by self (when recv a request from client) 87 | function server.request_handler(username, msg) 88 | local u = username_map[username] 89 | return skynet.tostring(skynet.rawcall(u.agent, "client", msg)) 90 | end 91 | 92 | -- call by self (when gate open) 93 | function server.register_handler(name) 94 | skynet.register(SERVICE_NAME) 95 | end 96 | 97 | function server.cmd_handler(cmd, source, ...) 98 | local f = assert(CMD[cmd]) 99 | return f(...) 100 | end 101 | 102 | local function get_simple_users() 103 | local s_users = {} 104 | for uid, user in pairs(users) do 105 | s_users[uid] = {secret = user.secret, subid = user.subid} 106 | end 107 | return s_users 108 | end 109 | 110 | function CMD.ping() 111 | local ap = skynet.getenv('ap_ip') .. ':' .. skynet.getenv('port') 112 | local ret = {ap = ap, handshake = starttm, users = get_simple_users()} 113 | return ret 114 | end 115 | 116 | function CMD.recover_login(uid, secret) 117 | LOG_INFO('recover user login [%d]', uid) 118 | -- TODO: this maybe happen when some user login at recoving process. 119 | assert(users[uid] == nil) 120 | do_login(uid, secret) 121 | return internal_id 122 | end 123 | 124 | msgserver.start(server) 125 | 126 | -------------------------------------------------------------------------------- /hall/main.lua: -------------------------------------------------------------------------------- 1 | -- main.lua 2 | local skynet = require "skynet" 3 | local snax = require "skynet.snax" 4 | local cluster = require "skynet.cluster" 5 | local protobuf = require 'protobuf' 6 | 7 | local common = { 8 | { name = "d_room", key = "id", indexkey = "id" }, 9 | } 10 | 11 | local user = { 12 | { name = 'd_user', key = 'uid'}, 13 | } 14 | 15 | skynet.start(function() 16 | local log = skynet.uniqueservice("log") 17 | skynet.call(log, "lua", "start") 18 | 19 | skynet.newservice("debug_console", tonumber(skynet.getenv("debug_port"))) 20 | 21 | local dbmgr = skynet.uniqueservice("dbmgr") 22 | skynet.call(dbmgr, "lua", "start", {}, user, common) 23 | 24 | skynet.call(skynet.uniqueservice('roomproxy'), 'lua', 'start') 25 | 26 | local gate = skynet.uniqueservice("gated") 27 | skynet.call(gate, "lua", "open" , { 28 | port = tonumber(skynet.getenv("port")) or 8888, 29 | maxclient = tonumber(skynet.getenv("maxclient")) or 1024, 30 | servername = NODE_NAME, 31 | }) 32 | 33 | cluster.open(NODE_NAME) 34 | end) 35 | -------------------------------------------------------------------------------- /hall/msgagent.lua: -------------------------------------------------------------------------------- 1 | -- msgagent.lua 2 | local skynet = require "skynet" 3 | local snax = require "skynet.snax" 4 | local message = require 'message' 5 | local protobuf = require 'protobuf' 6 | 7 | local gate 8 | local user 9 | 10 | local CMD = {} 11 | 12 | skynet.register_protocol { 13 | name = "client", 14 | id = skynet.PTYPE_CLIENT, 15 | unpack = message.unpack, 16 | dispatch = function (_, _, netmsg) 17 | local ret = message.dispatch(netmsg, user) 18 | skynet.ret(ret) 19 | end 20 | } 21 | 22 | local function load_user_detail(r) 23 | local roomdc = snax.uniqueservice('roomdc') 24 | local room = r.room > 0 and roomdc.req.get(r.room) or nil 25 | if room then 26 | user.room = {rid = r.room, sname = room.server} 27 | LOG_INFO('associate room to login user, uid %d, rid %d, sname %s', 28 | r.uid, r.room, room.server) 29 | end 30 | end 31 | 32 | function CMD.login(source, uname, uid, sid, secret) 33 | -- you may use secret to make a encrypted data stream 34 | skynet.error(string.format("%s is login", uid)) 35 | gate = source 36 | user = { 37 | userid = uid, 38 | subid = sid, 39 | username = uname, 40 | secret = secret, 41 | service = skynet.self(), 42 | } 43 | local userdc = snax.uniqueservice('userdc') 44 | -- load user if exist, otherwise register 45 | userdc.req.ensure(uid, tostring(uid)) 46 | load_user_detail(userdc.req.get(uid)) 47 | end 48 | 49 | function CMD.set_room(_, rid, sname) 50 | LOG_INFO('set room [%d-%s] to user %d', rid, sname, user.userid) 51 | local userdc = snax.uniqueservice('userdc') 52 | userdc.req.setvalue(user.userid, 'room', rid) 53 | user.room = {rid = rid, sname = sname} 54 | end 55 | 56 | function CMD.get_room(_) 57 | return user.room 58 | end 59 | 60 | local function logout() 61 | if gate then 62 | skynet.call(gate, "lua", "logout", user.userid, user.subid) 63 | end 64 | skynet.exit() 65 | end 66 | 67 | function CMD.logout(source) 68 | -- NOTICE: The logout MAY be reentry 69 | skynet.error(string.format("%s is logout", user.userid)) 70 | logout() 71 | end 72 | 73 | function CMD.afk(source) 74 | -- the connection is broken, but the user may back 75 | skynet.error(string.format("AFK")) 76 | end 77 | 78 | skynet.start(function() 79 | message.mapping('hall') 80 | -- If you want to fork a work thread , you MUST do it in CMD.login 81 | skynet.dispatch("lua", function(session, source, command, ...) 82 | local f = assert(CMD[command]) 83 | skynet.ret(skynet.pack(f(source, ...))) 84 | end) 85 | protobuf.register_file("./protocol/netmsg.pb") 86 | end) 87 | -------------------------------------------------------------------------------- /hall/roomproxy.lua: -------------------------------------------------------------------------------- 1 | -- roomproxy.lua 2 | -- track all room in all game servers 3 | local skynet = require 'skynet' 4 | local snax = require "skynet.snax" 5 | require 'skynet.manager' 6 | local cluster = require 'skynet.cluster' 7 | 8 | local game_infos = {} -- game_id: rooms, auto updated from all game servers 9 | local gamelets = {} -- game_id: [server_name], auto updated from game servers 10 | local server_infos = {} -- auto updated 11 | local room2servers = {} -- auto updated 12 | local command = {} 13 | 14 | local function update_local(sname, pack) 15 | local games = pack.game_infos 16 | local server = pack.server 17 | server_infos[sname] = server 18 | for id, rooms in pairs(games) do 19 | if not game_infos[id] then game_infos[id] = {} end 20 | if not gamelets[id] then gamelets[id] = {} end 21 | table.insert(gamelets[id], sname) 22 | for _, room in ipairs(rooms) do 23 | room.sname = sname 24 | table.insert(game_infos[id], room) 25 | room2servers[room.id] = sname 26 | end 27 | end 28 | end 29 | 30 | local function clear() 31 | game_infos = {} 32 | gamelets = {} 33 | room2servers = {} 34 | end 35 | 36 | local function do_update(servers) 37 | clear() 38 | for _, s in ipairs(servers) do 39 | local ok, pack = pcall(cluster.call, s, 'roommanager', 'get') 40 | if not ok then 41 | LOG_WARNING('cluster.call game server gamelist [%s] failed', s) 42 | else 43 | update_local(s, pack) 44 | end 45 | end 46 | end 47 | 48 | local function ensure_rooms() 49 | local roomdc = snax.uniqueservice('roomdc') 50 | for _, row in pairs(roomdc.req.get_all()) do 51 | local server = row.server 52 | local ok, _ = pcall(cluster.call, server, 'roommanager', 'ensure_room', row) 53 | if not ok then 54 | LOG_WARNING('ensure room on server failed, rid %d, server %s', row.id, server) 55 | end 56 | end 57 | end 58 | 59 | local function update(servers) 60 | local tick = 0 61 | while true do 62 | if tick % 3 == 0 then -- target-based 63 | ensure_rooms() 64 | end 65 | do_update(servers) 66 | tick = tick + 1 67 | skynet.sleep(500) -- 5s 68 | end 69 | end 70 | 71 | function command.start() 72 | local servers = string.split(skynet.getenv('gameservers'), ';') 73 | skynet.fork(update, servers) 74 | end 75 | 76 | function command.get() 77 | return game_infos 78 | end 79 | 80 | function command.create_room(id, user, rid) 81 | -- NOTE: gamelets will not be cleard in `update' ? 82 | local snames = gamelets[id] 83 | if not snames then 84 | return nil, 'no server found' 85 | end 86 | local idx = 1 + math.random(65535) % #snames 87 | local name = snames[idx] 88 | local ok, ret = pcall(cluster.call, name, 'roommanager', 'create_room', id, user, rid) 89 | if not ok then 90 | return nil, string.format('call gameserver failed: %s', ret) 91 | end 92 | local sinfo = server_infos[name] 93 | return true, {server = sinfo, rid = ret}, name 94 | end 95 | 96 | function command.destroy_room(id, user) 97 | local sname = room2servers[id] 98 | if not sname then 99 | LOG_WARNING('not found room assocaited server %d', id) 100 | return 404 101 | end 102 | -- TODO: load from gameserver or from db/cache ? 103 | local roomdc = snax.uniqueservice('roomdc') 104 | local room_r = roomdc.req.get(id) 105 | if not room_r then 106 | LOG_WARNING('not found room record from db/cache for %d', id) 107 | return 404 108 | end 109 | if room_r.creator ~= user.userid then 110 | LOG_WARNING('only room creator can destroy room, rid %d, uid %d, cid %d', 111 | id, user.userid, room_r.creator) 112 | return 403 113 | end 114 | local ok, code = pcall(cluster.call, sname, 'roommanager', 'destroy_room', id, user) 115 | LOG_INFO('destroy room on game server code %d, rid %d', code, id) 116 | return code 117 | end 118 | 119 | -- TODO: only pass `rid' here ? 120 | function command.select_room(rid, sname, user) 121 | if server_infos[sname] == nil then 122 | LOG_WARNING('select room failed: not found gs [%s]', sname) 123 | return 404 124 | end 125 | local ok, ret = pcall(cluster.call, sname, 'roommanager', 'select_room', rid, user) 126 | if not ok then 127 | return 500 128 | end 129 | if ret ~= 200 then 130 | LOG_WARNING('room select failed on game server [%s] by rid %d, code %d', sname, rid, ret) 131 | return ret 132 | end 133 | local sinfo = server_infos[sname] 134 | return 200, sinfo 135 | end 136 | 137 | function command.leave_room(user) 138 | local room = skynet.call(user.service, 'lua', 'get_room') 139 | local rid = room.rid 140 | local sname = room2servers[rid] 141 | if not sname then 142 | LOG_WARNING('not found room assocaited server %d', rid) 143 | return 404 144 | end 145 | local ok, ret = pcall(cluster.call, sname, 'roommanager', 'leave_room', user) 146 | return ret 147 | end 148 | 149 | skynet.start(function() 150 | math.randomseed(os.time()) 151 | skynet.dispatch("lua", function(session, address, cmd, ...) 152 | local f = command[cmd] 153 | if f then 154 | skynet.ret(skynet.pack(f(...))) 155 | else 156 | error(string.format("Unknown command %s", tostring(cmd))) 157 | end 158 | end) 159 | skynet.register('roomproxy') 160 | end) 161 | 162 | -------------------------------------------------------------------------------- /hall/rpc/hall.lua: -------------------------------------------------------------------------------- 1 | -- hall.lua 2 | local skynet = require 'skynet' 3 | local snax = require "skynet.snax" 4 | local protobuf = require "protobuf" 5 | local table = require 'table' 6 | local message = require 'message' 7 | 8 | local roomdc 9 | 10 | function init(...) 11 | protobuf.register_file("./protocol/netmsg.pb") 12 | protobuf.register_file("./protocol/hall.pb") 13 | roomdc = snax.uniqueservice('roomdc') 14 | end 15 | 16 | function exit(...) 17 | end 18 | 19 | function response.GetGameList(data) 20 | local _ = pb_decode(data) 21 | local game_infos = skynet.call('roomproxy', 'lua', 'get') 22 | local games = {} 23 | for id, rooms in pairs(game_infos) do 24 | local room_infos = {} 25 | for _, room in ipairs(rooms) do 26 | table.insert(room_infos, {gid = id, sname = room.sname, rid = room.id}) 27 | end 28 | local game = {gid = id, rooms = room_infos} 29 | table.insert(games, game) 30 | end 31 | return message.pack("hall.GetGameListResponse", {games = games}) 32 | end 33 | 34 | function response.CreateRoom(data, user) 35 | local args = pb_decode(data) 36 | local rid = roomdc.req.get_nextid() 37 | local ok, ret, sname = skynet.call('roomproxy', 'lua', 'create_room', args.id, user, rid) 38 | if not ok then -- room id will have a gap 39 | error(ret) 40 | end 41 | local row = {id = rid, creator = user.userid, game = args.id, server = sname} 42 | local ok = roomdc.req.add(row) 43 | if not ok then 44 | LOG_ERROR('add to roomdc failed, game %d, uid %d', args.id, user.userid) 45 | error(ok) 46 | end 47 | skynet.call(user.service, 'lua', 'set_room', rid, sname) 48 | return message.pack('hall.CreateRoomResponse', ret) 49 | end 50 | 51 | function response.DestroyRoom(data, user) 52 | local args = pb_decode(data) 53 | local code = skynet.call('roomproxy', 'lua', 'destroy_room', args.id, user) 54 | if code == 200 then 55 | roomdc.req.delete({id = args.id}) 56 | end 57 | return message.pack('hall.DestroyRoomResponse', {code = code}) 58 | end 59 | 60 | function response.SelectRoom(data, user) 61 | local args = pb_decode(data) 62 | local code, sinfo = skynet.call('roomproxy', 'lua', 'select_room', args.rid, args.sname, user) 63 | if code == 200 then 64 | skynet.call(user.service, 'lua', 'set_room', args.rid, args.sname) 65 | end 66 | return message.pack('hall.SelectRoomResponse', {code = code, server = sinfo}) 67 | end 68 | 69 | function response.ReconnRoom(data, user) 70 | local room = skynet.call(user.service, 'lua', 'get_room') 71 | if room then 72 | local code, sinfo = skynet.call('roomproxy', 'lua', 'select_room', room.rid, room.sname, user) 73 | return message.pack('hall.ReconnRoomResponse', {code = code, server = sinfo, rid = room.rid}) 74 | end 75 | return message.pack('hall.ReconnRoomResponse', {code = 404}) 76 | end 77 | 78 | function response.LeaveRoom(data, user) 79 | LOG_DEBUG('rpc recv LeaveRoom from user %d', user.userid) 80 | local args = pb_decode(data) 81 | local code = skynet.call('roomproxy', 'lua', 'leave_room', user) 82 | if code == 200 then 83 | skynet.call(user.service, 'lua', 'set_room', 0, '') 84 | end 85 | return message.pack('hall.LeaveRoomResponse', {code = code}) 86 | end 87 | 88 | function response.GetUserInfo(data, user) 89 | local room = user.room 90 | print('get user info ') 91 | local result = {} 92 | if room then 93 | result.room = room 94 | end 95 | return message.pack('hall.GetUserInfoResponse', result) 96 | end 97 | 98 | -------------------------------------------------------------------------------- /login/main.lua: -------------------------------------------------------------------------------- 1 | -- main.lua 2 | -- pigybug 3 | local skynet = require "skynet" 4 | local cluster = require "skynet.cluster" 5 | 6 | local common = { 7 | { name = "d_account", key = "sdkid,pid", indexkey = "id" }, 8 | } 9 | 10 | skynet.start(function() 11 | local log = skynet.uniqueservice("log") 12 | skynet.call(log, "lua", "start") 13 | 14 | local dbmgr = skynet.uniqueservice("dbmgr") 15 | skynet.call(dbmgr, "lua", "start", {}, {}, common) 16 | 17 | local logind = skynet.uniqueservice("logind") 18 | skynet.call(logind, 'lua', 'start') 19 | cluster.open("login") 20 | end) 21 | -------------------------------------------------------------------------------- /lualib-src/lua-zlib.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinlynx/pigy/08755bae537f6e3fd32de3bf64237babf445e536/lualib-src/lua-zlib.c -------------------------------------------------------------------------------- /lualib-src/pbc/.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.sdf 3 | *.opensdf 4 | *.d 5 | *.o 6 | Debug 7 | Release 8 | build 9 | .DS_Store 10 | 11 | -------------------------------------------------------------------------------- /lualib-src/pbc/.travis.yml: -------------------------------------------------------------------------------- 1 | # ref: http://docs.travis-ci.com/user/build-configuration 2 | 3 | language: erlang 4 | 5 | env: 6 | global: 7 | - DEPS_BUILD_DIR=$TRAVIS_BUILD_DIR/deps 8 | - LJ_REPO="http://luajit.org/git/luajit-2.0.git" 9 | - INC_DIR=/usr/local/include 10 | matrix: 11 | - LUA=lua-5.1 LUA_TYPE=lua LUA_BIN=lua LUA_DIST=lua-5.1.5 LUA_BD=lua LUA_INC=$INC_DIR 12 | - LUA=lua-5.2 LUA_TYPE=lua LUA_BIN=lua LUA_DIST=lua-5.2.4 LUA_BD=lua LUA_INC=$INC_DIR 13 | - LUA=lua-5.3 LUA_TYPE=lua LUA_BIN=lua LUA_DIST=lua-5.3.0 LUA_BD=lua53 LUA_INC=$INC_DIR 14 | - LUA=luajit-2.0 LUA_TYPE=luajit LUA_BIN=luajit LJ_BR=master LUA_BD=lua LUA_INC=$INC_DIR/$LUA 15 | - LUA=luajit-2.1 LUA_TYPE=luajit LUA_BIN=luajit-2.1.0-alpha LJ_BR=v2.1 LUA_BD=lua LUA_INC=$INC_DIR/$LUA 16 | 17 | before_install: 18 | - mkdir -p $DEPS_BUILD_DIR 19 | - sudo apt-get update -qq 20 | - sudo apt-get install libprotobuf-dev protobuf-compiler 21 | 22 | install: 23 | # install Lua/LuaJIT 24 | - cd $DEPS_BUILD_DIR 25 | - if [ "$LUA_TYPE" == "luajit" ]; then 26 | git clone -b $LJ_BR $LJ_REPO luajit2.git && cd luajit2.git && sudo make install; 27 | fi 28 | - if [ "$LUA_TYPE" == "lua" ]; then 29 | wget "http://www.lua.org/ftp/$LUA_DIST.tar.gz" && tar xzf $LUA_DIST.tar.gz && cd $LUA_DIST && sudo make linux test install; 30 | fi 31 | # build lib 32 | - cd $TRAVIS_BUILD_DIR 33 | - make 34 | # build lib bindings 35 | - make -C binding/$LUA_BD LUADIR=$LUA_INC 36 | 37 | before_script: 38 | # back to home directory 39 | - cd $TRAVIS_BUILD_DIR 40 | # check executables 41 | - which $LUA_BIN 2>/dev/null && $LUA_BIN -v 42 | 43 | script: 44 | - cd $TRAVIS_BUILD_DIR/binding/$LUA_BD 45 | - $LUA_BIN test.lua 46 | 47 | notifications: 48 | email: 49 | on_success: change 50 | on_failure: always 51 | -------------------------------------------------------------------------------- /lualib-src/pbc/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := pbc 6 | 7 | LOCAL_MODULE_FILENAME := libpbc 8 | 9 | LOCAL_SRC_FILES := \ 10 | src/alloc.c \ 11 | src/array.c \ 12 | src/bootstrap.c \ 13 | src/context.c \ 14 | src/decode.c \ 15 | src/map.c \ 16 | src/pattern.c \ 17 | src/proto.c \ 18 | src/register.c \ 19 | src/rmessage.c \ 20 | src/stringpool.c \ 21 | src/varint.c \ 22 | src/wmessage.c \ 23 | 24 | 25 | 26 | LOCAL_C_INCLUDES+= src\ 27 | 28 | 29 | include $(BUILD_STATIC_LIBRARY) 30 | -------------------------------------------------------------------------------- /lualib-src/pbc/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -O2 -fPIC -Wall 3 | AR = ar rc 4 | 5 | BUILD = build 6 | 7 | .PHONY : all lib clean tool 8 | 9 | LIBSRCS = context.c varint.c array.c pattern.c register.c proto.c map.c alloc.c rmessage.c wmessage.c bootstrap.c stringpool.c decode.c 10 | LIBNAME = libpbc.a 11 | 12 | TESTSRCS = addressbook.c pattern.c pbc.c float.c map.c test.c decode.c 13 | PROTOSRCS = addressbook.proto descriptor.proto float.proto test.proto 14 | 15 | BUILD_O = $(BUILD)/o 16 | 17 | all : lib test 18 | 19 | lib : $(LIBNAME) 20 | 21 | clean : 22 | rm -rf $(BUILD) 23 | 24 | $(BUILD) : $(BUILD_O) 25 | 26 | $(BUILD_O) : 27 | mkdir -p $@ 28 | 29 | TOOL := $(BUILD)/dump 30 | 31 | tool : $(TOOL) 32 | 33 | $(TOOL) : | $(BUILD) 34 | $(TOOL) : $(LIBNAME) 35 | $(TOOL) : tool/dump.c 36 | cd $(BUILD) && $(CC) $(CFLAGS) -I.. -L. -o dump ../$< -lpbc 37 | 38 | LIB_O := 39 | 40 | define BUILD_temp 41 | TAR := $(BUILD_O)/$(notdir $(basename $(1))) 42 | LIB_O := $(LIB_O) $$(TAR).o 43 | $$(TAR).o : | $(BUILD_O) 44 | -include $$(TAR).d 45 | $$(TAR).o : src/$(1) 46 | $(CC) $(CFLAGS) -c -Isrc -I. -o $$@ -MMD $$< 47 | endef 48 | 49 | $(foreach s,$(LIBSRCS),$(eval $(call BUILD_temp,$(s)))) 50 | 51 | $(LIBNAME) : $(LIB_O) 52 | cd $(BUILD) && $(AR) $(LIBNAME) $(addprefix ../,$^) 53 | 54 | TEST := 55 | 56 | define TEST_temp 57 | TAR := $(BUILD)/$(notdir $(basename $(1))) 58 | TEST := $(TEST) $$(TAR) 59 | $$(TAR) : | $(BUILD) 60 | $$(TAR) : $(LIBNAME) 61 | $$(TAR) : test/$(1) 62 | cd $(BUILD) && $(CC) $(CFLAGS) -I.. -L. -o $$(notdir $$@) ../$$< -lpbc 63 | endef 64 | 65 | $(foreach s,$(TESTSRCS),$(eval $(call TEST_temp,$(s)))) 66 | 67 | test : $(TEST) proto 68 | 69 | PROTO := 70 | 71 | define PROTO_temp 72 | TAR := $(BUILD)/$(notdir $(basename $(1))) 73 | PROTO := $(PROTO) $$(TAR).pb 74 | $$(TAR).pb : | $(BUILD) 75 | $$(TAR).pb : test/$(1) 76 | protoc -o$$@ $$< 77 | endef 78 | 79 | $(foreach s,$(PROTOSRCS),$(eval $(call PROTO_temp,$(s)))) 80 | 81 | proto : $(PROTO) 82 | 83 | .PHONY : all lib test proto clean 84 | 85 | -------------------------------------------------------------------------------- /lualib-src/pbc/README.md: -------------------------------------------------------------------------------- 1 | ## PBC 2 | 3 | [![travis-ci status](https://travis-ci.org/cloudwu/pbc.svg?branch=master)](https://travis-ci.org/cloudwu/pbc) 4 | 5 | PBC is a google protocol buffers library for C without code generation. 6 | 7 | ## Quick Example 8 | 9 | package tutorial; 10 | 11 | message Person { 12 | required string name = 1; 13 | required int32 id = 2; // Unique ID number for this person. 14 | optional string email = 3; 15 | 16 | enum PhoneType { 17 | MOBILE = 0; 18 | HOME = 1; 19 | WORK = 2; 20 | } 21 | 22 | message PhoneNumber { 23 | required string number = 1; 24 | optional PhoneType type = 2 [default = HOME]; 25 | } 26 | 27 | repeated PhoneNumber phone = 4; 28 | } 29 | 30 | ```C 31 | struct pbc_rmessage * m = pbc_rmessage_new(env, "tutorial.Person", slice); 32 | printf("name = %s\n", pbc_rmessage_string(m , "name" , 0 , NULL)); 33 | printf("id = %d\n", pbc_rmessage_integer(m , "id" , 0 , NULL)); 34 | printf("email = %s\n", pbc_rmessage_string(m , "email" , 0 , NULL)); 35 | 36 | int phone_n = pbc_rmessage_size(m, "phone"); 37 | int i; 38 | 39 | for (i=0;i 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /lualib-src/pbc/pbc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_H 2 | #define PROTOBUF_C_H 3 | 4 | #include 5 | #include 6 | 7 | #define PBC_ARRAY_CAP 64 8 | 9 | #define PBC_NOEXIST -1 10 | #define PBC_INT 1 11 | #define PBC_REAL 2 12 | #define PBC_BOOL 3 13 | #define PBC_ENUM 4 14 | #define PBC_STRING 5 15 | #define PBC_MESSAGE 6 16 | #define PBC_FIXED64 7 17 | #define PBC_FIXED32 8 18 | #define PBC_BYTES 9 19 | #define PBC_INT64 10 20 | #define PBC_UINT 11 21 | #define PBC_UNKNOWN 12 22 | #define PBC_REPEATED 128 23 | 24 | typedef struct _pbc_array { char _data[PBC_ARRAY_CAP]; } pbc_array[1]; 25 | 26 | struct pbc_slice { 27 | void *buffer; 28 | int len; 29 | }; 30 | 31 | struct pbc_pattern; 32 | struct pbc_env; 33 | struct pbc_rmessage; 34 | struct pbc_wmessage; 35 | 36 | struct pbc_env * pbc_new(void); 37 | void pbc_delete(struct pbc_env *); 38 | int pbc_register(struct pbc_env *, struct pbc_slice * slice); 39 | int pbc_type(struct pbc_env *, const char * type_name , const char * key , const char ** type); 40 | const char * pbc_error(struct pbc_env *); 41 | 42 | // callback api 43 | union pbc_value { 44 | struct { 45 | uint32_t low; 46 | uint32_t hi; 47 | } i; 48 | double f; 49 | struct pbc_slice s; 50 | struct { 51 | int id; 52 | const char * name; 53 | } e; 54 | }; 55 | 56 | typedef void (*pbc_decoder)(void *ud, int type, const char * type_name, union pbc_value *v, int id, const char *key); 57 | int pbc_decode(struct pbc_env * env, const char * type_name , struct pbc_slice * slice, pbc_decoder f, void *ud); 58 | 59 | // message api 60 | 61 | struct pbc_rmessage * pbc_rmessage_new(struct pbc_env * env, const char * type_name , struct pbc_slice * slice); 62 | void pbc_rmessage_delete(struct pbc_rmessage *); 63 | 64 | uint32_t pbc_rmessage_integer(struct pbc_rmessage * , const char *key , int index, uint32_t *hi); 65 | double pbc_rmessage_real(struct pbc_rmessage * , const char *key , int index); 66 | const char * pbc_rmessage_string(struct pbc_rmessage * , const char *key , int index, int *sz); 67 | struct pbc_rmessage * pbc_rmessage_message(struct pbc_rmessage *, const char *key, int index); 68 | int pbc_rmessage_size(struct pbc_rmessage *, const char *key); 69 | int pbc_rmessage_next(struct pbc_rmessage *, const char **key); 70 | 71 | struct pbc_wmessage * pbc_wmessage_new(struct pbc_env * env, const char *type_name); 72 | void pbc_wmessage_delete(struct pbc_wmessage *); 73 | 74 | // for negative integer, pass -1 to hi 75 | int pbc_wmessage_integer(struct pbc_wmessage *, const char *key, uint32_t low, uint32_t hi); 76 | int pbc_wmessage_real(struct pbc_wmessage *, const char *key, double v); 77 | int pbc_wmessage_string(struct pbc_wmessage *, const char *key, const char * v, int len); 78 | struct pbc_wmessage * pbc_wmessage_message(struct pbc_wmessage *, const char *key); 79 | void * pbc_wmessage_buffer(struct pbc_wmessage *, struct pbc_slice * slice); 80 | 81 | // array api 82 | 83 | int pbc_array_size(pbc_array); 84 | uint32_t pbc_array_integer(pbc_array array, int index, uint32_t *hi); 85 | double pbc_array_real(pbc_array array, int index); 86 | struct pbc_slice * pbc_array_slice(pbc_array array, int index); 87 | 88 | void pbc_array_push_integer(pbc_array array, uint32_t low, uint32_t hi); 89 | void pbc_array_push_slice(pbc_array array, struct pbc_slice *); 90 | void pbc_array_push_real(pbc_array array, double v); 91 | 92 | struct pbc_pattern * pbc_pattern_new(struct pbc_env * , const char * message, const char *format, ...); 93 | void pbc_pattern_delete(struct pbc_pattern *); 94 | 95 | // return unused bytes , -1 for error 96 | int pbc_pattern_pack(struct pbc_pattern *, void *input, struct pbc_slice * s); 97 | 98 | // <0 for error 99 | int pbc_pattern_unpack(struct pbc_pattern *, struct pbc_slice * s , void * output); 100 | 101 | void pbc_pattern_set_default(struct pbc_pattern * , void *data); 102 | void pbc_pattern_close_arrays(struct pbc_pattern *, void *data); 103 | 104 | int pbc_enum_id(struct pbc_env *env, const char *enum_type, const char *enum_name); 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /lualib-src/pbc/pbc.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pbc", "pbc.vcxproj", "{82356F33-956B-4931-9977-BD7994B1C761}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {82356F33-956B-4931-9977-BD7994B1C761}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {82356F33-956B-4931-9977-BD7994B1C761}.Debug|Win32.Build.0 = Debug|Win32 14 | {82356F33-956B-4931-9977-BD7994B1C761}.Release|Win32.ActiveCfg = Release|Win32 15 | {82356F33-956B-4931-9977-BD7994B1C761}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /lualib-src/pbc/pbc.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {82356F33-956B-4931-9977-BD7994B1C761} 43 | Win32Proj 44 | ConsoleApplication1 45 | 46 | 47 | 48 | StaticLibrary 49 | true 50 | v110_xp 51 | Unicode 52 | 53 | 54 | StaticLibrary 55 | false 56 | v110_xp 57 | true 58 | Unicode 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Level3 74 | Disabled 75 | WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) 76 | .;.\pbc;.\pbc\src 77 | true 78 | CompileAsCpp 79 | 4146;4273;4244;4018 80 | 81 | 82 | Windows 83 | true 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | MaxSpeed 91 | true 92 | true 93 | WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) 94 | .;.\pbc;.\pbc\src 95 | true 96 | CompileAsCpp 97 | 4146;4273;4244;4018 98 | 99 | 100 | Windows 101 | true 102 | true 103 | true 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static int _g = 0; 5 | 6 | void * _pbcM_malloc(size_t sz) { 7 | ++ _g; 8 | return malloc(sz); 9 | } 10 | 11 | void _pbcM_free(void *p) { 12 | if (p) { 13 | -- _g; 14 | free(p); 15 | } 16 | } 17 | 18 | void* _pbcM_realloc(void *p, size_t sz) { 19 | return realloc(p,sz); 20 | } 21 | 22 | void _pbcM_memory() { 23 | printf("%d\n",_g); 24 | } 25 | 26 | struct heap_page { 27 | struct heap_page * next; 28 | }; 29 | 30 | struct heap { 31 | struct heap_page *current; 32 | int size; 33 | int used; 34 | }; 35 | 36 | struct heap * 37 | _pbcH_new(int pagesize) { 38 | int cap = 1024; 39 | while(cap < pagesize) { 40 | cap *= 2; 41 | } 42 | struct heap * h = (struct heap *)_pbcM_malloc(sizeof(struct heap)); 43 | h->current = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + cap); 44 | h->size = cap; 45 | h->used = 0; 46 | h->current->next = NULL; 47 | return h; 48 | } 49 | 50 | void 51 | _pbcH_delete(struct heap *h) { 52 | struct heap_page * p = h->current; 53 | struct heap_page * next = p->next; 54 | for(;;) { 55 | _pbcM_free(p); 56 | if (next == NULL) 57 | break; 58 | p = next; 59 | next = p->next; 60 | } 61 | _pbcM_free(h); 62 | } 63 | 64 | void* 65 | _pbcH_alloc(struct heap *h, int size) { 66 | size = (size + 3) & ~3; 67 | if (h->size - h->used < size) { 68 | struct heap_page * p; 69 | if (size < h->size) { 70 | p = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + h->size); 71 | } else { 72 | p = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + size); 73 | } 74 | p->next = h->current; 75 | h->current = p; 76 | h->used = size; 77 | return (p+1); 78 | } else { 79 | char * buffer = (char *)(h->current + 1); 80 | buffer += h->used; 81 | h->used += size; 82 | return buffer; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_ALLOC_H 2 | #define PROTOBUF_C_ALLOC_H 3 | 4 | #include 5 | #include 6 | 7 | void * _pbcM_malloc(size_t sz); 8 | void _pbcM_free(void *p); 9 | void * _pbcM_realloc(void *p, size_t sz); 10 | void _pbcM_memory(); 11 | 12 | struct heap; 13 | 14 | struct heap * _pbcH_new(int pagesize); 15 | void _pbcH_delete(struct heap *); 16 | void* _pbcH_alloc(struct heap *, int size); 17 | 18 | #define HMALLOC(size) ((h) ? _pbcH_alloc(h, size) : _pbcM_malloc(size)) 19 | 20 | #define malloc _pbcM_malloc 21 | #define free _pbcM_free 22 | #define realloc _pbcM_realloc 23 | #define memory _pbcM_memory 24 | 25 | #ifdef _WIN32 26 | 27 | #include 28 | 29 | #endif 30 | 31 | #ifdef _MSC_VER 32 | 33 | #define alloca _alloca 34 | 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/array.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | #include "array.h" 3 | #include "alloc.h" 4 | 5 | #include 6 | #include 7 | 8 | struct array { 9 | int number; 10 | struct heap *heap; 11 | union _pbc_var * a; 12 | }; 13 | 14 | #define INNER_FIELD ((PBC_ARRAY_CAP - sizeof(struct array)) / sizeof(pbc_var)) 15 | 16 | void 17 | _pbcA_open(pbc_array _array) { 18 | struct array * a = (struct array *)_array; 19 | a->number = 0; 20 | a->heap = NULL; 21 | a->a = (union _pbc_var *)(a+1); 22 | } 23 | 24 | void 25 | _pbcA_open_heap(pbc_array _array, struct heap *h) { 26 | struct array * a = (struct array *)_array; 27 | a->number = 0; 28 | a->heap = h; 29 | a->a = (union _pbc_var *)(a+1); 30 | } 31 | 32 | void 33 | _pbcA_close(pbc_array _array) { 34 | struct array * a = (struct array *)_array; 35 | if (a->heap == NULL && a->a != NULL && (union _pbc_var *)(a+1) != a->a) { 36 | _pbcM_free(a->a); 37 | a->a = NULL; 38 | } 39 | } 40 | 41 | void 42 | _pbcA_push(pbc_array _array, pbc_var var) { 43 | struct array * a = (struct array *)_array; 44 | if (a->number == 0) { 45 | a->a = (union _pbc_var *)(a+1); 46 | } else if (a->number >= INNER_FIELD) { 47 | if (a->number == INNER_FIELD) { 48 | int cap = 1; 49 | while (cap <= a->number + 1) 50 | cap *= 2; 51 | struct heap * h = a->heap; 52 | union _pbc_var * outer = (union _pbc_var *)HMALLOC(cap * sizeof(union _pbc_var)); 53 | memcpy(outer , a->a , INNER_FIELD * sizeof(pbc_var)); 54 | a->a = outer; 55 | } else { 56 | int size=a->number; 57 | if (((size + 1) ^ size) > size) { 58 | struct heap * h = a->heap; 59 | if (h) { 60 | void * old = a->a; 61 | a->a = (union _pbc_var *)_pbcH_alloc(h, sizeof(union _pbc_var) * (size+1) * 2); 62 | memcpy(a->a, old, sizeof(union _pbc_var) * size); 63 | } else { 64 | a->a = (union _pbc_var *)_pbcM_realloc(a->a,sizeof(union _pbc_var) * (size+1) * 2); 65 | } 66 | } 67 | } 68 | } 69 | a->a[a->number] = *var; 70 | ++ a->number; 71 | } 72 | 73 | void 74 | _pbcA_index(pbc_array _array, int idx, pbc_var var) 75 | { 76 | struct array * a = (struct array *)_array; 77 | var[0] = a->a[idx]; 78 | } 79 | 80 | void * 81 | _pbcA_index_p(pbc_array _array, int idx) 82 | { 83 | struct array * a = (struct array *)_array; 84 | return &(a->a[idx]); 85 | } 86 | 87 | int 88 | pbc_array_size(pbc_array _array) { 89 | struct array * a = (struct array *)_array; 90 | return a->number; 91 | } 92 | 93 | uint32_t 94 | pbc_array_integer(pbc_array array, int index, uint32_t *hi) { 95 | pbc_var var; 96 | _pbcA_index(array , index , var); 97 | if (hi) { 98 | *hi = var->integer.hi; 99 | } 100 | return var->integer.low; 101 | } 102 | 103 | double 104 | pbc_array_real(pbc_array array, int index) { 105 | pbc_var var; 106 | _pbcA_index(array , index , var); 107 | return var->real; 108 | } 109 | 110 | struct pbc_slice * 111 | pbc_array_slice(pbc_array _array, int index) { 112 | struct array * a = (struct array *)_array; 113 | if (index <0 || index > a->number) { 114 | return NULL; 115 | } 116 | return (struct pbc_slice *) &(a->a[index]); 117 | } 118 | 119 | void 120 | pbc_array_push_integer(pbc_array array, uint32_t low, uint32_t hi) { 121 | pbc_var var; 122 | var->integer.low = low; 123 | var->integer.hi = hi; 124 | _pbcA_push(array,var); 125 | } 126 | 127 | void 128 | pbc_array_push_slice(pbc_array array, struct pbc_slice *s) { 129 | pbc_var var; 130 | var->m = *s; 131 | _pbcA_push(array,var); 132 | } 133 | 134 | void 135 | pbc_array_push_real(pbc_array array, double v) { 136 | pbc_var var; 137 | var->real = v; 138 | _pbcA_push(array,var); 139 | } 140 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/array.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_ARRAY_H 2 | #define PROTOBUF_C_ARRAY_H 3 | 4 | #include "varint.h" 5 | #include "pbc.h" 6 | #include "alloc.h" 7 | 8 | typedef union _pbc_var { 9 | struct longlong integer; 10 | double real; 11 | struct { 12 | const char * str; 13 | int len; 14 | } s; 15 | struct { 16 | int id; 17 | const char * name; 18 | } e; 19 | struct pbc_slice m; 20 | void * p[2]; 21 | } pbc_var[1]; 22 | 23 | void _pbcA_open(pbc_array); 24 | void _pbcA_open_heap(pbc_array, struct heap *h); 25 | void _pbcA_close(pbc_array); 26 | 27 | void _pbcA_push(pbc_array, pbc_var var); 28 | void _pbcA_index(pbc_array , int idx, pbc_var var); 29 | void * _pbcA_index_p(pbc_array _array, int idx); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/bootstrap.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_BOOTSTRAP_H 2 | #define PROTOBUF_C_BOOTSTRAP_H 3 | 4 | #include "proto.h" 5 | #include "pbc.h" 6 | 7 | void _pbcB_init(struct pbc_env *); 8 | void _pbcB_register_fields(struct pbc_env *, pbc_array queue); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/context.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | #include "alloc.h" 3 | #include "varint.h" 4 | #include "context.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #ifndef _MSC_VER 10 | #include 11 | #endif 12 | 13 | #define INNER_ATOM ((PBC_CONTEXT_CAP - sizeof(struct context)) / sizeof(struct atom)) 14 | 15 | static char * 16 | wiretype_decode(uint8_t *buffer, int cap , struct atom *a , int start) 17 | { 18 | uint8_t temp[10]; 19 | struct longlong r; 20 | int len; 21 | if (cap >= 10) { 22 | len = _pbcV_decode(buffer, &r); 23 | if (r.hi !=0) 24 | return NULL; 25 | } else { 26 | memcpy(temp, buffer , cap); 27 | len = _pbcV_decode(temp, &r); 28 | if (len > cap || r.hi !=0) 29 | return NULL; 30 | } 31 | 32 | int wiretype = r.low & 7; 33 | a->wire_id = r.low; 34 | buffer += len; 35 | start += len; 36 | cap -=len; 37 | 38 | switch (wiretype) { 39 | case WT_VARINT : 40 | if (cap >=10) { 41 | len = _pbcV_decode(buffer, &a->v.i); 42 | } else { 43 | memcpy(temp, buffer , cap); 44 | len = _pbcV_decode(temp, &a->v.i); 45 | if (cap < len) 46 | return NULL; 47 | } 48 | return (char *)buffer+len; 49 | case WT_BIT64 : 50 | if (cap < 8) 51 | return NULL; 52 | a->v.i.low = buffer[0] | 53 | buffer[1] << 8 | 54 | buffer[2] << 16 | 55 | buffer[3] << 24; 56 | a->v.i.hi = buffer[4] | 57 | buffer[5] << 8 | 58 | buffer[6] << 16 | 59 | buffer[7] << 24; 60 | return (char *)buffer + 8; 61 | case WT_LEND : 62 | if (cap >=10) { 63 | len = _pbcV_decode(buffer, &r); 64 | } else { 65 | memcpy(temp, buffer , cap); 66 | len = _pbcV_decode(temp, &r); 67 | } 68 | if (cap < len + r.low || r.hi !=0) 69 | return NULL; 70 | a->v.s.start = start + len; 71 | a->v.s.end = start + len + r.low; 72 | return (char *)buffer + len + r.low; 73 | case WT_BIT32 : 74 | if (cap < 4) 75 | return NULL; 76 | a->v.i.low = buffer[0] | 77 | buffer[1] << 8 | 78 | buffer[2] << 16 | 79 | buffer[3] << 24; 80 | a->v.i.hi = 0; 81 | return (char *)buffer + 4; 82 | default: 83 | return NULL; 84 | } 85 | } 86 | 87 | static inline int 88 | _decode_varint(uint8_t * buffer, int size , struct atom * a) { 89 | a->wire_id = WT_VARINT; 90 | if (size < 10) { 91 | uint8_t temp[10]; 92 | memcpy(temp,buffer,size); 93 | return _pbcV_decode(temp , &(a->v.i)); 94 | } else { 95 | return _pbcV_decode(buffer , &(a->v.i)); 96 | } 97 | } 98 | 99 | static int 100 | _open_packed_varint(struct context * ctx , uint8_t * buffer, int size) { 101 | struct atom * a = (struct atom *)(ctx + 1); 102 | 103 | int i; 104 | 105 | for (i=0;ia = a; 115 | } else { 116 | int cap = 64; 117 | ctx->a = (struct atom *)malloc(cap * sizeof(struct atom)); 118 | while (size > 0) { 119 | if (i >= cap) { 120 | cap = cap + 64; 121 | ctx->a = (struct atom *)realloc(ctx->a, cap * sizeof(struct atom)); 122 | continue; 123 | } 124 | int len = _decode_varint(buffer, size, &a[i]); 125 | buffer += len; 126 | size -= len; 127 | 128 | ++i; 129 | } 130 | memcpy(ctx->a, a , sizeof(struct atom) * INNER_ATOM); 131 | } 132 | ctx->number = i; 133 | 134 | return i; 135 | } 136 | 137 | int 138 | _pbcC_open_packed(pbc_ctx _ctx, int ptype, void *buffer, int size) { 139 | struct context * ctx = (struct context *)_ctx; 140 | ctx->buffer = (char *)buffer; 141 | ctx->size = size; 142 | ctx->number = 0; 143 | ctx->a = NULL; 144 | 145 | if (buffer == NULL || size == 0) { 146 | return 0; 147 | } 148 | 149 | int bits = 0; 150 | 151 | switch (ptype) { 152 | case PTYPE_INT64: 153 | case PTYPE_UINT64: 154 | case PTYPE_INT32: 155 | case PTYPE_BOOL: 156 | case PTYPE_UINT32: 157 | case PTYPE_ENUM: 158 | case PTYPE_SINT32: 159 | case PTYPE_SINT64: 160 | return _open_packed_varint(ctx , (uint8_t *)buffer, size); 161 | case PTYPE_DOUBLE: 162 | case PTYPE_FIXED64: 163 | case PTYPE_SFIXED64: 164 | ctx->number = size / 8; 165 | bits = 64; 166 | break; 167 | case PTYPE_FLOAT: 168 | case PTYPE_FIXED32: 169 | case PTYPE_SFIXED32: 170 | ctx->number = size / 4; 171 | bits = 32; 172 | break; 173 | default: 174 | return 0; 175 | } 176 | 177 | struct atom * a = (struct atom *)(ctx + 1); 178 | 179 | if (ctx->number > INNER_ATOM) { 180 | ctx->a = (struct atom *)malloc(ctx->number * sizeof(struct atom)); 181 | a = ctx->a; 182 | } else { 183 | ctx->a = a; 184 | } 185 | 186 | int i; 187 | if (bits == 64) { 188 | uint8_t * data = (uint8_t *)buffer; 189 | for (i=0;inumber;i++) { 190 | a[i].wire_id = WT_BIT64; 191 | a[i].v.i.low = data[0] | 192 | data[1] << 8 | 193 | data[2] << 16 | 194 | data[3] << 24; 195 | a[i].v.i.hi = data[4] | 196 | data[5] << 8 | 197 | data[6] << 16 | 198 | data[7] << 24; 199 | data += 8; 200 | } 201 | } else { 202 | uint8_t * data = (uint8_t *)buffer; 203 | for (i=0;inumber;i++) { 204 | a[i].wire_id = WT_BIT32; 205 | a[i].v.i.low = data[0] | 206 | data[1] << 8 | 207 | data[2] << 16 | 208 | data[3] << 24; 209 | a[i].v.i.hi = 0; 210 | data += 4; 211 | } 212 | } 213 | 214 | return ctx->number; 215 | } 216 | 217 | int 218 | _pbcC_open(pbc_ctx _ctx , void *buffer, int size) { 219 | struct context * ctx = (struct context *)_ctx; 220 | ctx->buffer = (char *)buffer; 221 | ctx->size = size; 222 | 223 | if (buffer == NULL || size == 0) { 224 | ctx->number = 0; 225 | ctx->a = NULL; 226 | return 0; 227 | } 228 | 229 | struct atom * a = (struct atom *)(ctx + 1); 230 | 231 | int i; 232 | int start = 0; 233 | 234 | ctx->a = a; 235 | 236 | for (i=0;i 0) { 248 | int cap = 64; 249 | ctx->a = (struct atom *)malloc(cap * sizeof(struct atom)); 250 | while (size > 0) { 251 | if (i >= cap) { 252 | cap = cap + 64; 253 | ctx->a = (struct atom *)realloc(ctx->a, cap * sizeof(struct atom)); 254 | continue; 255 | } 256 | char * next = wiretype_decode((uint8_t *)buffer, size , &ctx->a[i] , start); 257 | if (next == NULL) { 258 | return -i; 259 | } 260 | start += next - (char *)buffer; 261 | size -= next - (char *)buffer; 262 | buffer = next; 263 | ++i; 264 | } 265 | memcpy(ctx->a, a , sizeof(struct atom) * INNER_ATOM); 266 | } 267 | ctx->number = i; 268 | 269 | return i; 270 | } 271 | 272 | 273 | void 274 | _pbcC_close(pbc_ctx _ctx) { 275 | struct context * ctx = (struct context *)_ctx; 276 | if (ctx->a != NULL && (struct atom *)(ctx+1) != ctx->a) { 277 | free(ctx->a); 278 | ctx->a = NULL; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/context.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_CONTEXT_H 2 | #define PROTOBUF_C_CONTEXT_H 3 | 4 | #include 5 | 6 | #include "array.h" 7 | 8 | #define PBC_CONTEXT_CAP 256 9 | 10 | // wiretype 11 | 12 | #define WT_VARINT 0 13 | #define WT_BIT64 1 14 | #define WT_LEND 2 15 | #define WT_BIT32 5 16 | 17 | #define CTYPE_INT32 1 18 | #define CTYPE_INT64 2 19 | #define CTYPE_DOUBLE 3 20 | #define CTYPE_FLOAT 4 21 | #define CTYPE_POINTER 5 22 | #define CTYPE_BOOL 6 23 | #define CTYPE_INT8 7 24 | #define CTYPE_INT16 8 25 | #define CTYPE_ARRAY 9 26 | #define CTYPE_VAR 10 27 | #define CTYPE_PACKED 11 28 | 29 | #define PTYPE_DOUBLE 1 30 | #define PTYPE_FLOAT 2 31 | #define PTYPE_INT64 3 // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if negative values are likely. 32 | #define PTYPE_UINT64 4 33 | #define PTYPE_INT32 5 // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if negative values are likely. 34 | #define PTYPE_FIXED64 6 35 | #define PTYPE_FIXED32 7 36 | #define PTYPE_BOOL 8 37 | #define PTYPE_STRING 9 38 | #define PTYPE_GROUP 10 // Tag-delimited aggregate. 39 | #define PTYPE_MESSAGE 11 // Length-delimited aggregate. 40 | #define PTYPE_BYTES 12 41 | #define PTYPE_UINT32 13 42 | #define PTYPE_ENUM 14 43 | #define PTYPE_SFIXED32 15 44 | #define PTYPE_SFIXED64 16 45 | #define PTYPE_SINT32 17 // Uses ZigZag encoding. 46 | #define PTYPE_SINT64 18 // Uses ZigZag encoding. 47 | 48 | struct slice { 49 | int start; 50 | int end; 51 | }; 52 | 53 | struct atom { 54 | int wire_id; 55 | union { 56 | struct slice s; 57 | struct longlong i; 58 | } v; 59 | }; 60 | 61 | struct context { 62 | char * buffer; 63 | int size; 64 | int number; 65 | struct atom * a; 66 | }; 67 | 68 | typedef struct _pbc_ctx { char _data[PBC_CONTEXT_CAP]; } pbc_ctx[1]; 69 | 70 | int _pbcC_open(pbc_ctx , void *buffer, int size); // <=0 failed 71 | int _pbcC_open_packed(pbc_ctx _ctx, int ptype, void *buffer, int size); 72 | void _pbcC_close(pbc_ctx); 73 | 74 | static inline double 75 | read_double(struct atom * a) { 76 | union { 77 | uint64_t i; 78 | double d; 79 | } u; 80 | u.i = (uint64_t) a->v.i.low | (uint64_t) a->v.i.hi << 32; 81 | return u.d; 82 | } 83 | 84 | static inline float 85 | read_float(struct atom * a) { 86 | union { 87 | uint32_t i; 88 | float f; 89 | } u; 90 | u.i = a->v.i.low; 91 | return u.f; 92 | } 93 | 94 | static inline void 95 | double_encode(double v , uint8_t * buffer) { 96 | union { 97 | double v; 98 | uint64_t e; 99 | } u; 100 | u.v = v; 101 | buffer[0] = (uint8_t) (u.e & 0xff); 102 | buffer[1] = (uint8_t) (u.e >> 8 & 0xff); 103 | buffer[2] = (uint8_t) (u.e >> 16 & 0xff); 104 | buffer[3] = (uint8_t) (u.e >> 24 & 0xff); 105 | buffer[4] = (uint8_t) (u.e >> 32 & 0xff); 106 | buffer[5] = (uint8_t) (u.e >> 40 & 0xff); 107 | buffer[6] = (uint8_t) (u.e >> 48 & 0xff); 108 | buffer[7] = (uint8_t) (u.e >> 56 & 0xff); 109 | } 110 | 111 | static inline void 112 | float_encode(float v , uint8_t * buffer) { 113 | union { 114 | float v; 115 | uint32_t e; 116 | } u; 117 | u.v = v; 118 | buffer[0] = (uint8_t) (u.e & 0xff); 119 | buffer[1] = (uint8_t) (u.e >> 8 & 0xff); 120 | buffer[2] = (uint8_t) (u.e >> 16 & 0xff); 121 | buffer[3] = (uint8_t) (u.e >> 24 & 0xff); 122 | } 123 | 124 | #define CHECK_LEND(a,err) if ((a->wire_id & 7) != WT_LEND) return err; 125 | 126 | #if 0 127 | /* maybe we don't need check these wire type */ 128 | #define CHECK_VARINT(a,err) if ((a->wire_id & 7) != WT_VARINT) return err; 129 | #define CHECK_BIT32(a,err) if ((a->wire_id & 7) != WT_BIT32) return err; 130 | #define CHECK_BIT64(a,err) if ((a->wire_id & 7) != WT_BIT64) return err; 131 | 132 | #else 133 | 134 | #define CHECK_VARINT(a,err) 135 | #define CHECK_BIT32(a,err) 136 | #define CHECK_BIT64(a,err) 137 | 138 | #endif 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/map.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_MAP_H 2 | #define PROTOBUF_C_MAP_H 3 | 4 | #include "alloc.h" 5 | 6 | struct map_ip; 7 | struct map_si; 8 | struct map_sp; 9 | 10 | struct map_kv { 11 | int id; 12 | void *pointer; 13 | }; 14 | 15 | struct map_si * _pbcM_si_new(struct map_kv * table, int size); 16 | int _pbcM_si_query(struct map_si *map, const char *key, int *result); 17 | void _pbcM_si_delete(struct map_si *map); 18 | 19 | struct map_ip * _pbcM_ip_new(struct map_kv * table, int size); 20 | struct map_ip * _pbcM_ip_combine(struct map_ip * a, struct map_ip * b); 21 | void * _pbcM_ip_query(struct map_ip * map, int id); 22 | void _pbcM_ip_delete(struct map_ip *map); 23 | 24 | struct map_sp * _pbcM_sp_new(int max, struct heap *h); 25 | void _pbcM_sp_insert(struct map_sp *map, const char *key, void * value); 26 | void * _pbcM_sp_query(struct map_sp *map, const char *key); 27 | void ** _pbcM_sp_query_insert(struct map_sp *map, const char *key); 28 | void _pbcM_sp_delete(struct map_sp *map); 29 | void _pbcM_sp_foreach(struct map_sp *map, void (*func)(void *p)); 30 | void _pbcM_sp_foreach_ud(struct map_sp *map, void (*func)(void *p, void *ud), void *ud); 31 | void * _pbcM_sp_next(struct map_sp *map, const char ** key); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/pattern.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_PATTERN_H 2 | #define PROTOBUF_C_PATTERN_H 3 | 4 | #include "pbc.h" 5 | #include "context.h" 6 | #include "array.h" 7 | 8 | struct _pattern_field { 9 | int id; 10 | int offset; 11 | int ptype; 12 | int ctype; 13 | int label; 14 | pbc_var defv; 15 | }; 16 | 17 | struct pbc_pattern { 18 | struct pbc_env * env; 19 | int count; 20 | struct _pattern_field f[1]; 21 | }; 22 | 23 | struct pbc_pattern * _pbcP_new(struct pbc_env * env, int n); 24 | int _pbcP_unpack_packed(uint8_t *buffer, int size, int ptype, pbc_array array); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/proto.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | #include "proto.h" 3 | #include "pattern.h" 4 | #include "map.h" 5 | #include "alloc.h" 6 | #include "stringpool.h" 7 | #include "bootstrap.h" 8 | 9 | #include 10 | #include 11 | 12 | const char * 13 | pbc_error(struct pbc_env * p) { 14 | const char *err = p->lasterror; 15 | p->lasterror = ""; 16 | return err; 17 | } 18 | 19 | struct _message * 20 | _pbcP_get_message(struct pbc_env * p , const char *name) { 21 | return (struct _message *)_pbcM_sp_query(p->msgs, name); 22 | } 23 | 24 | struct pbc_env * 25 | pbc_new(void) { 26 | struct pbc_env * p = (struct pbc_env *)malloc(sizeof(*p)); 27 | p->files = _pbcM_sp_new(0 , NULL); 28 | p->enums = _pbcM_sp_new(0 , NULL); 29 | p->msgs = _pbcM_sp_new(0 , NULL); 30 | p->lasterror = ""; 31 | 32 | _pbcB_init(p); 33 | 34 | return p; 35 | } 36 | 37 | static void 38 | free_enum(void *p) { 39 | struct _enum * e = (struct _enum *)p; 40 | _pbcM_ip_delete(e->id); 41 | _pbcM_si_delete(e->name); 42 | 43 | free(p); 44 | } 45 | 46 | static void 47 | free_stringpool(void *p) { 48 | _pbcS_delete((struct _stringpool *)p); 49 | } 50 | 51 | static void 52 | free_msg(void *p) { 53 | struct _message * m = (struct _message *)p; 54 | if (m->id) 55 | _pbcM_ip_delete(m->id); 56 | free(m->def); 57 | _pbcM_sp_foreach(m->name, free); 58 | _pbcM_sp_delete(m->name); 59 | free(p); 60 | } 61 | 62 | void 63 | pbc_delete(struct pbc_env *p) { 64 | _pbcM_sp_foreach(p->enums, free_enum); 65 | _pbcM_sp_delete(p->enums); 66 | 67 | _pbcM_sp_foreach(p->msgs, free_msg); 68 | _pbcM_sp_delete(p->msgs); 69 | 70 | _pbcM_sp_foreach(p->files, free_stringpool); 71 | _pbcM_sp_delete(p->files); 72 | 73 | free(p); 74 | } 75 | 76 | struct _enum * 77 | _pbcP_push_enum(struct pbc_env * p, const char *name, struct map_kv *table, int sz) { 78 | void * check = _pbcM_sp_query(p->enums, name); 79 | if (check) 80 | return NULL; 81 | struct _enum * v = (struct _enum *)malloc(sizeof(*v)); 82 | v->key = name; 83 | v->id = _pbcM_ip_new(table,sz); 84 | v->name = _pbcM_si_new(table,sz); 85 | v->default_v->e.id = table[0].id; 86 | v->default_v->e.name = (const char *)table[0].pointer; 87 | 88 | _pbcM_sp_insert(p->enums, name , v); 89 | return v; 90 | } 91 | 92 | void 93 | _pbcP_push_message(struct pbc_env * p, const char *name, struct _field *f , pbc_array queue) { 94 | struct _message * m = (struct _message *)_pbcM_sp_query(p->msgs, name); 95 | if (m==NULL) { 96 | m = (struct _message *)malloc(sizeof(*m)); 97 | m->def = NULL; 98 | m->key = name; 99 | m->id = NULL; 100 | m->name = _pbcM_sp_new(0 , NULL); 101 | m->env = p; 102 | _pbcM_sp_insert(p->msgs, name, m); 103 | } 104 | struct _field * field = (struct _field *)malloc(sizeof(*field)); 105 | memcpy(field,f,sizeof(*f)); 106 | _pbcM_sp_insert(m->name, field->name, field); 107 | pbc_var atom; 108 | atom->m.buffer = field; 109 | if (f->type == PTYPE_MESSAGE || f->type == PTYPE_ENUM) { 110 | _pbcA_push(queue, atom); 111 | } 112 | } 113 | 114 | struct _iter { 115 | int count; 116 | struct map_kv * table; 117 | }; 118 | 119 | static void 120 | _count(void *p, void *ud) { 121 | struct _iter *iter = (struct _iter *)ud; 122 | iter->count ++; 123 | } 124 | 125 | static void 126 | _set_table(void *p, void *ud) { 127 | struct _field * field = (struct _field *)p; 128 | struct _iter *iter = (struct _iter *)ud; 129 | iter->table[iter->count].id = field->id; 130 | iter->table[iter->count].pointer = field; 131 | ++iter->count; 132 | } 133 | 134 | struct _message * 135 | _pbcP_init_message(struct pbc_env * p, const char *name) { 136 | struct _message * m = (struct _message *)_pbcM_sp_query(p->msgs, name); 137 | if (m == NULL) { 138 | m = (struct _message *)malloc(sizeof(*m)); 139 | m->def = NULL; 140 | m->key = name; 141 | m->id = NULL; 142 | m->name = _pbcM_sp_new(0 , NULL); 143 | m->env = p; 144 | _pbcM_sp_insert(p->msgs, name, m); 145 | 146 | return m; 147 | } 148 | if (m->id) { 149 | // extend message, delete old id map. 150 | _pbcM_ip_delete(m->id); 151 | } 152 | struct _iter iter = { 0, NULL }; 153 | _pbcM_sp_foreach_ud(m->name, _count, &iter); 154 | iter.table = (struct map_kv *)malloc(iter.count * sizeof(struct map_kv)); 155 | iter.count = 0; 156 | _pbcM_sp_foreach_ud(m->name, _set_table, &iter); 157 | 158 | m->id = _pbcM_ip_new(iter.table , iter.count); 159 | 160 | free(iter.table); 161 | 162 | return m; 163 | } 164 | 165 | int 166 | _pbcP_message_default(struct _message * m, const char * name, pbc_var defv) { 167 | struct _field * f= (struct _field *)_pbcM_sp_query(m->name, name); 168 | if (f==NULL) { 169 | // invalid key 170 | defv->p[0] = NULL; 171 | defv->p[1] = NULL; 172 | return -1; 173 | } 174 | *defv = *(f->default_v); 175 | return f->type; 176 | } 177 | 178 | int 179 | _pbcP_type(struct _field * field, const char ** type) { 180 | if (field == NULL) { 181 | return 0; 182 | } 183 | int ret = 0; 184 | switch (field->type) { 185 | case PTYPE_DOUBLE: 186 | case PTYPE_FLOAT: 187 | ret = PBC_REAL; 188 | break; 189 | case PTYPE_INT64: 190 | case PTYPE_SINT64: 191 | ret = PBC_INT64; 192 | break; 193 | case PTYPE_INT32: 194 | case PTYPE_SINT32: 195 | ret = PBC_INT; 196 | break; 197 | case PTYPE_UINT32: 198 | case PTYPE_UINT64: 199 | ret = PBC_UINT; 200 | break; 201 | case PTYPE_FIXED32: 202 | case PTYPE_SFIXED32: 203 | ret = PBC_FIXED32; 204 | break; 205 | case PTYPE_SFIXED64: 206 | case PTYPE_FIXED64: 207 | ret = PBC_FIXED64; 208 | break; 209 | case PTYPE_BOOL: 210 | ret = PBC_BOOL; 211 | break; 212 | case PTYPE_STRING: 213 | ret = PBC_STRING; 214 | break; 215 | case PTYPE_BYTES: 216 | ret = PBC_BYTES; 217 | break; 218 | case PTYPE_ENUM: 219 | ret = PBC_ENUM; 220 | if (type) { 221 | *type = field->type_name.e->key; 222 | } 223 | break; 224 | case PTYPE_MESSAGE: 225 | ret = PBC_MESSAGE; 226 | if (type) { 227 | *type = field->type_name.m->key; 228 | } 229 | break; 230 | default: 231 | return 0; 232 | } 233 | if (field->label == LABEL_REPEATED || 234 | field->label == LABEL_PACKED) { 235 | ret |= PBC_REPEATED; 236 | } 237 | 238 | return ret; 239 | } 240 | 241 | int 242 | pbc_type(struct pbc_env * p, const char * type_name , const char * key , const char ** type) { 243 | struct _message *m = _pbcP_get_message(p, type_name); 244 | if (m==NULL) { 245 | return 0; 246 | } 247 | if (key == NULL) { 248 | return PBC_NOEXIST; 249 | } 250 | struct _field * field = (struct _field *)_pbcM_sp_query(m->name, key); 251 | return _pbcP_type(field, type); 252 | } 253 | 254 | int 255 | pbc_enum_id(struct pbc_env *env, const char *enum_type, const char *enum_name) { 256 | struct _enum *enum_map = (struct _enum *)_pbcM_sp_query(env->enums, enum_type); 257 | if(!enum_map) { 258 | return -1; 259 | } 260 | int32_t enum_id = 0; 261 | int err = _pbcM_si_query(enum_map->name, enum_name, &enum_id); 262 | if(err) { 263 | return -1; 264 | } 265 | return enum_id; 266 | } 267 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/proto.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUFC_PROTO_H 2 | #define PROTOBUFC_PROTO_H 3 | 4 | #include "pbc.h" 5 | #include "map.h" 6 | #include "array.h" 7 | #ifndef _MSC_VER 8 | #include 9 | #endif 10 | #include 11 | 12 | struct map_ip; 13 | struct map_si; 14 | struct map_sp; 15 | struct _message; 16 | struct _enum; 17 | 18 | #define LABEL_OPTIONAL 0 19 | #define LABEL_REQUIRED 1 20 | #define LABEL_REPEATED 2 21 | #define LABEL_PACKED 3 22 | 23 | struct _field { 24 | int id; 25 | const char *name; 26 | int type; 27 | int label; 28 | pbc_var default_v; 29 | union { 30 | const char * n; 31 | struct _message * m; 32 | struct _enum * e; 33 | } type_name; 34 | }; 35 | 36 | struct _message { 37 | const char * key; 38 | struct map_ip * id; // id -> _field 39 | struct map_sp * name; // string -> _field 40 | struct pbc_rmessage * def; // default message 41 | struct pbc_env * env; 42 | }; 43 | 44 | struct _enum { 45 | const char * key; 46 | struct map_ip * id; 47 | struct map_si * name; 48 | pbc_var default_v; 49 | }; 50 | 51 | struct pbc_env { 52 | struct map_sp * files; // string -> void * 53 | struct map_sp * enums; // string -> _enum 54 | struct map_sp * msgs; // string -> _message 55 | const char * lasterror; 56 | }; 57 | 58 | struct _message * _pbcP_init_message(struct pbc_env * p, const char *name); 59 | void _pbcP_push_message(struct pbc_env * p, const char *name, struct _field *f , pbc_array queue); 60 | struct _enum * _pbcP_push_enum(struct pbc_env * p, const char *name, struct map_kv *table, int sz ); 61 | int _pbcP_message_default(struct _message * m, const char * name, pbc_var defv); 62 | struct _message * _pbcP_get_message(struct pbc_env * p, const char *name); 63 | int _pbcP_type(struct _field * field, const char **type); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/stringpool.c: -------------------------------------------------------------------------------- 1 | #include "alloc.h" 2 | 3 | #include 4 | #include 5 | 6 | #define PAGE_SIZE 256 7 | 8 | struct _stringpool { 9 | char * buffer; 10 | size_t len; 11 | struct _stringpool *next; 12 | }; 13 | 14 | struct _stringpool * 15 | _pbcS_new(void) { 16 | struct _stringpool * ret = (struct _stringpool *)malloc(sizeof(struct _stringpool) + PAGE_SIZE); 17 | ret->buffer = (char *)(ret + 1); 18 | ret->len = 0; 19 | ret->next = NULL; 20 | return ret; 21 | } 22 | 23 | void 24 | _pbcS_delete(struct _stringpool *pool) { 25 | while(pool) { 26 | struct _stringpool *next = pool->next; 27 | free(pool); 28 | pool = next; 29 | } 30 | } 31 | 32 | const char * 33 | _pbcS_build(struct _stringpool *pool, const char * str , int sz) { 34 | size_t s = sz + 1; 35 | if (s < PAGE_SIZE - pool->len) { 36 | char * ret = pool->buffer + pool->len; 37 | memcpy(pool->buffer + pool->len, str, s); 38 | pool->len += s; 39 | return ret; 40 | } 41 | if (s > PAGE_SIZE) { 42 | struct _stringpool * next = (struct _stringpool *)malloc(sizeof(struct _stringpool) + s); 43 | next->buffer = (char *)(next + 1); 44 | memcpy(next->buffer, str, s); 45 | next->len = s; 46 | next->next = pool->next; 47 | pool->next = next; 48 | return next->buffer; 49 | } 50 | struct _stringpool *next = (struct _stringpool *)malloc(sizeof(struct _stringpool) + PAGE_SIZE); 51 | next->buffer = pool->buffer; 52 | next->next = pool->next; 53 | next->len = pool->len; 54 | 55 | pool->next = next; 56 | pool->buffer = (char *)(next + 1); 57 | memcpy(pool->buffer, str, s); 58 | pool->len = s; 59 | return pool->buffer; 60 | } 61 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/stringpool.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_STRINGPOOL_H 2 | #define PROTOBUF_C_STRINGPOOL_H 3 | 4 | struct _stringpool; 5 | 6 | struct _stringpool * _pbcS_new(void); 7 | void _pbcS_delete(struct _stringpool *pool); 8 | const char * _pbcS_build(struct _stringpool *pool, const char * str , int sz); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/varint.c: -------------------------------------------------------------------------------- 1 | #include "varint.h" 2 | 3 | #include "pbc.h" 4 | 5 | #include 6 | 7 | inline int 8 | _pbcV_encode32(uint32_t number, uint8_t buffer[10]) 9 | { 10 | if (number < 0x80) { 11 | buffer[0] = (uint8_t) number ; 12 | return 1; 13 | } 14 | buffer[0] = (uint8_t) (number | 0x80 ); 15 | if (number < 0x4000) { 16 | buffer[1] = (uint8_t) (number >> 7 ); 17 | return 2; 18 | } 19 | buffer[1] = (uint8_t) ((number >> 7) | 0x80 ); 20 | if (number < 0x200000) { 21 | buffer[2] = (uint8_t) (number >> 14); 22 | return 3; 23 | } 24 | buffer[2] = (uint8_t) ((number >> 14) | 0x80 ); 25 | if (number < 0x10000000) { 26 | buffer[3] = (uint8_t) (number >> 21); 27 | return 4; 28 | } 29 | buffer[3] = (uint8_t) ((number >> 21) | 0x80 ); 30 | buffer[4] = (uint8_t) (number >> 28); 31 | return 5; 32 | } 33 | 34 | int 35 | _pbcV_encode(uint64_t number, uint8_t buffer[10]) 36 | { 37 | if ((number & 0xffffffff) == number) { 38 | return _pbcV_encode32((uint32_t)number , buffer); 39 | } 40 | int i = 0; 41 | do { 42 | buffer[i] = (uint8_t)(number | 0x80); 43 | number >>= 7; 44 | ++i; 45 | } while (number >= 0x80); 46 | buffer[i] = (uint8_t)number; 47 | return i+1; 48 | } 49 | 50 | int 51 | _pbcV_decode(uint8_t buffer[10], struct longlong *result) { 52 | if (!(buffer[0] & 0x80)) { 53 | result->low = buffer[0]; 54 | result->hi = 0; 55 | return 1; 56 | } 57 | uint32_t r = buffer[0] & 0x7f; 58 | int i; 59 | for (i=1;i<4;i++) { 60 | r |= ((buffer[i]&0x7f) << (7*i)); 61 | if (!(buffer[i] & 0x80)) { 62 | result->low = r; 63 | result->hi = 0; 64 | return i+1; 65 | } 66 | } 67 | uint64_t lr = 0; 68 | for (i=4;i<10;i++) { 69 | lr |= ((uint64_t)(buffer[i] & 0x7f) << (7*(i-4))); 70 | if (!(buffer[i] & 0x80)) { 71 | result->hi = (uint32_t)(lr >> 4); 72 | result->low = r | (((uint32_t)lr & 0xf) << 28); 73 | return i+1; 74 | } 75 | } 76 | 77 | result->low = 0; 78 | result->hi = 0; 79 | return 10; 80 | } 81 | 82 | int 83 | _pbcV_zigzag32(int32_t n, uint8_t buffer[10]) 84 | { 85 | n = (n << 1) ^ (n >> 31); 86 | return _pbcV_encode32(n,buffer); 87 | } 88 | 89 | int 90 | _pbcV_zigzag(int64_t n, uint8_t buffer[10]) 91 | { 92 | n = (n << 1) ^ (n >> 63); 93 | return _pbcV_encode(n,buffer); 94 | } 95 | 96 | void 97 | _pbcV_dezigzag64(struct longlong *r) 98 | { 99 | uint32_t low = r->low; 100 | r->low = ((low >> 1) | ((r->hi & 1) << 31)) ^ - (low & 1); 101 | r->hi = (r->hi >> 1) ^ - (low & 1); 102 | } 103 | 104 | void 105 | _pbcV_dezigzag32(struct longlong *r) 106 | { 107 | uint32_t low = r->low; 108 | r->low = (low >> 1) ^ - (low & 1); 109 | r->hi = -(low >> 31); 110 | } 111 | -------------------------------------------------------------------------------- /lualib-src/pbc/src/varint.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOBUF_C_VARINT_H 2 | #define PROTOBUF_C_VARINT_H 3 | 4 | #include 5 | 6 | struct longlong { 7 | uint32_t low; 8 | uint32_t hi; 9 | }; 10 | 11 | int _pbcV_encode32(uint32_t number, uint8_t buffer[10]); 12 | int _pbcV_encode(uint64_t number, uint8_t buffer[10]); 13 | int _pbcV_zigzag32(int32_t number, uint8_t buffer[10]); 14 | int _pbcV_zigzag(int64_t number, uint8_t buffer[10]); 15 | 16 | int _pbcV_decode(uint8_t buffer[10], struct longlong *result); 17 | void _pbcV_dezigzag64(struct longlong *r); 18 | void _pbcV_dezigzag32(struct longlong *r); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/addressbook.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "readfile.h" 9 | 10 | static void 11 | dump(uint8_t *buffer, int sz) { 12 | int i , j; 13 | for (i=0;i=32 && c<127) { 19 | printf("%c",c); 20 | } else { 21 | printf("."); 22 | } 23 | } 24 | printf("\n"); 25 | } 26 | } 27 | 28 | printf("\n"); 29 | } 30 | 31 | static void 32 | test_rmessage(struct pbc_env *env, struct pbc_slice *slice) { 33 | struct pbc_rmessage * m = pbc_rmessage_new(env, "tutorial.Person", slice); 34 | if (m==NULL) { 35 | printf("Error : %s",pbc_error(env)); 36 | return; 37 | } 38 | printf("name = %s\n", pbc_rmessage_string(m , "name" , 0 , NULL)); 39 | printf("id = %d\n", pbc_rmessage_integer(m , "id" , 0 , NULL)); 40 | printf("email = %s\n", pbc_rmessage_string(m , "email" , 0 , NULL)); 41 | 42 | int phone_n = pbc_rmessage_size(m, "phone"); 43 | int i; 44 | const char * field_name; 45 | pbc_type(env, "tutorial.Person", "phone", &field_name); 46 | printf("phone type [%s]\n",field_name); 47 | 48 | for (i=0;i 6 | 7 | int 8 | main() 9 | { 10 | pbc_array array; 11 | pbc_var v; 12 | 13 | _pbcA_open(array); 14 | 15 | int i ; 16 | 17 | for (i=0;i<100;i++) { 18 | v->real = (double)i; 19 | printf("push %d\n",i); 20 | _pbcA_push(array, v); 21 | } 22 | 23 | int s = pbc_array_size(array); 24 | 25 | for (i=0;ireal); 28 | } 29 | 30 | _pbcA_close(array); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/decode.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "readfile.h" 8 | 9 | static void 10 | decode_all(void *ud , int type, const char * typename , union pbc_value *v, int id, const char *key) { 11 | printf("%s : ", key ) ; 12 | switch(type & ~PBC_REPEATED) { 13 | case PBC_MESSAGE: 14 | printf("[%s] -> \n" , typename); 15 | pbc_decode(ud, typename, &(v->s), decode_all, ud); 16 | printf("---------\n"); 17 | break; 18 | case PBC_INT: 19 | printf("%d\n", (int)v->i.low); 20 | break; 21 | case PBC_REAL: 22 | printf("%lf\n", v->f); 23 | break; 24 | case PBC_BOOL: 25 | printf("<%s>\n", v->i.low ? "true" : "false"); 26 | break; 27 | case PBC_ENUM: 28 | printf("[%s:%d]\n", v->e.name , v->e.id); 29 | break; 30 | case PBC_STRING: { 31 | char buffer[v->s.len+1]; 32 | memcpy(buffer, v->s.buffer, v->s.len); 33 | buffer[v->s.len] = '\0'; 34 | printf("\"%s\"\n", buffer); 35 | break; 36 | } 37 | case PBC_BYTES: { 38 | int i; 39 | uint8_t *buffer = v->s.buffer; 40 | for (i=0;is.len;i++) { 41 | printf("%02X ",buffer[i]); 42 | } 43 | printf("\n"); 44 | break; 45 | } 46 | case PBC_INT64: { 47 | printf("0x%x%08x\n",v->i.hi, v->i.low); 48 | break; 49 | } 50 | case PBC_UINT: 51 | printf("%u\n",v->i.low); 52 | break; 53 | default: 54 | printf("!!! %d\n", type); 55 | break; 56 | } 57 | } 58 | 59 | void 60 | test_decode(struct pbc_env * env , const char * pb) 61 | { 62 | struct pbc_slice slice; 63 | read_file(pb, &slice); 64 | 65 | pbc_decode(env, "google.protobuf.FileDescriptorSet", &slice, decode_all , env); 66 | 67 | int ret = pbc_register(env, &slice); 68 | 69 | printf("Register %d\n",ret); 70 | 71 | free(slice.buffer); 72 | } 73 | 74 | int 75 | main(int argc, char *argv[]) 76 | { 77 | struct pbc_env * env = pbc_new(); 78 | 79 | test_decode(env,argv[1]); 80 | 81 | pbc_delete(env); 82 | 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/float.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "readfile.h" 9 | 10 | static void 11 | dump(uint8_t *buffer, int sz) { 12 | int i , j; 13 | for (i=0;i=32 && c<127) { 19 | printf("%c",c); 20 | } else { 21 | printf("."); 22 | } 23 | } 24 | printf("\n"); 25 | } 26 | } 27 | 28 | printf("\n"); 29 | } 30 | 31 | static void 32 | test_rmessage(struct pbc_env *env, struct pbc_slice *slice) { 33 | struct pbc_rmessage * m = pbc_rmessage_new(env, "real", slice); 34 | printf("f = %f\n", pbc_rmessage_real(m , "f" , 0 )); 35 | printf("d = %f\n", pbc_rmessage_real(m , "d" , 0 )); 36 | pbc_rmessage_delete(m); 37 | } 38 | 39 | static struct pbc_wmessage * 40 | test_wmessage(struct pbc_env * env) 41 | { 42 | struct pbc_wmessage * msg = pbc_wmessage_new(env, "real"); 43 | 44 | pbc_wmessage_real(msg, "f", 1.0); 45 | pbc_wmessage_real(msg, "d" , 4.0); 46 | 47 | return msg; 48 | } 49 | 50 | int 51 | main() 52 | { 53 | struct pbc_slice slice; 54 | read_file("float.pb", &slice); 55 | if (slice.buffer == NULL) 56 | return 1; 57 | struct pbc_env * env = pbc_new(); 58 | pbc_register(env, &slice); 59 | 60 | free(slice.buffer); 61 | 62 | struct pbc_wmessage *msg = test_wmessage(env); 63 | 64 | pbc_wmessage_buffer(msg, &slice); 65 | 66 | dump(slice.buffer, slice.len); 67 | 68 | test_rmessage(env, &slice); 69 | 70 | pbc_wmessage_delete(msg); 71 | pbc_delete(env); 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/float.proto: -------------------------------------------------------------------------------- 1 | message real { 2 | optional float f = 1; 3 | optional double d = 2; 4 | } 5 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/map.c: -------------------------------------------------------------------------------- 1 | #include "src/map.h" 2 | 3 | #include 4 | #include 5 | 6 | int 7 | main() 8 | { 9 | struct map_kv kv[] = { 10 | {1,"alice"}, 11 | {3,"bob" }, 12 | {99,"carol"}, 13 | }; 14 | 15 | struct map_ip * map = _pbcM_ip_new(kv, sizeof(kv)/sizeof(kv[0])); 16 | struct map_si * map2 = _pbcM_si_new(kv, sizeof(kv)/sizeof(kv[0])); 17 | int i; 18 | 19 | for (i=0;i<100;i++) { 20 | void *p= _pbcM_ip_query(map,i); 21 | if (p) { 22 | int id = 0; 23 | _pbcM_si_query(map2,p,&id); 24 | printf("%d %s\n",id,(const char *)p); 25 | } 26 | } 27 | 28 | struct map_sp * map3 = _pbcM_sp_new(0, NULL); 29 | _pbcM_sp_insert(map3,"Alice","alice"); 30 | _pbcM_sp_insert(map3,"Bob","bob"); 31 | 32 | void ** r = _pbcM_sp_query_insert(map3, "Carol"); 33 | *r = "carol"; 34 | 35 | r = _pbcM_sp_query_insert(map3, "Alice"); 36 | *r = "not alice"; 37 | 38 | printf("%s\n",(const char *)_pbcM_sp_query(map3,"Alice")); 39 | printf("%s\n",(const char *)_pbcM_sp_query(map3,"Bob")); 40 | printf("%s\n",(const char *)_pbcM_sp_query(map3,"Carol")); 41 | 42 | const char * key = NULL; 43 | for (;;) { 44 | void * v = _pbcM_sp_next(map3, &key); 45 | if (key == NULL) 46 | break; 47 | printf("%s : %s\n", key, (const char *)v); 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/pattern.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "readfile.h" 9 | 10 | static void 11 | dump(uint8_t *buffer, int sz) { 12 | int i , j; 13 | for (i=0;i=32 && c<127) { 19 | printf("%c",c); 20 | } else { 21 | printf("."); 22 | } 23 | } 24 | printf("\n"); 25 | } 26 | } 27 | 28 | printf("\n"); 29 | } 30 | 31 | static struct pbc_pattern *pat; 32 | static struct pbc_pattern *pat_phone; 33 | 34 | struct person_phone { 35 | struct pbc_slice number; 36 | int32_t type; 37 | }; 38 | 39 | struct person { 40 | struct pbc_slice name; 41 | int32_t id; 42 | struct pbc_slice email; 43 | pbc_array phone; 44 | pbc_array test; 45 | }; 46 | 47 | 48 | static void 49 | test_pattern_unpack(struct pbc_env *env, struct pbc_slice * slice) { 50 | struct person p; 51 | int r = pbc_pattern_unpack(pat, slice, &p); 52 | if (r>=0) { 53 | printf("name = %s\n",(const char *)p.name.buffer); 54 | printf("id = %d\n",p.id); 55 | printf("email = %s\n",(const char *)p.email.buffer); 56 | int n = pbc_array_size(p.phone); 57 | int i; 58 | for (i=0;ilen = 0; 102 | return slice->len; 103 | } 104 | 105 | pbc_array_push_slice(p.phone, &phone_slice); 106 | 107 | pbc_pattern_set_default(pat_phone, &phone); 108 | 109 | phone.number.buffer = (void *)"87654321"; 110 | phone.number.len = -1; 111 | 112 | char temp2[128]; 113 | struct pbc_slice phone_slice2 = { temp2, sizeof(temp2) }; 114 | 115 | unused = pbc_pattern_pack(pat_phone, &phone , &phone_slice2); 116 | 117 | if (unused < 0) { 118 | slice->len = 0; 119 | return slice->len; 120 | } 121 | 122 | pbc_array_push_slice(p.phone, &phone_slice2); 123 | 124 | int i; 125 | for (i=0;i<3;i++) { 126 | pbc_array_push_integer(p.test, -i*4,0); 127 | } 128 | 129 | int r = pbc_pattern_pack(pat, &p, slice); 130 | 131 | pbc_pattern_close_arrays(pat,&p); 132 | printf("pack into %d bytes\n", slice->len); 133 | 134 | return r; 135 | } 136 | 137 | int 138 | main() 139 | { 140 | struct pbc_slice slice; 141 | read_file("addressbook.pb", &slice); 142 | if (slice.buffer == NULL) 143 | return 1; 144 | struct pbc_env * env = pbc_new(); 145 | pbc_register(env, &slice); 146 | 147 | free(slice.buffer); 148 | 149 | pat = pbc_pattern_new(env, "tutorial.Person" , 150 | "name %s id %d email %s phone %a test %a", 151 | offsetof(struct person, name) , 152 | offsetof(struct person, id) , 153 | offsetof(struct person, email) , 154 | offsetof(struct person, phone) , 155 | offsetof(struct person, test)); 156 | 157 | pat_phone = pbc_pattern_new(env, "tutorial.Person.PhoneNumber", 158 | "number %s type %d", 159 | offsetof(struct person_phone, number), 160 | offsetof(struct person_phone, type)); 161 | 162 | 163 | char buffer[4096]; 164 | struct pbc_slice message = { buffer, sizeof(buffer) }; 165 | 166 | test_pattern_pack(env, &message); 167 | 168 | dump(message.buffer, message.len); 169 | 170 | test_pattern_unpack(env, &message); 171 | 172 | pbc_pattern_delete(pat); 173 | pbc_pattern_delete(pat_phone); 174 | 175 | pbc_delete(env); 176 | 177 | return 0; 178 | } 179 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/pbc.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "readfile.h" 7 | 8 | void 9 | test_des(struct pbc_env * env , const char * pb) 10 | { 11 | struct pbc_slice slice; 12 | read_file(pb, &slice); 13 | 14 | struct pbc_rmessage * msg = pbc_rmessage_new(env, "google.protobuf.FileDescriptorSet", &slice); 15 | 16 | struct pbc_rmessage * file = pbc_rmessage_message(msg,"file",0); 17 | 18 | printf("name = %s\n",pbc_rmessage_string(file, "name", 0 , NULL)); 19 | printf("package = %s\n",pbc_rmessage_string(file, "package", 0 , NULL)); 20 | 21 | int sz = pbc_rmessage_size(file, "dependency"); 22 | printf("dependency[%d] =\n" , sz); 23 | int i; 24 | for (i=0;i 5 | #include 6 | 7 | static void 8 | read_file (const char *filename , struct pbc_slice *slice) { 9 | FILE *f = fopen(filename, "rb"); 10 | if (f == NULL) { 11 | slice->buffer = NULL; 12 | slice->len = 0; 13 | return; 14 | } 15 | fseek(f,0,SEEK_END); 16 | slice->len = ftell(f); 17 | fseek(f,0,SEEK_SET); 18 | slice->buffer = malloc(slice->len); 19 | if (fread(slice->buffer, 1 , slice->len , f) == 0) 20 | exit(1); 21 | fclose(f); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /lualib-src/pbc/test/test.c: -------------------------------------------------------------------------------- 1 | #include "pbc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define COUNT 1000000 8 | 9 | #include "readfile.h" 10 | 11 | static void 12 | test(struct pbc_env *env) { 13 | int i; 14 | for(i=0; i 2 | 3 | #include "varint.h" 4 | #include "pbc.h" 5 | 6 | static void 7 | dump(uint8_t buffer[10], int s) 8 | { 9 | int i; 10 | for (i=0;i 2 | #include 3 | #include 4 | #include 5 | 6 | #include "pbc.h" 7 | 8 | static void 9 | read_file(const char *filename , struct pbc_slice *slice) { 10 | FILE *f = fopen(filename, "rb"); 11 | if (f == NULL) { 12 | fprintf(stderr, "Can't open file %s\n", filename); 13 | exit(1); 14 | } 15 | fseek(f,0,SEEK_END); 16 | slice->len = ftell(f); 17 | fseek(f,0,SEEK_SET); 18 | slice->buffer = malloc(slice->len); 19 | fread(slice->buffer, 1 , slice->len , f); 20 | fclose(f); 21 | } 22 | 23 | static void 24 | dump_bytes(const char *data, size_t len) { 25 | size_t i; 26 | for (i = 0; i < len; i++) 27 | if (i == 0) 28 | fprintf(stdout, "%02x", 0xff & data[i]); 29 | else 30 | fprintf(stdout, " %02x", 0xff & data[i]); 31 | } 32 | 33 | static void dump_message(struct pbc_rmessage *m, int level); 34 | 35 | static void 36 | dump_value(struct pbc_rmessage *m, const char *key, int type, int idx, int level) { 37 | int i; 38 | for (i=0;i= data->len) { 140 | data->len *= 2; 141 | data->buffer = realloc(data->buffer, data->len); 142 | } 143 | ((uint8_t *)data->buffer)[idx] = (uint8_t)byte; 144 | } 145 | 146 | static void 147 | read_stdin(int mode, struct pbc_slice *data) { 148 | data->len = 128; 149 | data->buffer = malloc(data->len); 150 | int idx = 0; 151 | while(!feof(stdin)) { 152 | int byte; 153 | int r = scanf("%d" , &byte); 154 | if (r == 0) { 155 | break; 156 | } 157 | push_byte(byte, data, idx); 158 | ++idx; 159 | } 160 | data->len = idx; 161 | } 162 | 163 | static void 164 | usage(const char *argv0) { 165 | printf(" -h help.\n" 166 | " -p protobuf file\n" 167 | " -m \n" 168 | " -d \n" 169 | " -D input from stdin (DEC number)\n" 170 | ); 171 | } 172 | 173 | int 174 | main(int argc , char * argv[]) 175 | { 176 | int ch; 177 | const char * proto = NULL; 178 | const char * message = NULL; 179 | const char * datafile = NULL; 180 | int mode = 0; 181 | while ((ch = getopt(argc, argv, "hDp:m:d:")) != -1) { 182 | switch(ch) { 183 | case 'h': 184 | usage(argv[0]); 185 | return 0; 186 | case 'p': 187 | proto = optarg; 188 | break; 189 | case 'm': 190 | message = optarg; 191 | break; 192 | case 'd': 193 | datafile = optarg; 194 | break; 195 | case 'D': 196 | mode = 10; 197 | break; 198 | default: 199 | usage(argv[0]); 200 | return 1; 201 | } 202 | } 203 | 204 | if (proto == NULL || message == NULL) { 205 | usage(argv[0]); 206 | return 1; 207 | } 208 | 209 | struct pbc_slice data; 210 | 211 | if (datafile == NULL) { 212 | read_stdin(mode, &data); 213 | } else { 214 | read_file(datafile , &data); 215 | } 216 | 217 | dump(proto , message , &data); 218 | 219 | return 0; 220 | } 221 | 222 | -------------------------------------------------------------------------------- /lualib/message.lua: -------------------------------------------------------------------------------- 1 | -- message.lua 2 | -- RPC based on protobuf 3 | local skynet = require 'skynet' 4 | local snax = require 'skynet.snax' 5 | local protobuf = require "protobuf" 6 | local message_map = require 'message_map' 7 | 8 | local message = {} 9 | 10 | message.mapping = message_map.mapping 11 | 12 | function message.unpack(msg, sz) 13 | local data = skynet.tostring(msg, sz) 14 | local netmsg = protobuf.decode("netmsg.NetMsg", data) 15 | if not netmsg then 16 | error("msg_unpack error") 17 | end 18 | return netmsg 19 | end 20 | 21 | function message.pack_raw(id, payload, code) 22 | local d = {id = id, payload = payload} 23 | if code ~= nil then d.code = code end 24 | return protobuf.encode("netmsg.NetMsg", d) 25 | end 26 | 27 | function message.pack(proto, data) 28 | local payload = protobuf.encode(proto, data) 29 | return message.pack_raw(proto, payload) 30 | end 31 | 32 | function message.dispatch(netmsg, ...) 33 | local id = netmsg.id 34 | local module, method = message_map.get_name(id) 35 | if not method then 36 | return message.pack_raw(-1, 'not found service', -1) 37 | end 38 | local ok, obj = pcall(snax.uniqueservice, module) 39 | if not ok then 40 | local msg = string.format('not found module %s', module) 41 | LOG_WARNING(msg) 42 | return message.pack_raw(id, msg, -1) 43 | end 44 | local proto_name = message_map.proto_name(module, method) 45 | ok, ret = pcall(obj.req[method], {name = proto_name, payload = netmsg.payload}, ...) 46 | if not ok then 47 | LOG_WARNING('call rpc service failed: %s', ret) 48 | return message.pack_raw(id, ret, -1) 49 | end 50 | return ret 51 | end 52 | 53 | function message.dispatch_game(netmsg, ...) 54 | local id = netmsg.id 55 | local module, method = message_map.get_name(id) 56 | if not method then 57 | return message.pack_raw(-1, 'not found service', -1) 58 | end 59 | local ok, obj = pcall(snax.uniqueservice, module) 60 | if not ok then 61 | local msg = string.format('not found module %s', module) 62 | LOG_WARNING(msg) 63 | return message.pack_raw(id, msg, -1) 64 | end 65 | local proto_name = message_map.proto_name(module, method) 66 | ok, ret = pcall(obj.post[method], {name = proto_name, payload = netmsg.payload}, ...) 67 | if not ok then 68 | LOG_WARNING('call rpc service failed: %s', ret) 69 | return message.pack_raw(id, string.format('post message to game failed: %s', ret)) 70 | end 71 | return nil 72 | end 73 | 74 | return message 75 | -------------------------------------------------------------------------------- /lualib/message_map.lua: -------------------------------------------------------------------------------- 1 | -- message_map.lua 2 | -- map between message id to RPC (module + mehtod) 3 | local messageids = require 'messageid' 4 | 5 | local message = {_ids = {}, _names = {}} 6 | 7 | local function full_name(module, method) 8 | return module .. '.' .. method 9 | end 10 | 11 | local function mapping(t) 12 | local ids = assert(messageids[t]) 13 | local base = ids[1] 14 | for k, v in pairs(ids) do 15 | if type(k) == 'string' then 16 | local id = base + v 17 | message._ids[id] = {module = t, method = k} 18 | message._names[full_name(t, k)] = id 19 | end 20 | end 21 | end 22 | 23 | function message.mapping(...) 24 | local ts = {...} 25 | for _, v in ipairs(ts) do 26 | mapping(v) 27 | end 28 | end 29 | 30 | function message.get_id(proto_name) 31 | return message._names[proto_name] 32 | end 33 | 34 | function message.get_name(id) 35 | local mm = message._ids[id] 36 | if not mm then return nil end 37 | return mm.module, mm.method 38 | end 39 | 40 | function message.proto_name(module, method) 41 | return full_name(module, method) 42 | end 43 | 44 | return message 45 | 46 | -------------------------------------------------------------------------------- /lualib/messageid.lua: -------------------------------------------------------------------------------- 1 | -- messageid.lua 2 | -- define all message id mapping to RPC methods 3 | -- TODO: to write a DSL parser 4 | local hall = { 5 | 100, 6 | 'Hall', 7 | GetGameList = 1, 8 | CreateRoom = 2, 9 | DestroyRoom = 3, 10 | SelectRoom = 4, 11 | ReconnRoom = 5, 12 | LeaveRoom = 6, 13 | GetUserInfo = 7, 14 | } 15 | 16 | local game = { 17 | 500, 18 | 'Game', 19 | EnterRoom = 1, 20 | PlayGame = 2, 21 | } 22 | 23 | return {hall = hall, game = game} 24 | -------------------------------------------------------------------------------- /lualib/msgserver0.lua: -------------------------------------------------------------------------------- 1 | -- msgserver0.lua 2 | -- a skynet msgserver without cache response 3 | local skynet = require "skynet" 4 | local netpack = require "skynet.netpack" 5 | local crypt = require "skynet.crypt" 6 | local socketdriver = require "skynet.socketdriver" 7 | local gateserver = require 'snax.gateserver' 8 | local assert = assert 9 | local b64encode = crypt.base64encode 10 | local b64decode = crypt.base64decode 11 | 12 | local user_online = {} -- username: user 13 | local connection = {} -- fd: user 14 | 15 | local server = {} 16 | 17 | skynet.register_protocol { 18 | name = "client", 19 | id = skynet.PTYPE_CLIENT, 20 | } 21 | 22 | function server.username(uid, subid, servername) 23 | return string.format("%s@%s#%s", b64encode(uid), b64encode(servername), b64encode(tostring(subid))) 24 | end 25 | 26 | function server.logout(username) 27 | local u = user_online[username] 28 | user_online[username] = nil 29 | if u.fd then 30 | gateserver.closeclient(u.fd) 31 | connection[u.fd] = nil 32 | end 33 | end 34 | 35 | function server.login(username, secret) 36 | assert(user_online[username] == nil) 37 | user_online[username] = { 38 | secret = secret, 39 | username = username 40 | } 41 | end 42 | 43 | function server.start(conf) 44 | local handler = {} 45 | local handshake = {} -- awaiting handshake client 46 | 47 | assert(conf.cmd_handler) 48 | -- compatible with skynet.msgserver 49 | local CMD = { 50 | login = assert(conf.login_handler), 51 | logout = assert(conf.logout_handler), 52 | kick = assert(conf.kick_handler), 53 | } 54 | 55 | function handler.command(cmd, source, ...) 56 | if CMD[cmd] then 57 | return CMD[cmd](...) 58 | else 59 | return conf.cmd_handler(cmd, source, ...) 60 | end 61 | end 62 | 63 | function handler.open(source, gateconf) 64 | local servername = assert(gateconf.servername) 65 | return conf.register_handler(servername) 66 | end 67 | 68 | function handler.connect(fd, addr) 69 | handshake[fd] = addr 70 | gateserver.openclient(fd) 71 | end 72 | 73 | function handler.disconnect(fd) 74 | handshake[fd] = nil 75 | local c = connection[fd] 76 | if c then 77 | c.fd = nil 78 | connection[fd] = nil 79 | if conf.disconnect_handler then 80 | conf.disconnect_handler(c.username) 81 | end 82 | end 83 | end 84 | 85 | handler.error = handler.disconnect 86 | 87 | -- atomic , no yield 88 | local function do_auth(fd, message, addr) 89 | local username, hmac = string.match(message, "([^:]*):([^:]*)") 90 | local u = user_online[username] 91 | if u == nil then 92 | return string.format("404 User Not Found:%s", username) 93 | end 94 | hmac = b64decode(hmac) 95 | local text = username 96 | local v = crypt.hmac_hash(u.secret, text) 97 | if v ~= hmac then 98 | return "401 Unauthorized" 99 | end 100 | u.fd = fd 101 | u.ip = addr 102 | connection[fd] = u 103 | end 104 | 105 | local function auth(fd, addr, msg, sz) 106 | local message = netpack.tostring(msg, sz) 107 | local ok, result = pcall(do_auth, fd, message, addr) 108 | if not ok then 109 | skynet.error(result) 110 | result = "400 Bad Request" 111 | end 112 | local close = result ~= nil 113 | if result == nil then 114 | result = "200 OK" 115 | end 116 | socketdriver.send(fd, netpack.pack(result)) 117 | if close then 118 | gateserver.closeclient(fd) 119 | end 120 | end 121 | 122 | local function do_request(fd, message) 123 | local u = assert(connection[fd], "invalid fd") 124 | local ok, result = pcall(conf.request_handler, u.username, message) 125 | result = result or "" 126 | if not ok then 127 | skynet.error(result) 128 | end 129 | local resp = string.pack(">s2", result) 130 | 131 | if connection[fd] then 132 | socketdriver.send(fd, resp) 133 | end 134 | end 135 | 136 | local function request(fd, msg, sz) 137 | local message = netpack.tostring(msg, sz) 138 | local ok, err = pcall(do_request, fd, message) 139 | -- not atomic, may yield 140 | if not ok then 141 | skynet.error(string.format("Invalid package %s : %s", err, message)) 142 | if connection[fd] then 143 | gateserver.closeclient(fd) 144 | end 145 | end 146 | end 147 | 148 | function handler.message(fd, msg, sz) 149 | local addr = handshake[fd] 150 | if addr then 151 | auth(fd, addr, msg, sz) 152 | handshake[fd] = nil 153 | else 154 | request(fd, msg, sz) 155 | end 156 | end 157 | 158 | return gateserver.start(handler) 159 | end 160 | 161 | return server 162 | -------------------------------------------------------------------------------- /lualib/random.lua: -------------------------------------------------------------------------------- 1 | 2 | local Random = {} 3 | 4 | -- 此函数用法等价于math.random 5 | -- Random.Get(m,n) 6 | do 7 | local randomtable 8 | local tablesize = 97 9 | 10 | function Random.Get(m, n) 11 | -- 初始化随机数与随机数表,生成97个[0,1)的随机数 12 | if not randomtable then 13 | -- 避免种子过小 14 | math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6))) 15 | randomtable = {} 16 | for i = 1, tablesize do 17 | randomtable[i] = math.random() 18 | end 19 | end 20 | 21 | local x = math.random() 22 | local i = 1 + math.floor(tablesize*x) -- i取值范围[1,97] 23 | x, randomtable[i] = randomtable[i], x -- 取x为随机数,同时保证randomtable的动态性 24 | 25 | if not m then 26 | return x 27 | elseif not n then 28 | n = m 29 | m = 1 30 | end 31 | 32 | --if not Check(m <= n) then return end 33 | 34 | local offset = x*(n-m+1) 35 | return m + math.floor(offset) 36 | end 37 | end 38 | 39 | -- 取得[m, n]连续范围内的k个不重复的随机数 40 | function Random.GetRange(m, n, k) 41 | 42 | --if not Check(m <= n) then return end 43 | --if not Check(k <= m-n+1) then return end 44 | 45 | local t = {} 46 | for i = m, n do 47 | t[#t + 1] = i 48 | end 49 | 50 | local size = #t 51 | for i = 1, k do 52 | local x = Random.Get(i, size) 53 | t[i], t[x] = t[x], t[i] -- t[i]与t[x]交换 54 | end 55 | 56 | local result = {} 57 | for i = 1, k do 58 | result[#result + 1] = t[i] 59 | end 60 | 61 | return result 62 | end 63 | 64 | -- 问题描述:"有N个物品,每个物品都有对应的被选中的概率,求随机选出k个物品" 65 | -- data是一个数组,每一个元素是这样的table{ id = 0, rate = 0 }, 其中id表示物品的id, 66 | -- rate表示物品被选中的概率.所有元素的rate值加起来为1 67 | -- 返回被选中的物品id 68 | function Random.GetIds(t, k) 69 | --if not Check(k <= #t) then return end 70 | 71 | local rate_left = 1 72 | for i = 1, k do 73 | local x = Random.Get() * rate_left 74 | local rate = 0 75 | local n 76 | for j = 1, #t do 77 | --Check(t[j].rate) 78 | rate = rate + t[j].rate 79 | if rate >= x then 80 | n = j 81 | break 82 | end 83 | end 84 | --if not Check(n, x) then return end 85 | -- t[i]与t[n]交换 86 | --[[local tmp = t[n] 87 | t[n] = t[i] 88 | t[i] = tmp--]] 89 | 90 | t[i], t[n] = t[n], t[i] 91 | rate_left = rate_left - t[i].rate 92 | end 93 | 94 | local result = {} 95 | for i = 1, k do 96 | --Check(t[i].id) 97 | result[#result + 1] = t[i].id 98 | end 99 | 100 | return result 101 | end 102 | 103 | -- 问题描述:"有N个物品,每个物品都有对应的被选中的概率,求随机选出1个物品" 104 | -- data是一个数组,每一个元素是这样的table{ id = 0, rate = 0 }, 其中id表示物品的id, 105 | -- rate表示物品被选中的概率.所有元素的rate值加起来为1 106 | -- 返回被选中的物品id 107 | function Random.GetId(t) 108 | local ids = Random.GetIds(t, 1) 109 | return ids[1] 110 | end 111 | 112 | return Random 113 | 114 | -------------------------------------------------------------------------------- /protocol/game.pb: -------------------------------------------------------------------------------- 1 | 2 | H 3 | 4 | game.protogame" 5 | EnterRoom 6 | rid (" 7 | PlayGame 8 | detail ( -------------------------------------------------------------------------------- /protocol/game.proto: -------------------------------------------------------------------------------- 1 | package game; 2 | 3 | message EnterRoom { 4 | required int32 rid = 1; 5 | } 6 | 7 | message PlayGame { 8 | optional bytes detail = 1; 9 | } 10 | -------------------------------------------------------------------------------- /protocol/hall.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinlynx/pigy/08755bae537f6e3fd32de3bf64237babf445e536/protocol/hall.pb -------------------------------------------------------------------------------- /protocol/hall.proto: -------------------------------------------------------------------------------- 1 | package hall; 2 | 3 | message GetUserInfo { 4 | } 5 | 6 | message UserRoomInfo { 7 | required string sname = 1; 8 | required int32 rid = 2; 9 | } 10 | 11 | message GetUserInfoResponse { 12 | optional UserRoomInfo room = 1; 13 | } 14 | 15 | message Room { 16 | required int32 rid = 1; 17 | required int32 gid = 2; 18 | required string sname = 3; 19 | } 20 | 21 | message GameInfo { 22 | repeated Room rooms = 1; 23 | required int32 gid = 2; 24 | } 25 | 26 | message GetGameList { 27 | } 28 | 29 | message GetGameListResponse { 30 | repeated GameInfo games = 1; 31 | } 32 | 33 | message ServerInfo { 34 | required string ip = 1; 35 | required int32 port = 2; 36 | } 37 | 38 | message CreateRoom { 39 | required int32 id = 1; 40 | } 41 | 42 | message CreateRoomResponse { 43 | required ServerInfo server = 1; 44 | required int32 rid = 2; 45 | } 46 | 47 | message SelectRoom { 48 | required int32 rid = 1; 49 | required string sname = 2; 50 | } 51 | 52 | message SelectRoomResponse { 53 | optional ServerInfo server = 1; 54 | required int32 code = 2; 55 | } 56 | 57 | // if user re-login and already entered in some room 58 | message ReconnRoom { 59 | } 60 | 61 | message ReconnRoomResponse { 62 | optional ServerInfo server = 1; 63 | required int32 code = 2; 64 | optional int32 rid = 3; 65 | } 66 | 67 | message LeaveRoom { 68 | } 69 | 70 | message LeaveRoomResponse { 71 | required int32 code = 1; 72 | } 73 | 74 | // destroy a room by its creator 75 | message DestroyRoom { 76 | required int32 id = 1; 77 | } 78 | 79 | message DestroyRoomResponse { 80 | required int32 code = 1; // 200 ok, 403 forbidden, 404 not found 81 | } 82 | 83 | -------------------------------------------------------------------------------- /protocol/netmsg.pb: -------------------------------------------------------------------------------- 1 | 2 | N 3 | netmsg.protonetmsg"6 4 | NetMsg 5 | 6 | id ( 7 | payload (  8 | code (:0 -------------------------------------------------------------------------------- /protocol/netmsg.proto: -------------------------------------------------------------------------------- 1 | package netmsg; 2 | 3 | message NetMsg { 4 | required int32 id = 1; 5 | required bytes payload = 2; 6 | optional int32 code = 3 [default = 0]; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /protocol/protoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinlynx/pigy/08755bae537f6e3fd32de3bf64237babf445e536/protocol/protoc -------------------------------------------------------------------------------- /protocol/protogenpb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | protofiles=`ls | grep .proto` 3 | rm -rf *.pb 4 | for f in ${protofiles[*]} 5 | do 6 | ./protoc $f -o ${f/".proto"/".pb"} 2>&1 7 | done 8 | -------------------------------------------------------------------------------- /redis/redis-server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinlynx/pigy/08755bae537f6e3fd32de3bf64237babf445e536/redis/redis-server -------------------------------------------------------------------------------- /sh/game.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PIGY_PORT=5188 4 | export PIGY_MYSQL_HOST='10.101.83.238' 5 | export PIGY_MYSQL_PORT=3306 6 | export PIGY_MYSQL_USER='drogo' 7 | export PIGY_MYSQL_DB='pigy' 8 | export PIGY_MYSQL_PWD='drogo' 9 | 10 | ./skynet/skynet ./etc/config.game 11 | -------------------------------------------------------------------------------- /sh/hall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PIGY_PORT=5188 4 | export PIGY_MYSQL_HOST='10.101.83.238' 5 | export PIGY_MYSQL_PORT=3306 6 | export PIGY_MYSQL_USER='drogo' 7 | export PIGY_MYSQL_DB='pigy' 8 | export PIGY_MYSQL_PWD='drogo' 9 | 10 | ./skynet/skynet ./etc/config.hall 11 | -------------------------------------------------------------------------------- /sh/login.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PIGY_PORT=5188 4 | export PIGY_MYSQL_HOST='10.101.83.238' 5 | export PIGY_MYSQL_PORT=3306 6 | export PIGY_MYSQL_USER='drogo' 7 | export PIGY_MYSQL_DB='pigy' 8 | export PIGY_MYSQL_PWD='drogo' 9 | 10 | ./skynet/skynet ./etc/config.login 11 | -------------------------------------------------------------------------------- /sh/pigy.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE IF NOT EXISTS `d_account` ( 3 | `id` int(11) unsigned NOT NULL COMMENT '系统编号,对应d_user表的uid', 4 | `pid` varchar(50) NOT NULL COMMENT '平台下发的id', 5 | `sdkid` int(11) unsigned NOT NULL COMMENT 'sdkid', 6 | `password` varchar(32) NOT NULL DEFAULT '', 7 | PRIMARY KEY (`id`), 8 | UNIQUE KEY `pid_sdkid` (`pid`,`sdkid`) 9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='帐号表'; 10 | 11 | -- 正在导出表 metoo.d_account 的数据:~12 rows (大约) 12 | DELETE FROM `d_account`; 13 | /*!40000 ALTER TABLE `d_account` DISABLE KEYS */; 14 | INSERT INTO `d_account` (`id`, `pid`, `sdkid`) VALUES 15 | (1, '188', 1), 16 | (2, '189', 1), 17 | (3, '190', 1), 18 | (4, '191', 1), 19 | (5, '192', 1), 20 | (6, '193', 1), 21 | (7, '194', 1), 22 | (8, '195', 1), 23 | (9, '196', 1), 24 | (10, '197', 1), 25 | (11, '198', 1), 26 | (12, '199', 1); 27 | /*!40000 ALTER TABLE `d_account` ENABLE KEYS */; 28 | 29 | CREATE TABLE IF NOT EXISTS `d_room` ( 30 | `id` INT(10) UNSIGNED NOT NULL, 31 | `game` SMALLINT(5) UNSIGNED NOT NULL, 32 | `server` VARCHAR(50) NOT NULL COLLATE 'utf8_unicode_ci', 33 | `creator` INT(10) UNSIGNED NULL DEFAULT NULL, 34 | `status` tinyint default 0, 35 | PRIMARY KEY (`id`) 36 | ) 37 | COLLATE='utf8_unicode_ci' 38 | ENGINE=MyISAM 39 | ; 40 | 41 | CREATE TABLE IF NOT EXISTS `d_user` ( 42 | `uid` INT(10) UNSIGNED NOT NULL, 43 | `room` INT(10) UNSIGNED NOT NULL DEFAULT 0, 44 | `money` BIGINT(20) UNSIGNED NOT NULL, 45 | `nick` VARCHAR(128) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci', 46 | PRIMARY KEY (`uid`) 47 | ) 48 | COLLATE='utf8_unicode_ci' 49 | ENGINE=MyISAM 50 | ; 51 | 52 | -------------------------------------------------------------------------------- /sh/redis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./redis/redis-server ./redis/redis1.conf 3 | 4 | -------------------------------------------------------------------------------- /test/c_game.lua: -------------------------------------------------------------------------------- 1 | -- c_game.lua 2 | -- connect to game server 3 | local socket = require "client.socket" 4 | local crypt = require "client.crypt" 5 | local util = require 'client_util' 6 | local protobuf = require 'protobuf' 7 | 8 | protobuf.register_file("protocol/netmsg.pb") 9 | protobuf.register_file("protocol/game.pb") 10 | 11 | local fd 12 | 13 | local c_game = {} 14 | 15 | function c_game.handshake(ip, port, secret, username) 16 | fd = assert(socket.connect(ip, port)) 17 | util.send_package(fd, crypt.base64encode(secret) .. '@' .. username) 18 | ok, msg = util.gs_recv_response(util.readpackage(fd)) 19 | return ok, msg 20 | end 21 | 22 | function c_game.enter_game(rid) 23 | util.gs_send_request(fd, util.encode("game.EnterRoom", {rid = rid})) 24 | end 25 | 26 | function c_game.send_game_msg(data) 27 | util.gs_send_request(fd, util.encode("game.PlayGame", {detail = data})) 28 | end 29 | 30 | function c_game.recv_game_msg() 31 | ok, msg = util.gs_recv_response(util.readpackage(fd)) 32 | assert(ok) 33 | return msg 34 | end 35 | 36 | function c_game.shutdown() 37 | socket.close(fd) 38 | end 39 | 40 | return c_game 41 | 42 | -------------------------------------------------------------------------------- /test/c_hall.lua: -------------------------------------------------------------------------------- 1 | -- c_hall.lua 2 | -- client connect to hall server 3 | local socket = require "client.socket" 4 | local crypt = require "client.crypt" 5 | local util = require 'client_util' 6 | local protobuf = require 'protobuf' 7 | 8 | protobuf.register_file("protocol/netmsg.pb") 9 | protobuf.register_file("protocol/hall.pb") 10 | 11 | local session = 0 12 | local fd 13 | local ls 14 | 15 | local c_hall = {} 16 | 17 | -- not used with msgserver0 18 | local function next_sid() 19 | return session 20 | end 21 | 22 | function c_hall.connect(login_state, index) 23 | ls = login_state 24 | local vec = string.split(ls.server, ':') 25 | local ip, port = vec[1], tonumber(vec[2]) 26 | fd = assert(socket.connect(ip, port)) 27 | 28 | local handshake = ls.username 29 | local hmac = crypt.hmac64(crypt.hashkey(handshake), ls.secret) 30 | util.send_package(fd, handshake .. ":" .. crypt.base64encode(hmac)) 31 | local resp = util.readpackage(fd) 32 | print(resp) 33 | return resp 34 | end 35 | 36 | function c_hall.get_user() 37 | util.hs_send_request(fd, util.encode("hall.GetUserInfo", {}), next_sid()) 38 | local ok, msg, sess = util.hs_recv_response(util.readpackage(fd)) 39 | assert(sess == session) 40 | msg = util.decode(msg) 41 | local submsg = protobuf.decode("hall.GetUserInfoResponse", msg.payload) 42 | print('get user response') 43 | return submsg 44 | end 45 | 46 | function c_hall.get_game_list() 47 | util.hs_send_request(fd, util.encode("hall.GetGameList", {}), next_sid()) 48 | local ok, msg, sess = util.hs_recv_response(util.readpackage(fd)) 49 | assert(sess == session) 50 | msg = util.decode(msg) 51 | local submsg = protobuf.decode("hall.GetGameListResponse", msg.payload) 52 | return submsg 53 | end 54 | 55 | function c_hall.create_room(id) 56 | util.hs_send_request(fd, util.encode("hall.CreateRoom", {id = id}), next_sid()) 57 | ok, msg, sess = util.hs_recv_response(util.readpackage(fd)) 58 | assert(sess == session) 59 | msg = util.decode(msg) 60 | submsg = protobuf.decode("hall.CreateRoomResponse", msg.payload) 61 | return submsg.rid, submsg.server 62 | end 63 | 64 | function c_hall.destroy_room(id) 65 | util.hs_send_request(fd, util.encode("hall.DestroyRoom", {id = id}), next_sid()) 66 | ok, msg, sess = util.hs_recv_response(util.readpackage(fd)) 67 | assert(sess == session) 68 | msg = util.decode(msg) 69 | submsg = protobuf.decode("hall.DestroyRoomResponse", msg.payload) 70 | return submsg.code 71 | end 72 | 73 | function c_hall.leave_room() 74 | util.hs_send_request(fd, util.encode("hall.LeaveRoom", {}), next_sid()) 75 | ok, msg, sess = util.hs_recv_response(util.readpackage(fd)) 76 | assert(sess == session) 77 | msg = util.decode(msg) 78 | submsg = protobuf.decode("hall.LeaveRoomResponse", msg.payload) 79 | return submsg.code 80 | end 81 | 82 | function c_hall.select_room(rid, sname) 83 | util.hs_send_request(fd, util.encode("hall.SelectRoom", {rid = rid, sname = sname}), next_sid()) 84 | ok, msg, sess = util.hs_recv_response(util.readpackage(fd)) 85 | assert(sess == session) 86 | msg = util.decode(msg) 87 | submsg = protobuf.decode("hall.SelectRoomResponse", msg.payload) 88 | print(string.format('select room resp: %d', submsg.code)) 89 | assert(submsg.code == 200) 90 | return submsg.server 91 | end 92 | 93 | function c_hall.reconn_room() 94 | util.hs_send_request(fd, util.encode("hall.ReconnRoom", {}), next_sid()) 95 | ok, msg, sess = util.hs_recv_response(util.readpackage(fd)) 96 | assert(sess == session) 97 | msg = util.decode(msg) 98 | submsg = protobuf.decode("hall.ReconnRoomResponse", msg.payload) 99 | print(string.format('reconn room resp: %d: %d@%s:%d', submsg.code, submsg.rid, submsg.server.ip, 100 | submsg.server.port)) 101 | assert(submsg.code == 200) 102 | return submsg.server, submsg.rid 103 | end 104 | 105 | function c_hall.shutdown() 106 | if fd ~= nil then 107 | socket.close(fd) 108 | end 109 | end 110 | 111 | return c_hall 112 | -------------------------------------------------------------------------------- /test/c_login.lua: -------------------------------------------------------------------------------- 1 | -- c_login.lua 2 | -- client login 3 | local socket = require "client.socket" 4 | local crypt = require "client.crypt" 5 | local util = require 'client_util' 6 | 7 | local ip, port 8 | local c_login = {} 9 | 10 | local function encode_token(pid, server, sdkid) 11 | local password = '' 12 | local token = crypt.base64encode(pid .. password) 13 | return string.format("%s:%s:%s:%d", server, token, sdkid, pid) 14 | end 15 | 16 | function c_login.set_addr(ip_, port_) 17 | ip = ip_ 18 | port = port_ 19 | end 20 | 21 | function c_login.register(sdkid, pid, password) 22 | local fd = assert(socket.connect(ip, port)) 23 | util.writeline(fd, 'LS register') 24 | util.writeline(fd, string.format('%d:%s:%s', sdkid, pid, password or '')) 25 | print(util.readline(fd)) 26 | socket.close(fd) 27 | end 28 | 29 | function c_login.bind(sdkid1, pid1, sdkid2, pid2) 30 | local fd = assert(socket.connect(ip, port)) 31 | util.writeline(fd, 'LS bind') 32 | util.writeline(fd, string.format('%d:%s:%d:%s', sdkid1, pid1, sdkid2, pid2)) 33 | print(util.readline(fd)) 34 | socket.close(fd) 35 | end 36 | 37 | function c_login.login(pid, server, sdkid) 38 | local fd = assert(socket.connect(ip, port)) 39 | util.writeline(fd, 'LS login') 40 | local challenge = crypt.base64decode(util.readline(fd)) 41 | local clientkey = crypt.randomkey() 42 | util.writeline(fd, crypt.base64encode(crypt.dhexchange(clientkey))) 43 | local secret = crypt.dhsecret(crypt.base64decode(util.readline(fd)), clientkey) 44 | local hmac = crypt.hmac64(challenge, secret) 45 | util.writeline(fd, crypt.base64encode(hmac)) 46 | local etoken = crypt.desencode(secret, encode_token(pid, server, sdkid)) 47 | local b = crypt.base64encode(etoken) 48 | util.writeline(fd, crypt.base64encode(etoken)) 49 | 50 | local result = util.readline(fd) 51 | local code = tonumber(string.sub(result, 1, 3)) 52 | socket.close(fd) 53 | if code ~= 200 then 54 | return code 55 | end 56 | local pack = crypt.base64decode(string.sub(result, 5)) 57 | local vec = string.split(pack, ' ') 58 | local username, server = vec[1], vec[2] 59 | return code, {username = username, server = server, hmac = hmac, secret = secret} 60 | end 61 | 62 | return c_login 63 | 64 | -------------------------------------------------------------------------------- /test/client_util.lua: -------------------------------------------------------------------------------- 1 | -- client_util.lua 2 | local socket = require "client.socket" 3 | local crypt = require "client.crypt" 4 | local protobuf = require 'protobuf' 5 | local message_map = require 'message_map' 6 | 7 | message_map.mapping('hall', 'game') 8 | 9 | local util = {} 10 | 11 | local function unpack_line(text) 12 | local from = text:find("\n", 1, true) 13 | if from then 14 | return text:sub(1, from-1), text:sub(from+1) 15 | end 16 | return nil, text 17 | end 18 | 19 | local last = "" 20 | 21 | local function unpack_f(f) 22 | local function try_recv(fd, last) 23 | local result 24 | result, last = f(last) 25 | if result then 26 | return result, last 27 | end 28 | local r = socket.recv(fd) 29 | if not r then 30 | return nil, last 31 | end 32 | if r == "" then 33 | error "Server closed" 34 | end 35 | return f(last .. r) 36 | end 37 | 38 | return function(fd) 39 | while true do 40 | local result 41 | result, last = try_recv(fd, last) 42 | if result then 43 | return result 44 | end 45 | socket.usleep(100) 46 | end 47 | end 48 | end 49 | 50 | util.readline = unpack_f(unpack_line) 51 | 52 | function util.writeline(fd, text) 53 | socket.send(fd, text .. "\n") 54 | end 55 | 56 | function util.encode(name, data) 57 | local payload = protobuf.encode(name, data) 58 | local id = message_map.get_id(name) 59 | local netmsg = { id = id, payload = payload } 60 | local pack = protobuf.encode("netmsg.NetMsg", netmsg) 61 | return pack 62 | end 63 | 64 | function util.decode(data) 65 | local netmsg = protobuf.decode("netmsg.NetMsg", data) 66 | if netmsg.code ~= 0 then 67 | error('decode msg failed:' .. netmsg.payload) 68 | end 69 | return netmsg 70 | end 71 | 72 | local function unpack_package(text) 73 | local size = #text 74 | if size < 2 then 75 | return nil, text 76 | end 77 | local s = text:byte(1) * 256 + text:byte(2) 78 | if size < s+2 then 79 | return nil, text 80 | end 81 | 82 | return text:sub(3,2+s), text:sub(3+s) 83 | end 84 | 85 | function util.send_package(fd, pack) 86 | local package = string.pack(">s2", pack) 87 | socket.send(fd, package) 88 | end 89 | 90 | util.readpackage = unpack_f(unpack_package) 91 | 92 | function util.hs_send_request(fd, v, session) 93 | local size = #v 94 | local package = string.pack(">I2", size)..v 95 | socket.send(fd, package) 96 | return v, session 97 | end 98 | 99 | function util.hs_recv_response(v) 100 | local size = #v 101 | local content, ok = string.unpack("c"..tostring(size), v) 102 | return ok ~=0 , content, 0 103 | end 104 | 105 | function util.gs_send_request(fd, v) 106 | local size = #v 107 | local package = string.pack(">I2", size)..v 108 | socket.send(fd, package) 109 | return v 110 | end 111 | 112 | function util.gs_recv_response(v) 113 | local size = #v 114 | local content, ok = string.unpack("c"..tostring(size), v) 115 | return ok ~=0 , content 116 | end 117 | 118 | function util.write_login_state(username, secret, index) 119 | local fp = io.open('logins', 'w') 120 | local s = string.format('%s:%d:%s', username, index, crypt.base64encode(secret)) 121 | fp:write(s) 122 | fp:close() 123 | end 124 | 125 | local function split(s, delim) 126 | local split = {} 127 | local pattern = "[^" .. delim .. "]+" 128 | string.gsub(s, pattern, function(v) table.insert(split, v) end) 129 | return split 130 | end 131 | 132 | function util.read_login_state() 133 | local fp = io.open('logins') 134 | if not fp then 135 | return nil 136 | end 137 | local s = fp:read('*a') 138 | local sec = split(s, ':') 139 | return {username = sec[1], index = tonumber(sec[2]), secret = crypt.base64decode(sec[3])} 140 | end 141 | 142 | return util 143 | 144 | -------------------------------------------------------------------------------- /test/i_client.lua: -------------------------------------------------------------------------------- 1 | -- i_client.lua 2 | -- interactive test client 3 | package.cpath = "skynet/luaclib/?.so;luaclib/?.so" 4 | local service_path = "./lualib/?.lua;" .. "./common/?.lua;" .. "./global/?.lua;" .. "./?.lua;" .. './test/?.lua' 5 | package.path = "skynet/lualib/?.lua;skynet/service/?.lua;" .. service_path 6 | 7 | require 'luaext' 8 | local c_login = require 'c_login' 9 | local c_hall = require 'c_hall' 10 | local c_game = require 'c_game' 11 | local socket = require "client.socket" 12 | local util = require 'client_util' 13 | 14 | local CMD = {} 15 | local login_addr = {ip = '127.0.0.1', port = 5188} 16 | local token = { 17 | server = "", 18 | pid = "123", 19 | sdkid = 1 20 | } 21 | local ls 22 | local index = 1 23 | 24 | local function help() 25 | print('> command list:') 26 | for k, v in pairs(CMD) do 27 | print(k) 28 | end 29 | end 30 | 31 | c_login.set_addr(login_addr.ip, login_addr.port) 32 | 33 | function CMD.login() 34 | code, ls = c_login.login(token.pid, token.server, token.sdkid) 35 | assert(code == 200) 36 | print('login ok, username: ', ls.username) 37 | print('get hall server:', ls.server) 38 | end 39 | 40 | function CMD.register(sdkid, pid, password) 41 | if not sdkid or not pid then 42 | print('require skdid/pid') 43 | return 44 | end 45 | c_login.register(tonumber(sdkid), pid, password) 46 | end 47 | 48 | function CMD.bind(sdkid1, pid1, sdkid2, pid2) 49 | if not sdkid1 or not pid1 or not sdkid2 or not pid2 then 50 | print('require 2 sdkid/pid') 51 | return 52 | end 53 | c_login.bind(tonumber(sdkid1), pid1, tonumber(sdkid2), pid2) 54 | end 55 | 56 | local function get_user() 57 | local user_info = c_hall.get_user() 58 | print(user_info) 59 | if user_info.room.rid > 0 then 60 | print(string.format('user has room info: %d:%s', user_info.room.rid, user_info.room.sname)) 61 | end 62 | return user_info 63 | end 64 | 65 | function CMD.get_user() 66 | get_user() 67 | end 68 | 69 | function CMD.enter_hall() 70 | local ret = c_hall.connect(ls, index) 71 | print('enter hall result: ', ret) 72 | local uinfo = get_user() 73 | if uinfo.room.rid > 0 then 74 | print('has entered room, reconn room ...') 75 | CMD.reconn_room() 76 | end 77 | end 78 | 79 | function CMD.reconn_hall() 80 | c_hall.shutdown() 81 | local state = ls 82 | index = index + 1 83 | local ret = c_hall.connect(state, index) 84 | print('reconnect hall result: ', ret) 85 | get_user() 86 | end 87 | 88 | function CMD.stay_hall() 89 | CMD.login() 90 | CMD.enter_hall() 91 | end 92 | 93 | function CMD.game_list() 94 | local list = c_hall.get_game_list() 95 | print('game list:') 96 | for _, g in ipairs(list.games) do 97 | print(string.format('game id: %d', g.gid)) 98 | for _, r in ipairs(g.rooms) do 99 | print(string.format(' > room %d on %s', r.rid, r.sname)) 100 | end 101 | end 102 | end 103 | 104 | function CMD.create_room(id) 105 | if not id then 106 | print('require game id') 107 | return 108 | end 109 | local rid, server = c_hall.create_room(id) 110 | print(string.format('created room id: %d, server: %s:%d', rid, server.ip, server.port)) 111 | local ok, msg = c_game.handshake(server.ip, server.port, ls.secret, ls.username) 112 | print(string.format('handshake to game server response: %s', msg)) 113 | c_game.enter_game(rid) 114 | c_game.send_game_msg('hello world') 115 | local msg = c_game.recv_game_msg() 116 | print('recv game message:' .. msg) 117 | end 118 | 119 | function CMD.select_room(rid, sname) 120 | local server = c_hall.select_room(rid, sname) 121 | print(string.format('select room got server: %s:%d', server.ip, server.port)) 122 | local ok, msg = c_game.handshake(server.ip, server.port, ls.secret, ls.username) 123 | print(string.format('handshake to game server response: %s', msg)) 124 | c_game.enter_game(rid) 125 | c_game.send_game_msg('hello world') 126 | local msg = c_game.recv_game_msg() 127 | print('recv game message:' .. msg) 128 | end 129 | 130 | function CMD.reconn_room() 131 | local server, rid = c_hall.reconn_room() 132 | local ok, msg = c_game.handshake(server.ip, server.port, ls.secret, ls.username) 133 | print(string.format('handshake to game server response: %s', msg)) 134 | c_game.enter_game(rid) 135 | c_game.send_game_msg('hello world') 136 | local msg = c_game.recv_game_msg() 137 | print('recv game message:' .. msg) 138 | end 139 | 140 | function CMD.leave_room() 141 | local code = c_hall.leave_room() 142 | print('leave room ret:' .. code) 143 | end 144 | 145 | function CMD.destroy_room(id) 146 | if not id then 147 | print('require room id') 148 | return 149 | end 150 | local code = c_hall.destroy_room(id) 151 | print('destroy room code: ' .. tostring(code)) 152 | end 153 | 154 | local function run_command(cmd, ...) 155 | if CMD[cmd] then 156 | CMD[cmd](...) 157 | else 158 | help() 159 | end 160 | end 161 | 162 | while true do 163 | local cmd = socket.readstdin() 164 | if cmd then 165 | if cmd == "quit" then 166 | return 167 | else 168 | local secs = string.split(cmd, ' ') 169 | run_command(secs[1], table.unpack(secs, 2)) 170 | end 171 | else 172 | socket.usleep(100) 173 | end 174 | end 175 | --------------------------------------------------------------------------------