local enet = require("enet") local socket = require("socket") local util = require("util") local common = require("common") local client = {} local function create_client(secret) local clt = {} clt.host = enet.host_create() clt.secret = secret return clt end local function connect(clt, addr) clt.server = clt.host:connect(addr) clt.server_req = socket.gettime() clt.status = "wait_server" end function client.join(invite, match_addr) local invite_dec = util.base64_dec(invite) if not invite_dec then return nil, "invalid_invite" end local game_id = invite_dec:sub(1, common.gameid_len) local secret = invite_dec:sub(common.gameid_len+1) local clt = create_client(secret) clt.match = clt.host:connect(match_addr or common.default_match_addr) util.send(clt.match, { type = "match_join", game_id = game_id }) clt.match_req = socket.gettime() clt.game_id = game_id clt.status = "wait_match" return clt end function client.connect(addr, secret) local clt = create_client(secret) connect(clt, addr) return clt end local function handle_match(clt, pkt) if pkt.type == "client_join" then if type(pkt.peer_addr) ~= "string" then print("[client] client_join: invalid peer_addr") return end connect(clt, pkt.peer_addr) elseif pkt.type == "client_join_fail" then clt.status = "fail_match" end end local function handle_server(clt, pkt) if pkt.type == "client_hi" then clt.status = "active" elseif pkt.type == "client_reject" then clt.status = "fail_server" elseif pkt.type == "client_info" then clt.info = pkt elseif pkt.type == "client_player" then clt.player = pkt.name elseif pkt.type == "client_player_fail" then clt.player_error = pkt.error end end function client.update(clt) local event = clt.host:service(20) while event do if event.type == "receive" then local pkt = util.json_dec(event.data) if pkt then if event.peer == clt.match and clt.status == "wait_match" then handle_match(clt, pkt) clt.match:disconnect() clt.match = nil elseif event.peer == clt.server then handle_server(clt, pkt) end end elseif event.type == "connect" then if event.peer == clt.match and clt.status == "wait_match" then util.send(clt.match, { type = "match_join", game_id = util.base64_enc(clt.game_id) }) elseif event.peer == clt.server and clt.status == "wait_server" then util.send(clt.server, { type = "server_hi", secret = util.base64_enc(clt.secret) }) else event.peer:disconnect_now() end print("[client] connect " .. tostring(event.peer)) elseif event.type == "disconnect" then print("[client] disconnect " .. tostring(event.peer)) if event.peer == clt.server and clt.status == "active" then clt.status = "disco" end end event = clt.host:service() end end function client.status(clt) if clt.status == "wait_match" and clt.match_req+3 < socket.gettime() then clt.status = "timeout_match" elseif clt.status == "wait_server" and clt.server_req+5 < socket.gettime() then clt.status = "timeout_server" end return clt.status end function client.select_player(clt, name, create) util.send(clt.server, { type = "server_player", name = name, create = create, }) end function client.close(clt) if clt.match then clt.match:disconnect() end if clt.server then clt.server:disconnect() end clt.host:flush() clt.host:destroy() end return client