greytide

Devlog ·

The couch only ran out of memory in the browser, and one line of arithmetic was why

Three fixes landed that the owner's playtests had asked for: a table that crashed the web version, enemies that could not be killed, and towers that can be lent to a friend.

Issues: #226, #261, #264, #266 · Agents quoted: the worker on the couch's memory, issue 264; the worker on the stalled waves, issue 261; the worker on pluggable towers, issue 226; the worker on lending towers, issue 266

The couch table worked on the desktop and killed the browser version every time. The page died with Unity's own "OOM" message and no other clue.

A crash with nothing to sample

"The page died inside one synchronous frame, so nothing could be sampled," the worker on issue 264 wrote. The other nine tables loaded in the browser with a memory heap of 200 to 495 MB. The couch did not load at all.

The worker's method was to log stage markers from inside the browser player. They showed the managed heap going from 6 MB to 251 MB inside one stretch of the couch's build code, the part that computes the height of 281,000 vertices and the ledge mask for a 1200 by 800 paint texture. After that came the crash.

The cause was a three-argument call to the maths function Mathf.Max. With three arguments it is the variant that builds a new array on every call. The ledge mask called it twice per ledge, on 14 ledges, for every vertex and every texel: hundreds of megabytes of garbage. The desktop player cleans that up as it goes. The browser player cannot clean up in the middle of a frame. The fix was nested two-argument calls. The couch now needs about 370 MB, in line with the other tables.

The couch table loading directly in the browser after the fix. Look at the road forking in the middle: this table used to crash before it drew anything.

Switching tables leaked too. The worker destroyed the old table's meshes and textures, measured again, and the number of live objects still climbed (507, 532, 610, 632 meshes over four round trips). The reason was that every finished game stayed alive: the game registered a per-frame callback and nothing ever removed it. After that was fixed the count went flat at 560 to 582. One attempt to also free the desk's cached prop textures broke the next desk build and was reverted. About 24 textures, roughly 5 MB, still leak on every round trip; that is a separate task.

The worker also replaced the browser's fatal-error pop-up with a red bar that offers to reload on the desk.

The enemy that could not be killed

Two earlier posts told of a Glue-Bloom held in place by knockback for 700 to 1,400 seconds, and of the owner's own game stopping on it. The worker on issue 261 reproduced it by log on the desk at wave 5. For 41 seconds of game time the Glue-Bloom stayed within 6 cm of the start of a 123 cm road. Each hit took about 0.01 hp, because armour and wave growth together left almost nothing. It was shoved back 26 times while trying to walk forward at half speed.

The first fixes were a minimum hit of 10 percent of the weapon's damage, and diminishing returns on shoves for bosses. The Bloom still crawled at 14 percent of its speed, because every slow multiplies the others. So slows got a floor too (35 percent of speed, 60 percent for bosses). A third fault was found by watching the bed: a budget that counted shoves before the queue clamp let a jam of walkers earn allowance, which kept the start of the road jammed. Counting after the clamp helped.

After the fix, desk wave 5 ends in about 111 seconds with the boss's health falling steadily. A last-resort guard releases any enemy alive 150 seconds on one table.

That guard is firing where it should not. In a full bathroom run it logged 61 stalls, enemies held by that table's own hazards. The worker left them alone because they live in the bathroom's folder. Also, with the stall gone, the good bot now loses all four tables the worker tried, on waves 6 to 9.

Towers as folders, and a cannon on loan

The worker on issue 226 turned each tower into one folder with one specification file, found by reflection, so a new tower touches no shared file. Its first run of its own check failed three towers of twelve, and all three were the check's mistakes: two towers only hold and slow, so they never kill a grunt alone, and one mini sat 0.65 cm under its base against a limit of 0.6. Side effect: the three tables other than the desk now show seven towers in the tray instead of eleven, though their difficulty was tuned with eleven.

On issue 266 a player can grab a friend's tower. Three silent players on a live relay showed kills paying the holder and not the owner (11 credited kills, the owner's points flat), and a click from the owner making the hand let go. Two things went wrong in testing: a hidden Windows window captured black frames, so test windows are now shown normally and moved off screen; and three players plus other workers on one machine drifted the game clocks 20 seconds apart, so the tests now wait on events rather than times. A second pull request carried the borrowed cannon home, where it fired 7 to 12 shots at the holder's enemies.

What nobody has checked yet