Second Chance GazetteDevlogToken Use

Home Lights

unreal-enginecpppluginainpcnavmeshlightingpieclaude-codegta-6

Keep twenty-five first

It was a Monday, so James was at work, and most of what he put into the day arrived as short typed replies. Install and build. Keep 25 first. Mark the doors. Do all of it. yes gate links. The rest of it was done by agents.

He had handed it to the Projects feature in Claude Code, which is in beta. A planner takes the job and hands pieces of it out to several agents running Opus 5.5, each in its own session, each reporting back up the chain. The job was a hundred people. Second Chance already has thirty named townsfolk with their own week - Baz at the bar by one most days, Shirley opening the post office at seven - but the plan is a whole town's worth: a hundred residents in households, with jobs, school runs, cars and bus fares, all of it running whether the player is watching or not.

The part that does the thinking had been written as a C++ plugin, which is a first for a project that has been Blueprint-only since the day it started. It came down to his machine as a zip of 181 files. The first agent of the morning - Claude Code Opus 5.5, like the rest of them - was given one instruction: make it compile, and do not touch anything else in the game. Then James was asked how much of it to switch on. He chose to keep the existing thirty-odd citizens visible and let the hundred run in the background, unseen, until the plumbing between the two was proven. That was the sensible answer. It also meant that by the end of the day there would be a hundred people going about their day in Radworth that nobody could see.

GTA 6 has been built by a studio the size of a small town. This town had a Monday and some agents.

Nineteen errors and a roundabout

The first build stopped in the header tool, which refused a type alias inside a property it was meant to expose to the editor. The second got further and stopped on nineteen errors across ten files. None of them were mysterious. Every one was the engine having moved since the code was written: a getter that Epic have made private in 5.8, a weak pointer that can no longer be tested like a true-or-false value, a navmesh function that now takes one argument and returns the answer instead of filling one in, a maths constant that does not exist under the name the code used. The agent fixed them in the plugin source, logged every change in a table, and had a clean build by quarter past eight. It kept a patch of the differences so the copy the other agents work from could be brought into line.

Then it pressed play, and the editor fell over 23 seconds later.

The assert was the kind that tells you exactly what happened and not why it matters. The road system builds itself from the level's road splines when the world starts. Where a road is a loop, it closes the loop by adding the first point to the end of the list - and it did that by handing the list a reference to its own first element while the list was growing. Unreal checks for that in the editor and stops dead. Radworth has roundabouts. So every play session crashed before a single citizen moved.

The fix was one line, twice: copy the point first, then add the copy. The same mistake was sitting in the bus route code, waiting for the first looping route. The rebuild was refused by the machine's permission check, and it sat there until James typed rebuild ok from wherever he was. By ten past one the game ran.

Six of six citizens spawned within a second of the intro being skipped. That mattered more than it sounds. In August a playtest had found none at all. It did not reproduce. They spawn when the player is within 70 metres of their home, which the August test had not been.

Ian and Paul then walked out to their front doors and stood there. Both doors read locked. Only five houses in the whole estate had ever been marked as a citizen's home. The agent proved it with a trick it could only do in a running game - it moved Paul four steps past his own door, and he set off for the market at a steady walk - and then asked. James said mark the doors. Twenty-three were marked. Two more were held back, because there is a furnished burglary set behind each of them, and that is a different conversation.

The sun in somebody's front room

The brief asked for screenshots of the street. The screenshots came back black.

An almost completely black frame of a street at two in the afternoon. Only the faint outline of a window and a small bright patch of sky between rooftops can be made out.
The street at two in the afternoon in PIE. The sun is still in the level and still set to cast light. Something had switched it off.

The agent's first theory was shaders still compiling after a restart. Its second was exposure, and it switched the camera's eye adaptation off from the console to get a fixed brightness. That made the shots white instead of black. It also did something that took four and a half hours to come to light: a show flag set from the console is not a setting on a camera, it is a global override, and it outlives the play session. James's own editor viewport stayed blown out to white from quarter past one until quarter to six, when it was found and put back. The value that means "default" is 2. Setting it to 1 does not put it back; it forces it on.

The sun was found mid-afternoon, and it was the best fault of the day.

One of the plugin's systems gives every house a life of its own after dark: lights on in the front room, off again at bedtime, off in the day when nobody needs them. To know which lights belong to which house, it draws a box round each one and adopts every movable light inside it, filtered by name. Unreal puts the sun at the very centre of the world unless you move it. In Radworth, the very centre of the world is beside 13 and 15 Brickyard Close. So the house adopted the sun, and at about 41 seconds into each session, finding it was daytime, it turned the big light off.

A sunny junction between red-brick terraces under a blue sky with white clouds. A man in a tan jacket walks into the bottom right of the frame; a taxi waits down the road and wheelie bins stand on the pavement.
The same estate after the fix, at twenty past two. Charlie is walking to the shops, the sun has stayed on for seven minutes, and nobody's front room is in charge of it.

There were two fixes and James said do all of it, so both went in: the sun's own kind of light is now on the house system's ignore list in the settings, and the code that adopts lights now skips a directional light outright, whatever it is called. After the rebuild the sun held for seven minutes and counting.

Over the garden wall

At twenty to four James sent a message that had nothing to do with the sun. Charlie was going out of his front door and stepping over the low garden wall instead of using the gap in it in front of the door. Nobody does that. He wanted them to use the gate.

The cause was a number. The navmesh had been set to let walkers climb up to 60 centimetres, weeks earlier, to get people up a kerb onto a raised road platform. The terrace garden walls are 60 and 61 centimetres. So the pathfinder was happy to route straight over them, and the citizens, who can climb rather less than that, wedged.

The agent's written investigation suggested 55 would bring the effective climb down to 50. It tested 55. Every one of 1,638 probe results came back identical to 60. The engine rounds the step up to a whole number of 10-centimetre cells, and 55 rounds up to six. So it went to 50, added seven nav links through the garden gates where the lower step left a doorstep stranded, ran its checks that nothing else in the town had lost a route, and saved the navmesh. Routes that crossed more than 45 centimetres of something went from 186 to 14.

The fresh load found two new ones. Pete and Baz both live in mirrored houses, where the door swings the other way, and the path out now ran tight along the hinge line and straight into their own opened door. They stood just inside their hallways for eighty seconds with a complete path in front of them. That is where it was left.

Nobody works here

At half five James asked for something different: watch a whole day, seven till ten, and see where people go and what they do when they get there.

At twenty-four minutes past twelve Baz walked to Boss Man's corner shop and stopped against the side wall, by the window. James said he was going to work. The schedule said Shopping. It also said nobody works at Boss Man's at all. The marker for the shop had been put on the wall line, with the shelves on the other side of the bricks, and when Baz reached it there was nothing more for him to do. James's answer was to put him behind the counter for now.

The chippy at dinner was worse, or funnier.

Four people standing close together against a plain orange-brown side wall of a building, on a strip of grass. A red-brick house with a blue front door is in the foreground on the right.
Lunch at the chippy. Four regulars have walked across town for their snap and are stood against the blank side wall of the building next door, five and a half metres from the door.

Nine citizens go to the chippy at one on weekdays. The four nearest the player turned up, walked to the marker, and huddled against a blank wall round the corner from the door. The counter is inside, the three chairs are inside, and the navmesh reaches all of them. What is missing is anything telling a citizen what to do on arrival. Of all the places in the town, only the pub has that, and it shows.

Inside a pub with loud red and gold patterned wallpaper and carpet. Two men stand at the bar, a woman in a purple dress sits on a bar stool, the barmaid is behind the pumps, and a green sign over the bar reads The Slag & Slurry.
Nine at night in the Slag & Slurry. Baz and Pete are inside by the bar, placed there by the pub's own drinks routine. It is the one venue where arriving means going in.

James also said the bricks on that side of Boss Man's were flashing. The agent went looking for two faces fighting over the same spot and did not find one: no brick sits on brick anywhere on the shop. What it did find is the window glass, 5 millimetres behind the brick face, poking through in thin strips round every frame. A two-frame comparison from a fixed camera showed no flicker on the plain brick at all. It wrote down both explanations and sent the question back - is it at the window edges or in the middle of the wall? - which is the right question and not an answer.

From six minutes past six James had the camera himself, flying round the town in the free view. The agent's screenshots from then on were of whatever James was looking at. It said so in the report.

A phone you have to buy

Halfway through the evening James made a call that changes the opening of the game. The player no longer starts with a phone. He comes out on the release programme with nothing, and the phone is something he buys.

Every GTA since the fourth one has put a phone in your pocket in the first hour, because the story needs to be able to ring you. There is no story here that needs to ring you. So there are two places to get one. Boss Man sells a burner from behind his counter for £50. He takes £40 if you push, and refuses £30. Baz will sell you the same phone in the pub, at the same prices, in his own words. Once you have one, neither of them sells again.

A first-person view across a corner shop counter with a till. Behind it stands an older bald man with a white beard in a blue and white argyle jumper, in front of a white wall of cubby shelves; a drinks fridge is on the right.
Boss Man behind his counter at the corner shop, where the player can now buy a phone for fifty quid, or forty if he haggles.

The lines on the Boss Man side are James's own, word for word, and the build script refuses to run if they drift. It was tested with Baz in the running game: press U to talk, ask for less, take forty. Cash went from 100 to 60 and the phone came on. Everything that uses a phone - the texts, the delivery app, the phone on the e-bike handlebars - now checks first whether you have one.

The children

The town's hundred include children. In the simulation there are households with children in them, the under-elevens walked to school by an adult, and Radworth Primary running from quarter to nine to quarter past three. They live through the day like everybody else. Until the second batch builds the bridge between the simulation and the screen, they do it out of sight.

Three child characters of increasing height and an adult woman stand side by side in a plain blue editor space inside a yellow wireframe box. The children wear light blue and grey tops with grey joggers or cargo trousers and have no hair yet.
Three of the town's children dressed for the street next to an adult resident, at 99, 125 and 149 centimetres. Hair, eyebrows and lashes are next.

Three child characters are built and dressed - a hoodie and joggers, a tee and cargo trousers - in free Fab clothing chosen against what a British high street is actually selling to kids this autumn. The next job is making them walk like children. Adult clips on a 99-centimetre body carry an adult stride, and that is exactly the sort of thing a British player would clock in half a second.

Three of the agents on this thread ran out of room before they finished, and each handed over to the next with a written note: what was done, what was proven, the exact next action. Just before one in the morning the last of them sent Ian to work for the first time. At twenty-four minutes past five, game time, he put a hi-vis on and walked to the depot.

The fault

A C++ plugin fails to compile in Unreal Engine 5.8: UHT "Unable to find" and 19 errors

A third-party gameplay plugin, installed into a Blueprint-only project. Two builds before it linked.

  • UHT: Unable to find ... 'FTownPlaceId' - a using alias used as a UPROPERTY type. The header tool cannot resolve aliases.
  • C2248 USkeletalMeshComponentBudgeted::GetAnimationBudgetHandle is private in 5.8.
  • C2280 TWeakObjectPtr has no usable operator bool in 5.8.
  • C2660 ARecastNavMesh::GetPolyAreaID takes a different number of arguments.
  • C2039 TNumericLimits<double>::Quiet_NaN does not exist.
  • C2397 brace-init narrowing double to float (large world coordinates make Dist2D and Rotation().Yaw doubles).
  • C2365/C2440 an enumerator and a struct member with the same name, Node.
  • C2275 UNavigationSystemV1::GetCurrent<...> used as an expression.
  • C4458 a local called Owner hiding AActor::Owner, as warning-as-error.

The fix

Port the plugin to the 5.8 API, one error at a time, and keep a patch

  • Alias: use int32 in the UPROPERTY; keep the alias for C++ only.
  • Budget handle: drop the getter check. FAnimationBudgetAllocator::RegisterComponent / UnregisterComponent already ignore a repeat.
  • Weak pointer: .IsValid().
  • GetPolyAreaID: uint32 AreaId = GetPolyAreaID(Ref). It returns RECAST_DEFAULT_AREA on failure.
  • NaN: std::numeric_limits<double>::quiet_NaN().
  • Narrowing: static_cast<float>.
  • UNavigationSystemV1::GetCurrent<...> as an expression: FNavigationSystem::GetCurrent<UNavigationSystemV1>(World).

The guard: log every change in a table and regenerate a unified diff against the pristine zip, so the copy the other agents work from can be patched. Keep LF line endings; a Python write_text on Windows writes CRLF and turns every file into a diff.

The fault

PIE crash: "Attempting to use a container element which already comes from the container being modified"

Full assert: Addr < GetData() || Addr >= (GetData() + ArrayMax), SizeofElement 24. Fired 23 s into every PIE on a map with a looped road spline. Points.Add(Points[0]) passes a reference into the array being grown; if Add reallocates, the reference dangles, and the editor build asserts on it.

The fix

Copy the element before adding it

const FVector First = R.Points[0]; R.Points.Add(First); The same pattern was in the bus-route code (Stops.Add(Stops[0])). The guard: a regex sweep for X.Add/Push/Insert/AddUnique/Emplace(X[...]) across the plugin; it found those two and no more.

The fault

DirectionalLight hidden about 41 seconds into PIE, streets render black

The sun was in the level, movable, and visible in the editor. In PIE it went off at about 41 s. A home-lights system adopted every movable ALight inside each house's search box, matched by name, and switched house lights off by day. The sun sat at world origin, which in this level is beside two terraced houses.

Wrong turn on the way: ShowFlag.EyeAdaptation 0 to get a fixed exposure for the screenshots. Show flags set from the console are global overrides that outlive PIE. The editor viewport stayed blown out white for four and a half hours.

The fix

Skip directional lights in the light binder, and restore show flags with 2

  • Settings: the house system's ignore-by-name list gained directionallight (alongside street, skylight and sun), written to the project's ini so it survives a restart.
  • Code: the bind step returns early for a UDirectionalLightComponent, whatever it is named.
  • Show flags: 0 forces off, 1 forces on, 2 is default. Restore with 2, never 1.

Verified: after a fresh launch the ignore list reads four entries and the PIE sun stayed on for seven minutes.

The fault

Navmesh routes step over 60 cm garden walls, and AgentMaxStepHeight 55 changes nothing

With AgentMaxStepHeight 60 the pathfinder routed citizens over 60-61 cm terrace garden walls that their capsule could not climb; they wedged. 55 produced 1,638 probe results identical to 60. Recast computes walkableClimb = CeilToInt(AgentMaxStepHeight / CellHeight), and with 10 cm cells 55 rounds up to 6 cells, which is 60.

The fix

AgentMaxStepHeight 50 on all three resolution settings (5 cells), plus re-seated nav links through the garden gates where the lower step stranded a doorstep. Before the save: 453/453 terrace doors routed to the street, 81/81 citizen routes, the loo and house-6 route probes unchanged. Routes crossing more than 45 cm of geometry fell from 186 to 14. Backups of the navmesh were taken first; it is a level actor and there is no undo.

The kit

ToolWhy this one
Claude Code Projects (beta)a planner splitting one large job across several Opus 5.5 agents, with James's choices relayed down as short replies
Unreal Build Tool, from the command linethe project has no Visual Studio solution; Build.bat UnrealEditor Win64 Development -Project=... builds the plugin on its own
Monolith MCPstarting and stopping PIE, running the editor Python probes, firing HighResShot
A written handover per sessionthree agents hit the context limit on the same thread; each picked up from the note

Plain English

TermWhat it means
pluginA self-contained bundle of extra code and settings that drops into an Unreal project and adds features to it, here a whole town simulation written in C++.
Unreal Header ToolThe program that reads a C++ plugin's headers before the compiler does, to work out which parts the editor and Blueprints are allowed to see; if it rejects something, nothing gets compiled at all.
PIE"Play In Editor" - pressing play inside the editor to test the game without building it first. Fast, and it behaves subtly differently from the real build.
navmeshAn invisible map of walkable ground that characters use to work out routes. No navmesh, no walking.
nav linkA hand-placed bridge that tells the pathfinder two patches of navmesh are joined, for gaps it cannot see across on its own - a narrow doorway, a jump, a ladder. Both ends snap to the nearest walkable floor, which is right until the nearest floor is the one below.
step heightThe tallest ledge a walker is allowed to step straight up; the navmesh and the character each have their own, and when they disagree, people get routed over walls they cannot climb.
directional lightThe light that stands in for the sun: it shines the same way everywhere in the level, from infinitely far away.
show flagA console switch that turns one kind of rendering on or off, such as fog or auto-exposure; set from the console it overrides every view in the editor until it is put back.
exposureHow bright the virtual camera treats the scene, like a real camera's aperture and shutter. A higher EV100 number darkens the picture. This project pins it per building so a room does not slowly brighten while you stand in it.
citizen directorThe part of the game that brings townspeople into being near the player and puts them away again when the player leaves, so a whole town can run without every person existing at once.
MetaHumanEpic's system for realistic human characters - face, body, hair and clothing, ready to animate. Every named person in the game is one.
z-fightingWhat happens when two surfaces sit in exactly the same place, both facing the same way: the renderer has no way to decide which is in front, so it picks differently from frame to frame and the wall flickers. On this project it is a photosensitivity requirement rather than a polish one, so the geometry is measured for it rather than eyeballed.
context windowHow much of a conversation an AI model can hold in mind at once; when a session fills it, the work has to be written down and handed to a fresh one.

Things we still need to fix tomorrow:

  • Pete and Baz stand behind their own open front doors in the mirrored houses. The door links sit on the hinge line; moving them off it is next.
  • Fourteen routes still cross a garden wall, and three citizens can only get partway to the health centre from the back-garden pockets they start in.
  • An arrival routine for every venue: in through the door, queue at the counter, buy, sit or take away. The chippy first.
  • Baz behind Boss Man's counter, the club's bouncer and barmaid, and the Palace staff whose bodies sit 57-60 cm above their seats.
  • Boss Man's side wall: window edges or plain brick. James to say which.
  • The bridge that puts the hundred on screen, children and school run included. Child walk and idle clips, and hair, are needed first.

← All devlog entries

Watch it get built. All of this goes up on YouTube as it happens - broken animations, buildings hovering a foot off the ground, the lot.

Subscribe on YouTube