项目实战--网页五子棋(匹配模块)(5)

上期我们实现了websocket后端的大部分代码,这期我们实现具体的匹配逻辑

1. 定义Mather类

我们新建一个Matcher类用来实现匹配逻辑

@Component
public class Matcher {
    //每个匹配队列代表不同的段位,这里约定每一千分为一个段位
    private ArrayList<Queue<User>> matchQueueList = new ArrayList<>();
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private OnlineUserManager onlineUserManager;
    public Matcher() {
        //暂定三个段位
        for(int i = 0; i < 3; i++) {
            matchQueueList.add(new LinkedList<>());
        }
    }
    public void add(User user) {
        int index = Math.min(user.getScore() / 3, 2);
        Queue<User> queue = matchQueueList.get(index);
        //对操作的队列加锁保证线程安全
        synchronized (queue) {
            queue.offer(user);
            queue.notify();
        }
        System.out.println("用户 " + user.getUsername() + " 加入了 " + index + "号 队列");
    }

    public void remove(User user) {
        int index = Math.min(user.getScore() / 3, 2);
        Queue<User> queue = matchQueueList.get(index);
        synchronized (queue) {
            queue.remove(user);
        }
        System.out.println("把用户 " + user.getUsername() + " 从 " + index + "号 队列中删除");
    }
}

2.修改websocket后端代码

    //接收到请求后执行
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User user = (User) session.getAttributes().get("user");
        MatchRequest request = objectMapper.readValue(message.getPayload(), MatchRequest.class);
        MatchResponse response = new MatchResponse();
        if(request.getMessage().equals("startMatch")) {
            //开始匹配,把用户加入匹配队列
            matcher.add(user);
            response.setOk(true);
            response.setMessage("startMatch");
        }else if(request.getMessage().equals("stopMatch")) {
            //取消匹配,从匹配队列中移除用户
            matcher.remove(user);
            response.setOk(true);
            response.setMessage("stopMatch");
        }else{
            response.setOk(false);
            response.setErrMsg("非法请求");
        }
        //返回响应
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }

由于存在在匹配中途断开连接的情况,所有我们还要在断开连接代码中增加退出队列的代码进行:

    //连接异常时执行
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //连接异常断开,玩家下线
        try {
            User user = (User)session.getAttributes().get("user");
            //防止重复登录时删除正常登录的在线信息
            if(onlineUser.getFromHall(user.getUserId()).equals(session)) {
                onlineUser.exitGameHall(user.getUserId());
                System.out.println("连接异常断开,用户:" + user.getUsername() + " 已下线");
                //用户可能还在匹配队列中
                matcher.remove(user);
            }
        }catch (NullPointerException e) {
            e.printStackTrace();
            MatchResponse response = new MatchResponse();
            response.setMessage("no_login");
            response.setOk(false);
            response.setErrMsg("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
    }
    //连接正常断开后执行
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //连接正常断开,玩家下线
        try {
            User user = (User)session.getAttributes().get("user");

            //防止重复登录时删除正常登录的在线信息
            if(onlineUser.getFromHall(user.getUserId()).equals(session)) {
                onlineUser.exitGameHall(user.getUserId());
                System.out.println("连接正常断开,用户:" + user.getUsername() + " 已下线");
                //用户可能还在匹配队列中
                matcher.remove(user);
            }
        }catch (NullPointerException e) {
            e.printStackTrace();
            MatchResponse response = new MatchResponse();
            response.setMessage("no_login");
            response.setOk(false);
            response.setErrMsg("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
    }

3. 实现匹配功能

3.1 创建线程扫描队列

我们为每个匹配队列创建一个线程,用来实现匹配功能,我们在构造方法中创建线程:

    public Matcher() {
        //暂定三个段位
        for(int i = 0; i < 3; i++) {
            matchQueueList.add(new LinkedList<>());
        }

        //每个队列创建一个线程扫描完成匹配功能
        for(Queue<User> q : matchQueueList) {
            Thread t = new Thread(()->{
                while(true) {
                    //调用handlerMatch()完成匹配功能
                    handlerMatch(q);
                }
            });
        }
    }
3.2 实现handlerMatch()方法进行匹配
public void handlerMatch(Queue<User> matchQueue) {
        try {
            //对操作的队列加锁保证线程安全
            synchronized (matchQueue) {
                //1.检测队列中是否有两个元素
                while(matchQueue.size() < 2) {
                    matchQueue.wait();
                }

                //2.从队列中取出两个玩家
                User user1 = matchQueue.poll();
                User user2 = matchQueue.poll();

                //3.获取到两个玩家的会话信息
                WebSocketSession session1 = onlineUserManager.getFromHall(user1.getId());
                WebSocketSession session2 = onlineUserManager.getFromHall(user2.getId());

                //4.todo 把两个玩家放到一个游戏房间中

                //5.给用户返回匹配成功的响应
                MatchResponse response = new MatchResponse();
                response.setOk(true);
                response.setMessage("success");
                String json = objectMapper.writeValueAsString(response);
                session1.sendMessage(new TextMessage(json));
                session2.sendMessage(new TextMessage(json));
            }
        }catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }

游戏房间功能我们下一期再实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ting-yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值