协程机制 =========== FK的游戏逻辑是全Lua的,这就有一个很大的问题:Lua是单线程的,那如果我要在 对局中做一些和游戏流程无关、但又不完全无关的操作(比如预亮技能,cpp可是对 技能一无所知的),是不是还要创另一个线程去执行? 由于所有游戏有关的数据全部在Lua,所以去Cpp代码多弄个线程处理预亮、托管、旁观 等等操作并不可行。事实上这是通过协程机制实现的,请接着看。 协程简介 ---------- 我这里就简单的讲一下得了,关于协程的更多详情自己去查询吧。 协程其实就是一种特殊的调用函数的过程,他的特点就是可以随时通过 `` coroutine.yield `` 中止执行,然后调用者也能通过resume来让中断的函数 继续执行。 通过协程不仅实现了这样的一个预亮处理机制等等,还实现了游戏事件机制, 利用协程可以实现事件的中断机制(结束本回合之类的)。 我们先来看预亮技能之类的行为是如何实现的吧。 处理客户端的request -------------------- 像托管、预亮等等行为,称为“客户端对服务器发起的request”。 从通信机制一节中我们知道只有服务端才能发起request,这里所谓客户端 request本质上只是对服务器的一种notify罢了。 在Room类(Cpp类)中,维护了一个请求队列,用来保存这些来自客户端的请求。 而当Lua游戏逻辑有空休息时,就把 **游戏逻辑协程** 暂时挂起(yield),然后再 切换到 **请求处理协程** 。关于种种请求的处理详情请查看request.lua。 这里来看看这两个协程是如何创建的,以及如何执行、如何切换。 首先是创建协程,这个是在Room(Lua类)启动的时候创建的: .. 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吧。