FreeKill/lua/server/room.lua

278 lines
7.2 KiB
Lua

---@class Room : Object
---@field room fk.Room
---@field players ServerPlayer[]
---@field alive_players ServerPlayer[]
---@field current ServerPlayer
---@field game_finished boolean
---@field timeout integer
local Room = class("Room")
-- load classes used by the game
GameLogic = require "server.gamelogic"
ServerPlayer = require "server.serverplayer"
fk.room_callback = {}
---@param _room fk.Room
function Room:initialize(_room)
self.room = _room
self.room.callback = function(_self, command, jsonData)
local cb = fk.room_callback[command]
if (type(cb) == "function") then
cb(jsonData)
else
print("Lobby error: Unknown command " .. command);
end
end
self.room.startGame = function(_self)
self:run()
end
self.players = {}
self.alive_players = {}
self.current = nil
self.game_finished = false
self.timeout = _room:getTimeout()
end
-- When this function returns, the Room(C++) thread stopped.
function Room:run()
for _, p in fk.qlist(self.room:getPlayers()) do
local player = ServerPlayer:new(p)
player.state = p:getStateString()
player.room = self
table.insert(self.players, player)
end
self.logic = GameLogic:new(self)
self.logic:run()
end
---@param player ServerPlayer
---@param property string
function Room:broadcastProperty(player, property)
for _, p in ipairs(self.players) do
self:notifyProperty(p, player, property)
end
end
---@param p ServerPlayer
---@param player ServerPlayer
---@param property string
function Room:notifyProperty(p, player, property)
p:doNotify("PropertyUpdate", json.encode{
player:getId(),
property,
player[property],
})
end
---@param command string
---@param jsonData string
---@param players ServerPlayer[] # default all players
function Room:doBroadcastNotify(command, jsonData, players)
players = players or self.players
local tolist = fk.SPlayerList()
for _, p in ipairs(players) do
tolist:append(p.serverplayer)
end
self.room:doBroadcastNotify(tolist, command, jsonData)
end
---@param player ServerPlayer
---@param command string
---@param jsonData string
---@param wait boolean # default true
---@return string | nil
function Room:doRequest(player, command, jsonData, wait)
if wait == nil then wait = true end
player:doRequest(command, jsonData, self.timeout)
if wait then
return player:waitForReply(self.timeout)
end
end
---@param command string
---@param players ServerPlayer[]
function Room:doBroadcastRequest(command, players)
players = players or self.players
self:notifyMoveFocus(players, command)
for _, p in ipairs(players) do
self:doRequest(p, command, p.request_data, false)
end
local remainTime = self.timeout
local currentTime = os.time()
local elapsed = 0
for _, p in ipairs(players) do
elapsed = os.time() - currentTime
remainTime = remainTime - elapsed
p:waitForReply(remainTime)
end
end
---@param players ServerPlayer | ServerPlayer[]
---@param command string
function Room:notifyMoveFocus(players, command)
if (players.class) then
players = {players}
end
local ids = {}
for _, p in ipairs(players) do
table.insert(ids, p:getId())
end
self:doBroadcastNotify("MoveFocus", json.encode{
ids,
command
})
end
function Room:adjustSeats()
local players = {}
local p = 0
for i = 1, #self.players do
if self.players[i].role == "lord" then
p = i
break
end
end
for j = p, #self.players do
table.insert(players, self.players[j])
end
for j = 1, p - 1 do
table.insert(players, self.players[j])
end
self.players = players
local player_circle = {}
for i = 1, #self.players do
self.players[i].seat = i
table.insert(player_circle, self.players[i]:getId())
end
self:doBroadcastNotify("ArrangeSeats", json.encode(player_circle))
end
---@return ServerPlayer | nil
function Room:getLord()
local lord = self.players[1]
if lord.role == "lord" then return lord end
for _, p in ipairs(self.players) do
if p.role == "lord" then return p end
end
return nil
end
---@param expect ServerPlayer
---@return ServerPlayer[]
function Room:getOtherPlayers(expect)
local ret = {table.unpack(self.players)}
table.removeOne(ret, expect)
return ret
end
---@param player ServerPlayer
---@param generals string[]
---@return string
function Room:askForGeneral(player, generals)
local command = "AskForGeneral"
self:notifyMoveFocus(player, command)
if #generals == 1 then return generals[1] end
local defaultChoice = generals[1]
if (player.state == "online") then
local result = self:doRequest(player, command, json.encode(generals))
if result == "" then
return defaultChoice
else
-- TODO: result is a JSON array
-- update here when choose multiple generals
return json.decode(result)[1]
end
end
return defaultChoice
end
function Room:gameOver()
self.game_finished = true
-- dosomething
self.room:gameOver()
end
---@param id integer
function Room:findPlayerById(id)
for _, p in ipairs(self.players) do
if p:getId() == id then
return p
end
end
return nil
end
---@param player ServerPlayer
---@param choices string[]
---@param skill_name string
function Room:askForChoice(player, choices, skill_name, data)
if #choices == 1 then return choices[1] end
local command = "AskForChoice"
self:notifyMoveFocus(player, skill_name)
local result = self:doRequest(player, command, json.encode{
choices, skill_name
})
if result == "" then result = choices[1] end
return result
end
---@param player ServerPlayer
---@param skill_name string
---@return boolean
function Room:askForSkillInvoke(player, skill_name, data)
local command = "AskForSkillInvoke"
self:notifyMoveFocus(player, skill_name)
local invoked = false
local result = self:doRequest(player, command, skill_name)
if result ~= "" then invoked = true end
return invoked
end
fk.room_callback["QuitRoom"] = function(jsonData)
-- jsonData: [ int uid ]
local data = json.decode(jsonData)
local player = fk.ServerInstance:findPlayer(tonumber(data[1]))
local room = player:getRoom()
if not room:isLobby() then
room:removePlayer(player)
end
end
fk.room_callback["PlayerStateChanged"] = function(jsonData)
-- jsonData: [ int uid, string stateString ]
-- note: this function is not called by Router.
-- note: when this function is called, the room must be started
local data = json.decode(jsonData)
local id = data[1]
local stateString = data[2]
RoomInstance:findPlayerById(id).state = stateString
end
fk.room_callback["DoLuaScript"] = function(jsonData)
-- jsonData: [ int uid, string luaScript ]
-- warning: only use this in debugging mode.
if not DebugMode then return end
local data = json.decode(jsonData)
assert(load(data[2]))()
end
function CreateRoom(_room)
RoomInstance = Room:new(_room)
end