技术复盘:指令帧同步Unity RTS手游(3)Java Web服务器匹配房间(2)

文章描述了一个基于SpringBoot的房间匹配系统,通过Web服务器进行玩家房间匹配,定义了HallMatcher类来管理房间列表、用户房间绑定和超时清理。还包含了创建房间、查询房间状态、退出房间等功能的实现。
摘要由CSDN通过智能技术生成

简单的房间对象创建好后,后续功能先留白。

继续解决玩家匹配的问题,目前只是创建好了房间的对象,但是玩家怎么找到这个房间,这里就需要一个房间匹配类。

匹配建议还是走Web服务器这块,因为涉及到一些简单的短连接,javaweb这块是最简单的,当然最主要的还是要为后续的数据库交互做准备。

依旧是先定义一些基本属性,当然这是一个springboot 应用,必须加上相应注解

HallMatcher类

1.定义一些基本属性,注意Controller是单例,其方法不是线程安全的,因此老规矩,需要修改的变量全部写成静态变量

@RequestMapping("/match")
@Controller
public class HallMatcher {
    //所有房间列表
    public static ArrayList<Room> roomArrayList = new ArrayList<>();
    //用户ID~房间绑定方便查询
    public static Hashtable<String, Room> userRoomMap = new Hashtable<String, Room>();
    //用户ID~最后访问时间表:用于清理超时未访问厅的用户
    public static Hashtable<String, Long> userLastAccessTime = new Hashtable<String, Long>();
    //线程池:处理玩家访问
    private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
    //根据服务器的性能限定最多的房间数量,建议使用Lombook写在配置表中
    public static int maxRoomCount = 50;
    //对战服务器,后续再说
    private TCPServer tcpserver;
    //大厅信息
    public String hallMessage;

2.1大厅信息生成:每隔一段时间,遍历列表中的所有房间,找到所有未开始的非空非满房间

//1秒更新一次房间信息
@PostConstruct
public void HallMsgUpdater() {
    executorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            try {
                hallMessage = "";
                int valueRoom = 0;
                for (Room room : roomArrayList) {
                    if (room.ifEmpty || room.ifStartGame)
                        continue;
                    hallMessage += room.toString();
                    valueRoom++;
                }
            }catch (Exception e){
                System.out.println();
            }
        }
    }, 0, 1, TimeUnit.SECONDS);
}

2.2客户端通过web请求得到该大厅数据

  @PostMapping("/rooms")
    public ResponseEntity<ServerMsg> GetRoomsInformation(@RequestBody ClientMsg data) {
        // 创建一个ServerMsg对象并设置值
        ServerMsg serverMsg = new ServerMsg();
        serverMsg.setCmd("rooms");
        serverMsg.setData(hallMessage);
        return ResponseEntity.ok(serverMsg);
    }

ServerMsg对象类

非常简单的一个java bean,用Lombook注入但是记得重写toString方法

@Data
public class ServerMsg {
    public String cmd;
    public String data;
    public String room;
    @Override
    public String toString() {
        return "ServerMsg{" +
                "cmd='" + cmd + '\'' +
                ", data='" + data + '\'' +
                ", room='" + room + '\'' +
                '}';
    }
}

 ClientMsg对象类,同上

import lombok.Data;

@Data
public class ClientMsg {
    public String id;
    public String cmd;
    public String name;
    public int size;
    public int roomHashCode;

    public ClientMsg() {
    }
    
    @Override
    public String toString() {
        return "ClientMsg{" +
                "id='" + id + '\'' +
                ", cmd='" + cmd + '\'' +
                ", name='" + name + '\'' +
                ", size=" + size +
                ", roomHashCode=" + roomHashCode +
                '}';
    }
}

这里的信息将展示在这里

3.最后就是对客户端的响应请求做出回应(创建房间、退出房间、加入房间、等待开始等)

 @PostMapping("/gr")
    public ResponseEntity<ServerMsg> handleJsonRequest(@RequestBody ClientMsg data) {
        // 在这里处理JSON数据
        String userId = data.getId();
        String userCmd = data.getCmd();
        // 创建一个ServerMsg对象并设置值
        ServerMsg serverMsg = new ServerMsg();
        // 检查用户数据是否正确
        if (userId != null && userCmd != null) {
            switch (userCmd) {
                case "hall":
                    serverMsg.setCmd("hall");
                    serverMsg.setData(hallMessage);
                    break;
                case "create":
                    //超出服务器限制则返回已满
                    if(Room.roomCout>=maxRoomCount)
                    {
                        serverMsg.setCmd("error");
                        break;}
                    serverMsg = createRoom(data);
                    break;
                case "join":
                    //房间满员返回错误
                    serverMsg = joinRoom(data);
                    break;
                case "quit":
                    quitRoom(data);
                    break;
                case "match":
                    serverMsg = matchRoom(userId);
                    break;
                case "wait":
                    Room userRoom = userRoomMap.get(userId);
                    userLastAccessTime.replace(userId, System.currentTimeMillis());
                    serverMsg.setData(userRoom + "");
                    if (!userRoom.ifStartGame) {
                        serverMsg.setCmd("wait");

                    } else
                        serverMsg.setCmd("ok");
                    break;
            }
        } else
            serverMsg.setCmd("error");
        // 返回ServerMsg对象
        return ResponseEntity.ok(serverMsg);
    }

 相应的方法

//创建房间
    synchronized ServerMsg createRoom(ClientMsg clientMsg) {
        ServerMsg serverMsg = new ServerMsg();
        if (roomArrayList.size() >= maxRoomCount) {
            serverMsg.setCmd("full");
            return serverMsg;
        } else {
            Room room = new Room(clientMsg.getSize(), clientMsg.getName());
            roomArrayList.add(room);
            userRoomMap.put(clientMsg.getId(), room);
            room.InRoom(clientMsg.getId(), clientMsg.getName());
            serverMsg.setCmd("new");
            serverMsg.setData("" + room);
        }
        return serverMsg;
    }
//退出房间
   synchronized void quitRoom(ClientMsg clientMsg) {
        if (userRoomMap.containsKey(clientMsg.getId())) {
            Room room = userRoomMap.get(clientMsg.getId());
            room.OutRoom(clientMsg.getId());
            userRoomMap.remove(clientMsg.getId());
            userLastAccessTime.remove(clientMsg.getId());
        }

    }

//检查加入的房间是否已经开始
    @GetMapping("/checkIfStart")
    public ResponseEntity<ServerMsg> checkIfStart(@RequestParam String userId) {
        ServerMsg serverMsg = new ServerMsg();
        Room userRoom = userRoomMap.get(userId);
        if (!userRoom.ifStartGame) {
            serverMsg.setCmd("wait");
        } else
            serverMsg.setCmd("ok");
        return ResponseEntity.ok(serverMsg);
    }

//匹配房间(盲配,这里已经弃用)
    public ServerMsg matchRoom(String userId) {
        ServerMsg serverMsg = new ServerMsg();
        Room room = null;
        //检查用户是否已经拥有房间
        if (!userRoomMap.containsKey(userId)) {
            //未拥有房间的,申请房间
            room = getInRoom(userId);
            //申请成功
            if (room != null) {
                serverMsg.setRoom(room + "");
                serverMsg.setCmd("new");
                userRoomMap.put(userId, room);
                userLastAccessTime.put(userId, System.currentTimeMillis());
            }
            //申请失败
            else {
                serverMsg.setCmd("failed");
            }
        } else {
            //如果已经有房间,但是房间已经开始,则返回失败
            if (!userRoomMap.get(userId).ifStartGame) {
                serverMsg.setRoom(userRoomMap.get(userId) + "");
                serverMsg.setCmd("exist");
            } else {
                serverMsg.setCmd("busy");
            }

        }
        return serverMsg;
    }
//加入房间,需要加锁,防止多线程冲突
    synchronized public Room getInRoom(String userId) {
        boolean ifHasEmptyRoom = false;
        Room freeRoom = null;
        for (Room room : roomArrayList) {
            if (!room.ifFull) {
                freeRoom = room;
            }
        }
        freeRoom.InRoom(userId, "");
        return freeRoom;
    }

至此,一个房间匹配器初步完成,

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值