FreeKill/docs/inner/04-coroutine.rst
2023-05-13 14:45:38 +08:00

77 lines
3.4 KiB
ReStructuredText
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

协程机制
===========
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吧。