从零开始手写mmo游戏从框架到爆炸(二)— 核心组件抽离与工厂模式创建

本文主要是介绍从零开始手写mmo游戏从框架到爆炸(二)— 核心组件抽离与工厂模式创建,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        上一章我们已经完成了一个基本netty的通信,但是netty的启动很多代码都是重复的,所以我们使用工厂模式来生成不同的ServerBootstrap。

首先创建一个新的组件core组件,和common组件,主要用于netty通信和工具类,从server中分离出来没有本质的区别,就是希望可以把功能分散在不同的组件中,后续方便多人进行协同开发(如果有多人的话)。

eternity-server的pom文件中增加依赖:

    <dependencies><dependency><groupId>com.loveprogrammer</groupId><artifactId>eternity-core</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

eternity-core的pom文件中增加依赖:

    <dependencies><dependency><groupId>com.loveprogrammer</groupId><artifactId>eternity-common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

公共变量

ConstantValue.java common:src/../constants

package com.loveprogrammer.constants;/*** @ClassName ConstantValue* @Description 静态数据类* @Author admin* @Date 2024/1/30 10:01* @Version 1.0*/
public class ConstantValue {public static final String CHANNEL_TYPE_NIO = "NIO";public static final String CHANNEL_TYPE_OIO = "OIO";public static final String PROTOCOL_TYPE_HTTP = "HTTP";public static final String PROTOCOL_TYPE_HTTPS = "HTTPS";public static final String PROTOCOL_TYPE_TCP = "TCP";public static final String PROTOCOL_TYPE_PROTOBUF = "PROTOBUF";public static final String PROTOCOL_TYPE_WEBSOCKET = "WEBSOCKET";public static final String MESSAGE_TYPE_STRING = "STRING";public static final String MESSAGE_TYPE_BYTE = "BYTE";public static final String PROJECT_CHARSET = "UTF-8";public static final int MESSAGE_CODEC_MAX_FRAME_LENGTH = 1024 * 1024;public static final int MESSAGE_CODEC_LENGTH_FIELD_LENGTH = 4;public static final int MESSAGE_CODEC_LENGTH_FIELD_OFFSET = 2;public static final int MESSAGE_CODEC_LENGTH_ADJUSTMENT = 0;public static final int MESSAGE_CODEC_INITIAL_BYTES_TO_STRIP = 0;/*** 登录和下线队列*/public static final int QUEUE_LOGIN_LOGOUT = 1;/*** 业务队列*/public static final int QUEUE_LOGIC = 2;private ConstantValue() {}}

ServerException.java common:src/../exception

public class ServerException extends Exception{private String errMsg;public ServerException(String errMsg) {super(errMsg);this.errMsg = errMsg;}public ServerException(Throwable cause) {super(cause);}
}

 下面是core中的新增代码

ServerConfig.java

/*** @ClassName ServerConfig* @Description 服务基本配置类* @Author admin* @Date 2024/2/4 15:12* @Version 1.0*/
public class ServerConfig {private static final Logger logger = LoggerFactory.getLogger(ServerConfig.class);private Integer port;private String channelType;private String protocolType;private static ServerConfig instance = null;private ServerConfig() {}public static ServerConfig getInstance() {if (instance == null) {instance = new ServerConfig();instance.init();instance.printServerInfo();}return instance;}private void init() {port = 8088;channelType = "NIO";protocolType = "TCP";}public void printServerInfo() {logger.info("**************Server INFO******************");logger.info("protocolType  : " + protocolType);logger.info("port          : " + port);logger.info("channelType   : " + channelType);logger.info("**************Server INFO******************");}public Integer getPort() {return port;}public void setPort(Integer port) {this.port = port;}public String getChannelType() {return channelType;}public void setChannelType(String channelType) {this.channelType = channelType;}public String getProtocolType() {return protocolType;}public void setProtocolType(String protocolType) {this.protocolType = protocolType;}
}

ServerBootstrapFactory.java

package com.loveprogrammer.base.factory;import com.loveprogrammer.base.bean.ServerConfig;
import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.exception.ServerException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;/*** @ClassName ServerBootstrapFactory* @Description Bootstrap工厂类* @Author admin* @Date 2024/2/4 15:13* @Version 1.0*/
public class ServerBootstrapFactory {private ServerBootstrapFactory() {}public static ServerBootstrap createServerBootstrap() throws ServerException {ServerBootstrap serverBootstrap = new ServerBootstrap();switch (ServerConfig.getInstance().getChannelType()) {case ConstantValue.CHANNEL_TYPE_NIO:EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();serverBootstrap.group(bossGroup, workerGroup);serverBootstrap.channel(NioServerSocketChannel.class);return serverBootstrap;case ConstantValue.CHANNEL_TYPE_OIO:serverBootstrap.group(new OioEventLoopGroup());serverBootstrap.channel(OioServerSocketChannel.class);return serverBootstrap;default:throw new ServerException("Failed to create ServerBootstrap,  " +ServerConfig.getInstance().getChannelType() + " not supported!");}}
}

ServerChannelFactory.java

package com.loveprogrammer.base.factory;import com.loveprogrammer.base.bean.ServerConfig;
import com.loveprogrammer.base.network.channel.tcp.str.TcpServerStringInitializer;
import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.exception.ServerException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @ClassName ServerChannelFactory* @Description channel工厂类* @Author admin* @Date 2024/2/4 15:13* @Version 1.0*/
public class ServerChannelFactory {private static final Logger logger = LoggerFactory.getLogger(ServerChannelFactory.class);public static Channel createAcceptorChannel() throws ServerException {Integer port = ServerConfig.getInstance().getPort();final ServerBootstrap serverBootstrap = ServerBootstrapFactory.createServerBootstrap();serverBootstrap.childHandler(getChildHandler());logger.info("创建Server...");try {ChannelFuture channelFuture = serverBootstrap.bind(port).sync();channelFuture.awaitUninterruptibly();if(channelFuture.isSuccess()) {return channelFuture.channel();}else{String errMsg = "Failed to open socket! Cannot bind to port: " + port + "!";logger.error(errMsg);throw new ServerException(errMsg);}} catch (Exception e) {logger.debug(port + "is bind");throw new ServerException(e);}}private static ChannelInitializer<SocketChannel> getChildHandler() throws ServerException {String protocolType = ServerConfig.getInstance().getProtocolType();if (ConstantValue.PROTOCOL_TYPE_HTTP.equals(protocolType) || ConstantValue.PROTOCOL_TYPE_HTTPS.equals(protocolType)) {} else if (ConstantValue.PROTOCOL_TYPE_TCP.equals(protocolType)) {return new TcpServerStringInitializer();} else if (ConstantValue.PROTOCOL_TYPE_WEBSOCKET.equals(protocolType)) {} else if (ConstantValue.PROTOCOL_TYPE_PROTOBUF.equals(protocolType)) {} else {}String errMsg = "undefined protocol:" + protocolType + "!";throw new ServerException(errMsg);}}

TcpMessageStringHandler.java

package com.loveprogrammer.base.network.channel.tcp.str;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @ClassName TcpMessageStringHandler* @Description tcp消息处理类* @Author admin* @Date 2024/2/4 15:16* @Version 1.0*/
public class TcpMessageStringHandler extends SimpleChannelInboundHandler<String> {private static final Logger logger = LoggerFactory.getLogger(TcpMessageStringHandler.class);@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {logger.debug("异常发生", throwable);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {super.channelRead(ctx, msg);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {logger.info("数据内容:data=" + msg);String result = "我是服务器,我收到了你的信息:" + msg;result += "\r\n";ctx.writeAndFlush(result);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {logger.info("建立连接");super.channelActive(ctx);}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {logger.info("连接断开");super.channelInactive(ctx);}
}

 TcpServerStringInitializer.java

package com.loveprogrammer.base.network.channel.tcp.str;import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;/*** @ClassName TcpServerStringInitializer* @Description TODO* @Author admin* @Date 2024/2/4 15:15* @Version 1.0*/
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());pipeline.addLast(new TcpMessageStringHandler());}}

  修改启动类EternityServerMain :

package com.loveprogrammer;import com.loveprogrammer.base.factory.ServerBootstrapFactory;
import com.loveprogrammer.base.factory.ServerChannelFactory;
import com.loveprogrammer.exception.ServerException;
import com.loveprogrammer.netty.simple.SocketServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Hello world!**/
public class EternityServerMain
{// 为了保证使用时,不需要每次都去创建logger 对象,我们声明静态常量public static final Logger LOGGER = LoggerFactory.getLogger(EternityServerMain.class);public static void main( String[] args ){LOGGER.info( "Hello World!" );// 最基本的启动方法
//        try {
//            LOGGER.info("开始启动Socket服务器...");
//            new SocketServer().run();
//        } catch (Exception e) {
//            LOGGER.error( "服务器启动失败",e);
//        }// 工厂模式启动方法try {Channel channel = ServerChannelFactory.createAcceptorChannel();channel.closeFuture().sync();} catch (Exception e) {LOGGER.error( "服务器启动失败",e);}}
}

 全部源码详见:

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

分支:step-02

上一章:

从零开始手写mmo游戏从框架到爆炸(一)— 开发环境-CSDN博客

下一章:从零开始手写mmo游戏从框架到爆炸(三)— 服务启动接口与网络事件监听器-CSDN博客

参考:

java游戏服务器开发: https://blog.csdn.net/cmqwan/category_7690685.html

这篇关于从零开始手写mmo游戏从框架到爆炸(二)— 核心组件抽离与工厂模式创建的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/680112

相关文章

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

mysql外键创建不成功/失效如何处理

《mysql外键创建不成功/失效如何处理》文章介绍了在MySQL5.5.40版本中,创建带有外键约束的`stu`和`grade`表时遇到的问题,发现`grade`表的`id`字段没有随着`studen... 当前mysql版本:SELECT VERSION();结果为:5.5.40。在复习mysql外键约

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

Window Server2016 AD域的创建的方法步骤

《WindowServer2016AD域的创建的方法步骤》本文主要介绍了WindowServer2016AD域的创建的方法步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、准备条件二、在ServerA服务器中常见AD域管理器:三、创建AD域,域地址为“test.ly”

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

基于Qt Qml实现时间轴组件

《基于QtQml实现时间轴组件》时间轴组件是现代用户界面中常见的元素,用于按时间顺序展示事件,本文主要为大家详细介绍了如何使用Qml实现一个简单的时间轴组件,需要的可以参考下... 目录写在前面效果图组件概述实现细节1. 组件结构2. 属性定义3. 数据模型4. 事件项的添加和排序5. 事件项的渲染如何使用

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.