KOB项目
用户表设计
注册流程
前端向后端发送请求
Controller层接收到请求并调用具体业务逻辑
Service层处理完请求返回给前端
前端接收到后端发来的数据后进行相关操作
登录流程
前端有两个函数,login
函数用来根据用户名和密码生成token,getinfo
函数用来根据token得到用户的id、用户名和头像。
user.js
login函数
getinfo函数
获取token
根据token获得相关信息
登出函数
直接纯前端实现。删除local storage里的jwt_token,然后把用户相关的变量清空即可。
bot相关API
一共有三个功能,创建、修改和删除。具体流程和登录注册差不多不再赘述。
需要注意的是,需要在编写一个辅助函数refresh_bots
并绑定后端apigetBotsInfo
。每次修改完bot都要执行该辅助函数。
还有一件事,每次调用后端API的时候,请求头都要加上jwtToken来验证。
Bot表设计
获取天梯排行榜
前端:点击页码,根据页码去后端获取当前页码相对应的user
信息,然后利用函数update_pages()
更新页码信息。
后端则和之前不同,直接返回一个JSON对象列表,列表里存放两类数据:用户信息「头像、名字、天梯分」以及用户总数。
另外,关于分页,Mybatis-Plus自带的插件IPage
会自动处理好。配合条件构造器queryWrapper
即可根据前端传来的信息筛选出相对应的页面以及该页面所携带的用户信息。
获取录像列表
Record表设计
前端逻辑和获取天梯排行榜一样,不再赘述。
后端也差不多。JSON对象列表里存放以下数据「两个用户的头像、两个用户的名字、对局信息(回放用)、对局结果、对局时间」以及对局总数。
然后配合分页插件和条件构造器筛选出相对应的页面,将相关信息返回给前端。
查看录像
纯前端实现。后端之前已经把对局信息喂给前端并存储到store里,所以其实相当于两个机器人在对战。
这时就可以复用PK界面的函数GameMap.js
以及其他辅助函数。
1 | const stringTo2D = map => { |
updateIsRecord
用于标记某场对局是不是录像,对于录像和真实的对战会有不同的逻辑updateGame
里面会更新游戏地图信息,真实对战游戏地图信息是随机生成的,显然这里与后端数据一致updateSteps
则是给出两条蛇的路径,根据这个信息可以生成蛇的移动
一些前端逻辑的调用
可以看到这里判断出该局游戏是「录像」,于是直接取store
里的数据来渲染游戏界面。
前端开始匹配
前端发送开始匹配请求,后端将用户塞入匹配池,维护一个以用户id为键的websocket池。
一旦用户进入PK界面,就和后端建立一个websocket连接。
若点击了「开始匹配」按钮,前端便向后端发送一个websocket请求:
后端handler接收到调用相对应的函数
把这个用户塞入匹配池。构造SpringCloud服务之间通信用的是RestTemplate
。
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高客户端的编写效率。
MatchingPool的Controller捕捉到该请求,调用addPlayer方法
多线程添加player
前端取消匹配
若点击了「取消匹配」按钮,前端向后端发送一个websocket请求:
后端收到请求,调用相关函数
匹配池的handler收到后端发来的请求
调用removePlayer
函数删除player
匹配池匹配成功
匹配池线程一直运转,根据相应的算法不断匹配用户
若发现两名用户符合匹配条件,那么就利用restTemplate
向后端发送匹配成功的结果并在匹配池中删除这两名玩家。
![image-20230406163830680](/Users/jinhui/Library/Application Support/typora-user-images/image-20230406163830680.png)
后端接收到匹配池发来的信息,调用接口生成游戏
ws服务器开始处理生成游戏的逻辑,首先是生成地图。注意这里的start()
是指开启一个新线程。
然后利用websocket的全双工通信向前端发送消息。
前端收到匹配成功的信息,跳转到游戏界面
蛇移动「键盘输入」
处理输入
前端监听函数检测到了键盘输入的事件,向后端发送websocket请求。
ws服务器先判断出是哪名用户,然后调用该用户的相关函数
设置游戏类里面玩家的nextStep
属性
后端逻辑判断
nextStep()
函数用于确认玩家是否都进行了操作。如果函数返回结果为false,那么说明游戏结束,直接返回对战结果。
judge()
函数则是确保双方的下一步操作都合法。如果出现非法操作,那么说明游戏结束。
如果双方的操作都是合法的,那么后端将把本次移动的信息返回给两位玩家。
前端收到ws服务器传来的消息,开始渲染蛇。
蛇移动「代码执行」
如何判断是否为代码执行?
前面的都一样。观察nextStep()
函数的这两行代码。
这里就是发送Bot的代码给botRunning微服务。
可以看到该函数会判断当前Player是手动输入还是代码执行。手动输入是默认值-1,代码执行的话botId
这个参数会另外设置过。
这些东西在开始匹配的时候就已经考虑到了,看下面的前端代码。
在botRunning微服务里
通过restTemplate
发送到SpringCloud微服务后,微服务的Controller捕捉到该请求,并调用相关函数,把bot加到bot池里。
在bot池里和手动输入的差不多,做一个生产者-消费者模型。把bot存到一个队列里。然后按照FIFO原则,挨个执行bot。
bot也得异步执行,得用start()
函数新开一个线程。
然后根据工具,运行代码,跑出结果,利用restTemplate
向后端发送结果。
在后端
接收到了微服务里发来的信息,并调用相关函数。
然后后端就会根据收到的结果调用setNextStep()
操作,之后的步骤就和人工操作一样了。
总结:这段过程差不多是整个项目中最绕的了。
- 前端带着bot相关信息发送给ws服务器 -> ws服务器发现是bot,将bot发送给微服务 -> 微服务用一个生产者-消费者模型不断执行bot结果 -> 微服务将结果发送给后端 -> 后端得到了下一步操作的结果,然后进行操作的合法性判断
发送对局结果
上述游戏不会一直进行下去,若至少有一名玩家停止操作或进行了非法操作,后端就会宣告游戏结束。
这里主要干了两件事。一是告诉前端游戏已结束,二是将本局的对局结果存储在数据库,以便能够回放对局。
前端收到结果后停止游戏的渲染。