[social-pip-avocado] GetGroundZFor_3dCoord misbehaves / causes crash when using vector3

GetGroundZFor_3dCoord takes an x, y and z argument, common practice is to pass a vector in other natives but doing so will cause the native to error.
According to the native ref, there is an extra unknown parameter that’s “almost always 0”.
Passing a value together with a vector will crash the game.
The crash appears to differ based on the value passed in the last argument.

Using a false value (0 or false)

This is what is “almost always used” in decompiled scripts

Repro (using runcode):

crun GetGroundZFor_3dCoord(vector3(0.0, 0.0, 0.0), 0)

Crash Dialog:
image

Crash Dump:
CfxCrashDump_2020_09_15_13_18_26.zip (1.5 MB)

Using a true value (1 or true)

Repro (using runcode):

crun GetGroundZFor_3dCoord(vector3(0.0, 0.0, 0.0), 1)

Crash Dialog:

Crash Dump:
CfxCrashDump_2020_09_15_13_31_13.zip (1.4 MB)

This is most likely not a FiveM specific issue, but I figured I’d report it since it’s easy to reproduce.
The obvious workaround is just not to use a vector as an argument.

1 Like

I believe it does not crash if you don’t use a vector?

This is an artifact as to how vectors are unrolled in the ScriptRuntime. If you look at the native definition,

// GetGroundZFor_3dCoord
BOOL GET_GROUND_Z_FOR_3D_COORD(float x, float y, float z, float* groundZ, BOOL unk);

and corresponding codegen output:

function Global.GetGroundZFor_3dCoord(x, y, z, unk)
	return _in(0xC906A7DAB05C8D2B, x, y, z, _f, unk, _r)
end

By using a vector3 (in Lua): x will correspond to vector3(0, 0, 0), which is unrolled to (0, 0, 0) by the runtime. Meanwhile, y will be zero/one and correspond to the fourth argument in the native, i.e., a pointer (groundZ); you are essentially passing a nullptr to the native.

Edit: Generally speaking, when the native depends upon pointers you shouldn’t be passing vector3’s to that native. There is one potential workaround for the fxv2 API, e.g.,

nCtx.SetArgument(0, (float)lua_utonumber(L, 1)); // x
nCtx.SetArgument(1, (float)lua_utonumber(L, 2)); // y
nCtx.SetArgument(2, (float)lua_utonumber(L, 3)); // z
nCtx.SetArgument(3, &ref_groundZ);

becoming aware of vector-types,

int stack_counter = 0;
if  (lua_isvector3(...)) {
	// gritLua fetch vector.
}
else {
	nCtx.SetArgument(stack_counter++, (float)lua_utonumber(L, 1)); // x
	nCtx.SetArgument(stack_counter++, (float)lua_utonumber(L, 2)); // y
	nCtx.SetArgument(stack_counter++, (float)lua_utonumber(L, 3)); // z
}
nCtx.SetArgument(stack_counter++, &ref_groundZ);

But that is a low-priority feature and would require changes to the codegen to be conscious of “float x, float y, float z” parameter sequences.

2 Likes

Thank you gottfried for providing an in-depth explanation of the behavior

Ah, that’s a nice little mental note to keep in mind. Thanks!

That’s correct. I mentioned that as the “workaround”.