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

使用SpringBoot进行游戏服务器开发

背景:

之前一直只考虑用JavaSe进行游戏服务器开发,目前项目使用了Spring,发现还是非常好的,好处如下:

        好处1:依赖注入非常方便,我们只使用Spring最基本的功能即可,这样子就算是有一些模块不使用Spring管理也是非常方便的,因为我现在已经能轻松控制住Spring容器的声明周期。

        好处2: 模块之间就像搭建积木即可,又相互配合。 我想支持web也是非常轻松。

        好处3: 这样子再去整合Mybatis、或者其它的一些MQ、ES之类的中间件,就太简单了。

pom.xml   // 项目中使用了lettuce,这里作为演示

       <dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>5.1.8.RELEASE</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><!--(起码1.2.48以上)因为这个版本一下存在漏洞--><version>1.2.48</version></dependency>

1.Application.java

package com.example.springbootgame.application;import org.springframework.context.ApplicationContext;public class Application {private static ApplicationContext applicationContext;public static ApplicationContext getApplicationContext() {return applicationContext;}public static void setApplicationContext(ApplicationContext applicationContext) {Application.applicationContext = applicationContext;}public static <T> T getBean(Class<T> requiredType) {if (applicationContext == null) {return null;}return applicationContext.getBean(requiredType);}}

2.RedisConfig.java // 使用Configuration引入一些自定义的Bean

package com.example.springbootgame.config;import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.sync.RedisCommands;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.time.Duration;
import java.time.temporal.ChronoUnit;@Configuration
public class RedisConfig {@Beanpublic GameRedis getGameRedis() {RedisURI redisURI = RedisURI.builder().withHost("localhost").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();RedisClient redisClient = RedisClient.create(redisURI);return new GameRedis(redisClient.connect().sync());}
}

3.GameRedis.java // 包装器模式。包装出自己的访问接口

package com.example.springbootgame.config;import io.lettuce.core.api.sync.RedisCommands;public class GameRedis {private RedisCommands redisCommands;public GameRedis(RedisCommands redisCommands) {this.redisCommands = redisCommands;}public <K, V> V get(K k) {return (V) redisCommands.get(k);}public <K, V> void set(K k, V v) {redisCommands.set(k, v);}
}

4.LoginHandler.java

package com.example.springbootgame.handler;import com.alibaba.fastjson.JSON;
import com.example.springbootgame.config.GameRedis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;import java.util.HashMap;
import java.util.Map;@Controller
public class LoginHandler {@AutowiredGameRedis gameRedis;/*** 玩家登录*/public void onCSLogin() {// 简单类型gameRedis.set("123", "abc");String v = gameRedis.get("123");System.out.println(v);// 复杂类型Data data = new Data();data.map.put("k", 6666);gameRedis.set("obj", JSON.toJSONString(data));Data obj = JSON.parseObject(gameRedis.get("obj"), Data.class);System.out.println(obj);}private static class Data {public int num = 1;public Map<String, Integer> map = new HashMap<>();@Overridepublic String toString() {return "Data{" +"num=" + num +", map=" + map +'}';}}
}

5.GameServer.java

package com.example.springbootgame;import com.example.springbootgame.application.Application;
import com.example.springbootgame.handler.LoginHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;import java.io.IOException;@SpringBootApplication
@Slf4j
public class GameServer {public static void registerShutdownHook() {Runtime.getRuntime().addShutdownHook(new Thread(() -> {log.info("gs shutdown in {}", Thread.currentThread().getName());// 测试bean的获取LoginHandler loginHandler = Application.getBean(LoginHandler.class);loginHandler.onCSLogin();// 关闭Spring容器ApplicationContext applicationContext = Application.getApplicationContext();if (applicationContext != null) {ConfigurableApplicationContext cac = (ConfigurableApplicationContext) applicationContext;cac.close();}}, "ShutdownHook-GameServer-Thread"));}public static void main(String[] args) {registerShutdownHook();// 启动Spring容器ApplicationContext applicationContext = SpringApplication.run(GameServer.class, args);Application.setApplicationContext(applicationContext);// 初始化各个模块,如:进行handler的扫描// 阻塞关服try {System.in.read();} catch (IOException e) {log.error("exception", e);}}}
  .   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::               (v2.7.17)2023-11-18 20:56:48.393  INFO 7056 --- [           main] com.example.springbootgame.GameServer    : Starting GameServer using Java 11.0.11 on DESKTOP-JTMBOEI with PID 7056 (D:\2_test_java\SpringBootGame\target\classes started by Administrator in D:\2_test_java\SpringBootGame)
2023-11-18 20:56:48.397  INFO 7056 --- [           main] com.example.springbootgame.GameServer    : No active profile set, falling back to 1 default profile: "default"
2023-11-18 20:56:48.960  INFO 7056 --- [           main] io.lettuce.core.EpollProvider            : Starting without optional epoll library
2023-11-18 20:56:48.961  INFO 7056 --- [           main] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
2023-11-18 20:56:49.492  INFO 7056 --- [           main] com.example.springbootgame.GameServer    : Started GameServer in 1.512 seconds (JVM running for 2.701)
1
2023-11-18 20:56:50.723  INFO 7056 --- [meServer-Thread] com.example.springbootgame.GameServer    : gs shutdown in ShutdownHook-GameServer-Thread
abc
Data{num=1, map={k=6666}}

相关文章:

使用SpringBoot进行游戏服务器开发

背景&#xff1a; 之前一直只考虑用JavaSe进行游戏服务器开发&#xff0c;目前项目使用了Spring&#xff0c;发现还是非常好的&#xff0c;好处如下: 好处1:依赖注入非常方便&#xff0c;我们只使用Spring最基本的功能即可&#xff0c;这样子就算是有一些模块不使用Spring管理…...

数据结构——树状数组

文章目录 前言问题引入问题分析树状数组lowbit树状数组特性初始化一个树状数组更新操作前缀和计算区间查询 总结 前言 原题的连接 最近刷leetcode的每日一题的时候&#xff0c;遇到了一个区间查询的问题&#xff0c;使用了一种特殊的数据结构树状数组&#xff0c;学习完之后我…...

Untiy 使用RotateAround()方法实现物体围绕某个点或者某个物体旋转

Untiy 实现物体围绕指定点或者某个物体旋转&#xff0c;可使用RotateAround()方法。 语法&#xff1a; public void RotateAround(Vector3 point, Vector3 axis, float angle); 其中&#xff0c;point:旋转中心点位置&#xff1b; axis:要围绕的轴&#xff0c;如x,y,z angel…...

图像分类(五) 全面解读复现ResNet

解读 Abstract—摘要 翻译 更深的神经网络往往更难以训练&#xff0c;我们在此提出一个残差学习的框架&#xff0c;以减轻网络的训练负担&#xff0c;这是个比以往的网络要深的多的网络。我们明确地将层作为输入学习残差函数&#xff0c;而不是学习未知的函数。我们提供了非…...

使用html2canvas转换table为图片时合并单元格rowspan失效,无边框显示问题解决(React实现)

最近使用 html2canvas导出Table表单为图片&#xff0c;但是转换出的图片被合并的单元格没有显示边框 查了原因是因为我为tr设置了背景色&#xff0c;然后td设置了rowspan&#xff0c;设置了rowspan的单元格就会出现边框不显示的问题。 解决方法就是取消tr的背景色&#xff0c;然…...

pandas教程:Time Series Basics 时间序列基础

文章目录 11.2 Time Series Basics&#xff08;时间序列基础&#xff09;1 Indexing, Selection, Subsetting&#xff08;索引&#xff0c;选择&#xff0c;取子集&#xff09;2 Time Series with Duplicate Indices&#xff08;重复索引的时间序列&#xff09; 11.2 Time Seri…...

【C++初阶】STL详解(四)vector的模拟实现

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…...

Zookeeper学习笔记(2)—— Zookeeper API简单操作

前置知识&#xff1a;Zookeeper学习笔记&#xff08;1&#xff09;—— 基础知识-CSDN博客 Zookeeper集群搭建部分 前提&#xff1a;保证zookeeper集群处于启动状态 环境搭建 依赖配置 <dependencies><dependency><groupId>junit</groupId><arti…...

YOLOv8-Seg改进:Backbone改进 |Next-ViT堆栈NCB和NTB 构建先进的CNN-Transformer混合架构

🚀🚀🚀本文改进:Next-ViT堆栈NCB和NTB 构建先进的CNN-Transformer混合架构,包括nextvit_small, nextvit_base, nextvit_large,相比较yolov8-seg各个版本如下: layersparametersgradientsGFLOPsnextvit_small61033841075...

DocCMS keyword SQL注入漏洞复现 [附POC]

文章目录 DocCMS keyword SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 DocCMS keyword SQL注入漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测…...

利用(Transfer Learning)迁移学习在IMDB数据上训练一个文本分类模型

1. 背景 有些场景下&#xff0c;开始的时候数据量很小&#xff0c;如果我们用一个几千条数据训练一个全新的深度机器学习的文本分类模型&#xff0c;效果不会很好。这个时候你有两种选择&#xff0c;1.用传统的机器学习训练&#xff0c;2.利用迁移学习在一个预训练的模型上训练…...

pom.xml格式化快捷键

在软件开发和编程领域&#xff0c;"格式化"通常指的是将代码按照一定的规范和风格进行排列&#xff0c;以提高代码的可读性和维护性。格式化代码有助于使代码结构清晰、统一&#xff0c;并符合特定的编码规范。 格式化可以包括以下方面&#xff1a; 缩进&#xff1a…...

【短文】【踩坑】可以在Qt Designer给QTableWidge添加右键菜单吗?

2023年11月18日&#xff0c;周六上午 今天早上在网上找了好久都没找到教怎么在Qt Designer给QTableWidge添加右键菜单的文章 答案是&#xff1a;不可以 在Qt Designer中无法直接为QTableWidget添加右键菜单。 Qt Designer主要用于创建界面布局和设计&#xff0c;无法直接添加…...

Git常用配置

git log 美化输出 全局配置参数 git config --global alias.lm "log --no-merges --color --dateformat:%Y-%m-%d %H:%M:%S --authorghost --prettyformat:%Cred%h%Creset - %Cgreen(%cd)%C(yellow)%d%Cblue %s %C(bold blue)<%an>%Creset --abbrev-commit"…...

力扣每日一题-数位和相等数对的最大和-2023.11.18

力扣每日一题&#xff1a;数位和相等数对的最大和 开篇 这道每日一题还是挺需要思考的&#xff0c;我绕晕了好久&#xff0c;根据题解的提示才写出来。 题目链接:2342.数位和相等数对的最大和 题目描述 代码思路 1.创建一个数组存储每个数位的数的最大值&#xff0c;创建一…...

【win32_001】win32命名规、缩写、窗口

整数类型 bool类型 使用注意&#xff1a; 一般bool 的false0&#xff1b;true1 | 2 | …|n false是为0&#xff0c;true是非零 不建议这样用&#xff1a; if (result TRUE) // Wrong! 因为result不一定只返回1&#xff08;true&#xff09;&#xff0c;当返回2时&#xff0c…...

机器学习第8天:SVM分类

文章目录 机器学习专栏 介绍 特征缩放 示例代码 硬间隔与软间隔分类 主要代码 代码解释 非线性SVM分类 结语 机器学习专栏 机器学习_Nowl的博客-CSDN博客 介绍 作用&#xff1a;判别种类 原理&#xff1a;找出一个决策边界&#xff0c;判断数据所处区域来识别种类 简单…...

AI工具合集

网站&#xff1a;未来百科 | 为发现全球优质AI工具产品而生 (6aiq.com) 如今&#xff0c;AI技术涉及到了很多领域&#xff0c;比如去水印、一键抠图、图像处理、AI图像生成等等。站长之家之前也分享过一些&#xff0c;但是在网上要搜索找到它们还是费一些功夫。 今天发现了一…...

代码随想录算法训练营Day 54 || 392.判断子序列、115.不同的子序列

392.判断子序列 力扣题目链接(opens new window) 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;&quo…...

C 语言 gets()和puts()

C 语言 gets()和puts() gets()和puts()在头文件stdio.h中声明。这两个函数用于字符串的输入/输出操作。 C gets()函数 gets()函数使用户可以输入一些字符&#xff0c;然后按Enter键。 用户输入的所有字符都存储在字符数组中。 空字符将添加到数组以使其成为字符串。 gets()允…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

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

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

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...