首页
/ Doorstop入口点与启动机制

Doorstop入口点与启动机制

2026-02-04 05:15:14作者:贡沫苏Truman

BepInEx通过Doorstop实现Unity游戏的插件注入机制,Doorstop作为一个轻量级的注入器,能够在游戏启动前加载BepInEx的核心组件。整个配置与启动流程涉及多个关键配置文件和启动脚本,为不同Unity运行时环境(Mono和IL2CPP)提供定制化的注入方案,包括详细的配置参数、环境变量设置和平台特定的处理逻辑。

Doorstop配置与启动流程

BepInEx通过Doorstop实现Unity游戏的插件注入机制,Doorstop作为一个轻量级的注入器,能够在游戏启动前加载BepInEx的核心组件。整个配置与启动流程涉及多个关键配置文件和启动脚本,为不同Unity运行时环境提供定制化的注入方案。

配置文件架构

Doorstop支持两种主要的Unity运行时配置:Mono和IL2CPP。每种运行时都有对应的配置文件,采用INI格式进行配置管理。

Mono运行时配置 (doorstop_config_mono.ini)

[General]
enabled = true
target_assembly = BepInEx\core\BepInEx.Unity.Mono.Preloader.dll
redirect_output_log = false
boot_config_override =
ignore_disable_switch = false

[UnityMono]
dll_search_path_override = "BepInEx\core"
debug_enabled = false
debug_start_server = true
debug_address = 127.0.0.1:10000
debug_suspend = false

IL2CPP运行时配置 (doorstop_config_il2cpp.ini)

[General]
enabled = true
target_assembly = BepInEx\core\BepInEx.Unity.IL2CPP.dll
redirect_output_log = false
boot_config_override =
ignore_disable_switch = false

[UnityMono]
dll_search_path_override =
debug_enabled = false
debug_start_server = true
debug_address = 127.0.0.1:10000
debug_suspend = false

[Il2Cpp]
coreclr_path = dotnet\coreclr.dll
corlib_dir = dotnet

配置参数详解

配置节 参数名称 类型 默认值 描述
General enabled bool true 启用Doorstop注入功能
General target_assembly string 运行时特定 目标程序集路径,包含入口点方法
General redirect_output_log bool false 是否重定向Unity输出日志
General boot_config_override string 覆盖默认boot.config文件路径
General ignore_disable_switch bool false 忽略DOORSTOP_DISABLE环境变量
UnityMono dll_search_path_override string 运行时特定 Mono DLL搜索路径覆盖
UnityMono debug_enabled bool false 启用Mono调试器服务器
UnityMono debug_start_server bool true Doorstop是否初始化调试器服务器
UnityMono debug_address string 127.0.0.1:10000 调试器服务器地址
UnityMono debug_suspend bool false 调试时是否挂起游戏执行
Il2Cpp coreclr_path string dotnet\coreclr.dll CoreCLR运行时路径
Il2Cpp corlib_dir string dotnet CoreCLR托管核心库目录

启动脚本机制

BepInEx提供了跨平台的启动脚本(run_bepinex_mono.sh和run_bepinex_il2cpp.sh),这些脚本负责设置环境变量并启动游戏进程。

启动流程序列图

sequenceDiagram
    participant User
    participant LaunchScript
    participant DoorstopLib
    participant UnityProcess
    participant BepInExPreloader

    User->>LaunchScript: 执行启动命令
    LaunchScript->>LaunchScript: 解析配置参数
    LaunchScript->>LaunchScript: 设置环境变量
    LaunchScript->>UnityProcess: 启动Unity进程
    UnityProcess->>DoorstopLib: 加载libdoorstop
    DoorstopLib->>BepInExPreloader: 调用target_assembly
    BepInExPreloader->>BepInExPreloader: 执行Start()方法
    BepInExPreloader->>UnityProcess: 继续正常启动流程

环境变量设置

启动脚本通过设置以下环境变量来控制Doorstop行为:

export DOORSTOP_ENABLED="1"
export DOORSTOP_TARGET_ASSEMBLY="BepInEx/core/BepInEx.Unity.Mono.Preloader.dll"
export DOORSTOP_IGNORE_DISABLED_ENV="0"
export DOORSTOP_MONO_DLL_SEARCH_PATH_OVERRIDE="BepInEx/core"
export DOORSTOP_MONO_DEBUG_ENABLED="0"
export DOORSTOP_MONO_DEBUG_START_SERVER="0"
export DOORSTOP_MONO_DEBUG_ADDRESS="127.0.0.1:10000"
export DOORSTOP_MONO_DEBUG_SUSPEND="0"
export DOORSTOP_CLR_RUNTIME_CORECLR_PATH=""
export DOORSTOP_CLR_CORLIB_DIR=""

入口点实现

Doorstop要求目标程序集包含特定签名的入口点方法。BepInEx的实现如下:

// ReSharper disable once CheckNamespace
namespace Doorstop;

internal static class Entrypoint
{
    /// <summary>
    ///     The main entrypoint of BepInEx, called from Doorstop.
    /// </summary>
    public static void Start()
    {
        try
        {
            EnvVars.LoadVars();
            var gamePath = Path.GetDirectoryName(EnvVars.DOORSTOP_PROCESS_PATH) ?? ".";
            
            // 使用反射调用预加载器以避免过早的程序集解析问题
            typeof(Entrypoint).Assembly.GetType($"BepInEx.Unity.Mono.Preloader.{nameof(UnityPreloaderRunner)}")
                              ?.GetMethod(nameof(UnityPreloaderRunner.PreloaderPreMain))
                              ?.Invoke(null, null);
        }
        catch (Exception ex)
        {
            File.WriteAllText(silentExceptionLog, ex.ToString());
        }
    }
}

平台特定处理

启动脚本包含针对不同操作系统的处理逻辑:

操作系统 可执行文件处理 库文件扩展名 环境变量
Linux 直接路径处理 .so LD_PRELOAD, LD_LIBRARY_PATH
macOS .app包解析 .dylib DYLD_INSERT_LIBRARIES, DYLD_LIBRARY_PATH
Windows 不适用(使用Windows版本) .dll 不适用

Steam启动兼容性

启动脚本特别处理了通过Steam启动的情况,确保Steam覆盖功能正常工作:

if [ "$2" = "SteamLaunch" ]; then
    # 重新组织参数并通过Steam启动器重新执行
    exec "$@"
fi

架构检测与验证

脚本包含架构检测逻辑,确保只在支持的架构上运行:

file_out="$(LD_PRELOAD="" file -b "${executable_path}")"
case "${file_out}" in
    *PE32*)
        echo "Windows可执行文件,请使用Windows版BepInEx"
        exit 1
    ;;
    *64-bit*)
        arch="x64"
    ;;
    *32-bit*)
        arch="x86"
    ;;
    *)
        echo "不支持的架构"
        exit 1
    ;;
esac

这种配置与启动机制确保了BepInEx能够在各种Unity游戏环境中稳定运行,为mod开发者提供了统一的插件加载基础。

Mono与IL2CPP差异化配置

在Unity游戏开发中,Mono和IL2CPP是两种不同的脚本后端运行时环境,BepInEx通过Doorstop入口点机制为这两种运行时提供了精细化的差异化配置支持。这种配置差异主要体现在目标程序集、运行时路径设置、调试选项以及核心库处理等多个方面。

配置文件结构对比

BepInEx为Mono和IL2CPP运行时分别提供了专门的配置文件,两者在结构上保持一致性但在具体参数上存在显著差异:

配置项 Mono配置 IL2CPP配置 说明
target_assembly BepInEx\core\BepInEx.Unity.Mono.Preloader.dll BepInEx\core\BepInEx.Unity.IL2CPP.dll 目标程序集路径
dll_search_path_override "BepInEx\core" 空值 DLL搜索路径重写
coreclr_path 无此配置 dotnet\coreclr.dll CoreCLR运行时路径
corlib_dir 无此配置 dotnet 核心库目录

运行时特性差异分析

Mono运行时配置特点

Mono配置主要针对传统的Mono运行时环境,其核心特性包括:

[UnityMono]
dll_search_path_override = "BepInEx\core"
debug_enabled = false
debug_start_server = true
debug_address = 127.0.0.1:10000
debug_suspend = false

Mono配置中的dll_search_path_override参数尤为重要,它指示Mono运行时优先从BepInEx的核心目录搜索程序集,这在游戏原始Managed文件夹中的mscorlib等核心库被剥离的情况下尤为关键。

IL2CPP运行时配置特点

IL2CPP配置则针对现代化的IL2CPP运行时,新增了CoreCLR相关的配置节:

[Il2Cpp]
coreclr_path = dotnet\coreclr.dll
corlib_dir = dotnet

IL2CPP配置取消了Mono特有的DLL搜索路径重写,因为IL2CPP使用完全不同的程序集加载机制,转而专注于CoreCLR运行时的正确配置。

技术实现机制

BepInEx通过统一的配置接口处理两种运行时的差异,在代码层面通过运行时检测自动选择相应的配置策略:

flowchart TD
    A[Doorstop启动] --> B{检测运行时类型}
    B -->|Mono| C[加载Mono配置]
    B -->|IL2CPP| D[加载IL2CPP配置]
    
    C --> E[设置DLL搜索路径]
    C --> F[配置Mono调试器]
    
    D --> G[定位CoreCLR运行时]
    D --> H[设置核心库目录]
    
    E --> I[加载BepInEx.Mono.Preloader]
    F --> I
    
    G --> J[加载BepInEx.IL2CPP]
    H --> J

调试配置的统一处理

尽管两种运行时在底层实现上存在差异,但在调试配置方面保持了高度的一致性:

# 通用调试配置
debug_enabled = false
debug_start_server = true
debug_address = 127.0.0.1:10000
debug_suspend = false

这种统一的设计使得开发者可以在不同的运行时环境下使用相同的调试工作流,降低了学习和使用的复杂度。

实际应用场景

在实际游戏模组开发中,这种差异化配置机制确保了:

  1. 兼容性保障:自动适应不同Unity版本和运行时环境
  2. 性能优化:针对不同运行时特性进行针对性优化
  3. 调试便利:统一的调试接口简化开发流程
  4. 部署简化:自动化的配置选择减少手动干预

通过这种精细化的配置差异处理,BepInEx为Unity游戏模组开发者提供了稳定可靠的运行时环境支持,无论是传统的Mono后端还是现代的IL2CPP后端都能获得最佳的使用体验。

调试支持与输出重定向

BepInEx 提供了强大的调试支持和输出重定向机制,确保在复杂的 Unity 游戏环境中能够准确捕获和分析运行时信息。这些功能对于插件开发和故障排查至关重要。

标准输出重定向机制

BepInEx 通过 ConsoleSetOutFix 类实现了标准输出的重定向和日志记录功能。该机制确保所有控制台输出都被正确捕获并记录到 BepInEx 的日志系统中。

public static class ConsoleSetOutFix
{
    private static LoggedTextWriter loggedTextWriter;
    internal static ManualLogSource ConsoleLogSource = Logger.CreateLogSource("Console");

    public static void Apply()
    {
        loggedTextWriter = new LoggedTextWriter { Parent = Console.Out };
        Console.SetOut(loggedTextWriter);
        Harmony.CreateAndPatchAll(typeof(ConsoleSetOutFix));
    }

    [HarmonyPatch(typeof(Console), nameof(Console.SetOut))]
    [HarmonyPrefix]
    private static bool OnSetOut(TextWriter newOut)
    {
        loggedTextWriter.Parent = newOut;
        return false;
    }
}

该实现的核心是一个自定义的 LoggedTextWriter 类,它继承自 TextWriter 并重写了关键的写入方法:

internal class LoggedTextWriter : TextWriter
{
    public override Encoding Encoding { get; } = Encoding.UTF8;
    public TextWriter Parent { get; set; }

    public override void Flush() => Parent.Flush();

    public override void Write(string value)
    {
        ConsoleSetOutFix.ConsoleLogSource.Log(LogLevel.Info, value);
        Parent.Write(value);
    }

    public override void WriteLine(string value)
    {
        ConsoleSetOutFix.ConsoleLogSource.Log(LogLevel.Info, value);
        Parent.WriteLine(value);
    }
}

这种设计确保了:

  • 所有控制台输出都被记录到 BepInEx 日志系统
  • 原始输出功能保持不变
  • 支持动态切换输出目标

标准错误重定向

对于 IL2CPP 运行时,BepInEx 提供了专门的 RedirectStdErrFix 来处理标准错误输出:

flowchart TD
    A[应用程序标准错误输出] --> B[Windows 平台]
    A --> C[Unix 平台]
    B --> D[CreateFile API 创建日志文件]
    D --> E[SetStdHandle 重定向句柄]
    E --> F[ErrorLog.log 文件]
    C --> G[系统管道重定向]
    G --> H[标准错误日志文件]

Windows 平台的具体实现:

public static void Apply()
{
    if (PlatformHelper.Is(Platform.Windows))
    {
        var errorFile = CreateFile(Path.Combine(Paths.BepInExRootPath, "ErrorLog.log"), 
                                  GENERIC_WRITE, FILE_SHARE_READ, 0, 
                                  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
        
        if (errorFile != INVALID_HANDLE_VALUE)
        {
            SetStdHandle(STD_ERROR_HANDLE, errorFile);
        }
    }
    // Unix 平台使用标准管道重定向
}

终端信息修复 (XTermFix)

对于 Unix 系统上的终端兼容性问题,BepInEx 提供了 XTermFix 类来解决老版本 Unity 的终端信息读取问题:

internal static class XTermFix
{
    public static int intOffset;

    public static void Apply()
    {
        if (PlatformHelper.Is(Platform.Windows))
            return;

        var harmony = new Harmony("com.bepinex.xtermfix");
        
        harmony.Patch(AccessTools.Method("System.TermInfoReader:ReadHeader"),
                      new HarmonyMethod(typeof(XTermFix), nameof(ReadHeaderPrefix)));

        harmony.Patch(AccessTools.Method("System.TermInfoReader:Get", 
                      new[] { AccessTools.TypeByName("System.TermInfoNumbers") }),
                      transpiler: new HarmonyMethod(typeof(XTermFix), 
                      nameof(GetTermInfoNumbersTranspiler)));
    }
}

该修复主要处理两种不同的终端信息格式:

格式标识 整数偏移量 描述
0x11a 2 bytes 旧版终端信息格式
0x21e 4 bytes 新版终端信息格式

调试输出流程

BepInEx 的调试输出重定向遵循以下处理流程:

sequenceDiagram
    participant App as 应用程序
    participant Console as System.Console
    participant LoggedWriter as LoggedTextWriter
    participant BepInExLog as BepInEx 日志系统
    participant File as 日志文件

    App->>Console: Write/WriteLine 调用
    Console->>LoggedWriter: 转发输出
    LoggedWriter->>BepInExLog: 记录到日志系统
    LoggedWriter->>Console.Out: 原始输出
    BepInExLog->>File: 写入磁盘日志

平台特定的调试支持

BepInEx 针对不同平台提供了专门的调试支持:

Windows 平台特性:

  • 使用内核 API 进行标准错误重定向
  • 支持完整的句柄管理
  • 提供详细的错误日志记录

Unix 平台特性:

  • 利用系统管道进行输出重定向
  • 终端信息格式自动检测和修复
  • 兼容多种终端类型

调试配置选项

通过 Doorstop 配置文件,用户可以调整调试相关的设置:

[Debug]
; 启用详细调试输出
debug.enabled = true
; 调试日志级别
debug.logLevel = Info
; 控制台输出重定向
redirectOutput = true
; 标准错误重定向
redirectError = true

故障排查与日志分析

BepInEx 的调试系统提供了丰富的日志信息,帮助开发者快速定位问题:

  1. 控制台输出日志:记录所有控制台输出内容
  2. 错误日志:专门的标准错误输出记录
  3. 调试日志:详细的运行时调试信息
  4. 性能日志:插件加载和执行性能数据

这种多层次的调试支持确保了在各种复杂的游戏环境中都能够获得准确的运行时信息,为插件开发和问题排查提供了强有力的工具支持。

Shell启动脚本实现

BepInEx在Linux和macOS平台上的启动机制依赖于精心设计的Shell脚本,这些脚本负责配置环境变量、处理平台差异以及确保Doorstop注入器正确工作。run_bepinex_il2cpp.shrun_bepinex_mono.sh是两个核心启动脚本,它们分别针对IL2CPP和Mono运行时环境进行了优化。

脚本架构与设计理念

启动脚本采用模块化设计,分为配置段、平台检测段、参数处理段和环境设置段四个主要部分。这种设计使得脚本既可以通过命令行参数动态配置,也可以通过编辑脚本内部的默认值进行静态配置。

#!/bin/sh
# BepInEx start script
#
# Run the script to start the game with BepInEx enabled
#
# There are two ways to use this script
#
# 1. Via CLI: Run ./run_bepinex.sh <path to game> [doorstop arguments] [game arguments]
# 2. Via config: edit the options below and run ./run.sh without any arguments

核心配置参数详解

脚本提供了丰富的配置选项,涵盖了Doorstop注入器的各个方面:

配置参数 默认值 说明
enabled "1" 是否启用Doorstop注入
target_assembly IL2CPP: "BepInEx/core/BepInEx.Unity.IL2CPP.dll"
Mono: "BepInEx/core/BepInEx.Unity.Mono.Preloader.dll"
目标程序集路径
dll_search_path_override IL2CPP: ""
Mono: "BepInEx/core"
Mono DLL搜索路径覆盖
debug_enable "0" 是否启用Mono调试器
coreclr_path IL2CPP: "dotnet/libcoreclr"
Mono: ""
CoreCLR运行时路径

平台检测与路径解析机制

脚本通过uname -s命令检测操作系统类型,并据此设置相应的库文件扩展名和可执行文件路径处理逻辑:

flowchart TD
    A[检测操作系统类型] --> B{Linux系统?}
    A --> C{macOS系统?}
    A --> D[其他系统]
    
    B --> E[设置lib_extension为.so]
    B --> F[处理Linux可执行文件路径]
    
    C --> G[设置lib_extension为.dylib]
    C --> H[处理macOS .app包结构]
    
    E --> I[路径解析完成]
    F --> I
    G --> I
    H --> I
    
    D --> J[报错并退出]

对于macOS平台,脚本需要特殊处理.app应用程序包的结构:

# macOS特殊处理
real_executable_name="${executable_name}"
if ! echo "$real_executable_name" | grep "^.*\.app$"; then
    real_executable_name="${real_executable_name}.app"
fi
inner_executable_name=$(defaults read "${real_executable_name}/Contents/Info" CFBundleExecutable)
executable_path="${real_executable_name}/Contents/MacOS/${inner_executable_name}"

Steam启动兼容性处理

脚本特别考虑了通过Steam启动的情况,确保Steam覆盖层能够正常工作:

# Special case: program is launched via Steam
if [ "$2" = "SteamLaunch" ]; then
    # 重新通过Steam的启动器运行脚本
    to_rotate=4
    rotated=0
    while [ $((to_rotate-=1)) -ge 0 ]; do
        while [ "z$1" = "z--" ]; do
            set -- "$@" "$1"
            shift
            rotated=$((rotated+1))
        done
        set -- "$@" "$1"
        shift
        rotated=$((rotated+1))
    done
    exec "$@"
fi

环境变量配置与注入机制

脚本的核心功能是通过设置环境变量来配置Doorstop注入器:

# 设置Doorstop环境变量
export DOORSTOP_ENABLED="$enabled"
export DOORSTOP_TARGET_ASSEMBLY="$target_assembly"
export DOORSTOP_IGNORE_DISABLED_ENV="$ignore_disable_switch"

# 设置库加载路径
export LD_LIBRARY_PATH="${doorstop_directory}:${corlib_dir}:${LD_LIBRARY_PATH}"
if [ -z "$LD_PRELOAD" ]; then
    export LD_PRELOAD="${doorstop_name}"
else
    export LD_PRELOAD="${doorstop_name}:${LD_PRELOAD}"
fi

架构检测与错误处理

脚本使用file命令检测可执行文件的架构,确保兼容性:

# 检测可执行文件架构
file_out="$(LD_PRELOAD="" file -b "${executable_path}")"
case "${file_out}" in
    *PE32*)
        echo "Windows可执行文件,需要使用Wine/Proton"
        exit 1
    ;;
    *64-bit*)
        arch="x64"
    ;;
    *32-bit*)
        arch="x86"
    ;;
    *)
        echo "不支持的架构: ${file_out}"
        exit 1
    ;;
esac

参数解析与布尔值处理

脚本提供了灵活的命令行参数解析机制,包括布尔值转换函数:

# 布尔值转换辅助函数
doorstop_bool() {
    case "$1" in
        TRUE|true|t|T|1|Y|y|yes)
            echo "1"
        ;;
        FALSE|false|f|F|0|N|n|no)
            echo "0"
        ;;
    esac
}

# 命令行参数解析
while :; do
    case "$1" in
        --doorstop_enabled)
            enabled="$(doorstop_bool "$2")"
            shift
        ;;
        # 其他参数处理...
    esac
    shift
done

跨平台路径处理

脚本实现了完整的路径解析链,确保在不同平台和符号链接情况下都能正确找到目标文件:

sequenceDiagram
    participant User
    participant Script
    participant System
    participant Doorstop

    User->>Script: 执行启动脚本
    Script->>System: 检测操作系统类型
    System-->>Script: 返回系统信息
    Script->>Script: 解析可执行文件路径
    Script->>System: 检测文件架构
    System-->>Script: 返回架构信息
    Script->>Script: 处理命令行参数
    Script->>Script: 设置环境变量
    Script->>Doorstop: 配置注入参数
    Script->>System: 启动游戏进程
    System-->>User: 游戏运行中

这种精心的Shell脚本设计确保了BepInEx在Unix-like系统上的可靠运行,为Unity游戏模组生态提供了稳定的基础环境。脚本的模块化设计和丰富的配置选项使得它能够适应各种复杂的部署场景,从简单的本地测试到通过Steam等平台的复杂启动流程。

BepInEx的Shell启动脚本实现了跨平台的Unity游戏插件注入机制,通过精心的模块化设计和丰富的配置选项,确保了在Linux和macOS系统上的可靠运行。脚本提供了完整的路径解析、架构检测、环境变量配置和Steam启动兼容性处理,能够适应各种复杂的部署场景。这种设计为Unity游戏模组生态提供了稳定的基础环境,从简单的本地测试到通过Steam等平台的复杂启动流程都能得到良好支持。

登录后查看全文
热门项目推荐
相关项目推荐