From ecf1e680d6fbeb5aba40aa8df51706a18c8b7828 Mon Sep 17 00:00:00 2001 From: MiTHRAL Date: Mon, 24 Aug 2026 13:54:41 -0400 Subject: [PATCH] =?UTF-8?q?v2.1.5:=20self-learning=20ownership=20=E2=80=94?= =?UTF-8?q?=20inject=20queried=20out-of-catalog=20defs=20(Supporter/DLC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User session traces: only defs 7 & 51 queried all session, both OUTSIDE the 59-id store catalog -> those are DLC-granted items (Supporter flashlights). Blind 1..256 sweep polluted userItems and broke pet display (reverted v2.1.4). Now hUserHasItem/hPlayerHasItem remember every def the game asks about into a global registry; fabricateOwnership() injects any remembered-but-missing id on its next 15s cycle. Only game-validated ids enter the list — converges to everything-owned without garbage. --- src/il2cpp_resolver.cpp | 18 +++++++++++++++++- src/il2cpp_resolver.hpp | 7 +++++++ src/patches.cpp | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/il2cpp_resolver.cpp b/src/il2cpp_resolver.cpp index 44ef4b5..ee8b285 100644 --- a/src/il2cpp_resolver.cpp +++ b/src/il2cpp_resolver.cpp @@ -17,6 +17,10 @@ namespace devour { +static std::set g_queriedDefs; +std::set& QueriedDefs(){ return g_queriedDefs; } +void RememberQueriedDef(uint64_t id){ g_queriedDefs.insert(id); } + #ifdef _WIN32 static HMODULE g_gameAsm = nullptr; @@ -560,7 +564,19 @@ int Il2CppResolver::fabricateOwnership(){ for(int i=0;i #include #include +#include // Minimal IL2CPP types — avoids needing il2cpp headers at compile time struct Il2CppString { void* klass; void* monitor; int32_t length; wchar_t chars[1]; }; @@ -9,6 +10,12 @@ struct MethodInfo; namespace devour { +// Def ids the game itself asked UserHasItem/PlayerHasItem about (DLC/promo defs +// absent from the store catalog live here). Fabricator injects these too. +std::set& QueriedDefs(); +void RememberQueriedDef(uint64_t id); + + // Resolves IL2CPP method pointers without hard offsets. // Strategy 1: via il2cpp exports (class/method lookup) // Strategy 2: xref string sigscan as fallback diff --git a/src/patches.cpp b/src/patches.cpp index bda572a..2ba3af8 100644 --- a/src/patches.cpp +++ b/src/patches.cpp @@ -91,6 +91,7 @@ static void __fastcall hUpdateLockedRobes(void* thiz, void* method) { if (oUpdRo // LOGS every query — if the pet UI never appears in the log, it reads the userItems list field directly. static bool __fastcall hUserHasItem(void* thiz, unsigned long long itemDef, void* method) { // pass-through + trace (v2.1.0 proved forcing true blocks purchases) + RememberQueriedDef((uint64_t)itemDef); char b[96]; snprintf(b,sizeof(b),"[trace] UserHasItem(def=%llu)", (unsigned long long)itemDef); logPatch(b); if (oUserHasItem) return oUserHasItem(thiz, itemDef, method); return false; @@ -99,6 +100,7 @@ static bool __fastcall hUserHasItem(void* thiz, unsigned long long itemDef, void static bool __fastcall hPlayerHasItem(void* thiz, int connectionId, unsigned long long itemDef, void* method) { // MUST stay pass-through: forcing true desyncs lobby validation vs injected // userItems and UNLOCKS REGRESS (pets locked again). See v2.1.1 regression. + RememberQueriedDef((uint64_t)itemDef); char b[128]; snprintf(b,sizeof(b),"[trace] PlayerHasItem(conn=%d def=%llu)", connectionId, (unsigned long long)itemDef); logPatch(b); if (oPlayerHasItem) return oPlayerHasItem(thiz, connectionId, itemDef, method); return false;