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

news2024/11/25 23:01:21

        上一章我们已经完成了一个基本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);

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {
        logger.debug("异常发生", throwable);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, msg);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        logger.info("数据内容:data=" + msg);
        String result = "我是服务器,我收到了你的信息:" + msg;
        result += "\r\n";
        ctx.writeAndFlush(result);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("建立连接");
        super.channelActive(ctx);
    }

    @Override
    public 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> {

    @Override
    protected 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1433587.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day31 JS执行机制

目录 前言同步和异步JS执行机制 前言 JavaScript语言的一大特点是单线程。 JavaScript是为处理页面中用户的交互&#xff0c;以及操作DOM而诞生的。比如对某个DOM元素进行添加和删除操作&#xff0c;不能同时进行&#xff0c;应该先进行添加再继续删除。 示例&#xff08;解…

服务器性能监控管理方法及工具

服务器是组织数据中心的主干&#xff0c;无论是优化的用户体验&#xff0c;还是管理良好的资源&#xff0c;服务器都能为您完成所有工作&#xff0c;保持服务器随时可用和可访问对于面向业务的应用程序和服务以最佳水平运行至关重要。 理想的服务器性能需要主动监控物理和虚拟…

为电子表格嵌入数据库,Excel/WPS一键升级为管理系统

将Excel表格转化为管理系统&#xff0c;这款工具能够实现只需导入表格数据&#xff0c;即可自动生成相应的软件和APP。 表格办公的烦恼&#xff0c;有遇到吧&#xff1f; 对于具有一定规模的企业而言&#xff0c;各类表格如同繁星般众多&#xff0c;既有日常使用的常规表格&a…

力扣刷题之旅:进阶篇(一)

力扣&#xff08;LeetCode&#xff09;是一个在线编程平台&#xff0c;主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目&#xff0c;以及它们的解题代码。 --点击进入刷题地址 题目1&#xff1a;三数之和 题目描述&#xff1a; 给定一个包含n个…

轻型民用无人机驾驶航空器安全操控——理论考试多旋翼部分笔记

今天已经可以在线考取轻型民用无人机驾驶航空器执照了&#xff0c;所以我也在在线观看完视频之后整理了如下的知识点&#xff0c;所有知识点全部来自UOM平台。 目录 航空器知识 &#xff08;1&#xff09;多旋翼民用无人驾驶航空器螺旋桨的作用 &#xff08;2&#x…

[office] Excel表格中的日期怎么快速标注双休日- #经验分享#职场发展#职场发展

Excel表格中的日期怎么快速标注双休日? Excel表格中的日期怎么快速标注双休日&#xff1f;excel表格中的数据想要标注出双休日&#xff0c;该怎么给双休日填充黄色背景呢&#xff1f;下面我们就来看看详细的教程&#xff0c;需要的朋友可以参考下 excel表格中的数据&#xff…

ReactNative实现的横向滑动条

OK&#xff0c;我们先看下效果图 注意使用到了两个库 1.react-native-linear-gradient 2.react-native-gesture-handler ok&#xff0c;我们看下面的代码 import {Image, TouchableWithoutFeedback, StyleSheet, View} from react-native; import LinearGradient from reac…

[linux] kolla-ansible 部署的openstack 修改mariadb默认端口号

kolla-ansible 部署前修改global.yml #freezer_database_backend: "mariadb" database_port: 9306 mariadb_port: 9306如果已经部署成功&#xff0c;直接修改配置文件里的端口号重启是没有用的&#xff0c;怀疑内部做了缓存&#xff0c;查看openstack 使用的memcach…

Abap与eCharts

一&#xff0c;简介 利用html与eCharts来绘图&#xff0c;然后用cl_gui_html_viewer将html呈现到abap屏幕中。 二&#xff0c;使用eCharts画图 在一个文件夹中准备如下文件&#xff0c;index.html和echarts.js是必须的&#xff0c;data.json(作为数据源)和jquery.js如果用到就可…

windows安装Visual Studio Code,配置C/C++运行环境(亲测可行)

一.下载 Visual Studio Code https://code.visualstudio.com/ 二.安装 选择想要安装的位置: 后面的点击下一步即可。 三.下载编译器MinGW vscode只是写代码的工具&#xff0c;使用编译器才能编译写的C/C程序&#xff0c;将它转为可执行文件。 MinGW下载链接&#xff1a;…

【节选】Go语言的100个错误使用场景|数据类型

Data types &#x1f31f; 章节概述&#xff1a; 基本类型涉及的常见错误 掌握 slice 和 map 的基本概念&#xff0c;避免使用时产生 bug 值的比较 低效的切片初始化&#xff08;#21&#xff09; 实现一个 conver 方法&#xff0c;将一个切片 Foo 转换成另一个类型的切片 Ba…

在线视频格式转换,就是这么简单!(免费)

随着数字化时代的发展&#xff0c;我们在日常生活中越来越频繁地与各种视频文件打交道。然而&#xff0c;不同设备和平台对于视频格式的支持可能存在差异&#xff0c;这就导致了我们有时需要进行视频格式的转换&#xff0c;以确保视频在各种环境中流畅播放。而幸运的是&#xf…

简单使用阿里云OSS对象存储

首先我们先去阿里云控制台开通oss对象存储&#xff08;阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台&#xff09; 这篇文件是借鉴至&#xff08;教你三分钟上手阿里云OOS上传操作_阿里云定时上传怎么使用-CSDN博客&#xff09;的&#xff0c;源码也给了&a…

Python代码混淆工具,Python源代码保密、加密、混

引言 Python作为一种高级脚本语言&#xff0c;便捷的语法和丰富的库使它成为众多开发者的首选。然而&#xff0c;有时候我们希望保护我们的Python源代码&#xff0c;避免被他人轻易获取和篡改。为了实现这一目标&#xff0c;我们可以采取代码混淆的技术手段。本文将介绍Python…

深度学习系列55:深度学习加速技术概述

总体有两个方向&#xff1a;模型优化 / 框架优化 1. 模型优化 1.1 量化 最常见的量化方法为线性量化&#xff0c;权重从float32量化为int8&#xff0c;将输入数据映射在[-128,127]的范围内。在 nvdia gpu&#xff0c;x86、arm 和 部分 AI 芯片平台上&#xff0c;均支持 8bit…

Vue中keep-alive的作用、原理及应用场景

在进行Vue开发的过程中&#xff0c;我们经常会遇到需要进行组件缓存的场景&#xff0c;这时候Vue提供的keep-alive组件就派上了用场。keep-alive组件是Vue内置的一个抽象组件&#xff0c;它可以将其包裹的组件进行缓存&#xff0c;提高组件的性能&#xff0c;同时也可以节省服务…

docker-学习-3

docker 学习第三天 docker 学习第三天1. 回顾一下1.1. 对比图1.2. docker和虚拟机的区别1.3. 在容器化部署中&#xff0c;为什么有些场景更适合选择Docker而非虚拟机&#xff1f;1.4. 有哪些场景适合选择虚拟机而不是Docker进行部署&#xff1f;1.5. 虚拟机和Docker在性能和资源…

Python 数据分析(PYDA)第三版(二)

原文&#xff1a;wesmckinney.com/book/ 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 四、NumPy 基础知识&#xff1a;数组和向量化计算 原文&#xff1a;wesmckinney.com/book/numpy-basics 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 此开放访问网络版…

Java tomcat 使用spring-task,实现定时任务功能

前言 今天接触到一个需求&#xff0c;需要添加一个定时任务功能&#xff0c;第一反应是启动类EnableScheduling、定时任务方法使用Scheduled实现&#xff0c;导入项目后才发现&#xff0c;这个项目是ssm整合框架的tomcat项目&#xff0c;没有启动类&#xff0c; 于是改变了思路…

protoc结合go完成protocol buffers协议的序列化与反序列化

下载protoc编译器 下载 https://github.com/protocolbuffers/protobuf/releases ps: 根据平台选择需要的编译器&#xff0c;这里选择windows 解压 加入环境变量 安装go专用protoc生成器 https://blog.csdn.net/qq_36940806/article/details/135017748?spm1001.2014.3001.…