What's the Optimal Way to Get Hashkeys?

Whilst searching through the FiveM docs, and reading the Lua tutorials, I came across this tidbit;

Now, we call REQUEST_MODEL to load the actual vehicle model. This native takes a Hash argument, but in Lua you can also just pass a string and it’ll be converted to a hash. You’ll often see people use GetHashKey (GET_HASH_KEY), but if the native is specified as taking a Hash , you actually don’t need this.

Now I’m also aware of the use of tildes’ or ’ model ’ to convert a string to a hash, as well as joaat(model) and of course the native. The question I pose is, what’s the most optimal way to get a hash for a RequestModel or similar call?
Should I as a developer convert to a hash before using a function, to relieve strain or allow the natives like RequestModel convert the string to a hash themselves?

I mean it really depends on what you are trying to accomplish.

If you have a script that has some models predefined in a table, convert it into a hash table.

-- "normal"
local models = {
    "model_name_1",
    "model_name_2",
    "model_name_3"
}

-- "hashed"
local modelHashs = {
    `model_name_1`,
    `model_name_2`,
    `model_name_3`
}

Then you can just work with the hashs directly and have no overhead whatsoever inside your actual spawning code.

If you are spawning vehicles or something like that via a command that let’s you define the model yourself, just immediately use the hash or convert it into one.

RegisterCommand("v", function(src, args, raw)
    local model = args[1]

    -- check if user already input a number/hash or hash it yourself
    local hash = tonumber(model)
    if (not hash) then
        hash = joaat(model)
    end

    -- use hash for spawning or whatever
end, false)

GetHashKey should be avoided as it is a native call which results in a much higher overhead than actually necessary in most cases.
That being said, if you greatly limit its use to just a simple function call. Why not? If it makes the code more readable for you.

Simple answer: Don’t spam any native calls. Try to only call them once and save the result in a variable and use that one. Hashes don’t change. They always stay the same for the same input data.

So that’s exactly what I’ve been doing mostly, storing static models in a table with tildes’ and joaat() when I need it dynamically.
I guess what threw me off, was the fact that some natives, whose parameter is marked as a hash, can actually convert a string to a hash inside the function: ie RequestModel(‘adder’).
So my question really was, are we better off converting it to a hash before calling the native or just let the native do the grunt work? (I guess this would depend on what method the native uses to convert the string to a hash).

To be quite honest, I didn’t even know that was a thing until now that you mentioned it.

But as you already said, only “some” of the natives work like that. Sooo I would still go with what I usually do as it is at least consistent.
Also if RequestModel takes a string and e.g. HasModelLoaded requires a hash, you would need to do it anyways.

Well that’s the thing, according to what I’ve quoted above, any Native that has a hash parameter can automatically convert string to hashes.

Well, I just tested this using the following code:

function SpawnVehicle(model)
	RequestModel(model)
	while (not HasModelLoaded(model)) do
		Wait(0)
	end
	local veh = CreateVehicle(model, GetEntityCoords(PlayerPedId()), 0.0, true, true)
end

RegisterCommand("v1", function(src, args, raw)
	SpawnVehicle(joaat(args[1]))
end, false)

RegisterCommand("v2", function(src, args, raw)
	SpawnVehicle(args[1])
end, false)

And both of the work on a 5848 server and current beta client build.

Also done some testing regarding performance using the following code:

RegisterCommand("perf", function(src, args, raw)
	local model = "adder"
	Wait(0)
	for i = 1, 100000 do
		RequestModel(model)
		HasModelLoaded(model)
	end
	Wait(0)
	print("string:", GetFrameTime())

	model = `adder`
	Wait(0)
	for i = 1, 100000 do
		RequestModel(model)
		HasModelLoaded(model)
	end
	Wait(0)
	print("hash:", GetFrameTime())
end, false)

image

Using the string version seems to be slower at 60% of the hash version. But it barely makes any sort of difference if you call it only once. I also tried it with “just a thousand times” and they are basically the same.

Tested on a beefy system (Ryzen 7 5800x, Zotac GTX 1080 Amp Extreme, ~100-180fps) and the worst client side resources were the default chat and the spawnmanager xD

1 Like

I was just testing this then, but you beat me to the punch! So it’s definitely better to first convert static tables using tildes’ and dynamic ones using joaat(model) BUT it’s a negligible difference.

For those wondering where I found the original quote; Creating your first script in Lua - Cfx.re Docs

1 Like