citizenfx / fivem

The source code for the Cfx.re modification frameworks, such as FiveM, RedM and LibertyM, as well as FXServer.
https://cfx.re/
3.4k stars 1.99k forks source link

RegisterPedheadshotTransparent and RegisterPedheadshot randomly not working #2611

Open TrymTube opened 5 days ago

TrymTube commented 5 days ago

What happened?

Issue:

I am trying to use RegisterPedheadshotTransparent but this is not reliable and it is randomly working. It is like a 50/50 Chance that it gives me a Headshot or not.

Expected result

it should give me a Picture of my player head

Reproduction steps

try using it and displaying it

Importancy

Unknown

Area(s)

FiveM

Specific version(s)

FiveM Build 3095 // Artifacts 8633

Additional information

No response

AvarianKnight commented 5 days ago

This isn't a bug, first if you aren't please make sure you wait for IsPedheadshotReady (and possibly HasStreamedPedAssetsLoaded) and make sure you UnregisterPedheadshot when you're finished with them.

A hopefully understandable explanation on why this happens (thanks to gottfriedleibniz for most of this, this is largely paraphrasing him):

REGISTER_PEDHEADSHOT_TRANSPARENT is hard coded to use pedmugshot_01.ytd (128x128px) the ytds are encoded as DXT5/BC3 and has 128x128 which has alpha channels.

REGISTER_PEDHEADSHOT_HIRES is coded to use pedmugshot_0[2..8].ytd (which is also 128x128px) the ytds are encoded as DXT1/BC1.

REGISTER_PEDHEADSHOT is coded to use pedmugshot_0[9..34].ytd (which is 64x64px) these ytds are encoded as DXT1/BC1

To note: BC1 has 1 bit for alpha data BC3 has 1 byte for alpha data

The notable things here though is that the they both use 128x128px textures, so if you replace pedmugshot_0[2..8].ytd with a copy of pedmugshot_01.ytd it will be BC3 which means it will actually be able to store alpha data, and you'll be able to use 7 ped headshots at a time (or 8 if you still use RegisterPedheadshotTransparent)

TLDR: RegisterPedheadshotTransparent only has 1 available texture slot to draw to (being pedmugshot_01.ytd), which is why this happens.

You can use RegisterPedheadshot_3 (aka REGISTER_PEDHEADSHOT_HIRES) with transparent textures streamed over the original slots and it will work the same as RegisterPedshotTransparent, you will only have 7 slots to work with at a time though.

Here's a zip with the stream-able files

Put these into a resources stream/ folder and you should be able to request 7 slots at a time for RegisterPedheadshot_3.

Some example code for how I've used this:

Client:

// note this isn't complete code, we have stuff to request this 7 at a time & spam retry 20 times until it eventually get it
// or just fails. 
const getPedHeadshot = async (pedHandle: number): Promise<boolean> => {
    let pedShot = RegisterPedheadshot_3(pedHandle);
    const abortAt = GetGameTimer() + 100;

    while (!IsPedheadshotReady(pedShot)) {
        const gt = GetGameTimer();
        if (gt > abortAt) {
            // might get stuck if we don't stop the request
            UnregisterPedheadshot(pedShot);
            return false;
        }
        await Delay(0);
    }

    const txd: string = GetPedheadshotTxdString(pedShot);

    await nuiHandler.sendNUIMessage({
        app: "headshot",
        method: "RegisterHeadshot",
        data: txd 
    });

    UnregisterPedheadshot(pedShot);
    return true;
};

Ui:

useNuiEvent("headshot", "RegisterHeadshot", async (txd: string) => {
    const imgFetch = await fetch(`https://nui-img/${txd}/${txd}`);
    const imgBlob = await imgFetch.blob();

    setImageBlob(imgBlob);
});
TrymTube commented 5 days ago

This isn't a bug, first if you aren't please make sure you wait for IsPedheadshotReady (and possibly HasStreamedPedAssetsLoaded) and make sure you UnregisterPedheadshot when you're finished with them

I have done the exact that, I have been taking the headshot and I have a wait to ensure the headshot is usable and ready, at the end I use Unregister, but still sometimes when I try to load my ID the photo is not there and it is stuck in an endless loop trying to wait for a usable headshot. I dont know if Understood that correct but if I put in the Stream files it might work better?

TrymTube commented 5 days ago

this is currently the code for the headshot

local function getPlayerHeadshot(playerSource)
    local playerPed = GetPlayerPed(GetPlayerFromServerId(playerSource))

    local handle, test = RegisterPedheadshotTransparent(playerPed)

    local abortAt = GetGameTimer() + 100

    -- print(1, handle, IsPedheadshotReady(handle), IsPedheadshotValid(handle))

    while not IsPedheadshotReady(handle) do
        local gt = GetGameTimer()

        if gt > abortAt then
            UnregisterPedheadshot(handle)
            return false
        end
        Wait(0)
    end

    local txd = GetPedheadshotTxdString(handle)

    UnregisterPedheadshot(handle)
    return txd
end

function ClientShowCardEvent(data)
    data.player.character.head = './images/person.png'

    if data.player.character.head == './images/person.png' then
        local headTxt = getPlayerHeadshot(data.playerSource)

        if headTxt then
            data.player.character.head = string.format("https://nui-img/%s/%s", headTxt, headTxt)
        end
    end

    SetNuiFocus(true, true)
    SendNUIMessage({
        action = 'showCard',
        data = data
    })
    SendNUIMessage({
        action = 'setVisible',
        data = true
    })
end