跳到正文
本頁目錄

鉤子指南

介紹

Hook 是一種在運行時修改函數行為的技術。通常用於在不修改源代碼的情況下修改函數行為。

wikipedia 關於 Hook 的解釋:Hooking

在 LeviLamina 中,我們提供了封裝好的Hook API,使得你可以快速便捷地對 Minecraft 基岩版中的遊戲函數進行行為修改。

Hook的類型

在ll/api/memory/Hook.h 中,我們定義了以下幾種Hook 宏:

C++
#define LL_TYPE_STATIC_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...)

#define LL_STATIC_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...)

#define LL_TYPE_INSTANCE_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...)

#define LL_INSTANCE_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...)

#define LL_AUTO_TYPE_STATIC_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...)

#define LL_AUTO_STATIC_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...)

#define LL_AUTO_TYPE_INSTANCE_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...)

#define LL_AUTO_INSTANCE_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...)

其中,AUTO 標註的 Hook 會自動註冊,即運行時自動調用hook()函數進行註冊。 TYPE 標註的 Hook 會給你的 DEF_TYPE 繼承到你指定的類型。

STATIC_HOOK

針對靜態函數的Hook。

INSTANCE_HOOK

針對成員函數的Hook。

Hook的參數解釋

DEF_TYPE:你給你這個 Hook 的起的類型名。

PRIORITY:Hook的優先級,例如ll::memory::HookPriority::Normal

Note

一般不是特殊需求,我們不推薦過高的優先級,Normal 即可。

TYPE:你的定義的 DEF_TYPE 繼承到的類型。

IDENTIFIER:Hook 的查詢函數使用的標識符,可以為:函數修飾後名稱,函數的字節碼,函數定義。

RET_TYPE:Hook 的函數的返回值類型。

...:Hook 的函數的參數列表。

Hook的使用

你可以查閱 LeviLamina 提供的Fake Header來獲取你想要 Hook 的函數的定義。

簡單的Hook示例

C++
#include "ll/api/memory/Hook.h"
#include "ll/api/io/LoggerRegistry.h"
#include "mc/server/DedicatedServer.h"

auto dedicatedServerLogger = ll::io::LoggerRegistry::getInstance().getOrCreate("DedicatedServer");

LL_AUTO_TYPE_INSTANCE_HOOK(
    DedicatedServerCtorHook,
    ll::memory::HookPriority::Normal,
    DedicatedServer,
    &DedicatedServer::$ctor,
    void*
) {
    dedicatedServerLogger->info("DedicatedServer::DedicatedServer");
    return origin();
}

這段代碼會 Hook DedicatedServer 的構造函數,並在構造函數被調用時打印一條日誌。

解析:

根據查閱 LeviLamina 提供的DedicatedServer.h,我們可以得到 DedicatedServer 的構造函數的thunk函數。

又由於構造函數是類的成員函數,所以我們使用了INSTANCE_HOOK類型的Hook,這使我們不需要填寫由編譯器產生的第一個this指针參數。

又由於我們希望在模組被加載的時候自動註冊,所以我們使用了 AUTO標註的 Hook。

最後由於方便,我們使用了 TYPE標註的 Hook,使得我們可以直接在函數體內調用 DedicatedServer 類型下的函數,雖然在這段代碼中我們並沒有使用到,但是這是一個好習慣。

Hook的註冊和卸載

註冊

針對非自動註冊的 Hook,你需要在你模組需要註冊 Hook 的時機調用hook()函數進行註冊。

卸載

所有的 Hook 都會在 Minecraft 卸載時自動卸載,你也可以在你模組需要卸載 Hook 的時機調用unhook()函數進行卸載。

寫在最後

Hook 是一種非常強大的技術,但是也是一把雙刃劍,如果使用不當,可能會導致Minecraft本體崩潰,或者模組崩潰,嚴重的甚至會對存檔產生影響。

所以請在使用 Hook 的時候一定要謹慎,仔細檢查你的代碼,避免出現不必要的錯誤。