Load pathnodes close to point

Is there any way to load pathnodes close to a certain area?
I need them for several calculations however I am almost certain that the player is not near enough for them to be loaded automatically.
I need one of the following natives to work independently of players:

GetClosestVehicleNode
GetClosestVehicleNodeWithHeading
GetNthClosestVehicleNode
GetNthClosestVehicleNodeWithHeading

I tried the following natives:

AddNavmeshRequiredRegion - it didn’t seem to do anything
GenerateDirectionsToCoord - this did load pathnodes of a few zones but not all necessary ones
LoadAllPathNodes - Did nothing but that’s to be expected given its removal
SetZoneEnabled - it didn’t seem to do anything

RequestPathsPreferAccurateBoundingstruct - it didn’t seem to do anything
SetAllPathsCacheBoundingstruct - also tried using RequestPaths together with this. Also didn’t seem to do anything

Also checked with this:
AreNodesLoadedForArea - returned false

The usage was always the same. I changed the code accordingly, spawned on the server (always at Mission Row PD - loading screen is located at the bridge Raton Canyon (the bridge on the highway north of Fort Zancudo), and activated the resource. Example for SetZoneEnabled (all code is clientside):

local Ax = 2450.84
local Ay = 5619.26
local Az = 44.98

-- replace this part with proper versions of the above natives
local zone = GetZoneAtCoords(Ax, Ay, Az)
SetZoneEnabled(zone, true)
-- end replace

for i = 1, 20 do
  local _, position, heading = GetNthClosestVehicleNodeWithHeading(Ax, Ay, Az, i, 0, 3.0, 0) -- replace this native if necessary
  print(position.x, position.y, position.z, heading)
end

Note: I know that the native-description for GetNthClosestVehicleNodeWithHeading mentions the variables being 9, 3.0 and 0. If I was standing close to the above coordinates, the returned nodes were all valid. If standing far away (important: restart FiveM, otherwise the nodes sometimes are still in cache) I got a lot of 0.0, 0.0, 0.0 meaning it didn’t find a valid node.

I might have missed something that I also tested.
Another option that might work is placing the players’ waypoint on point A. Only issue is I can’t access the current position of the waypoint or check whether one is placed. I found Game.IsWaypointActive and World.WaypointPosition but I’m not sure if I can access that data with lua?

RequestPathsPreferAccurateBoundingstruct - FiveM Natives @ Cfx.re Docs should at least enqueue a request for CPathFind to process later - did you wait for the loaded call to return though?

Just tried, didn’t get better results. If I understood correctly I would need to do

-- Starting point
local Ax = 166.24
local Ay = 6520.88

-- End point
local Bx = 2450.84
local By = 5619.26

local retval = RequestPathsPreferAccurateBoundingstruct(Ax, Ay, Bx, By)

while not retval do
  Wait(10)
end

retval is immediately true, the while-loop is never needed.
Something to note though is that upon rerunning the same script a part of the pathnodes is loaded and can be accessed. Others stay unloaded though, further reruns do not yield additional pathnodes.

Some additional info: The loading screen is at the mentioned bridge. In this example using GetClosestVehicleNodeWithHeading and point A yields the closest pathnode inside area Paleto Forest (I believe point A ist only just in area Paleto Bay). Point B is in Braddock Pass, GetClosestVehicleNodewithHeading returns false / (0.0, 0.0, 0.0) here. My take from this is that area Paleto Forest is loaded thanks to proximity from the loading screen, whereas Braddock pass is not.

… right, the AreNodesLoadedForArea call, whoops.

The code in CPathFind does look odd though but I’m currently not able to focus enough to read the disassembly there to find out what’s missing here.

1 Like

In the following reply I mention zones multiple times. I’ll always refer to the following map:
ZonesMap
I do know about a map that consists of squares completely, although I didn’t look further into it whether that one is the one to be used here.


Not sure I’m completely catching you here.
AreNodesLoadedForArea is just a check, not the function triggering loading, correct?
I took the above code and replaced RequestPaths... with AreNodesLoadedForArea. This got the function stuck in the while-loop.

Then I went a little overboard. I added every native that I have tried so far at the same time. The GPS-Route I plot accurately does what I want. The GPS-route stys on the screen now, tries before had it vanish point by point.

Note: Startposition and Endposition differ from points A and B but are properly defined in the script.

local startzone = GetZoneAtCoords(Ax, Ay, Az)
local startzonename = GetNameOfZone(Ax, Ay, Az)
print(startzone, startzonename)

local endzone = GetZoneAtCoords(Bx, By, Bz)
local endzonename = GetNameOfZone(Bx, By, Bz)
print(endzone, endzonename)
SetZoneEnabled(startzone, true)
SetZoneEnabled(endzone, true)

local centerx = (Bx + Ax)/2
local centery = (Ay + By)/2
local radius = math.abs(Bx-Ax)
if math.abs(By-Ay) > radius then
    radius = math.abs(By-Ay)
end

AddNavmeshRequiredRegion(centerx, centery, radius)

ClearGpsMultiRoute()
StartGpsMultiRoute(6, false, true)
AddPointToGpsMultiRoute(Startposition.x, Startposition.y, Startposition.z)
AddPointToGpsMultiRoute(Ax, Ay, Az)
AddPointToGpsMultiRoute(Bx, By, Bz)
AddPointToGpsMultiRoute(Endposition.x, Endposition.y, Endposition.z)
SetGpsMultiRouteRender(true)

local _ = RequestPathsPreferAccurateBoundingstruct(Ax, Ay, Bx, By)

local _, _, _, _ = GenerateDirectionsToCoord(Startposition.x, Startposition.y, Startposition.z, 0)
local _, _, _, _ = GenerateDirectionsToCoord(Ax, Ay, Az, 0)
local _, _, _, _ = GenerateDirectionsToCoord(Bx, By, Bz, 0)
local _, _, _, _ = GenerateDirectionsToCoord(Endposition.x, Endposition.y, Endposition.z, 0)
    
local retval = false
retval = AreNodesLoadedForArea(Ax, Ay, Bx, By)
local k = 0
print('Requested', retval)
while not retval do
    print('Waiting for return')
    k = k + 1
    Wait(10)
    if k > 100 then
        print('Aborted')
    end
end

The above function gets stuck in the while-loop (forgot the break in there). After I removed the while-loop the function afterwards ran through.
What I want the function to output is best displayed in the following picture. The picture is taken with one player on the server, me. I’m standing in Paleto Bay and just used NoClip to fly over part of the highway.

I’d like the car to drive from a starting point (the leftmost yellow blip on the map) to a goal (the upmost green blip) without a player being near (if you remember the last posts, I’m that lunatic). For that I placed a few nodes myself which function as anchors and divide the whole part in multiple sections (in the picture: yellow is from start to my first node. White is first to second node, blue is second to third node, green is third node to finish).
Let’s assume that for each section the car drives from point A to point B. I execute the following code (not actual copy, just the idea. I omitted a few safeguards that I put in place):

local x, y, z = GetClosestVehicleNode(Ax, Ay, Az, 0, 3.0, 0) -- start coordinates
local tx, ty, tz = GetClosestVehicleNode(Bx, By, Bz, 0, 3.0, 0) -- target coordinates
-- if the nodes are loaded both my start and target are always exactly on a vehicle node position.
-- natives are done wrong in this example but it's done properly in the script
local distance = Vdist(x, y, z, tx, ty, tz)
while distance > 1.0 do -- reach position close to point B iteratively
    local newx, newy, newz = 0 -- initialize temp coordinate values
    local newdistance = 999999.9 -- initialize temp distance
    for i = 1, 40 do
        local _, newpos = GetNthClosestVehicleNode(x, y, z, i, 0, 3.0, 2.5) -- find 40 pathnodes close to the initial position
        newdistance = Vdist(newpos.x, newpos.y, newpos.z, tx, ty, tz) -- calculate the distance to the goal from the new position
        if newdistance < distance then
            -- the new position is better than the old so is saved
            newx, newy, newz = newpos
            distance = newdistance
        end
    end
    -- after the for-loop a new best node is found, which gets saved
    x = newx
    y = newy
    z = newz
    distance = Vdist(x, y, z, tx, ty, tz) -- calculating new distance
end

As you can see from the above picture, the function works just fine if I am in the general area, meaning that in this case there is no obvious error in my logic.
It also works just fine if I fly over the highway once, then teleport far away and execute the function in a moderate timeframe. The function will still find all appropriate positions and resolve in the above way. I don’t know an upper bound for how long it will reliably work.

However it is definitely not a given that a player was at many places, also most likely the game is going to unload pathnodes after some time. If that happens the results look like in the below picture. This is the exact picture I got after executing the above script without the while-loop. However I could get to this result a few times with a sensible amount of natives that are supposed to load pathnodes or navmesh.

Not everything has failed: Blue and Green function executed as they should. White and yellow however didn’t. There exists only one yellow node, at (0, 0, 0). White should start at the northern end of Paleto Bay (compare with the correct attempt, the colors match). However it starts somewhere beneath the Paleto Bay PD, which lines up pretty much perfect with the border between the areas Paleto Bay and Paleto Forest. That’s where my suspicion comes from that Paleto Forest is loaded, Paleto Bay isn’t. The issue starts in line 1 - the start coordinates that are returned do not match the input at all (I checked this by printing the inputs and the output). Also the white function tries to reach (0, 0, 0). This means that for white the target coordinate (line 2) native failed to return a pathnode, defaulting to the standard values.

Same is true for yellow, where both start and target coordinates return (0, 0, 0).

Also something interesting to note:
Because of the way the zones lay on the map both white and yellow go through three zones whereas blue and green only impact two zones. Could this be what in the end results in the failure? Because I’m only loading the zones at start and finish, the zone in the middle might not be loaded thus resulting in false returns?