上期我们已经实现了对战功能的绝大部分代码, 这期我们来对项目中存在的一些问题做补充和优化
1. 对手掉线处理
如果对手在落子前就掉线了,根据我们现有的代码,当前玩家会一直卡在对手玩家落子的界面,所以我们需要在玩家掉线时给对手发送一个通知:
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
try {
User user = (User)session.getAttributes().get("user");
//通知对手获胜
noticeWinner(user);
WebSocketSession exitSession = onlineUserManager.getFromRoom(user.getUserId());
if(exitSession.equals(session)) {
onlineUserManager.exitGameRoom(user.getUserId());
System.out.println("连接异常断开," + user.getUsername() + "退出游戏房间");
}
}catch (NullPointerException e) {
//出现空指针异常说明用户未登录
//e.printStackTrace();
System.out.println("GameWebSocket.handleTransportError 用户未登录");
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
try {
User user = (User)session.getAttributes().get("user");
//通知对手获胜
noticeWinner(user);
WebSocketSession exitSession = onlineUserManager.getFromRoom(user.getUserId());
if(exitSession.equals(session)) {
onlineUserManager.exitGameRoom(user.getUserId());
System.out.println("连接正常断开," + user.getUsername() + "退出游戏房间");
}
}catch (NullPointerException e) {
//出现空指针异常说明用户未登录
//e.printStackTrace();
System.out.println("GameWebSocket.afterConnectionClosed 用户未登录");
}
}
private void noticeWinner(User user) throws IOException {
Room room = roomManager.getRoomByUserId(user.getUserId());
if(room == null) {
//房间已经被释放,游戏正常结束
return;
}
User winner = user == room.getUser1() ? room.getUser2() : room.getUser1();
WebSocketSession session = onlineUserManager.getFromRoom(winner.getUserId());
if(session == null) {
//说明对手也掉线了
return;
}
GameResponse response = new GameResponse();
response.setMessage("putChess");
response.setUserId(user.getUserId());
response.setWinnerId(winner.getUserId());
session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
//释放房间
roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());
}
2.更新用户数据
当用户参与比赛后,应该对用户的比赛信息进行更新当前我们代码中还没有相关的逻辑
@Mapper
public interface UserMapper {
@Select("select * from user where username = #{username}")
User getUserByName(String username);
@Insert("insert into user(username, password) values (#{username}, #{password})")
Integer insertUser(User user);
//获胜
@Update("update user set score = score + 30, total_count = total_count + 1, win_count = win_count + 1 where user_id = #{userId}")
Integer userWin(int userId);
//失败
@Update("update user set score = score - 30, total_count = total_count + 1 where user_id = #{userId}")
Integer userLose(int userId);
}
我们在判断用户胜利的两个地方加上相关代码:
落子方法的最后:
当我们做玩这些会发现一局游戏之后,用户的数据并没有即时得到更新,这是因为我们在游戏大厅页面使用的是session中的用户信息并不是数据库中最新的用户信息:
@RequestMapping("/getLoginUser")
public User getLoginUser(HttpServletRequest request) {
HttpSession session = request.getSession(false);//回话不存在时不允许创建会话
try{
User user = (User)session.getAttribute("user");
//返回数据库中最新的用户数据
return userMapper.getUserByName(user.getUsername());
}catch(NullPointerException e) {
//session为null返回空对象
return new User();
}
}
修改了这部分代码,我们的用户信息就能得到及时的更新
3. 游戏结束提示优化
在我们之前的 代码中,游戏结束时,前端是使用一个alert()弹窗进行提示,这个弹窗是一个模态弹窗,会阻塞页面,就会导致最后一个棋子无法绘制出,所以我们对提示方式进行一些修改:
//判断游戏是否结束
if(resp.winnerId != -1) {
let screenDiv = document.querySelector('#screen');
if (resp.winnerId == gameInfo.userId1) {
over = true;
// alert("你赢了!!!");
screenDiv.innerHTML = '你赢了!!!(点击返回大厅)';
}
if (resp.winnerId == gameInfo.userId2) {
over = true;
// alert("你输了");
screenDiv.innerHTML = '你输了(点击返回大厅)';
}
screenDiv.onclick = function() {
location.href = "/hall.html";
}
}
4. 页面跳转优化
如果用户点击浏览器的回退键,会导致用户回退到之前浏览器缓存的页面,这个页面的状态都是之前的,可能会导致一些问题,我们可以使用location.replace()来进行跳转,浏览器就不会缓存页面了。