前两张,我们已经实现了登陆界面和游戏的主界面。不过游戏主界面的数据都是在前端写死的文本,本章我们给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信息。代码如下:
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,从原地图在线列表中移除此角色,在目标地图在线列表中添加此角色。并返回目标地图的信息给前端以便展示。
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文件如下:
main.js
小结
本章主要实现的socket的通信逻辑,对消息的处理涉及了游戏的业务处理逻辑,仅简单的讲了一些。
另外因为时隔较长,代码裁剪工作量较大。本章仅对已完成的代码做了粗略裁剪。源代码的一些变动,文中将讲解一些主要的,其他的就不再赘述了。
对一些边角的内容,代码会变化,但文中未体现的,如有问题,可留言咨询。