mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-15 19:22:25 +08:00
修小bug,分离事件栈 (#303)
This commit is contained in:
parent
1005863b1e
commit
51a10ebcf4
|
@ -505,7 +505,9 @@ end
|
|||
function Stack:pop()
|
||||
if self.p == 0 then return nil end
|
||||
self.p = self.p - 1
|
||||
return self.t[self.p + 1]
|
||||
local ret = self.t[self.p + 1]
|
||||
self.t[self.p + 1] = nil
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
-- 某类事件对应的结束事件,其id刚好就是那个事件的相反数
|
||||
-- GameEvent.EventFinish = -1
|
||||
|
||||
GameEvent.Game = 0
|
||||
|
||||
GameEvent.ChangeHp = 1
|
||||
GameEvent.Damage = 2
|
||||
GameEvent.LoseHp = 3
|
||||
|
@ -44,10 +46,10 @@ dofile "lua/server/events/pindian.lua"
|
|||
|
||||
-- 20 = CardEffect
|
||||
GameEvent.ChangeProperty = 21
|
||||
dofile "lua/server/events/misc.lua"
|
||||
|
||||
-- TODO: fix this
|
||||
GameEvent.BreakEvent = 999
|
||||
-- 新的clear函数专用
|
||||
GameEvent.ClearEvent = 9999
|
||||
dofile "lua/server/events/misc.lua"
|
||||
|
||||
for _, l in ipairs(Fk._custom_events) do
|
||||
local name, p, m, c, e = l.name, l.p, l.m, l.c, l.e
|
||||
|
@ -58,6 +60,8 @@ for _, l in ipairs(Fk._custom_events) do
|
|||
end
|
||||
|
||||
local eventTranslations = {
|
||||
[GameEvent.Game] = "GameEvent.Game",
|
||||
|
||||
[GameEvent.ChangeHp] = "GameEvent.ChangeHp",
|
||||
[GameEvent.Damage] = "GameEvent.Damage",
|
||||
[GameEvent.LoseHp] = "GameEvent.LoseHp",
|
||||
|
@ -80,7 +84,7 @@ local eventTranslations = {
|
|||
|
||||
[GameEvent.ChangeProperty] = "GameEvent.ChangeProperty",
|
||||
|
||||
[GameEvent.BreakEvent] = "GameEvent.BreakEvent",
|
||||
[GameEvent.ClearEvent] = "GameEvent.ClearEvent",
|
||||
}
|
||||
|
||||
function GameEvent.static:translate(id)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
GameEvent.functions[GameEvent.Game] = function(self)
|
||||
self.room.logic:run()
|
||||
end
|
||||
|
||||
GameEvent.functions[GameEvent.ChangeProperty] = function(self)
|
||||
local data = table.unpack(self.data)
|
||||
local room = self.room
|
||||
|
@ -120,3 +124,25 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self)
|
|||
|
||||
logic:trigger(fk.AfterPropertyChange, player, data)
|
||||
end
|
||||
|
||||
GameEvent.functions[GameEvent.ClearEvent] = function(self)
|
||||
local event = self.data[1]
|
||||
local logic = self.room.logic
|
||||
event:clear_func()
|
||||
for _, f in ipairs(event.extra_clear_funcs) do
|
||||
if type(f) == "function" then f(event) end
|
||||
end
|
||||
|
||||
-- cleaner顺利执行完了,出栈吧
|
||||
local end_id = logic.current_event_id + 1
|
||||
if event.id ~= end_id - 1 then
|
||||
logic.all_game_events[end_id] = event.event
|
||||
logic.current_event_id = end_id
|
||||
event.end_id = end_id
|
||||
else
|
||||
event.end_id = event.id
|
||||
end
|
||||
|
||||
logic.game_event_stack:pop()
|
||||
logic.cleaner_stack:pop()
|
||||
end
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
---@field public extra_clear_funcs fun(self:GameEvent)[] @ 事件结束时执行的自定义函数列表
|
||||
---@field public exit_func fun(self: GameEvent) @ 事件结束后执行的函数
|
||||
---@field public extra_exit_funcs fun(self:GameEvent)[] @ 事件结束后执行的自定义函数
|
||||
---@field public exec_ret boolean? @ exec函数的返回值,可能不存在
|
||||
---@field public interrupted boolean @ 事件是否是因为被中断而结束的,可能是防止事件或者被杀
|
||||
---@field public killed boolean @ 事件因为终止一切结算而被中断(所谓的“被杀”)
|
||||
---@field public revived boolean @ 事件被killed,但因为在cleaner中发生而被复活
|
||||
local GameEvent = class("GameEvent")
|
||||
|
||||
---@type (fun(self: GameEvent): bool)[]
|
||||
|
@ -163,190 +163,28 @@ function GameEvent:searchEvents(eventType, n, func, endEvent)
|
|||
return ret
|
||||
end
|
||||
|
||||
function GameEvent:clear()
|
||||
local clear_co = coroutine.create(function()
|
||||
self:clear_func()
|
||||
for _, f in ipairs(self.extra_clear_funcs) do
|
||||
if type(f) == "function" then f(self) end
|
||||
end
|
||||
end)
|
||||
function GameEvent:exec()
|
||||
local room = self.room
|
||||
local logic = room.logic
|
||||
self.parent = logic:getCurrentEvent()
|
||||
|
||||
local zhuran_jmp, zhuran_msg -- SB老朱然
|
||||
if self:prepare_func() then return true end
|
||||
|
||||
while true do
|
||||
local err, yield_result, extra_yield_result = coroutine.resume(clear_co)
|
||||
logic:pushEvent(self)
|
||||
|
||||
if err == false then
|
||||
-- handle error, then break
|
||||
if not string.find(yield_result, "__manuallyBreak") then
|
||||
fk.qCritical(yield_result .. "\n" .. debug.traceback(clear_co))
|
||||
end
|
||||
coroutine.close(clear_co)
|
||||
break
|
||||
end
|
||||
local co = coroutine.create(self.main_func)
|
||||
self._co = co
|
||||
|
||||
if yield_result == "__handleRequest" then
|
||||
-- yield to requestLoop
|
||||
coroutine.yield(yield_result, extra_yield_result)
|
||||
coroutine.yield(self, "__newEvent")
|
||||
|
||||
elseif type(yield_result) == "table" and yield_result.class
|
||||
and yield_result:isInstanceOf(GameEvent) and self ~= yield_result then
|
||||
|
||||
-- 不是,谁TM还在cleaner里面玩老朱然啊
|
||||
-- 总之,cleaner不能断
|
||||
-- 倒是没必要手动resume,新一轮while true会自动resume,只要把返回值
|
||||
-- 传回去就行
|
||||
|
||||
-- 一般来说都是由cleaner中的trigger引起
|
||||
-- 以胆守合击为例就是trigger -> SkillEffect事件 -> UseCard事件 -> 胆守
|
||||
-- 此时胆守的话最后从SkillEffect事件的exec内部yield出来
|
||||
-- 当前协程就应该正在执行room:useSkill函数,resume会去只会让那个函数返回
|
||||
|
||||
if zhuran_jmp == nil or zhuran_jmp.id > yield_result.id then
|
||||
zhuran_jmp = yield_result
|
||||
zhuran_msg = extra_yield_result
|
||||
end
|
||||
|
||||
-- 自己本来应该被杀的但是因为自己正在执行self:clear()而逃过一劫啊
|
||||
-- 还是得标记一下被杀才行,顺便因为实际上没死所以标记被复活
|
||||
self.killed = true
|
||||
self.revived = true
|
||||
-- 什么都不做,等下轮while自己resume
|
||||
else
|
||||
coroutine.close(clear_co)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- cleaner顺利执行完了,出栈吧
|
||||
local logic = RoomInstance.logic
|
||||
local end_id = logic.current_event_id + 1
|
||||
if self.id ~= end_id - 1 then
|
||||
logic.all_game_events[end_id] = self.event
|
||||
logic.current_event_id = end_id
|
||||
self.end_id = end_id
|
||||
else
|
||||
self.end_id = self.id
|
||||
end
|
||||
|
||||
logic.game_event_stack:pop()
|
||||
|
||||
-- 好了确保cleaner走完了,此时中断就会进入下层事件的正常中断处理
|
||||
if zhuran_jmp then
|
||||
coroutine.close(self._co)
|
||||
coroutine.yield(zhuran_jmp, zhuran_msg)
|
||||
|
||||
-- 此时仍可能出现在插结在其他事件的clear函数中
|
||||
-- 但就算被交付回去了,也能安然返回而不是继续while
|
||||
-- 但愿如此吧
|
||||
end
|
||||
|
||||
-- 保险而已,其实如果被杀的话应该已经在前面的yield终止了
|
||||
-- 但担心cleaner嵌套(三国杀是这样的)还是补一刀
|
||||
if self.killed then return end
|
||||
|
||||
-- 恭喜没被杀掉,我们来执行一些事件结束之后的结算吧
|
||||
Pcall(self.exit_func, self)
|
||||
for _, f in ipairs(self.extra_exit_funcs) do
|
||||
if type(f) == "function" then
|
||||
Pcall(f, self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function breakEvent(self, extra_yield_result)
|
||||
local cancelEvent = GameEvent:new(GameEvent.BreakEvent, self)
|
||||
cancelEvent.toId = self.id
|
||||
local notcanceled = cancelEvent:exec()
|
||||
local ret, extra_ret = false, nil
|
||||
if not notcanceled then
|
||||
self.interrupted = true
|
||||
self:clear()
|
||||
ret = true
|
||||
extra_ret = extra_yield_result
|
||||
end
|
||||
return ret, extra_ret
|
||||
end
|
||||
|
||||
function GameEvent:exec()
|
||||
local room = self.room
|
||||
local logic = room.logic
|
||||
local ret = false -- false or nil means this event is running normally
|
||||
local extra_ret
|
||||
self.parent = logic:getCurrentEvent()
|
||||
|
||||
if self:prepare_func() then return true end
|
||||
|
||||
logic.game_event_stack:push(self)
|
||||
|
||||
logic.current_event_id = logic.current_event_id + 1
|
||||
self.id = logic.current_event_id
|
||||
logic.all_game_events[self.id] = self
|
||||
logic.event_recorder[self.event] = logic.event_recorder[self.event] or {}
|
||||
table.insert(logic.event_recorder[self.event], self)
|
||||
|
||||
local co = coroutine.create(self.main_func)
|
||||
self._co = co
|
||||
while true do
|
||||
local err, yield_result, extra_yield_result = coroutine.resume(co)
|
||||
|
||||
if err == false then
|
||||
-- handle error, then break
|
||||
if not string.find(yield_result, "__manuallyBreak") then
|
||||
fk.qCritical(yield_result .. "\n" .. debug.traceback(co))
|
||||
end
|
||||
self.interrupted = true
|
||||
self:clear()
|
||||
ret = true
|
||||
coroutine.close(co)
|
||||
break
|
||||
end
|
||||
|
||||
if yield_result == "__handleRequest" then
|
||||
-- yield to requestLoop
|
||||
coroutine.yield(yield_result, extra_yield_result)
|
||||
|
||||
elseif type(yield_result) == "table" and yield_result.class
|
||||
and yield_result:isInstanceOf(GameEvent) then
|
||||
|
||||
if self ~= yield_result then
|
||||
-- yield to corresponding GameEvent, first pop self from stack
|
||||
self.interrupted = true
|
||||
self.killed = true -- 老朱然!你不得好死
|
||||
self:clear()
|
||||
-- logic.game_event_stack:pop(self)
|
||||
coroutine.close(co)
|
||||
|
||||
-- then, call yield
|
||||
coroutine.yield(yield_result, extra_yield_result)
|
||||
|
||||
-- 如果是在cleaner/exit里面发生此类中断的话是会被cleaner原地返回的
|
||||
-- 此时正常执行程序流就变成继续while循环了,这是不行的
|
||||
break
|
||||
elseif extra_yield_result == "__breakEvent" then
|
||||
if breakEvent(self) then
|
||||
coroutine.close(co)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
elseif yield_result == "__breakEvent" then
|
||||
-- try to break this event
|
||||
if breakEvent(self) then
|
||||
coroutine.close(co)
|
||||
break
|
||||
end
|
||||
|
||||
else
|
||||
-- normally exit, simply break the loop
|
||||
self:clear()
|
||||
extra_ret = yield_result
|
||||
coroutine.close(co)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return ret, extra_ret
|
||||
return self.interrupted, self.exec_ret
|
||||
end
|
||||
|
||||
function GameEvent:shutdown()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
---@field public refresh_skill_table table<Event, TriggerSkill[]>
|
||||
---@field public skills string[]
|
||||
---@field public game_event_stack Stack
|
||||
---@field public cleaner_stack Stack
|
||||
---@field public role_table string[][]
|
||||
---@field public all_game_events GameEvent[]
|
||||
---@field public event_recorder table<integer, GameEvent>
|
||||
|
@ -20,6 +21,7 @@ function GameLogic:initialize(room)
|
|||
self.refresh_skill_table = {}
|
||||
self.skills = {} -- skillName[]
|
||||
self.game_event_stack = Stack:new()
|
||||
self.cleaner_stack = Stack:new()
|
||||
self.all_game_events = {}
|
||||
self.event_recorder = {}
|
||||
self.current_event_id = 0
|
||||
|
@ -389,9 +391,7 @@ function GameLogic:trigger(event, target, data, refresh_only)
|
|||
skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
||||
|
||||
broken = broken or (event == fk.AskForPeaches
|
||||
and room:getPlayerById(data.who).hp > 0) or cur_event.revived
|
||||
-- ^^^^^^^^^^^^^^^^^
|
||||
-- 如果事件复活了,那么其实说明事件已经死过了,赶紧break掉
|
||||
and room:getPlayerById(data.who).hp > 0) or cur_event.killed
|
||||
|
||||
if broken then break end
|
||||
end
|
||||
|
@ -408,6 +408,138 @@ function GameLogic:trigger(event, target, data, refresh_only)
|
|||
return broken
|
||||
end
|
||||
|
||||
-- 此为启动事件管理器并启动第一个事件的初始函数
|
||||
function GameLogic:start()
|
||||
local root_event = GameEvent:new(GameEvent.Game)
|
||||
|
||||
self:pushEvent(root_event)
|
||||
|
||||
-- 此时的协程:room.main_co
|
||||
-- 事件管理器协程,同时也是Game事件
|
||||
-- 当新事件想要exec时,就切回此处,由这里负责调度协程
|
||||
-- 一个事件结束后也切回此处,然后resume
|
||||
local co = coroutine.create(root_event.main_func)
|
||||
root_event._co = co
|
||||
|
||||
local jump_to -- shutdown函数用
|
||||
|
||||
while true do
|
||||
-- 对于cleaner和正常事件,处理更后面来的
|
||||
local ne = self:getCurrentEvent()
|
||||
local ce = self:getCurrentCleaner()
|
||||
local e = ce and (ce.id >= ne.id and ce or ne) or ne
|
||||
|
||||
-- 如果正在jump的话,判断是否需要继续clean,否则正常继续
|
||||
if e == ne and jump_to ~= nil then
|
||||
e.interrupted = true
|
||||
e.killed = e ~= jump_to
|
||||
self:clearEvent(e)
|
||||
coroutine.close(e._co)
|
||||
if e == jump_to then jump_to = nil end -- shutdown结束了
|
||||
e = self:getCurrentCleaner()
|
||||
end
|
||||
|
||||
-- ret, evt解释:
|
||||
-- * true, nil: 中止
|
||||
-- * false, nil: 正常结束
|
||||
-- * true, GameEvent: 中止直到某event
|
||||
-- * false, GameEvent: 未结束,插入新event
|
||||
-- 若jump_to不为nil,表示正在中断至某某事件
|
||||
local ret, evt = self:resumeEvent(e)
|
||||
if evt == nil then
|
||||
e.interrupted = ret
|
||||
self:clearEvent(e)
|
||||
coroutine.close(e._co)
|
||||
elseif ret == true then
|
||||
-- 跳到越早发生的事件越好
|
||||
if not jump_to then
|
||||
jump_to = evt
|
||||
else
|
||||
jump_to = jump_to.id < evt.id and jump_to or evt
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param event GameEvent
|
||||
function GameLogic:pushEvent(event)
|
||||
self.game_event_stack:push(event)
|
||||
|
||||
self.current_event_id = self.current_event_id + 1
|
||||
event.id = self.current_event_id
|
||||
self.all_game_events[event.id] = event
|
||||
self.event_recorder[event.event] = self.event_recorder[event.event] or {}
|
||||
table.insert(self.event_recorder[event.event], event)
|
||||
end
|
||||
|
||||
-- 一般来说从GameEvent:exec切回start再被start调用
|
||||
-- 作用是启动新事件 都是结构差不多的函数
|
||||
---@param event GameEvent
|
||||
---@return boolean, GameEvent?
|
||||
function GameLogic:resumeEvent(event, ...)
|
||||
local ret, evt
|
||||
|
||||
local co = event._co
|
||||
|
||||
while true do
|
||||
local err, yield_result, extra_yield_result = coroutine.resume(co, ...)
|
||||
|
||||
if err == false then
|
||||
-- handle error, then break
|
||||
if not string.find(yield_result, "__manuallyBreak") then
|
||||
fk.qCritical(yield_result .. "\n" .. debug.traceback(co))
|
||||
end
|
||||
ret = true
|
||||
break
|
||||
end
|
||||
|
||||
if yield_result == "__handleRequest" then
|
||||
-- yield to requestLoop
|
||||
coroutine.yield(yield_result, extra_yield_result)
|
||||
|
||||
elseif type(yield_result) == "table" and yield_result.class
|
||||
and yield_result:isInstanceOf(GameEvent) then
|
||||
|
||||
if extra_yield_result == "__newEvent" then
|
||||
ret, evt = false, yield_result
|
||||
break
|
||||
elseif extra_yield_result == "__breakEvent" then
|
||||
ret, evt = true, yield_result
|
||||
if event.event ~= GameEvent.ClearEvent then break end
|
||||
end
|
||||
|
||||
elseif yield_result == "__breakEvent" then
|
||||
ret = true
|
||||
if event.event ~= GameEvent.ClearEvent then break end
|
||||
|
||||
else
|
||||
ret = false
|
||||
event.exec_ret = yield_result
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return ret, evt
|
||||
end
|
||||
|
||||
---@return GameEvent
|
||||
function GameLogic:getCurrentCleaner()
|
||||
return self.cleaner_stack.t[self.cleaner_stack.p]
|
||||
end
|
||||
|
||||
-- 事件中的清理。
|
||||
-- cleaner单独开协程运行,exitFunc须转到上个事件的协程内执行
|
||||
-- 注意插入新event
|
||||
---@param event GameEvent
|
||||
function GameLogic:clearEvent(event)
|
||||
if event.event == GameEvent.ClearEvent then return end
|
||||
local ce = GameEvent(GameEvent.ClearEvent, event)
|
||||
ce.id = self.current_event_id
|
||||
local co = coroutine.create(ce.main_func)
|
||||
ce._co = co
|
||||
self.cleaner_stack:push(ce)
|
||||
end
|
||||
|
||||
---@return GameEvent
|
||||
function GameLogic:getCurrentEvent()
|
||||
return self.game_event_stack.t[self.game_event_stack.p]
|
||||
|
|
|
@ -254,9 +254,11 @@ function Room:run()
|
|||
end
|
||||
|
||||
local mode = Fk.game_modes[self.settings.gameMode]
|
||||
self.logic = (mode.logic and mode.logic() or GameLogic):new(self)
|
||||
if mode.rule then self.logic:addTriggerSkill(mode.rule) end
|
||||
self.logic:run()
|
||||
local logic = (mode.logic and mode.logic() or GameLogic):new(self)
|
||||
self.logic = logic
|
||||
if mode.rule then logic:addTriggerSkill(mode.rule) end
|
||||
-- GameEvent(GameEvent.Game):exec()
|
||||
logic:start()
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
|
|
@ -93,6 +93,12 @@ local function mainLoop()
|
|||
|
||||
if over then
|
||||
-- verbose('[#] %s is finished, removing ...', tostring(room))
|
||||
for _, e in ipairs(room.logic.game_event_stack.t) do
|
||||
coroutine.close(e._co)
|
||||
end
|
||||
for _, e in ipairs(room.logic.cleaner_stack.t) do
|
||||
coroutine.close(e._co)
|
||||
end
|
||||
room.logic = nil
|
||||
runningRooms[room.id] = nil
|
||||
else
|
||||
|
|
|
@ -321,6 +321,16 @@ function ServerPlayer:reconnect()
|
|||
local room = self.room
|
||||
self.serverplayer:setState(fk.Player_Online)
|
||||
|
||||
self:doNotify("Setup", json.encode{
|
||||
self.id,
|
||||
self._splayer:getScreenName(),
|
||||
self._splayer:getAvatar(),
|
||||
})
|
||||
self:doNotify("AddTotalGameTime", json.encode {
|
||||
self.id,
|
||||
self._splayer:getTotalGameTime(),
|
||||
})
|
||||
|
||||
self:doNotify("EnterLobby", "")
|
||||
self:doNotify("EnterRoom", json.encode{
|
||||
#room.players, room.timeout, room.settings,
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
static int GetMicroSecond(lua_State *L) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
long long microsecond = tv.tv_sec * 1000000 + tv.tv_usec;
|
||||
long long microsecond = (long long)tv.tv_sec * 1000000 + tv.tv_usec;
|
||||
lua_pushnumber(L, microsecond);
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user