The webSocket component was added in the previous section to enable communication between the front and back ends. Then we just need to implement the various features gradually according to the business logic of the game.

In addition, in the realization of specific business logic, we found that the message object designed in the previous chapter is a little unreasonable. Due to the coarse granularity, there are few reusable parts, and the communication model here is not one request for one response mode. For example: Player A moves from map A to map B. At this point, A sends a move request. The server returns map B’s information and online list to A. Also send the latest online list to other players on map B, C,d…. Here the other player didn’t send a request, but received a response message. Therefore, the message types are regrouped into those sent by the client and those sent by the server, starting with “3000” and “6000” respectively.

Const MessageCode = {// Client sends message type CLoadCache: "30000001", // Cache load CLogin: "30001001", // log in CLoadMap: CLoadOnline: "30001003", // Read online list CChat: "30002001", // chat CMove: "30002002", // Map moving // Message type sent by the server SLoadCache: "60000001", // Cache loading SLoadMap: "60001002", // Reading map information SLoadOnline: "60001003", // Read online list SChat: "60002001", // chat};Copy the code

Players to log in

Enter the main interface of the game, socket connection, that is, send landing message. The main logic includes:

1. Load player role information (including map ID, etc.) and cache player information and session information to the server.

2. Load the map information of the player (map description, map monster list, online player list, etc.) and send it to the client

3. Notify other players on your map to update your online list

Mobile map

The player moves on the map, where the client first clicks on the anchor points corresponding to other map locations on the picture. Of course, it can also be achieved by giving the player a list menu to choose from.

The specific implementation code is similar to the following: anchor a set of coordinates to the IMG tag, and click on the graph range where the coordinates are to trigger the event. The data of the anchor point here is configured to the background and read dynamically by defining the class MapCoord.

<! - map image and anchor - > < img id = "mapImg" SRC = "/ images/wow/map / ${map. The name}. JPG" width = "100%" height = "100%" Style = "opacity: 0.8; border-radius: 10px;" usemap="#map-coords"/> <map id="map-coords" name="map-coords"> <area shape="circle" coords="35, 160, 20" onclick="wowClient.move('19');" href="javascript:void(0);" Alt =" Wild west "title=" Wild West "/> </map>Copy the code

The business logic of movement, taking player A as an example from map A to map B, includes the following:

Server:

1. Cache data in the information server (the character information data of player A, the ID of the map where player A is located is updated with the ID of map B, and the online player list of map A and B is updated)

Client:

1. Update player A’s map information to Map B

2.1) Update player A’s current map of player B’s online player list

2.2) Update the list of monsters on player A’s current map B

3. Update the online list of all players on map A (remove player A from it)

4. Update the online list of all players on map B (add player A from it)

The background message processing logic is as follows:

private void handleMoveMessage(Session session, CMoveMessage message) { Character character = GameWorld.OnlineCharacter.get(session.getId()); String fromMapId = character.getMapId(); String destMapId = message.getDestMapId(); character.setMapId(destMapId); GameWorld.MapCharacter.get(fromMapId).remove(character); GameWorld.MapCharacter.get(destMapId).add(character); GameWorld.OnlineCharacter.get(session.getId()).setMapId(destMapId); // Notify the player to update the map information this.sendLoadMap(session, destMapId); // Notify original map players to update online list this.sendLoadOnlineToMap(fromMapId); // Notify target map players to update online list this.sendLoadOnlineToMap(destMapId); } /** * @param session session * @param mapId mapId */ private void sendLoadMap(session session, String mapId) { WowMessageHeader header = new WowMessageHeader(WowMessageCode.SLoadMap); MapInfoVO mapInfoVO = this.loadMapInfo(mapId); SLoadMapMessage content = new SLoadMapMessage(); content.setMapInfo(mapInfoVO); WowMessage<SLoadMapMessage> wowMessage = new WowMessage<>(header, content); this.sendOne(session, wowMessage); } /** * @param mapId mapId */ private void sendLoadOnlineToMap(String mapId) {WowMessageHeader header = new WowMessageHeader(WowMessageCode.SLoadOnline); OnlineInfoVO onlineInfoVO = this.loadOnlineInfo(mapId); SLoadOnlineMessage content = new SLoadOnlineMessage(); content.setOnlineInfo(onlineInfoVO); WowMessage<SLoadOnlineMessage> wowMessageLoadOnline = new WowMessage<>(header, content); List<Character> mapChars = GameWorld.MapCharacter.get(mapId); for (Character mapChar : mapChars) { this.sendOne(GameWorld.OnlineSession.get(mapChar.getId()), wowMessageLoadOnline); }}Copy the code

chat

At present, there are three main chat channels: [local], [world] and [private chat].

One thing to note here is whether the chat history should be displayed on player A’s client immediately after the message is sent, or only after the message is successfully sent. I choose the latter, considering that if B is already offline when the message is sent, the message fails but the chat history is still displayed, it seems unreasonable.

When dealing with the chat logic of the local and world channels, A, as A member of the local and world online list, can normally receive messages.

During a private chat channel chat, the message is sent to USER B, and user B’s client can display it normally. However, USER A does not receive any chat messages, so the private chat messages sent by user A are not displayed. In this case, user A needs to return A message notifying the client to display chat records or notifying user B that the chat sending fails.

Considering that when A sends A chat message to B, B happens to be offline and the message fails to be sent, there should be an error message type and processing logic, which has not been implemented yet and is listed in the TODO list.

The chat message processing logic is currently as follows:

private void handleChatMessage(Session session, CChatMessage message) { Character character = GameWorld.OnlineCharacter.get(session.getId()); WowMessageHeader header = new WowMessageHeader(WowMessageCode.SChat); SChatMessage response = new SChatMessage(); response.setSendId(character.getId()); response.setSendName(character.getName()); response.setRecvId(message.getRecvId()); response.setRecvName(message.getRecvName()); response.setMessage(message.getMessage()); response.setChannel(message.getChannel()); WowMessage wowMessage = new WowMessage<>(header, response); String chatChannel = message.getChannel(); if (chatChannel.equals(GameConst.ChatChannel.Local)) { List<Character> mapChars = GameWorld.MapCharacter.get(character.getMapId()); for (Character mapChar : mapChars) { Session recvSession = GameWorld.OnlineSession.get(mapChar.getId()); if (recvSession ! = null && recvSession.isOpen()) { this.sendOne(recvSession, wowMessage); } } } else if (chatChannel.equals(GameConst.ChatChannel.World)) { this.sendAll(wowMessage); } else if (chatChannel.equals(GameConst.ChatChannel.Whisper)) { Session recvSession = GameWorld.OnlineSession.get(message.getRecvId()); if (recvSession ! = null && recvSession.isOpen()) { this.sendOne(session, wowMessage); this.sendOne(recvSession, wowMessage); } else {// todo sends error messages}} else {// Todo other channel chat to be implemented}} /** * Send messages to the specified client ** @param session Client session * @param WowMessage message object */ private void sendOne(Session Session, WowMessage wowMessage) { try { String message = JSON.toJSONString(wowMessage); session.getBasicRemote().sendText(message); } catch (Exception ex) { logger.error(ex.getMessage(), ex); } /** * @param wowMessage message object */ private void sendAll(wowMessage wowMessage) {try {String message = JSON.toJSONString(wowMessage); Collection<Session> sessions = GameWorld.OnlineSession.values(); for (Session session : sessions) { session.getBasicRemote().sendText(message); } } catch (Exception ex) { logger.error(ex.getMessage(), ex); }}Copy the code

other

In addition to the business processing logic, the code in this chapter adds a model mapping component, DozerMapper, which is primarily used for model transformation.

Because the previously defined models are database mapping models, including isDelete, createTime, createUser and other fields mainly used for system operation and maintenance, they do not need to be exposed to the client during communication, which not only increases the amount of communication data, but also exposes potential risks. Therefore, VO, view model, is created uniformly for models that need to communicate. After the conversion, it is sent to the client.

For the use of DozerMapper, check out the official documentation (recommended), which is comprehensive, in English only, or any other blog that introduces this component.

Results demonstrate

Here I have enabled Chrom and 360 browser and logged into two different accounts to test the map movement and chat functions, as shown below.

 

The summary of this chapter

This chapter mainly realizes the basic function map movement and chat, the architecture adds the dozerMapper component.

Front-end also made part of the reconstruction, but not the key, in the source can understand, will modify. Refer to the source code for details not described in detail.

Download this chapter source code address: 545c.com/file/149603…

In this paper, the original address: www.cnblogs.com/lyosaki88/p…

Project Exchange Group: 329989095 (welcome to join the group for any reason)