팰월드 모드 제작법7

후킹 함수 개요 보기.

Featured image

후킹 함수의 개요

이 튜토리얼에는 파티에서 팰을 소환하는 기능을 연결합니다.

후킹 함수


UE4SS 에서 함수를 연결하려면 해당 RegisterHook 함수를 사용합니다. 해당 함수의 시그니처는 다음과 같습니다 :

  RegisterHook(FunctionName, Callback)
**❔함수의 시그니처란?** 
함수가 어떤 매개변수를 가지고 있고 어떤 값을 반환하는지에 대한 설명.

매개변수는 다음과 같습니다:

객체 생성 등록하기

그러나 RegisterHook 함수가 작동하려면 우리의 객체가 미리 존재해야 합니다. 따라서 여기에 또 다른 중요한 함수인 NotifyOnNewObject가 필요합니다.

이 함수는 특정 클래스의 객체가 생성될 때 일부 코드를 실행할 수 있게 해줍니다. 함수의 시그니처는 다음과 같습니다:

  NotifyOnNewObject(ObjectPath, Callback)

매개변수는 다음과 같습니다:

1번째 후크작성하기

이 간단한 예제에서 파티에서 소환한 팰들을 스케일 다운(축소) 시키는 코드 스니펫을 작성하려 합니다.

우선, 이 게임에서 우리 파티에 속한 모든 팰들을 otomo라고 부르기로 합시다. 그러기 때문에 우리는 이와 관련된 내용을 찾아야 합니다.

BP_OtomoPalHolderComponent 가 발견되었습니다! 이 컴포넌트는 파티의 팰들을 저장하고 필요시 소환하는 역할을 담당합니다.

그 함수를 살펴본 후 이와 유사한 시그니처를 가지고 있는 함수들을 찾을 수 있습니다 :

  void ActivateOtomo(int32 SlotID, FTransform StartTransform, bool& IsSuccess);

이 함수는 파티에서 팰을 소환을 담당하는 함수입니다(던짐).

컴포넌트 생성 구독하기

그 함수를 후킹해봅시다! 먼저, 훅을 원하는 객체가 존재하는지 확인해야 하므로, 새 객체가 생성될 때마다 후크를 설정하도록 하겠습니다. 그리고 이를 위한 NotifyOnNewObject를 사용해야 합니다.

main.lua

print("Hello world from Lua!")

RegisterHook("/Script/Engine.PlayerController:ClientRestart", function (Context)
    NotifyOnNewObject("/Game/Pal/Blueprint/Component/OtomoHolder/BP_OtomoPalHolderComponent.BP_OtomoPalHolderComponent_C", function (Component)
        print("New component!")
    end)
end)

게임 내 월드 접속 시 “Hello World from Lua!” 메시지가 나타나야 합니다. 그러니까 게임을 열고 시도해봅시다!

  모드의 메시지가 출력되지 않는다면 `enabled.txt` 가 모드 폴더에 있는지 확인합니다.
  여전히 메시지가 출력되지 않으면 `mods.txt` 를 편집해 모드를 포함하고 해당 모드를 활성화합니다.

로그는 아래와 같습니다.
로그

  ❕NOTE
  `NotifyOnNewObject`에 등록하기 전에 `ClientRestart`에 대한 후크를 등록하는 이유는 그렇게 하지않으면 등록의 신뢰성이 없어 컴포넌트가 생성될 시 항상 호출되지 않을 수 있기 때문입니다.

그 함수 후킹하기

이제 원하는 후크를 작성할 차례입니다. 지금은 간단하게 팰이 활성화되었다는 메시지를 출력해보도록 하겠습니다. 이를 위해 코드를 다음과 같이 변경합니다:

main.lua

print("Hello world from Lua!")

RegisterHook("/Script/Engine.PlayerController:ClientRestart", function (Context)
    NotifyOnNewObject("/Game/Pal/Blueprint/Component/OtomoHolder/BP_OtomoPalHolderComponent.BP_OtomoPalHolderComponent_C", function (Component)
        print("New component!")
        RegisterHook("/Game/Pal/Blueprint/Component/OtomoHolder/BP_OtomoPalHolderComponent.BP_OtomoPalHolderComponent_C:ActivateOtomo", function (self, SlotId)
            print("Activating otomo: " .. SlotId:get())
        end)
    end)
end)

이렇게 하고 나면 UE4SS에서 Restart All Modes 버튼을 누를수 있습니다.

리스타트버튼

모드를 다시 로드한 후 타이틀 화면으로 돌아가 월드에 재접속하십시오. 이제 파티에 있는 팰을 하나 던져보면 콘솔에 다음과 같이 출력됩니다.
콘솔로그

이제 누가 파티에서 팰을 소환할때마다 알림을 받게 됩니다. 이제 유용한 작업을 수행해봅시다. 이 튜토리얼의 목적을 위한 팰을 약간 축소시켜봅니다.

이를 위해서 팰의 액터 인스턴스를 얻어야 하는데 어떻게 할까요? 결국 우리는 SlotID만 가지고 있습니다.

사실 우리가 가진 컴포넌트는 UPalOtomoHolderComponentBase에서 상속되었기 때문에, palworld 모딩 킷 헤더에서 다른 함수들을 살펴볼 수 있습니다. 흥미로운 함수를 볼 수 있습니다:

UFUNCTION(BlueprintCallable, BlueprintPure)
APalCharacter* TryGetOtomoActorBySlotIndex(const int32 SlotIndex) const;

이 함수는 슬롯인덱스를 받아 친구 액터를 반환합니다. 우리가 바로 필요한 그것입니다! 이를 사용합시다.

main.lua

print("Hello world from Lua!")

RegisterHook("/Script/Engine.PlayerController:ClientRestart", function (Context)
    NotifyOnNewObject("/Game/Pal/Blueprint/Component/OtomoHolder/BP_OtomoPalHolderComponent.BP_OtomoPalHolderComponent_C", function (Component)
        print("New component!")
        RegisterHook("/Game/Pal/Blueprint/Component/OtomoHolder/BP_OtomoPalHolderComponent.BP_OtomoPalHolderComponent_C:ActivateOtomo", function (self, SlotId)
            print("Activating otomo: " .. SlotId:get())

            local HolderComponent = self:get()
            local OtomoActor = HolderComponent:TryGetOtomoActorBySlotIndex(SlotId:get())

            OtomoActor:SetActorScale3D({X = 0.6, Y = 0.6, Z = 0.6})
        end)
    end)
end)
**❕NOTE**
Q : 왜 가끔 `:get()`을 호출합니까?
A : 우리가 후크 내부에서 얻는 일부 매개변수는 실제로 우리가 훅 내부에서 얻는 일부 매개변수는 실제로 `RemoteUnrealParam`이며, 일반적으로 이들은 액터, 객체 등의 인스턴스입니다. 따라서 이들의 내부 값을 얻으려면 `:get()`을 호출해야 합니다.

따라서 이 코드는 컴포넌트 인스턴스를 가져와 현재 활성 슬롯 인덱스로부터 친구 액터를 가져온 다음, 해당 액터의 3D 액터 스케일을 원래 값의 60%로 설정합니다. 게임으로 이동하여 확인해 보겠습니다!

팰사이즈