fivem icon indicating copy to clipboard operation
fivem copied to clipboard

RegisterPedheadshotTransparent and RegisterPedheadshot randomly not working

Open TrymTube opened this issue 1 year ago • 5 comments

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

TrymTube avatar Jul 02 '24 17:07 TrymTube

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);
});

AvarianKnight avatar Jul 03 '24 04:07 AvarianKnight

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 avatar Jul 03 '24 05:07 TrymTube

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

TrymTube avatar Jul 03 '24 15:07 TrymTube

Please refer to this part of my explanation:

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.

If you try to request another ped headshot while one is still loading you'll get a weird lockup (I don't know how else to describe it) where the texture refuses to get released even though it was never properly loaded.

AvarianKnight avatar Jul 11 '24 18:07 AvarianKnight

@AvarianKnight I've recently had to implement a new feature with ped headshots, i saw your message before and went ahead and used RegisterPedheadshot_3 along with streaming those textures so i can process multiple images.

The issue for me as of currently is the fact that these images come out with missing pixels.

Example: {9AA25AB5-B4A3-42C8-855D-8A61A5CFD6BB}

My Implementation:

const handleHeadshot = async (): Promise<string | boolean> => {
  const ped = PlayerPedId();
  let pedShot = RegisterPedheadshot_3(ped);
  const abortAt = GetGameTimer() + 100000;

  while (!IsPedheadshotReady(pedShot)) {
    const gt = GetGameTimer();
    if (gt > abortAt) {
      console.log('(handleHeadshot) Aborting request');
      UnregisterPedheadshot(pedShot);
      return false;
    }
    await sleep(0);
  }

  const txd: string = GetPedheadshotTxdString(pedShot);

  console.log('(handleHeadshot) txd: ', txd);

  return txd;
};

Front End:

  const characterImage = useMemo(() => {
    if (!character.texture) {
      console.error('(CharacterCard) character.texture is null');
      return '';
    }

    return `https://nui-img/${character.texture}/${character.texture}?v=${Date.now()}`;
  }, [character.texture]);
          <img
            className={cn(
              'w-full h-full transition-all group-focus:scale-110 group-hover:scale-110 object-contain',
              isSelected && 'scale-110'
            )}
            src={characterImage}
            alt={`Character Image`}
          />

vipexv avatar Oct 02 '24 01:10 vipexv