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

Add template function for calling native gta functions #3768

Closed

Conversation

FileEX
Copy link
Contributor

@FileEX FileEX commented Oct 4, 2024

This PR adds a simple template function that is used to call native GTA functions. This is more readable than manually casting or "using".

The PrepareSignature function prepares arguments of the appropriate types, and CallGTAFunction calls the function and optionally returns the result.

The syntax is simple

CallGTAFunction<return type, calling convention>(function address, arguments returned by PrepareSignature)

Example

    auto args = PrepareSignature(self, colModel, applyToPairedModel);
    CallGTAFunction<void, __THISCALL>(0x4C4BC0, args);

Calling conventions are given in capital letters.

@FileEX FileEX marked this pull request as draft October 4, 2024 18:07
@CrosRoad95
Copy link
Contributor

In my opinion for now you should refactor only few functions calls, see how it behave in production, and then refactor more. You may miss something, make mistake and it is kinda hard to verify if all your refactors are allright.

Copy link
Contributor

@TracerDS TracerDS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you've taken a liking to my method :)
This way dealing with raw memory would be much better.

Client/game_sa/gamesa_init.h Outdated Show resolved Hide resolved
@FileEX
Copy link
Contributor Author

FileEX commented Oct 4, 2024

In my opinion for now you should refactor only few functions calls, see how it behave in production, and then refactor more. You may miss something, make mistake and it is kinda hard to verify if all your refactors are allright.

I tested some functions that I changed and so far everything works fine, but maybe you're right, that's why this is enough for now. Only when the next nightly after merging this PR turns out to work without any problems, then I will finish the remaining files

@FileEX FileEX marked this pull request as ready for review October 4, 2024 18:19
@TheNormalnij
Copy link
Member

MSVC doesn't optimize your calls. link

@FileEX
Copy link
Contributor Author

FileEX commented Oct 4, 2024

MSVC doesn't optimize your calls. link

Is there anything I can do about it?

Client/game_sa/gamesa_init.h Outdated Show resolved Hide resolved
Client/game_sa/gamesa_init.h Outdated Show resolved Hide resolved
@TheNormalnij
Copy link
Member

Is there anything I can do about it?

My bad there was an invalid compiler option. There should be /O2 instead of -O3

@FileEX FileEX requested a review from tederis October 5, 2024 14:18
Client/game_sa/gamesa_init.h Outdated Show resolved Hide resolved
Client/game_sa/gamesa_init.h Outdated Show resolved Hide resolved
@sbx320
Copy link
Member

sbx320 commented Oct 9, 2024

This seems like a suboptimal approach to me. I think we should rather replace address #defines with proper typed function declarations.

e.g.

instead of

#define FUNC_HasCollisionBeenLoaded                         0x410CE0

and

    DWORD dwFunc = FUNC_HasCollisionBeenLoaded;
    bool  bRet = false;
    _asm
    {
        push    0
        push    vecPosition
        call    dwFunc
        mov     bRet, al
        add     esp, 8
    }
    return bRet;

just do

static auto FUNC_HasCollisionBeenLoaded = reinterpret_cast<bool(__cdecl*)(CVector* vecPosition, bool flag)>(0x410CE0);

and

    return FUNC_HasCollisionBeenLoaded(vecPosition, false);

@TracerDS
Copy link
Contributor

TracerDS commented Oct 9, 2024

This seems like a suboptimal approach to me. I think we should rather replace address #defines with proper typed function declarations.

e.g.

instead of

#define FUNC_HasCollisionBeenLoaded                         0x410CE0

and

    DWORD dwFunc = FUNC_HasCollisionBeenLoaded;
    bool  bRet = false;
    _asm
    {
        push    0
        push    vecPosition
        call    dwFunc
        mov     bRet, al
        add     esp, 8
    }
    return bRet;

just do

static auto FUNC_HasCollisionBeenLoaded = reinterpret_cast<bool(__cdecl*)(CVector* vecPosition, bool flag)>(0x410CE0);

and

    return FUNC_HasCollisionBeenLoaded(vecPosition, false);

Even better, create the appropriate methods in the class you are calling it for (if any) and then call that method instead (definition would just be reinterpreted mem address).
But thats for other PR maybe

@FileEX
Copy link
Contributor Author

FileEX commented Oct 9, 2024

This seems like a suboptimal approach to me. I think we should rather replace address #defines with proper typed function declarations.

e.g.

instead of

#define FUNC_HasCollisionBeenLoaded                         0x410CE0

and

    DWORD dwFunc = FUNC_HasCollisionBeenLoaded;
    bool  bRet = false;
    _asm
    {
        push    0
        push    vecPosition
        call    dwFunc
        mov     bRet, al
        add     esp, 8
    }
    return bRet;

just do

static auto FUNC_HasCollisionBeenLoaded = reinterpret_cast<bool(__cdecl*)(CVector* vecPosition, bool flag)>(0x410CE0);

and

    return FUNC_HasCollisionBeenLoaded(vecPosition, false);

In that case, I have an even better suggestion. Maybe we should create a single header file, something like gta_functions.h, where we define all native functions. This way, we will have access to native functions in every file, and they will be defined once in one place. It's similar to how it's done in gamesa_renderware.h

@TheNormalnij
Copy link
Member

In that case, I have an even better suggestion. Maybe we should create a single header file, something like gta_functions.h, where we define all native functions. This way, we will have access to native functions in every file, and they will be defined once in one place. It's similar to how it's done in gamesa_renderware.h

This file will be too messy. I prefer declaring methods to to access engine functions.
Instead of

void someLogic() {
        // some code
        using CEntity_ResolveReferences = void*(__thiscall*)(CEntitySAInterface*);
        ((CEntity_ResolveReferences)0x571A40)(m_pInterface);
        // some code
}

CEntity_ResolveReferences can be declared in CEntitySAInterface

CEntitySAInterface {
 //...
    void* ResolveReferences()
   {
       // we can skip additional definition for 0x571A40
       // this number is described in the context
       return ((void*(__thiscall*)(CEntitySAInterface*))0x571A40)(this);
   };
}

void someLogic() {
    // code
    m_pInterface->ResolveReferences();
    // code
}

I kinda agree that this PR gives too small improvements.

@FileEX
Copy link
Contributor Author

FileEX commented Oct 9, 2024

/////////////////////////////
/// CPed
/////////////////////////////
static auto FUNC_SetModelIndex = reinterpret_cast<void(__thiscall*)(CPedSAInterface* ped, int index)>(0x5E4880);
static auto FUNC_AttachPedToEntity = reinterpret_cast<void(__thiscall*)(CPedSAInterface* ped, CEntitySAInterface* entity, float offsetX, float offsetY, float offsetZ, int direction, float rotationLimit, std::uint32_t weaponType)>(0x5E7CB0);
static auto FUNC_SetIsStanding = reinterpret_cast<void(__thiscall*)(CPedSAInterface* ped, bool standing)>(0x4ABBE0);
static auto FUNC_SetCurrentWeapon = reinterpret_cast<void(__thiscall*)(CPedSAInterface* ped, std::uint8_t slot)>(0x5E61F0);
static auto FUNC_GiveWeapon = reinterpret_cast<std::uint32_t(__thiscall*)(CPedSAInterface* ped, std::uint32_t weaponID, int ammo, int unusedUnknown)>(0x5E6080);
static auto FUNC_GetBonePosition = reinterpret_cast<void(__thiscall*)(CPedSAInterface* ped, CVector* pos, std::uint32_t boneID, bool dynamic)>(0x5E4280);

/////////////////////////////
/// CPlayerPed
/////////////////////////////
static auto FUNC_MakeChangesForNewWeapon_Slot = reinterpret_cast<void(__thiscall*)(CPlayerPedSAInterface* playerPed, std::uint8_t weaponSlot)>(0x60D000);

Personally, I wouldn't say that it looks messy. Additionally, this file would be rarely edited.

Will editing CEntitySAInterface cause any issues due to incorrect size?

@TheNormalnij
Copy link
Member

Personally, I wouldn't say that it looks messy. Additionally, this file would be rarely edited.

FUNC_SetModelIndex(pPedInterface, 200) vs pPedInterface->SetModelIndex(200)

Will editing CEntitySAInterface cause any issues due to incorrect size?

Methods don't change data struct. Only virtual functions add vtbl reference

@FileEX
Copy link
Contributor Author

FileEX commented Oct 9, 2024

Personally, I wouldn't say that it looks messy. Additionally, this file would be rarely edited.

FUNC_SetModelIndex(pPedInterface, 200) vs pPedInterface->SetModelIndex(200)

Will editing CEntitySAInterface cause any issues due to incorrect size?

Methods don't change data struct. Only virtual functions add vtbl reference

What about cdecl functions that are not tied to any specific interface?

@TracerDS
Copy link
Contributor

TracerDS commented Oct 9, 2024

What about cdecl functions that are not tied to any specific interface?

What do you think about a namespace for all gta global funcs?

@TheNormalnij
Copy link
Member

What about cdecl functions that are not tied to any specific interface?

cdecl functions can be added to related SA class.

@FileEX FileEX closed this Oct 9, 2024
@FileEX FileEX deleted the refactor/template_gta_func branch October 9, 2024 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants