BepInEx终极指南:Unity游戏Mod兼容性原理与实战
1. 项目概述为什么我们需要BepInEx如果你是一个Unity游戏的Mod开发者或者是一个热衷于为游戏增添新内容的玩家那么你一定遇到过这样的困境辛辛苦苦写好的插件换了个游戏版本就彻底失效或者好不容易找到一个功能强大的Mod却因为游戏更新而无法使用屏幕上只剩下一个令人沮丧的错误弹窗。这种“版本一更新Mod全报废”的兼容性问题几乎是所有Unity游戏Mod社区最头疼的“老大难”。这正是BepInEx这类插件框架存在的核心价值。它不是一个简单的Mod加载器而是一个标准化的、工程化的运行时环境。你可以把它想象成游戏和Mod之间的一个“翻译官”和“协调员”。游戏本体Unity引擎说一种语言而来自不同作者、使用不同技术编写的Mod可能说着五花八门的方言。BepInEx的作用就是建立一套统一的沟通协议和运行沙箱确保这些Mod能够和平共处并且不会因为游戏版本的小幅变动比如Unity引擎的补丁更新就集体罢工。最近在社区里关于“BepInEx 炉石”的讨论热度很高这恰恰印证了兼容性问题的普遍性。像《炉石传说》这类持续运营、频繁更新的商业游戏其底层Unity版本和代码结构可能每个季度都在变。如果没有一个像BepInEx这样能够进行运行时补丁Runtime Patching和依赖管理Dependency Management的框架Mod的维护成本将高到无法想象几乎每次游戏更新都意味着Mod作者需要从头开始适配。所以这个“终极指南”的目标非常明确我们不只教你如何安装BepInEx更要深入其内部机制让你理解它是如何解决兼容性问题的。掌握了这套方法论你就能举一反三无论是面对“unity程序打开黑屏无响应”的启动问题还是处理“unity打包安卓”时的环境配置冲突都能有一套清晰的排查和解决思路。2. BepInEx核心架构与兼容性原理拆解要解决兼容性问题首先得知道问题出在哪。Unity游戏的Mod兼容性挑战主要来自三个方面Unity引擎版本差异、游戏程序集Assembly的混淆与变更以及跨平台Windows/Android的运行时环境不同。BepInEx的整个架构设计就是针对这三座大山进行的。2.1 分层架构隔离与稳定的基石BepInEx采用了经典的分层架构这是其高兼容性的根本。我们可以将其分为四层引导层Bootstrap这是最先执行的部分通常是一个轻量级的注入器如winhttp.dll或UnityDoorstop。它的唯一任务是在游戏主程序UnityPlayer.dll或GameAssembly.dll启动前将BepInEx的核心加载器注入到游戏进程的地址空间中。这一层必须极其稳定和通用因为它要应对不同Windows系统版本如热词中提到的从Win10向Win11迁移时的兼容性顾虑和游戏打包方式的差异。核心层Core这是BepInEx的心脏提供了插件管理、配置系统、日志记录等基础服务。它负责初始化一个独立的AppDomain应用程序域或利用最新的.NET托管环境来加载插件。这个隔离的沙箱环境至关重要它确保了即使某个插件崩溃也不会导致整个游戏进程雪崩最多只是该插件功能失效。Unity层Unity这一层包含了与Unity引擎交互的专用组件。例如Chainloader它负责在Unity游戏生命周期中的特定时刻如Awake、Start加载插件。还有Harmony库的集成这是实现运行时补丁的关键允许插件在不修改原始游戏文件的情况下动态修改游戏代码。插件层Plugins最上层才是我们开发者编写的具体功能插件。它们通过BepInEx提供的标准化API如BaseUnityPlugin与下层交互无需直接处理复杂的注入或内存管理。这种分层设计的意义在于当游戏更新时可能只有Unity层需要针对新的Unity API进行适配而核心层和插件层的接口可以保持相对稳定。这就大大降低了整体生态的维护成本。2.2 运行时补丁解决代码偏移的利器游戏更新最常见的兼容性问题就是“代码偏移”。比如游戏版本v1.0中你需要的某个函数在内存中的位置是固定的到了v1.1开发者添加了几行代码这个函数的位置就向后“偏移”了直接调用旧地址必然导致崩溃或未定义行为。BepInEx集成的Harmony库通过特性Attribute和补丁Patch机制优雅地解决了这个问题。插件开发者不是硬编码函数地址而是声明要修改哪个类、哪个方法。Harmony会在游戏运行时利用JIT即时编译或解释器层面的技术动态地将插件代码“织入”到目标方法执行流程的前后或内部。[HarmonyPatch(typeof(PlayerController), “Update”)] // 声明要补丁的类和方法 [HarmonyPostfix] // 声明在目标方法执行后运行 public static void Update_Postfix(PlayerController __instance) { // 你的插件逻辑例如让玩家无限跳跃 __instance.canJump true; }即使PlayerController.Update方法在游戏更新后其内部实现地址变了只要它的方法签名名称、参数、返回类型没变Harmony就能通过反射机制再次找到它并成功应用补丁。这为Mod应对游戏小版本更新提供了巨大的弹性。2.3 依赖管理与元数据避免“DLL地狱”另一个兼容性噩梦是“DLL地狱”插件A需要Newtonsoft.Json版本12.0插件B需要版本13.0两者冲突导致游戏无法启动。BepInEx通过插件的清单文件manifest.json和内置的依赖解析器来解决这个问题。每个BepInEx插件都包含一个manifest.json其中明确声明了id插件唯一标识符。version插件版本。name显示名称。dependencies依赖的其他插件ID及版本范围。BepInEx在加载时会构建一个依赖关系图确保依赖的插件先于被依赖者加载并且可以配置将特定版本的公共库如JSON.NET进行隔离加载避免冲突。这就像是一个为Mod量身定做的NuGet包管理器。3. 实战从零开始配置与故障排查理解了原理我们进入实战环节。这里以在Windows平台为一个典型的Unity独立游戏安装BepInEx为例并穿插讲解如何应对各种兼容性问题。3.1 环境准备与安装获取游戏根目录找到你的游戏安装位置。通常是通过Steam库“浏览本地文件”或直接找到游戏的.exe文件所在文件夹。选择正确的BepInEx版本这是最关键的一步。你需要根据游戏的Unity版本和位数x86/x64来选择。查看游戏版本可以看游戏文件夹内是否有UnityPlayer.dll较新版本或GameAssembly.dll使用IL2CPP后端编译的游戏。也可以通过工具UnityEX或直接查看游戏日志文件来确认Unity版本。访问BepInEx的GitHub发布页下载对应版本。通常对于较新的Unity 2017游戏选择BepInEx 5.x或6.x的x64版本。安装将下载的BepInEx压缩包内所有文件解压到游戏根目录即.exe文件所在目录。确保BepInEx文件夹、doorstop_config.ini、winhttp.dll等文件都位于根目录。首次运行启动游戏。如果一切正常游戏启动后会在根目录生成完整的BepInEx文件夹结构包括plugins存放插件、config插件配置文件、logs运行日志等子文件夹。注意如果游戏使用了反作弊系统如EasyAntiCheat, BattlEye安装任何Mod框架都可能导致封号风险。请务必在单机游戏或明确允许Mod的游戏中使用。3.2 黑屏、无响应、启动失败的深度排查“unity程序打开黑屏无响应”是最高频的故障之一。当安装BepInEx后游戏无法启动请按以下步骤排查检查日志这是最重要的诊断工具。游戏启动后立即查看BepInEx/LogOutput.log。如果这个文件没有生成说明BepInEx的引导层没有成功注入问题出在早期。可能原因ADoorstop配置错误。打开doorstop_config.ini检查targetAssembly路径是否正确指向了BepInEx/core/BepInEx.Preloader.dll。对于不同版本这个路径可能略有不同。可能原因B注入器不兼容。某些游戏可能需要特定的注入方式。可以尝试将winhttp.dll重命名为version.dll如果游戏使用UnityPlayer.dll或使用UnityDoorstop的替代配置。查看Unity自身日志在%USERPROFILE%/AppData/LocalLow/[游戏公司名]/[游戏名]或游戏根目录的Logs文件夹中寻找Player.log或output_log.txt。这里记录了Unity引擎的崩溃信息可能指出是某个原生插件Native Plugin冲突或图形API初始化失败。版本不匹配如果日志显示“MethodNotFoundException”或“TypeLoadException”这几乎肯定是BepInEx核心或插件与当前游戏Unity版本不兼容。你需要确认BepInEx版本是否支持游戏的Unity版本。BepInEx 6.x专为现代Unity2018.4和IL2CPP后端优化。确认插件是否适用于当前游戏版本。插件作者通常会在发布页说明支持的版本。依赖缺失如果日志显示“FileNotFoundException”找不到某个.dll可能是插件依赖的运行时库如.NET Framework某个版本、VC Redistributable未安装。确保系统环境完整。3.3 插件加载与配置实战假设现在BepInEx基础框架已成功运行我们要安装一个名为“AwesomeMod”的插件。安装插件将AwesomeMod.dll及其可能附带的manifest.json、icon.png等文件放入BepInEx/plugins文件夹。如果是压缩包通常解压后是一个以插件ID命名的文件夹将这个整个文件夹放入plugins即可。处理依赖如果AwesomeMod依赖另一个插件CoreLib你需要确保CoreLib已经安装在plugins文件夹中。BepInEx会在启动时检查如果依赖缺失会在日志中给出明确警告并可能阻止AwesomeMod加载。运行时配置许多插件支持运行时配置。启动一次游戏后在BepInEx/config文件夹下会生成以插件ID命名的.cfg文件。你可以用文本编辑器直接修改或者使用社区工具如ConfigurationManager本身也是一个BepInEx插件在游戏内按F1打开图形化界面进行实时调整。热重载部分BepInEx插件支持热重载Hot Reload。修改插件代码并重新编译后在游戏运行时按快捷键如F5即可重新加载插件无需重启游戏极大提升了开发调试效率。4. 进阶跨平台与特定场景兼容性处理BepInEx的影响力早已不限于PC平台这也是其“终极兼容性”能力的体现。4.1 Android平台安卓版BepInEx部署详解在Android上为Unity游戏安装Mod原理类似但环境更复杂。热词中提到的“安卓版bepinex分步安装教程”和“android 修改unity入口文件替换untiy 入口文件”都指向了这一过程。获取游戏APK与解包使用工具如AssetStudio或APK Easy Tool将游戏APK文件解包。关键目标是找到游戏的libil2cpp.so代码逻辑和global-metadata.dat元数据文件以及Assembly-CSharp.dll如果使用Mono后端。准备BepInEx for Android需要专门为ARM架构编译的BepInEx版本。社区通常会有移植版。核心文件是libBepInEx.so相当于PC的winhttp.dll和BepInEx的托管核心文件。注入与重打包修改入口文件这是核心步骤。需要反编译游戏的AndroidManifest.xml找到Unity的启动Activity通常是com.unity3d.player.UnityPlayerActivity并修改其父类或通过包装器使其首先加载libBepInEx.so。这需要一定的Android开发知识也可能涉及使用Xposed或Magisk模块进行运行时注入后者对系统有要求但更便捷。替换/添加文件将libBepInEx.so放入APK的lib/armeabi-v7a或lib/arm64-v8a目录根据手机架构。将BepInEx的核心托管DLL和plugins文件夹放入APK的assets目录或游戏的数据目录。重签名与安装修改后的APK需要重新签名使用调试密钥或自有证书才能安装到非Root设备上。权限与路径Android有严格的沙箱限制。插件需要读写配置或存档时不能直接访问/assets只读而应使用Application.persistentDataPath这个路径在Android上通常指向/storage/emulated/0/Android/data/[游戏包名]/files。插件代码中所有文件操作路径都必须据此调整。这个过程技术门槛较高涉及逆向工程和Android系统知识是兼容性挑战的终极体现。4.2 应对Unity引擎特定版本问题Unity本身也在不断演进BepInEx需要与之同步。Unity 2022 LTS与.NET版本Unity 2022 LTS默认使用.NET Standard 2.1或.NET 6/7。BepInEx 6.x开始支持这些新的运行时。如果你的插件引用了一些旧的.NET Framework库可能需要更新或寻找替代包。IL2CPP后端越来越多的游戏使用IL2CPP将C#代码转换为C再编译为原生代码以提升性能和安全性。这给传统的基于C#反射的Mod带来了巨大挑战。BepInEx通过BepInEx.IL2CPP版本利用IL2CPP运行时提供的有限反射接口和Unhollowed工具生成的伪程序集重新实现了插件加载和Harmony补丁功能。但兼容性和性能仍不如Mono后端完美。Unity引擎模块兼容如果插件涉及到URP通用渲染管线、Timeline、Shader等特定引擎模块如热词中的“unity urp shader 体积光”、“unity——scroll view 滑动居中”那么插件的兼容性就与这些模块的API稳定性强相关。BepInEx框架本身无法解决这类问题需要插件作者针对不同Unity版本进行适配。5. 插件开发中的兼容性最佳实践作为一名插件开发者如何从一开始就写出兼容性更好的Mod最小化补丁范围使用Harmony时尽量使用[HarmonyPrefix]、[HarmonyPostfix]这种非侵入式补丁避免使用[HarmonyTranspiler]直接操作IL指令码除非绝对必要。Transpiler对游戏代码的微小变动极其敏感。使用反射时增加容错不要直接假设某个类或方法一定存在。使用Type.GetType()并检查null或者使用AccessToolsHarmony提供的工具类来安全地查找成员。var targetType AccessTools.TypeByName(“Game.Player.AdvancedController”); if (targetType null) { Logger.LogWarning(“AdvancedController not found, maybe game version is too old.”); return; // 优雅降级禁用部分功能 }明确声明依赖与版本在manifest.json中精确声明你的插件所依赖的其他插件和库的版本范围如[1.2.0, 1.3.0)。这能帮助BepInEx和用户管理冲突。提供详细的日志在你的插件中实现完善的日志输出。在Awake()或Start()方法中输出当前插件的版本、检测到的游戏版本、以及关键功能是否成功初始化的信息。这能在出现问题时为用户和你自己提供第一手的诊断依据。配置驱动将可能因游戏版本而变的参数如某个字段的偏移量、某个资源文件的路径做成可配置项放在.cfg文件里。这样当游戏更新导致原有参数失效时高级用户可以通过修改配置来临时修复而无需等待你发布新版本。6. 社区、资源与持续维护面对浩瀚如海的Unity游戏和不断更新的引擎个人的力量是有限的。BepInEx的强大离不开其背后活跃的社区。核心资源站GitHub: BepInEx的官方仓库是获取最新版本、阅读源码和提交问题的第一站。BepInExPack项目提供了针对热门游戏如《雨中冒险2》、《英灵神殿》的预配置包是新手入门的最佳选择。游戏Mod社区如Nexus Mods、Mod DB以及各类游戏的Discord频道、贴吧、QQ群。这里聚集了具体的插件和针对特定游戏的安装、故障排除经验。学习与调试工具dnSpy / ILSpy: 反编译游戏程序集Assembly-CSharp.dll分析游戏代码结构是寻找补丁目标的必备工具。Unity Explorer: 一个强大的BepInEx插件可以在游戏内实时查看和修改Unity对象、组件、资源是调试和探索游戏内部状态的利器。ConfigurationManager: 前述的图形化配置管理插件极大方便了插件的配置调整。心态与维护为Unity游戏开发Mod尤其是线上游戏需要保持平和的心态。游戏更新导致Mod失效是常态。建立有效的反馈渠道如GitHub Issues、Disc频道在插件说明中清晰标注支持的版本并在游戏大更新后积极测试和适配是维持插件生命力的关键。兼容性问题从来不是一劳永逸的它是一个持续的过程。BepInEx框架提供了一套强大的工具和规范将这个过程从杂乱无章的“打地鼠”变成了有迹可循的“系统工程”。掌握它你就能在Unity游戏的Mod世界里拥有应对变化的底气和能力。

相关新闻

Mirror IL后处理技术:实现Unity零开销RPC调用的原理与实战

Mirror IL后处理技术:实现Unity零开销RPC调用的原理与实战

1. 项目概述:从“反射”到“织入”的性能革命 在Unity网络游戏开发领域,性能优化是一个永恒的话题。但凡做过联机游戏的开发者,对RPC(远程过程调用)一定又爱又恨。爱的是它概念清晰,能让客户端像调用本地函…

2026/10/12 19:27:39 阅读更多 →
UniApp页面onshow触发子组件更新:从Prop驱动到状态管理的完整方案

UniApp页面onshow触发子组件更新:从Prop驱动到状态管理的完整方案

1. 项目概述:当页面“活”过来时,如何让子组件也“醒一醒”在UniApp或微信小程序这类基于Vue.js的跨端框架里开发,onShow生命周期钩子是我们再熟悉不过的老朋友了。每当一个页面从后台被切回前台,或者通过导航跳转进入时&#xff…

2026/10/3 1:18:52 阅读更多 →
从InterAccel到Raw Accel:鼠标加速技术的进化与革新

从InterAccel到Raw Accel:鼠标加速技术的进化与革新

从InterAccel到Raw Accel:鼠标加速技术的进化与革新 【免费下载链接】rawaccel kernel mode mouse accel 项目地址: https://gitcode.com/gh_mirrors/ra/rawaccel 你是否曾在游戏中感受到鼠标响应不够灵敏,或者在进行精细设计时觉得光标控制不够精…

2026/10/8 6:23:32 阅读更多 →

最新新闻

Python字典的__missing__魔术方法:缺失键的优雅处理机制

Python字典的__missing__魔术方法:缺失键的优雅处理机制

就像所有键都找不到时会手足无措的字典一样,Python里的dict其实留了一扇专门处理这种情况的门,这门就是魔术方法__missing__。它是字典变体类的核心钩子:当d[key]访问的键不在字典里时,解释器不会直接抛KeyError,而是先…

2026/10/13 14:00:49 阅读更多 →
SELinux强制访问控制实战:从Permissive调试到Enforcing上线

SELinux强制访问控制实战:从Permissive调试到Enforcing上线

1. 这不是“开个开关”那么简单:SELinux到底在防什么,又为什么总被关掉?“ENGINEER 02:系统安全保护 启用SELinux保护的详细教程步骤,学不会来找我”——这个标题里藏着一个行业内心照不宣的真相:绝大多数人…

2026/10/13 14:00:49 阅读更多 →
Canvas图编辑器核心实现:数据驱动、交互机制与性能优化

Canvas图编辑器核心实现:数据驱动、交互机制与性能优化

做了几年前端和图形方向的东西,我最大的感受是:绘图类的项目,难的不是画一根线、拖一个框,而是把“交互”和“数据”揉在一起还能保持稳定。最近我在做一个内部工具,名字就叫 diagram-design,用途是给业务系…

2026/10/13 14:00:49 阅读更多 →
LiteLLM:轻量级大模型网关与智能调度中间件实战指南

LiteLLM:轻量级大模型网关与智能调度中间件实战指南

1. 项目概述:LiteLLM 是什么,它解决的不是“模型调用”而是“模型调度”的本质问题LiteLLM 这个名字刚出现时,很多人第一反应是:“又一个 LLM 封装库?”——但实际用过两周后,我把它从测试环境直接推到了生…

2026/10/13 14:00:49 阅读更多 →
C++ gRPC grpc::Status 状态码与错误处理排查实战指南

C++ gRPC grpc::Status 状态码与错误处理排查实战指南

最近经常有朋友拿着一堆 gRPC 报错来问我:C 项目里客户端跑着跑着就冒出 unexpected status 401 unauthorized: incorrect api key provided 、 429 too many requests 、 502 bad gateway 这些东西,日志打了一屏,却不知道下一步该点哪…

2026/10/13 14:00:49 阅读更多 →
Slicer 在 GNU/Linux 下的 C++ 调试实战:GDB 附加、执行器环境与 Core 转储分析

Slicer 在 GNU/Linux 下的 C++ 调试实战:GDB 附加、执行器环境与 Core 转储分析

医疗健康图像处理数据可视化桌面应用3D渲染 【免费下载链接】Slicer Multi-platform, free open source software for visualization and image computing. 项目地址: https://gitcode.com/gh_mirrors/sl/Slicer 点击查看 免费下载 本文基于 Slicer 官方文档 Docs/…

2026/10/13 13:59:47 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →