Skip to content

Order Generation and Routing

noteMASTER11 edited this page Sep 4, 2026 · 6 revisions

Order Generation and Routing

Dispatcher pool

Going online creates a target pool of 10–12 offers. The plan is shuffled and normally includes:

  • 2–4 rush requests;
  • 2–3 multi-stop requests when the map has enough semantic candidates;
  • 5–7 cargo deliveries;
  • regular requests for the remaining slots.

The final counts are clamped to the 10–12 offer target. Offers are inserted one at a time. The initial delay is 1.5 seconds and completed offers are separated by a random 1.2–2.2 seconds. The shuffled plan keeps a normal order first so long-distance delivery searches cannot block the first visible card.

Dispatcher comparison and sorting

Every card includes estimated fare efficiency per minute and per kilometre/mile. The visible pool can be sorted without regenerating offers by:

  • highest fare;
  • nearest pickup;
  • shortest estimated duration;
  • highest income per kilometre/mile.

The selector is implemented as a custom button menu because BeamNG's embedded CEF can misroute native HTML select popup clicks when the complete UI App is scaled.

Incremental graph work

Offer creation can involve map-object scans, road projection, random paths, and route-distance validation. These operations run inside a coroutine job managed by offerGenerator.lua.

  • A suspended job is resumed at most once per configured work interval (0.02 s).
  • Semantic object scans yield every six objects.
  • Semantic candidate and random road attempts yield between attempts.
  • Generation pauses with dtSim when the game is paused.

This design reduces sustained per-frame load. It deliberately trades dispatcher fill time for smoother gameplay.

Candidate sources

The semantic cache is built once per level from:

  1. BeamNGTrigger objects whose type is busstop;
  2. parking spots from current-level sites files, excluding mission, quickrace, and scenario data;
  3. BeamNGPointOfInterest objects.

Candidates within the same 8 × 8 metre cell are deduplicated. Banned site parking spots are ignored.

Road-edge projection

A semantic anchor is mapped to its closest usable road segment. The projection then:

  1. rejects anchors farther than 120 m from the road centre;
  2. applies controlled longitudinal jitter along the segment;
  3. keeps an endpoint margin;
  4. chooses the side nearest to the semantic anchor;
  5. offsets toward the road edge using interpolated node radius;
  6. requests legal traffic direction through trafficUtils.finalizeSpawnPoint.

The lateral offset is clamped to 1.5–14 m. This is intended to place a taxi at the outer road edge rather than an inner lane.

Road fallback

If no semantic point passes route validation, the generator asks BeamNG's graph path system for a random route from the closest road. A desired distance is selected with a short-distance bias and the result is projected to the road edge.

Dispatcher search immediately creates a map-specific cache below BeamNG's active user folder:

settings/TaxiDriver/route_cache/routes_[map-name]_[map-id].json

Every successfully generated offer is appended when it becomes visible in the dispatcher UI. Each routes record contains its pickup, optional stops, destination, per-leg distances, route type, and displayed fare metadata. If live offer generation later returns an error, TaxiDriver selects a compatible cached record, refreshes the pickup distance when routing remains available, assigns a new offer ID, and publishes it through the normal UI path.

BeamNG virtual settings paths follow a user folder moved through the Launcher; the mod does not assume that it remains under %LOCALAPPDATA%. The JSON file contains complete UI-published offers only. A separate in-memory sampler builds validated road-edge points from usable public roads. Bus-stop, Sites, and POI enumeration failures are isolated, so a broken community module can remove semantic candidates without disabling cached-offer restoration or ordinary road-graph order generation.

Usable public road links require:

  • drivability of at least 0.7;
  • node radius of at least 2.4 m;
  • a non-private link.

Semantic parking and related anchors may allow private links with a lower minimum radius of 1.8 m.

Distance rules

Segment Minimum Maximum
Pickup from current vehicle 400 m 3,500 m
Regular passenger ride 1,000 m 25,000 m
Multi-stop segment normally 3,000 m 7,000 m
Cargo delivery 2,000 m 25,000 m

Version 3.1.0 Beta adds Unlimited route length. When enabled, routePlanner.isDistanceAllowed() receives no maximum for passenger or cargo candidates, while the 1 km/2 km minimums, drivability, endpoint, diversity and route-validity checks stay active. Very long graph searches can take longer to yield a visible offer; generation remains incremental rather than blocking a simulation frame.

After repeated multi-stop failures, the minimum segment length can relax in 500 m steps, but never below 1,500 m.

All distances are route distances calculated by BeamNG's Route planner, not straight-line distances.

Multi-stop protection

A multi-stop ride requires at least 20 semantic stop candidates. If that requirement fails, or a multi-stop request cannot be generated, the dispatcher falls back to regular work for the rest of the pool.

Multi-stop rides are never marked as rush rides.

Repetition and route-pair control

The last 48 pickup, stop, and destination positions are remembered. A newly generated point is considered recent when it is within 150 m of one of them.

The generator prefers unused positions but retains a recent valid candidate as a fallback. This prevents variety rules from emptying the dispatcher on small maps.

routeDiversity.lua also converts endpoints into 400 m spatial cells and stores the last 20 route pairs. A-to-B and B-to-A are treated as the same pair. For at least the first 65% of a pool, strict validation requires endpoint separation and rejects repeated cell pairs when alternatives exist.

Each order may test three spatial variants. The first valid route is retained as a fallback, so a sparse road graph continues producing work even if the requested diversity cannot be achieved.

Navigation ownership

The GE extension sends the selected target to core_groundMarkers with cutOffDrivability = 0.7. It temporarily controls road guidance visibility and restores the previous settings after the route ends.

The native minimap is shown only during toPickup, toStop, toDestination, and toFuelStation. The UI reports a normalized rectangle and occlusion regions so the stock minimap renders inside either the full phone or the separate minimized dashboard without covering ETA, speed-limit, or notification overlays.

Dynamic zoom wraps the stock player-drawing path while TaxiDriver owns the minimap, then restores the original implementation when the map is hidden.

Covered-stop filtering in 4.0.3

Both live generation and cached-offer restoration reject stops beneath static cover. The filter probes upward for 60 metres from one metre above the candidate position; it checks road-center/curb candidates and restored pickup, destination and intermediate stops. This prevents tunnel drop-offs while still allowing routes through tunnels. Bridges and awnings can also disqualify a stop; map geometry affects the result.

Clone this wiki locally