Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(extra-natives/five): REGISTER_ROPE_DATA #3062

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions code/components/extra-natives-five/include/ropeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ class grcTexture;
class ropeData
{
public:
virtual ~ropeData() = 0;
ropeData();

virtual const char* GetName() = 0;
virtual ~ropeData() {}

virtual parStructure* parser_GetStructure() = 0;
void* operator new(size_t size);

void operator delete(void* pointer);

uint32_t numSections; // 0x8
float radius; // 0xC
Expand All @@ -43,6 +45,10 @@ class ropeDataManager
public:
virtual ~ropeDataManager() = 0;

void UnloadRopeTextures();

void Load();

atArray<ropeData*> typeData; // 0x8

static ropeDataManager* GetInstance();
Expand Down
32 changes: 32 additions & 0 deletions code/components/extra-natives-five/src/NativeFixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "Resource.h"
#include "ScriptWarnings.h"
#include <Train.h>
#include "ropeManager.h"

static void BlockForbiddenNatives()
{
Expand Down Expand Up @@ -540,6 +541,35 @@ static void FixMissionTrain()
});
}

static void FixAddRopeNative()
{
constexpr const uint64_t ADD_ROPE = 0xE832D760399EB220;
auto handler = fx::ScriptEngine::GetNativeHandler(ADD_ROPE);

if (!handler)
{
return;
}

fx::ScriptEngine::RegisterNativeHandler(ADD_ROPE, [handler](fx::ScriptContext& ctx)
{
rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance();
if (manager)
{
auto ropeIndex = ctx.GetArgument<int>(7);
if (ropeIndex >= 0 && ropeIndex < manager->typeData.GetCount())
{
return handler(ctx);
}
else
{
fx::scripting::Warningf("natives", "Invalid rope type was passed to ADD_ROPE (%d), should be from 0 to %d\n", ropeIndex, manager->typeData.GetCount() - 1);
}
}
ctx.SetResult(0);
});
}

static HookFunction hookFunction([]()
{
g_fireInstances = (std::array<FireInfoEntry, 128>*)(hook::get_address<uintptr_t>(hook::get_pattern("74 47 48 8D 0D ? ? ? ? 48 8B D3", 2), 3, 7) + 0x10);
Expand Down Expand Up @@ -582,6 +612,8 @@ static HookFunction hookFunction([]()

FixMissionTrain();

FixAddRopeNative();

if (xbr::IsGameBuildOrGreater<2612>())
{
// IS_BIT_SET is missing in b2612+, re-adding for compatibility
Expand Down
136 changes: 136 additions & 0 deletions code/components/extra-natives-five/src/RopeNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,52 @@
#include <Hooking.h>
#include <ScriptEngine.h>
#include <ScriptSerialization.h>
#include <ScriptWarnings.h>
#include <Pool.h>
#include <ICoreGameInit.h>

namespace rage
{
static hook::thiscall_stub<void(ropeData* self)> ropeData_ropeData([]()
{
return hook::get_pattern("48 89 5C 24 ? 48 89 7C 24 ? 41 56 48 83 EC 20 8B 3D ? ? ? ? C7 41");
});

ropeData::ropeData()
{
ropeData_ropeData(this);
}

void* ropeData::operator new(size_t size)
{
return PoolAllocate(GetPool<ropeData>("ropeData"));
}

void ropeData::operator delete(void* pointer)
{
PoolRelease(GetPool<ropeData>("ropeData"), pointer);
}

static hook::thiscall_stub<void(ropeDataManager* self)> ropeDataManager_UnloadRopeTextures([]()
{
return hook::get_call(hook::get_pattern("E8 ? ? ? ? 48 8B CF E8 ? ? ? ? 48 8B 9F ? ? ? ? 48 8B 8B"));
});

void ropeDataManager::UnloadRopeTextures()
{
ropeDataManager_UnloadRopeTextures(this);
}

static hook::thiscall_stub<void(ropeDataManager* self)> ropeDataManager_Load([]()
{
return hook::get_pattern("48 83 EC 48 E8 ? ? ? ? 4C 8B 0D");
});

void ropeDataManager::Load()
{
ropeDataManager_Load(this);
}

static ropeDataManager* g_ropeDataManager;

ropeDataManager* ropeDataManager::GetInstance()
Expand All @@ -33,6 +76,9 @@ ropeManager* ropeManager::GetInstance()
}
}

static int* ropeDataManager__txdStatus;
static bool g_hasCustomRopeTypes = false;

static HookFunction hookFunction([]()
{
{
Expand Down Expand Up @@ -143,4 +189,94 @@ static HookFunction hookFunction([]()

context.SetResult(updateOrder);
});

{
auto location = hook::get_pattern<char>("48 83 EC 28 33 D2 8D 4A 07");
ropeDataManager__txdStatus = (int*)(hook::get_address<char*>(location + 16) + 1);
}

fx::ScriptEngine::RegisterNativeHandler("REGISTER_ROPE_DATA", [](fx::ScriptContext& context)
{
int numSections = context.GetArgument<int>(0);
float radius = context.GetArgument<float>(1);
const char* diffuseTextureName = context.CheckArgument<const char*>(2);
const char* normalMapName = context.CheckArgument<const char*>(3);
float distanceMappingScale = context.GetArgument<float>(4);
float uvScaleX = context.GetArgument<float>(5);
float uvScaleY = context.GetArgument<float>(6);
float specularFresnel = context.GetArgument<float>(7);
float specularFalloff = context.GetArgument<float>(8);
float specularIntensity = context.GetArgument<float>(9);
float bumpiness = context.GetArgument<float>(10);
int color = context.GetArgument<int>(11);

context.SetResult(-1);

if (numSections <= 0)
{
fx::scripting::Warningf("natives", "Invalid numSections was passed to REGISTER_ROPE_DATA (%d), should greater than 0\n", numSections);
return;
}

if (radius <= 0.0)
{
fx::scripting::Warningf("natives", "Invalid radius was passed to REGISTER_ROPE_DATA (%f), should greater than 0.0\n", radius);
return;
}

rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance();
if (!manager)
{
return;
}

atPool<rage::ropeData>* pool = rage::GetPool<rage::ropeData>("ropeData");
if (pool->GetCount() == pool->GetSize())
{
fx::scripting::Warningf("natives", "Unable to allocate rope data in REGISTER_ROPE_DATA, pool is full\n");
return;
}

rage::ropeData* ropeData = new rage::ropeData();

ropeData->numSections = numSections;
ropeData->radius = radius;
ropeData->diffuseTextureNameHash = HashString(diffuseTextureName);
ropeData->normalMapNameHash = HashString(normalMapName);
ropeData->distanceMappingScale = distanceMappingScale;
ropeData->UVScaleX = uvScaleX;
ropeData->UVScaleY = uvScaleY;
ropeData->specularFresnel = specularFresnel;
ropeData->specularFalloff = specularFalloff;
ropeData->specularIntensity = specularIntensity;
ropeData->bumpiness = bumpiness;
ropeData->color = color;

int ropeType = manager->typeData.GetCount();
manager->typeData.Set(ropeType, ropeData);

// If rope textures are loaded then put them back into the loading state
if (*ropeDataManager__txdStatus == 2)
{
manager->UnloadRopeTextures();
*ropeDataManager__txdStatus = 1;
}

g_hasCustomRopeTypes = true;

context.SetResult(ropeType);
});

Instance<ICoreGameInit>::Get()->OnShutdownSession.Connect([]()
{
if (g_hasCustomRopeTypes)
{
rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance();
if (manager)
{
manager->Load();
}
g_hasCustomRopeTypes = false;
}
});
});
2 changes: 1 addition & 1 deletion data/client/citizen/common/data/gameconfig.xml
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@
</Item>
<Item>
<PoolName>ropeData</PoolName>
<PoolSize value="24"/>
<PoolSize value="64"/>
</Item>
</Entries>
</PoolSizes>
Expand Down
42 changes: 42 additions & 0 deletions ext/native-decls/RegisterRopeData.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
ns: CFX
apiset: client
game: gta5
---
## REGISTER_ROPE_DATA

```c
int REGISTER_ROPE_DATA(int numSections, float radius, char* diffuseTextureName, char* normalMapName, float distanceMappingScale, float uvScaleX, float uvScaleY, float specularFresnel, float specularFalloff, float specularIntensity, float bumpiness, int color);
```

Registers a custom rope data with the game. For guidance on what these values should be use common:/data/ropedata.xml as a reference.
Returns a rope type which can be passed into [ADD_ROPE](?_0xE832D760399EB220) to use a custom rope design.
Once a rope data is registered it can be used indefinitely and you should take caution not too register too many as to exceed the games limit.

## Examples

```lua
-- Create a thick steel cable rope above the players head
local ropeType = RegisterRopeData(6, 0.15, "steel_cable", "steel_cable_n", 1.0, 1.0, 8.775, 0.97, 30.0, 0.25, 1.775, 0x00FFFF00)
if ropeType ~= -1 then
local coords = GetEntityCoords(PlayerPedId()) + vector3(0.0, 0.0, 5.0)
AddRope(coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 25.0, ropeType, 10.0, 0.0, 1.0, false, false, false, 1.0, false, 0)
RopeLoadTextures()
end
```

## Parameters
* **numSections**:
* **radius**:
* **diffuseTextureName**:
* **normalMapName**:
* **uvScaleX**:
* **uvScaleY**:
* **specularFresnel**:
* **specularFalloff**:
* **specularIntensity**:
* **bumpiness**:
* **color**:

## Return value
Returns a non-negative value on success, or -1 if the rope data could not be registered or an invalid argument is passed.
Loading