-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(game/five): Add natives to operate with drawables through collec…
…tions Added analogues to natives like SET_PED_COMPONENT_VARIATION that use collection name and local (relative to the start of collection) index instead of the global index. With the old natives indexes would often shift after Title update which would make migration to the newer game build harder. With the collection relative indexing this would not be a problem as the local indexes remain stable. The new natives basically provide interface to the base game functionality that already existed but was unavailable before.
- Loading branch information
1 parent
09c04d4
commit 7dad6da
Showing
21 changed files
with
901 additions
and
0 deletions.
There are no files selected for viewing
389 changes: 389 additions & 0 deletions
389
code/components/extra-natives-five/src/PedCollectionsNatives.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,389 @@ | ||
#include <StdInc.h> | ||
|
||
#include <atArray.h> | ||
#include <EntitySystem.h> | ||
#include <Hooking.h> | ||
#include <Local.h> | ||
#include <ScriptEngine.h> | ||
#include <Utils.h> | ||
|
||
class CPedPropInfo | ||
{ | ||
}; | ||
|
||
class CPedVariationInfo | ||
{ | ||
}; | ||
|
||
class CPedVariationInfoCollection | ||
{ | ||
public: | ||
atArray<CPedVariationInfo*> m_infos; | ||
// There are more fields after m_infos, but we don't care about them. | ||
}; | ||
|
||
static uint32_t g_collectionInfoHashOffset; | ||
static uint32_t g_dynamicEntityArchetypeOffset; | ||
static uint32_t g_pedModelInfoVarInfoCollectionOffset; | ||
static uint32_t g_variationInfoPropInfoOffset; | ||
|
||
static hook::cdecl_stub<int(CPedVariationInfoCollection*, int, uint32_t, uint32_t)> g_GetGlobalDrawableIndex([]() | ||
{ | ||
return hook::get_pattern("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 56 41 57 48 83 EC ? 0F B7 41 ? 33 F6 45 8B F1"); | ||
}); | ||
|
||
static hook::cdecl_stub<int(CPedVariationInfoCollection*, int, uint32_t, uint32_t)> g_GetGlobalPropIndex([]() | ||
{ | ||
return hook::get_pattern("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 56 41 57 48 83 EC ? 33 FF 45 8B F1"); | ||
}); | ||
|
||
static hook::cdecl_stub<int(CPedVariationInfoCollection*, uint32_t, uint32_t)> g_GetDlcDrawableIdx([]() | ||
{ | ||
return hook::get_call(hook::get_pattern("E8 ? ? ? ? 44 8B C3 48 8B 5D")); | ||
}); | ||
|
||
static hook::cdecl_stub<int(CPedVariationInfoCollection*, uint32_t, uint32_t)> g_GetDlcPropIdx([]() | ||
{ | ||
return hook::get_call(hook::get_pattern("E8 ? ? ? ? 45 33 F6 44 8B E8 44 38 75")); | ||
}); | ||
|
||
static hook::cdecl_stub<uint8_t(CPedVariationInfo*, uint32_t)> g_GetMaxNumDrawables([]() | ||
{ | ||
return hook::get_pattern("48 89 5C 24 ? 57 48 83 EC ? 8B DA 48 8B F9 E8 ? ? ? ? 48 85 C0 74 ? 8B D3"); | ||
}); | ||
|
||
static hook::cdecl_stub<uint8_t(CPedPropInfo*, uint32_t)> g_GetMaxNumProps([]() | ||
{ | ||
return hook::get_pattern("48 89 5C 24 ? 0F B7 41 ? 45 33 C0 45 8B C8 45 8B D0 8B D8"); | ||
}); | ||
|
||
static hook::cdecl_stub<CPedVariationInfo*(CPedVariationInfoCollection*, uint32_t, uint32_t)> g_GetVariationInfoFromDrawableIdx([]() | ||
{ | ||
return hook::get_pattern("48 8B C4 48 89 58 ? 48 89 68 ? 48 89 70 ? 48 89 78 ? 41 56 48 83 EC ? 33 DB 41 8B F0 8B EA 48 8B F9 66 3B 59 ? 73 ? 48 8B 0F"); | ||
}); | ||
|
||
static hook::cdecl_stub<CPedVariationInfo*(CPedVariationInfoCollection*, uint32_t, uint32_t)> g_GetVariationInfoFromPropIdx([]() | ||
{ | ||
return hook::get_pattern("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC ? 0F B7 41 ? 33 DB"); | ||
}); | ||
|
||
static hook::cdecl_stub<const char*(CPedVariationInfo*)> g_GetCollectionName([]() | ||
{ | ||
return hook::get_pattern("8B 51 ? 85 D2 75 ? 33 C0"); | ||
}); | ||
|
||
static const char* GetCollectionName(CPedVariationInfo* info) | ||
{ | ||
const char* collectionName = g_GetCollectionName(info); | ||
if (collectionName == nullptr) | ||
{ | ||
// g_GetCollectionName returns nullptr for empty string which corresponds to the base game collection. | ||
return ""; | ||
} | ||
else | ||
{ | ||
return collectionName; | ||
} | ||
} | ||
|
||
static uint32_t GetCollectionHash(CPedVariationInfo* info) | ||
{ | ||
return *(uint32_t*)((uintptr_t)info + g_collectionInfoHashOffset); | ||
}; | ||
|
||
static CPedVariationInfoCollection* GetPedVariationInfoCollection(uint32_t pedId) | ||
{ | ||
auto ped = rage::fwScriptGuid::GetBaseFromGuid(pedId); | ||
if (!ped || !ped->IsOfType<CPed>()) | ||
{ | ||
return nullptr; | ||
} | ||
|
||
auto pedModelInfo = *(void**)((uintptr_t)ped + g_dynamicEntityArchetypeOffset); | ||
return *(CPedVariationInfoCollection**)((uintptr_t)pedModelInfo + g_pedModelInfoVarInfoCollectionOffset); | ||
} | ||
|
||
static CPedVariationInfo* GetVariationInfoFromCollection(CPedVariationInfoCollection* self, const char* collectionName) | ||
{ | ||
uint32_t collectionNameHash = HashString(collectionName); | ||
for (int i = 0; i < self->m_infos.GetCount(); i++) | ||
{ | ||
auto variationInfo = self->m_infos[i]; | ||
if (variationInfo && GetCollectionHash(variationInfo) == collectionNameHash) | ||
{ | ||
return variationInfo; | ||
} | ||
} | ||
return nullptr; | ||
} | ||
|
||
static int GetGlobalIndex(uint32_t pedId, int slotId, const char* collectionName, int localIndex, hook::cdecl_stub<int(CPedVariationInfoCollection*, int, uint32_t, uint32_t)> indexGetter) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(pedId); | ||
if (!variationInfoCollection) | ||
{ | ||
return -1; | ||
} | ||
|
||
auto variationInfo = GetVariationInfoFromCollection(variationInfoCollection, collectionName); | ||
// Collection does not exist. | ||
if (!variationInfo) | ||
{ | ||
return -1; | ||
} | ||
|
||
uint32_t collectionNameHash = HashString(collectionName); | ||
return indexGetter(variationInfoCollection, localIndex, slotId, collectionNameHash); | ||
} | ||
|
||
template<typename T> | ||
static int VariadicGetArgument(fx::ScriptContext& context, fx::ScriptContextBuffer& newContext, int index) | ||
{ | ||
newContext.Push(context.GetArgument<T>(index)); | ||
return index + 1; | ||
} | ||
|
||
template<typename T, typename... AdditionalArguments> | ||
static void RedirectNativeCallWithGlobalIndex(fx::ScriptContext& context, hook::cdecl_stub<int(CPedVariationInfoCollection*, int, uint32_t, uint32_t)> indexGetter, uint64_t nativeIdentifier) | ||
{ | ||
uint32_t pedId = context.GetArgument<uint32_t>(0); | ||
int componentId = context.GetArgument<int>(1); | ||
int globalIndex = GetGlobalIndex(pedId, componentId, context.CheckArgument<const char*>(2), context.GetArgument<int>(3), indexGetter); | ||
if (globalIndex == -1) | ||
{ | ||
if constexpr (!std::is_same<T, void>::value) | ||
{ | ||
context.SetResult<T>(T()); | ||
} | ||
return; | ||
} | ||
|
||
fx::ScriptContextBuffer newContext; | ||
newContext.Push(pedId); | ||
newContext.Push(componentId); | ||
newContext.Push(globalIndex); | ||
int index = 4; | ||
((index = VariadicGetArgument<AdditionalArguments>(context, newContext, index)), ...); | ||
|
||
fx::ScriptEngine::CallNativeHandler(nativeIdentifier, newContext); | ||
if constexpr (!std::is_same<T, void>::value) | ||
{ | ||
context.SetResult<T>(newContext.GetResult<T>()); | ||
} | ||
} | ||
|
||
static HookFunction hookFunction([]() | ||
{ | ||
g_collectionInfoHashOffset = *hook::get_pattern<uint8_t>("8B 51 ? 85 D2 75 ? 33 C0", 2); | ||
g_dynamicEntityArchetypeOffset = *hook::get_pattern<uint8_t>("4C 8B 61 ? 48 8B F9 4D 63 E9", 3); | ||
g_pedModelInfoVarInfoCollectionOffset = *hook::get_pattern<uint32_t>("49 8B 8C 24 ? ? ? ? 49 8B E8", 4); | ||
g_variationInfoPropInfoOffset = *hook::get_pattern<uint8_t>("48 83 C1 ? E8 ? ? ? ? 48 8D 7F", 3); | ||
|
||
// Natives to iterate over all collections. | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_COLLECTIONS_COUNT", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
if (variationInfoCollection) | ||
{ | ||
context.SetResult<int>(variationInfoCollection->m_infos.GetCount()); | ||
} | ||
else | ||
{ | ||
context.SetResult<int>(0); | ||
} | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_COLLECTION_NAME", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
int index = context.GetArgument<int>(1); | ||
if (variationInfoCollection && index >= 0 && index < variationInfoCollection->m_infos.GetCount()) | ||
{ | ||
auto variationInfo = variationInfoCollection->m_infos[context.GetArgument<int>(1)]; | ||
context.SetResult<const char*>(GetCollectionName(variationInfo)); | ||
} | ||
else | ||
{ | ||
context.SetResult<const char*>(nullptr); | ||
} | ||
}); | ||
|
||
// Natives to convert between global drawable/prop indices and local collections drawable/prop indices. | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_COLLECTION_NAME_FROM_DRAWABLE", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
if (variationInfoCollection) | ||
{ | ||
auto variationInfo = g_GetVariationInfoFromDrawableIdx(variationInfoCollection, context.GetArgument<int>(1), context.GetArgument<int>(2)); | ||
if (variationInfo) | ||
{ | ||
context.SetResult<const char*>(GetCollectionName(variationInfo)); | ||
return; | ||
} | ||
} | ||
context.SetResult<const char*>(nullptr); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_COLLECTION_NAME_FROM_PROP", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
if (variationInfoCollection) | ||
{ | ||
auto variationInfo = g_GetVariationInfoFromPropIdx(variationInfoCollection, context.GetArgument<int>(1), context.GetArgument<int>(2)); | ||
if (variationInfo) | ||
{ | ||
context.SetResult<const char*>(GetCollectionName(variationInfo)); | ||
return; | ||
} | ||
} | ||
context.SetResult<const char*>(nullptr); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_COLLECTION_LOCAL_INDEX_FROM_DRAWABLE", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
if (variationInfoCollection) | ||
{ | ||
context.SetResult<int>(g_GetDlcDrawableIdx(variationInfoCollection, context.GetArgument<int>(1), context.GetArgument<int>(2))); | ||
} | ||
else | ||
{ | ||
context.SetResult<int>(-1); | ||
} | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_COLLECTION_LOCAL_INDEX_FROM_PROP", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
if (variationInfoCollection) | ||
{ | ||
context.SetResult<int>(g_GetDlcPropIdx(variationInfoCollection, context.GetArgument<int>(1), context.GetArgument<int>(2))); | ||
} | ||
else | ||
{ | ||
context.SetResult<int>(-1); | ||
} | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_DRAWABLE_GLOBAL_INDEX_FROM_COLLECTION", [](fx::ScriptContext& context) | ||
{ | ||
context.SetResult<int>(GetGlobalIndex(context.GetArgument<uint32_t>(0), context.GetArgument<int>(1), context.CheckArgument<const char*>(2), context.GetArgument<int>(3), g_GetGlobalDrawableIndex)); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_PROP_GLOBAL_INDEX_FROM_COLLECTION", [](fx::ScriptContext& context) | ||
{ | ||
context.SetResult<int>(GetGlobalIndex(context.GetArgument<uint32_t>(0), context.GetArgument<int>(1), context.CheckArgument<const char*>(2), context.GetArgument<int>(3), g_GetGlobalPropIndex)); | ||
}); | ||
|
||
// Natives to set component/prop variation using collections. | ||
fx::ScriptEngine::RegisterNativeHandler("SET_PED_COLLECTION_COMPONENT_VARIATION", [](fx::ScriptContext& context) | ||
{ | ||
// Call SET_PED_COMPONENT_VARIATION using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<void, int, int>(context, g_GetGlobalDrawableIndex, 0x262B14F48D29DE80); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("SET_PED_COLLECTION_PROP_INDEX", [](fx::ScriptContext& context) | ||
{ | ||
// Call SET_PED_PROP_INDEX using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<void, int, bool>(context, g_GetGlobalPropIndex, 0x93376B65A266EB5F); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("SET_PED_COLLECTION_PRELOAD_VARIATION_DATA", [](fx::ScriptContext& context) | ||
{ | ||
// Call SET_PED_PRELOAD_VARIATION_DATA using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<void, int>(context, g_GetGlobalDrawableIndex, 0x39D55A620FCB6A3A); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("SET_PED_COLLECTION_PRELOAD_PROP_DATA", [](fx::ScriptContext& context) | ||
{ | ||
// Call SET_PED_PROP_INDEX using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<void, int>(context, g_GetGlobalPropIndex, 0x2B16A3BFF1FBCE49); | ||
}); | ||
|
||
// Natives to get component/prop variation using collections. | ||
fx::ScriptEngine::RegisterNativeHandler("GET_NUMBER_OF_PED_COLLECTION_DRAWABLE_VARIATIONS", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
if (variationInfoCollection) | ||
{ | ||
auto variationInfo = GetVariationInfoFromCollection(variationInfoCollection, context.CheckArgument<const char*>(2)); | ||
if (variationInfo) | ||
{ | ||
context.SetResult<int>(static_cast<int>(g_GetMaxNumDrawables(variationInfo, context.GetArgument<int>(1)))); | ||
return; | ||
} | ||
} | ||
context.SetResult<int>(0); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_NUMBER_OF_PED_COLLECTION_PROP_DRAWABLE_VARIATIONS", [](fx::ScriptContext& context) | ||
{ | ||
auto variationInfoCollection = GetPedVariationInfoCollection(context.GetArgument<uint32_t>(0)); | ||
if (variationInfoCollection) | ||
{ | ||
auto variationInfo = GetVariationInfoFromCollection(variationInfoCollection, context.CheckArgument<const char*>(2)); | ||
if (variationInfo) | ||
{ | ||
auto propInfo = (CPedPropInfo*)((uintptr_t)variationInfo + g_variationInfoPropInfoOffset); | ||
context.SetResult<int>(static_cast<int>(g_GetMaxNumProps(propInfo, context.GetArgument<int>(1)))); | ||
return; | ||
} | ||
} | ||
context.SetResult<int>(0); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_NUMBER_OF_PED_COLLECTION_TEXTURE_VARIATIONS", [](fx::ScriptContext& context) | ||
{ | ||
// Call GET_NUMBER_OF_PED_TEXTURE_VARIATIONS using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<int>(context, g_GetGlobalDrawableIndex, 0x8F7156A3142A6BAD); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_NUMBER_OF_PED_COLLECTION_PROP_TEXTURE_VARIATIONS", [](fx::ScriptContext& context) | ||
{ | ||
// Call GET_NUMBER_OF_PED_PROP_TEXTURE_VARIATIONS using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<int>(context, g_GetGlobalPropIndex, 0xA6E7F1CEB523E171); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("IS_PED_COLLECTION_COMPONENT_VARIATION_VALID", [](fx::ScriptContext& context) | ||
{ | ||
// Call IS_PED_COMPONENT_VARIATION_VALID using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<bool, int>(context, g_GetGlobalDrawableIndex, 0xE825F6B6CEA7671D); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("IS_PED_COLLECTION_COMPONENT_VARIATION_GEN9_EXCLUSIVE", [](fx::ScriptContext& context) | ||
{ | ||
// Call IS_PED_COMPONENT_VARIATION_GEN9_EXCLUSIVE using the globalPropIndex | ||
RedirectNativeCallWithGlobalIndex<bool>(context, g_GetGlobalDrawableIndex, 0xC767B581); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_DRAWABLE_VARIATION_COLLECTION_LOCAL_INDEX", [](fx::ScriptContext& context) | ||
{ | ||
uint32_t pedId = context.GetArgument<uint32_t>(0); | ||
int componentId = context.GetArgument<int>(1); | ||
auto variationInfoCollection = GetPedVariationInfoCollection(pedId); | ||
if (!variationInfoCollection) | ||
{ | ||
context.SetResult<int>(-1); | ||
return; | ||
} | ||
|
||
fx::ScriptContextBuffer newContext; | ||
newContext.Push(pedId); | ||
newContext.Push(componentId); | ||
// Call GET_PED_DRAWABLE_VARIATION to get global drawable index. | ||
fx::ScriptEngine::CallNativeHandler(0x67F3780DD425D4FC, newContext); | ||
int globalDrawableIndex = newContext.GetResult<int>(); | ||
context.SetResult<int>(g_GetDlcDrawableIdx(variationInfoCollection, componentId, globalDrawableIndex)); | ||
}); | ||
fx::ScriptEngine::RegisterNativeHandler("GET_PED_DRAWABLE_VARIATION_COLLECTION_NAME", [](fx::ScriptContext& context) | ||
{ | ||
uint32_t pedId = context.GetArgument<uint32_t>(0); | ||
int componentId = context.GetArgument<int>(1); | ||
auto variationInfoCollection = GetPedVariationInfoCollection(pedId); | ||
if (!variationInfoCollection) | ||
{ | ||
context.SetResult<const char*>(nullptr); | ||
return; | ||
} | ||
|
||
fx::ScriptContextBuffer newContext; | ||
newContext.Push(pedId); | ||
newContext.Push(componentId); | ||
// Call GET_PED_DRAWABLE_VARIATION to get global drawable index. | ||
fx::ScriptEngine::CallNativeHandler(0x67F3780DD425D4FC, newContext); | ||
int globalDrawableIndex = newContext.GetResult<int>(); | ||
auto variationInfo = g_GetVariationInfoFromDrawableIdx(variationInfoCollection, componentId, globalDrawableIndex); | ||
if (!variationInfo) | ||
{ | ||
context.SetResult<const char*>(nullptr); | ||
return; | ||
} | ||
|
||
context.SetResult<const char*>(GetCollectionName(variationInfo)); | ||
}); | ||
}); |
Oops, something went wrong.
7dad6da
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Nobelium-cfx hey, do these new component and prop natives work? I have tested a lot and it seems it's not working properly for props
for example:
outputs when I don't wear anything:
PROPID[0] = -F_P_0_0_-1
PROPID[1] = -F_P_1_0_-1
PROPID[2] = mp_f_luxe_01-F_P_2_0_-1
PROPID[3] = null-F_P_3_0_-1
PROPID[4] = null-F_P_4_0_-1
PROPID[5] = null-F_P_5_0_-1
PROPID[6] = Female_freemode_business-F_P_6_0_-1
PROPID[7] = mp_f_luxe_02-F_P_7_0_-1
PROPID[8] = null-F_P_8_0_-1
PROPID[9] = null-F_P_9_0_-1
PROPID[10] = null-F_P_10_0_-1
PROPID[11] = null-F_P_11_0_-1
outputs when I wear this hat:
PROPID[0] = -F_P_0_0_20
PROPID[1] = -F_P_1_0_-1
PROPID[2] = mp_f_luxe_01-F_P_2_0_-1
PROPID[3] = null-F_P_3_0_-1
PROPID[4] = null-F_P_4_0_-1
PROPID[5] = null-F_P_5_0_-1
PROPID[6] = Female_freemode_business-F_P_6_0_-1
PROPID[7] = mp_f_luxe_02-F_P_7_0_-1
PROPID[8] = null-F_P_8_0_-1
PROPID[9] = null-F_P_9_0_-1
PROPID[10] = null-F_P_10_0_-1
PROPID[11] = null-F_P_11_0_-1
weird that only textid changes and no collection name outputs. Info about hat:
and if I'm not mistaken collection name should be "mp_f_gunrunning_01"
thanks a lot ❤
7dad6da
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be helpful to include the game version you were using too
7dad6da
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure. b3285 (Beta) onesync infinity
7dad6da
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey just an update, it seems that I've made a mistake on the custom data side. "DrawableId" field is global index not the local collection index. "RelativeCollectionDrawableId" is the correct localindex. Everything works fine when I used local index instead of global index in the code.
7dad6da
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was in a process of testing if I could reproduce the error :). Happy to hear everything works now! Let me know if you need any other help with this.
7dad6da
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the fast answer, there seems the be problem again, but I will make sure it's not my mistake like before. I will comment here if I can't figure it out