Skip to content

ntre_redlight_ctg: navmesh rework - #139

Merged
sunzenshen merged 4 commits into
NeotokyoRebuild:masterfrom
sunzenshen:navmesh-redlight-release
Oct 14, 2026
Merged

sunzenshen merged 4 commits into
NeotokyoRebuild:masterfrom
sunzenshen:navmesh-redlight-release

Conversation

@sunzenshen

@sunzenshen sunzenshen commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

ntre_redlight_ctg: navmesh rework

One file, neo/maps/nav/ntre_redlight_ctg.nav.

Dependencies and status

Needs (worse than master, or broken, without it):

  • None. No open bot code PR has to be reviewed or merged before this mesh: the map has no nav ladders and no NAV_MESH_AVOID marks. Its 2 NAV_MESH_PRECISE areas use the movement rules of Bots follow their path on NAV_MESH_PRECISE areas neo#2168 (merged). The scooter is carved because it does not move when bots press it (commit 4), so the mesh does not wait on runtime prop avoidance either.

Helped by (measured better with it; the mesh does not wait on it):

Pending before review:

  • Nothing: ready for review.

At a glance

master this PR
nav areas 1077 872
file size 858,940 bytes 696,572 bytes (-19 %)
areas no spawn or objective can reach 103 (10 pockets) 2 (a van roof and a mech top bots can drop off, see commit 3)
areas the engine blocks at every round start 8 0
bot stuck events a match (commits 1-2, see below) 35.9 17.7

Four commits:

  1. Rework - the mesh itself.
  2. Id alignment with master - ids only, so an id-keyed diff against master reads sensibly.
  3. Review fixes (added 2026-10-02) - one-way links and climbs no class can make, found by an outside review.
  4. Areas inside props, and the scooter (added 2026-10-02) - found with the engine hull audit run for commit 3.

1. What changed

  • Areas the engine blocks at round start. CNavArea::UpdateBlocked finds a solid (mostly a prop) at the center of 8 of master's areas on every restart. The path cost then refuses them for the whole round, so bots plan routes they cannot take. They are cut around the obstacle or dropped.
  • Props and thin obstacles:
    • standing room is carved around props
    • five walk links a player hull cannot cross are cut: past a wall brush, a panel van and two cars (two of them in one direction only until commit 3)
  • Hand edits where test bots got stuck: a wall gap and a street lamp.
  • Unreachable floor is removed. Master has 103 areas no spawn or objective reaches, most of them on two rooftop regions. The roofs are linked out only by drops of 177-344 u, and the map geometry shows no way up. Raised tops no bot can climb are removed as well. One top at +59 u gets a one-way climb link instead (within a Recon's crouch jump); commit 3 takes it out again.
  • Re-analyzed in game after the edits, so the per-area visibility lists bots use for what they can see are current.

2. Id alignment

889 of the 895 areas carry the id of the master area they match. Kept ids that sit more than 64 u from their master location drop from 11 to 3, and an id-keyed diff shows 6 added / 188 removed areas. Geometry, connections, attributes and the analysis data are unchanged by this step.

3-4. Review fixes

An outside review of this PR found walk links that run one way only and raised tops that only climbs no class can make lead to. Commit 3 fixes those:

  • One-way links. Seven links between areas at the same height ran one way only: earlier passes that cut links a player hull cannot cross (section 1's five among them) judged each direction on its own. Each pair was swept in both directions with the engine's hull audit (a player hull moved along the link in the server, recording the props and brushes it hits). Four are blocked both ways and are now cut both ways: a wall near (-325, -888), a signpost near (-1725, 462), the panel van near (762, -40) and a car near (1341, -500). Three are clear on every lane and get their missing direction back, near (-88, 900) and (-75, -75).
  • The ledge at (-238, 1025, 188), 159 u above the street, is deleted (2 hiding spots): a flood fill over the map's geometry from the ledge, walking and jumping 40, 45 or 62 u, never reaches the mesh.
  • The police van roof near (-738, 200), 78 u up, loses its climb (over every class's limit) and stays as an area bots can drop off: a player reaches it by chained jumps by the open door.
  • The top of the destroyed mech near (-1512, 1188). Section 1's climb onto it is 66.8 u by the engine's measure (portal to portal; the 59 u was the area's center). A Recon plans that, and its crouch jump (62.8 u at most) does not reach it from the side the link starts on. A player gets on from the east by chained jumps. The climb is removed and the top stays as an area bots can drop off.

Beyond the review (commit 4). The engine hull audit of commit 3's mesh (a player hull also stood every 4 u on every area) lists areas no hull fits in and links blocked on every lane in both directions. Each was checked against whether bots stood in it or crossed it in the test matches below, and the stuck events near it.

  • 20 areas no hull fits in (every stand sample blocked by a static prop or a wall, or all but one), none of which a bot stood in during 48 matches, are deleted (14 hiding spots): inside the planter bushes near (962, -1450) and (962, -1690) with the strips on top of the bushes, under the destroyed mech, under the panel van and two cars, on the winged car near (-1512, -188) whose wing sits over its roof, beside a vending machine, between a mailbox and a fire box, a ledge by a street lamp, and slivers against walls and on a box top near (1210, -120).
  • The scooter near (-1824, 469). Two areas lie under it, and bots squeezing between it and the street lamp made 297 of the 777 stuck events in the first batch below, and every world-damage death in both batches (18; this was "Known, not changed" before). The scooter is a physics prop, and this series leaves props that move to the bots at run time; this one does not move when bots press it, like the two parked motorcycles carved on pissalley (ntre_pissalley_ctg: navmesh rework #137) and ballistrade (ntre_ballistrade_ctg: navmesh rework #138). The two areas are deleted; the floor round the scooter keeps the way past.
  • 309 <-> 1698 near (-338, -850), blocked by a wall on every lane both ways and crossed once in 48 matches, is cut.

Kept: two small areas the audit reads blocked that bots stand in (near (1219, -219) and (-1562, 88)), and six pairs blocked by props that move (recycle bins, a fence door, a paper lantern).

Existing areas keep their ids. Re-analyzed in game after each commit.

How it was tested

  • Reachability:

    • every spawn can reach every other spawn
    • every objective is reachable from a spawn
    • 2 areas no spawn or objective can reach, the van roof and the mech top of commit 3, which lead back into the mesh by drops
    • counting no walk link that climbs 68 u or more (the most a Recon bot plans to jump), the same 2 and no more: no area is reached only by a climb no class can make (master 107 areas)
    • every walk link between two areas at the same height runs both ways, and no climb leads into an area without a way back: no dead end and no trap for any class (master has one one-way link at the same height)
    • the engine hull audit of this mesh: of 1439 pairs of areas at the same height, none is blocked both ways by a wall or a static prop except the three pairs at the two areas bots stand in; 6 are blocked by props that move (commit 4)
    • no area is blocked at round start: offline, and in the engine in a test match (master: 8)
    • the mesh is one connected piece
  • Master against this mesh, same bot code, one batch. Byte-identical copies of the map, one carrying each .nav. CTG with the ghost pinned at each of the 6 ghost spawns, 3 matches each, 30 bots, default weapon damage. The count excludes stuck events in the spawn crush at round start and the weapon-scavenging behavior.

    • stuck events 35.9 -> 17.7 a match (paired x0.47, 95 % interval [0.35, 0.66], 15 of 18 pairs down)
    • FAIL_NO_PATH_EXISTS move failures 19.1 -> 9.9 a match
    • kills 48.9 -> 50.2 a match
  • Bots still see and shoot across the mesh. 96 % of kills are between bots in different nav areas and 90 % in non-adjacent ones, the same as on master (kill distance median 657 u against 642 u).

  • Commits 3-4 against commits 1-2 (2026-10-02), on upstream master + #2176 + #2186 with logging, the same design (each of the 6 ghost spawns pinned, 30 bots, default weapon damage, 8 matches at a time). Every match reached match end with 0 asserts and no crash (120 matches).

    • Commits 1-4 against commits 1-2, 24 pairs: stuck events 16.5 -> 8.5 a match (paired x0.66, 95 % interval [0.46, 0.99], 18 pairs down, 6 up)
    • Commit 3 against commits 1-2, 48 pairs over two batches: 15.6 -> 17.4 a match (x1.03 [0.73, 1.44], 23 down / 25 up); within 64 u of its changed links 77 -> 43 stuck events (the wall near (-325, -888): 33 -> 0)
    • Commit 4 against commit 3, 24 pairs: x0.63 [0.44, 0.88] (17 down, 6 up); at the scooter and the cut wall link 196 -> 24 stuck events (143 on commits 1-2 alone)
    • world-damage deaths 0.2 -> 0 a match; FAIL_NO_PATH_EXISTS 12.0 -> 14.2; kills 50.8 -> 49.4; ghost captures 0 -> 1 of 48 rounds; 96 % of kills between bots in different nav areas (97 % before)

How the pinned-ghost probes work

The offline checks show that the mesh links an area to a spawn. They do not show that a bot will walk there: a link can cross a gap the player hull cannot fit through, or lead along a route path planning never picks. To test one area in game, the area is made the objective and bots are sent to get it.

  • The ghost is placed on the area by a server cvar. A harness-only neo patch, used on test builds and not part of this PR or any neo PR, adds sv_neo_ghost_spawn_pos "x y z". It replaces the position the round's chosen ghost spawn would have used, so any point becomes the CTG objective. The ghost is dropped a little above the area and settles onto it as it would from a real spawn. The map file is not edited.
  • A pickup at the spot means bots can reach the area. Bots run their normal CTG errand to the ghost. A pickup is the clear signal; a bot coming within 160 u is a weaker one. The position of the first pickup is checked, because a ghost on a low raised top can be taken from the floor below, and a ghost on a narrow ledge can fall off it.
  • A miss only counts with a control. One run is a screen. A spot bots miss is re-run 6 times beside a control spot on the same map that bots are known to reach, in the same batch. Without the control, "not reachable" and "the probe did not work" look the same.
  • Weapon damage is turned down for these probes, so bots move instead of dying in fights on the way.
  • Master's mesh can run beside this one. The game loads a .nav by map name, so the harness copies the .bsp byte for byte under a second name (for example ntre_oilstainb_ctg) that loads its own .nav. The same pin then runs on both meshes in one batch. The copy only carries the second mesh; the ghost comes from the cvar.

On this map. Earlier screens on older revisions got pickups at the largest floor region removed from master's mesh (4/4) and at one area no bot had visited. A pocket at (-488, 1375, 308) was 0/6, closest 295 u, with its control picked up 3/3. The two rooftop regions were judged from the map geometry, not probed.

Fixed since: the street lamp deaths

Commits 1-2 listed a handful of world-damage deaths by the street lamp near (-1804, 458) on both meshes (2 on master, 3 here over 18 matches each) as a property of the map. They happen in the area under the scooter beside the lamp (all 18 in the 2026-10-02 batches); commit 4 removes it, and the batch had none.

One file. Against master: 1077 -> 895 areas, 858,940 -> 722,681
bytes; 103 areas no spawn or objective reaches (10 pockets) -> 0; 8
areas the engine blocks at every round start -> 0.

- Areas CNavArea::UpdateBlocked finds solid at their center at round
  start are cut around the obstacle or dropped.
- Standing room is carved around props, and 5 walk links a player hull
  cannot cross (a wall brush, a panel van, two cars) are cut.
- Hand edits where test bots got stuck: a wall gap and a street lamp.
- Unreachable floor is removed: two rooftop regions with no way up and
  raised tops no bot can climb; one top at +59 u gets a Recon climb.
- Re-analyzed in game after the edits.
Renumbers area ids only, so an id-keyed diff against master reads
sensibly: 889 of the 895 areas carry the id of the master area they
match, kept ids that moved more than 64 u from their master location
drop from 11 to 3, and the diff shows 6 added / 188 removed areas.
Geometry, connections, attributes and the analyzed visibility data are
unchanged (verified topology-identical).

@sunzenshen sunzenshen left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

The main savings come from deleting the roof and scenery navmeshes where no one should be able to reach:

Image

@sunzenshen
sunzenshen marked this pull request as ready for review September 28, 2026 07:37
An outside review of this mesh found walk links that run one way
only and raised tops reached only by climbs no class makes.

- One-way links, each swept with a player hull in both directions by
  the engine's hull audit: cut both ways the four pairs blocked both
  ways (a wall, a signpost, a panel van, a car); restore the missing
  direction of the three pairs clear both ways.
- Delete the ledge at (-238, 1025, 188), 159 u over the street, which
  a player cannot get onto.
- Remove the 78 u climb onto the police van roof near (-738, 200) and
  the 67 u climb onto the destroyed mech near (-1512, 1188): no class
  plans the first, and the second is over a Recon's jump. A player
  reaches both by chained jumps, so they stay as areas bots can drop
  off.

Existing areas keep their ids. Re-analyzed in game (same graph,
fresh visibility).
The engine hull audit run for the review fixes also lists areas no
player hull fits in. Where the blocker is static and no bot was seen
in the area in 48 test matches, delete it: inside the planter bushes
near (962, -1450) and (962, -1690) with the strips on the bush tops,
under the destroyed mech, under a panel van and two cars, on the
winged car whose wing sits over its roof, beside a vending machine,
between a mailbox and a fire box, a ledge by a street lamp, and
slivers against walls and on a box top (20 areas, 14 hiding spots).

Also delete the two areas under the scooter near (-1824, 469). Bots
squeezing between it and the street lamp made 38 % of the stuck
events in the test matches. The scooter can be pushed but stays put
in play; the floor round it keeps the way past.

Cut 309 <-> 1698, blocked by a wall on every lane both ways. Existing
areas keep their ids. Re-analyzed in game (same graph, fresh
visibility).

@sunmachine sunmachine left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got really worried looking at this one in the audit until I actually finished reading the PR detail in full. Makes complete sense what happened!

DELETE SUPER THIN SECTIONS. HYPOTHETICAL PATH FOR ATTACKER INCREASES BY 40%. Literally not a real problem, looks great.

@sunmachine
sunmachine requested a review from a team October 14, 2026 03:13

@sunmachine sunmachine left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passes analysis and eyetest in Wireframe.

@sunzenshen
sunzenshen merged commit 48ebcc7 into NeotokyoRebuild:master Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants