Fabric 服务端插件开发简述与聊天事件监听转发
原文链接:Fabric 服务端插件开发简述与聊天事件监听转发 < Ping通途说
0. 引言
以前写过Spigot的插件,非常简单,仅需调用官方封装好的Event类即可。但Fabric这边在开发时由于官方文档和现有互联网资料来看,可能会具有一定的误导性,使得开发者使用更加底层的Mixin注入开发。但后来才发现官方已经封装了这些实现方法,为了避免后续开发者踩坑,也能让开发者明白Fabric的技术实现,于是才写本文。
本文以构建一个监听游戏内玩家聊天事件、玩家进入服务器事件、玩家离开服务器事件,并遵循OnebotV11协议转发到对应服务器的Fabric服务端插件。
环境搭建与项目创建本文不再阐述,有关资料可参考:
- 设置开发环境 | Fabric 文档 - Fabric 的环境搭建
- Template mod generator | Fabric - Fabric 模板模组生成器
- Minecraft 模组编写基础 [Fabric Wiki] - Fabric的基本介绍
- 教程:更新Java - 中文 Minecraft Wiki - 各版本Minecraft最低JAVA版本要求
1. 直接调用Event方法
1.1 获取Event API方法
初学者可能会被官方文档迷惑,在监听事件 [Fabric Wiki]中,只看到了几个官方封装的方法,自己想要的类并不在以下列表中:
点击链接后又发现跳转到了项目源码中,如果对Java不熟悉的开发者可能就不明白为何会跳转到源码,然后我应该看什么。虽然 事件 | Fabric 文档 该文档中简单描述了事件监听的基本过程,但依然没有列出想要事件列表。而官方描述的 net.fabricmc.fabric.api.event
,在进入源代码后发现也只不过是所有Event的抽象方法。
那么真正的Event API到底在哪,官方文档没有像Bukkit/Spigot那样详细的列出来,我们只能借助Deepseek来查询。通过查询后,本文中我们需要监听的聊天事件,就在net.fabricmc.fabric.api.message.v1.ServerMessageEvents.CHAT_MESSAGE
中。
进入 ServerMessageEvents查看,可以看到除玩家聊天事件外,还有游戏消息事件和执行指令事件。
再通过AI我们可以找到玩家进入服务器(ServerPlayConnectionEvents.JOIN
)和玩家离开服务器(ServerPlayConnectionEvents.DISCONNECT
)的事件。而该包存在于net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents
类中,因此可以猜测,官方似乎放弃了对net.fabricmc.fabric.api.event
的维护,将所有Event分散到不同的模块中。
对于API的查询,官方可能已经放出了相关文档,但普通的开发者无法通过正常的方式通过互联网搜索或从官网中进入。在此官方改善该问题前只能通过AI辅助查找方法实现。
1.2 插件开发
找到API那开发起来就非常简单了,直接在插件初始化中注册事件:
public class EventForwardobv11 implements ModInitializer {public static final String MOD_ID = "event-forward-obv11";public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);public static ModConfig CONFIG;@Overridepublic void onInitialize() {CONFIG = ConfigManager.loadConfig();ServerMessageEvents.CHAT_MESSAGE.register((message, sender, params) -> {HttpUtil.sendGet("[" + sender.getName().getString() + "] " + message.getContent().getString());});ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {ServerPlayerEntity player = handler.player;HttpUtil.sendGet(player.getName().getString() + "加入服务器 (" + (server.getCurrentPlayerCount() + 1) + "/" + server.getMaxPlayerCount() + ")");});ServerPlayConnectionEvents.DISCONNECT.register((handler, sender) -> {ServerPlayerEntity player = handler.player;HttpUtil.sendGet(player.getName().getString() + "退出服务器");});}
}
注册完成后,我们需要将事件通过OnebotV11协议转发至群中,查看协议API,发送群消息的接口是 /send_group_msg
。对于单个小型Minecraft 服务器,请求应该不会大到哪里去,因此我们选择HTTP GET的方式向服务器发送请求,这里需要封装一个请求方法:
// GET 方法实现
class HttpUtil {public static void sendGet(String message) {ModConfig config = EventForwardobv11.CONFIG;String url = "http://"+config.obServer + ":" + config.obPort + "/send_group_msg?access_token=" + config.obToken + "&group_id=" + config.forwardGroup + "&message=" + URLEncoder.encode(message, StandardCharsets.UTF_8);try (CloseableHttpClient client = HttpClients.createDefault()) {HttpGet get = new HttpGet(url);HttpResponse response = client.execute(get);switch (response.getStatusLine().getStatusCode()) {case 401:EventForwardobv11.LOGGER.error("鉴权失败,当前请求需要Token");break;case 403:EventForwardobv11.LOGGER.error("鉴权失败,Token无效");break;case 404:EventForwardobv11.LOGGER.error("请求失败,无效接口:{}",url);}// 主要是根据状态码判断错误,正常返回结果直接忽略} catch (Exception e) {EventForwardobv11.LOGGER.error("发送消息失败:{}", e.getMessage());}}
}
关于代码中的异常处理,根据 HTTP | OneBot 11 标准只有在请求失败时才会返回200以外的状态码。服务器返回200对插件没有任何作用,因此只需关注其他状态码并处理异常即可。
服务器地址与端口是不确定的,因此需要用户来自己配置,我们来自己实现一个配置类。本插件是服务端使用的,因此无需像客户端那样展示GUI配置界面,因此可以利用GSON来生成读取配置:
class ModConfig { // 遵循Onebotv11协议发送事件public String obServer = "127.0.0.1"; // Onebotv11服务器地址public int obPort = 8080; // http端口public String obToken = ""; // 鉴权tokenpublic String forwardGroup = ""; // 要转发的群聊
}class ConfigManager {private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();private static final Path CONFIG_PATH = Paths.get("config", "event-forward-obv11.json");public static ModConfig loadConfig() {try {File configFile = CONFIG_PATH.toFile();if (!configFile.exists()) {return createDefaultConfig(); // 如果文件不存在,创建默认配置}try (FileReader reader = new FileReader(configFile)) {return GSON.fromJson(reader, ModConfig.class); // 读取配置}} catch (Exception e) {System.err.println();EventForwardobv11.LOGGER.error("无法加载配置文件,使用默认配置:{}", e.getMessage());return new ModConfig();}}public static ModConfig createDefaultConfig() {try {File configFile = CONFIG_PATH.toFile();configFile.getParentFile().mkdirs(); // 确保目录存在ModConfig defaultConfig = new ModConfig();try (FileWriter writer = new FileWriter(configFile)) {GSON.toJson(defaultConfig, writer); // 写入默认配置}return defaultConfig;} catch (Exception e) {EventForwardobv11.LOGGER.error("无法创建默认配置文件:{}", e.getMessage());return new ModConfig();}}
现在,插件全部功能已经实现。将构建后的插件装载至服务端的mod文件夹,当玩家进出服务器和聊天时就会转发事件:
2. Mixin注入简述与示例
Mixin是我刚接触Fabric就误入歧途了解的东西。Mixin的主要用途是修改基本游戏中已存在的代码,可以是通过注入自定义逻辑、移除机制或者修改值。
我是完全参考该项目进行学习与开发的: GitHub - IotaBread/player-events: Fabric mod. Sends a configurable message/command when a player joins the server.
这个项目实现了对玩家事件的监听,例如玩家进服、玩家出服、玩家死亡等事件。正好缺少监听聊天事件,我们可以通过学习项目中玩家进出服的实现,来注册一个我们自己实现的聊天事件监听器!
先来到插件入口,可以看到自定义回调在这里注册了,回调参数通过读取Config文件来获取并传递到函数中。
2.1 玩家进入服务器事件监听器
以玩家加入服务器事件监听器为例子,找到PlayerJoinCallback(api/src/main/java/me/bymartrixx/playerevents/api/event/PlayerJoinCallback.java
)。这里是自定义事件的处理模块,
随后继续查阅文件,可以发现在PlayerManagerMixin(api/src/main/java/me/bymartrixx/playerevents/api/mixin/PlayerManagerMixin.java
)中引用了以上方法。可以看到Mixin在代码中出现了,其中使用了@Injects
向Minecraft源代码中注入了捕获玩家连接的事件。
关于注入(Injects):允许你在已存在的模组中的特定位置放置自定义的代码。
其中,at
参数代表当前代码段将会在Minecraft函数中何时执行,method
参数则代表代码段会在Minecraft哪个函数中执行。可以看到代码中使用了 TAIL
,即代表在Minecraft中onPlayerConnect函数在即将返回值时运行注入的代码。
另外的if判断如果玩家离开服务器的次数少于1次,那就可以判定该玩家是第一次进入服务器,随后再将事件传递到专门的监听器中。
再讲讲代码中的 CallbackInfo
,CallbackInfo
(或 CallbackInfoReturnable
)用于控制 原方法的执行流程。它是 Mixin 注入点时的一个参数,可以:
- 取消原方法执行(
ci.cancel()
)。 - 提前返回(
ci.setReturnValue(...)
,仅限CallbackInfoReturnable
)。 - 获取方法执行状态(如是否已被取消)
什么时候用 CallbackInfo
和 CallbackInfoReturnable<T>
:
- 用
CallbackInfo
:当目标方法是void
或无返回值修改需求时。 - 用
CallbackInfoReturnable<T>
:当需要修改返回值时(如public boolean someMethod()
)。
2.2 玩家离开服务器事件监听器
再来看看玩家离开服务器事件监听器的代码:
可以看到,除调用方法名称有区别外,与玩家进入服务器的代码基本一致。
同理,再前往调用了PlayerLeaveCallback
函数的Mixin中查看:
哦豁,跟玩家进入服务器注入器代码有很大区别,多了很多东西。第一个注入器监听的是指令被执行后的事件,第二个注入器监听的则是玩家离开服务器事件。
首先是 @Shadow
,@Shadow
用于 “声明” 目标类(被 Mixin 的类)的 字段或方法,但并不实际修改它们。它只是让 Mixin 知道:“这个字段/方法在目标类中存在,我可以访问它”。不会生成任何实际代码,仅仅是一个引用标记。
在实际代码中,ServerPlayNetworkHandler
类(原版 Minecraft)有一个 player
字段,存储当前连接的玩家。@Shadow public ServerPlayerEntity player;
的作用是,告诉 Mixin:“ServerPlayNetworkHandler
里有一个 player
字段,我要用它”。这样你就可以在 Mixin 中直接通过 this.player
访问原版字段。
两个函数的at
都使用了INVOKE
,这代表当前方法将会在Minecraft对应函数被调用前执行。那target
参数又是什么呢,为何他的值又是一长串?method参数为什么也是一长串?
首先@At
的 target
是指定注入点的目标方法,它指向的是 目标类中的某个方法或字段(通常是原版 Minecraft 的方法)。指定的是 “在哪个方法调用或字段访问时触发注入”。直接看target地址:
Lnet/minecraft/server/network/ServerPlayNetworkHandler;checkForSpam()V
- 当
ServerPlayNetworkHandler.checkForSpam()
被调用时,触发注入。 - 格式:
L包路径/类名;方法名(参数类型)返回值类型
(JVM 字节码描述符)。
这段的知识面涉及JVM的标准设计第 4 章.类 File Format,字段描述符可以参考:
字段描述符 | 原名 | 描述 |
---|---|---|
B | byte | 带符号的字节 |
C | char | Basic Multilingual Plane 中的 Unicode 字符代码点,使用 UTF-16 编码 |
D | double | 双精度浮点值 |
F | float | 单精度浮点值 |
I | int | 正数 |
J | long | 长整数 |
L类名称; | reference | 类名称的实例 |
S | short | 带符号的短整型 |
Z | boolean | true 或 false |
[ | reference | 单数组维度 |
而4.3.3的方法描述符基本与字段描述符一致,只是多了一个 V
用于表示 void
。
以下是Fabric文档中的官方描述:
再来看看 method
,它指定的是要注入的目标方法,用于告诉 Mixin “你要修改的是哪个方法”,指向的是 被注入的目标方法(即你要修改的方法)。
method = "method_44356(Lnet/minecraft/class_7472;Ljava/util/Optional;)V"
因此这里的 method 表示:
- 要修改的方法是 method_44356(可能是混淆后的名称)。
- 它的参数是 ChatCommandC2SPacket(class_7472)和 Optional<MessageSignatureList>。
- 返回值是 void(V)。
那么如何找到以上我们需要的地址?这就需要反编译Minecraft代码了:配置模组开发环境 [Fabric Wiki]
Fabric已经配置好了Gradle任务,直接运行就可以了,非常的方便。
一般情况下仅需指定target类地址就可以了,如果在实际运行中出现无法找到对应方法的问题,就需要再去指定method的地址。比如例子中的checkForSpam,通过搜索源代码(客户端版本:1.21.5)发现,该方法存在于:
c ate net/minecraft/class_3244 net/minecraft/server/network/ServerPlayNetworkHandler
...
m ()V r method_43669 checkForSpam
因此如果想要让执行指令后监听器生效,那就需要将代码修改为:
@Inject(at=@At(value="INVOKE", target="Lnet/minecraft/server/network/ServerPlayNetworkHandler;checkForSpam()V"),method = "method_43669(Lnet/minecraft/class_3244;Ljava/util/Optional;)V", require = 0)
所以,指定地址的弊端就出现了,客户端一更新,插件就需要重新反编译Minecraft代码来获取对应方法地址。
2.3 一起来实现聊天事件监听!
根据以上我们了解的知识,可以模仿来创作一个新的自定义Mixin!
@Mixin(ServerPlayNetworkHandler.class)
public abstract class ServerPlayNetworkHandlerMixin {@Inject(method = "handleMessage", at = @At("HEAD"))private void onChatMessage(Text message, CallbackInfo ci) {ServerPlayerEntity player = ((ServerPlayNetworkHandler)(Object)this).player;MinecraftServer server = player.getServer();// 触发你的回调事件ActionResult result = PlayerChatCallback.EVENT.invoker().playerChat(player, server);}
}
以及自定义回调事件:
public interface PlayerChatCallback {Event<PlayerChatCallback> EVENT = EventFactory.createArrayBacked(PlayerChatCallback.class,(listeners)->(player, server)->{for (PlayerChatCallback listener: listeners){listener.playerChat(player,server);}});void playerChat(ServerPlayerEntity player, MinecraftServer server);
}
最后在初始化类中注册这个监听器:
public class MyMod implements ModInitializer {@Overridepublic void onInitialize() {// 注册聊天事件处理器PlayerChatHandler.register();}
}
可不要忘记 mixin 配置,让插件知道你创建了新的Mixin
在 src/main/resources/mixins.modid.json
中添加你的 mixin,package
为存放Mixin的目录。
{"package": "com.example.mymod.api.mixin","mixins": ["ServerPlayNetworkHandlerMixin"]
}
3. Fabric 与 Bukkit区别
1. 架构设计差异
特性 | Bukkit/Spigot/Paper | Fabric |
---|---|---|
目标定位 | 为服务器插件提供高层 API,屏蔽底层实现 | 提供底层 Mod 支持,允许直接修改 Minecraft 代码 |
事件系统 | 提供完整的 事件总线,插件直接注册监听器 | 部分事件需通过 Mixin 注入 或 Fabric API 提供的事件 |
底层访问权限 | 限制较多,只能通过 Bukkit API 操作 | 允许直接修改游戏代码(通过 Mixin) |
兼容性维护 | 由 Bukkit/Spigot 团队维护 API 兼容性 | 依赖社区和 Fabric 团队,部分 API 可能变动 |
2. 底层实现对比
Bukkit 的实现方式
- Bukkit 在服务端启动时,通过反射/CraftBukkit 修改 Minecraft 代码,插入事件触发点。
- 插件开发者无需关心注入逻辑,直接使用封装好的
Event
类。
// 伪代码:Bukkit 在底层注入事件调用
public void handleChatPacket(Packet packet) {Player player = getPlayerFromPacket(packet);PlayerChatEvent event = new PlayerChatEvent(player, packet.getMessage());Bukkit.getPluginManager().callEvent(event); // 触发事件if (!event.isCancelled()) {originalHandlePacket(packet); // 继续原逻辑}
}
Fabric 的实现方式
- Fabric 本身不修改原版代码,而是通过 Mixin 在编译时动态注入。
- 事件系统需要手动实现(或依赖 Fabric API 提供的事件)。
- 开发者可以完全控制事件行为,但需自行维护兼容性。
// 伪代码:Fabric 通过 Mixin 注入事件
@Inject(method = "handleChat", at = @At("HEAD"), cancellable = true)
private void onChat(MessagePacket packet, CallbackInfo ci) {PlayerChatEvent event = new PlayerChatEvent(player, packet.getMessage());EventManager.fireEvent(event); // 自定义事件总线if (event.isCancelled()) {ci.cancel(); // 取消原逻辑}
}
相关文章:
Fabric 服务端插件开发简述与聊天事件监听转发
原文链接:Fabric 服务端插件开发简述与聊天事件监听转发 < Ping通途说 0. 引言 以前写过Spigot的插件,非常简单,仅需调用官方封装好的Event类即可。但Fabric这边在开发时由于官方文档和现有互联网资料来看,可能会具有一定的误…...
Wise Disk Cleaner:免费系统清理工具,释放空间,提升性能
Wise Disk Cleaner是一款功能强大且完全免费的系统清理工具,专为帮助用户清理系统中的无用文件和垃圾文件而设计。它能够有效释放磁盘空间,提高系统运行速度,确保电脑始终保持最佳性能。无论是日常维护还是深度清理,Wise Disk Cle…...
排序算法之高效排序:快速排序,归并排序,堆排序详解
排序算法之高效排序:快速排序、归并排序、堆排序详解 前言一、快速排序(Quick Sort)1.1 算法原理1.2 代码实现(Python)1.3 性能分析 二、归并排序(Merge Sort)2.1 算法原理2.2 代码实现…...
主打「反激进」的一汽丰田,靠稳扎稳打的技术实现突围
文/王俣祺 导语:今年的上海车展,当新势力都在用“1000TOPS算力”“激光雷达矩阵”等参数堆砌着一个个技术神话的时候,一汽丰田却选择了一条不同的路——用“反激进”的技术哲学,在电动化和智能化的大风向中,构建独特的…...
变量赋值和数据类型
对象 Python是面相对象的编程语言,在Python一些都是对象,对象由标识、类型、值三部分组成,本质上来讲,系统分配一块内存,这块内存中存储了特定了的值,还支持特定类型的相关操作。 标识:即对象…...
【笔记】cri-docker.service和containerd
cri-docker.service 和 containerd 都是 Kubernetes 支持的容器运行时组件,但它们的架构、功能定位及与 Docker 的关系有显著差异。以下是它们的核心区别和关联: 1. 功能定位 组件核心角色是否直接支持 CRIcontainerd轻量级容器运行时,直接管…...
技术文章:解决汇川MD500系列变频器干扰问题——GRJ9000S EMC滤波器的应用
1. 引言 汇川MD500系列变频器(Variable Frequency Drive, VFD)以其高性能、宽功率范围(0.4kW-500kW)和灵活的控制方式,广泛应用于工业自动化领域,如风机、水泵、传送带和压缩机等。然而,MD500系…...
频域中的反射-信号完整性分析
频域中的反射: 频域与时域的桥梁是傅里叶变换,一个周期信号可以拆分为许多个正弦波。所谓从频域中看信号,看到的可以是很多个频域中的点,也可以是许多个正弦波。 所以在大家眼中看到的信号如图4-13所示。我们可以将该信号分解为图4-14所示信号。 让我们来思考下面这个问题:…...
window nvidia-smi命令 Failed to initialize NVML: Unknown Error
如果驱动目录下的可以执行,那可能版本原因 "C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi"复制"C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe"替换 C:\Windows\System32\nvidia-smi.exe 或者 把C:\Windows\System3…...
ubuntu 20.04 更改国内镜像源-阿里源 确保可用
镜像源是跟linux版本一一对应的,查询自己系统的版本号: 命令:lsb_release -a macw:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal macw:~$…...
Elasticsearch 学习(一)如何在Linux 系统中下载、安装
目录 一、Elasticsearch 下载二、使用 yum、dnf、zypper 命令下载安装三、使用 Docker 本地快速启动安装(ESKibana)【测试推荐】3.1 介绍3.2 下载、安装、启动3.3 访问3.4 修改配置,支持ip访问 官网地址: https://www.elastic.co/…...
PYTHON训练营DAY27
装饰器 编写一个装饰器 logger,在函数执行前后打印日志信息(如函数名、参数、返回值) logger def multiply(a, b):return a * bmultiply(2, 3) # 输出: # 开始执行函数 multiply,参数: (2, 3), {} # 函数 multiply 执行完毕&a…...
Shell脚本日志输出完整指南(AI)
一、基础日志输出方法 1. 标准输出与错误重定向 在Shell脚本中,可以使用重定向操作符将命令输出记录到日志文件: >:覆盖写入文件>>:追加写入文件2>:重定向错误输出&>:同时重定向标准…...
node.js文件系统(fs) - 创建文件、打开文件、写入数据、追加数据、读取数据、创建目录、删除目录
注意:以下所有示例均是异步语法! 注意:以下所有示例均是异步语法! 创建文件 node.js 允许我们在计算机本地创建文件,例如创建一个 word 文件: // 引入核心模块(fs) var fs require(fs)// API fs.writeF…...
关于如何本地启动xxl-job,并且整合SpringBoot
1. 本地安装xxl-job并启动 拉取xxl-job的代码 git clone gitgithub.com:xuxueli/xxl-job.git配置xxl-job数据库 拉取代码后,代码的doc/db目录下有官方配置好的sql脚本,执行里面的sql脚本至本地数据库 3. 修改xxl-job默认的数据库配置 spring.dataso…...
基于Unity的简单2D游戏开发
基于Unity的简单2D游戏开发 摘要 本文围绕基于Unity的简单2D游戏开发进行深入探讨,旨在分析其开发过程中的技术架构与实现策略。通过文献综述与市场分析,研究发现,近年来Unity引擎因其优秀的跨平台特性及可视化编程理念,成为2D游戏开发的主要工具。文章首先梳理了游戏开发的…...
在服务器上安装AlphaFold2遇到的问题(3)_cat: /usr/include/cudnn_version.h: 没有那个文件或目录
[rootlocalhost ~]# cat /usr/include/cudnn_version.h cat: /usr/include/cudnn_version.h: 没有那个文件或目录这个错误表明系统找不到 cudnn_version.h 头文件,说明 cuDNN 的开发文件(头文件)没有正确安装。以下是完整的解决方案ÿ…...
Java生产环境设限参数教学
哈哈,这个问题问得好!咱们用开餐厅的比喻来理解生产环境的四大必须设限参数,保证你听完再也不会忘!(搓手手) 1. 堆内存上限:-Xmx(厨房的最大容量) 问题:想象…...
武汉火影数字全息剧秀制作:科技与艺术的梦幻联动
全息剧秀是通过全息投影技术、多媒体互动技术、舞台表演艺术等元素深度融合的新型演出形式。 随着科技的不断进步,投影技术的更加成熟,全息剧秀作为演艺行业的创新力量,正以其独特的魅力和无限的潜力,为观众带来全新的视听盛宴。 …...
MySQL锁机制详解与加锁流程全解析
一、MySQL锁机制全景图 1.1 锁类型体系 #mermaid-svg-czUB6iJgmHuOPdN1 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-czUB6iJgmHuOPdN1 .error-icon{fill:#552222;}#mermaid-svg-czUB6iJgmHuOPdN1 .error-text{f…...
云轴科技ZStack官网上线Support AI,智能助手助力高效技术支持
5月16日,云轴科技ZStack在官网(www.zstack.io)正式上线ZStack Support AI智能助手。该系统是ZStack应用人工智能于技术支持服务领域的重要创新,基于自研ZStack AIOS平台智塔及LLMOPS技术打造。 ZStack Support AI定位为智能客服&…...
深度学习笔记23-LSTM实现火灾预测(Tensorflow)
🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者: 一、前期准备 1.导入数据 import pandas as pd import numpy as npdf_1 pd.read_csv("D:\TensorFlow1\woodpine2.csv") df_1import matplotlib.pyplot as…...
单例模式(Singleton Pattern)详解
单例模式(Singleton Pattern)详解 1. 定义与核心目标 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。核心目标: 控制实例数量:防止重复创建对象,节省资源。统一管理共享资源:如配置管理、数据库连接池、日志处理器等。2. 实现方式及对比 (…...
IntelliJ IDEA打开项目后,目录和文件都不显示,只显示pom.xml,怎样可以再显示出来?
检查.idea文件夹 如果项目目录中缺少.idea文件夹,可能导致项目结构无法正确加载。可以尝试删除项目根目录下的.idea文件夹,然后重新打开项目,IDEA会自动生成新的.idea文件夹和相关配置文件,从而恢复项目结构。 问题解决࿰…...
LongRefiner:解决长文档检索增强生成的新思路
大语言模型与RAG的应用越来越广泛,但在处理长文档时仍面临不少挑战。今天我们来聊聊一个解决这类问题的新方法——LongRefiner。 背景问题:长文档处理的两大难题 使用检索增强型生成(RAG)系统处理长文档时,主要有两个…...
Tcping详细使用教程
Tcping详细使用教程 下载地址 https://download.elifulkerson.com/files/tcping/0.39/在windows环境下安装tcping 在以上的下载地中找到exe可执行文件,其中tcping.exe适用于32位Windows系统,tcping64.exe适用于64位Windows操作系统。 其实tcping是个…...
Java + 鸿蒙双引擎:ZKmall开源商城如何定义下一代B2C商城技术标准?
在 B2C 电商领域持续革新的当下,技术架构的优劣成为决定商城竞争力的核心要素。ZKmall开源商城以其创新融合的 Java 与鸿蒙双引擎,为下一代 B2C 商城技术标准勾勒出全新蓝图,在性能、兼容性、拓展性等关键维度实现了重大突破。 一、Java 技术…...
华为云Flexus+DeepSeek征文|基于Dify平台tiktok音乐领域热门短视频分析Ai agent
前言 在当今数字化快速发展的时代,人工智能技术尤其是大模型的应用,正逐渐成为推动各行业创新与变革的关键力量。大模型凭借其强大的语言理解、生成和逻辑推理能力,为企业和开发者提供了全新的解决方案和应用可能性。然而,将这些…...
排序算法之线性时间排序:计数排序,基数排序,桶排序详解
排序算法之线性时间排序:计数排序、基数排序、桶排序详解 前言一、计数排序(Counting Sort)1.1 算法原理1.2 代码实现(Python)1.3 性能分析1.4 适用场景 二、基数排序(Radix Sort)2.1 算法原理2…...
HarmonyOS 开发之 —— 合理使用动画与转场
HarmonyOS 开发之 —— 合理使用动画与转场 谢谢关注!! 前言:上一篇文章主要介绍HarmonyOs开发之———UIAbility进阶:https://blog.csdn.net/this_is_bug/article/details/147976323?spm=1011.2415.3001.10575&sharefrom=mp_manage_link 在移动应用开发中,动画与转…...
网络流量分析 | NetworkMiner
介绍 NetworkMiner 是一款适用于Windows(也适用于Linux/Mac)的开源网络取证分析工具。它可被用作被动网络嗅探器/数据包捕获工具,也可被用于检测操作系统、会话、主机名、开放端口等,还能被用于解析pcap文件进行离线分析。点击此…...
EtherCAT转ProfiNet智能网关选型策略匹配S7-1500与CX5140通讯需求的关键参数对比
一、案例背景 随着新能源行业的迅猛发展,锂电池生产制造企业面临着日益激烈的市场竞争和不断增长的生产需求。某锂电池生产企业在扩大产能的过程中,新建了一条锂电池生产线。该生产线采用了倍福CX5140PLC作为EtherCAT协议主站,控制着涂布机、…...
适合学校使用的桌面信息看板,具有倒计时、桌面时钟、课程表、天气预报、自动新闻联播、定时关机、消息通知栏、随机点名等功能。
简介 教育时钟(Education Clock) 是一款致力于帮助学习者科学规划学习时间、提高学习效率的开源工具。由 Return-Log 团队开发,适配多平台(Windows、Mac、Linux),界面简洁直观,操作便捷。通过设…...
兰亭妙微设计:为生命科技赋予人性化的交互语言
在医疗科技日新月异的今天,卓越的硬件性能唯有匹配恰如其分的交互语言,方能真正发挥价值。作为专注于医疗UI/UX设计的专业团队,兰亭妙微设计(www.lanlanwork.com)始终相信:每一处像素的排布,都应…...
redis数据结构-12(配置 RDB 快照:保存间隔和压缩)
配置 RDB 快照:保存间隔和压缩 Redis 持久性对于确保在服务器重启或发生故障时数据不会丢失至关重要。虽然 Redis 以其内存中数据存储而闻名,但它提供了将数据持久化到磁盘的机制。本章节重点介绍其中一种机制:Redis 数据库 (RDB…...
SG7050VAN差分晶振,X1G0042810033,EPSON爱普生以太网6G晶振
产品简介 SG7050VAN差分晶振,X1G0042810033,EPSON爱普生以太网6G晶振,日本EPSON爱普生株式会社,进口晶振型号:SG7050VAN,编码为:X1G0042810033,频率为:156.250000 MHz,小体积晶振尺…...
nfs网络文件系统
nfs网络文件系统简介 NFS (Network File system ,网络文件系统)是由SUN公司研制的UNIX表示层协议,它允许网络中的计算机(不同的计算机、不同的操作系统)之间通过TCP/IP网络共享资源,主要在unix系列操作系统上使用。在NFS的应用中,本地NFS的客…...
西安前端面试
面试1 1.vue2和vue3的原理及区别 2.伪数组 3.对箭头函数怎么理解的 4.vue父子组件传值的几种方式 5.对Promise的理解 面试2 1.两个升序数组实现合并升序排序 2.数组拍平[3, [[7, [1, 5]], 4], 8, [6]] 面试3 1.let var const的区别,什么时候const能改变 …...
Linux常用命令42——tar压缩和解压缩文件
在使用Linux或macOS日常开发中,熟悉一些基本的命令有助于提高工作效率,tar 是 Linux 和 Unix 系统中用于归档文件和目录的强大命令行工具。tar 名字来自 "tape archive"(磁带归档),最初用于将文件打包到磁带…...
AML 数据集
在公开的AML(急性髓性白血病)数据集中,有几个包含图像和多组学数据的资源,且部分带有生存状态和生存时间的标签。以下是一些相关数据集: 1. TCGA-AML (The Cancer Genome Atlas - Acute Myeloid Leukemia) 描述&…...
什么是Rosetta?
Apple 提供的「动态二进制翻译器」,让基于 Intel 的 x86_64 应用/二进制在 Apple Silicon(M1/M2/M3,ARM 架构)上运行 项目说明🧠 Rosetta 2是 Apple 提供的一种「Intel → ARM 翻译器」🖥️ 功能让你的 AR…...
Redis解析
Redis解析 一、单线程模型 redis在io层面是多线程的,在数据处理层面是单线程的。 多线程一般用于: 关闭连接删除/淘汰内存网络IO 1.1 io多路复用 redis使用nio(select、poll、epoll)的方式处理socket 主线程负责接收建立连接…...
轨迹误差评估完整流程总结(使用 evo 工具)
roslaunch .launch rosbag play your_dataset.bag -r 2.0 ✅ 第二步:录制估计轨迹 bash 复制编辑 rosbag record -O traj_only.bag /aft_mapped_to_init 运行一段时间后 CtrlC 停止,生成 traj_only.bag 第三步:提取估计轨迹和真值轨迹为…...
服务器死机了需要检查哪些问题
在这个数字化的时代,服务器就像是我们信息世界的“大管家”,可要是它突然死机了,那可真是让人头疼。今天咱们就来聊聊,服务器死机了,到底需要检查哪些问题。 一、硬件问题 电源供应:检查电源是否稳定&…...
秒杀案例讲解
技术择型 Springboot 接收请求并操作 redis 和 mysqlRedis 用于缓存分布式锁Rocketmq 用于解耦 削峰,异步Mysql 用于存放真实的商品信息Mybatis 用于操作数据库的 orm 框架 架构图 spike-web(接受用户秒杀请求) pom.xml <?xml versio…...
Qt图表绘制(QtCharts)- 性能优化(13)
文章目录 1 批量替换代替追加1.1 测试11.2 测试21.3 测试3 2 开启OpenGL2.1 测试12.2 测试22.3 测试32.4 测试4 更多精彩内容👉内容导航 👈👉Qt开发 👈👉QtCharts绘图 👈👉python开发 …...
[逆向工程]DebugView捕获WPS日志?解析未运行WPS时Shell扩展加载的原因与解决方案(二十五)
[逆向工程]DebugView捕获WPS日志?解析未运行WPS时Shell扩展加载的原因与解决方案(二十五) 引言:一个“幽灵”般的日志问题 你是否在使用 DebugView 排查系统问题时,发现日志中频繁出现 WPS 相关模块(如 k…...
【打破信息差】萌新认识与入门算法竞赛
阅前须知 XCPC萌新互助进步群2️⃣:174495261 博客主页:resot (关注resot谢谢喵) 针对具体问题,应当进行具体分析;并无放之四海而皆准的方法可适用于所有人。本人尊重并支持每位学习者对最佳学习路径的自主选择。本篇所列训练方…...
Ai Agent革命:不是流程驱动,而是模型为魂
前言:AI 智能体的未来:模型才是“主旋律”,工作流只是“插曲” 在 AI 智能体的未来舞台上,模型本身才是永恒的“主旋律”,而工作流不过是短暂的“插曲”。以 Manus 为例,其基于“预先编排好的提示词与工具…...
使用CherryStudio +SiliconFlow 部署独立的deepseek+知识库
deepseek知识库,独立的deepseek 首先我们先了解 CherryStudio?SiliconFlow? CherryStudio是一个支持多平台的AI客户端,我们致力于让更多人能够享受到AI带来的便利。 简单来说,它是一个能让普通人轻松用上AI 的「万能工…...