当前位置: 首页 > news >正文

从零开始手写mmo游戏从框架到爆炸(七)— 消息封装

导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客      

    上一篇,我们初步把消息handler 注册到了服务中,在进行后续工作之前我们需要再做一些准备工作。

        第一:把之前自己管理的bean放到spring中去管理,后面大部分的bean都通过spring来管理。

        第二:为了方便路由消费,我们要创建一个消息体方便byte字节数组传输。

Spring        

先把spring上下文变量工具整好

SpringContextHelper.java

package com.loveprogrammer.base.factory;import org.springframework.context.ApplicationContext;public class SpringContextHelper {private static ApplicationContext ac;public static void setApplicationContext(ApplicationContext ac) {SpringContextHelper.ac = ac;}public static ApplicationContext getContext() {return ac;}public static Object getBean(String name) {return ac.getBean(name);}public static Object getBean(Class clazz) {return ac.getBean(clazz);}}

CommonBootConfig.java

import com.loveprogrammer.base.factory.SpringContextHelper;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;@Configuration
public class CommonBootConfig implements ApplicationContextAware {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextHelper.setApplicationContext(applicationContext);}
}

修改 NetworkListener.java

@Component
public class NetworkListener implements INetworkEventListener {

 修改 TcpMessageStringHandler.java

@Component
public class TcpMessageStringHandler extends SimpleChannelInboundHandler<String> {private static final Logger logger = LoggerFactory.getLogger(TcpMessageStringHandler.class);@Autowiredprivate INetworkEventListener listener;//    public TcpMessageStringHandler(INetworkEventListener listener) {
//        this.listener = listener;
//    }

修改TcpServerStringInitializer.java

public class TcpServerStringInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));pipeline.addLast("decoder", new StringDecoder());pipeline.addLast("encoder", new StringEncoder());TcpMessageStringHandler handler = (TcpMessageStringHandler) SpringContextHelper.getBean(TcpMessageStringHandler.class);pipeline.addLast(handler);}}

 消息封装

创建一个类 StringMessage

package com.loveprogrammer.pojo;import com.alibaba.fastjson2.JSON;/*** @ClassName StringMessage* @Description string类型的请求的请求体* @Author admin* @Date 2024/1/31 10:35* @Version 1.0*/
public class StringMessage {/****         topicId 路由主键对应class*         tagId 路由副健,对应method*         statusC内容的长ode主要是返回内容是告诉客户端消息的状态,成功为1,其他不同的错误使用不同的错误码*         length是度,内容的长度是不可控制的,所以使用一个长度进行定义*         body是具体的内容*/private int topicId;private int tagId;private int statusCode;private int length;private String body;public StringMessage() {}//    public StringMessage(short messageId) {
//        this.messageId = messageId;
//    }public static StringMessage create(int topicId,int tagId) {StringMessage stringMessage = new StringMessage();stringMessage.setTopicId(topicId);stringMessage.setTagId(tagId);return stringMessage;}public static StringMessage create(String origin) {StringMessage stringMessage = JSON.parseObject(origin, StringMessage.class);return stringMessage;}public static StringMessage create(int length, int topicId,int tagId , int statusCode, String content) {return new StringMessage(length, topicId, tagId, statusCode, content);}private StringMessage(int length, int topicId,int tagId, int statusCode, String body) {this.length = length;this.topicId = topicId;this.tagId = tagId;this.statusCode = statusCode;this.body = body;}public int getTopicId() {return topicId;}public void setTopicId(int topicId) {this.topicId = topicId;}public int getTagId() {return tagId;}public void setTagId(int tagId) {this.tagId = tagId;}public int getStatusCode() {return statusCode;}public void setStatusCode(int statusCode) {this.statusCode = statusCode;}public int getLength() {return length;}public void setLength(int length) {this.length = length;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}@Overridepublic String toString() {return "StringMessage{" + "messageId=" + topicId + ", statusCode=" + statusCode + ", length=" + length+ ", body='" + body + '\'' + '}';}
}

  创建两个编解码类 

  MessageDecoder.java

package com.loveprogrammer.codec;import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;/*** @ClassName MessageDecoder* @Description 消息解码器* @Author admin* @Date 2024/1/31 10:43* @Version 1.0*/
public class MessageDecoder extends LengthFieldBasedFrameDecoder {//判断传送客户端传送过来的数据是否按照协议传输,头部信息的大小应该是 int+int+int = 4+4+4 = 12private static final int HEADER_SIZE = 12;private int topicId;private int tagId;private int statusCode;private int length;private String body;/***** @param maxFrameLength 解码时,处理每个帧数据的最大长度* @param lengthFieldOffset 该帧数据中,存放该帧数据的长度的数据的起始位置* @param lengthFieldLength 记录该帧数据长度的字段本身的长度* @param lengthAdjustment 修改帧数据长度字段中定义的值,可以为负数* @param initialBytesToStrip 解析的时候需要跳过的字节数* @param failFast 为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常*/public MessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);}@Overrideprotected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {if(in == null){return null;}if(in.readableBytes() < HEADER_SIZE) {throw new Exception("可读信息段比头部信息都小");}// 注意在读的过程中,readIndex的指针也在移动topicId = in.readInt();tagId = in.readInt();statusCode = in.readInt();length = in.readInt();if(in.readableBytes() < length) {throw new Exception("body获取长度" + length + ",实际长度没有达到");}ByteBuf buf = in.readBytes(length);byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);body = new String(req, ConstantValue.PROJECT_CHARSET);StringMessage stringMessage = StringMessage.create(length, topicId, tagId, statusCode, body);return stringMessage;}
}

MessageEncoder.java

package com.loveprogrammer.codec;import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;import java.nio.charset.Charset;/*** @ClassName MessageEncoder* @Description 消息编码器* @Author admin* @Date 2024/1/31 10:40* @Version 1.0*/
public class MessageEncoder extends MessageToByteEncoder<StringMessage> {@Overrideprotected void encode(ChannelHandlerContext ctx, StringMessage msg, ByteBuf out) throws Exception {if(null == msg) {throw new Exception("msg is null");}String body = msg.getBody();byte[] bodyBytes = body.getBytes(Charset.forName(ConstantValue.PROJECT_CHARSET));out.writeInt(msg.getTopicId());out.writeInt(msg.getTagId());out.writeInt(msg.getStatusCode());out.writeInt(bodyBytes.length);out.writeBytes(bodyBytes);}
}

 下一章我们来实现byte数组传输数据

上一篇:从零开始手写mmo游戏从框架到爆炸(六)— 消息处理工厂-CSDN博客

全部源码详见:

gitee : eternity-online: 多人在线mmo游戏 - Gitee.com

分支:step-06

相关文章:

从零开始手写mmo游戏从框架到爆炸(七)— 消息封装

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 上一篇&#xff0c;我们初步把消息handler 注册到了服务中&#xff0c;在进行后续工作之前我们需要再做一些准备工作。 第一&#xff1a;把之前自己管理的bean放到spring中…...

从Unity到Three.js(画线组件line)

JavaScript 0基础&#xff0c;只是照着官方文档临摹了下&#xff0c;之后有时间再进行细节学习和功能封装。 import * as THREE from three; //引入threejsconst renderer new THREE.WebGLRenderer();//创建渲染器 //设置渲染范围&#xff0c;当前撑满全屏,屏幕左上角是&…...

LCP 30. 魔塔游戏 - 力扣(LeetCode)

题目描述 小扣当前位于魔塔游戏第一层&#xff0c;共有 N 个房间&#xff0c;编号为 0 ~ N-1。每个房间的补血道具/怪物对于血量影响记于数组 nums&#xff0c;其中正数表示道具补血数值&#xff0c;即血量增加对应数值&#xff1b;负数表示怪物造成伤害值&#xff0c;即血量减…...

数据结构——单向链表和双向链表的实现(C语言版)

目录 前言 1. 链表 1.1 链表的概念及结构 1.2 链表的分类 2. 单链表接口实现 2.1 数据结构设计与接口函数声明 2.2 创建结点&#xff0c;打印&#xff0c;查找 2.3 尾插&#xff0c;头插&#xff0c;尾删&#xff0c;头删 2.4 插入或删除 2.4.1在指定位置后 2.4.2在…...

TCP和UDP相关问题(重点)(4)——4.使用TCP的协议有哪些?使用UDP的协议有哪些?

4.使用TCP的协议有哪些&#xff1f;使用UDP的协议有哪些&#xff1f; 使用TCP的协议有&#xff1a;HTTP3.0之前的HTTP协议、HTTPS、FTP、SMTP、SSH... 使用UDP的协议有&#xff1a;HTTP3.0、DNS、DHCP......

Python进阶--爬取美女图片壁纸(基于回车桌面网的爬虫程序)

目录 一、前言 二、爬取下载美女图片 1、抓包分析 a、分析页面 b、明确需求 c、抓包搜寻 d、总结特点 2、编写爬虫代码 a、获取图片页网页源代码 b、提取所有图片的链接和标题 c、下载并保存这组图片 d、 爬取目录页的各种类型美女图片的链接 e、实现翻页 三、各…...

[office] excel如何计算毛重和皮重的时间间隔 excel计算毛重和皮重时间间隔方法 #笔记#学习方法

excel如何计算毛重和皮重的时间间隔 excel计算毛重和皮重时间间隔方法 在日常工作中经常会到用excel&#xff0c;有时需要计算毛重和皮重的时间间隔&#xff0c;具体的计算方式是什么&#xff0c;一起来了解一下吧 在日常工作中经常会到用excel&#xff0c;在整理编辑过磅数据…...

Pandas 对带有 Multi-column(多列名称) 的数据排序并写入 Excel 中

Pandas 从Excel 中读取带有 Multi-column的数据 正文 正文 我们使用如下方式写入数据&#xff1a; import pandas as pd import numpy as npdf pd.DataFrame(np.array([[10, 2, 0], [6, 1, 3], [8, 10, 7], [1, 3, 7]]), columns[[Number, Name, Name, ], [col 1, col 2, co…...

如何为Kafka加上账号密码(一)

Kafka认证基本概念 一直以来&#xff0c;我们公司内网的Kafka集群都是在裸奔&#xff0c;只要知道端口号&#xff0c;任何人都能连上集群操作一番。直到有个主题莫名消失&#xff0c;才引起我们的警觉&#xff0c;是时候该考虑为它添加一套认证策略了。 认证和授权就是一对孪生…...

Elasticsearch的Index Lifecycle Management(ILM)

Elasticsearch的Index Lifecycle Management&#xff08;ILM&#xff09;功能提供了一种自动化管理索引生命周期的方式。ILM使得用户可以基于特定的条件&#xff08;如索引的年龄、大小等&#xff09;来自动执行如回滚、删除等操作&#xff0c;进而优化存储和提高查询性能。ILM…...

2、学习 Nacos 注册中心

学习 Nacos 注册中心 一、使用Nacos作为注册中心1、父pom.xml文件配置SpringCloudAlibaba的dependency-management依赖2、在微服务中添加Nacos客户端依赖3、配置Nacos服务地址 二、服务的分级存储模型1、配置实例的集群属性2、权重配置 三、命名空间 一、使用Nacos作为注册中心…...

Java 如何操作 nginx 服务器上的文件?

随着Java技术的不断发展&#xff0c;越来越多的开发人员开始使用Java来操作服务器上的文件。其中&#xff0c;如何操作nginx服务器上的文件也是许多Java开发人员所关注的重点之一。本文将介绍Java操作nginx服务器上文件的基本方法。 一、使用Java的File类 Java的File类可以用…...

时序预测 | MATLAB实现基于CNN-GRU-AdaBoost卷积门控循环单元结合AdaBoost时间序列预测

时序预测 | MATLAB实现基于CNN-GRU-AdaBoost卷积门控循环单元结合AdaBoost时间序列预测 目录 时序预测 | MATLAB实现基于CNN-GRU-AdaBoost卷积门控循环单元结合AdaBoost时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于CNN-GRU-AdaBo…...

中创ET4410 台式LCR数字电桥 简单开箱测评

最近买了一台LCR电桥&#xff0c;完善一下自己实验室的设备&#xff0c;选了中创ET4410&#xff0c;这款性价比高一点。 1199元在PDD买的&#xff0c;好像胜利的VC4090C也是找中创代工的。 ET4410介绍 本系列LCR数字电桥是采用自动平衡电桥原理设计的元件参数分析仪&#xf…...

格式化dingo返回内容

dingo api返回的内容中添加code 和 message &#xff0c;保持与异常返回的内容格式相一致。 失败会存在code 和 message &#xff0c;我们只需要关注成功的情况 非分页返回&#xff0c;可以创建一个父类controller&#xff0c;通过调用sucess方法来返回 class Controller ext…...

QGIS编译(跨平台编译)之四十六:minizip编译(Windows、Linux、MacOS环境下编译)

文章目录 一、minizip介绍二、minizip下载三、Linux下编译四、MacOS下编译五、Windows下编译一、minizip介绍 Minizip 是一个用于处理 ZIP 文件的开源库,它基于 zlib 库构建。zlib 是一个广泛使用的、免费的、开源的压缩库,提供数据压缩和解压缩功能。Minizip 扩展了 zlib 的…...

MySQL进阶查询篇(1)-索引的类型与创建

MySQL数据库索引是提高查询效率的重要手段之一。索引是一种特殊的数据结构&#xff0c;用于快速定位数据。通过创建索引&#xff0c;可以大大提高查询性能&#xff0c;减少数据库的IO操作。 MySQL数据库支持多种不同类型的索引&#xff0c;常用的索引类型包括&#xff1a; B-…...

【STL】list模拟实现

vector模拟实现 一、接口大框架函数声明速览二、结点类的模拟实现1、构造函数 三、迭代器类的模拟实现1、迭代器类存在的意义2、迭代器类的模板参数说明3、构造函数4、运算符的重载&#xff08;前置和后置&#xff09;&#xff08;1&#xff09;前置&#xff08;2&#xff09;后…...

常用的文件系统、存储类型小整理

最近接触到了五花八门的文件系统、存储类型&#xff0c;名词听得头大&#xff0c;趁假期整理学习一番~ 名称OSSFastDFSJuiceFSCIFSCephFSEFSNFS全称Object Storage Service (对象存储服务)Fast Distributed File System (快速分布式文件系统)Juice File System (Juice 文件系统…...

Java写标准输出进度条

学Java这么久了&#xff0c;突发奇想写一个 进度条 玩玩&#xff0c;下面展示一下成功吧&#xff01; Java代码实现如下 public class ProcessBar {public static void main(String[] args) {//进度条StringBuilder processBarnew StringBuilder();//进度条长度int total100;/…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...