fix(runtime): guard animated texture crashes

This commit is contained in:
Jacobwasbeast
2026-03-07 21:21:41 -06:00
parent ed78317b96
commit b924105102
6 changed files with 253 additions and 1 deletions

View File

@@ -88,6 +88,24 @@ static bool IsFatalException(DWORD code)
}
}
static bool ShouldWriteVectoredReport(EXCEPTION_RECORD* er)
{
if (!er)
return false;
switch (er->ExceptionCode)
{
case STATUS_STACK_BUFFER_OVERRUN:
case STATUS_INVALID_CRUNTIME_PARAMETER:
case STATUS_FAIL_FAST_EXCEPTION:
case EXCEPTION_STACK_OVERFLOW:
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return true;
default:
return false;
}
}
static void GetModuleForAddr(DWORD64 addr, char* nameBuf, size_t nameBufSize, DWORD64* outBase)
{
*outBase = 0;
@@ -353,7 +371,16 @@ static LONG WINAPI VectoredHandler(EXCEPTION_POINTERS* ep)
if (!IsFatalException(code))
return EXCEPTION_CONTINUE_SEARCH;
WriteCrashReport("VectoredExceptionHandler", ep->ExceptionRecord, ep->ContextRecord);
// TODO: Remove this suppression once the animated texture pack fault is fixed
// at the source. For now, a vectored handler sees first-chance exceptions
// before local __try/__except blocks run, and logging those handled faults
// as crashes causes severe log spam and stalls under Windows.
//
// Keep vectored reporting only for fail-fast/noncontinuable cases that will
// not normally make it to the top-level unhandled filter.
if (ShouldWriteVectoredReport(ep->ExceptionRecord))
WriteCrashReport("VectoredExceptionHandler", ep->ExceptionRecord, ep->ContextRecord);
return EXCEPTION_CONTINUE_SEARCH;
}

View File

@@ -31,6 +31,12 @@ namespace GameHooks
ItemInstanceGetIcon_fn Original_ItemInstanceGetIcon = nullptr;
EntityRendererBindTextureResource_fn Original_EntityRendererBindTextureResource = nullptr;
ItemRendererRenderItemBillboard_fn Original_ItemRendererRenderItemBillboard = nullptr;
AnimatedTextureCycleFrames_fn Original_CompassTextureCycleFrames = nullptr;
AnimatedTextureCycleFrames_fn Original_ClockTextureCycleFrames = nullptr;
TextureGetSourceDim_fn Original_CompassTextureGetSourceWidth = nullptr;
TextureGetSourceDim_fn Original_CompassTextureGetSourceHeight = nullptr;
TextureGetSourceDim_fn Original_ClockTextureGetSourceWidth = nullptr;
TextureGetSourceDim_fn Original_ClockTextureGetSourceHeight = nullptr;
ItemInstanceMineBlock_fn Original_ItemInstanceMineBlock = nullptr;
ItemMineBlock_fn Original_ItemMineBlock = nullptr;
ItemMineBlock_fn Original_DiggerItemMineBlock = nullptr;
@@ -76,6 +82,7 @@ namespace GameHooks
static thread_local bool s_hasForcedBillboardRoute = false;
static thread_local int s_forcedBillboardAtlas = -1;
static thread_local int s_forcedBillboardPage = 0;
static int s_animatedTextureGuardLogCount = 0;
struct TextureNameArrayNative
{
@@ -1062,6 +1069,102 @@ namespace GameHooks
s_forcedBillboardPage = prevPage;
}
static void LogAnimatedTextureGuard(const char* what, void* thisPtr)
{
if (s_animatedTextureGuardLogCount >= 12)
return;
LogUtil::Log("[WeaveLoader] AnimatedTextureGuard: %s fallback for %p", what, thisPtr);
s_animatedTextureGuardLogCount++;
}
void __fastcall Hooked_CompassTextureCycleFrames(void* thisPtr)
{
if (!Original_CompassTextureCycleFrames)
return;
__try
{
Original_CompassTextureCycleFrames(thisPtr);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
LogAnimatedTextureGuard("CompassTexture::cycleFrames", thisPtr);
}
}
void __fastcall Hooked_ClockTextureCycleFrames(void* thisPtr)
{
if (!Original_ClockTextureCycleFrames)
return;
__try
{
Original_ClockTextureCycleFrames(thisPtr);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
LogAnimatedTextureGuard("ClockTexture::cycleFrames", thisPtr);
}
}
int __fastcall Hooked_CompassTextureGetSourceWidth(void* thisPtr)
{
if (!Original_CompassTextureGetSourceWidth)
return 16;
__try
{
return Original_CompassTextureGetSourceWidth(thisPtr);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
LogAnimatedTextureGuard("CompassTexture::getSourceWidth", thisPtr);
return 16;
}
}
int __fastcall Hooked_CompassTextureGetSourceHeight(void* thisPtr)
{
if (!Original_CompassTextureGetSourceHeight)
return 16;
__try
{
return Original_CompassTextureGetSourceHeight(thisPtr);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
LogAnimatedTextureGuard("CompassTexture::getSourceHeight", thisPtr);
return 16;
}
}
int __fastcall Hooked_ClockTextureGetSourceWidth(void* thisPtr)
{
if (!Original_ClockTextureGetSourceWidth)
return 16;
__try
{
return Original_ClockTextureGetSourceWidth(thisPtr);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
LogAnimatedTextureGuard("ClockTexture::getSourceWidth", thisPtr);
return 16;
}
}
int __fastcall Hooked_ClockTextureGetSourceHeight(void* thisPtr)
{
if (!Original_ClockTextureGetSourceHeight)
return 16;
__try
{
return Original_ClockTextureGetSourceHeight(thisPtr);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
LogAnimatedTextureGuard("ClockTexture::getSourceHeight", thisPtr);
return 16;
}
}
void __fastcall Hooked_ItemInstanceMineBlock(void* thisPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr)
{
s_itemMineBlockHookCalls++;

View File

@@ -21,6 +21,8 @@ typedef void* (__fastcall *RegisterIcon_fn)(void* thisPtr, const std::wstring& n
typedef void* (__fastcall *ItemInstanceGetIcon_fn)(void* thisPtr);
typedef void (__fastcall *EntityRendererBindTextureResource_fn)(void* thisPtr, void* resourcePtr);
typedef void (__fastcall *ItemRendererRenderItemBillboard_fn)(void* thisPtr, void* entitySharedPtr, void* iconPtr, int count, float a, float red, float green, float blue);
typedef void (__fastcall *AnimatedTextureCycleFrames_fn)(void* thisPtr);
typedef int (__fastcall *TextureGetSourceDim_fn)(void* thisPtr);
typedef void (__fastcall *ItemInstanceMineBlock_fn)(void* thisPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);
typedef bool (__fastcall *ItemMineBlock_fn)(void* thisPtr, void* itemInstanceSharedPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);
typedef bool (__fastcall *GameModeUseItem_fn)(void* thisPtr, void* playerSharedPtr, void* level, void* itemInstanceSharedPtr, bool bTestUseOnly);
@@ -57,6 +59,12 @@ namespace GameHooks
extern ItemInstanceGetIcon_fn Original_ItemInstanceGetIcon;
extern EntityRendererBindTextureResource_fn Original_EntityRendererBindTextureResource;
extern ItemRendererRenderItemBillboard_fn Original_ItemRendererRenderItemBillboard;
extern AnimatedTextureCycleFrames_fn Original_CompassTextureCycleFrames;
extern AnimatedTextureCycleFrames_fn Original_ClockTextureCycleFrames;
extern TextureGetSourceDim_fn Original_CompassTextureGetSourceWidth;
extern TextureGetSourceDim_fn Original_CompassTextureGetSourceHeight;
extern TextureGetSourceDim_fn Original_ClockTextureGetSourceWidth;
extern TextureGetSourceDim_fn Original_ClockTextureGetSourceHeight;
extern ItemInstanceMineBlock_fn Original_ItemInstanceMineBlock;
extern ItemMineBlock_fn Original_ItemMineBlock;
extern ItemMineBlock_fn Original_DiggerItemMineBlock;
@@ -86,6 +94,12 @@ namespace GameHooks
void* __fastcall Hooked_ItemInstanceGetIcon(void* thisPtr);
void __fastcall Hooked_EntityRendererBindTextureResource(void* thisPtr, void* resourcePtr);
void __fastcall Hooked_ItemRendererRenderItemBillboard(void* thisPtr, void* entitySharedPtr, void* iconPtr, int count, float a, float red, float green, float blue);
void __fastcall Hooked_CompassTextureCycleFrames(void* thisPtr);
void __fastcall Hooked_ClockTextureCycleFrames(void* thisPtr);
int __fastcall Hooked_CompassTextureGetSourceWidth(void* thisPtr);
int __fastcall Hooked_CompassTextureGetSourceHeight(void* thisPtr);
int __fastcall Hooked_ClockTextureGetSourceWidth(void* thisPtr);
int __fastcall Hooked_ClockTextureGetSourceHeight(void* thisPtr);
void __fastcall Hooked_ItemInstanceMineBlock(void* thisPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);
bool __fastcall Hooked_ItemMineBlock(void* thisPtr, void* itemInstanceSharedPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);
bool __fastcall Hooked_DiggerItemMineBlock(void* thisPtr, void* itemInstanceSharedPtr, void* level, int tile, int x, int y, int z, void* ownerSharedPtr);

View File

@@ -124,6 +124,90 @@ bool HookManager::Install(const SymbolResolver& symbols)
}
}
if (symbols.pCompassTextureCycleFrames)
{
if (MH_CreateHook(symbols.pCompassTextureCycleFrames,
reinterpret_cast<void*>(&GameHooks::Hooked_CompassTextureCycleFrames),
reinterpret_cast<void**>(&GameHooks::Original_CompassTextureCycleFrames)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook CompassTexture::cycleFrames");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked CompassTexture::cycleFrames (texture pack crash guard)");
}
}
if (symbols.pClockTextureCycleFrames)
{
if (MH_CreateHook(symbols.pClockTextureCycleFrames,
reinterpret_cast<void*>(&GameHooks::Hooked_ClockTextureCycleFrames),
reinterpret_cast<void**>(&GameHooks::Original_ClockTextureCycleFrames)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook ClockTexture::cycleFrames");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked ClockTexture::cycleFrames (texture pack crash guard)");
}
}
if (symbols.pCompassTextureGetSourceWidth)
{
if (MH_CreateHook(symbols.pCompassTextureGetSourceWidth,
reinterpret_cast<void*>(&GameHooks::Hooked_CompassTextureGetSourceWidth),
reinterpret_cast<void**>(&GameHooks::Original_CompassTextureGetSourceWidth)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook CompassTexture::getSourceWidth");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked CompassTexture::getSourceWidth (texture pack crash guard)");
}
}
if (symbols.pCompassTextureGetSourceHeight)
{
if (MH_CreateHook(symbols.pCompassTextureGetSourceHeight,
reinterpret_cast<void*>(&GameHooks::Hooked_CompassTextureGetSourceHeight),
reinterpret_cast<void**>(&GameHooks::Original_CompassTextureGetSourceHeight)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook CompassTexture::getSourceHeight");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked CompassTexture::getSourceHeight (texture pack crash guard)");
}
}
if (symbols.pClockTextureGetSourceWidth)
{
if (MH_CreateHook(symbols.pClockTextureGetSourceWidth,
reinterpret_cast<void*>(&GameHooks::Hooked_ClockTextureGetSourceWidth),
reinterpret_cast<void**>(&GameHooks::Original_ClockTextureGetSourceWidth)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook ClockTexture::getSourceWidth");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked ClockTexture::getSourceWidth (texture pack crash guard)");
}
}
if (symbols.pClockTextureGetSourceHeight)
{
if (MH_CreateHook(symbols.pClockTextureGetSourceHeight,
reinterpret_cast<void*>(&GameHooks::Hooked_ClockTextureGetSourceHeight),
reinterpret_cast<void**>(&GameHooks::Original_ClockTextureGetSourceHeight)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook ClockTexture::getSourceHeight");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked ClockTexture::getSourceHeight (texture pack crash guard)");
}
}
if (symbols.pItemMineBlock)
{
if (MH_CreateHook(symbols.pItemMineBlock,

View File

@@ -21,6 +21,12 @@ static const char* SYM_REGISTER_ICON = "?registerIcon@PreStitchedTextureMap@@UEA
static const char* SYM_ITEMINSTANCE_GETICON = "?getIcon@ItemInstance@@QEAAPEAVIcon@@XZ";
static const char* SYM_ENTITYRENDERER_BINDTEXTURE_RESOURCE = "?bindTexture@EntityRenderer@@MEAAXPEAVResourceLocation@@@Z";
static const char* SYM_ITEMRENDERER_RENDERITEMBILLBOARD = "?renderItemBillboard@ItemRenderer@@EEAAXV?$shared_ptr@VItemEntity@@@std@@PEAVIcon@@HMMMM@Z";
static const char* SYM_COMPASS_CYCLEFRAMES = "?cycleFrames@CompassTexture@@UEAAXXZ";
static const char* SYM_CLOCK_CYCLEFRAMES = "?cycleFrames@ClockTexture@@UEAAXXZ";
static const char* SYM_COMPASS_GETSOURCEWIDTH = "?getSourceWidth@CompassTexture@@UEBAHXZ";
static const char* SYM_COMPASS_GETSOURCEHEIGHT = "?getSourceHeight@CompassTexture@@UEBAHXZ";
static const char* SYM_CLOCK_GETSOURCEWIDTH = "?getSourceWidth@ClockTexture@@UEBAHXZ";
static const char* SYM_CLOCK_GETSOURCEHEIGHT = "?getSourceHeight@ClockTexture@@UEBAHXZ";
static const char* SYM_ITEMINSTANCE_MINEBLOCK = "?mineBlock@ItemInstance@@QEAAXPEAVLevel@@HHHHV?$shared_ptr@VPlayer@@@std@@@Z";
static const char* SYM_ITEM_MINEBLOCK = "?mineBlock@Item@@UEAA_NV?$shared_ptr@VItemInstance@@@std@@PEAVLevel@@HHHHV?$shared_ptr@VLivingEntity@@@3@@Z";
static const char* SYM_DIGGERITEM_MINEBLOCK = "?mineBlock@DiggerItem@@UEAA_NV?$shared_ptr@VItemInstance@@@std@@PEAVLevel@@HHHHV?$shared_ptr@VLivingEntity@@@3@@Z";
@@ -122,6 +128,12 @@ bool SymbolResolver::ResolveGameFunctions()
pItemInstanceGetIcon = Resolve(SYM_ITEMINSTANCE_GETICON);
pEntityRendererBindTextureResource = Resolve(SYM_ENTITYRENDERER_BINDTEXTURE_RESOURCE);
pItemRendererRenderItemBillboard = Resolve(SYM_ITEMRENDERER_RENDERITEMBILLBOARD);
pCompassTextureCycleFrames = Resolve(SYM_COMPASS_CYCLEFRAMES);
pClockTextureCycleFrames = Resolve(SYM_CLOCK_CYCLEFRAMES);
pCompassTextureGetSourceWidth = Resolve(SYM_COMPASS_GETSOURCEWIDTH);
pCompassTextureGetSourceHeight = Resolve(SYM_COMPASS_GETSOURCEHEIGHT);
pClockTextureGetSourceWidth = Resolve(SYM_CLOCK_GETSOURCEWIDTH);
pClockTextureGetSourceHeight = Resolve(SYM_CLOCK_GETSOURCEHEIGHT);
pItemInstanceMineBlock = Resolve(SYM_ITEMINSTANCE_MINEBLOCK);
pItemMineBlock = Resolve(SYM_ITEM_MINEBLOCK);
pDiggerItemMineBlock = Resolve(SYM_DIGGERITEM_MINEBLOCK);
@@ -181,6 +193,12 @@ bool SymbolResolver::ResolveGameFunctions()
logSym("ItemInstance::getIcon", pItemInstanceGetIcon);
logSym("EntityRenderer::bindTexture(ResourceLocation)", pEntityRendererBindTextureResource);
logSym("ItemRenderer::renderItemBillboard", pItemRendererRenderItemBillboard);
logSym("CompassTexture::cycleFrames", pCompassTextureCycleFrames);
logSym("ClockTexture::cycleFrames", pClockTextureCycleFrames);
logSym("CompassTexture::getSourceWidth", pCompassTextureGetSourceWidth);
logSym("CompassTexture::getSourceHeight", pCompassTextureGetSourceHeight);
logSym("ClockTexture::getSourceWidth", pClockTextureGetSourceWidth);
logSym("ClockTexture::getSourceHeight", pClockTextureGetSourceHeight);
logSym("ItemInstance::mineBlock", pItemInstanceMineBlock);
logSym("Item::mineBlock", pItemMineBlock);
logSym("DiggerItem::mineBlock", pDiggerItemMineBlock);

View File

@@ -26,6 +26,12 @@ public:
void* pItemInstanceGetIcon = nullptr; // ItemInstance::getIcon()
void* pEntityRendererBindTextureResource = nullptr; // EntityRenderer::bindTexture(ResourceLocation*)
void* pItemRendererRenderItemBillboard = nullptr; // ItemRenderer::renderItemBillboard(shared_ptr<ItemEntity>,Icon*,...)
void* pCompassTextureCycleFrames = nullptr; // CompassTexture::cycleFrames()
void* pClockTextureCycleFrames = nullptr; // ClockTexture::cycleFrames()
void* pCompassTextureGetSourceWidth = nullptr; // CompassTexture::getSourceWidth() const
void* pCompassTextureGetSourceHeight = nullptr; // CompassTexture::getSourceHeight() const
void* pClockTextureGetSourceWidth = nullptr; // ClockTexture::getSourceWidth() const
void* pClockTextureGetSourceHeight = nullptr; // ClockTexture::getSourceHeight() const
void* pItemInstanceMineBlock = nullptr; // ItemInstance::mineBlock(Level*,int,int,int,int,shared_ptr<Player>)
void* pItemMineBlock = nullptr; // Item::mineBlock(shared_ptr<ItemInstance>,Level*,int,int,int,int,shared_ptr<LivingEntity>)
void* pDiggerItemMineBlock = nullptr; // DiggerItem::mineBlock(shared_ptr<ItemInstance>,Level*,int,int,int,int,shared_ptr<LivingEntity>)