本节开始进入源码解析部分。


系列文章


启动流程

goworld程序是cmd目录下的文件编译出来,所以要看看goworld的启动方法,要从main.go看起,如下图。

goworld的程序很简单,因为它只是个引导启动的程序。如下图的代码中,首先是解析参数,如果输入的是goworld build xxx 就调用build方法,如果是goworld start xxx 就调用start方法,其他的方法同理,我们找一些有代表性的来看看。

build

输入go build xxx,会进入如下的代码,分别编译game、dispatcher和gate。

而构建的过程实际只是进入到各个程序的目录下,然后执行命令行go build xxxx,这个过程和手动进入到目录然后编译go程序一样。

status

输入go status xxxx可以查看服务的启动状态。goworld是个多进程程序,那么它怎样管理进程呢?相关代码如下图,实际上它和在linux下打印ps -anu 差不多,把操作系统上运行的进程的名字和pid都收集起来,然后通过筛选进程名字去确定是不是goworld的进程。这种做法有个缺点,使得一台机器只能开一个goworld集群。理应可以改进。

start

输入goworld start xxxx可以开启服务端,它会开启dispatcher、game和gate。start会做一些判断,如果当前有goworld的进程在运行,会提示“server is already running”。也由于goworld通过上述status的方法检测有没有进程开启,使得一台服务器只能开启一个goworld服务端集群。

以dispatchers为例,goworld会读取配置,确定要开多少个dispatcher,然后用命令行启动进程。整个过程和手动进入目录启动程序是一样的。

热更新reload

go的热更新一直是个大问题,goworld能够实现简单的热更新,输入goworld reload xxx既能热更服务器,我们看看怎样实现。相关代码如下。

实际上它是在做了一些判断后,把全部游戏服关掉,然后再开启。通过FreezeSignal参数,让游戏服在关闭时保存所有数据,也让它们在开启时读取所有数据。

那么goworld到底保存和读取哪些数据呢,可以看看下面的代码。goworld会遍历每个game进程,然后使用proc.Signal给它发送停服的信号,然后进入循环,等待完成关闭。

接着我们看到game服中处理信号的逻辑。

当收到FreezeSignal,也就是通过reload命令停服时,它会调用startFreeze保存状态,然后等待保存完成,再退出。

所以关键就在于startFreeze保存了哪些数据,当然保存之前它会通知dispater,避免一些进程不同步的问题。实际上它是把所有的entity给序列化并保存到文件中,代码如下。

再跟踪代码,entity.Freeze会获取游戏服里所有的entity的冻结信息,保存有如下的信息。


可见这种热更新方案聊胜于无,只存储了entity的数据。然而游戏中会有很多临时状态,甚至会有各种逻辑层的队列、开启协程,这些状态都不能够恢复。这样的热更新对逻辑编写有着很大的限制,未能解决热更问题。

推荐些资料

笔者所著《Unity3D网络游戏实战(第2版)》是一本专门介绍如何开发多人网络游戏的实战书籍,手把手教你搭建网络框架,制作大型项目。

「同步」也是网络游戏开发的核心课题。玩家的位置和旋转需要同步给其他玩家,然而网络条件差,会不同步和卡顿。笔者主讲的live《网络游戏同步算法》揭示做好同步的方法,欢迎收听。