How To Solve the RedMenu Skin Delay / Scripting Tutorial

Introduction

So, we’re going to talk about the standard playerSpawned event in conjunction with RedMenu . There is a problem - when the Default Skin is selected in RedMenu (see Picture 1), there may be issues during development using the playerSpawned event and native PlayerPedId().

image

Picture 1. RedMenu Default Skin.

Problem Description

The issue is that if you want to do something with the player’s ped right after they spawn, it might not work as the player’s ped changes approximately 4-4.5 seconds later, as indicated in the code here and here.

Example

AddEventHandler('playerSpawned', function()
    print(PlayerPedId()) -- 1584645
    Wait(4500)
    print(PlayerPedId()) -- 1210117

    SetEntityMaxHealth(PlayerPedId(), 250)
    SetEntityHealth(PlayerPedId(), 250)
end)

Initial Solution

The simplest solution is to add a delay as follows:

AddEventHandler('playerSpawned', function()
    Wait(4500) -- We wait for RedMenu to set Default Skin
    SetEntityMaxHealth(PlayerPedId(), 250)
    SetEntityHealth(PlayerPedId(), 250)
end)

1. Critique of Initial Solution and Improved Solution with Check

However, proposed approach is not yet effective, due to that:

  • It introduces a hard-coded delay, regardless of whether the Default Skin is selected in RedMenu.

To address this concern, a better practice would be to check whether RedMenu is running and only introduce a delay under specific conditions:

AddEventHandler('playerSpawned', function()
    -- 1. Checking if RedMenu is running
    if GetResourceState('RedMenu') == 'started' then
        Wait(4500)
    end

    SetEntityMaxHealth(PlayerPedId(), 250)
    SetEntityHealth(PlayerPedId(), 250)
end)

Remaining Issues

Despite the improvement, some issues remain:

  • Occasionally, the delay may need to be longer due to factors such as extended loading times for in-game models (e.g., RequestModel).
  • If RedMenu is running but the Default Skin is not selected, the delay will still be applied.

2. Optimized Solution with KVP Check

A more thorough solution involves exploring the RedM source code to identify event triggers or alternative approaches. Unfortunately, no triggers were found, but we did discover a valuable piece of information. RedMenu employs a Key-Value Pair (KVP) storage, which can be leveraged. Specifically, RedMenu stores the default skin under the key int_PlayerDefaultSavedPed (source code).

With this knowledge, the delay can be refined to apply only when the Default Ped is set:

AddEventHandler('playerSpawned', function()
    -- 1. Checking if RedMenu is running
    if GetResourceState('RedMenu') == 'started' then
        -- 2. Checking if Default Ped is selected
        if GetExternalKvpInt('RedMenu', 'int_PlayerDefaultSavedPed') ~= 0 then
            Wait(4500)
        end
    end

    SetEntityMaxHealth(PlayerPedId(), 250)
    SetEntityHealth(PlayerPedId(), 250)
end)

3. Eliminating the Delay

To further optimize this, we can eliminate the delay by continuously checking for changes in the player’s ped:

AddEventHandler('playerSpawned', function()
    -- 1. Checking if RedMenu is running
    if GetResourceState('RedMenu') == 'started' then
        -- 2. Checking if Default Ped is selected
        if GetExternalKvpInt('RedMenu', 'int_PlayerDefaultSavedPed') ~= 0 then
            -- 3. Waiting until our player ped model changes
            local oldPed = PlayerPedId()
            while PlayerPedId() == oldPed do
                Wait(0)
            end
        end
    end

    SetEntityMaxHealth(PlayerPedId(), 250)
    SetEntityHealth(PlayerPedId(), 250)
end)

4. Adding a Timeout Check

Additionally, we can add a check for the duration of the loop execution, just in case our player ped hasn’t changed for some reason:

AddEventHandler('playerSpawned', function()
    -- 1. Checking if RedMenu is running
    if GetResourceState('RedMenu') == 'started' then
        -- 2. Checking if Default Ped is selected
        if GetExternalKvpInt('RedMenu', 'int_PlayerDefaultSavedPed') ~= 0 then
            -- 3. Waiting until our player ped model changes
            local oldPed = PlayerPedId()
            -- 4. Additionally, add the timeout check
            local timeoutAt = GetGameTimer() + 10000
            while PlayerPedId() == oldPed and GetGameTimer() < timeoutAt do
                Wait(0)
            end
        end
    end

    SetEntityMaxHealth(PlayerPedId(), 250)
    SetEntityHealth(PlayerPedId(), 250)
end)

Summary

In summary, we’ve examined the playerSpawned event in conjunction with RedMenu and the challenges it poses, particularly with the RedMenu Default Skin. We’ve explored several solutions, ranging from simple delays to more sophisticated options like KVP storage.

3 Likes