Pārlūkot izejas kodu

Merge branch 'dev' of sunkean/raex_mmo into master

sunkean 2 gadi atpakaļ
vecāks
revīzija
9e33c92dc5

+ 2 - 0
src/main/java/com/izouma/meta/config/Constants.java

@@ -45,6 +45,8 @@ public interface Constants {
 
     String REDIS_PREFIX = "meta:";
 
+    String REDIS_USER_KEY = "redis_user_key:";
+
     String META_ROBOT_NICK_NAME = "metaRobot";
 
     String META_ROBOT_USER_ID = "7970191";

+ 18 - 0
src/main/java/com/izouma/meta/domain/MetaMMOLoginInfo.java

@@ -110,6 +110,18 @@ public class MetaMMOLoginInfo extends BaseEntity {
     @ExcelProperty("变身id")
     private Long turnedId;
 
+    @ApiModelProperty("称号id")
+    @ExcelProperty("称号id")
+    private Long titleId;
+
+    @ApiModelProperty("座位id")
+    @ExcelProperty("座位id")
+    private Long seatId;
+
+    @ApiModelProperty("跑步id")
+    @ExcelProperty("跑步id")
+    private Long runId;
+
     /**
      * 根据玩家历史登陆信息初始化本次登陆信息
      *
@@ -136,6 +148,9 @@ public class MetaMMOLoginInfo extends BaseEntity {
             newMetaMMOLoginInfo.setEmoji(0);
             newMetaMMOLoginInfo.setVehicleId(-1L);
             newMetaMMOLoginInfo.setTurnedId(-1L);
+            newMetaMMOLoginInfo.setTitleId(-1L);
+            newMetaMMOLoginInfo.setSeatId(-1L);
+            newMetaMMOLoginInfo.setRunId(-1L);
             newMetaMMOLoginInfo.setMoveType(MoveType.WALK);
             return newMetaMMOLoginInfo;
         }
@@ -158,6 +173,9 @@ public class MetaMMOLoginInfo extends BaseEntity {
         newMetaMMOLoginInfo.setMoveType(Objects.isNull(metaMMOLoginInfo.getMoveType()) ? MoveType.WALK : metaMMOLoginInfo.getMoveType());
         newMetaMMOLoginInfo.setVehicleId(metaMMOLoginInfo.getVehicleId());
         newMetaMMOLoginInfo.setTurnedId(metaMMOLoginInfo.getTurnedId());
+        newMetaMMOLoginInfo.setTitleId(metaMMOLoginInfo.getTitleId());
+        newMetaMMOLoginInfo.setSeatId(metaMMOLoginInfo.getSeatId());
+        newMetaMMOLoginInfo.setRunId(metaMMOLoginInfo.getRunId());
         return newMetaMMOLoginInfo;
     }
 

+ 57 - 0
src/main/java/com/izouma/meta/utils/HttpUtils.java

@@ -0,0 +1,57 @@
+package com.izouma.meta.utils;
+
+import com.izouma.meta.config.GeneralProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+@RequiredArgsConstructor
+public class HttpUtils {
+
+    private final PoolingHttpClientConnectionManager connManager;
+    private final GeneralProperties                  generalProperties;
+    private final HttpClient                         client;
+
+    @Autowired
+    public HttpUtils(GeneralProperties generalProperties) {
+        this.generalProperties = generalProperties;
+        this.connManager = new PoolingHttpClientConnectionManager();
+        this.connManager.setMaxTotal(100); // 最大连接数
+        this.connManager.setDefaultMaxPerRoute(20); // 每个路由最大连接数
+        this.client = HttpClientBuilder.create().setConnectionManager(connManager).build();
+    }
+
+    public String doGetNoParams(String url) {
+        String requestUrl = generalProperties.getUrlPrefix().concat(url);
+        HttpGet get = new HttpGet(requestUrl);
+        try (CloseableHttpResponse res = (CloseableHttpResponse) client.execute(get)) {
+            log.info("execute get request url : {} ", requestUrl);
+            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                HttpEntity entity = res.getEntity();
+                if (entity != null) {
+                    return EntityUtils.toString(entity);
+                }
+                return null;
+            } else {
+                String errMsg = String.format("request [%s] returned error", requestUrl);
+                log.error(errMsg);
+                throw new RuntimeException(errMsg);
+            }
+        } catch (Exception e) {
+            String errMsg = String.format("client failed to execute request for url: %s", requestUrl);
+            log.error(errMsg, e);
+            throw new RuntimeException(errMsg, e);
+        }
+    }
+}

+ 5 - 1
src/main/java/com/izouma/meta/web/MMOWebSocketController.java

@@ -20,7 +20,11 @@ import java.util.stream.Collectors;
 @RestController
 @RequestMapping("/mmo/websocket")
 @AllArgsConstructor
-@CrossOrigin(origins = {"http://localhost:8081", "https://raex.vip/", "https://test.raex.vip/", "https://www.raex.vip/"})
+@CrossOrigin(origins = {"http://localhost:8081",
+        "https://raex.vip/",
+        "https://test.raex.vip/",
+        "https://www.raex.vip/",
+        "https://pre.raex.vip/"})
 public class MMOWebSocketController {
 
     private MMOWebSocket            mmoWebSocket;

+ 4 - 4
src/main/java/com/izouma/meta/websocket/LivePublicScreenChatWebsocket.java

@@ -168,13 +168,13 @@ public class LivePublicScreenChatWebsocket {
         // 心跳检测
         if (Constants.HEART_RECEIVE.equals(type)) {
             log.info(String.format("sessionId:[%S] userId:[%S] 连接正常", session.getId(), userId));
-            websocketCommon.sendMessageTo(clients, Constants.HEART_RETURN, PREFIX.concat(userId));
+            websocketCommon.sendMessageToSingle(clients, Constants.HEART_RETURN, PREFIX.concat(userId));
             return;
         }
         // 查询需要撤回的消息
         if (Constants.MESSAGE_RECALL.equals(type)) {
             List<PublicScreenChat> publicScreenChats = publicScreenChatRepo.findAllByTypeAndRecallAndDel(2, 2, false);
-            websocketCommon.sendMessageTo(clients, JSONObject.toJSONString(publicScreenChats), PREFIX.concat(userId));
+            websocketCommon.sendMessageToSingle(clients, JSONObject.toJSONString(publicScreenChats), PREFIX.concat(userId));
             return;
         }
         // 消息撤回确定
@@ -194,7 +194,7 @@ public class LivePublicScreenChatWebsocket {
             ids.forEach(id -> {
                 publicScreenChatRepo.recallConfirm(id);
             });
-            websocketCommon.sendMessageTo(clients, "消息已被撤回", PREFIX.concat(userId));
+            websocketCommon.sendMessageToSingle(clients, "消息已被撤回", PREFIX.concat(userId));
             return;
         }
         // 正常发送消息
@@ -264,7 +264,7 @@ public class LivePublicScreenChatWebsocket {
     private void exceptionHandle(String userId, PublicScreenChatExceptionMsg msg) {
         log.error(JSON.toJSONString(msg));
         // 推送消息给该玩家
-        websocketCommon.sendMessageTo(clients, JSON.toJSONString(msg), PREFIX.concat(userId));
+        websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(msg), PREFIX.concat(userId));
     }
 }
 

+ 4 - 4
src/main/java/com/izouma/meta/websocket/LiveWebSocket.java

@@ -74,7 +74,7 @@ public class LiveWebSocket {
             MMOSingleMessage mmoSingleMessage = new MMOSingleMessage();
             mmoSingleMessage.setMessageType(6);
             mmoSingleMessage.setMessage("您已在其他地方登录,已为您关闭上次登陆信息");
-            websocketCommon.sendMessageTo(clients, JSON.toJSONString(mmoSingleMessage), REDIS_PREFIX.concat(userId));
+            websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(mmoSingleMessage), REDIS_PREFIX.concat(userId));
             try {
                 log.info("关闭上次登陆的session连接");
                 clients.get(REDIS_PREFIX.concat(userId)).close();
@@ -92,7 +92,7 @@ public class LiveWebSocket {
         mmoMessage.setMessageType(5);
         mmoMessage.setMessage(metaVisitorMMOLoginInfo);
         log.info(String.format("通知玩家[%S],本次登陆信息[%S]", userId, JSON.toJSONString(mmoMessage)));
-        websocketCommon.sendMessageTo(clients, JSON.toJSONString(mmoMessage), REDIS_PREFIX.concat(userId));
+        websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(mmoMessage), REDIS_PREFIX.concat(userId));
     }
 
     @OnError
@@ -144,7 +144,7 @@ public class LiveWebSocket {
         }
         if (Constants.HEART_RECEIVE.equals(message)) {
             log.info(String.format("sessionId:[%S] userId:[%S] 连接正常", session.getId(), userId));
-            websocketCommon.sendMessageTo(clients, Constants.HEART_RETURN, REDIS_PREFIX.concat(userId));
+            websocketCommon.sendMessageToSingle(clients, Constants.HEART_RETURN, REDIS_PREFIX.concat(userId));
             return;
         }
         JSONObject jsonObject = JSON.parseObject(message);
@@ -270,7 +270,7 @@ public class LiveWebSocket {
         MetaVisitorMMOMessage mmoMessage = new MetaVisitorMMOMessage();
         mmoMessage.setMessageType(messageType);
         mmoMessage.setMap(metaMMOLoginInfos);
-        websocketCommon.sendMessageTo(clients, JSON.toJSONString(mmoMessage), userId);
+        websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(mmoMessage), userId);
     }
 
     /**

+ 109 - 317
src/main/java/com/izouma/meta/websocket/MMOWebSocket.java

@@ -4,36 +4,35 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.izouma.meta.config.Constants;
 import com.izouma.meta.domain.MetaMMOLoginInfo;
-import com.izouma.meta.domain.MetaObjectMove;
-import com.izouma.meta.domain.MetaObjectMoveCoordinate;
 import com.izouma.meta.dto.MMOMessage;
 import com.izouma.meta.dto.MMOSingleMessage;
 import com.izouma.meta.dto.MetaRestResult;
 import com.izouma.meta.dto.MetaServiceResult;
-import com.izouma.meta.enums.MoveType;
 import com.izouma.meta.repo.MetaMMOLoginInfoRepo;
-import com.izouma.meta.repo.MetaObjectMoveRepo;
 import com.izouma.meta.utils.ApplicationContextUtil;
 import com.izouma.meta.utils.ObjUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.core.RedisOperations;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.SessionCallback;
 import org.springframework.stereotype.Service;
 
 import javax.websocket.*;
 import javax.websocket.server.PathParam;
 import javax.websocket.server.ServerEndpoint;
 import java.time.LocalDateTime;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
 
 @Service
-@ServerEndpoint(value = "/websocket/mmo/{nickName}/{userId}")
+@ServerEndpoint(value = "/websocket/mmo/{nickname}/{userId}")
 @Slf4j
 public class MMOWebSocket {
 
@@ -45,7 +44,7 @@ public class MMOWebSocket {
     private RedisTemplate        redisTemplate;
     private MetaMMOLoginInfoRepo metaMMOLoginInfoRepo;
     private WebsocketCommon      websocketCommon;
-    private MetaObjectMoveRepo   metaObjectMoveRepo;
+    private WebsocketHandle      websocketHandle;
 
     private void init() {
         if (Objects.isNull(redisTemplate)) {
@@ -57,47 +56,78 @@ public class MMOWebSocket {
         if (Objects.isNull(websocketCommon)) {
             websocketCommon = (WebsocketCommon) ApplicationContextUtil.getBean("websocketCommon");
         }
-        if (Objects.isNull(metaObjectMoveRepo)) {
-            metaObjectMoveRepo = (MetaObjectMoveRepo) ApplicationContextUtil.getBean("metaObjectMoveRepo");
+        if (Objects.isNull(websocketHandle)) {
+            websocketHandle = (WebsocketHandle) ApplicationContextUtil.getBean("websocketHandle");
         }
     }
 
     @OnOpen
-    public void onOpen(@PathParam("nickName") String nickName, @PathParam("userId") String userId, Session session) {
+    public void onOpen(@PathParam("nickname") String nickname,
+                       @PathParam("userId") String userId, Session session) {
         init();
-        // 判断当前玩家是否在其他地方登陆
-        if (clients.containsKey(Constants.REDIS_PREFIX.concat(userId))) {
-            log.info(String.format("当前玩家: %s 已经在别处登陆, sessionId为: %s", userId, session.getId()));
-            // 关闭连接
-            MMOSingleMessage mmoSingleMessage = new MMOSingleMessage();
-            mmoSingleMessage.setMessageType(6);
-            mmoSingleMessage.setMessage("您已在其他地方登录,已为您关闭上次登陆信息");
-            websocketCommon.sendMessageTo(clients, JSON.toJSONString(mmoSingleMessage), Constants.REDIS_PREFIX.concat(userId));
-            try {
-                log.info("关闭上次登陆的session连接");
-                clients.get(Constants.REDIS_PREFIX.concat(userId)).close();
-            } catch (Exception e) {
-                log.error("session close throw exception:", e);
-            }
-        }
-        log.info("现在来连接的sessionId:" + session.getId() + "玩家id:" + userId + "玩家昵称" + nickName);
+        handleOtherLogin(userId, session);
+        log.info("现在来连接的sessionId:" + session.getId() + "玩家id:" + userId + "玩家昵称" + nickname);
         clients.put(Constants.REDIS_PREFIX.concat(userId), session);
-        // 获取上次登录的信息
+        MetaMMOLoginInfo metaMMOLoginInfo = saveLoginInfo(nickname, userId, session.getId());
+        sendLoginMessage(userId, metaMMOLoginInfo);
+    }
+
+    /**
+     * 重复登录处理
+     *
+     * @param userId  用户id
+     * @param session session信息
+     */
+    private void handleOtherLogin(String userId, Session session) {
+        if (!clients.containsKey(Constants.REDIS_PREFIX.concat(userId))) {
+            return;
+        }
+        log.info(String.format("当前玩家: %s 已经在别处登陆, sessionId为: %s", userId, session.getId()));
+        MMOSingleMessage mmoSingleMessage = new MMOSingleMessage();
+        mmoSingleMessage.setMessageType(6);
+        mmoSingleMessage.setMessage("您已在其他地方登录,已为您关闭上次登陆信息");
+        websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(mmoSingleMessage), Constants.REDIS_PREFIX.concat(userId));
+        try {
+            log.info("关闭上次登陆的session连接");
+            clients.get(Constants.REDIS_PREFIX.concat(userId)).close();
+        } catch (Exception e) {
+            log.error("session close throw exception:", e);
+        }
+    }
+
+    /**
+     * 保存登录信息
+     *
+     * @param nickname  昵称
+     * @param userId    用户id
+     * @param sessionId session id
+     * @return 登录信息
+     */
+    private MetaMMOLoginInfo saveLoginInfo(String nickname, String userId, String sessionId) {
         MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findLastByUserId(Long.parseLong(userId));
-        // 玩家登陆信息入库
         MetaMMOLoginInfo metaMMOLoginInfo = MetaMMOLoginInfo.initMetaMMOLoginInfo(dbMetaMMOLoginInfo);
-        metaMMOLoginInfo.setNickname(nickName);
+        metaMMOLoginInfo.setNickname(nickname);
         metaMMOLoginInfo.setUserId(Long.parseLong(userId));
         metaMMOLoginInfo.setOnLineTime(LocalDateTime.now());
-        metaMMOLoginInfo.setSessionId(session.getId());
-        MetaMMOLoginInfo save = metaMMOLoginInfoRepo.save(metaMMOLoginInfo);
+        metaMMOLoginInfo.setSessionId(sessionId);
+        return metaMMOLoginInfoRepo.save(metaMMOLoginInfo);
+    }
+
+    /**
+     * 发送登录通知给用户
+     *
+     * @param userId    用户id
+     * @param loginInfo 登录信息
+     */
+    private void sendLoginMessage(String userId, MetaMMOLoginInfo loginInfo) {
         MMOMessage mmoMessage = new MMOMessage();
         mmoMessage.setMessageType(5);
-        mmoMessage.setMessage(save);
+        mmoMessage.setMessage(loginInfo);
         log.info(String.format("通知玩家: %s, 本次登陆信息: %s", userId, JSON.toJSONString(mmoMessage)));
-        websocketCommon.sendMessageTo(clients, JSON.toJSONString(mmoMessage), Constants.REDIS_PREFIX.concat(userId));
+        websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(mmoMessage), Constants.REDIS_PREFIX.concat(userId));
     }
 
+
     @OnError
     public void onError(Session session, Throwable error) {
         // 异常处理
@@ -107,22 +137,23 @@ public class MMOWebSocket {
     @OnClose
     public void onClose(@PathParam("userId") String userId) {
         init();
-        // 查询地图中玩家信息
-        MetaMMOLoginInfo metaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(Constants.REDIS_PREFIX.concat(userId));
-        if (Objects.isNull(metaMMOLoginInfo)) {
-            // 如果缓存中玩家信息为空,根据userId和sessionId查询数据库
-            MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findByUserIdAndSessionIdAndDel(Long.parseLong(userId), clients.get(Constants.REDIS_PREFIX.concat(userId)).getId(), false);
-            dbMetaMMOLoginInfo.setOffLineTime(LocalDateTime.now());
-            // 更新离线时间
-            metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
-            clients.remove(Constants.REDIS_PREFIX.concat(userId));
+        String redisKey = Constants.REDIS_PREFIX.concat(userId);
+        MetaMMOLoginInfo metaMMOLoginInfo = Optional.ofNullable(redisTemplate.opsForValue().get(redisKey))
+                                                    .map(MetaMMOLoginInfo.class::cast)
+                                                    .orElseGet(() -> {
+                                                        MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findByUserIdAndSessionIdAndDel(Long.parseLong(userId), clients.get(redisKey).getId(), false);
+                                                        dbMetaMMOLoginInfo.setOffLineTime(LocalDateTime.now());
+                                                        metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
+                                                        clients.remove(redisKey);
+                                                        return null;
+                                                    });
+        if (metaMMOLoginInfo == null) {
             return;
         }
-        String key = String.valueOf(metaMMOLoginInfo.getCityId()).concat(":").concat(String.valueOf(metaMMOLoginInfo.getRegionId()));
-        List<String> otherUserIds = remove(key, userId);
-        if (CollectionUtils.isNotEmpty(otherUserIds)) {
-            // 分发下线消息给区域内其他玩家
-            buildMessageForSendingToAllOther(otherUserIds, 3, metaMMOLoginInfo);
+        String key = String.format("%d:%d", metaMMOLoginInfo.getCityId(), metaMMOLoginInfo.getRegionId());
+        List<String> userIds = websocketHandle.allUserIdExcept(key, userId);
+        if (CollectionUtils.isNotEmpty(userIds)) {
+            websocketCommon.sendMessageToMultiple(clients, JSON.toJSONString(new MMOMessage(3, metaMMOLoginInfo, null)), userIds);
         }
         MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findById(metaMMOLoginInfo.getId()).orElse(null);
         if (Objects.isNull(dbMetaMMOLoginInfo)) {
@@ -132,15 +163,12 @@ public class MMOWebSocket {
         ObjUtils.merge(dbMetaMMOLoginInfo, metaMMOLoginInfo);
         dbMetaMMOLoginInfo.setOffLineTime(LocalDateTime.now());
         metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
-        clients.remove(Constants.REDIS_PREFIX.concat(userId));
-        // 删除redis中自己的信息
-        redisTemplate.delete(Constants.REDIS_PREFIX.concat(userId));
-        // 移除地图中自己的信息
-        redisTemplate.opsForList().remove(Constants.REDIS_PREFIX.concat(key), 0, Constants.REDIS_PREFIX.concat(userId));
+        clients.remove(redisKey);
+        closeExecute(key, userId);
     }
 
     @OnMessage
-    public void onMessage(@PathParam("nickName") String nickName, @PathParam("userId") String userId, String message, Session session) {
+    public void onMessage(@PathParam("userId") String userId, String message, Session session) {
         init();
         if (StringUtils.isBlank(message)) {
             log.error("Illegal parameter : message can not be null");
@@ -148,7 +176,7 @@ public class MMOWebSocket {
         }
         if (Constants.HEART_RECEIVE.equals(message)) {
             log.info(String.format("sessionId: %s , userId: %s 连接正常", session.getId(), userId));
-            websocketCommon.sendMessageTo(clients, Constants.HEART_RETURN, Constants.REDIS_PREFIX.concat(userId));
+            websocketCommon.sendMessageToSingle(clients, Constants.HEART_RETURN, Constants.REDIS_PREFIX.concat(userId));
             return;
         }
         JSONObject jsonObject = JSON.parseObject(message);
@@ -157,283 +185,43 @@ public class MMOWebSocket {
             log.error(result.getMessage());
             return;
         }
+        log.info(String.format("来自客户端消息:%s 客户端的id是:%s", message, Objects.isNull(session) ? null : session.getId()));
         int type = Integer.parseInt(jsonObject.getString("type"));
-        // type = 9 广播物体坐标信息
         if (type == 9) {
-            log.info(String.format("来自客户端消息: %s", message));
-            Long objectId;
-            try {
-                objectId = Long.parseLong(jsonObject.getString("objectId"));
-            } catch (Exception e) {
-                log.error(String.format("objectId parse to long throw exception: %s", e.getMessage()));
-                return;
-            }
-            MetaObjectMove metaObjectMove = metaObjectMoveRepo.findByObjectIdAndDel(objectId, false);
-            if (Objects.isNull(metaObjectMove)) {
-                log.error(String.format("物体: %s 不存在", objectId));
-                return;
-            }
-            if (!metaObjectMove.isRun()) {
-                log.error(String.format("物体: %s 已经停运", objectId));
-                return;
-            }
-            String cityId = String.valueOf(metaObjectMove.getCityId());
-            String regionId = String.valueOf(metaObjectMove.getRegionId());
-            String key = cityId.concat(":").concat(regionId);
-            List<String> otherUserIds = remove(key, userId);
-            if (CollectionUtils.isEmpty(otherUserIds)) {
-                return;
-            }
-            MetaObjectMoveCoordinate metaObjectMoveCoordinate;
-            try {
-                String str = websocketCommon.getRequest("/metaObjectMove/".concat(String.valueOf(metaObjectMove.getObjectId())).concat("/queryCoordinate"));
-                metaObjectMoveCoordinate = JSONObject.parseObject(str, MetaObjectMoveCoordinate.class);
-            } catch (Exception e) {
-                String errMsg = String.format("MetaObjectMoveCoordinate JSON parse throw exception: %s", e);
-                log.error(errMsg);
-                return;
-            }
-            if (Objects.isNull(metaObjectMoveCoordinate)) {
-                log.error(String.format("物体: %s 坐标信息不存在", metaObjectMove.getObjectId()));
-                return;
-            }
-            buildMessageForSendingToAllOther(otherUserIds, 9, metaObjectMoveCoordinate);
+            log.info("当前操作类型为: 9 -> 广播物体坐标信息");
+            websocketHandle.handleTypeNine(clients, jsonObject, userId);
             return;
         }
-        log.info(String.format("来自客户端消息:%s 客户端的id是:%s", message, session.getId()));
         String cityId = jsonObject.getString("cityId");
         String regionId = jsonObject.getString("regionId");
         String key = cityId.concat(":").concat(regionId);
-        List<String> otherUserIds = remove(key, userId);
+        List<String> otherUserIds = websocketHandle.allUserIdExcept(key, userId);
         MetaMMOLoginInfo metaMMOLoginInfo;
         List<MetaMMOLoginInfo> metaMMOLoginInfos = redisTemplate.opsForValue().multiGet(otherUserIds);
         switch (type) {
             case 1:
                 log.info(String.format("当前操作类型为: %s -> 玩家进入地图", type));
-                metaMMOLoginInfo = buildMetaMMOLoginInfo(jsonObject, Long.parseLong(cityId), Long.parseLong(regionId), nickName, userId);
-                if (CollectionUtils.isNotEmpty(otherUserIds)) {
-                    if (CollectionUtils.isNotEmpty(metaMMOLoginInfos)) {
-                        // 分发区域内其他玩家信息给自己
-                        buildMessageForSendingToUser(Constants.REDIS_PREFIX.concat(userId), 1, metaMMOLoginInfos);
-                        // 分发自己信息给区域内其他玩家
-                        buildMessageForSendingToAllOther(otherUserIds, 4, metaMMOLoginInfo);
-                    }
-                }
-                // 将自己信息存到redis中
-                redisTemplate.opsForValue().set(Constants.REDIS_PREFIX.concat(userId), metaMMOLoginInfo);
-                redisTemplate.opsForList().leftPush(Constants.REDIS_PREFIX.concat(key), Constants.REDIS_PREFIX.concat(userId));
+                metaMMOLoginInfo = websocketHandle.buildMetaMMOLoginInfo(jsonObject, Long.parseLong(cityId), Long.parseLong(regionId), userId);
+                websocketHandle.handleTypeOne(clients, otherUserIds, metaMMOLoginInfos, metaMMOLoginInfo, userId, key);
                 break;
             case 2:
                 log.info(String.format("当前操作类型为: %s -> 玩家切换区域", type));
-                metaMMOLoginInfo = buildMetaMMOLoginInfo(jsonObject, Long.parseLong(cityId), Long.parseLong(regionId), nickName, userId);
-                if (CollectionUtils.isNotEmpty(otherUserIds)) {
-                    // 分发自己信息给区域内其他玩家
-                    buildMessageForSendingToAllOther(otherUserIds, 4, metaMMOLoginInfo);
-                    // 分发区域内其他玩家信息给自己
-                    buildMessageForSendingToUser(Constants.REDIS_PREFIX.concat(userId), 1, metaMMOLoginInfos);
-                }
-                MetaMMOLoginInfo oldMetaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(Constants.REDIS_PREFIX.concat(userId));
-                if (Objects.isNull(oldMetaMMOLoginInfo)) {
-                    log.error("缺失玩家上次区域的地图缓存信息");
-                    break;
-                }
-                String oldKey = String.valueOf(oldMetaMMOLoginInfo.getCityId()).concat(":").concat(String.valueOf(oldMetaMMOLoginInfo.getRegionId()));
-                List<String> oldUserIds = redisTemplate.opsForList().range(Constants.REDIS_PREFIX.concat(oldKey), 0, -1);
-                if (CollectionUtils.isEmpty(oldUserIds)) {
-                    log.error("查询不到上次所进入的区域的玩家信息");
-                    break;
-                }
-                // 分发消息给之前区域的玩家
-                buildMessageForSendingToAllOther(oldUserIds, 3, oldMetaMMOLoginInfo);
-                // 清除玩家上次缓存的地图信息
-                redisTemplate.opsForList().remove(Constants.REDIS_PREFIX.concat(oldKey), 0, Constants.REDIS_PREFIX.concat(userId));
-                // 缓存玩家新的地图信息
-                redisTemplate.opsForValue().set(Constants.REDIS_PREFIX.concat(userId), metaMMOLoginInfo);
-                redisTemplate.opsForList().leftPush(Constants.REDIS_PREFIX.concat(key), Constants.REDIS_PREFIX.concat(userId));
+                metaMMOLoginInfo = websocketHandle.buildMetaMMOLoginInfo(jsonObject, Long.parseLong(cityId), Long.parseLong(regionId), userId);
+                websocketHandle.handleTypeTwo(clients, otherUserIds, metaMMOLoginInfos, metaMMOLoginInfo, userId, key);
                 break;
             case 3:
                 log.info(String.format("当前操作类型为: %s -> 玩家在地图内移动", type));
                 metaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(Constants.REDIS_PREFIX.concat(userId));
-                if (Objects.isNull(metaMMOLoginInfo)) {
-                    log.error("缓存中不存在本玩家信息");
-                    break;
-                }
-                // 更新玩家位置信息
-                buildCommonProperty(metaMMOLoginInfo, jsonObject);
-                // 分发玩家信息给区域内其他玩家
-                buildMessageForSendingToAllOther(otherUserIds, 2, metaMMOLoginInfo);
-                redisTemplate.opsForValue().set(Constants.REDIS_PREFIX.concat(userId), metaMMOLoginInfo);
+                websocketHandle.handleTypeThree(clients, otherUserIds, jsonObject, metaMMOLoginInfo, userId);
                 break;
             case 4:
                 log.info(String.format("当前操作类型为: %s -> 玩家进入大厅", type));
                 metaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(Constants.REDIS_PREFIX.concat(userId));
-                if (Objects.isNull(metaMMOLoginInfo)) {
-                    log.error("缓存中不存在本玩家信息");
-                    break;
-                }
-                // 分发玩家信息给区域内其他玩家
-                buildMessageForSendingToAllOther(otherUserIds, 3, metaMMOLoginInfo);
-                // 更新库中玩家位置信息
-                MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findById(metaMMOLoginInfo.getId()).orElse(null);
-                if (Objects.isNull(dbMetaMMOLoginInfo)) {
-                    log.error(String.format("数据库不存在id: %s 的记录", metaMMOLoginInfo.getId()));
-                    break;
-                }
-                dbMetaMMOLoginInfo.setAxisX(metaMMOLoginInfo.getAxisX());
-                dbMetaMMOLoginInfo.setAxisY(metaMMOLoginInfo.getAxisY());
-                dbMetaMMOLoginInfo.setAxisZ(metaMMOLoginInfo.getAxisZ());
-                dbMetaMMOLoginInfo.setEulerX(metaMMOLoginInfo.getEulerX());
-                dbMetaMMOLoginInfo.setEulerY(metaMMOLoginInfo.getEulerY());
-                dbMetaMMOLoginInfo.setEulerZ(metaMMOLoginInfo.getEulerZ());
-                dbMetaMMOLoginInfo.setCityId(metaMMOLoginInfo.getCityId());
-                dbMetaMMOLoginInfo.setRegionId(metaMMOLoginInfo.getRegionId());
-                dbMetaMMOLoginInfo.setTop(metaMMOLoginInfo.getTop());
-                dbMetaMMOLoginInfo.setHat(metaMMOLoginInfo.getHat());
-                dbMetaMMOLoginInfo.setShoes(metaMMOLoginInfo.getShoes());
-                dbMetaMMOLoginInfo.setDown(metaMMOLoginInfo.getDown());
-                dbMetaMMOLoginInfo.setJewelry(metaMMOLoginInfo.getJewelry());
-                dbMetaMMOLoginInfo.setEmoji(metaMMOLoginInfo.getEmoji());
-                dbMetaMMOLoginInfo.setAnim(metaMMOLoginInfo.getAnim());
-                dbMetaMMOLoginInfo.setRole(metaMMOLoginInfo.getRole());
-                dbMetaMMOLoginInfo.setMoveType(metaMMOLoginInfo.getMoveType());
-                dbMetaMMOLoginInfo.setTurnedId(metaMMOLoginInfo.getTurnedId());
-                dbMetaMMOLoginInfo.setVehicleId(metaMMOLoginInfo.getVehicleId());
-                metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
+                websocketHandle.handleTypeFour(clients, otherUserIds, metaMMOLoginInfo);
                 break;
             default:
                 log.error(String.format("不存在的操作类型: %s", type));
         }
-
-    }
-
-    /**
-     * 分发玩家信息给区域内其他玩家
-     *
-     * @param userIds     玩家id集合
-     * @param messageType 消息类型
-     * @param data        消息体
-     */
-    private void buildMessageForSendingToAllOther(List<String> userIds, int messageType, Object data) {
-        MMOMessage mmoMessage = new MMOMessage();
-        mmoMessage.setMessageType(messageType);
-        mmoMessage.setMessage(data);
-        // 进行类型转换和判断
-        if (data instanceof MetaMMOLoginInfo) {
-            MetaMMOLoginInfo metaMMOLoginInfo = (MetaMMOLoginInfo) data;
-            if (!clients.containsKey(Constants.REDIS_PREFIX.concat(String.valueOf(metaMMOLoginInfo.getUserId())))) {
-                log.error("session信息不存在");
-                return;
-            }
-        }
-        userIds.forEach(id -> {
-            try {
-                if (!clients.containsKey(id)) {
-                    log.error("session信息不存在");
-                    return;
-                }
-                log.info(String.format("服务器给所有当前区域内在线用户发送消息, 当前在线人员为: %s, 消息内容: %s", id, JSON.toJSONString(mmoMessage)));
-                clients.get(id).getBasicRemote().sendText(JSON.toJSONString(mmoMessage));
-            } catch (Exception e) {
-                log.error(String.format("send message %s to %s throw exception %s:", JSON.toJSONString(mmoMessage), id, e));
-            }
-        });
-    }
-
-    /**
-     * 分发区域内其他玩家信息给自己
-     *
-     * @param userId            玩家id
-     * @param messageType       消息类型
-     * @param metaMMOLoginInfos 消息体(其他玩家信息)
-     */
-    private void buildMessageForSendingToUser(String userId, int messageType, List<MetaMMOLoginInfo> metaMMOLoginInfos) {
-        MMOMessage mmoMessage = new MMOMessage();
-        mmoMessage.setMessageType(messageType);
-        mmoMessage.setMap(metaMMOLoginInfos);
-        websocketCommon.sendMessageTo(clients, JSON.toJSONString(mmoMessage), userId);
-    }
-
-    /**
-     * 构建玩家登陆信息
-     *
-     * @param jsonObject 玩家位置等信息json对象
-     * @param cityId     城市id
-     * @param regionId   区域id
-     * @return 玩家位置信息
-     */
-    private MetaMMOLoginInfo buildMetaMMOLoginInfo(JSONObject jsonObject, Long cityId, Long regionId, String nickName, String userId) {
-        // 获取到进入地图时自己的信息
-        MetaMMOLoginInfo metaMMOLoginInfo = new MetaMMOLoginInfo();
-        buildCommonProperty(metaMMOLoginInfo, jsonObject);
-        metaMMOLoginInfo.setCityId(cityId);
-        metaMMOLoginInfo.setRegionId(regionId);
-        metaMMOLoginInfo.setUserId(Long.parseLong(userId));
-        metaMMOLoginInfo.setNickname(nickName);
-        if (Objects.nonNull(jsonObject.getString("role"))) {
-            metaMMOLoginInfo.setRole(jsonObject.getString("role"));
-        }
-        if (Objects.nonNull(jsonObject.getString("id"))) {
-            metaMMOLoginInfo.setId(Long.parseLong(jsonObject.getString("id")));
-        }
-        metaMMOLoginInfo.setCityId(cityId);
-        return metaMMOLoginInfo;
-    }
-
-    /**
-     * 根据jsonObject构建玩家位置信息
-     *
-     * @param metaMMOLoginInfo 玩家登陆信息
-     * @param jsonObject       玩家位置信息json对象
-     */
-    private void buildCommonProperty(MetaMMOLoginInfo metaMMOLoginInfo, JSONObject jsonObject) {
-        if (Objects.nonNull(jsonObject.getString("axisX"))) {
-            metaMMOLoginInfo.setAxisX(Float.parseFloat(jsonObject.getString("axisX")));
-        }
-        if (Objects.nonNull(jsonObject.getString("axisY"))) {
-            metaMMOLoginInfo.setAxisY(Float.parseFloat(jsonObject.getString("axisY")));
-        }
-        if (Objects.nonNull(jsonObject.getString("axisZ"))) {
-            metaMMOLoginInfo.setAxisZ(Float.parseFloat(jsonObject.getString("axisZ")));
-        }
-        if (Objects.nonNull(jsonObject.getString("eulerX"))) {
-            metaMMOLoginInfo.setEulerX(Float.parseFloat(jsonObject.getString("eulerX")));
-        }
-        if (Objects.nonNull(jsonObject.getString("eulerY"))) {
-            metaMMOLoginInfo.setEulerY(Float.parseFloat(jsonObject.getString("eulerY")));
-        }
-        if (Objects.nonNull(jsonObject.getString("eulerZ"))) {
-            metaMMOLoginInfo.setEulerZ(Float.parseFloat(jsonObject.getString("eulerZ")));
-        }
-        if (Objects.nonNull(jsonObject.getString("top"))) {
-            metaMMOLoginInfo.setTop(Integer.parseInt(jsonObject.getString("top")));
-        }
-        if (Objects.nonNull(jsonObject.getString("hat"))) {
-            metaMMOLoginInfo.setHat(Integer.parseInt(jsonObject.getString("hat")));
-        }
-        if (Objects.nonNull(jsonObject.getString("down"))) {
-            metaMMOLoginInfo.setDown(Integer.parseInt(jsonObject.getString("down")));
-        }
-        if (Objects.nonNull(jsonObject.getString("shoes"))) {
-            metaMMOLoginInfo.setShoes(Integer.parseInt(jsonObject.getString("shoes")));
-        }
-        if (Objects.nonNull(jsonObject.getString("jewelry"))) {
-            metaMMOLoginInfo.setJewelry(Integer.parseInt(jsonObject.getString("jewelry")));
-        }
-        if (Objects.nonNull(jsonObject.getString("anim"))) {
-            metaMMOLoginInfo.setAnim(Integer.parseInt(jsonObject.getString("anim")));
-        }
-        if (Objects.nonNull(jsonObject.getString("emoji"))) {
-            metaMMOLoginInfo.setEmoji(Integer.parseInt(jsonObject.getString("emoji")));
-        }
-        if (Objects.nonNull(jsonObject.getString("moveType"))) {
-            metaMMOLoginInfo.setMoveType(MoveType.valueOf(jsonObject.getString("moveType")));
-        }
-        if (Objects.nonNull(jsonObject.getString("vehicleId"))) {
-            metaMMOLoginInfo.setVehicleId(Long.parseLong(jsonObject.getString("vehicleId")));
-        }
-        if (Objects.nonNull(jsonObject.getString("turnedId"))) {
-            metaMMOLoginInfo.setTurnedId(Long.parseLong(jsonObject.getString("turnedId")));
-        }
     }
 
     /**
@@ -468,16 +256,6 @@ public class MMOWebSocket {
         return MetaServiceResult.returnSuccess("check success");
     }
 
-    private List<String> remove(String key, String userId) {
-        List<String> userIds = redisTemplate.opsForList().range(Constants.REDIS_PREFIX.concat(key), 0, -1);
-        //  去除当前玩家id
-        List<String> otherUserIds = new ArrayList<>();
-        if (CollectionUtils.isNotEmpty(userIds)) {
-            otherUserIds = userIds.stream().filter(id -> !Objects.equals(id, Constants.REDIS_PREFIX.concat(userId))).collect(Collectors.toList());
-        }
-        return otherUserIds;
-    }
-
     public MetaRestResult<Void> clearMMO(String userId, String regionId, String cityId) {
         init();
         if (clients.containsKey(Constants.REDIS_PREFIX.concat(userId))) {
@@ -486,8 +264,22 @@ public class MMOWebSocket {
             return MetaRestResult.returnError(errMsg);
         }
         String key = cityId.concat(":").concat(regionId);
-        redisTemplate.delete(Constants.REDIS_PREFIX.concat(userId));
-        redisTemplate.opsForList().remove(Constants.REDIS_PREFIX.concat(key), 0, Constants.REDIS_PREFIX.concat(userId));
+        closeExecute(key, userId);
         return MetaRestResult.returnSuccess("清除成功");
     }
+
+    private void closeExecute(String key, String userId) {
+        String userKey = Constants.REDIS_PREFIX.concat(userId);
+        redisTemplate.execute(new SessionCallback<>() {
+            @Override
+            public Object execute(@NotNull RedisOperations operations) throws DataAccessException {
+                operations.multi();
+                redisTemplate.delete(userKey);
+                redisTemplate.opsForList().remove(Constants.REDIS_PREFIX.concat(key), 0, userKey);
+                redisTemplate.delete(Constants.REDIS_USER_KEY.concat(userId));
+                return operations.exec();
+            }
+        });
+
+    }
 }

+ 15 - 12
src/main/java/com/izouma/meta/websocket/PublicScreenChatWebsocket.java

@@ -12,6 +12,7 @@ import com.izouma.meta.exception.BusinessException;
 import com.izouma.meta.repo.PublicScreenChatRepo;
 import com.izouma.meta.service.ContentAuditService;
 import com.izouma.meta.utils.ApplicationContextUtil;
+import com.izouma.meta.utils.HttpUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.stereotype.Service;
@@ -35,13 +36,12 @@ public class PublicScreenChatWebsocket {
      */
     private static final Map<String, Session> clients = new ConcurrentHashMap();
 
-    private PublicScreenChatRepo publicScreenChatRepo;
-
     private final String PREFIX = "meta-chat:";
 
-    private ContentAuditService contentAuditService;
-
-    private WebsocketCommon websocketCommon;
+    private PublicScreenChatRepo publicScreenChatRepo;
+    private ContentAuditService  contentAuditService;
+    private WebsocketCommon      websocketCommon;
+    private HttpUtils            httpUtils;
 
     private void init() {
         if (Objects.isNull(publicScreenChatRepo)) {
@@ -53,6 +53,9 @@ public class PublicScreenChatWebsocket {
         if (Objects.isNull(websocketCommon)) {
             websocketCommon = (WebsocketCommon) ApplicationContextUtil.getBean("websocketCommon");
         }
+        if (Objects.isNull(httpUtils)) {
+            httpUtils = (HttpUtils) ApplicationContextUtil.getBean("httpUtils");
+        }
     }
 
     @OnOpen
@@ -118,7 +121,7 @@ public class PublicScreenChatWebsocket {
         }
         if (Constants.HEART_RECEIVE.equals(message)) {
             log.info(String.format("sessionId:[%S] userId:[%S] 连接正常", session.getId(), userId));
-            websocketCommon.sendMessageTo(clients, Constants.HEART_RETURN, PREFIX.concat(userId));
+            websocketCommon.sendMessageToSingle(clients, Constants.HEART_RETURN, PREFIX.concat(userId));
             return;
         }
         log.info(String.format("收到来自userId[%S]的消息[%S]", userId, message));
@@ -174,7 +177,7 @@ public class PublicScreenChatWebsocket {
             sendMessageToAll(clients, JSON.toJSONString(publicScreenChat), PREFIX.concat(userId));
         }
         if (Constants.PRIVATE_CHAT.equals(type)) {
-            websocketCommon.sendMessageTo(clients, JSON.toJSONString(publicScreenChat), PREFIX.concat(toUserId));
+            websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(publicScreenChat), PREFIX.concat(toUserId));
         }
 
     }
@@ -200,7 +203,7 @@ public class PublicScreenChatWebsocket {
     }
 
     private PublicScreenChat savePublicScreenChat(String userId, String messageInfo, boolean illegal, ChatType chatType, String toUserId) {
-        String resultForWebsocketUser = websocketCommon.getRequest("/user/websocket/".concat(userId));
+        String resultForWebsocketUser = httpUtils.doGetNoParams("/user/websocket/".concat(userId));
         log.info(String.format("userId:[%S] userInfo:[%S]", userId, resultForWebsocketUser));
         WebsocketUser websocketUser;
         try {
@@ -220,7 +223,7 @@ public class PublicScreenChatWebsocket {
         publicScreenChat.setAvatar(websocketUser.getAvatar());
         publicScreenChat.setNickname(websocketUser.getNickname());
         publicScreenChat.setLevel(websocketUser.getLevel());
-        String resultForPurchaseLevel = websocketCommon.getRequest("/purchaseLevel/websocket/".concat(String.valueOf(websocketUser.getLevel())));
+        String resultForPurchaseLevel = httpUtils.doGetNoParams("/purchaseLevel/websocket/".concat(String.valueOf(websocketUser.getLevel())));
         log.info(String.format("userId:[%S] purchaseLevelInfo:[%S]", userId, resultForPurchaseLevel));
         PurchaseLevelDTO purchaseLevelDTO;
         try {
@@ -244,7 +247,7 @@ public class PublicScreenChatWebsocket {
         if (!ChatType.PRIVATE.equals(chatType)) {
             return publicScreenChatRepo.save(publicScreenChat);
         }
-        String resultForWebsocketToUser = websocketCommon.getRequest("/user/websocket/".concat(toUserId));
+        String resultForWebsocketToUser = httpUtils.doGetNoParams("/user/websocket/".concat(toUserId));
         log.info(String.format("toUserId:[%S] toUserInfo:[%S]", toUserId, resultForWebsocketToUser));
         WebsocketUser websocketToUser;
         try {
@@ -262,7 +265,7 @@ public class PublicScreenChatWebsocket {
         publicScreenChat.setToUserAvatar(websocketToUser.getAvatar());
         publicScreenChat.setToUserNickname(websocketToUser.getNickname());
         publicScreenChat.setToUserLevel(websocketToUser.getLevel());
-        String resultForToUserPurchaseLevel = websocketCommon.getRequest("/purchaseLevel/websocket/".concat(String.valueOf(websocketToUser.getLevel())));
+        String resultForToUserPurchaseLevel = httpUtils.doGetNoParams("/purchaseLevel/websocket/".concat(String.valueOf(websocketToUser.getLevel())));
         log.info(String.format("toUserId:[%S] toUserPurchaseLevelInfo:[%S]", toUserId, resultForToUserPurchaseLevel));
         PurchaseLevelDTO toUserPurchaseLevelDTO;
         try {
@@ -284,7 +287,7 @@ public class PublicScreenChatWebsocket {
     private void exceptionHandle(String userId, PublicScreenChatExceptionMsg msg) {
         log.error(JSON.toJSONString(msg));
         // 推送消息给该玩家
-        websocketCommon.sendMessageTo(clients, JSON.toJSONString(msg), PREFIX.concat(userId));
+        websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(msg), PREFIX.concat(userId));
     }
 }
 

+ 6 - 6
src/main/java/com/izouma/meta/websocket/WebSocket.java

@@ -86,14 +86,14 @@ public class WebSocket {
             return;
         }
         if (metaSwitchFire.isStatus()) {
-            websocketCommon.sendMessageTo(clients, Constants.META_WEBSOCKET_NOTICE_FIRE_OPEN, userId);
+            websocketCommon.sendMessageToSingle(clients, Constants.META_WEBSOCKET_NOTICE_FIRE_OPEN, userId);
         }
         MetaSwitch metaSwitchLive = metaSwitchRepo.findByNameAndDel(Constants.META_WEBSOCKET_NOTICE_LIVE, false);
         if (Objects.isNull(metaSwitchLive)) {
             return;
         }
         if (metaSwitchLive.isStatus()) {
-            websocketCommon.sendMessageTo(clients, Constants.META_WEBSOCKET_NOTICE_LIVE_OPEN, userId);
+            websocketCommon.sendMessageToSingle(clients, Constants.META_WEBSOCKET_NOTICE_LIVE_OPEN, userId);
         }
 
     }
@@ -120,7 +120,7 @@ public class WebSocket {
         }
         if (Constants.HEART_RECEIVE.equals(message)) {
             log.info(String.format("sessionId:[%S] userId:[%S] 连接正常", session.getId(), userId));
-            websocketCommon.sendMessageTo(clients, Constants.HEART_RETURN, userId);
+            websocketCommon.sendMessageToSingle(clients, Constants.HEART_RETURN, userId);
             return;
         }
         JSONObject jsonObject = JSON.parseObject(message);
@@ -135,7 +135,7 @@ public class WebSocket {
                 log.info("查询邮件");
                 try {
                     List<MetaEmail> metaEmails = queryEmail(Long.parseLong(userId));
-                    websocketCommon.sendMessageTo(clients, CollectionUtils.isNotEmpty(metaEmails) ? JSON.toJSONString(metaEmails) : JSON.toJSONString(new ArrayList<>()), userId);
+                    websocketCommon.sendMessageToSingle(clients, CollectionUtils.isNotEmpty(metaEmails) ? JSON.toJSONString(metaEmails) : JSON.toJSONString(new ArrayList<>()), userId);
                 } catch (Exception e) {
                     log.error(String.format("查询邮件失败,错误信息[%S]", e.getMessage()));
                 }
@@ -188,7 +188,7 @@ public class WebSocket {
             case Constants.META_NEW_FRIEND_APPLY:
                 log.info("新好友申请通知");
                 String friendId = jsonObject.getString("value");
-                websocketCommon.sendMessageTo(clients, Constants.META_NEW_FRIEND_APPLY, friendId);
+                websocketCommon.sendMessageToSingle(clients, Constants.META_NEW_FRIEND_APPLY, friendId);
                 break;
             default:
                 log.error(String.format("不支持的类型[%S]", type));
@@ -240,7 +240,7 @@ public class WebSocket {
     private void exceptionHandle(String userId, PublicScreenChatExceptionMsg msg) {
         log.error(JSON.toJSONString(msg));
         // 推送消息给该玩家
-        websocketCommon.sendMessageTo(clients, JSON.toJSONString(msg), userId);
+        websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(msg), userId);
     }
 
 }

+ 35 - 47
src/main/java/com/izouma/meta/websocket/WebsocketCommon.java

@@ -1,18 +1,13 @@
 package com.izouma.meta.websocket;
 
 
-import com.izouma.meta.config.GeneralProperties;
+import com.alibaba.excel.util.CollectionUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
 import org.springframework.stereotype.Service;
 
 import javax.websocket.Session;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -21,29 +16,6 @@ import java.util.Set;
 @AllArgsConstructor
 public class WebsocketCommon {
 
-    private final HttpClient client = HttpClients.createDefault();
-    private GeneralProperties generalProperties;
-
-    /**
-     * 给指定用户分发消息
-     *
-     * @param message  消息体
-     * @param toUserId 用户id
-     */
-    public void sendMessageTo(Map<String, Session> clients, String message, String toUserId) {
-        if (!clients.containsKey(toUserId)) {
-            log.error("session信息不存在");
-            return;
-        }
-        log.info(String.format("给指定的在线用户发送消息,当前在线人员为[%S]。消息:[%S]", toUserId, message));
-        try {
-            clients.get(toUserId).getBasicRemote().sendText(message);
-        } catch (Exception e) {
-            log.error(String.format("send message [%S] to [%S] throw exception [%S]:", message, toUserId, e));
-        }
-        log.info("send message success");
-    }
-
     /**
      * 给所有用户发消息
      *
@@ -55,32 +27,48 @@ public class WebsocketCommon {
         userIds.forEach(id -> {
             try {
                 if (!id.equals(userId)) {
-                    log.info(String.format("服务器给所有在线用户发送消息,当前在线人员为[%S]。消息:[%S]", id, message));
+                    log.info(String.format("服务器给所有在线用户发送消息,当前在线人员为 %s 。消息:%s", id, message));
                     clients.get(id).getBasicRemote().sendText(message);
                 }
             } catch (Exception e) {
-                log.error(String.format("send message [%S] to [%S] throw exception [%S]:", message, id, e));
+                log.error(String.format("send message %s to %s throw exception %s :", message, id, e));
             }
         });
     }
 
-    public String getRequest(String url) {
-        String requestUrl = generalProperties.getUrlPrefix().concat(url);
-        HttpGet get = new HttpGet(requestUrl);
-        try (CloseableHttpResponse res = (CloseableHttpResponse) client.execute(get)) {
-            log.info(String.format("execute get request url : %s ", requestUrl));
-            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
-                return EntityUtils.toString(res.getEntity());
-            } else {
-                String errMsg = String.format("request [%s] returned error", requestUrl);
-                log.error(errMsg);
-                throw new RuntimeException(errMsg);
-            }
+    /**
+     * 给单个用户发消息
+     *
+     * @param clients 客户端map
+     * @param message 消息体
+     * @param userId  用户id
+     */
+    public void sendMessageToSingle(Map<String, Session> clients, String message, String userId) {
+        if (!clients.containsKey(userId)) {
+            log.error(String.format("用户id: %s 的session信息不存在", userId));
+            return;
+        }
+        log.info(String.format("send message %s to %s ", message, userId));
+        try {
+            clients.get(userId).getBasicRemote().sendText(message);
         } catch (Exception e) {
-            String errMsg = String.format("client failed to execute request for url: %s", requestUrl);
-            log.error(errMsg, e);
-            throw new RuntimeException(errMsg, e);
+            log.error(String.format("send message %s to %s throw exception %s:", message, userId, e));
+        }
+        log.info("send message success");
+    }
+
+    /**
+     * 给多个用户发消息
+     *
+     * @param clients 客户端
+     * @param message 消息体
+     * @param userIds 用户集合
+     */
+    public void sendMessageToMultiple(Map<String, Session> clients, String message, List<String> userIds) {
+        if (CollectionUtils.isEmpty(userIds)) {
+            return;
         }
+        userIds.forEach(id -> sendMessageToSingle(clients, message, id));
     }
 
 }

+ 291 - 0
src/main/java/com/izouma/meta/websocket/WebsocketHandle.java

@@ -0,0 +1,291 @@
+package com.izouma.meta.websocket;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.izouma.meta.config.Constants;
+import com.izouma.meta.domain.MetaMMOLoginInfo;
+import com.izouma.meta.domain.MetaObjectMove;
+import com.izouma.meta.domain.MetaObjectMoveCoordinate;
+import com.izouma.meta.dto.MMOMessage;
+import com.izouma.meta.enums.MoveType;
+import com.izouma.meta.repo.MetaMMOLoginInfoRepo;
+import com.izouma.meta.repo.MetaObjectMoveRepo;
+import com.izouma.meta.utils.HttpUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.websocket.Session;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+public class WebsocketHandle {
+
+    private MetaObjectMoveRepo   metaObjectMoveRepo;
+    private HttpUtils            httpUtils;
+    private RedisTemplate        redisTemplate;
+    private MetaMMOLoginInfoRepo metaMMOLoginInfoRepo;
+    private WebsocketCommon      websocketCommon;
+
+    public void handleTypeNine(Map<String, Session> clients, JSONObject jsonObject, String userId) {
+        Long objectId;
+        try {
+            objectId = Long.parseLong(jsonObject.getString("objectId"));
+        } catch (Exception e) {
+            log.error(String.format("objectId parse to long throw exception: %s", e.getMessage()));
+            return;
+        }
+        MetaObjectMove metaObjectMove = metaObjectMoveRepo.findByObjectIdAndDel(objectId, false);
+        if (Objects.isNull(metaObjectMove)) {
+            log.error(String.format("物体: %s 不存在", objectId));
+            return;
+        }
+        if (!metaObjectMove.isRun()) {
+            log.error(String.format("物体: %s 已经停运", objectId));
+            return;
+        }
+        String cityId = String.valueOf(metaObjectMove.getCityId());
+        String regionId = String.valueOf(metaObjectMove.getRegionId());
+        String key = cityId.concat(":").concat(regionId);
+        List<String> userIds = allUserIdExcept(key, userId);
+        if (CollectionUtils.isEmpty(userIds)) {
+            return;
+        }
+        MetaObjectMoveCoordinate metaObjectMoveCoordinate;
+        try {
+            String str = httpUtils.doGetNoParams("/metaObjectMove/".concat(String.valueOf(metaObjectMove.getObjectId())).concat("/queryCoordinate"));
+            metaObjectMoveCoordinate = JSONObject.parseObject(str, MetaObjectMoveCoordinate.class);
+        } catch (Exception e) {
+            String errMsg = String.format("MetaObjectMoveCoordinate JSON parse throw exception: %s", e);
+            log.error(errMsg);
+            return;
+        }
+        if (Objects.isNull(metaObjectMoveCoordinate)) {
+            log.error(String.format("物体: %s 坐标信息不存在", metaObjectMove.getObjectId()));
+            return;
+        }
+        websocketCommon.sendMessageToMultiple(clients, JSON.toJSONString(new MMOMessage(9, metaObjectMoveCoordinate, null)), userIds);
+    }
+
+    public void handleTypeOne(Map<String, Session> clients, List<String> otherUserIds, List<MetaMMOLoginInfo> metaMMOLoginInfos, MetaMMOLoginInfo metaMMOLoginInfo, String userId, String key) {
+        if (CollectionUtils.isNotEmpty(otherUserIds)) {
+            if (CollectionUtils.isNotEmpty(metaMMOLoginInfos)) {
+                // 分发自己信息给区域内其他玩家
+                websocketCommon.sendMessageToMultiple(clients, JSON.toJSONString(new MMOMessage(4, metaMMOLoginInfo, null)), otherUserIds);
+                // 分发区域内其他玩家信息给自己
+                websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(new MMOMessage(1, null, metaMMOLoginInfos)), Constants.REDIS_PREFIX.concat(userId));
+            }
+        }
+        redisExecute(metaMMOLoginInfo, userId, key);
+    }
+
+    public void handleTypeTwo(Map<String, Session> clients, List<String> otherUserIds, List<MetaMMOLoginInfo> metaMMOLoginInfos, MetaMMOLoginInfo metaMMOLoginInfo, String userId, String key) {
+        if (CollectionUtils.isNotEmpty(otherUserIds)) {
+            // 分发自己信息给区域内其他玩家
+            websocketCommon.sendMessageToMultiple(clients, JSON.toJSONString(new MMOMessage(4, metaMMOLoginInfo, null)), otherUserIds);
+            // 分发区域内其他玩家信息给自己
+            websocketCommon.sendMessageToSingle(clients, JSON.toJSONString(new MMOMessage(1, null, metaMMOLoginInfos)), Constants.REDIS_PREFIX.concat(userId));
+        }
+        MetaMMOLoginInfo oldMetaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(Constants.REDIS_PREFIX.concat(userId));
+        if (Objects.isNull(oldMetaMMOLoginInfo)) {
+            log.error("缺失玩家上次区域的地图缓存信息");
+            return;
+        }
+        String oldKey = String.valueOf(oldMetaMMOLoginInfo.getCityId()).concat(":").concat(String.valueOf(oldMetaMMOLoginInfo.getRegionId()));
+        List<String> oldUserIds = redisTemplate.opsForList().range(Constants.REDIS_PREFIX.concat(oldKey), 0, -1);
+        if (CollectionUtils.isEmpty(oldUserIds)) {
+            log.error("查询不到上次所进入的区域的玩家信息");
+            return;
+        }
+        // 分发消息给之前区域的玩家
+        websocketCommon.sendMessageToMultiple(clients, JSON.toJSONString(new MMOMessage(3, oldMetaMMOLoginInfo, null)), oldUserIds);
+        // 清除玩家上次缓存的地图信息
+        // redisTemplate.opsForList().remove(Constants.REDIS_PREFIX.concat(oldKey), 0, Constants.REDIS_PREFIX.concat(userId));
+        // 缓存玩家新的地图信息
+        redisExecute(metaMMOLoginInfo, userId, key);
+    }
+
+    public void handleTypeThree(Map<String, Session> clients, List<String> otherUserIds, JSONObject jsonObject, MetaMMOLoginInfo metaMMOLoginInfo, String userId) {
+        if (Objects.isNull(metaMMOLoginInfo)) {
+            log.error("缓存中不存在本玩家信息");
+            return;
+        }
+        // 更新玩家位置信息
+        buildCommonProperty(metaMMOLoginInfo, jsonObject);
+        // 分发玩家信息给区域内其他玩家
+        websocketCommon.sendMessageToMultiple(clients, JSON.toJSONString(new MMOMessage(2, metaMMOLoginInfo, null)), otherUserIds);
+        redisTemplate.opsForValue().set(Constants.REDIS_PREFIX.concat(userId), metaMMOLoginInfo);
+    }
+
+    public void handleTypeFour(Map<String, Session> clients, List<String> otherUserIds, MetaMMOLoginInfo metaMMOLoginInfo) {
+        if (Objects.isNull(metaMMOLoginInfo)) {
+            log.error("缓存中不存在本玩家信息");
+            return;
+        }
+        // 分发玩家信息给区域内其他玩家
+        websocketCommon.sendMessageToMultiple(clients, JSON.toJSONString(new MMOMessage(3, metaMMOLoginInfo, null)), otherUserIds);
+        // 更新库中玩家位置信息
+        MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findById(metaMMOLoginInfo.getId()).orElse(null);
+        if (Objects.isNull(dbMetaMMOLoginInfo)) {
+            log.error(String.format("数据库不存在id: %s 的记录", metaMMOLoginInfo.getId()));
+            return;
+        }
+        dbMetaMMOLoginInfo.setAxisX(metaMMOLoginInfo.getAxisX());
+        dbMetaMMOLoginInfo.setAxisY(metaMMOLoginInfo.getAxisY());
+        dbMetaMMOLoginInfo.setAxisZ(metaMMOLoginInfo.getAxisZ());
+        dbMetaMMOLoginInfo.setEulerX(metaMMOLoginInfo.getEulerX());
+        dbMetaMMOLoginInfo.setEulerY(metaMMOLoginInfo.getEulerY());
+        dbMetaMMOLoginInfo.setEulerZ(metaMMOLoginInfo.getEulerZ());
+        dbMetaMMOLoginInfo.setCityId(metaMMOLoginInfo.getCityId());
+        dbMetaMMOLoginInfo.setRegionId(metaMMOLoginInfo.getRegionId());
+        dbMetaMMOLoginInfo.setTop(metaMMOLoginInfo.getTop());
+        dbMetaMMOLoginInfo.setHat(metaMMOLoginInfo.getHat());
+        dbMetaMMOLoginInfo.setShoes(metaMMOLoginInfo.getShoes());
+        dbMetaMMOLoginInfo.setDown(metaMMOLoginInfo.getDown());
+        dbMetaMMOLoginInfo.setJewelry(metaMMOLoginInfo.getJewelry());
+        dbMetaMMOLoginInfo.setEmoji(metaMMOLoginInfo.getEmoji());
+        dbMetaMMOLoginInfo.setAnim(metaMMOLoginInfo.getAnim());
+        dbMetaMMOLoginInfo.setRole(metaMMOLoginInfo.getRole());
+        dbMetaMMOLoginInfo.setMoveType(metaMMOLoginInfo.getMoveType());
+        dbMetaMMOLoginInfo.setTurnedId(metaMMOLoginInfo.getTurnedId());
+        dbMetaMMOLoginInfo.setTitleId(metaMMOLoginInfo.getTitleId());
+        dbMetaMMOLoginInfo.setSeatId(metaMMOLoginInfo.getSeatId());
+        dbMetaMMOLoginInfo.setRunId(metaMMOLoginInfo.getRunId());
+        dbMetaMMOLoginInfo.setNickname(metaMMOLoginInfo.getNickname());
+        dbMetaMMOLoginInfo.setVehicleId(metaMMOLoginInfo.getVehicleId());
+        metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
+    }
+
+
+    /**
+     * 根据jsonObject构建玩家位置信息
+     *
+     * @param metaMMOLoginInfo 玩家登陆信息
+     * @param jsonObject       玩家位置信息json对象
+     */
+    public void buildCommonProperty(MetaMMOLoginInfo metaMMOLoginInfo, JSONObject jsonObject) {
+        if (Objects.nonNull(jsonObject.getString("axisX"))) {
+            metaMMOLoginInfo.setAxisX(Float.parseFloat(jsonObject.getString("axisX")));
+        }
+        if (Objects.nonNull(jsonObject.getString("axisY"))) {
+            metaMMOLoginInfo.setAxisY(Float.parseFloat(jsonObject.getString("axisY")));
+        }
+        if (Objects.nonNull(jsonObject.getString("axisZ"))) {
+            metaMMOLoginInfo.setAxisZ(Float.parseFloat(jsonObject.getString("axisZ")));
+        }
+        if (Objects.nonNull(jsonObject.getString("eulerX"))) {
+            metaMMOLoginInfo.setEulerX(Float.parseFloat(jsonObject.getString("eulerX")));
+        }
+        if (Objects.nonNull(jsonObject.getString("eulerY"))) {
+            metaMMOLoginInfo.setEulerY(Float.parseFloat(jsonObject.getString("eulerY")));
+        }
+        if (Objects.nonNull(jsonObject.getString("eulerZ"))) {
+            metaMMOLoginInfo.setEulerZ(Float.parseFloat(jsonObject.getString("eulerZ")));
+        }
+        if (Objects.nonNull(jsonObject.getString("top"))) {
+            metaMMOLoginInfo.setTop(Integer.parseInt(jsonObject.getString("top")));
+        }
+        if (Objects.nonNull(jsonObject.getString("hat"))) {
+            metaMMOLoginInfo.setHat(Integer.parseInt(jsonObject.getString("hat")));
+        }
+        if (Objects.nonNull(jsonObject.getString("down"))) {
+            metaMMOLoginInfo.setDown(Integer.parseInt(jsonObject.getString("down")));
+        }
+        if (Objects.nonNull(jsonObject.getString("shoes"))) {
+            metaMMOLoginInfo.setShoes(Integer.parseInt(jsonObject.getString("shoes")));
+        }
+        if (Objects.nonNull(jsonObject.getString("jewelry"))) {
+            metaMMOLoginInfo.setJewelry(Integer.parseInt(jsonObject.getString("jewelry")));
+        }
+        if (Objects.nonNull(jsonObject.getString("anim"))) {
+            metaMMOLoginInfo.setAnim(Integer.parseInt(jsonObject.getString("anim")));
+        }
+        if (Objects.nonNull(jsonObject.getString("emoji"))) {
+            metaMMOLoginInfo.setEmoji(Integer.parseInt(jsonObject.getString("emoji")));
+        }
+        if (Objects.nonNull(jsonObject.getString("moveType"))) {
+            metaMMOLoginInfo.setMoveType(MoveType.valueOf(jsonObject.getString("moveType")));
+        }
+        if (Objects.nonNull(jsonObject.getString("vehicleId"))) {
+            metaMMOLoginInfo.setVehicleId(Long.parseLong(jsonObject.getString("vehicleId")));
+        }
+        if (Objects.nonNull(jsonObject.getString("turnedId"))) {
+            metaMMOLoginInfo.setTurnedId(Long.parseLong(jsonObject.getString("turnedId")));
+        }
+        if (Objects.nonNull(jsonObject.getString("titleId"))) {
+            metaMMOLoginInfo.setTitleId(Long.parseLong(jsonObject.getString("titleId")));
+        }
+        if (Objects.nonNull(jsonObject.getString("seatId"))) {
+            metaMMOLoginInfo.setSeatId(Long.parseLong(jsonObject.getString("seatId")));
+        }
+        if (Objects.nonNull(jsonObject.getString("runId"))) {
+            metaMMOLoginInfo.setRunId(Long.parseLong(jsonObject.getString("runId")));
+        }
+        if (Objects.nonNull(jsonObject.getString("nickname"))) {
+            metaMMOLoginInfo.setNickname(jsonObject.getString("nickname"));
+        }
+    }
+
+
+    public List<String> allUserIdExcept(String key, String userId) {
+        List<String> userIds = redisTemplate.opsForList().range(Constants.REDIS_PREFIX.concat(key), 0, -1);
+        //  去除当前玩家id
+        List<String> otherUserIds = new ArrayList<>();
+        if (CollectionUtils.isNotEmpty(userIds)) {
+            otherUserIds = userIds.stream().filter(id -> !Objects.equals(id, Constants.REDIS_PREFIX.concat(userId))).collect(Collectors.toList());
+        }
+        return otherUserIds;
+    }
+
+    /**
+     * 构建玩家登陆信息
+     *
+     * @param jsonObject 玩家位置等信息json对象
+     * @param cityId     城市id
+     * @param regionId   区域id
+     * @return 玩家位置信息
+     */
+    public MetaMMOLoginInfo buildMetaMMOLoginInfo(JSONObject jsonObject, Long cityId, Long regionId, String userId) {
+        // 获取到进入地图时自己的信息
+        MetaMMOLoginInfo metaMMOLoginInfo = new MetaMMOLoginInfo();
+        buildCommonProperty(metaMMOLoginInfo, jsonObject);
+        metaMMOLoginInfo.setCityId(cityId);
+        metaMMOLoginInfo.setRegionId(regionId);
+        metaMMOLoginInfo.setUserId(Long.parseLong(userId));
+        if (Objects.nonNull(jsonObject.getString("role"))) {
+            metaMMOLoginInfo.setRole(jsonObject.getString("role"));
+        }
+        if (Objects.nonNull(jsonObject.getString("id"))) {
+            metaMMOLoginInfo.setId(Long.parseLong(jsonObject.getString("id")));
+        }
+        metaMMOLoginInfo.setCityId(cityId);
+        return metaMMOLoginInfo;
+    }
+
+    public void redisExecute(MetaMMOLoginInfo metaMMOLoginInfo, String userId, String key) {
+        String userKey = Constants.REDIS_PREFIX.concat(userId);
+        Boolean exist = redisTemplate.hasKey(Constants.REDIS_USER_KEY.concat(userId));
+        if (Boolean.TRUE.equals(exist)) {
+            String oldKey = (String) redisTemplate.opsForValue().get(Constants.REDIS_USER_KEY.concat(userId));
+            if (StringUtils.isNotBlank(oldKey)) {
+                redisTemplate.opsForList().remove(oldKey, 0, userKey);
+            }
+        }
+        // 将自己信息存到redis中
+        redisTemplate.opsForValue().set(userKey, metaMMOLoginInfo);
+        redisTemplate.opsForList().leftPush(Constants.REDIS_PREFIX.concat(key), userKey);
+        // 更新
+        redisTemplate.opsForValue().set(Constants.REDIS_USER_KEY.concat(userId), Constants.REDIS_PREFIX.concat(key));
+    }
+
+}