主頁 >  其他 > 從零開始實作放置游戲(十三)——實作戰斗掛機(4)添加websocket組件

從零開始實作放置游戲(十三)——實作戰斗掛機(4)添加websocket組件

2020-09-10 12:34:59 其他

  前兩張,我們已經實作了登陸界面和游戲的主界面,不過游戲主界面的資料都是在前端寫死的文本,本章我們給game模塊添加websocket組件,實作前后端通信,這樣,前端的資料就可以從后端動態獲取到了,

一、添加maven依賴

  在game模塊的pom中,我們添加3個依賴包如下:

 1         <!-- websocket組件 -->
 2         <dependency>
 3             <groupId>org.springframework</groupId>
 4             <artifactId>spring-websocket</artifactId>
 5             <version>5.1.6.RELEASE</version>
 6         </dependency>
 7         <dependency>
 8             <groupId>org.springframework</groupId>
 9             <artifactId>spring-messaging</artifactId>
10             <version>5.1.6.RELEASE</version>
11         </dependency>
12         <dependency>
13             <groupId>javax.websocket</groupId>
14             <artifactId>javax.websocket-api</artifactId>
15             <version>1.1</version>
16             <scope>provided</scope>
17         </dependency>

二、后端添加MessageHub

  在com.idlewow.game.hub下MessageHub,這個類將主要負責接收客戶端的websocket資訊,代碼如下:

 1 @Component
 2 @ServerEndpoint(value = "https://www.cnblogs.com/hub", configurator = HttpSessionConfigurator.class)
 3 public class MessageHub {
 4     private static final Logger logger = LogManager.getLogger(MessageHub.class);
 5 
 6     @Autowired
 7     MessageHandler messageHandler;
 8     @Autowired
 9     CharacterService characterService;
10 
11     @OnOpen
12     public void onOpen(Session session, EndpointConfig config) {
13         logger.info("[websocket][" + session.getId() + "]建立連接");
14         try {
15             HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getSimpleName());
16             if (httpSession == null) {
17                 logger.error("[websocket][" + session.getId() + "]獲取HttpSession失敗!");
18                 throw new Exception("獲取HttpSession失敗!");
19             }
20 
21 
22             if (httpSession.getAttribute(GameWorld.SK_CharId) == null) {
23                 logger.error("[websocket][" + session.getId() + "]獲取角色Id為空!");
24                 throw new Exception("獲取角色ID為空!");
25             }
26 
27             String charId = httpSession.getAttribute(GameWorld.SK_CharId).toString();
28             CommonResult commonResult = characterService.find(charId);
29             if (commonResult.isSuccess()) {
30                 Character character = (Character) commonResult.getData();
31                 /* 加載成功,添加快取 */
32                 GameWorld.OnlineSession.add(session);
33                 GameWorld.OnlineCharacter.put(session.getId(), character);
34                 GameWorld.MapCharacter.get(character.getMapId()).add(character);
35             } else {
36                 logger.error("加載角色資訊失敗!charId:" + charId + " message:" + commonResult.getMessage());
37                 throw new Exception("加載角色資訊失敗!");
38             }
39         } catch (Exception ex) {
40             logger.error("[websocket][" + session.getId() + "]建立連接例外:" + ex.getMessage(), ex);
41             this.closeSession(session, ex.getMessage());
42         }
43     }
44 
45     @OnMessage
46     public void onMessage(Session session, String message) {
47         logger.info("[websocket][" + session.getId() + "]接收訊息:" + message);
48         messageHandler.handleMessage(session, message);
49     }
50 
51     @OnClose
52     public void onClose(Session session) {
53         logger.info("[websocket][" + session.getId() + "]關閉連接");
54         /* 清理快取 */
55         Character character = GameWorld.OnlineCharacter.get(session.getId());
56         GameWorld.OnlineSession.remove(session);
57         GameWorld.OnlineCharacter.remove(session.getId());
58         GameWorld.MapCharacter.get(character.getMapId()).remove(character);
59     }
60 
61     @OnError
62     public void one rror(Session session, Throwable t) {
63         logger.error("[websocket][" + session.getId() + "]發生例外:" + t.getMessage(), t);
64     }
65 
66     private void closeSession(Session session, String message) {
67         try {
68             logger.info("[websocket][" + session.getId() + "]關閉連接,原因:" + message);
69             CloseReason closeReason = new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, message);
70             session.close(closeReason);
71         } catch (Exception ex) {
72             logger.error("[websocket]關閉連接例外:" + ex.getMessage(), ex);
73         }
74     }
75 }
MessageHub

  Hub類主要包括OnOpen、OnMessage、OnClose、OnError 4個方法,

  在OnOpen建立連接時,我們從HttpSession中獲取角色Id,并加載角色資訊,更新在線資料等,這里我們創建一個GameWorld類,將在線串列等游戲世界的全域靜態資料保存在其中,

  在OnMessage方法接收到客戶端資料時,我們將訊息在MessageHandler中統一處理,

  OnClose和OnError對應關閉連接和例外發生事件,關閉連接時,需要將游戲角色從在線串列中清除,發生例外時,我們暫時僅記錄日志,

  注意:在MesssageHub的注解中,我們給其配置了一個HttpSessionConfigurator,是為了在socket訊息中獲取到HttpSession資料,如果不加這個配置,HttpSession是獲取不到的,其代碼如下:

1 public class HttpSessionConfigurator extends SpringConfigurator {
2     @Override
3     public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
4         HttpSession httpSession = (HttpSession) request.getHttpSession();
5         sec.getUserProperties().put(HttpSession.class.getSimpleName(), httpSession);
6         super.modifyHandshake(sec, request, response);
7     }
8 }

三、定義訊息型別

  在socket通信時,我們必須定義訊息的資料結構,并準備相應檔案,方便前后端通信,

  這里我們創建訊息類WowMessage,并規定其由header和content兩部分構成,header中主要包括訊息型別,請求時間等通用引數,content主要包括具體的業務資料,

  整個訊息類的UML圖如下,其中例舉了4種具體的訊息型別,LoadCache快取加載,Login登陸訊息,Chat聊天訊息,Move地圖移動訊息,

  

 

四、后端訊息處理

 

   在定義好訊息型別后,我們即可在后端對相應的訊息進行處理,代碼如下:

  在handleMessage方法中,我們根據header中傳入的messageCode,來確定是何種訊息,并轉入對應的處理子方法,

  比如處理地圖移動的handleMoveMessage方法,在這個方法中,我們將人物資訊快取資料中的當前地圖ID修改為移動后的地圖ID,從原地圖在線串列中移除此角色,在目標地圖在線串列中添加此角色,并回傳目標地圖的資訊給前端以便展示,

@Component
public class MessageHandler {
    private static final Logger logger = LogManager.getLogger(MessageHandler.class);

    @Autowired
    CharacterService characterService;
    @Autowired
    WowMapService wowMapService;
    @Autowired
    MapMobService mapMobService;
    @Autowired
    MapCoordService mapCoordService;

    /**
     * 訊息處理
     *
     * @param session session
     * @param message 訊息
     */
    public void handleMessage(Session session, String message) {
        WowMessage<?> wowMessage = JSONObject.parseObject(message, WowMessage.class);
        WowMessageHeader header = wowMessage.getHeader();
        String messageCode = header.getMessageCode();
        switch (messageCode) {
            case WowMessageCode.LoadCache:
                this.handleLoadCacheMessage(session, (WowMessage<LoadCacheRequest>) wowMessage);
                break;
            case WowMessageCode.RefreshOnline:
                this.handleRefreshOnlineMessage(session, (WowMessage<RefreshOnlineRequest>) wowMessage);
                break;
            case WowMessageCode.Login:
                this.handleLoginMessage(session, (WowMessage<LoginRequest>) wowMessage);
                break;
            case WowMessageCode.Chat:
                this.handleChatMessage(session, (WowMessage<ChatRequest>) wowMessage);
                break;
            case WowMessageCode.Move:
                this.handleMoveMessage(session, (WowMessage<MoveRequest>) wowMessage);
                break;
            default:
                break;
        }
    }

    /**
     * 給指定客戶端發送訊息
     *
     * @param session 客戶端session
     * @param message 訊息內容
     */
    private void sendOne(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    /**
     * 給所有客戶端發送訊息
     *
     * @param message 訊息內容
     */
    private void sendAll(String message) {
        try {
            for (Session session : GameWorld.OnlineSession) {
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    /**
     * 登陸加載
     *
     * @param session session
     * @param message 訊息
     */
    private void handleLoginMessage(Session session, WowMessage<LoginRequest> message) {
        WowMessageHeader header = message.getHeader();
        header.setResponseTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        LoginResponse response = new LoginResponse();
        Character character = GameWorld.OnlineCharacter.get(session.getId());
        String mapId = character.getMapId();
        MapInfo mapInfo = this.loadMapInfo(mapId);
        response.setMapInfo(mapInfo);
        OnlineInfo onlineInfo = this.loadOnlineInfo(mapId);
        response.setOnlineInfo(onlineInfo);
        WowMessage wowMessage = new WowMessage<>(header, response);
        this.sendOne(session, JSON.toJSONString(wowMessage));
    }

    /**
     * 發送聊天
     *
     * @param session session
     * @param message 訊息
     */
    private void handleChatMessage(Session session, WowMessage<ChatRequest> message) {
        WowMessageHeader header = message.getHeader();
        header.setResponseTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        ChatRequest request = message.getContent();
        ChatResponse response = new ChatResponse();
        response.setSendId(request.getSendId());
        response.setSendName(request.getSendName());
        response.setRecvId(request.getRecvId());
        response.setRecvName(request.getRecvName());
        response.setMessage(request.getMessage());
        response.setChannel(request.getChannel());
        WowMessage wowMessage = new WowMessage<>(header, response);
        if (request.getChannel().equals(GameWorld.ChatChannel.WORLD)) {
            this.sendAll(JSON.toJSONString(wowMessage));
        } else if (request.getChannel().equals(GameWorld.ChatChannel.PRIVATE)) {
            // todo 發送訊息給指定玩家
        } else if (request.getChannel().equals(GameWorld.ChatChannel.LOCAL)) {
            // todo 發送訊息給當前地圖玩家
        }
    }

    /**
     * 加載快取
     *
     * @param session session
     * @param message 訊息
     */
    private void handleLoadCacheMessage(Session session, WowMessage<LoadCacheRequest> message) {
        WowMessageHeader header = message.getHeader();
        header.setResponseTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        LoadCacheResponse response = new LoadCacheResponse();
        Map<String, Integer> levelExpMap = new HashMap<>();
        for (Integer key : CacheUtil.levelExpMap.keySet()) {
            levelExpMap.put(key.toString(), CacheUtil.levelExpMap.get(key));
        }

        response.setLevelExpMap(levelExpMap);
        WowMessage wowMessage = new WowMessage<>(header, response);
        this.sendOne(session, JSON.toJSONString(wowMessage));
    }

    /**
     * 地圖移動
     *
     * @param session session
     * @param message 訊息
     */
    private void handleMoveMessage(Session session, WowMessage<MoveRequest> message) {
        WowMessageHeader header = message.getHeader();
        header.setResponseTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        MoveRequest request = message.getContent();
        Character character = GameWorld.OnlineCharacter.get(session.getId());
        String fromMapId = character.getMapId();
        String destMapId = request.getDestMapId();
        GameWorld.MapCharacter.get(fromMapId).remove(character);
        GameWorld.MapCharacter.get(destMapId).add(character);
        character.setMapId(destMapId);
        MapInfo mapInfo = this.loadMapInfo(destMapId);
        OnlineInfo onlineInfo = this.loadOnlineInfo(destMapId);
        MoveResponse response = new MoveResponse();
        response.setMapInfo(mapInfo);
        response.setOnlineInfo(onlineInfo);
        WowMessage wowMessage = new WowMessage<>(header, response);
        this.sendOne(session, JSON.toJSONString(wowMessage));
    }

    /**
     * 重繪在線串列
     *
     * @param session session
     * @param message 訊息
     */
    private void handleRefreshOnlineMessage(Session session, WowMessage<RefreshOnlineRequest> message) {
        WowMessageHeader header = message.getHeader();
        header.setResponseTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        Character character = GameWorld.OnlineCharacter.get(session.getId());
        String mapId = character.getMapId();
        OnlineInfo onlineInfo = this.loadOnlineInfo(mapId);
        RefreshOnlineResponse response = new RefreshOnlineResponse();
        response.setOnlineInfo(onlineInfo);
        WowMessage wowMessage = new WowMessage<>(header, response);
        this.sendOne(session, JSON.toJSONString(wowMessage));
    }

    /**
     * 讀取地圖資訊
     *
     * @param mapId 地圖ID
     * @return
     */
    private MapInfo loadMapInfo(String mapId) {
        MapInfo mapInfo = new MapInfo();
        CommonResult commonResult = wowMapService.find(mapId);
        if (commonResult.isSuccess()) {
            WowMap wowMap = (WowMap) commonResult.getData();
            mapInfo.setWowMap(wowMap);
        }

        List<MapCoord> mapCoordList = mapCoordService.listByFromMapId(mapId);
        mapInfo.setMapCoordList(mapCoordList);

        return mapInfo;
    }

    /**
     * 讀取在線串列
     *
     * @param mapId 地圖ID
     * @return
     */
    private OnlineInfo loadOnlineInfo(String mapId) {
        OnlineInfo onlineInfo = new OnlineInfo();
        List<MapMob> mapMobList = mapMobService.listByMapId(mapId);
        onlineInfo.setMapMobList(mapMobList);
        List<Character> mapCharacterList = GameWorld.MapCharacter.get(mapId);
        onlineInfo.setMapCharacterList(mapCharacterList);
        return onlineInfo;
    }
}
MessageHandler

五、前端socket處理

  對應后端的MessageHub,前端也需要一個socket客戶端,這里我們創建一個WowClient物件,負責最外層的訊息處理邏輯,

 1 const WowClient = function () {
 2     this.cache = {
 3         version: 0,
 4         levelExpMap: []
 5     };
 6     this.cacheKey = "idlewow_client_cache";
 7     this.hubUrl = "ws://localhost:20010/hub";
 8     this.webSocket = new WebSocket(this.hubUrl);
 9     this.webSocket.onopen = function (event) {
10         console.log('WebSocket建立連接');
11         wowClient.sendLogin();
12         wowClient.loadCache();
13     };
14     this.webSocket.onmessage = function (event) {
15         console.log('WebSocket收到訊息:%c' + event.data, 'color:green');
16         var message = JSON.parse(event.data) || {};
17         console.log(message);
18         wowClient.receive(message);
19     };
20     this.webSocket.onclose = function (event) {
21         console.log('WebSocket關閉連接');
22     };
23     this.webSocket.onerror = function (event) {
24         console.log('WebSocket發生例外');
25     };
26 };

  另外,前端同樣也需要定義訊息型別,

 1 const RequestMessage = function () {
 2     this.header = {
 3         messageCode: "",
 4         requestTime: new Date(),
 5         version: "1.0"
 6     };
 7     this.content = {};
 8 };
 9 
10 const MessageCode = {
11     // 預處理
12     LoadCache: "0010",
13     // 系統命令
14     Login: "1001",
15     RefreshOnline: "1002",
16     // 玩家命令
17     Chat: "2001",
18     Move: "2002",
19     BattleMob: "2100"
20 };

  具體的訊息處理邏輯和訊息物體的創建,通過原型方法生成,完整的js檔案如下:

  1 const WowClient = function () {
  2     this.cache = {
  3         version: 0,
  4         levelExpMap: []
  5     };
  6     this.cacheKey = "idlewow_client_cache";
  7     this.hubUrl = "ws://localhost:20010/hub";
  8     this.webSocket = new WebSocket(this.hubUrl);
  9     this.webSocket.onopen = function (event) {
 10         console.log('WebSocket建立連接');
 11         wowClient.sendLogin();
 12         wowClient.loadCache();
 13     };
 14     this.webSocket.onmessage = function (event) {
 15         console.log('WebSocket收到訊息:%c' + event.data, 'color:green');
 16         var message = JSON.parse(event.data) || {};
 17         console.log(message);
 18         wowClient.receive(message);
 19     };
 20     this.webSocket.onclose = function (event) {
 21         console.log('WebSocket關閉連接');
 22     };
 23     this.webSocket.onerror = function (event) {
 24         console.log('WebSocket發生例外');
 25     };
 26 };
 27 
 28 const RequestMessage = function () {
 29     this.header = {
 30         messageCode: "",
 31         requestTime: new Date(),
 32         version: "1.0"
 33     };
 34     this.content = {};
 35 };
 36 
 37 const MessageCode = {
 38     // 預處理
 39     LoadCache: "0010",
 40     // 系統命令
 41     Login: "1001",
 42     RefreshOnline: "1002",
 43     // 玩家命令
 44     Chat: "2001",
 45     Move: "2002",
 46     BattleMob: "2100"
 47 };
 48 
 49 WowClient.prototype = {
 50     //////////////////
 51     //// 對外介面 ////
 52     //////////////////
 53     // 讀取快取
 54     loadCache: function () {
 55         let storage = localStorage.getItem(this.cacheKey);
 56         let cache = storage ? JSON.parse(storage) : null;
 57         if (!cache || (new Date().getTime() - cache.version) > 1000 * 60 * 60 * 24) {
 58             this.sendLoadCache();
 59         } else {
 60             this.cache = cache;
 61         }
 62     },
 63 
 64     //////////////////
 65     //// 訊息處理 ////
 66     //////////////////
 67 
 68     // 發送訊息
 69     send: function (message) {
 70         let msg = JSON.stringify(message);
 71         this.webSocket.send(msg);
 72     },
 73     // 接收訊息
 74     receive: function (message) {
 75         switch (message.header.messageCode) {
 76             case MessageCode.LoadCache:
 77                 this.recvLoadCache(message);
 78                 break;
 79             case MessageCode.RefreshOnline:
 80                 this.recvRefreshOnline(message);
 81                 break;
 82             case MessageCode.Login:
 83                 this.recvLogin(message);
 84                 break;
 85             case MessageCode.Chat:
 86                 this.recvChat(message);
 87                 break;
 88             case MessageCode.Move:
 89                 this.recvMove(message);
 90                 break;
 91             case MessageCode.BattleMob:
 92                 this.recvBattleMob(message);
 93                 break;
 94             default:
 95                 break;
 96         }
 97     },
 98 
 99     // 讀取快取
100     sendLoadCache: function () {
101         this.send(new RequestMessage().loadCache());
102     },
103     recvLoadCache: function (message) {
104         this.cache.levelExpMap = message.content.levelExpMap;
105         this.cache.version = new Date().getTime();
106         localStorage.setItem(this.cacheKey, JSON.stringify(this.cache));
107     },
108     // 重繪在線串列
109     sendRefreshOnline: function () {
110         this.send(new RequestMessage().refreshOnline());
111     },
112     recvRefreshOnline: function (message) {
113         this.refreshOnlineInfo(message.content.onlineInfo);
114     },
115     // 登陸
116     sendLogin: function () {
117         this.send(new RequestMessage().login());
118     },
119     recvLogin: function (message) {
120         this.refreshMapInfo(message.content.mapInfo);
121         this.refreshOnlineInfo(message.content.onlineInfo);
122     },
123     // 聊天
124     sendChat: function () {
125         this.send(new RequestMessage().chat());
126     },
127     recvChat: function (message) {
128         let channel = "【當前】";
129         let content = "<p>" + channel + message.content.senderName + ": " + message.content.message + "</p>";
130         $('.msg-chat').append(content);
131     },
132     // 移動
133     sendMove: function (mapId) {
134         this.send(new RequestMessage().move(mapId));
135     },
136     recvMove: function (message) {
137         this.refreshMapInfo(message.content.mapInfo);
138         this.refreshOnlineInfo(message.content.onlineInfo);
139     },
140     // 戰斗
141     sendBattleMob: function (mobId) {
142         this.send(new RequestMessage().battleMob(mobId));
143     },
144     recvBattleMob: async function (message) {
145         $('.msg-battle').html('');
146         let battleResult = message.content.battleResult;
147         if (battleResult.roundList) {
148             var rounds = battleResult.roundList;
149             for (var i = 0; i < rounds.length; i++) {
150                 var round = rounds[i];
151                 var content = "<p>【第" + round.round + "回合】</p>";
152                 if (round.atkStage) {
153                     content += "<p>" + round.atkStage.desc + "</p>";
154                 }
155 
156                 if (round.defStage) {
157                     content += "<p>" + round.defStage.desc + "</p>";
158                 }
159 
160                 $('.msg-battle').append(content);
161                 await this.sleep(1500);
162             }
163 
164             $('.msg-battle').append("<p><strong>戰斗結束," + battleResult.winName + " 獲得勝利!</strong></p>");
165             if (battleResult.isPlayerWin) {
166                 this.settlement(battleResult);
167             }
168 
169             let that = this;
170             await this.sleep(5000).then(function () {
171                 that.sendBattleMob(battleResult.atkId, battleResult.defId);
172             });
173         }
174     },
175 
176     //////////////////
177     //// 輔助方法 ////
178     //////////////////
179 
180     // 重繪地圖資訊
181     refreshMapInfo: function (mapInfo) {
182         let wowMap = mapInfo.wowMap;
183         let mapCoordList = mapInfo.mapCoordList;
184         $('#mapName').html(wowMap.name);
185         $('#mapDesc').html(wowMap.description);
186         $('#mapImg').attr('src', '/images/wow/map/' + wowMap.name + '.jpg');
187         let coordsHtml = '';
188         for (let index in mapCoordList) {
189             let mapCoord = mapCoordList[index];
190             coordsHtml += '<area shape="' + mapCoord.shape + '" coords="' + mapCoord.coord + '" onclick="wowClient.sendMove(\'' + mapCoord.destMapId + '\');" href="javascript:void(0);" alt="' + mapCoord.destMapName + '" title="' + mapCoord.destMapName + '"/>';
191         }
192 
193         $('#map-coords').html(coordsHtml);
194     },
195     // 重繪在線串列
196     refreshOnlineInfo: function (onlineInfo) {
197         let mapCharacterList = onlineInfo.mapCharacterList;
198         let mapMobList = onlineInfo.mapMobList;
199         // 更新在線串列
200         $('#online-all').html('');
201         $('#online-player').html('');
202         $('#online-mob').html('');
203         for (let index in mapCharacterList) {
204             let mapCharacter = mapCharacterList[index];
205             let row = '<div ><div ><label style="color: blue;">' + mapCharacter.name + '</label><label> - 等級:' + mapCharacter.level + '</label></div><div ><button type="button" style="height:14px;line-height: 14px;">私聊</button></div></div>';
206             $('#online-all').append(row);
207             $('#online-player').append(row);
208         }
209 
210         for (let index in mapMobList) {
211             let mapMob = mapMobList[index];
212             let row = '<div ><div ><label style="color: red;">' + mapMob.name + '</label><label> - 等級:' + mapMob.level + '</label></div><div ><button type="button" style="height:14px;line-height: 14px;" onclick="wowClient.sendBattleMob(\'' + mapMob.id + '\');">戰斗</button><button type="button" style="height:14px;line-height:14px;" onclick="guaji();">掛機</button></div></div>';
213             $('#online-all').append(row);
214             $('#online-mob').append(row);
215         }
216     },
217     // 戰斗結算
218     settlement: function (battleResult) {
219         $('.lbl-level').html(battleResult.settleLevel);
220         $('.lbl-exp').html(battleResult.settleExp);
221     },
222     // 休眠
223     sleep: function (milliseconds) {
224         let p = new Promise(function (resolve) {
225             setTimeout(function () {
226                 resolve();
227             }, milliseconds)
228         });
229         return p;
230     },
231     // 關閉
232     close: function () {
233         this.webSocket.close();
234     }
235 };
236 
237 RequestMessage.prototype = {
238     loadCache: function () {
239         this.header.messageCode = MessageCode.LoadCache;
240     },
241     login: function () {
242         this.header.messageCode = MessageCode.Login;
243     },
244     chat: function () {
245         this.header.messageCode = MessageCode.Chat;
246         this.content = {
247             senderId: charId,
248             senderName: charName,
249             receiverId: '',
250             receiverName: '',
251             message: $('#msg').val()
252         };
253     },
254     move: function (mapId) {
255         this.header.messageCode = MessageCode.Move;
256         this.content = {
257             destMapId: mapId
258         };
259     },
260     battleMob: function (mobId) {
261         this.header.messageCode = MessageCode.BattleMob;
262         this.content = {
263             mobId: mobId
264         };
265     },
266     refreshOnline: function () {
267         this.header.messageCode = MessageCode.RefreshOnline;
268     }
269 };
270 
271 // wow客戶端
272 window.wowClient = new WowClient();
273 
274 // 關閉視窗
275 window.onbeforeunload = function (event) {
276     wowClient.close();
277 };
278 
279 document.onkeydown = function (event) {
280     let e = event || window.event || arguments.callee.caller.arguments[0];
281     if (e.keyCode === 13 && document.activeElement.id === 'msg') {
282         wowClient.sendChat();
283     }
284 };
main.js

 

小結

  本章主要實作的socket的通信邏輯,對訊息的處理涉及了游戲的業務處理邏輯,僅簡單的講了一些,

  另外因為時隔較長,代碼裁剪作業量較大,本章僅對已完成的代碼做了粗略裁剪,源代碼的一些變動,文中將講解一些主要的,其他的就不再贅述了,

  對一些邊角的內容,代碼會變化,但文中未體現的,如有問題,可留言咨詢,

  原始碼下載地址:https://545c.com/file/14960372-438667281

  本文原文地址:https://www.cnblogs.com/lyosaki88/p/idlewow_13.html

  專案交流群:329989095 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/2219.html

標籤:其他

上一篇:【摸魚向】UE4的AI模塊探索手記(1)

下一篇:ML-Agents(七)訓練指令與訓練組態檔

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more