FreeKill/docs/inner/04-coroutine.rst

77 lines
3.4 KiB
ReStructuredText
Raw Normal View History

2023-05-13 14:45:38 +08:00
协程机制
===========
FK的游戏逻辑是全Lua的这就有一个很大的问题Lua是单线程的那如果我要在
对局中做一些和游戏流程无关、但又不完全无关的操作比如预亮技能cpp可是对
技能一无所知的),是不是还要创另一个线程去执行?
由于所有游戏有关的数据全部在Lua所以去Cpp代码多弄个线程处理预亮、托管、旁观
等等操作并不可行。事实上这是通过协程机制实现的,请接着看。
协程简介
----------
我这里就简单的讲一下得了,关于协程的更多详情自己去查询吧。
协程其实就是一种特殊的调用函数的过程,他的特点就是可以随时通过
`` coroutine.yield `` 中止执行然后调用者也能通过resume来让中断的函数
继续执行。
通过协程不仅实现了这样的一个预亮处理机制等等,还实现了游戏事件机制,
利用协程可以实现事件的中断机制(结束本回合之类的)。
我们先来看预亮技能之类的行为是如何实现的吧。
处理客户端的request
--------------------
像托管、预亮等等行为称为“客户端对服务器发起的request”。
从通信机制一节中我们知道只有服务端才能发起request这里所谓客户端
request本质上只是对服务器的一种notify罢了。
在Room类Cpp类维护了一个请求队列用来保存这些来自客户端的请求。
而当Lua游戏逻辑有空休息时就把 **游戏逻辑协程** 暂时挂起yield然后再
切换到 **请求处理协程** 。关于种种请求的处理详情请查看request.lua。
这里来看看这两个协程是如何创建的,以及如何执行、如何切换。
首先是创建协程这个是在RoomLua类启动的时候创建的
.. code:: lua
-- room.lua: 70行左右代码过长改为伪代码
local main_co = coroutine.create(function() self:run() end)
local request_co = coroutine.create
(function(rest) self:requestLoop(rest) end)
while not game_finished do
resume(main_co)
-- 如有错误则处理错误
resume(request_co)
-- 如有错误则处理错误
end
从这个无限循环可以看出,在主协程挂起之后,就会去处理请求处理协程。那个协程也
挂起之后,又开始新一轮循环,再去进入主协程。
主协程何时挂起?
-----------------
执行delay函数或者正在waitForReply的时候。这两个函数会给服务端留下空闲的
时间,因此主协程会在此时挂起。
全局搜索一下 ``__handleRequest`` 即可知道主协程所有挂起的时机。
调查代码可知request处理协程每次处理一条request就立刻挂起而请求处理都是
一些耗时很短的操作。所以这样就可以在不影响游戏逻辑的前提下暂时中断,执行一
些其他的操作。而他们本质上不是并行执行的,所以也无需操作加锁之类的问题。
用协程实现事件机制
-------------------
Fk的事件机制也是用协程实现的。每当启动一个新事件后实际上就创建了一个新的协程
并执行,还有一些别的什么处理逻辑等等。因为是在协程中实现的,这就有几个好处:
1. 当发生error时被杀死的仅仅只是这个协程而不是所有Lua代码。
2. 能随时用Yield函数去中断本协程因此可以实现“终止事件”。
具体请自行查阅gameevent.lua吧。