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

Fabric 服务端插件开发简述与聊天事件监听转发

原文链接:Fabric 服务端插件开发简述与聊天事件监听转发 < Ping通途说

0. 引言

以前写过Spigot的插件,非常简单,仅需调用官方封装好的Event类即可。但Fabric这边在开发时由于官方文档和现有互联网资料来看,可能会具有一定的误导性,使得开发者使用更加底层的Mixin注入开发。但后来才发现官方已经封装了这些实现方法,为了避免后续开发者踩坑,也能让开发者明白Fabric的技术实现,于是才写本文。

本文以构建一个监听游戏内玩家聊天事件、玩家进入服务器事件、玩家离开服务器事件,并遵循OnebotV11协议转发到对应服务器的Fabric服务端插件。

环境搭建与项目创建本文不再阐述,有关资料可参考:

  1. 设置开发环境 | Fabric 文档 - Fabric 的环境搭建
  2. Template mod generator | Fabric -  Fabric 模板模组生成器
  3. Minecraft 模组编写基础 [Fabric Wiki] - Fabric的基本介绍
  4. 教程:更新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次,那就可以判定该玩家是第一次进入服务器,随后再将事件传递到专门的监听器中。

再讲讲代码中的 CallbackInfoCallbackInfo(或 CallbackInfoReturnable)用于控制 原方法的执行流程。它是 Mixin 注入点时的一个参数,可以:

  • 取消原方法执行ci.cancel())。
  • 提前返回ci.setReturnValue(...),仅限 CallbackInfoReturnable)。
  • 获取方法执行状态(如是否已被取消)

什么时候用 CallbackInfoCallbackInfoReturnable<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,字段描述符可以参考:

字段描述符原名描述
Bbyte带符号的字节
CcharBasic Multilingual Plane 中的 Unicode 字符代码点,使用 UTF-16 编码
Ddouble双精度浮点值
Ffloat单精度浮点值
Iint正数
Jlong长整数
L类名称;reference类名称的实例
Sshort带符号的短整型
Zbooleantrue 或 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/PaperFabric
目标定位为服务器插件提供高层 API,屏蔽底层实现提供底层 Mod 支持,允许直接修改 Minecraft 代码
事件系统提供完整的 事件总线,插件直接注册监听器部分事件需通过 Mixin 注入 或 Fabric API 提供的事件
底层访问权限限制较多,只能通过 Bukkit API 操作允许直接修改游戏代码(通过 Mixin)
兼容性维护由 Bukkit/Spigot 团队维护 API 兼容性依赖社区和 Fabric 团队,部分 API 可能变动

2. 底层实现对比

Bukkit 的实现方式

  1. Bukkit 在服务端启动时,通过反射/CraftBukkit 修改 Minecraft 代码,插入事件触发点。
  2. 插件开发者无需关心注入逻辑,直接使用封装好的 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 的实现方式

  1. Fabric 本身不修改原版代码,而是通过 Mixin 在编译时动态注入。
  2. 事件系统需要手动实现(或依赖 Fabric API 提供的事件)。
  3. 开发者可以完全控制事件行为,但需自行维护兼容性。

// 伪代码: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 服务端插件开发简述与聊天事件监听转发

原文链接&#xff1a;Fabric 服务端插件开发简述与聊天事件监听转发 < Ping通途说 0. 引言 以前写过Spigot的插件&#xff0c;非常简单&#xff0c;仅需调用官方封装好的Event类即可。但Fabric这边在开发时由于官方文档和现有互联网资料来看&#xff0c;可能会具有一定的误…...

认识Docker/安装Docker

一、认识Docker Docker的定义 Docker 是一个开源的应用容器引擎&#xff0c;允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中。容器化技术使得应用可以在任何支持 Docker 的环境中运行&#xff0c;确保环境一致性。 Docker的核心组件 Docker Engine&#xff1a;负责…...

电商物流管理优化:从网络重构到成本管控的全链路解析

大家好&#xff0c;我是沛哥儿。作为电商行业&#xff0c;我始终认为物流是电商体验的“最后一公里”&#xff0c;更是成本控制的核心战场。随着行业竞争加剧&#xff0c;如何通过物流网络优化实现降本增效&#xff0c;已成为电商企业的必修课。本文将从物流网络的各个环节切入…...

Unity:延迟执行函数:Invoke()

目录 Unity 中的 Invoke() 方法详解 什么是 Invoke()&#xff1f; 基本使用方法 使用要点 延伸功能 ❗️Invoke 的局限与注意事项 在Unity中&#xff0c;延迟执行函数是游戏逻辑中常见的需求&#xff0c;比如&#xff1a; 延迟切换场景 延迟播放音效或动画 给玩家时间…...

移植RTOS,发现任务栈溢出怎么办?

目录 1、硬件检测方法 2、软件检测方法 3、预防堆栈溢出 4、处理堆栈溢出 在嵌入式系统中&#xff0c;RTOS通过管理多个任务来满足严格的时序要求。任务堆栈管理是RTOS开发中的关键环节&#xff0c;尤其是在将RTOS移植到新硬件平台时。堆栈溢出是嵌入式开发中常见的错误&am…...

k8s部署实战-springboot应用部署

在 Kubernetes 上部署 SpringBoot 应用实战指南 前言 本文将详细介绍如何将一个 SpringBoot 应用部署到 Kubernetes 集群中,包括制作镜像、编写部署文件、创建服务等完整步骤。 准备工作 1. 示例 SpringBoot 应用 假设我们有一个简单的 SpringBoot 应用,提供 REST API 服…...

【设计模式】- 结构型模式

代理模式 给目标对象提供一个代理以控制对该对象的访问。外界如果需要访问目标对象&#xff0c;需要去访问代理对象。 分类&#xff1a; 静态代理&#xff1a;代理类在编译时期生成动态代理&#xff1a;代理类在java运行时生成 JDK代理CGLib代理 【主要角色】&#xff1a; 抽…...

《Vuejs设计与实现》第 5 章(非原始值响应式方案) 中

目录 5.4 合理触发响应 5.5 浅响应与深响应 5.6 只读和浅只读 5.4 合理触发响应 为了合理触发响应,我们需要处理一些问题。 首先,当值没有变化时,我们不应该触发响应: const obj = { foo: 1 } const p = new Proxy(obj, { /* ... */ })effect(() => {console.log(p…...

rk3576 gstreamer opencv

安装gstreamer rk3588使用gstreamer推流_rk3588 gstreamer-CSDN博客 rk3588使用gstreamer推流_rk3588 gstreamer-CSDN博客 Installing on Linux sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-pl…...

数据服务共享平台方案

该文档聚焦数据服务共享平台方案,指出传统大数据管理存在数据定义不统一、开发困难、共享不足等挑战,提出通过自服务大数据平台实现数据 “采、存、管、用” 全流程优化,涵盖数据资产管理、自助数据准备、服务开发与共享、全链路监控等功能,并通过国家电网、东方航空、政府…...

skywalking使用教程

skywalking使用教程 一、介绍 skywalking 1.1 概念 skywalking是分布式系统的应用程序性能监视工具&#xff0c;专为微服务、云原生架构和基于容器&#xff08;Docker、K8s、Mesos&#xff09;架构而设计。SkyWalking 是观察性分析平台和应用性能管理系统&#xff0c;提供分布…...

C 语 言 - - - 简 易 通 讯 录

C 语 言 - - - 简 易 通 讯 录 代 码 全 貌 与 功 能 介 绍通 讯 录 的 功 能 说 明通 讯 录 效 果 展 示代 码 详 解contact.hcontact.ctest.c 总 结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验 助 你 入 门 C 语 言 &#x1f4a1;个 …...

大模型MCP之UV安装使用

1.Windows安装 1.1 pip安装 pip install uv -i https://pypi.tuna.tsinghua.edu.cn/simple如果需要centos安装pip sudo yum install python3-pipCentOS 8开始使用dnf作为包管理器&#xff1a; sudo dnf install python3-pip对于基于Debian的系统&#xff08;如Ubuntu&#…...

【C++】多线程和多进程

在C++中,多线程通信(同一进程内的线程间交互)和进程间通信(IPC,不同进程间的数据交换)是构建并发系统的核心技术。以下是两种通信机制的详细介绍和典型实现: 一、多线程通信(线程间同步与数据共享) 1. 共享内存与同步原语 通过全局变量或对象成员变量实现数据共享,…...

Vue百日学习计划Day16-18天详细计划-Gemini版

重要提示&#xff1a; 番茄时钟&#xff1a; 每个番茄钟为25分钟学习&#xff0c;之后休息5分钟。每完成4个番茄钟&#xff0c;进行一次15-30分钟的长休息。动手实践&#xff1a; DOM 操作和事件处理的理解高度依赖于实际编码。请务必在浏览器中创建 HTML 页面&#xff0c;并配…...

从验证码绕过到信息轰炸:全面剖析安全隐患与防范策略

在数字化交互场景中&#xff0c;验证码作为区分人类操作与自动化程序的核心屏障&#xff0c;广泛应用于用户身份核验、操作权限确认等关键环节。其设计初衷是通过人机识别机制&#xff0c;保障信息系统交互的安全性与可控性。然而&#xff0c;当验证码验证机制出现异常突破&…...

机器学习知识自然语言处理入门

一、引言&#xff1a;当文字遇上数学 —— 自然语言的数字化革命 在自然语言处理&#xff08;NLP&#xff09;的世界里&#xff0c;计算机要理解人类语言&#xff0c;首先需要将文字转化为数学向量。早期的 One-Hot 编码如同给每个词语分配一个唯一的 “房间号”&#xff0c;例…...

LeetCode 820 单词的压缩编码题解

LeetCode 820 单词的压缩编码题解 题目描述 题目链接 给定一个单词列表&#xff0c;将其编码为一个索引字符串S&#xff0c;格式为"单词1#单词2#…"。要求当某个单词是另一个单词的后缀时&#xff0c;该单词可以被省略。求最终编码字符串的最小长度。 解题思路 逆…...

论信息系统项目的范围管理

论信息系统项目的范围管理 前言一、规划范围管理&#xff0c;收集需求二、定义范围三、创建工作分解结构四、确认范围五、控制范围 前言 为了应对烟草零售客户数量大幅度增长所带来的问题&#xff0c;切实履行控烟履约的相关要求&#xff0c;同时也为了响应国务院“放管服”政策…...

MySQL数据库——支持远程IP访问的设置方法总结

【系列专栏】&#xff1a;博主结合工作实践输出的&#xff0c;解决实际问题的专栏&#xff0c;朋友们看过来&#xff01; 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实…...

Pageassist安装(ollama+deepseek-r1)

page-assist网站&#xff1a;https://github.com/n4ze3m/page-assist 首先电脑配置node.js&#xff0c;管理员打开命令窗口输入下面命令下载bun npm install -g buncd 到你想要安装page-assist的地方&#xff08;推荐桌面&#xff09; 输入下列命令 git clone https://gith…...

2025年渗透测试面试题总结-安恒[实习]安全服务工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 安恒[实习]安全服务工程师 1. SQLMap爆出当前库名的参数是什么&#xff1f; 2. Nmap探测系统的参数&am…...

C#运算符

&#x1f9e0; 一、C# 运算符列表&#xff08;按类别分类&#xff09; 类别运算符一元运算符, -, , --, !, ~, (T) x算术运算符, -, *, /, %赋值运算符, , -, *, /, %, &, 比较/关系运算符, !, <, >, <, >逻辑/布尔运算符&&, 按位运算符&, 条件运…...

五月份嵌入式面试总结

目录 1、札记 1.1、芯片的bring up 主要做哪些工作&#xff1a; 2、Linux驱动八股文 中断与同步互斥 2.1.1 内核同步互斥的几种方式 2.1.2 互斥锁和自旋锁的区别 2.1.3 spin_lock 和 spin_lock_irqsave 的区别 2.1.4 进程上下文和中断上下文有什么区别 2.1.5 进行上下…...

数据库行业竞争加剧,MySQL 9.3.0 企业版开始支持个人下载

最新发现&#xff0c;Oracle 官方网站放开了 MySQL 9.3.0 企业版下载链接&#xff0c;个人用户也可以免费下载&#xff0c;不过只能用于学习、开发或者原型测试&#xff0c;不能用于生产环境。 通常我们都是下载 MySQL 社区版&#xff0c;不过 MySQL 企业版可以支持更多高级功能…...

Python实例题:Django搭建简易博客

目录 Python实例题 题目 1. 创建 Django 项目和应用 2. 配置项目 3. 设计模型 blog_app templates blog_app post_list.html admin.py models.py urls.py views.py blog_project urls.py 代码解释 models.py&#xff1a; admin.py&#xff1a; urls.py&…...

Tcping详细使用教程

Tcping详细使用教程 下载地址 https://download.elifulkerson.com/files/tcping/0.39/在windows环境下安装tcping 在以上的下载地中找到exe可执行文件&#xff0c;其中tcping.exe适用于32位Windows系统&#xff0c;tcping64.exe适用于64位Windows操作系统。 其实tcping是个…...

PyCharm 快捷键指南

PyCharm 快捷键指南 常用编辑快捷键 代码完成&#xff1a;Ctrl Space 提供基本的代码完成选项&#xff08;类、方法、属性&#xff09;导入类&#xff1a;Ctrl Alt Space 快速导入所需类语句完成&#xff1a;Ctrl Shift Enter 自动结束代码&#xff08;如添加分号&#…...

如何更改远程桌面连接的默认端口?附外网访问内网计算机方法

远程连接端口根据协议和场景不同有所差异&#xff0c;以下是常见远程连接端口的设置及修改方法&#xff0c;同时附外网访问内网计算机操作。 一、Windows远程桌面默认端口 ‌默认端口‌&#xff1a;3389&#xff08;TCP协议&#xff09;&#xff0c;用于Windows远程桌面服务&…...

【GAN网络入门系列】一,手写字MINST图片生成

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 博主简介&#xff1a;努力学习的22级本科生一枚 &#x1f31f;​&#xff1b;探索AI算法&#xff0c;C&#xff0c;go语言的世界&#xff1b;在迷茫中寻找光芒…...