从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件

前两张,我们已经实现了登陆界面和游戏的主界面。不过游戏主界面的数据都是在前端写死的文本,本章我们给game模块添加websocket组件,实现前后端通信,这样,前端的数据就可以从后端动态获取到了。

一、添加maven依赖

在game模块的pom中,我们添加3个依赖包如下:

 1
 2         <dependency>
 3             <groupId>org.springframeworkgroupId>
 4             <artifactId>spring-websocketartifactId>
 5             <version>5.1.6.RELEASEversion>
 6         dependency>
 7         <dependency>
 8             <groupId>org.springframeworkgroupId>
 9             <artifactId>spring-messagingartifactId>
10             <version>5.1.6.RELEASEversion>
11         dependency>
12         <dependency>
13             <groupId>javax.websocketgroupId>
14             <artifactId>javax.websocket-apiartifactId>
15             <version>1.1version>
16             <scope>providedscope>
17         dependency>

二、后端添加MessageHub

在com.idlewow.game.hub下MessageHub,这个类将主要负责接收客户端的websocket信息。代码如下:

从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件从零开始实现放置游戏(十三)——实现战斗挂机(4)添加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地图移动消息。

从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件

四、后端消息处理

在定义好消息类型后,我们即可在后端对相应的消息进行处理。代码如下:

在handleMessage方法中,我们根据header中传入的messageCode,来确定是何种消息,并转入对应的处理子方法。

比如处理地图移动的handleMoveMessage方法,在这个方法中,我们将人物信息缓存数据中的当前地图ID修改为移动后的地图ID,从原地图在线列表中移除此角色,在目标地图在线列表中添加此角色。并返回目标地图的信息给前端以便展示。

从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件
@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) wowMessage);
                break;
            case WowMessageCode.RefreshOnline:
                this.handleRefreshOnlineMessage(session, (WowMessage) wowMessage);
                break;
            case WowMessageCode.Login:
                this.handleLoginMessage(session, (WowMessage) wowMessage);
                break;
            case WowMessageCode.Chat:
                this.handleChatMessage(session, (WowMessage) wowMessage);
                break;
            case WowMessageCode.Move:
                this.handleMoveMessage(session, (WowMessage) 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 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 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 message) {
        WowMessageHeader header = message.getHeader();
        header.setResponseTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        LoadCacheResponse response = new LoadCacheResponse();
        Map 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 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 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 mapCoordList = mapCoordService.listByFromMapId(mapId);
        mapInfo.setMapCoordList(mapCoordList);

        return mapInfo;
    }

    /**
     * 读取在线列表
     *
     * @param mapId 地图ID
     * @return
     */
    private OnlineInfo loadOnlineInfo(String mapId) {
        OnlineInfo onlineInfo = new OnlineInfo();
        List mapMobList = mapMobService.listByMapId(mapId);
        onlineInfo.setMapMobList(mapMobList);
        List 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文件如下:

从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件
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 = "" + channel + message.content.senderName + ": " + message.content.message + "";
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 = "【第" + round.round + "回合】";
152                 if (round.atkStage) {
153                     content += "" + round.atkStage.desc + "";
154                 }
155
156                 if (round.defStage) {
157                     content += "" + round.defStage.desc + "";
158                 }
159
160                 $('.msg-battle').append(content);
161                 await this.sleep(1500);
162             }
163
164             $('.msg-battle').append("战斗结束," + battleResult.winName + " 获得胜利!");
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 += '';
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 = '' + mapCharacter.name + ' - 等级:' + mapCharacter.level + '私聊';
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 = '' + mapMob.name + ' - 等级:' + mapMob.level + '战斗挂机';
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

Original: https://www.cnblogs.com/lyosaki88/p/idlewow_13.html
Author: 丶谦信
Title: 从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/573420/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球