新功能 (#141)

增加最大选将18
将退出房改为延迟按钮
修虚拟闪电bug
修看破bug
增加长度限制
将异步模块单独一个Lua
This commit is contained in:
notify 2023-04-30 18:51:05 +08:00 committed by GitHub
parent 29fdfca9bb
commit 3dfff72836
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 174 additions and 119 deletions

View File

@ -64,6 +64,14 @@ io = nil
package = nil package = nil
load = nil load = nil
loadfile = nil loadfile = nil
local _dofile = dofile
dofile = function(f)
local errmsg = "Refusing dofile that not in game directory"
assert(not f:startsWith("/"), errmsg)
assert(not f:startsWith(".."), errmsg)
assert(not f:find(":"), errmsg)
return _dofile(f)
end
-- 初始化Engine类并置于Fk全局变量中这里会加载拓展包 -- 初始化Engine类并置于Fk全局变量中这里会加载拓展包
dofile "lua/fk_ex.lua" dofile "lua/fk_ex.lua"

View File

@ -61,9 +61,13 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
for _, info in ipairs(data.moveInfo) do for _, info in ipairs(data.moveInfo) do
local realFromArea = self:getCardArea(info.cardId) local realFromArea = self:getCardArea(info.cardId)
local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special } local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
local virtualEquip
if table.contains(playerAreas, realFromArea) and data.from then if table.contains(playerAreas, realFromArea) and data.from then
self:getPlayerById(data.from):removeCards(realFromArea, { info.cardId }, info.fromSpecialName) local from = self:getPlayerById(data.from)
from:removeCards(realFromArea, { info.cardId }, info.fromSpecialName)
virtualEquip = from:getVirualEquip(info.cardId)
elseif realFromArea ~= Card.Unknown then elseif realFromArea ~= Card.Unknown then
local fromAreaIds = {} local fromAreaIds = {}
if realFromArea == Card.Processing then if realFromArea == Card.Processing then
@ -80,7 +84,10 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
end end
if table.contains(playerAreas, data.toArea) and data.to then if table.contains(playerAreas, data.toArea) and data.to then
self:getPlayerById(data.to):addCards(data.toArea, { info.cardId }, data.specialName) local to = self:getPlayerById(data.to)
if virtualEquip then to:addVirtualEquip(virtualEquip) end
to:addCards(data.toArea, { info.cardId }, data.specialName)
else else
local toAreaIds = {} local toAreaIds = {}
if data.toArea == Card.Processing then if data.toArea == Card.Processing then

129
lua/server/request.lua Normal file
View File

@ -0,0 +1,129 @@
-- 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.serverplayer:getScreenName(),
observee.serverplayer:getAvatar(),
})
player:doNotify("EnterRoom", json.encode{
#self.players, self.timeout, self.settings
})
-- send player data
for _, p in ipairs(self:getOtherPlayers(observee, true, true)) do
player:doNotify("AddPlayer", json.encode{
p.id,
p.serverplayer:getScreenName(),
p.serverplayer:getAvatar(),
})
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))
for _, p in ipairs(self.players) do
self:notifyProperty(player, p, "general")
self:notifyProperty(player, p, "deputyGeneral")
p:marshal(player)
end
player:doNotify("UpdateDrawPile", #self.draw_pile)
player:doNotify("UpdateRoundNum", self:getTag("RoundCount") or 0)
table.insert(self.observers, {observee.id, player})
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 __, p = table.unpack(t)
if p:getId() == id then
table.removeOne(self.observers, t)
self:doBroadcastNotify("RemoveObserver", json.encode{
p:getId(),
})
break
end
end
end
local request_handlers = {}
request_handlers["reconnect"] = function(room, id, reqlist)
local p = room:getPlayerById(id)
p:reconnect()
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)
p:prelightSkill(reqlist[3], reqlist[4] == "true")
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),
})
end
local function requestLoop(self, rest_time)
while true do
local ret = false
local request = self.room:fetchRequest()
if request ~= "" then
ret = true
local reqlist = request:split(",")
local id = tonumber(reqlist[1])
local command = reqlist[2]
request_handlers[command](self, id, reqlist)
elseif rest_time > 10 then
-- let current thread sleep 10ms
-- otherwise CPU usage will be 100% (infinite yield <-> resume loop)
fk.QThread_msleep(10)
end
coroutine.yield(ret)
end
end
return requestLoop

View File

@ -630,119 +630,7 @@ function Room:doRaceRequest(command, players, jsonData)
return ret return ret
end end
-- main loop for the request handling coroutine Room.requestLoop = require "server.request"
--- 这是个私有函数,不用管。
function Room:requestLoop(rest_time)
local function tellRoomToObserver(player)
local observee = self.players[1]
player:doNotify("Setup", json.encode{
observee.id,
observee.serverplayer:getScreenName(),
observee.serverplayer:getAvatar(),
})
player:doNotify("EnterRoom", json.encode{
#self.players, self.timeout, self.settings
})
-- send player data
for _, p in ipairs(self:getOtherPlayers(observee, true, true)) do
player:doNotify("AddPlayer", json.encode{
p.id,
p.serverplayer:getScreenName(),
p.serverplayer:getAvatar(),
})
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))
for _, p in ipairs(self.players) do
self:notifyProperty(player, p, "general")
self:notifyProperty(player, p, "deputyGeneral")
p:marshal(player)
end
player:doNotify("UpdateDrawPile", #self.draw_pile)
player:doNotify("UpdateRoundNum", self:getTag("RoundCount") or 0)
table.insert(self.observers, {observee.id, player})
end
local function addObserver(id)
local all_observers = self.room:getObservers()
for _, p in fk.qlist(all_observers) do
if p:getId() == id then
tellRoomToObserver(p)
self:doBroadcastNotify("AddObserver", json.encode{
p:getId(),
p:getScreenName(),
p:getAvatar()
})
break
end
end
end
local function removeObserver(id)
for _, t in ipairs(self.observers) do
local __, p = table.unpack(t)
if p:getId() == id then
table.removeOne(self.observers, t)
self:doBroadcastNotify("RemoveObserver", json.encode{
p:getId(),
})
break
end
end
end
while true do
local ret = false
local request = self.room:fetchRequest()
if request ~= "" then
ret = true
local reqlist = request:split(",")
local id = tonumber(reqlist[1])
local command = reqlist[2]
if command == "reconnect" then
self:getPlayerById(id):reconnect()
elseif command == "observe" then
addObserver(id)
elseif command == "leave" then
removeObserver(id)
elseif command == "prelight" then
self:getPlayerById(id):prelightSkill(reqlist[3], reqlist[4] == "true")
elseif command == "changeself" then
local toId = tonumber(reqlist[3])
local from = self:getPlayerById(id)
local to = self:getPlayerById(toId)
local from_sp = from._splayer
-- 注意发来信息的玩家的主视角可能已经不是自己了
-- 先换成正确的玩家
from = table.find(self.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),
})
end
elseif rest_time > 10 then
-- let current thread sleep 10ms
-- otherwise CPU usage will be 100% (infinite yield <-> resume loop)
fk.QThread_msleep(10)
end
coroutine.yield(ret)
end
end
--- 延迟一段时间。 --- 延迟一段时间。
--- ---
@ -2016,6 +1904,15 @@ function Room:doCardEffect(cardEffectEvent)
break break
end end
end end
if not table.contains(players, p) then
for _, s in ipairs(p.player_skills) do
if s.pattern and Exppattern:Parse("nullification"):matchExp(s.pattern)
and not table.contains(cardEffectEvent.disresponsiveList or {}, p.id) then
table.insert(players, p)
break
end
end
end
end end
local prompt = "" local prompt = ""

View File

@ -670,9 +670,13 @@ local lightningSkill = fk.CreateActiveSkill{
local nextp = to local nextp = to
repeat repeat
nextp = nextp:getNextAlive() nextp = nextp:getNextAlive()
if nextp == to then return end if nextp == to then break end
until not nextp:hasDelayedTrick("lightning") until not nextp:hasDelayedTrick("lightning")
if effect.card:isVirtual() then
nextp:addVirtualEquip(effect.card)
end
room:moveCards{ room:moveCards{
ids = room:getSubcardsByRule(effect.card, { Card.Processing }), ids = room:getSubcardsByRule(effect.card, { Card.Processing }),
to = nextp.id, to = nextp.id,

View File

@ -101,6 +101,7 @@ local test_active = fk.CreateActiveSkill{
} }
local test_vs = fk.CreateViewAsSkill{ local test_vs = fk.CreateViewAsSkill{
name = "test_vs", name = "test_vs",
pattern = "nullification",
card_filter = function(self, to_select, selected) card_filter = function(self, to_select, selected)
return #selected == 0 return #selected == 0
end, end,
@ -113,6 +114,8 @@ local test_vs = fk.CreateViewAsSkill{
"dismantlement", "dismantlement",
"savage_assault", "savage_assault",
"archery_attack", "archery_attack",
"lightning",
"nullification",
} }
} }
end, end,

View File

@ -83,6 +83,7 @@ Item {
} }
TextField { TextField {
id: screenNameEdit id: screenNameEdit
maximumLength: 32
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: qsTr("Username") placeholderText: qsTr("Username")
text: "" text: ""
@ -103,6 +104,7 @@ Item {
} }
TextField { TextField {
id: passwordEdit id: passwordEdit
maximumLength: 64
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: qsTr("Password") placeholderText: qsTr("Password")
text: "" text: ""

View File

@ -15,6 +15,7 @@ ColumnLayout {
} }
TextField { TextField {
id: roomName id: roomName
maximumLength: 64
font.pixelSize: 18 font.pixelSize: 18
text: Backend.translate("$RoomName").arg(Self.screenName) text: Backend.translate("$RoomName").arg(Self.screenName)
} }
@ -70,7 +71,7 @@ ColumnLayout {
SpinBox { SpinBox {
id: generalNum id: generalNum
from: 3 from: 3
to: 8 to: 18
value: config.preferredGeneralNum value: config.preferredGeneralNum
onValueChanged: { onValueChanged: {

View File

@ -27,6 +27,7 @@ ColumnLayout {
} }
TextField { TextField {
id: avatarName id: avatarName
maximumLength: 64
font.pixelSize: 18 font.pixelSize: 18
text: Self.avatar text: Self.avatar
} }

View File

@ -68,12 +68,13 @@ Item {
} }
// tmp // tmp
Button { DelayButton {
id: quitButton id: quitButton
text: "quit" text: "quit"
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
onClicked: { delay: Debugging ? 10 : 1000
onActivated: {
// ClientInstance.clearPlayers(); // ClientInstance.clearPlayers();
ClientInstance.notifyServer("QuitRoom", "[]"); ClientInstance.notifyServer("QuitRoom", "[]");
} }

View File

@ -22,6 +22,7 @@ Item {
spacing: 8 spacing: 8
TextField { TextField {
id: screenNameEdit id: screenNameEdit
maximumLength: 32
text: "player" text: "player"
onTextChanged: { onTextChanged: {
passwordEdit.text = ""; passwordEdit.text = "";
@ -35,6 +36,7 @@ Item {
} }
TextField { TextField {
id: passwordEdit id: passwordEdit
maximumLength: 64
text: "" text: ""
echoMode: TextInput.Password echoMode: TextInput.Password
passwordCharacter: "*" passwordCharacter: "*"