简单的房间对象创建好后,后续功能先留白。
继续解决玩家匹配的问题,目前只是创建好了房间的对象,但是玩家怎么找到这个房间,这里就需要一个房间匹配类。
匹配建议还是走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;
}
至此,一个房间匹配器初步完成,