Skip to content

Repository files navigation

RoadRoute

RoadRoute is a server-side road navigation plugin for SA-MP and open.mp servers. It calculates routes over a preprocessed GTA San Andreas vehicle road graph and streams directional navigation arrows as per-player objects.

RoadRoute preview

#include <RoadRoute>

public SomeFeature(playerid)
{
    RoadRoute_SetDestination(playerid, 1481.0, -1770.0, 18.8);
    return 1;
}

RoadRoute is not a gamemode and does not know about jobs, missions, taxis, trucking, police, roleplay data, inventories, databases, commands, or command processors. Any feature can call the same generic API.

What It Does

RoadRoute provides the missing navigation primitive for multiplayer servers:

  • Loads a compact roadgraph.dat database generated from legitimate GTA SA vehicle path node data.
  • Finds the nearest road node through a spatial hash instead of scanning the whole map every time.
  • Calculates road-following routes with A* in a bounded reusable worker pool.
  • Converts raw node paths into simplified route geometry and sampled arrows.
  • Tracks progress incrementally along the route.
  • Detects off-route movement with threshold, debounce, and cooldown rules.
  • Recalculates routes asynchronously when players leave the route.
  • Detects arrival and dispatches Pawn callbacks on the main script thread.
  • Streams only a sliding window of nearby arrows.
  • Reuses per-player PlayerObject slots through the Pawn include.

The full route is never rendered at once. Defaults are conservative for Android: 32 visible arrows, 8.0 unit spacing, 160.0 render distance, and throttled updates from the gamemode's OnPlayerUpdate or another timer.

Arrow rotation follows the sampled road direction. Around corners, RoadRoute uses neighbouring arrow samples to smooth the heading, so visible arrows rotate progressively through right and left turns instead of snapping from one straight segment to the next.

Repository Layout

include/RoadRoute.inc          Pawn public API and PlayerObject pool
src/core                       Route ownership, progress, reroute, arrival
src/graph                      Compact road graph and binary format
src/spatial                    Spatial hash nearest-road lookup
src/routing                    A*, simplification, sampling, turns
src/async                      Bounded worker pool
src/rendering                  Arrow-window generation
src/pawn                       SA-MP/open.mp plugin ABI and natives
tools/node_converter           roadgraph.dat converter
examples/roadroute_test.pwn    Dependency-free example filterscript
tests                          Native C++ test runner

Build

Linux:

cmake -S . -B build
cmake --build build --parallel
ctest --test-dir build --output-on-failure

Windows:

cmake -S . -B build -G "Visual Studio 17 2022"
cmake --build build --config Release

The build produces:

  • RoadRoute.so on Linux
  • RoadRoute.dll on Windows
  • RoadRouteNodeConverter

Installation

Copy the plugin binary to your server's plugins directory and add it to the server config:

plugins RoadRoute.so

or on Windows:

plugins RoadRoute.dll

Copy include/RoadRoute.inc into your Pawn include path. Create:

scriptfiles/RoadRoute/

and place roadgraph.dat there. On startup, RoadRoute validates the file magic, format version, counts, and checksum. If the database is missing or corrupted, the plugin logs the problem and route requests fail instead of silently using bad data.

Road Graph Generation

RoadRoute does not ship copyrighted GTA files. Generate roadgraph.dat from your own legitimate GTA San Andreas installation or a lawful export of vehicle path node data.

The included converter currently consumes a normalized CSV export:

external_id,x,y,z,flags,width,link_id;link_id;...

Example:

RoadRouteNodeConverter vehicle_nodes.csv scriptfiles/RoadRoute/roadgraph.dat

Only vehicle-compatible road/path nodes should be exported into the CSV. Preserve known flags and width values when your extractor can provide them, but do not invent semantics for undocumented path fields.

Pawn Usage

The simple API is designed for a few lines of Pawn:

RoadRoute_SetDestination(playerid, x, y, z);
RoadRoute_Clear(playerid);
RoadRoute_IsActive(playerid);
RoadRoute_GetProgress(playerid);
RoadRoute_GetRemainingDistance(playerid);

Add the processing calls from the include:

public OnPlayerConnect(playerid)
{
    RoadRoute_InitPlayer(playerid);
    return 1;
}

public OnPlayerUpdate(playerid)
{
    RoadRoute_ProcessPlayer(playerid);
    return 1;
}

public OnPlayerDisconnect(playerid, reason)
{
    RoadRoute_OnPlayerDisconnect(playerid);
    return 1;
}

You can call RoadRoute from any feature:

StartTruckDelivery(playerid)
{
    return RoadRoute_SetDestination(playerid, DeliveryX, DeliveryY, DeliveryZ);
}

Taxi_SetTarget(playerid, Float:x, Float:y, Float:z)
{
    return RoadRoute_SetDestination(playerid, x, y, z);
}

Mission_Start(playerid)
{
    return RoadRoute_SetDestination(playerid, MissionX, MissionY, MissionZ);
}

Configuration

Configuration is per player:

RoadRoute_SetArrowModel(playerid, 19133);
RoadRoute_SetArrowRotationOffset(playerid, -90.0, 0.0, 0.0);
RoadRoute_SetArrowHeight(playerid, 0.05);
RoadRoute_SetArrowSpacing(playerid, 8.0);
RoadRoute_SetRenderDistance(playerid, 160.0);
RoadRoute_SetMaxVisibleArrows(playerid, 32);
RoadRoute_SetRerouteDistance(playerid, 25.0);
RoadRoute_SetRerouteCooldown(playerid, 3000);
RoadRoute_SetArrivalRadius(playerid, 8.0);
RoadRoute_SetAutoClear(playerid, true);

Model 19133 is the default arrow candidate, but it is configurable because different GTA models have different orientation. Runtime recoloring is not configured by the native plugin directly; instead, the include exposes OnRoadRouteArrowCreated so each gamemode can optionally apply object materials or colours that match its own visual style.

RoadRoute_SetArrowRotationOffset is still applied after the automatic road heading. For example, if your arrow model needs 0.499998, -90.500022, -2.0 when driving straight, use those values as the offset. Right turns will reduce the final Z heading progressively, and left turns will increase it progressively as the sampled road direction changes.

Coordinates are always supplied by RoadRoute. If you want to use a custom arrow object setup, configure only the model, rotation offset, and optional material:

#define GPS_ARROW_USE_MATERIAL      (1)
#define GPS_ARROW_USE_MAT_COLOR     (0)

#define GPS_ARROW_MODEL             (1318)
#define GPS_ARROW_RX                (0.499998)
#define GPS_ARROW_RY                (-90.500022)
#define GPS_ARROW_RZ                (-2.000000)

#define GPS_ARROW_MAT_INDEX         (2)
#define GPS_ARROW_MAT_MODEL         (18241)
#define GPS_ARROW_MAT_TXD           "cw_tempstuffcs_t"
#define GPS_ARROW_MAT_TEXTURE       "bluemetal03"
#define GPS_ARROW_MAT_COLOR         (0x00000000)

stock ConfigureGPSArrow(playerid)
{
    RoadRoute_SetArrowModel(playerid, GPS_ARROW_MODEL);
    RoadRoute_SetArrowRotationOffset(playerid, GPS_ARROW_RX, GPS_ARROW_RY, GPS_ARROW_RZ);
    return 1;
}

public OnRoadRouteArrowCreated(playerid, objectid, slot)
{
    #if GPS_ARROW_USE_MATERIAL
        #if GPS_ARROW_USE_MAT_COLOR
            SetPlayerObjectMaterial(playerid, objectid, GPS_ARROW_MAT_INDEX, GPS_ARROW_MAT_MODEL, GPS_ARROW_MAT_TXD, GPS_ARROW_MAT_TEXTURE, GPS_ARROW_MAT_COLOR);
        #else
            SetPlayerObjectMaterial(playerid, objectid, GPS_ARROW_MAT_INDEX, GPS_ARROW_MAT_MODEL, GPS_ARROW_MAT_TXD, GPS_ARROW_MAT_TEXTURE);
        #endif
    #endif
    return 1;
}

Use SetPlayerObjectMaterial, not SetDynamicObjectMaterial, because RoadRoute streams arrows as per-player objects created with CreatePlayerObject.

Material toggles:

Setting Value Result
GPS_ARROW_USE_MATERIAL 0 Do not call SetPlayerObjectMaterial; the arrow uses the model's default texture.
GPS_ARROW_USE_MATERIAL 1 Apply GPS_ARROW_MAT_INDEX, GPS_ARROW_MAT_MODEL, GPS_ARROW_MAT_TXD, and GPS_ARROW_MAT_TEXTURE.
GPS_ARROW_USE_MAT_COLOR 0 Apply the material without a colour override.
GPS_ARROW_USE_MAT_COLOR 1 Apply the material and pass GPS_ARROW_MAT_COLOR as the material colour.

Minimal custom model and rotation with no material:

#define GPS_ARROW_USE_MATERIAL      (0)
#define GPS_ARROW_USE_MAT_COLOR     (0)

#define GPS_ARROW_MODEL             (1318)
#define GPS_ARROW_RX                (0.499998)
#define GPS_ARROW_RY                (-90.500022)
#define GPS_ARROW_RZ                (-2.000000)

stock ConfigureGPSArrow(playerid)
{
    RoadRoute_SetArrowModel(playerid, GPS_ARROW_MODEL);
    RoadRoute_SetArrowRotationOffset(playerid, GPS_ARROW_RX, GPS_ARROW_RY, GPS_ARROW_RZ);
    return 1;
}

public OnRoadRouteArrowCreated(playerid, objectid, slot)
{
    return 1;
}

Callbacks

forward OnRoadRouteCalculated(playerid, bool:success);
forward OnRoadRouteRecalculated(playerid);
forward OnRoadRouteArrived(playerid);
forward OnRoadRouteCleared(playerid);
forward OnRoadRouteTurnApproaching(playerid, turnType, Float:distance);
forward OnRoadRouteArrowCreated(playerid, objectid, slot);

Worker threads never call Pawn. Completed results are queued in native code and dispatched by RoadRoute_ProcessPlayer / RoadRoute_ProcessCallbacks on the main script thread. OnRoadRouteArrowCreated is called by the Pawn include on the main script thread each time it creates or recreates a visible arrow object.

Advanced API

The include exposes a route-handle compatibility layer:

new route = RoadRoute_Create(playerid);
RoadRoute_SetTarget(route, x, y, z);
RoadRoute_Stop(route);
RoadRoute_Destroy(route);

Internally this maps to the same per-player route engine used by the simple API. The current implementation intentionally supports one active navigation route per player, which matches the visible GPS use case and keeps object counts bounded.

SA-MP And open.mp Notes

The plugin exports the classic SA-MP plugin entry points and registers AMX natives. The core navigation engine is platform-independent C++17. The Pawn include performs vanilla CreatePlayerObject, SetPlayerObjectPos, SetPlayerObjectRot, and DestroyPlayerObject calls, which keeps route arrows per-player and client-mod-free.

For open.mp, load the plugin through the compatibility plugin layer or adapt src/pawn/PawnPlugin.cpp to the current open.mp component SDK while keeping the core library unchanged.

Optional Integrations

ColAndreas and MapAndreas are not required. A future surface adapter can use ColAndreas raycasts or MapAndreas elevation as optional arrow-placement helpers, but neither should become the pathfinding graph. The route graph must remain GTA vehicle path nodes.

Troubleshooting

  • Road graph not loaded: ensure scriptfiles/RoadRoute/roadgraph.dat exists.
  • Route request returns false: graph missing, queue full, or no nearby road node.
  • Arrows point sideways/upward: adjust RoadRoute_SetArrowRotationOffset.
  • Too many objects for mobile players: lower RoadRoute_SetMaxVisibleArrows or increase RoadRoute_SetArrowSpacing.
  • Recalculation feels too aggressive: increase reroute distance or cooldown.

Development

Run:

cmake -S . -B build -DROADROUTE_BUILD_PLUGIN=ON
cmake --build build --parallel
ctest --test-dir build --output-on-failure

The native tests cover graph loading, invalid graph detection, nearest-node lookup, bridge/tunnel-style vertical separation, short and unreachable routes, same-node routes, simplification, arrow sampling and orientation, turn detection, progress, arrival/clear behavior, disconnect handling, shutdown with pending jobs, and simultaneous async route calculation.

About

RoadRoute is a server-side road navigation plugin for SA-MP and open.mp servers.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages