Ok so I sifted through the GitHub repo that @ChristopherM linked and I narrowed down the code for correctly teleporting to a waypoint so this is what I ended up with here is the full code if people are interested
using CitizenFX.Core;
using System.Threading.Tasks;
using static CitizenFX.Core.Native.API;
public class Commands : BaseScript
{
public Commands()
{
RegisterCommand("tpm", new Action<int, List<object>, string> (async (src, args, raw) =>
{
TeleportToWaypoint();
}), false);
}
public static async void TeleportToWaypoint()
{
if(Game.IsWaypointActive)
{
var pos = World.WaypointPosition;
await TeleportToCoords(pos);
}
}
public static Vehicle GetVehicle(bool lastVehicle = false)
{
if (lastVehicle)
{
return Game.PlayerPed.LastVehicle;
}
else
{
if (Game.PlayerPed.IsInVehicle())
{
return Game.PlayerPed.CurrentVehicle;
}
}
return null;
}
public static async Task TeleportToCoords(Vector3 pos, bool
safeModeDisabled = false)
{
if (!safeModeDisabled)
{
// Is player in a vehicle and the driver? Then we'll use that to teleport.
var veh = GetVehicle();
bool inVehicle() => veh != null && veh.Exists() && Game.PlayerPed == veh.Driver;
bool vehicleRestoreVisibility = inVehicle() && veh.IsVisible;
bool pedRestoreVisibility = Game.PlayerPed.IsVisible;
// Freeze vehicle or player location and fade out the entity to the network.
if (inVehicle())
{
veh.IsPositionFrozen = true;
if (veh.IsVisible)
{
NetworkFadeOutEntity(veh.Handle, true, false);
}
}
else
{
ClearPedTasksImmediately(Game.PlayerPed.Handle);
Game.PlayerPed.IsPositionFrozen = true;
if (Game.PlayerPed.IsVisible)
{
NetworkFadeOutEntity(Game.PlayerPed.Handle, true, false);
}
}
// Fade out the screen and wait for it to be faded out completely.
DoScreenFadeOut(500);
while (!IsScreenFadedOut())
{
await Delay(0);
}
// This will be used to get the return value from the groundz native.
float groundZ = 850.0f;
// Bool used to determine if the groundz coord could be found.
bool found = false;
// Loop from 950 to 0 for the ground z coord, and take away 25 each time.
for (float zz = 950.0f; zz >= 0f; zz -= 25f)
{
float z = zz;
// The z coord is alternating between a very high number, and a very low one.
// This way no matter the location, the actual ground z coord will always be found the fastest.
// If going from top > bottom then it could take a long time to reach the bottom. And vice versa.
// By alternating top/bottom each iteration, we minimize the time on average for ANY location on the map.
if (zz % 2 != 0)
{
z = 950f - zz;
}
// Request collision at the coord. I've never actually seen this do anything useful, but everyone keeps telling me this is needed.
// It doesn't matter to get the ground z coord, and neither does it actually prevent entities from falling through the map, nor does
// it seem to load the world ANY faster than without, but whatever.
RequestCollisionAtCoord(pos.X, pos.Y, z);
// Request a new scene. This will trigger the world to be loaded around that area.
NewLoadSceneStart(pos.X, pos.Y, z, pos.X, pos.Y, z, 50f, 0);
// Timer to make sure things don't get out of hand (player having to wait forever to get teleported if something fails).
int tempTimer = GetGameTimer();
// Wait for the new scene to be loaded.
while (IsNetworkLoadingScene())
{
// If this takes longer than 1 second, just abort. It's not worth waiting that long.
if (GetGameTimer() - tempTimer > 1000)
{
// Log("Waiting for the scene to load is taking too long (more than 1s). Breaking from wait loop.");
break;
}
await Delay(0);
}
// If the player is in a vehicle, teleport the vehicle to this new position.
if (inVehicle())
{
SetEntityCoords(veh.Handle, pos.X, pos.Y, z, false, false, false, true);
}
// otherwise, teleport the player to this new position.
else
{
SetEntityCoords(Game.PlayerPed.Handle, pos.X, pos.Y, z, false, false, false, true);
}
// Reset the timer.
tempTimer = GetGameTimer();
// Wait for the collision to be loaded around the entity in this new location.
while (!HasCollisionLoadedAroundEntity(Game.PlayerPed.Handle))
{
// If this takes too long, then just abort, it's not worth waiting that long since we haven't found the real ground coord yet anyway.
if (GetGameTimer() - tempTimer > 1000)
{
//Log("Waiting for the collision is taking too long (more than 1s). Breaking from wait loop.");
break;
}
await Delay(0);
}
// Check for a ground z coord.
found = GetGroundZFor_3dCoord(pos.X, pos.Y, z, ref groundZ, false);
// If we found a ground z coord, then teleport the player (or their vehicle) to that new location and break from the loop.
if (found)
{
// Log($"Ground coordinate found: {groundZ}");
if (inVehicle())
{
SetEntityCoords(veh.Handle, pos.X, pos.Y, groundZ, false, false, false, true);
// We need to unfreeze the vehicle because sometimes having it frozen doesn't place the vehicle on the ground properly.
veh.IsPositionFrozen = false;
veh.PlaceOnGround();
// Re-freeze until screen is faded in again.
veh.IsPositionFrozen = true;
}
else
{
SetEntityCoords(Game.PlayerPed.Handle, pos.X, pos.Y, groundZ, false, false, false, true);
}
break;
}
// Wait 10ms before trying the next location.
await Delay(10);
}
// If the loop ends but the ground z coord has not been found yet, then get the nearest vehicle node as a fail-safe coord.
if (!found)
{
var safePos = pos;
GetNthClosestVehicleNode(pos.X, pos.Y, pos.Z, 0, ref safePos, 0, 0, 0);
// Teleport vehicle, or player.
if (inVehicle())
{
SetEntityCoords(veh.Handle, safePos.X, safePos.Y, safePos.Z, false, false, false, true);
veh.IsPositionFrozen = false;
veh.PlaceOnGround();
veh.IsPositionFrozen = true;
}
else
{
SetEntityCoords(Game.PlayerPed.Handle, safePos.X, safePos.Y, safePos.Z, false, false, false, true);
}
}
// Once the teleporting is done, unfreeze vehicle or player and fade them back in.
if (inVehicle())
{
if (vehicleRestoreVisibility)
{
NetworkFadeInEntity(veh.Handle, true);
if (!pedRestoreVisibility)
{
Game.PlayerPed.IsVisible = false;
}
}
veh.IsPositionFrozen = false;
}
else
{
if (pedRestoreVisibility)
{
NetworkFadeInEntity(Game.PlayerPed.Handle, true);
}
Game.PlayerPed.IsPositionFrozen = false;
}
// Fade screen in and reset the camera angle.
DoScreenFadeIn(500);
SetGameplayCamRelativePitch(0.0f, 1.0f);
}
}
This code doe not take permissions into account… my next task lol because i like to build my own stuff.
its safe to say I was missing some things but overall I think this setup is a lot more performant since it is also using async tasks… I have to say credits go to @ChristopherM for mentioning the repo and thanks to the person that made that menu system… even tho I’m not a fan of using other peoples work its nice to have a base to go off of so here is the link to the repo i got this from I just isolated the code… GitHub - TomGrobbe/vMenu: vMenu is a custom server sided trainer/menu, built using a custom version of NativeUI. It has full permissions support, so the server owner can decide who's allowed to do what.