FreeKill/doc/dev/protocol.md

6.7 KiB
Raw Blame History

FreeKill 的通信

dev > 通信


概述

FreeKill使用UTF-8文本进行通信。基本的通信格式为JSON数组

[requestId, packetType, command, jsonData]

其中:

  • requestId用来在request型通信使用用来确保收到的回复和发出的请求相对应。
  • packetType用来确定这条消息的类型以及发送的目的地。
  • command用来表示消息的类型。使用首字母大写的驼峰式命名因为下划线命名会造成额外的网络开销。
  • jsonData保存着这个消息的额外信息必须是一个JSON数组。数组中的具体内容详见源码及注释。

FreeKill通信有三大类型请求Request、回复Reply和通知Notification


从连接上到进入大厅

想要启动服务器,需要通过命令行终端:

$ ./FreeKill -s <port>

<port>是服务器运行的端口号如果不带任何参数则启动GUI界面在GUI界面里面只能加入服务器或者单机游戏。

服务器以TCP方式监听。在默认情况下比如单机启动服务器的端口号是9527。

每当任何一个客户端连接上了之后,游戏会先进行以下流程:

  1. 检查IP是否被封禁。 // TODO: 数据库
  2. 服务端将RSA公钥发给客户端然后检查客户端的延迟是否小于30秒。
  3. 在网络检测环节若客户端网速达标的话客户端应该会发回一个字符串。这个字符串保存着用户的用户名和RSA公钥加密后的密码服务端检查这个字符串是否合法。如果合法检查密码是否正确。
  4. 上述检查都通过后重连TODO:
  5. 不要重连的话,服务端便为新连接新建一个ServerPlayer对象,并将其添加到大厅中。

大厅和房间

大厅Lobby是一个比较特殊的房间。除了大厅之外所有的房间都被作为游戏房间对待。

对于普通房间而言,有这几个特点:

  1. 只要房间被添加玩家,那么那名玩家就自动从大厅移除。
  2. 当玩家离开房间时,玩家便会自动进入大厅。
  3. 当所有玩家都离开房间后房间被“销毁”其实是进入Server的空闲房间列表毕竟新建lua_State的开销十分大

大厅的特点:

  1. 只要有玩家进入,就刷新一次房间列表。
  2. 只要玩家变动就更新大厅内人数TODO:

因为上述特点都是通过信号槽实现的,通过阅读代码不易发现,故记录之。


对掉线的处理

因为每个连接都对应着一个new ClientSocketnew ServerPlayer,所以对于掉线的处理要慎重,处理不当会导致内存泄漏以及各种奇怪的错误。

一般来说掉线有以下几种情况:

  1. 刚刚登入,服务端还在检测时掉线。
  2. 在大厅里面掉线。
  3. 在未开始游戏的房间里面掉线。
  4. 在已开始游戏的房间里掉线。

首先对所有的这些情况都应该把ClientSocket释放掉。这部分代码写在server_socket.cpp里面。

对于2、3两种情况都算是在游戏开始之前的房间中掉线。这种情况下直接从房间中删除这个玩家并告诉其他玩家一声然后从服务器玩家列表中也删除那名玩家。但对于情况3因为从普通房间删除玩家的话那名玩家会自动进入大厅所以需要大厅再删除一次玩家。

对于情况4因为游戏已经开始所以不能直接删除玩家需要把玩家的状态设为“离线”并继续游戏。在游戏结束后若玩家仍未重连则按情况2、3处理。

Note: 这部分处理见于ServerPlayer类的析构函数。


断线重连TODO

根据用户名找到掉线的那位玩家,将玩家的状态设置为“在线”,并将房间的状态都发送给他即可。

但是为了UI不出错,依然需要对重连的玩家走一遍进大厅的流程。

重连的流程应为:

  1. 总之先新建ServerPlayer并加到大厅
  2. 在默认的处理流程中,此时会提醒玩家“已经有同名玩家加入”,然后断掉连接。
  3. 在这时可以改成如果这个已经在线的玩家是Offline状态那么就继续否则断开。
  4. pass之后走一遍流程把玩家加到大厅里面先。
  5. 既然是Offline那么掉线玩家肯定是在已经开始游戏的房间里面而且其socket处于deleted但没有置为nullptr的状态。
  6. 那么在pass之后不要创建旧的SPlayer对象而复用以前的。也不必走一次进lobby流程。
  7. 所以先手动发送Setup和EnterLobby消息。
  8. 发送Reconnect消息内含房间的所有信息。Client据此加入房间并设定好信息。

房间应该有哪些信息?

直接从UI着手

  1. 首先EnterRoom消息需要人数操作时长
  2. 既然需要人数了,那么就需要所有玩家
  3. 此外还需要让玩家知道牌堆、弃牌堆、轮数之类的。
  4. 玩家的信息就更多了武将、身份、血量、id...

信息要怎么发呢:

  • 一步一步的告诉重连中的玩家。
  • 全部汇总成字符串或者别的什么,然后可以压缩并发送。
  • 但以上两种都有问题许多信息保存在Lua中而Lua的运行是绝对不容其他线程打搅的。
  • 而且粗略一想这些东西都应该非常耗时而如今的线程只有Main线程和各大Room线程。有必要给Room加个子线程专门处理掉线这块的然后Room该怎么跑继续怎么跑。

或者换个思路:

  1. 首先EnterRoom消息需要人数操作时长
  2. 服务端将这个客户端的录像信息发给客户端客户端满速且不影响UI的播放录像。
  3. 在“播放录像”的过程中,客户端对于正在被收到的消息需进行特殊处理。
  4. 一个录像文件的体积会非常大。所以服务端所保存的客户端录像应该和真正的录像有差别才行。比如聊天、战报这种数据量大但又无关紧要的东西就不保存。
  5. 顺便这样也解决了多视角录像的问题,服务端给每个视角都录像就行了。

旁观TODO

因为房间不允许加入比玩家上限的玩家,可以考虑在房间里新建一个列表存储旁观中的玩家。但是这样或许会让某些处理(如掉线)变得复杂化。

也可以考虑旁观者在服务端中处于大厅中,自己的端中在旁观房间。但是这样的话无法在房间中发送聊天。

所以还是让旁观者在房间中吧。可以给ServerPlayer设置个属性保存正在旁观的房间的id。

旁观者的处理方式或许可以像观看录像那样过滤所有的request事件。这样就确确实实只能看着了。

而不过滤request的旁观就可以理解为操控其他玩家了。hhh