1. 程式人生 > >從零開始實現放置遊戲(十三)——實現戰鬥掛機(4)新增websocket元件

從零開始實現放置遊戲(十三)——實現戰鬥掛機(4)新增websocket元件

  前兩張,我們已經實現了登陸介面和遊戲的主介面。不過遊戲主介面的資料都是在前端寫死的文字,本章我們給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 = "/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 onError(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 class="layui-row"><div class="layui-col-md9"><label style="color: blue;">' + mapCharacter.name + '</label><label> - 等級:' + mapCharacter.level + '</label></div><div class="layui-col-md3"><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 class="layui-row"><div class="layui-col-md9"><label style="color: red;">' + mapMob.name + '</label><label> - 等級:' + mapMob.level + '</label></div><div class="layui-col-md3"><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-437680954

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

  專案交流群:329989095&n