-- SPDX-License-Identifier: GPL-3.0-or-later local function tellRoomToObserver(self, player) local observee = self.players[1] player:doNotify("Setup", json.encode{ observee.id, observee._splayer:getScreenName(), observee._splayer:getAvatar(), }) player:doNotify("EnterRoom", json.encode{ #self.players, self.timeout, self.settings }) player:doNotify("StartGame", "") -- send player data for _, p in ipairs(self:getOtherPlayers(observee, false, true)) do player:doNotify("AddPlayer", json.encode{ p.id, p._splayer:getScreenName(), p._splayer:getAvatar(), false, p._splayer:getTotalGameTime(), }) end local player_circle = {} for i = 1, #self.players do table.insert(player_circle, self.players[i].id) end player:doNotify("ArrangeSeats", json.encode(player_circle)) -- send printed_cards for i = -2, -math.huge, -1 do local c = Fk.printed_cards[i] if not c then break end player:doNotify("PrintCard", json.encode{ c.name, c.suit, c.number }) end -- send card marks for id, marks in pairs(self.card_marks) do for k, v in pairs(marks) do player:doNotify("SetCardMark", json.encode{ id, k, v }) end end -- send banners for k, v in pairs(self.banners) do player:doNotify("SetBanner", json.encode{ k, v }) end for _, p in ipairs(self.players) do self:notifyProperty(player, p, "general") self:notifyProperty(player, p, "deputyGeneral") p:marshal(player, true) end player:doNotify("UpdateDrawPile", #self.draw_pile) player:doNotify("UpdateRoundNum", self:getTag("RoundCount") or 0) table.insert(self.observers, {observee.id, player, player:getId()}) end local function addObserver(self, id) local all_observers = self.room:getObservers() for _, p in fk.qlist(all_observers) do if p:getId() == id then tellRoomToObserver(self, p) self:doBroadcastNotify("AddObserver", json.encode{ p:getId(), p:getScreenName(), p:getAvatar() }) break end end end local function removeObserver(self, id) for _, t in ipairs(self.observers) do local pid = t[3] if pid == id then table.removeOne(self.observers, t) self:doBroadcastNotify("RemoveObserver", json.encode{ pid }) break end end end local request_handlers = {} request_handlers["reconnect"] = function(room, id, reqlist) local p = room:getPlayerById(id) if p then p:reconnect() end end request_handlers["observe"] = function(room, id, reqlist) addObserver(room, id) end request_handlers["leave"] = function(room, id, reqlist) removeObserver(room, id) end request_handlers["prelight"] = function(room, id, reqlist) local p = room:getPlayerById(id) if p then p:prelightSkill(reqlist[3], reqlist[4] == "true") end end request_handlers["luckcard"] = function(room, id, reqlist) local p = room:getPlayerById(id) local cancel = reqlist[3] == "false" local luck_data = room:getTag("LuckCardData") if not (p and luck_data) then return end local pdata = luck_data[id] if not cancel then pdata.luckTime = pdata.luckTime - 1 luck_data.discardInit(room, p) luck_data.drawInit(room, p, pdata.num) else pdata.luckTime = 0 end if pdata.luckTime > 0 then p:doNotify("AskForLuckCard", pdata.luckTime) else p.serverplayer:setThinking(false) end room:setTag("LuckCardData", luck_data) end request_handlers["changeself"] = function(room, id, reqlist) local p = room:getPlayerById(id) local toId = tonumber(reqlist[3]) local from = p local to = room:getPlayerById(toId) local from_sp = from._splayer -- 注意发来信息的玩家的主视角可能已经不是自己了 -- 先换成正确的玩家 from = table.find(room.players, function(p) return table.contains(p._observers, from_sp) end) -- 切换视角 table.removeOne(from._observers, from_sp) table.insert(to._observers, from_sp) from_sp:doNotify("ChangeSelf", json.encode { id = toId, handcards = to:getCardIds(Player.Hand), special_cards = to.special_cards, }) end request_handlers["surrender"] = function(room, id, reqlist) local player = room:getPlayerById(id) if not player then return end local logic = room.logic local curEvent = logic:getCurrentEvent() if curEvent then curEvent:addCleaner( function() player.surrendered = true room:broadcastProperty(player, "surrendered") local mode = Fk.game_modes[room.settings.gameMode] local winner = Pcall(mode.getWinner, mode, player) if winner ~= nil then room:gameOver(winner) end -- 以防万一 player.surrendered = false room.hasSurrendered = false end ) room.hasSurrendered = true room:doBroadcastNotify("CancelRequest", "") end end request_handlers["updatemini"] = function(room, pid, reqlist) local player = room:getPlayerById(pid) local data = player.mini_game_data if not data then return end local game = Fk.mini_games[data.type] if not (game and game.update_func) then return end local dat = table.simpleClone(reqlist) table.remove(dat, 1) table.remove(dat, 1) game.update_func(player, dat) end request_handlers["newroom"] = function(s, id) s:registerRoom(id) end -- 处理异步请求的协程,本身也是个死循环就是了。 -- 为了适应调度器,目前又暂且将请求分为“耗时请求”和不耗时请求。 -- 耗时请求处理后会立刻挂起。不耗时的请求则会不断处理直到请求队列空后再挂起。 local function requestLoop(self) while true do local ret = false local request = self.thread:fetchRequest() if request ~= "" then ret = true local reqlist = request:split(",") local roomId = tonumber(table.remove(reqlist, 1)) local room = self:getRoom(roomId) if room then RoomInstance = room local id = tonumber(reqlist[1]) local command = reqlist[2] Pcall(request_handlers[command], room, id, reqlist) RoomInstance = nil end end if not ret then coroutine.yield() end end end return requestLoop