167 lines
5.3 KiB
C++
167 lines
5.3 KiB
C++
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <Mod/CppUserModBase.hpp>
|
|
#include <DynamicOutput/DynamicOutput.hpp>
|
|
|
|
#include <Unreal/Hooks.hpp>
|
|
|
|
#include <Static/EventHookRegistry.hpp>
|
|
#include <Api/Pal/PalGameMode.hpp>
|
|
#include <Features/IUIFeature.hpp>
|
|
#include <Features/PalBoxPalNumber.hpp>
|
|
#include <Features/PalBoxNightWorkIndicator.hpp>
|
|
#include <Features/PalStatusPalNumber.hpp>
|
|
|
|
using namespace RC;
|
|
using namespace RC::Unreal;
|
|
|
|
// D:\Project\PalworldMods\PalUIExtension\post_build.bat "$(TargetPath)" "C:\Program Files (x86)\Steam\steamapps\common\Palworld\Pal\Binaries\Win64\ue4ss\Mods\PalUIExtension\dlls\main.dll"
|
|
namespace PalUIExtension
|
|
{
|
|
class PalUIExtension : public RC::CppUserModBase
|
|
{
|
|
private:
|
|
std::vector<std::unique_ptr<Features::IUIFeature>> m_features{};
|
|
bool m_installed = false;
|
|
|
|
inline static PalUIExtension* s_instance{ nullptr };
|
|
|
|
public:
|
|
PalUIExtension() : CppUserModBase()
|
|
{
|
|
ModName = STR("PalUIExtension");
|
|
ModVersion = STR("0.2");
|
|
ModDescription = STR("PalUIExtension");
|
|
ModAuthors = STR("FerrenFX");
|
|
|
|
s_instance = this;
|
|
}
|
|
|
|
~PalUIExtension() override
|
|
{
|
|
// Order matters: hooks first, so nothing can fire a callback into features that are about to be destroyed.
|
|
Static::EventHookRegistry::Cleanup();
|
|
m_features.clear();
|
|
|
|
if (s_instance == this) s_instance = nullptr;
|
|
}
|
|
|
|
auto on_unreal_init() -> void override
|
|
{
|
|
if (m_installed) return;
|
|
|
|
BuildFeatures();
|
|
|
|
Static::EventHookRegistry::Initialize(m_features.size() * 4 + 2);
|
|
|
|
for (auto& Feature : m_features)
|
|
{
|
|
Feature->InstallHooks();
|
|
Output::send<LogLevel::Verbose>(
|
|
STR("[PalUIExtension] installed feature '{}'\n"), Feature->Name());
|
|
}
|
|
|
|
InstallWorldLifecycleHooks();
|
|
|
|
m_installed = true;
|
|
}
|
|
|
|
private:
|
|
auto BuildFeatures() -> void
|
|
{
|
|
m_features.push_back(std::make_unique<Features::PalBoxPalNumber>());
|
|
m_features.push_back(std::make_unique<Features::PalBoxNightWorkIndicator>());
|
|
m_features.push_back(std::make_unique<Features::PalStatusPalNumber>());
|
|
}
|
|
|
|
auto InstallWorldLifecycleHooks() -> void
|
|
{
|
|
const auto RestartId = Static::EventHookRegistry::Register(
|
|
STR("PalUIExtension::GameModeRestart"),
|
|
Api::Pal::PalGameMode::RestartGamePath(),
|
|
&PalUIExtension::OnRestartGamePre, // PRE: the world is still intact
|
|
nullptr,
|
|
this);
|
|
|
|
if (RestartId == Static::EventHookRegistry::InvalidHook)
|
|
{
|
|
Output::send<LogLevel::Warning>(
|
|
STR("[PalUIExtension] RestartGame hook was rejected. Teardown falls back to "
|
|
"LoadMap only, which is later but still correct.\n"));
|
|
}
|
|
|
|
// Engine level. No CustomData parameter, hence s_instance.
|
|
Hook::RegisterLoadMapPreCallback(
|
|
[](UEngine*, FWorldContext&, FURL, UPendingNetGame*, FString&)
|
|
-> std::pair<bool, bool>
|
|
{
|
|
if (s_instance)
|
|
{
|
|
s_instance->TeardownAllFeatures(STR("LoadMap"));
|
|
}
|
|
|
|
return { true, true };
|
|
});
|
|
|
|
Output::send<LogLevel::Verbose>(
|
|
STR("[PalUIExtension] world lifecycle hooks installed (restart={})\n"), RestartId);
|
|
}
|
|
|
|
static void OnRestartGamePre(UnrealScriptFunctionCallableContext& Ctx, void* CustomData)
|
|
{
|
|
auto* Self = static_cast<PalUIExtension*>(CustomData);
|
|
if (!Self) return;
|
|
|
|
Self->TeardownAllFeatures(STR("RestartGame"));
|
|
}
|
|
auto TeardownAllFeatures(const TCHAR* Reason) -> void
|
|
{
|
|
|
|
Static::ClassCache::Flush();
|
|
Static::WidgetRegistry::BeginGeneration();
|
|
|
|
int32 TornDown = 0;
|
|
|
|
for (auto& Feature : m_features)
|
|
{
|
|
if (!Feature) continue;
|
|
|
|
try
|
|
{
|
|
Feature->OnWorldTeardown();
|
|
++TornDown;
|
|
}
|
|
catch (...)
|
|
{
|
|
Output::send<LogLevel::Error>(
|
|
STR("[PalUIExtension] '{}' threw during teardown; its cached pointers "
|
|
"are now stale.\n"), Feature->Name());
|
|
}
|
|
}
|
|
|
|
Output::send<LogLevel::Verbose>(
|
|
STR("[PalUIExtension] teardown via {}: {} features\n"), Reason, TornDown);
|
|
}
|
|
|
|
static auto to_wstring(const char* s) -> std::wstring
|
|
{
|
|
if (!s) return std::wstring{};
|
|
return std::wstring(s, s + std::strlen(s)); // ASCII only; what() is never non-ASCII here
|
|
}
|
|
};
|
|
}
|
|
|
|
#define MY_AWESOME_MOD_API __declspec(dllexport)
|
|
extern "C"
|
|
{
|
|
MY_AWESOME_MOD_API RC::CppUserModBase* start_mod()
|
|
{
|
|
return new PalUIExtension::PalUIExtension();
|
|
}
|
|
|
|
MY_AWESOME_MOD_API void uninstall_mod(RC::CppUserModBase* mod)
|
|
{
|
|
delete mod;
|
|
}
|
|
} |