└── pomelo_quick_x ├── README.md └── scipts ├── ChatScene.lua ├── game.lua ├── main.lua └── pomelo ├── Emitter.lua ├── Message.lua ├── Package.lua ├── Pomelo.lua ├── Protobuf.lua ├── Protocol.lua └── functions.lua /pomelo_quick_x/README.md: -------------------------------------------------------------------------------- 1 | pomelo_quick_x 2 | ============== 3 | pomelo lua(quick-cocos2d-x) client. 4 | 5 | pomelo客户端lua版。只能在quick-x环境下使用。 6 | 7 | 还不是很完善,如发现不足和可改进之处,请留言。谢谢。 8 | 9 | 暂时不支持protobuf,希望有人一起完善。 10 | 11 | 使用 12 | ============== 13 | 1.先建立自己的服务器,参考pomelo官网的chat示例。 14 | 2.ChatScene.lua,chat客户端的示例代码 -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/ChatScene.lua: -------------------------------------------------------------------------------- 1 | local M = class("MainScene", function() 2 | return display.newScene("MainScene") 3 | end) 4 | 5 | function M:ctor() 6 | self.username = "user"..math.random(1,10000) 7 | self.rid = "rid"..math.random(1,1000) 8 | echoInfo("self.username=%s",self.username) 9 | echoInfo("self.rid=%s",self.rid) 10 | 11 | self:initView() 12 | self:initNet() 13 | end 14 | 15 | function M:initView() 16 | local loginLabel = ui.newTTFLabelMenuItem({ 17 | text = "login", 18 | size = 32, 19 | x = display.cx, 20 | y = display.top - 128, 21 | listener = handler(self, self.onLoginClick), 22 | }) 23 | 24 | local sendLabel = ui.newTTFLabelMenuItem({ 25 | text = "send message", 26 | size = 32, 27 | x = display.cx, 28 | y = display.top - 160, 29 | listener = handler(self, self.onSendMsgClick), 30 | }) 31 | 32 | self:addChild(ui.newMenu({loginLabel, sendLabel})) 33 | end 34 | 35 | function M:initNet() 36 | game.pomelo:on("onChat",handler(self,self.onChat)) 37 | game.pomelo:on("onAdd",handler(self,self.onAdd)) 38 | game.pomelo:on("onLeave",handler(self,self.onLeave)) 39 | end 40 | 41 | function M:onChat(data) 42 | echoInfo("onChat") 43 | echoInfo("%s receive message from:%s,content:%s",data.from,data.target,data.msg) 44 | end 45 | 46 | function M:onAdd(data) 47 | echoInfo("onAdd") 48 | echoInfo("user-%s login!",json.encode(data.user)) 49 | end 50 | 51 | function M:onLeave(data) 52 | echoInfo("onLeave") 53 | echoInfo("user-%s leave!",json.encode(data.user)) 54 | end 55 | 56 | function M:onLoginClick() 57 | self:queryEntry(function(host,port) 58 | game.pomelo:init({host=host,port=port}, 59 | function() 60 | local route = "connector.entryHandler.enter" 61 | game.pomelo:request(route,{username=self.username,rid=self.rid}, 62 | function(data) 63 | if data.error then 64 | echoInfo("login fail! error=%s",data.error) 65 | else 66 | echoInfo("login success!") 67 | echoInfo("ddfd data=%s",json.encode(data)) 68 | end 69 | end 70 | ) 71 | end) 72 | end) 73 | end 74 | 75 | function M:onSendMsgClick() 76 | local route = 'chat.chatHandler.send' 77 | game.pomelo:request(route,{rid=self.rid,content="hello!",from=self.username,target="*"}, 78 | function(data) 79 | end 80 | ) 81 | end 82 | 83 | -- query connector 84 | function M:queryEntry(cb) 85 | game.pomelo:init({host="127.0.0.1",port="3014"},--自己架设服务器,参考pomelo官方文档的chatofpomelo示例 86 | function() 87 | local route = 'gate.gateHandler.queryEntry' 88 | game.pomelo:request(route,{uid=self.username}, 89 | function(data) 90 | game.pomelo:disconnect() 91 | echoInfo("123 data=%s",json.encode(data)) 92 | if data.error then 93 | return 94 | end 95 | cb(data.host,data.port) 96 | end) 97 | end 98 | ) 99 | end 100 | 101 | function M:onEnter() 102 | 103 | end 104 | 105 | return M -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/game.lua: -------------------------------------------------------------------------------- 1 | require("framework.init") 2 | 3 | -- define global module 4 | game = {} 5 | game.pomelo = require("pomelo.Pomelo").new() 6 | 7 | function game.startup() 8 | game.enterMainScene() 9 | end 10 | 11 | function game.exit() 12 | CCDirector:sharedDirector():endToLua() 13 | end 14 | 15 | function game.enterMainScene() 16 | display.replaceScene(require("ChatScene").new()) 17 | end 18 | 19 | -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/main.lua: -------------------------------------------------------------------------------- 1 | 2 | -- for CCLuaEngine 3 | function __G__TRACKBACK__(errorMessage) 4 | CCLuaLog("----------------------------------------") 5 | CCLuaLog("LUA ERROR: "..tostring(errorMessage).."\n") 6 | CCLuaLog(debug.traceback("", 2)) 7 | CCLuaLog("----------------------------------------") 8 | end 9 | 10 | --CCLuaLoadChunksFromZip("res/framework_precompiled.zip") 11 | 12 | xpcall(function() 13 | require("game") 14 | game.startup() 15 | end, __G__TRACKBACK__) 16 | -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/pomelo/Emitter.lua: -------------------------------------------------------------------------------- 1 | local Emitter = class("Emitter") 2 | 3 | function Emitter:ctor() 4 | self._callbacks = {} 5 | end 6 | 7 | function Emitter:on(event,fn) 8 | if not self._callbacks[event] then 9 | self._callbacks[event] = {} 10 | end 11 | table.insert(self._callbacks[event],fn) 12 | end 13 | 14 | function Emitter:once(event,fn,args) 15 | function on() 16 | self:off(event,on) 17 | fn(args) 18 | end 19 | fn._off = on 20 | self:on(event,on) 21 | return self 22 | end 23 | 24 | function Emitter:removeAllListener() 25 | self._callbacks = {} 26 | return self 27 | end 28 | 29 | function Emitter:off(event,fn) 30 | return self:removeListener(event,fn) 31 | end 32 | 33 | function Emitter:removeListener(event,fn) 34 | local callbacks = self._callbacks[event] 35 | if not callbacks then 36 | return self 37 | end 38 | 39 | if not fn then 40 | self._callbacks[event] = nil 41 | return self 42 | end 43 | 44 | local i = table.indexOf(callbacks,fn._off or fn) 45 | if i then 46 | table.remove(callbacks,i) 47 | end 48 | 49 | return self 50 | end 51 | 52 | function Emitter:emit(event,args) 53 | -- echoInfo("Emitter:emit event=%s",event) 54 | local callbacks = self._callbacks[event] 55 | 56 | if callbacks then 57 | for i=1,#callbacks do 58 | callbacks[i](args) 59 | end 60 | end 61 | 62 | return self 63 | end 64 | 65 | function Emitter:listeners(event) 66 | return self._callbacks[event] or {} 67 | end 68 | 69 | function Emitter:hasListeners(event) 70 | return #self.listeners(event)>0 71 | end 72 | 73 | return Emitter 74 | 75 | -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/pomelo/Message.lua: -------------------------------------------------------------------------------- 1 | require("bit") 2 | 3 | local func = require("pomelo.functions") 4 | local Protocol = require("pomelo.Protocol") 5 | 6 | local Message = class("Message") 7 | 8 | local MSG_FLAG_BYTES = 1 9 | local MSG_ROUTE_CODE_BYTES = 2 10 | local MSG_ID_MAX_BYTES = 5 11 | local MSG_ROUTE_LEN_BYTES = 1 12 | 13 | local MSG_ROUTE_CODE_MAX = 0xffff 14 | local MSG_COMPRESS_ROUTE_MASK = 0x1 15 | local MSG_TYPE_MASK = 0x7 16 | 17 | Message.TYPE_REQUEST = 0 18 | Message.TYPE_NOTIFY = 1 19 | Message.TYPE_RESPONSE = 2 20 | Message.TYPE_PUSH = 3 21 | 22 | Message.encode = function(id,_type,compressRoute,route,msg) 23 | local idBytes = 0 24 | if Message._msgHasId(_type) then 25 | idBytes = Message._caculateMsgIdBytes(id) 26 | end 27 | local msgLen = MSG_FLAG_BYTES + idBytes 28 | 29 | if Message._msgHasRoute(_type) then 30 | if compressRoute~=0 then 31 | if type(route) ~= 'number' then 32 | echoError('error flag for number route!') 33 | return 34 | end 35 | msgLen = msgLen + MSG_ROUTE_CODE_BYTES 36 | else 37 | msgLen = msgLen + MSG_ROUTE_LEN_BYTES 38 | if route then 39 | route = Protocol.strencode(route) 40 | if #route > 255 then 41 | echoError('route maxlength is overflow') 42 | return 43 | end 44 | msgLen = msgLen + #route 45 | end 46 | end 47 | end 48 | 49 | if msg then 50 | msgLen = msgLen + #msg 51 | end 52 | 53 | local buffer = {} 54 | local offset = 1 55 | 56 | -- add flag 57 | offset = Message._encodeMsgFlag(_type,compressRoute,buffer,offset) 58 | 59 | -- add message id 60 | if Message._msgHasId(_type) then 61 | offset = Message._encodeMsgId(id,idBytes,buffer,offset) 62 | end 63 | 64 | -- add route 65 | if Message._msgHasRoute(_type) then 66 | offset = Message._encodeMsgRoute(compressRoute,route,buffer,offset) 67 | end 68 | 69 | -- add body 70 | if msg then 71 | offset = Message._encodeMsgBody(msg,buffer,offset) 72 | end 73 | 74 | return buffer 75 | end 76 | 77 | Message.decode = function(bytes) 78 | -- echoInfo("Message.decode") 79 | local offset = 1 80 | local id = 0 81 | local route = nil 82 | 83 | -- parse flag 84 | local flag = bytes[offset] 85 | offset = offset + 1 86 | local compressRoute = bit.band(flag,MSG_COMPRESS_ROUTE_MASK) 87 | local _type = bit.band(bit.rshift(flag,1),MSG_TYPE_MASK) 88 | 89 | -- parse id 90 | if Message._msgHasId(_type) then 91 | local byte = bytes[offset] 92 | offset = offset + 1 93 | id = bit.band(byte,0x7f) 94 | while bit.band(byte,0x80)~=0 do 95 | id = bit.lshift(id,7) 96 | byte = bytes[offset] 97 | offset = offset + 1 98 | id = bit.bor(id,bit.band(byte,0x7f)) 99 | end 100 | end 101 | 102 | -- parse route 103 | if Message._msgHasRoute(_type) then 104 | if compressRoute~=0 then 105 | local a = bit.lshift((bytes[offset]),8) 106 | offset = offset + 1 107 | route = bit.bor(a,bytes[offset]) 108 | offset = offset + 1 109 | else 110 | local routeLen = bytes[offset] 111 | offset = offset + 1 112 | if routeLen then 113 | route = {} 114 | func.copyArray(route,1,bytes,offset,routeLen) 115 | route = Protocol.strdecode(route) 116 | else 117 | route = '' 118 | end 119 | offset = offset + routeLen 120 | end 121 | end 122 | 123 | -- parse body 124 | local bodyLen = #bytes - offset 125 | local body = {} 126 | 127 | func.copyArray(body,1,bytes,offset,bodyLen+1) 128 | -- echoInfo("Protocol.strdecode(body)=%s",Protocol.strdecode(body)) 129 | 130 | return { 131 | id=id, 132 | type=_type, 133 | compressRoute=compressRoute, 134 | route=route, 135 | body=body 136 | } 137 | 138 | end 139 | 140 | Message._msgHasId = function(_type) 141 | return _type == Message.TYPE_REQUEST or 142 | _type == Message.TYPE_RESPONSE 143 | end 144 | 145 | Message._msgHasRoute = function(_type) 146 | return _type == Message.TYPE_REQUEST or 147 | _type == Message.TYPE_NOTIFY or 148 | _type == Message.TYPE_PUSH 149 | end 150 | 151 | Message._caculateMsgIdBytes = function(id) 152 | local len = 0 153 | repeat 154 | len = len + 1 155 | id = bit.rshift(id,7) 156 | until id <= 0 157 | return len 158 | end 159 | 160 | Message._encodeMsgFlag = function(_type,compressRoute,buffer,offset) 161 | if _type ~= Message.TYPE_REQUEST and _type ~= Message.TYPE_NOTIFY and _type ~= Message.TYPE_RESPONSE and _type ~= Message.TYPE_PUSH then 162 | echoError('unkonw message _type: %s',_type) 163 | end 164 | 165 | local a = 0 166 | if compressRoute~=0 then 167 | a = 1 168 | end 169 | buffer[offset] = bit.bor(bit.lshift(_type,1),a) 170 | 171 | return offset + MSG_FLAG_BYTES 172 | end 173 | 174 | Message._encodeMsgId = function(id,idBytes,buffer,offset) 175 | local index = offset + idBytes - 1 176 | buffer[index] = bit.band(id,0x7f) 177 | index = index - 1 178 | while index >= offset do 179 | id = bit.rshift(id,7) 180 | buffer[index] = bit.bor(bit.band(id,0x7f),0x80) 181 | index = index - 1 182 | end 183 | return offset + idBytes 184 | end 185 | 186 | Message._encodeMsgRoute = function(compressRoute,route,buffer,offset) 187 | if compressRoute~=0 then 188 | if route > MSG_ROUTE_CODE_MAX then 189 | echoError('route number is overflow') 190 | return 191 | end 192 | buffer[offset] = bit.band(bit.rshift(route,8),0xff) 193 | offset = offset + 1 194 | buffer[offset] = bit.band(route,0xff) 195 | offset = offset + 1 196 | else 197 | if route then 198 | buffer[offset] = bit.band(#route,0xff) 199 | offset = offset + 1 200 | func.copyArray(buffer,offset,route,1,#route) 201 | offset = offset + #route 202 | else 203 | buffer[offset] = 0 204 | offset = offset + 1 205 | end 206 | end 207 | return offset 208 | end 209 | 210 | Message._encodeMsgBody = function(msg,buffer,offset) 211 | func.copyArray(buffer,offset,msg,1,#msg) 212 | return offset + #msg 213 | end 214 | 215 | return Message -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/pomelo/Package.lua: -------------------------------------------------------------------------------- 1 | local func = require("pomelo.functions") 2 | 3 | local Package = {} 4 | 5 | local PKG_HEAD_BYTES = 4 6 | 7 | Package.TYPE_HANDSHAKE = 1 8 | Package.TYPE_HANDSHAKE_ACK = 2 9 | Package.TYPE_HEARTBEAT = 3 10 | Package.TYPE_DATA = 4 11 | Package.TYPE_KICK = 5 12 | 13 | Package.encode = function(_type,body) 14 | local length = 0 15 | if body then 16 | length = #body 17 | end 18 | local buffer = {} 19 | local index = 1 20 | buffer[index] = bit.band(_type,0xff) 21 | index = index + 1 22 | buffer[index] = bit.band(bit.rshift(length,16),0xff) 23 | index = index + 1 24 | buffer[index] = bit.band(bit.rshift(length,8),0xff) 25 | index = index + 1 26 | buffer[index] = bit.band(length,0xff) 27 | index = index + 1 28 | if body then 29 | func.copyArray(buffer,index,body,1,length) 30 | end 31 | return buffer 32 | end 33 | 34 | Package.decode = function(bytes) 35 | -- echoInfo("Package.decode") 36 | local _type = bytes[1] 37 | local index = 2 38 | local a = bit.lshift(bytes[index],16) 39 | index = index + 1 40 | local b = bit.lshift(bytes[index],8) 41 | index = index + 1 42 | local c = bit.bor(a,b,bytes[index]) 43 | index = index + 1 44 | local length = bit.arshift(c,0) 45 | local body = {} 46 | func.copyArray(body,1,bytes,PKG_HEAD_BYTES+1,length) 47 | return {type=_type,body=body} 48 | end 49 | 50 | return Package -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/pomelo/Pomelo.lua: -------------------------------------------------------------------------------- 1 | --local Protobuf = require("Protobuf") -- 暂时还不支持protobuf,如有可能,请加上并告知。谢谢。 2 | local scheduler = require("framework.scheduler") 3 | 4 | local Protocol = require("pomelo.Protocol") 5 | local Package = require("pomelo.Package") 6 | local Message = require("pomelo.Message") 7 | local Emitter = require("pomelo.Emitter") 8 | 9 | local RES_OK = 200 10 | local RES_FAIL = 500 11 | local RES_OLD_CLIENT = 501 12 | 13 | local LUA_WS_CLIENT_TYPE = 'lua-websocket' 14 | local LUA_WS_CLIENT_VERSION = '0.0.1' 15 | 16 | -- 继承Emitter 17 | local Pomelo = class("Pomelo",function() 18 | return Emitter.new() 19 | end) 20 | 21 | function Pomelo:ctor() 22 | self.socket = nil 23 | self.reqId = 1 24 | --Map from request id to route 25 | self.routeMap = {} 26 | 27 | self.heartbeatInterval = 0 28 | self.heartbeatTimeout = 0 29 | self.nextHeartbeatTimeout = 0 30 | self.gapThreshold = 1 -- heartbeat gap threashold 31 | self.heartbeatId = nil 32 | self.heartbeatTimeoutId = nil 33 | 34 | self.handshakeBuffer = { 35 | sys = { 36 | type = LUA_WS_CLIENT_TYPE, 37 | version = LUA_WS_CLIENT_VERSION 38 | }, 39 | user= {} 40 | } 41 | 42 | self.handlers = {} 43 | self.handlers[Package.TYPE_HANDSHAKE] = handler(self,self._handshake) 44 | self.handlers[Package.TYPE_HEARTBEAT] = handler(self,self.heartbeat) 45 | self.handlers[Package.TYPE_DATA] = handler(self,self._onData) 46 | self.handlers[Package.TYPE_KICK] = handler(self,self._onKick) 47 | 48 | end 49 | 50 | function Pomelo:init(params,cb) 51 | self.initCallback = cb 52 | 53 | local host = params.host 54 | local port = params.port 55 | 56 | local url = 'ws://' .. host 57 | if port then 58 | url = url .. ':' .. port 59 | end 60 | 61 | self.handshakeBuffer.user = params.user 62 | self.handshakeCallback = params.handshakeCallback 63 | 64 | self:_initWebSocket(url,cb) 65 | end 66 | 67 | function Pomelo:request(route,msg,cb) 68 | if not route then 69 | return 70 | end 71 | 72 | if not self:_isReady() then 73 | printError("Pomelo:request() - socket not ready") 74 | return 75 | end 76 | 77 | self.reqId = self.reqId + 1 78 | self:_sendMessage(self.reqId,route,msg) 79 | 80 | self._callbacks[self.reqId] = cb 81 | self.routeMap[self.reqId] = route 82 | end 83 | 84 | function Pomelo:notify(route,msg) 85 | if not self:_isReady() then 86 | printError("WebSockets:_send() - socket not ready") 87 | return 88 | end 89 | 90 | local msg = msg or {} 91 | self:_sendMessage(0,route,msg) 92 | end 93 | 94 | function Pomelo:disconnect() 95 | -- printf("Pomelo:disconnect()") 96 | if self.socket and self.socket:getReadyState() == kStateOpen then 97 | self.socket:close() 98 | self.socket = nil 99 | end 100 | 101 | if self.heartbeatId then 102 | self:_clearTimeout(self.heartbeatId) 103 | self.heartbeatId = nil 104 | end 105 | 106 | if self.heartbeatTimeoutId then 107 | self:_clearTimeout(self.heartbeatTimeoutId) 108 | self.heartbeatTimeoutId = nil 109 | end 110 | 111 | self:removeAllListener() 112 | end 113 | 114 | function Pomelo:_initWebSocket(url,cb) 115 | local onopen = function(event) 116 | local obj = Package.encode(Package.TYPE_HANDSHAKE,Protocol.strencode(json.encode(self.handshakeBuffer))) 117 | self:_send(obj) 118 | end 119 | 120 | local onmessage = function(message) 121 | self:_processPackage(Package.decode(message),cb) 122 | -- new package arrived,update the heartbeat timeout 123 | if self.heartbeatTimeout~=0 then 124 | self.nextHeartbeatTimeout = os.time() + self.heartbeatTimeout 125 | end 126 | end 127 | 128 | local onerror = function(event) 129 | self:emit('io-error',event) 130 | end 131 | 132 | local onclose = function(event) 133 | self:emit('close',event) 134 | end 135 | 136 | self.socket = cc.WebSocket:create(url) 137 | 138 | self.socket:registerScriptHandler(onopen,cc.WEBSOCKET_OPEN) 139 | self.socket:registerScriptHandler(onmessage,cc.WEBSOCKET_MESSAGE) 140 | self.socket:registerScriptHandler(onclose,cc.WEBSOCKET_CLOSE) 141 | self.socket:registerScriptHandler(onerror,cc.WEBSOCKET_ERROR) 142 | 143 | end 144 | 145 | function Pomelo:_processPackage(msg) 146 | self.handlers[msg.type](msg.body) 147 | end 148 | 149 | function Pomelo:_processMessage(msg) 150 | if msg.id==0 then 151 | -- server push message 152 | self:emit(msg.route,msg.body) 153 | end 154 | 155 | --if have a id then find the callback function with the request 156 | local cb = self._callbacks[msg.id] 157 | self._callbacks[msg.id] = nil 158 | if type(cb) ~= 'function' then 159 | return 160 | end 161 | 162 | cb(msg.body) 163 | end 164 | 165 | function Pomelo:_processMessageBatch(msgs) 166 | for i=1,#msgs do 167 | self:_processMessage(pomelo,msgs[i]) 168 | end 169 | end 170 | 171 | function Pomelo:_isReady() 172 | return self.socket and self.socket:getReadyState() == cc.WEBSOCKET_STATE_OPEN 173 | end 174 | 175 | function Pomelo:_sendMessage(reqId,route,msg) 176 | -- printf("Pomelo:_sendMessage") 177 | local _type = Message.TYPE_REQUEST 178 | if reqId == 0 then 179 | _type = Message.TYPE_NOTIFY 180 | end 181 | 182 | --compress message by Protobuf 183 | -- TODO 暂时不支持 Protobuf 184 | local protos = {} 185 | if self.data.protos then 186 | protos = self.data.protos.client 187 | end 188 | 189 | if protos[route] then 190 | msg = Protobuf.encode(route,msg) 191 | else 192 | msg = Protocol.strencode(json.encode(msg)) 193 | end 194 | 195 | local compressRoute = 0 196 | if self.dict and self.dict[route] then 197 | route = self.dict[route] 198 | compressRoute = 1 199 | end 200 | 201 | msg = Message.encode(reqId,_type,compressRoute,route,msg) 202 | 203 | local packet = Package.encode(Package.TYPE_DATA,msg) 204 | 205 | self:_send(packet) 206 | end 207 | 208 | function Pomelo:_send(packet) 209 | if self:_isReady() then 210 | self.socket:sendBinary(packet,table.nums(packet)) 211 | end 212 | end 213 | 214 | function Pomelo:heartbeat(data) 215 | -- printf("Pomelo:heartbeat(data)") 216 | 217 | if self.heartbeatInterval==0 then 218 | -- no heartbeat 219 | return 220 | end 221 | 222 | if self.heartbeatId~=nil then 223 | -- already in a heartbeat interval 224 | return 225 | end 226 | 227 | if self.heartbeatTimeoutId~=nil then 228 | self:_clearTimeout(self.heartbeatTimeoutId) 229 | self.heartbeatTimeoutId = nil 230 | end 231 | 232 | local obj = Package.encode(Package.TYPE_HEARTBEAT) 233 | self.heartbeatId = self:_setTimeout( 234 | function() 235 | self:_send(obj) 236 | 237 | -- self.nextHeartbeatTimeout = os.time() + self.heartbeatTimeout 238 | -- self.heartbeatTimeoutId = self:_setTimeout(handler(self,self.heartbeatTimeoutCb),self.heartbeatTimeout) 239 | 240 | self:_clearTimeout(self.heartbeatId) 241 | self.heartbeatId = nil 242 | end, 243 | self.heartbeatInterval) 244 | end 245 | 246 | function Pomelo:heartbeatTimeoutCb() 247 | local gap = self.nextHeartbeatTimeout - os.time() 248 | if gap > self.gapThreshold then 249 | self.heartbeatTimeoutId = self:_setTimeout(handler(self,self.heartbeatTimeoutCb),gap) 250 | else 251 | self:emit('heartbeat timeout') 252 | -- printf('heartbeat timeout') 253 | self:disconnect() 254 | end 255 | end 256 | 257 | function Pomelo:_handshake(data) 258 | data = json.decode(Protocol.strdecode(data)) 259 | 260 | if data.code == RES_OLD_CLIENT then 261 | self:emit('error','client version not fullfill') 262 | return 263 | end 264 | 265 | if data.code ~= RES_OK then 266 | self:emit('error','_handshake fail') 267 | return 268 | end 269 | 270 | self:_handshakeInit(data) 271 | 272 | local obj = Package.encode(Package.TYPE_HANDSHAKE_ACK) 273 | self:_send(obj) 274 | 275 | if self.initCallback then 276 | self:initCallback(self.socket) 277 | initCallback = nil 278 | end 279 | 280 | end 281 | 282 | function Pomelo:_onData(data) 283 | local msg = Message.decode(data) 284 | if msg.id > 0 then 285 | msg.route = self.routeMap[msg.id] 286 | self.routeMap[msg.id] = nil 287 | if not msg.route then 288 | return 289 | end 290 | end 291 | msg.body = self:_deCompose(msg) 292 | self:_processMessage(msg) 293 | end 294 | 295 | function Pomelo:_onKick(data) 296 | self:emit('onKick') 297 | end 298 | 299 | function Pomelo:_deCompose(msg) 300 | local protos = {} 301 | if self.data.protos then 302 | protos = self.data.protos.server 303 | end 304 | 305 | local abbrs = self.data.abbrs 306 | local route = msg.route 307 | 308 | --Decompose route from dict 309 | if msg.compressRoute~=0 then 310 | if not abbrs[route] then 311 | return {} 312 | end 313 | msg.route = abbrs[route] 314 | route = msg.route 315 | end 316 | 317 | if protos[route] then 318 | return Protobuf.decode(route,msg.body) 319 | else 320 | return json.decode(Protocol.strdecode(msg.body)) 321 | end 322 | 323 | return msg 324 | end 325 | 326 | function Pomelo:_handshakeInit(data) 327 | -- printf("Pomelo:_handshakeInit(data=)",json.encode(data)) 328 | if data.sys and data.sys.heartbeat then 329 | self.heartbeatInterval = data.sys.heartbeat -- heartbeat interval 330 | self.heartbeatTimeout = self.heartbeatInterval * 2 -- max heartbeat timeout 331 | else 332 | self.heartbeatInterval = 0 333 | self.heartbeatTimeout = 0 334 | end 335 | 336 | self:_initData(data) 337 | 338 | if type(self.handshakeCallback) == 'function' then 339 | self:handshakeCallback(data.user) 340 | end 341 | 342 | end 343 | 344 | --Initilize data used in pomelo client 345 | function Pomelo:_initData(data) 346 | if not data or not data.sys then 347 | return 348 | end 349 | 350 | self.data = self.data or {} 351 | local dict = data.sys.dict 352 | local protos = data.sys.protos 353 | 354 | --Init compress dict 355 | if dict then 356 | self.data.dict = dict 357 | self.data.abbrs = {} 358 | for k,v in ipairs(dict) do 359 | self.data.abbrs[dict[k]] = k 360 | end 361 | end 362 | 363 | -- --Init Protobuf protos 364 | -- if protos then 365 | -- self.data.protos = { 366 | -- server = protos.server or {}, 367 | -- client = protos.client or {} 368 | -- } 369 | -- if Protobuf then 370 | -- Protobuf.init({ 371 | -- encoderProtosprotos=client, 372 | -- decoderProtos=protos.server 373 | -- }) 374 | -- end 375 | -- end 376 | 377 | end 378 | 379 | function Pomelo:_setTimeout(fn,delay) 380 | scheduler.performWithDelayGlobal(fn,delay) 381 | end 382 | 383 | function Pomelo:_clearTimeout(fn) 384 | if fn and fn ~= 0 then 385 | scheduler.unscheduleGlobal(fn) 386 | end 387 | end 388 | 389 | return Pomelo 390 | -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/pomelo/Protobuf.lua: -------------------------------------------------------------------------------- 1 | -- 2 | --local Protobuf = {} 3 | -- 4 | --Protobuf.init = function(protos) 5 | -- Protobuf.encoder.init(protos.encoderProtos) 6 | -- Protobuf.decoder.init(protos.decoderProtos) 7 | --end 8 | -- 9 | --Protobuf.encode = function(key, msg) 10 | -- return Protobuf.encoder.encode(key, msg) 11 | --end 12 | -- 13 | --Protobuf.decode = function(key, msg) 14 | -- return Protobuf.decoder.decode(key, msg) 15 | --end 16 | -- 17 | -- 18 | -- 19 | -- 20 | --local constants = {} 21 | --constants.TYPES = { 22 | -- uInt32 = 0, 23 | -- sInt32 = 0, 24 | -- int32 = 0, 25 | -- double = 1, 26 | -- string = 2, 27 | -- message = 2, 28 | -- float = 5 29 | --} 30 | -- 31 | -- 32 | -- 33 | -- 34 | -- 35 | --local Util = {} 36 | -- 37 | --Util.isSimpleType = function(type) 38 | -- return ( type == 'uInt32' or 39 | -- type == 'sInt32' or 40 | -- type == 'int32' or 41 | -- type == 'uInt64' or 42 | -- type == 'sInt64' or 43 | -- type == 'float' or 44 | -- type == 'double' ) 45 | --end 46 | -- 47 | -- 48 | -- 49 | -- 50 | -- 51 | ----local Codec = exports.Protobuf.codec {} 52 | -- 53 | --local buffer = new ArrayBuffer(8) 54 | ----local float32Array = new Float32Array(buffer) 55 | ----local float64Array = new Float64Array(buffer) 56 | ----local uInt8Array = new Uint8Array(buffer) 57 | -- 58 | --Codec.encodeUInt32 = function(n) 59 | -- local n = parseInt(n) 60 | -- if isNaN(n) or n < 0) 61 | -- return nil 62 | -- end 63 | -- 64 | -- local result = {} 65 | -- do 66 | -- local tmp = n % 128 67 | -- local next = Math.floor(n/128) 68 | -- 69 | -- if next not == 0) 70 | -- tmp = tmp + 128 71 | -- end 72 | -- result.push(tmp) 73 | -- n = next 74 | -- while n not == 0) 75 | -- 76 | -- return result 77 | --end 78 | -- 79 | --Codec.encodeSInt32 = function(n) 80 | -- local n = parseInt(n) 81 | -- if isNaN(n)) 82 | -- return nil 83 | -- end 84 | -- n = n<0?(Math.abs(n)*2-1)=n*2 85 | -- 86 | -- return Codec.encodeUInt32(n) 87 | --end 88 | -- 89 | --Codec.decodeUInt32 = function(bytes) 90 | -- local n = 0 91 | -- 92 | -- for i = 0 i < bytes.length 93 | -- local m = parseInt(bytes{i}) 94 | -- n = n + ((m & 0x7f) * Math.pow(2,(7*i))) 95 | -- if m < 128) 96 | -- return n 97 | -- end 98 | -- end 99 | -- 100 | -- return n 101 | --end 102 | -- 103 | -- 104 | --Codec.decodeSInt32 = function(bytes) 105 | -- local n = this.decodeUInt32(bytes) 106 | -- local flag = ((n%2) == 1)?-1=1 107 | -- 108 | -- n = ((n%2 + n)/2)*flag 109 | -- 110 | -- return n 111 | --end 112 | -- 113 | ----Codec.encodeFloat = function(float) 114 | ---- float32Array{0} = float 115 | ---- return uInt8Array 116 | ----end 117 | ---- 118 | ----Codec.decodeFloat = function(bytes, offset) 119 | ---- if not bytes or bytes.length < (offset +4)) 120 | ---- return nil 121 | ---- end 122 | ---- 123 | ---- for i = 0 i < 4 124 | ---- uInt8Array{i} = bytes{offset + i} 125 | ---- end 126 | ---- 127 | ---- return float32Array{0} 128 | ----end 129 | ---- 130 | ----Codec.encodeDouble = function(double) 131 | ---- float64Array{0} = double 132 | ---- return uInt8Array.subarray(0, 8) 133 | ----end 134 | ---- 135 | ----Codec.decodeDouble = function(bytes, offset) 136 | ---- if not bytes or bytes.length < (8 + offset)) 137 | ---- return nil 138 | ---- end 139 | ---- 140 | ---- for i = 0 i < 8 141 | ---- uInt8Array{i} = bytes{offset + i} 142 | ---- end 143 | ---- 144 | ---- return float64Array{0} 145 | ----end 146 | -- 147 | --Codec.encodeStr = function(bytes, offset, str) 148 | -- for i = 0 i < str.length 149 | -- local code = str.charCodeAt(i) 150 | -- local codes = encode2UTF8(code) 151 | -- 152 | -- for j = 0 j < codes.length j++) 153 | -- bytes{offset} = codes{j} 154 | -- offset++ 155 | -- end 156 | -- end 157 | -- 158 | -- return offset 159 | --end 160 | -- 161 | --Codec.decodeStr = function(bytes, offset, length) 162 | -- local array = {} 163 | -- local length = offset + length 164 | -- 165 | -- while offset < end 166 | -- local code = 0 167 | -- 168 | -- if bytes{offset} < 128) 169 | -- code = bytes{offset} 170 | -- 171 | -- offset += 1 172 | -- else if bytes{offset} < 224) 173 | -- code = ((bytes{offset} & 0x3f)<<6) + (bytes{offset+1} & 0x3f) 174 | -- offset += 2 175 | -- else 176 | -- code = ((bytes{offset} & 0x0f)<<12) + ((bytes{offset+1} & 0x3f)<<6) + (bytes{offset+2} & 0x3f) 177 | -- offset += 3 178 | -- end 179 | -- 180 | -- array.push(code) 181 | -- 182 | -- end 183 | -- 184 | -- local str = '' 185 | -- for i = 0 i < array.length) 186 | -- str += String.fromCharCode.apply(nil, array.slice(i, i + 10000)) 187 | -- i += 10000 188 | -- end 189 | -- 190 | -- return str 191 | --end 192 | -- 193 | --Codec.byteLength = function(str) 194 | -- if typeof(str) not == 'string') 195 | -- return -1 196 | -- end 197 | -- 198 | -- local length = 0 199 | -- 200 | -- for i = 0 i < str.length 201 | -- local code = str.charCodeAt(i) 202 | -- length += codeLength(code) 203 | -- end 204 | -- 205 | -- return length 206 | --end 207 | -- 208 | --function encode2UTF8(charCode) 209 | -- if charCode <= 0x7f) 210 | -- return {charCode} 211 | -- else if charCode <= 0x7ff) 212 | -- return {0xc0|(charCode>>6), 0x80|(charCode & 0x3f)} 213 | -- else 214 | -- return {0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)} 215 | -- end 216 | --end 217 | -- 218 | --function codeLength(code) 219 | -- if code <= 0x7f) 220 | -- return 1 221 | -- else if code <= 0x7ff) 222 | -- return 2 223 | -- else 224 | -- return 3 225 | -- end 226 | --end 227 | -- 228 | -- 229 | -- 230 | -- 231 | --local protobuf = exports.Protobuf 232 | --local MsgEncoder = exports.Protobuf.encoder {} 233 | -- 234 | --local codec = protobuf.codec 235 | --local constant = protobuf.constants 236 | --local util = protobuf.util 237 | -- 238 | --MsgEncoder.init = function(protos) 239 | -- this.protos = protos 240 | --end 241 | -- 242 | --MsgEncoder.encode = function(route, msg) 243 | -- --Get protos from protos map use the route as key 244 | -- local protos = this.protos{route} 245 | -- 246 | -- --Check msg 247 | -- if not checkMsg(msg, protos)) 248 | -- return nil 249 | -- end 250 | -- 251 | -- --Set the length of the buffer 2 times bigger to prevent overflow 252 | -- local length = codec.byteLength(JSON.stringify(msg)) 253 | -- 254 | -- --Init buffer and offset 255 | -- local buffer = new ArrayBuffer(length) 256 | -- local uInt8Array = new Uint8Array(buffer) 257 | -- local offset = 0 258 | -- 259 | -- if protos) 260 | -- offset = encodeMsg(uInt8Array, offset, protos, msg) 261 | -- if offset > 0) 262 | -- return uInt8Array.subarray(0, offset) 263 | -- end 264 | -- end 265 | -- 266 | -- return nil 267 | --end 268 | -- 269 | --function checkMsg(msg, protos) 270 | -- if not protos) 271 | -- return false 272 | -- end 273 | -- 274 | -- for name in protos) 275 | -- local proto = protos{name} 276 | -- 277 | -- --All required element must exist 278 | -- switch(proto.option) 279 | -- case 'required' = 280 | -- if typeof(msg{name}) == 'undefined') 281 | -- return false 282 | -- end 283 | -- case 'optional' = 284 | -- if typeof(msg{name}) not == 'undefined') 285 | -- if protos.__messages{proto.type}) 286 | -- checkMsg(msg{name}, protos.__messages{proto.type}) 287 | -- end 288 | -- end 289 | -- break 290 | -- case 'repeated' = 291 | -- --Check nest message in repeated elements 292 | -- if msg{name} and protos.__messages{proto.type}) 293 | -- for i = 0 i < msg{name}.length 294 | -- if not checkMsg(msg{name}{i}, protos.__messages{proto.type})) 295 | -- return false 296 | -- end 297 | -- end 298 | -- end 299 | -- break 300 | -- end 301 | -- end 302 | -- 303 | -- return true 304 | --end 305 | -- 306 | --function encodeMsg(buffer, offset, protos, msg) 307 | -- for name in msg) 308 | -- if protos{name}) 309 | -- local proto = protos{name} 310 | -- switch(proto.option) 311 | -- case 'required' = 312 | -- case 'optional' = 313 | -- offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag)) 314 | -- offset = encodeProp(msg{name}, proto.type, offset, buffer, protos) 315 | -- break 316 | -- case 'repeated' = 317 | -- if msg{name}.length > 0) 318 | -- offset = encodeArray(msg{name}, proto, offset, buffer, protos) 319 | -- end 320 | -- break 321 | -- end 322 | -- end 323 | -- end 324 | -- 325 | -- return offset 326 | --end 327 | -- 328 | --function encodeProp(value, type, offset, buffer, protos) 329 | -- switch(type) 330 | -- case 'uInt32'= 331 | -- offset = writeBytes(buffer, offset, codec.encodeUInt32(value)) 332 | -- break 333 | -- case 'int32' = 334 | -- case 'sInt32'= 335 | -- offset = writeBytes(buffer, offset, codec.encodeSInt32(value)) 336 | -- break 337 | -- case 'float'= 338 | -- writeBytes(buffer, offset, codec.encodeFloat(value)) 339 | -- offset += 4 340 | -- break 341 | -- case 'double'= 342 | -- writeBytes(buffer, offset, codec.encodeDouble(value)) 343 | -- offset += 8 344 | -- break 345 | -- case 'string'= 346 | -- local length = codec.byteLength(value) 347 | -- 348 | -- --Encode length 349 | -- offset = writeBytes(buffer, offset, codec.encodeUInt32(length)) 350 | -- --write string 351 | -- codec.encodeStr(buffer, offset, value) 352 | -- offset += length 353 | -- break 354 | -- default = 355 | -- if protos.__messages{type}) 356 | -- --Use a tmp buffer to build an internal msg 357 | -- local tmpBuffer = new ArrayBuffer(codec.byteLength(JSON.stringify(value))) 358 | -- local length = 0 359 | -- 360 | -- length = encodeMsg(tmpBuffer, length, protos.__messages{type}, value) 361 | -- offset = writeBytes(buffer, offset, codec.encodeUInt32(length)) 362 | -- for i = 0 i < length 363 | -- buffer{offset} = tmpBuffer{i} 364 | -- offset++ 365 | -- end 366 | -- end 367 | -- break 368 | -- end 369 | -- 370 | -- return offset 371 | --end 372 | -- 373 | --function encodeArray(array, proto, offset, buffer, protos) 374 | -- local i = 0 375 | -- 376 | -- if util.isSimpleType(proto.type)) 377 | -- offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag)) 378 | -- offset = writeBytes(buffer, offset, codec.encodeUInt32(array.length)) 379 | -- for i = 0 i < array.length 380 | -- offset = encodeProp(array{i}, proto.type, offset, buffer) 381 | -- end 382 | -- else 383 | -- for i = 0 i < array.length 384 | -- offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag)) 385 | -- offset = encodeProp(array{i}, proto.type, offset, buffer, protos) 386 | -- end 387 | -- end 388 | -- 389 | -- return offset 390 | --end 391 | -- 392 | --function writeBytes(buffer, offset, bytes) 393 | -- for i = 0 i < bytes.length i++, offset++) 394 | -- buffer{offset} = bytes{i} 395 | -- end 396 | -- 397 | -- return offset 398 | --end 399 | -- 400 | --function encodeTag(type, tag) 401 | -- local value = constant.TYPES{type}or2 402 | -- return codec.encodeUInt32((tag<<3)|value) 403 | --end 404 | -- 405 | -- 406 | -- 407 | --local protobuf = exports.Protobuf 408 | --local MsgDecoder = exports.Protobuf.decoder {} 409 | -- 410 | --local codec = protobuf.codec 411 | --local util = protobuf.util 412 | -- 413 | --local buffer 414 | --local offset = 0 415 | -- 416 | --MsgDecoder.init = function(protos) 417 | -- this.protos = protos or end 418 | --end 419 | -- 420 | --MsgDecoder.setProtos = function(protos) 421 | -- if protos) 422 | -- this.protos = protos 423 | -- end 424 | --end 425 | -- 426 | --MsgDecoder.decode = function(route, buf) 427 | -- local protos = this.protos{route} 428 | -- 429 | -- buffer = buf 430 | -- offset = 0 431 | -- if protos) 432 | -- return decodeMsg(end, protos, buffer.length) 433 | -- end 434 | -- 435 | -- return nil 436 | --end 437 | -- 438 | --function decodeMsg(msg, protos, length) 439 | -- while offset>3 472 | -- end 473 | --end 474 | -- 475 | --function peekHead() 476 | -- local tag = codec.decodeUInt32(peekBytes()) 477 | -- 478 | -- return 479 | -- type = tag&0x7, 480 | -- tag = tag>>3 481 | -- end 482 | --end 483 | -- 484 | --function decodeProp(type, protos) 485 | -- switch(type) 486 | -- case 'uInt32'= 487 | -- return codec.decodeUInt32(getBytes()) 488 | -- case 'int32' = 489 | -- case 'sInt32' = 490 | -- return codec.decodeSInt32(getBytes()) 491 | -- case 'float' = 492 | -- local float = codec.decodeFloat(buffer, offset) 493 | -- offset += 4 494 | -- return float 495 | -- case 'double' = 496 | -- local double = codec.decodeDouble(buffer, offset) 497 | -- offset += 8 498 | -- return double 499 | -- case 'string' = 500 | -- local length = codec.decodeUInt32(getBytes()) 501 | -- 502 | -- local str = codec.decodeStr(buffer, offset, length) 503 | -- offset += length 504 | -- 505 | -- return str 506 | -- default = 507 | -- if protos and protos.__messages{type}) 508 | -- local length = codec.decodeUInt32(getBytes()) 509 | -- local msg {} 510 | -- decodeMsg(msg, protos.__messages{type}, offset+length) 511 | -- return msg 512 | -- end 513 | -- break 514 | -- end 515 | -- end 516 | -- 517 | -- function decodeArray(array, type, protos) 518 | -- if util.isSimpleType(type)) 519 | -- local length = codec.decodeUInt32(getBytes()) 520 | -- 521 | -- for i = 0 i < length 522 | -- array.push(decodeProp(type)) 523 | -- end 524 | -- else 525 | -- array.push(decodeProp(type, protos)) 526 | -- end 527 | -- end 528 | -- 529 | -- function getBytes(flag) 530 | -- local bytes = {} 531 | -- local pos = offset 532 | -- flag = flag or false 533 | -- 534 | -- local b 535 | -- 536 | -- do 537 | -- b = buffer{pos} 538 | -- bytes.push(b) 539 | -- pos++ 540 | -- while b >= 128) 541 | -- 542 | -- if not flag) 543 | -- offset = pos 544 | -- end 545 | -- return bytes 546 | -- end 547 | -- 548 | -- function peekBytes() 549 | -- return getBytes(true) 550 | -- end 551 | -- 552 | -- 553 | -- 554 | --(function (exports) 555 | -- 556 | -- exports.Protobuf.Parser {} 557 | -- local Parser = exports.Protobuf.Parser 558 | -- 559 | -- Parser.parse = function(protos) 560 | -- local maps {} 561 | -- for key in protos) 562 | -- maps{key} = parseObject(protos{key}) 563 | -- end 564 | -- 565 | -- return maps 566 | -- end 567 | -- 568 | -- function parseObject(obj) 569 | -- local proto {} 570 | -- local nestProtos {} 571 | -- local tags {} 572 | -- 573 | -- for name in obj) 574 | -- local tag = obj{name} 575 | -- local params = name.split(' ') 576 | -- 577 | -- switch(params{0}) 578 | -- case 'message'= 579 | -- if params.length not = 2) 580 | -- continue 581 | -- nestProtos{params{1}} = parseObject(tag) 582 | -- continue 583 | -- case 'required'= 584 | -- case 'optional'= 585 | -- case 'repeated'= 586 | -- --params length should be 3 and tag can't be duplicated 587 | -- if params.length not = 3 or tags{tag}) 588 | -- continue 589 | -- end 590 | -- proto{params{2}} = { 591 | -- option = params{0}, 592 | -- type = params{1}, 593 | -- tag = tag 594 | -- } 595 | -- tags{tag} = params{2} 596 | -- end 597 | -- end 598 | -- end 599 | -- 600 | -- proto.__messages = nestProtos 601 | -- proto.__tags = tags 602 | -- return proto 603 | --end 604 | -- 605 | -- 606 | -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/pomelo/Protocol.lua: -------------------------------------------------------------------------------- 1 | require("bit") 2 | 3 | local Protocol = class("Protocol") 4 | 5 | Protocol.strencode = function(str) 6 | do return {string.byte(str, 1, #str)} end 7 | end 8 | 9 | Protocol.strdecode = function(bytes) 10 | local array = {} 11 | local len = #bytes 12 | for i = 1, len do 13 | array[i] = string.char(bytes[i]) 14 | end 15 | return table.concat(array) 16 | end 17 | 18 | return Protocol 19 | -------------------------------------------------------------------------------- /pomelo_quick_x/scipts/pomelo/functions.lua: -------------------------------------------------------------------------------- 1 | local func = {} 2 | 3 | function func.copyArray(dest,doffset,src,soffset,length) 4 | if("table" ~= type(dest) or "table" ~= type(src) or length<=0 ) then 5 | return dest 6 | end 7 | for index=1,length do 8 | dest[doffset] = src[soffset] 9 | doffset = doffset + 1 10 | soffset = soffset + 1 11 | end 12 | end 13 | 14 | function func.printTable(t) 15 | for k,v in pairs(t) do 16 | echoInfo("k=%s,v=%s",k,v) 17 | end 18 | end 19 | 20 | return func --------------------------------------------------------------------------------