add spam protection cvar, prevent right click pvp attack, attempt to fix/log crashes on launch

This commit is contained in:
avitasia
2025-09-29 10:39:17 -07:00
parent dc38b57122
commit 288fe1c4e8
7 changed files with 238 additions and 47 deletions
+135 -33
View File
@@ -45,6 +45,8 @@
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstdio>
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE, uint32_t, void *);
@@ -155,8 +157,8 @@ namespace Nampower {
function(code, "Unused");
}
uintptr_t* GetLuaStatePtr() {
typedef uintptr_t* (__fastcall* GETCONTEXT)(void);
uintptr_t *GetLuaStatePtr() {
typedef uintptr_t *(__fastcall *GETCONTEXT)(void);
static auto p_GetContext = reinterpret_cast<GETCONTEXT>(0x7040D0);
return p_GetContext();
}
@@ -478,12 +480,22 @@ namespace Nampower {
// combat check from Script_UnitAffectingCombat
// only prevent right click if in combat
if (unit && ((*(uint32_t *) (*(int32_t *) (unit + 0x110) + 0xa0) >> 0x13 & 1) != 0)) {
if (unit && game::UnitIsInCombat(unit)) {
return 1;
}
}
}
if (gUserSettings.preventRightClickPvPAttack) {
// check if clicking player
auto playerUnit = game::ClntObjMgrObjectPtr(game::TYPEMASK_PLAYER, objectGUID);
if (playerUnit && game::UnitIsPvpFlagged(playerUnit)) {
// check if they have pvp enabled
return 1;
}
}
return onSpriteRightClick(objectGUID);
}
@@ -543,10 +555,18 @@ namespace Nampower {
gUserSettings.preventRightClickTargetChange = atoi(value) != 0;
DEBUG_LOG("Set NP_PreventRightClickTargetChange to " << gUserSettings.preventRightClickTargetChange);
} else if (strcmp(cvar, "NP_PreventRightClickPvPAttack") == 0) {
gUserSettings.preventRightClickPvPAttack = atoi(value) != 0;
DEBUG_LOG("Set NP_PreventRightClickPvPAttack to " << gUserSettings.preventRightClickPvPAttack);
} else if (strcmp(cvar, "NP_DoubleCastToEndChannelEarly") == 0) {
gUserSettings.doubleCastToEndChannelEarly = atoi(value) != 0;
DEBUG_LOG("Set NP_DoubleCastToEndChannelEarly to " << gUserSettings.doubleCastToEndChannelEarly);
} else if (strcmp(cvar, "NP_SpamProtectionEnabled") == 0) {
gUserSettings.spamProtectionEnabled = atoi(value) != 0;
DEBUG_LOG("Set NP_SpamProtectionEnabled to " << gUserSettings.spamProtectionEnabled);
} else if (strcmp(cvar, "NP_MinBufferTimeMs") == 0) {
gUserSettings.minBufferTimeMs = atoi(value);
DEBUG_LOG("Set NP_MinBufferTimeMs and current buffer to " << gUserSettings.minBufferTimeMs);
@@ -624,19 +644,43 @@ namespace Nampower {
}
}
bool isFileInUse(const char *filename) {
HANDLE file = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
if (error == ERROR_SHARING_VIOLATION || error == ERROR_LOCK_VIOLATION) {
return true;
}
return false;
}
CloseHandle(file);
return false;
}
bool safeRemove(const char *filename) {
if (isFileInUse(filename)) {
return false;
}
return remove(filename) == 0;
}
bool safeRename(const char *oldname, const char *newname) {
if (isFileInUse(oldname) || isFileInUse(newname)) {
return false;
}
return rename(oldname, newname) == 0;
}
void loadConfig() {
gStartTime = static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count());
try {
// remove/rename previous logs
remove("nampower_debug.log.3");
rename("nampower_debug.log.2", "nampower_debug.log.3");
rename("nampower_debug.log.1", "nampower_debug.log.2");
rename("nampower_debug.log", "nampower_debug.log.1");
} catch (...) {
// ignore any exceptions during log rotation
}
// remove/rename previous logs
safeRemove("nampower_debug.log.3");
safeRename("nampower_debug.log.2", "nampower_debug.log.3");
safeRename("nampower_debug.log.1", "nampower_debug.log.2");
safeRename("nampower_debug.log", "nampower_debug.log.1");
// open new log file
@@ -660,9 +704,12 @@ namespace Nampower {
gUserSettings.optimizeBufferUsingPacketTimings = false;
gUserSettings.preventRightClickTargetChange = false;
gUserSettings.preventRightClickPvPAttack = true;
gUserSettings.doubleCastToEndChannelEarly = false;
gUserSettings.spamProtectionEnabled = true;
gUserSettings.minBufferTimeMs = 55; // time in ms to buffer cast to minimize server failure
gUserSettings.nonGcdBufferTimeMs = 100; // time in ms to buffer non-GCD spells to minimize server failure
gUserSettings.maxBufferIncreaseMs = 30;
@@ -885,6 +932,16 @@ namespace Nampower {
0, // unk2
0); // unk3
char NP_PreventRightClickPvPAttack[] = "NP_PreventRightClickPvPAttack";
CVarRegister(NP_PreventRightClickPvPAttack, // name
nullptr, // help
0, // unk1
gUserSettings.preventRightClickPvPAttack ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_DoubleCastToEndChannelEarly[] = "NP_DoubleCastToEndChannelEarly";
CVarRegister(NP_DoubleCastToEndChannelEarly, // name
nullptr, // help
@@ -895,6 +952,16 @@ namespace Nampower {
0, // unk2
0); // unk3
char NP_SpamProtectionEnabled[] = "NP_SpamProtectionEnabled";
CVarRegister(NP_SpamProtectionEnabled, // name
nullptr, // help
0, // unk1
gUserSettings.spamProtectionEnabled ? defaultTrue : defaultFalse, // default value address
nullptr, // callback
1, // category
0, // unk2
0); // unk3
char NP_ChannelLatencyReductionPercentage[] = "NP_ChannelLatencyReductionPercentage";
CVarRegister(NP_ChannelLatencyReductionPercentage, // name
nullptr, // help
@@ -932,8 +999,12 @@ namespace Nampower {
loadUserVar("NP_PreventRightClickTargetChange");
loadUserVar("NP_PreventRightClickPvPAttack");
loadUserVar("NP_DoubleCastToEndChannelEarly");
loadUserVar("NP_SpamProtectionEnabled");
loadUserVar("NP_MinBufferTimeMs");
loadUserVar("NP_NonGcdBufferTimeMs");
loadUserVar("NP_MaxBufferIncreaseMs");
@@ -975,7 +1046,8 @@ namespace Nampower {
// Template function to simplify hook initialization with specific storage
template<typename FuncT, typename HookT>
std::unique_ptr<hadesmem::PatchDetour<FuncT>> createHook(const hadesmem::Process& process, Offsets offset, HookT hookFunc) {
std::unique_ptr<hadesmem::PatchDetour<FuncT>>
createHook(const hadesmem::Process &process, Offsets offset, HookT hookFunc) {
auto const originalFunc = hadesmem::detail::AliasCast<FuncT>(offset);
auto detour = std::make_unique<hadesmem::PatchDetour<FuncT>>(process, originalFunc, hookFunc);
detour->Apply();
@@ -991,30 +1063,50 @@ namespace Nampower {
gCastDetour = createHook<CastSpellT>(process, Offsets::Spell_C_CastSpell, &Spell_C_CastSpellHook);
gSendCastDetour = createHook<SendCastT>(process, Offsets::SendCast, &SendCastHook);
gCancelSpellDetour = createHook<CancelSpellT>(process, Offsets::CancelSpell, &CancelSpellHook);
gCastResultHandlerDetour = createHook<PacketHandlerT>(process, Offsets::CastResultHandler, &CastResultHandlerHook);
gSpellStartHandlerDetour = createHook<FastCallPacketHandlerT>(process, Offsets::SpellStartHandler, &SpellStartHandlerHook);
gPeriodicAuraLogHandlerDetour = createHook<FastCallPacketHandlerT>(process, Offsets::PeriodicAuraLogHandler, &PeriodicAuraLogHandlerHook);
gSpellNonMeleeDmgLogHandlerDetour = createHook<FastCallPacketHandlerT>(process, Offsets::SpellNonMeleeDmgLogHandler, &SpellNonMeleeDmgLogHandlerHook);
gSpellChannelStartHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellChannelStartHandler, &SpellChannelStartHandlerHook);
gSpellChannelUpdateHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellChannelUpdateHandler, &SpellChannelUpdateHandlerHook);
gSpellFailedDetour = createHook<Spell_C_SpellFailedT>(process, Offsets::Spell_C_SpellFailed, &Spell_C_SpellFailedHook);
gCastResultHandlerDetour = createHook<PacketHandlerT>(process, Offsets::CastResultHandler,
&CastResultHandlerHook);
gSpellStartHandlerDetour = createHook<FastCallPacketHandlerT>(process, Offsets::SpellStartHandler,
&SpellStartHandlerHook);
gPeriodicAuraLogHandlerDetour = createHook<FastCallPacketHandlerT>(process, Offsets::PeriodicAuraLogHandler,
&PeriodicAuraLogHandlerHook);
gSpellNonMeleeDmgLogHandlerDetour = createHook<FastCallPacketHandlerT>(process,
Offsets::SpellNonMeleeDmgLogHandler,
&SpellNonMeleeDmgLogHandlerHook);
gSpellChannelStartHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellChannelStartHandler,
&SpellChannelStartHandlerHook);
gSpellChannelUpdateHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellChannelUpdateHandler,
&SpellChannelUpdateHandlerHook);
gSpellFailedDetour = createHook<Spell_C_SpellFailedT>(process, Offsets::Spell_C_SpellFailed,
&Spell_C_SpellFailedHook);
gSpellGoDetour = createHook<SpellGoT>(process, Offsets::SpellGo, &SpellGoHook);
gSpellDelayedDetour = createHook<PacketHandlerT>(process, Offsets::SpellDelayed, &SpellDelayedHook);
gSpellTargetUnitDetour = createHook<LuaScriptT>(process, Offsets::Script_SpellTargetUnit, &Script_SpellTargetUnitHook);
gSpellStopCastingDetour = createHook<LuaScriptT>(process, Offsets::Script_SpellStopCasting, &Script_SpellStopCastingHook);
gSpell_C_TargetSpellDetour = createHook<Spell_C_TargetSpellT>(process, Offsets::Spell_C_TargetSpell, &Spell_C_TargetSpellHook);
gCastSpellByNameNoQueueDetour = createHook<LuaScriptT>(process, Offsets::Script_CastSpellByNameNoQueue, Script_CastSpellByNameNoQueue);
gQueueSpellByNameDetour = createHook<LuaScriptT>(process, Offsets::Script_QueueSpellByName, Script_QueueSpellByName);
gSpellTargetUnitDetour = createHook<LuaScriptT>(process, Offsets::Script_SpellTargetUnit,
&Script_SpellTargetUnitHook);
gSpellStopCastingDetour = createHook<LuaScriptT>(process, Offsets::Script_SpellStopCasting,
&Script_SpellStopCastingHook);
gSpell_C_TargetSpellDetour = createHook<Spell_C_TargetSpellT>(process, Offsets::Spell_C_TargetSpell,
&Spell_C_TargetSpellHook);
gCastSpellByNameNoQueueDetour = createHook<LuaScriptT>(process, Offsets::Script_CastSpellByNameNoQueue,
Script_CastSpellByNameNoQueue);
gQueueSpellByNameDetour = createHook<LuaScriptT>(process, Offsets::Script_QueueSpellByName,
Script_QueueSpellByName);
qQueueScriptDetour = createHook<LuaScriptT>(process, Offsets::Script_QueueScript, Script_QueueScript);
gIsSpellInRangeDetour = createHook<LuaScriptT>(process, Offsets::Script_IsSpellInRange, Script_IsSpellInRange);
gIsSpellUsableDetour = createHook<LuaScriptT>(process, Offsets::Script_IsSpellUsable, Script_IsSpellUsable);
gGetCurrentCastingInfoDetour = createHook<LuaScriptT>(process, Offsets::Script_GetCurrentCastingInfo, Script_GetCurrentCastingInfo);
gGetSpellIdForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellIdForName, Script_GetSpellIdForName);
gGetSpellNameAndRankForIdDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellNameAndRankForId, Script_GetSpellNameAndRankForId);
gGetSpellSlotAndTypeForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellSlotTypeIdForName, Script_GetSpellSlotTypeIdForName);
gOnSpriteRightClickDetour = createHook<OnSpriteRightClickT>(process, Offsets::OnSpriteRightClick, OnSpriteRightClickHook);
gChannelStopCastingNextTickDetour = createHook<LuaScriptT>(process, Offsets::Script_ChannelStopCastingNextTick, Script_ChannelStopCastingNextTick);
gGetNampowerVersionDetour = createHook<LuaScriptT>(process, Offsets::Script_GetNampowerVersion, Script_GetNampowerVersion);
gGetCurrentCastingInfoDetour = createHook<LuaScriptT>(process, Offsets::Script_GetCurrentCastingInfo,
Script_GetCurrentCastingInfo);
gGetSpellIdForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellIdForName,
Script_GetSpellIdForName);
gGetSpellNameAndRankForIdDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellNameAndRankForId,
Script_GetSpellNameAndRankForId);
gGetSpellSlotAndTypeForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellSlotTypeIdForName,
Script_GetSpellSlotTypeIdForName);
gOnSpriteRightClickDetour = createHook<OnSpriteRightClickT>(process, Offsets::OnSpriteRightClick,
OnSpriteRightClickHook);
gChannelStopCastingNextTickDetour = createHook<LuaScriptT>(process, Offsets::Script_ChannelStopCastingNextTick,
Script_ChannelStopCastingNextTick);
gGetNampowerVersionDetour = createHook<LuaScriptT>(process, Offsets::Script_GetNampowerVersion,
Script_GetNampowerVersion);
gGetItemLevelDetour = createHook<LuaScriptT>(process, Offsets::Script_GetItemLevel, Script_GetItemLevel);
gIEndSceneDetour = createHook<ISceneEndT>(process, Offsets::ISceneEndPtr, &ISceneEndHook);
}
@@ -1119,6 +1211,16 @@ namespace Nampower {
}
extern "C" __declspec(dllexport) uint32_t Load() {
Nampower::load();
try {
Nampower::load();
} catch (const std::exception &e) {
std::string errorMsg = "Exception in Nampower::load(): ";
errorMsg += e.what();
MessageBoxA(nullptr, errorMsg.c_str(), "Nampower Error", MB_OK | MB_ICONERROR);
return EXIT_FAILURE;
} catch (...) {
MessageBoxA(nullptr, "Unknown exception in Nampower::load()", "Nampower Error", MB_OK | MB_ICONERROR);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}