网络通信协议
一个完整的请求包含一个操作符 OP 以及至多两个数据段 data1 和 data2。操作符指定了当前请求需要执行的操作,数据段则在此基础上给出了一些额外信息。
下面给出本次作业中可能用到的所有请求及其处理行为。未提及到的数据段默认置空。如未说明 server/client 在收到某一请求后的行为,则表示不需要进行任何回复。
常规请求¶
END_GAME_OP¶
当所有玩家均完成游戏时,server 应向所有玩家发送 END_GAME_OP 请求,并将数据段 data1 设置为玩家排名列表,表示本轮游戏结束。
在 client 收到这一请求后,应向玩家展示排名列表,并自动退出房间。
END_TURN_OP¶
在某一次移动后,若有玩家获得胜利,server 应在处理完 MOVE_UP 请求后向对应 client 发送 END_TURN_OP 请求,使之确定获得胜利。
JOIN_ROOM_OP¶
当 client 尝试加入一个房间时,应向 server 发送 JOIN_ROOM_OP 请求,并将数据段 data1 和 data2 分别设置为房间号和用户名。
- 用户名为长度不超过 20 且仅包括大小写字母、数字和下划线
_的字符串。
Server 在收到请求后,首先应检查目标房间是否存在,若不存在则创建一个新的房间。随后检查目标房间状态(如是否在游戏中、是否已有同名的用户),检查失败则返回一个错误,否则返回一个 JOIN_ROOM_REPLY_OP 请求。
当一名玩家成功进入房间后,server 应向在当前房间内的所有其他玩家(即不包括刚进入房间的玩家)发送 JOIN_ROOM_OP 请求,并将数据段 data1 置为新加入玩家的用户名,表示有一名新的玩家进入了房间。
JOIN_ROOM_REPLY_OP¶
当玩家成功进入房间后,server 应向该玩家发送 JOIN_ROOM_REPLY_OP 请求,并将数据段 data1 和 data2 分别置为此前已进入房间的玩家列表及其状态。Client 在收到这一请求后可确定成功加入了房间。
- 玩家列表(数据段
data1)是一个字符串,包含所有玩家的用户名。不同玩家的用户名以单个空格分隔。- 下文中如再有发送玩家列表的情形均按照此格式进行。
- 状态列表(数据段
data2)是一个字符串,包含所有玩家的状态,其中0表示未就绪(Unready),1表示就绪(Ready)。状态列表应与玩家列表相对应。- 例如,若
data1段为"player1 player02"且data2段为"01",则表示当前玩家加入之前房间内已有两名玩家player1和player02,且player02已进入就绪状态。
- 例如,若
新加入房间的玩家应处于未就绪状态。
LEAVE_ROOM_OP¶
在 client 离开房间之前,应向 server 发送 LEAVE_ROOM_OP 请求,并将数据段 data1 和 data2 分别设置为房间号和当前用户名。
当有玩家离开房间时,server 应向在当前房间内的所有玩家发送 LEAVE_ROOM_OP 请求,并将数据段 data1 置为该玩家的用户名,表示有一名玩家离开了房间。
MOVE_OP¶
当玩家决定如何行棋后,client 应向 server 发送一个 MOVE_OP 请求,并将数据段 data1 设置为当前玩家的初始区域编号,数据段 data2 设置为棋子移动所经过的坐标列表。
- 以棋盘以 游戏玩法 中的配图所示摆放时,以棋盘正中心为原点,水平向右为 x 轴,倾斜向右上并与 x 轴呈 60 ^\circ 夹角为 y 轴建立斜坐标系,棋子的位置可由整数对 (x, y) 唯一确定。
- 坐标列表是一个字符串,其中包含 2n 个整数,其中第 2k - 1 和 2k 两个数给出路径上的第 k 个位置。
- 例如,若
data2段为"0 0 2 0 2 2",则这一步行棋将位于 (0, 0) 的棋子先跳跃至 (2, 0),再连跳至 (2, 2)。 - 又如,若
data2段为"0 0 -1 0",则这一步行棋将位于 (0, 0) 的棋子平移至 (-1, 0)。
- 例如,若
Server 在收到这一请求并检查合法后,应记录下玩家的这一步并向所有玩家转发同样的 MOVE_OP 请求。注意移动者自身不应收到这一请求。
非法行棋¶
若 server 收到的 MOVE_OP 请求中对棋子的移动违反了游戏规则,server 应返回一个错误,并等待这名玩家重新发送 MOVE_OP 请求。
需要注意的是非法行棋尽管不会直接判负,但回合的计时也不会因此重置。
超时判负¶
若在回合时限内 server 没有收到来自 client 的请求,表明该玩家行棋超时,按规则应直接判负并从棋盘上移除其所有棋子。
此时 server 应向所有玩家发送一个 MOVE_OP 请求,并将数据段 data1 设置为该玩家的初始区域编号,数据段 data2 设置为 "-1"。
PLAYER_READY_OP¶
当玩家准备就绪时,client 应向 server 发送 PLAYER_READY_OP 请求,并将数据段 data1 设置为用户名。
Server 在收到请求后,应向当前房间内的所有玩家转发这一请求。
Client 在收到请求后,应将相应玩家的状态更新为就绪(Ready)。
START_GAME_OP¶
当房间内玩家数量不低于 2 且所有玩家都处于就绪(Ready)状态时,server 应向所有玩家发送 START_GAME_OP 请求,表示游戏开始,并将数据段 data1 和 data2 分别设置为玩家列表和每个玩家被分到的初始区域。
- 棋盘上的区域从正上方开始顺时针依次标记为 A, B, C, D, E, F。
data2格式与data1相似,且玩家与其初始区域位置相对应。- 例如,若
data1段为"player1 player02"且data2段为"D A",玩家player1被分配至 D 区,玩家player02被分配至 A 区。
- 例如,若
- 游戏开始后,每回合内玩家按初始区域 A \sim F 的顺序行棋。
Client 在收到这一请求后,需要对数据段进行处理,并相应地更新图形化界面。
START_TURN_OP¶
当轮到某玩家移动棋子时,server 应向全体 client 发送 START_TURN_OP 请求,并将数据段 data1 和 data2 分别设置为「行棋方的初始区域编号」和「回合开始时刻的 Unix 时间戳」。
注意:已经获得胜利的玩家不应收到表明自己回合开始的请求。
错误处理¶
在任何时间,当 server 检查到 client 发来的请求产生错误时,应向 client 发送 ERROR_OP 请求,并将数据段 data1 设置为错误码,数据段 data2 设置为错误信息。Client 在收到这一请求后应视情况作出相应的处理。
- 为统一行为,在数据段
data1传递错误码时应使用枚举类ERRCODE底层对应的整型变量的数值而非变量名称,其调用方法为QString(static_cast<int>(ERRCODE::XXXXX))。
错误码的种类及其含义如下所示:
INVALID_JOIN:加入房间失败。可能由于用户名冲突引起。INVALID_MOVE:棋子移动非法。INVALID_REQ:无法解析该请求。NOT_IN_ROOM:当前用户不在房间内。OUTTURN_MOVE:当前不在自己行动的轮次。ROOM_IS_RUNNING:当前房间内的游戏正在进行。ROOM_NOT_RUNNING:当前房间内没有正在进行的游戏。OTHER_ERROR:前面没有提及的其他错误。
在 ERROR_OP 请求中,错误信息没有统一规定,同学们可以自行设计相关信息的格式或直接留空。