From b8b4492e2e59322ef51f27dc7b720807d55f7470 Mon Sep 17 00:00:00 2001 From: Identity-labs <600296+Identity-labs@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:06:31 +0200 Subject: [PATCH] feat(extra-natives-rdr3): Add CBlip in GetGamePool / Add natives GetBlipSprite, GetBlipRotation, GetBlipModifiers --- .../src/PoolTraversalNatives.cpp | 64 +++++++++-- .../extra-natives-rdr3/src/BlipNatives.cpp | 100 ++++++++++++++++++ ext/native-decls/GetBlipModifiers.md | 39 +++++++ ext/native-decls/GetBlipRotation.md | 35 ++++++ ext/native-decls/GetBlipSprite.md | 39 +++++++ ext/native-decls/GetGamePool.md | 10 ++ 6 files changed, 281 insertions(+), 6 deletions(-) create mode 100644 code/components/extra-natives-rdr3/src/BlipNatives.cpp create mode 100644 ext/native-decls/GetBlipModifiers.md create mode 100644 ext/native-decls/GetBlipRotation.md create mode 100644 ext/native-decls/GetBlipSprite.md diff --git a/code/components/extra-natives-five/src/PoolTraversalNatives.cpp b/code/components/extra-natives-five/src/PoolTraversalNatives.cpp index f09d17315f..7c43f2c91d 100644 --- a/code/components/extra-natives-five/src/PoolTraversalNatives.cpp +++ b/code/components/extra-natives-five/src/PoolTraversalNatives.cpp @@ -141,6 +141,32 @@ struct PickupPoolTraits } }; +#ifdef IS_RDR3 +struct BlipPoolTraits +{ + using PoolType = atPoolBase; + + static PoolType* GetPool() + { + return rage::GetPoolBase("fwuiBlip"); + } + + static uint32_t getScriptGuid(void* entry, int index) + { + auto pool = GetPool(); + if (!pool) + return 0; + + // Blips use flag value 2 + // The handle format is: (index << 8) | flag + uint32_t handle = (index << 8) | 2; + + return handle; + } +}; +#endif + + template static void SerializePool(fx::ScriptContext& context) { @@ -156,10 +182,10 @@ static void SerializePool(fx::ScriptContext& context) { if (!entry->GetNetObject()) { - continue; - } - } - + continue; + } + } + uint32_t guid = TTraits::getScriptGuid(entry); if (guid != 0) { @@ -171,6 +197,28 @@ static void SerializePool(fx::ScriptContext& context) context.SetResult(fx::SerializeObject(guids)); } +template +static void SerializePoolBase(fx::ScriptContext& context) +{ + std::vector guids; + + auto pool = static_cast(TTraits::GetPool()); + for (int i = 0; i < pool->GetCountDirect(); ++i) + { + auto entry = pool->GetAt(i); + if (entry) + { + uint32_t guid = TTraits::getScriptGuid(entry, i); + if (guid != 0) + { + guids.push_back(guid); + } + } + } + + context.SetResult(fx::SerializeObject(guids)); +} + struct FindHandle { void* pool; @@ -290,12 +338,16 @@ static InitFunction initFunction([]() SerializePool(context); else if (pool.compare("CObject") == 0) SerializePool(context); - else if (pool.compare("CNetObject") == 0) - SerializePool(context); + else if (pool.compare("CNetObject") == 0) + SerializePool(context); else if (pool.compare("CPickup") == 0) SerializePool(context); else if (pool.compare("CVehicle") == 0) SerializePool(context); +#ifdef IS_RDR3 + else if (pool.compare("CBlip") == 0) + SerializePoolBase(context); +#endif else { throw std::runtime_error(va("Invalid pool: %s", pool)); diff --git a/code/components/extra-natives-rdr3/src/BlipNatives.cpp b/code/components/extra-natives-rdr3/src/BlipNatives.cpp new file mode 100644 index 0000000000..c48257be1e --- /dev/null +++ b/code/components/extra-natives-rdr3/src/BlipNatives.cpp @@ -0,0 +1,100 @@ +#include "StdInc.h" + +#include +#include +#include + +#include +#include + +#include +#include + +struct BlipData +{ + char pad1[32]; // 0x0000-0x001F (32 bytes) + float x; // 0x0020 (4 bytes) + float y; // 0x0024 (4 bytes) + float z; // 0x0028 (4 bytes) + char vpad[4]; // 0x002C-0x002F (4 bytes) + uint16_t rotation; // 0x0030 (2 bytes) + char pad2[542]; // 0x0032-0x024F (542 bytes) + atArray modifiers; // 0x0250-0x025F (16 bytes) + char pad3[92]; // 0x0260-0x02BB (92 bytes) + uint32_t blipSprite; // 0x02BC (4 bytes) + char pad4[15]; // 0x02C0-0x02CE (15 bytes) + uint8_t blipFlags; // 0x02CF - bitfield containing blip type/flags + char pad5[16]; // 0x02D0-0x02DF (16 bytes) + void* vtable; // 0x02F0 (8 bytes) + void* unk; // 0x02F8 (8 bytes) +}; + +static auto GetBlipPool() +{ + static auto pool = rage::GetPoolBase("fwuiBlip"); + return pool; +} + +static BlipData* GetBlipData(uint32_t blipHandle) +{ + if (blipHandle == 0) + return nullptr; + + auto pool = GetBlipPool(); + if (!pool) + return nullptr; + + auto blip = pool->GetAtHandle(blipHandle); + return blip; +} + +static InitFunction initFunction([]() +{ + fx::ScriptEngine::RegisterNativeHandler("GET_BLIP_SPRITE", [](fx::ScriptContext& context) + { + uint32_t blipHandle = context.GetArgument(0); + + auto blip = GetBlipData(blipHandle); + if (blip) + { + context.SetResult(blip->blipSprite); + } + else + { + context.SetResult(0); + } + }); + + fx::ScriptEngine::RegisterNativeHandler("GET_BLIP_ROTATION", [](fx::ScriptContext& context) + { + uint32_t blipHandle = context.GetArgument(0); + + auto blip = GetBlipData(blipHandle); + if (blip) + { + float rotation = static_cast(blip->rotation) * (360.0f / 65535.0f); + context.SetResult(rotation); + } + else + { + context.SetResult(0.0f); + } + }); + + fx::ScriptEngine::RegisterNativeHandler("GET_BLIP_MODIFIERS", [](fx::ScriptContext& context) + { + uint32_t blipHandle = context.GetArgument(0); + + std::vector modifiers; + auto blip = GetBlipData(blipHandle); + if (blip) + { + for (int i = 0; i < blip->modifiers.GetCount(); ++i) + { + modifiers.push_back(blip->modifiers[i]); + } + } + context.SetResult(fx::SerializeObject(modifiers)); + }); + +}); \ No newline at end of file diff --git a/ext/native-decls/GetBlipModifiers.md b/ext/native-decls/GetBlipModifiers.md new file mode 100644 index 0000000000..b1eb9055df --- /dev/null +++ b/ext/native-decls/GetBlipModifiers.md @@ -0,0 +1,39 @@ +--- +ns: CFX +apiset: client +game: rdr3 +--- +## GET_BLIP_MODIFIERS + +```c +object GET_BLIP_MODIFIERS(int blipHandle); +``` + +Gets the modifiers array of a blip from its handle. + +## Parameters +* **blipHandle**: The blip handle obtained from GET_GAME_POOL("CBlip") + +## Return value +An array of uint32_t modifiers. Returns an empty array if the blip doesn't exist. + +## Examples +```lua +local blips = GetGamePool("CBlip") +for _, blipHandle in ipairs(blips) do + local modifiers = GetBlipModifiers(blipHandle) + if #modifiers > 0 and modifiers[1] == 0x9D6AB6CB then -- BLIP_STYLE_POI + print("Found POI style blip with handle:", blipHandle) + end +end +``` + +```js +const blips = GetGamePool("CBlip"); +blips.forEach(blipHandle => { + const modifiers = GetBlipModifiers(blipHandle); + if (modifiers.length > 0 && modifiers[0] === 0x9D6AB6CB) { // BLIP_STYLE_POI + console.log(`Found POI style blip with handle: ${blipHandle}`); + } +}); +``` diff --git a/ext/native-decls/GetBlipRotation.md b/ext/native-decls/GetBlipRotation.md new file mode 100644 index 0000000000..9c5661820b --- /dev/null +++ b/ext/native-decls/GetBlipRotation.md @@ -0,0 +1,35 @@ +--- +ns: CFX +apiset: client +game: rdr3 +--- +## GET_BLIP_ROTATION + +```c +float GET_BLIP_ROTATION(int blipHandle); +``` + +Gets the rotation of a blip from its handle. + +## Parameters +* **blipHandle**: The blip handle obtained from GET_GAME_POOL("CBlip") + +## Return value +The blip rotation in degrees as a float value (0.0 to 360.0). Returns 0.0 if the blip doesn't exist. + +## Examples +```lua +local blips = GetGamePool("CBlip") +for _, blipHandle in ipairs(blips) do + local rotation = GetBlipRotation(blipHandle) + print(string.format("Blip %d rotation: %.2f degrees", blipHandle, rotation)) +end +``` + +```js +const blips = GetGamePool("CBlip"); +blips.forEach(blipHandle => { + const rotation = GetBlipRotation(blipHandle); + console.log(`Blip ${blipHandle} rotation: ${rotation.toFixed(2)} degrees`); +}); +``` diff --git a/ext/native-decls/GetBlipSprite.md b/ext/native-decls/GetBlipSprite.md new file mode 100644 index 0000000000..ae72896d32 --- /dev/null +++ b/ext/native-decls/GetBlipSprite.md @@ -0,0 +1,39 @@ +--- +ns: CFX +apiset: client +game: rdr3 +--- +## GET_BLIP_SPRITE + +```c +Hash GET_BLIP_SPRITE(int blipHandle); +``` + +Gets the sprite/hash of a blip from its handle. + +## Parameters +* **blipHandle**: The blip handle obtained from GET_GAME_POOL("CBlip") + +## Return value +The blip sprite hash. Returns 0 if the blip doesn't exist. + +## Examples +```lua +local blips = GetGamePool("CBlip") +for _, blipHandle in ipairs(blips) do + local sprite = GetBlipSprite(blipHandle) + if sprite == 0x866B73BE then -- BLIP_POI + print("Found POI blip with handle:", blipHandle) + end +end +``` + +```js +const blips = GetGamePool("CBlip"); +blips.forEach(blipHandle => { + const sprite = GetBlipSprite(blipHandle); + if (sprite === 0x866B73BE) { // BLIP_POI + console.log(`Found POI blip with handle: ${blipHandle}`); + } +}); +``` diff --git a/ext/native-decls/GetGamePool.md b/ext/native-decls/GetGamePool.md index d334be345a..3317f7d09b 100644 --- a/ext/native-decls/GetGamePool.md +++ b/ext/native-decls/GetGamePool.md @@ -21,6 +21,7 @@ follows: * `CNetObject`: Networked objects * `CVehicle`: Vehicles. * `CPickup`: Pickups. +* `CBlip`: Blips (RDR3 only). ## Examples ```lua @@ -30,6 +31,15 @@ for i = 1, #vehiclePool do -- loop through each vehicle (entity) DeleteEntity(vehiclePool[i]) -- Delete vehicles (entities) that don't have a driver end end + +-- RDR3 only: Get all blips +local blipPool = GetGamePool('CBlip') +for i = 1, #blipPool do + local sprite = GetBlipSprite(blipPool[i]) + local rotation = GetBlipRotation(blipPool[i]) + print(string.format("Blip %d: sprite=0x%X, rotation=%d", + blipPool[i], sprite, rotation)) +end ``` ## Parameters