Skip to content

Repair every bundle that renders pink on macOS: overlays, deployment zones, silhouettes, ID tokens and the miniatures - #16

Open
ironsquadronfr-hub wants to merge 17 commits into
halestom15:Mandos+MacFixfrom
ironsquadronfr-hub:isq-metal-rebuilds
Open

Repair every bundle that renders pink on macOS: overlays, deployment zones, silhouettes, ID tokens and the miniatures#16
ironsquadronfr-hub wants to merge 17 commits into
halestom15:Mandos+MacFixfrom
ironsquadronfr-hub:isq-metal-rebuilds

Conversation

@ironsquadronfr-hub

@ironsquadronfr-hub ironsquadronfr-hub commented Aug 16, 2026

Copy link
Copy Markdown

This is the pink your Mac tester is seeing. It is not a link issue and it is not his cache.

What it is

Range, Cohesion and Movement all spawn Custom AssetBundles built around the BucketheadBits/Projector shaders, and those were compiled without a Metal variant. I pulled the published bundles down from the mod's own URLs just now and read the platform list out of each shader:

bundle, downloaded live from the mod's URL platforms in the shader Metal?
halfcohesion_27mm [4, 15] no
projector_bomb_cart [4, 15] no
projector_tokenrangetwo [4, 15] no
movement_27mm_speed1 [4, 15] no
projector_27mm [4, 15] no
projector_27mm_isq_v6 (ours, as a control) [4, 14] yes

4 is Direct3D 9, 15 is Direct3D 11, 14 is Metal. The file downloads fine — it is 8 to 13 KB, it opens, the shader is in there. It was simply never compiled for macOS. Your Windows box reads variant 15, which exists; a Mac has nothing to read, so Unity falls back to its error material and paints the projector pink. A Windows player with a completely cold cache would see exactly what you see.

What confirms it

His own report is the proof, once you look at what works. Range renders for him, cohesion does not. Our overlay module covers all eight base sizes for range, but only 100/120/150mm and the oblongs for cohesion — 27, 50 and 70mm fall back to the mod's bundles. Everything pink is a mod bundle, everything that renders is one of ours. No exceptions, including the max movement templates and the bomb cart and graffiti buttons he named.

What this does

One defect, three routes to it. 145 bundles now load a repaired, bi-platform rebuild, built from the mod's own prefabs and shader source and recompiled for both graphics APIs. Every replacement is byte for byte what has been validated in game. Windows reads the same variant it always did — that is the half of the test I cannot run, so it is the half worth your attention, and the section on what else changed is where to start.

40 in your own data files — Movement, Range and Cohesion, swapped where they live: MovementLinks, RangeRulerLinks, CohesionLinks. (An earlier version of this PR routed these through a lookup table in our module; plain URL swaps in your files are easier to read, revertible entry by entry, and behave identically for every player.)

23 more written straight into your files — never resolved through the link tables. Swapped in place, 31 occurrences across 16 files:

what shader where
8 deployment zones + the 3x3 masking projector Projector/Deployment SETUP_CONTROLLER, GAME_CONTROLLER
3 silhouettes (the SIL button) Silhouette Unit_Leader, POI_Token, BombCarts
10 unit ID tokens Tokens/Lambert Channel Mask 11 object JSON
the shelf hologram Logo/Fast Hologram Shelf

The deployment zones matter most here: they paint at the start of every single game, so on a Mac that is pink before anyone has touched anything.

82 miniatures — the unit models, from contrib/cards/**/minis[].bundle and official.json. 265 URL occurrences across 11 files. 66 carry BucketheadBits/Units/Color Replacer, the rest add Glow Geometry and Rim Glow Geometry Shield, and three carry Particles/Standard Unlit — a stock Unity name, but baked into the bundle without a Metal variant like the others, so it breaks identically. None of them is a Custom Model with a stock TTS material; those carry no shader at all and are untouched.

Nothing was assumed. Every bundle here was downloaded from its published URL and had its shaders read out of the typetree. The 82 miniatures came back 82 out of 82 on platforms [4, 15] — Direct3D 9 and 11, no Metal. Same for the other 63. As a control, one of ours came back [4, 14].

After this, no bundle without a Metal variant is referenced anywhere in the repository.

Where the files live, and the one thing to weigh

No binary lands in your repo — the whole PR is a text diff. The 63 small bundles sit under mod/data/isq-overlay-assets and the 82 miniatures (61 MB) under mod/data/isq-metal-rebuilds, both on the fork's isq-qol branch, same place ISQ_ASSETS already resolves to.

That is the honest cost of this PR: the mod pulls these bundles from a personal fork. I think it beats every Mac player seeing pink figures, but it is your call and I will not pretend it is free. Which is what the next section is about.

What else changed, besides the platform

I diffed every one of the 145 against the copy currently published — material properties and projector geometry, not just the shader — so this is measured rather than asserted.

116 of 145 are identical apart from the Metal variant. The other 29 fall into three groups, and only the first is a deliberate change:

18 movement bundles carry a fix for two upstream bugs in the Maximum Move footprint. Both are visible on Windows too, and they are the reason these came from a later build than the rest:

  • _BaseSize was left at 70 on every base above large — the ring radius _ProjectorRadius had been tuned per size, the base footprint had not, so they were all derived from the 70mm. Now set per base: 100, 120, 150 as appropriate. 15 bundles.
  • BB_MovementProjector places the footprint at halfBase / (_ProjectorRadius + halfBase) of the projector's extent, while that extent is _ProjectorRadius (checked: m_OrthographicSize == _ProjectorRadius on all 24). The drawn radius was therefore always smaller than the base and changed with speed, since R changes. A base footprint that grows when you move faster — reproduced in game. The three 27mm bundles compensate with _ProjectorRadius nudged by 0.087, which is the deployment offset the mod already applies in templateInfo.deployMod.small.

Happy to pull these out into their own PR if you would rather judge them separately — say the word.

One miniature drifted, unintentionally. ig-100_magnagaurd_mat has _SwapOutline at 0.217 where the published copy has 0.348. That is a rebuild artefact, not a decision. Flagging it rather than quietly patching a bundle I cannot test; if you want the published value back it is a one-property edit.

The remaining differences are properties the published materials never stored at all_BaseGap, _ArcCompress / _ArcSpacing, and the eighteen Color Replacer knobs on the three arc_* bundles. In every case the published material has no entry and the rebuilt one declares the shader's value. Rebuilding against the current shader writes out the full property set; nothing was retuned.

How thoroughly this was checked

Our own inventory of the broken bundles dates from May, and the mod has moved since — several assets have been re-uploaded under new UGC entries that the inventory never knew about. So the final sweep ignored the inventory entirely and worked from the repository itself: every URL used in a bundle role, found by its field — AssetbundleURL, minis[].bundle, sharedBundle. 190 unique bundle URLs.

  • 75 now point at a repaired copy.
  • 115 are still on Steam, and every single one of them contains no shader at all. They render through stock TTS materials, so there is nothing for a missing Metal variant to break. This includes the three sharedBundle entries in StarWarsLegion.lua, the table bundle in TABLE.3a3ed9.json and the Infantry Support Platform — all re-uploads that replaced assets our old inventory had flagged.

So the claim is not "we fixed the ones we knew about". It is: every bundle this repository loads has been downloaded and had its shaders read, and none of them is missing Metal any more.

No blind spots left, and one more thing found

I had listed Sabine Wren and Black Sun Enforcers as unreachable, because our old inventory has bundles for them that appear nowhere in the sources. Looking properly: they no longer use a bundle at all. Both now spawn from mesh + diffuse — plain Custom Models on stock TTS materials, which carry no shader and cannot be hit by this. Their URLs answer. Same story for the Infantry Support Platform. So there is nothing left unaccounted for.

While sweeping every mesh and diffuse in the card data — 911 URLs — one dead link turned up in the official set, and it is worth your attention because it is not a shader problem:

The operative token has a dead texture. Fourteen tokens share one disc mesh in official.json — Bane, Complete the Mission, Condition, Graffiti, Incognito, Objective, Operative, Proton Charge, Shield, Smoke, Victory, Wheel Mode — and one of the fourteen, the operative marker, points at a diffuse no Steam CDN serves any more. A Custom Model whose texture fails to load renders pink, and unlike everything else in this PR this one would show on Windows too, on any cold cache.

The right texture was already in the mod: Operative_Marker_Tokens.125746.json carries the same mesh with a diffuse that answers, 162 KB of JPEG. Both copies of official.json now use it.

Two other dead-link problems I did not touch, so you know they exist:

  • templateInfo.templateMesh and templateInfo.templateCollider in StarWarsLegion.lua — the three physical movement template models and their colliders — are 404 everywhere, as is the Mud map's diffuse. Cold cache means pink movement templates, Windows included. These need re-uploading; I have nothing to point them at.
  • contrib/cards/homebrew.json is largely gone: 368 of its 482 model and texture URLs are 404. That is community content whose uploads have expired, not mod assets, so I left it alone entirely — but if the homebrew spawner is meant to work, most of it currently cannot.

What we would like to happen next

This is a workaround, not where it should end up. It serves the repaired bundles from our fork, which is fine for now and not fine forever. The sequence we are hoping for:

  1. You confirm it works on your side — his Mac fixed, and your Windows unchanged. That second half matters: the rebuilds are bi-platform and Windows should read exactly the variant it read before, but it deserves a look rather than our word for it.
  2. You upload the repaired bundles to Steam UGC under your publishing account. UGC entries are immutable, so this means new entries and new URLs, and it is the one step nobody outside the mod can do. The whole repaired set — 152 bundles, covering far more than the 40 here — is already public in the Metal fix release, ready to upload as-is.
  3. You repoint MovementLinks, RangeRulerLinks and CohesionLinks at the new URLs.
  4. Then the swapped URLs get pointed back at Steam. Every swap is a one-line edit in the file where the URL lives, so retiring the workaround is a mechanical find-and-replace.

No rush on our side, and no hard feelings if you would rather ship the workaround for a while — the Mac players stop seeing pink either way. But step 2 is the only thing that actually retires the bug for everyone, including anyone who never installs anything.

Separate bug, worth knowing about

While checking every URL in mod/src I found seven that are 404 on every Steam CDN, not just the dead cloud-3 host:

  • StarWarsLegion.lua:113templateInfo.templateMesh, the three physical movement template models
  • StarWarsLegion.lua:71-73templateInfo.templateCollider, their three colliders
  • Custom_Maps/Mud/BATTLEFIELD.526772.json:39 — the Mud map's DiffuseURL

Those really are gone, and that one is a cache story: anyone with a cold cache gets pink movement templates, Windows included. You only don't see it because yours have been cached for years. Not fixed here — those assets need re-uploading.

Update, 18 Aug — the follow-up commits

The same branch now also carries, each in its own commit:

  • Movement templates that survive a load. The two spawned templates are wired to each other through volatile script state; after any save, load or undo they still stand on the table but every touch throws attempt to index a nil value (turnToCursor, positionTemplate, onCollisionStay spam). They now remove themselves when the wiring is gone. Upstream bug, every platform — to reproduce: start a move, save, reload, touch the template.
  • Maximum-move rings that a load orphaned get swept. Same volatility, other family: the only reference to the ring is script state no save carries, so one that made it into a save could never be cleared again. Your own load sweep — standbyTokens, last call of onload — already destroys Cohesion Ruler and Range Ruler by name; Maximum Move was missing from its list and now joins it.
  • The overlay switch survives saves and undos. Same treatment as the chess clocks and the welcome dialog: the mode rides in the save blob and load imposes it instead of toggling.
  • The overlay toggle no longer errors on tokens with a ruler out, and it resets every family's button flag so no button is left reading the wrong state.
  • The hover hotkeys no longer draw on order tokens. An assigned order token carries the selected unit's unitData in its script globals, so it passed for a unit and range bands or a cohesion ruler got drawn around the token itself. Range hover now draws for unit figures, range-bearing tokens (everything that declares a rangeKey) and bomb carts; cohesion hover for unit figures only — which is what the vanilla path already enforces through its unitData check.
  • The three movement template speeds get clearly distinct colors, and the speed-1 template's collider is pulled back so it stops pushing notched bases — four retuned template bundles, hosted with the rest on isq-qol.
  • Two small cleanups: an unused trigger and the two aliases only it called.

The serving strategy also changed since the first push, as described above: the lookup table inside our module is gone, every repaired URL is swapped in place in the file where it lives, and no binary is committed in the PR.

@ironsquadronfr-hub ironsquadronfr-hub changed the title Spawn the repaired bundles so the overlays stop coming out pink on macOS Repair the bundles that render pink on macOS: overlays, deployment zones, silhouettes, ID tokens Aug 16, 2026
@ironsquadronfr-hub ironsquadronfr-hub changed the title Repair the bundles that render pink on macOS: overlays, deployment zones, silhouettes, ID tokens Repair every bundle that renders pink on macOS: overlays, deployment zones, silhouettes, ID tokens and the miniatures Aug 16, 2026
Martin Pourrat and others added 13 commits August 17, 2026 23:23
…nothing renders pink on macOS

The three link tables the mod spawns its projector tools from still named
the original bundles, which ship no Metal variant: on macOS the shader is
missing at load and the tool renders solid pink. Every URL here now names
the bi-platform rebuild of the same bundle -- same prefabs, same geometry,
same shader source, recompiled for both graphics APIs. Windows reads the
same variant it always did.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dles too

Same defect as the previous commit, reached by a different route. These
bundles are not spawned through the overlay module, so the substitution
table cannot see them: the URLs are written straight into the mod's own
controllers and object JSON. Swapped in place instead.

Twenty-three bundles, all confirmed shipping only [4, 15] -- Direct3D 9 and
11, no Metal -- and all replaced by a rebuild that is byte for byte the one
validated in game:

  * 8 deployment zones and the 3x3 masking projector
    (BucketheadBits/Projector/Deployment, SETUP_CONTROLLER and
    GAME_CONTROLLER). These paint at the start of every single game, so
    they are the most visible of the lot.
  * 3 silhouettes (BucketheadBits/Silhouette), the SIL button on
    Unit_Leader, POI_Token and BombCarts.
  * 10 unit ID tokens (BucketheadBits/Tokens/Lambert Channel Mask).
  * the shelf hologram (BucketheadBits/Logo/Fast Hologram).

31 URL occurrences across 16 files, nothing else touched. These are plain
URL swaps rather than a lookup table because the mod writes them inline;
repointing them at Steam later is the same one-line-per-entry edit.
… on macOS

The last group, and the one that matters most: the unit models themselves.
82 bundles, referenced from contrib/cards/**/minis[].bundle and official.json.

They were checked rather than assumed. Every one of the 82 was downloaded
from its published URL and its shaders read out of the typetree: 82 out of
82 ship platforms [4, 15] only, Direct3D 9 and 11, no Metal. 66 carry
BucketheadBits/Units/Color Replacer, the rest add Glow Geometry and Rim Glow
Geometry Shield, and three carry Particles/Standard Unlit -- a stock Unity
name, but baked into the bundle without a Metal variant like the others, so
it breaks the same way. None of them is a Custom Model with a stock TTS
material; those carry no shader at all and are unaffected.

265 URL occurrences across 11 files. Each replacement is byte for byte the
bundle validated in the local TTS cache.

The 61 MB of rebuilds are hosted on the fork rather than committed here, so
this commit stays a text diff you can actually read. They are all under
mod/data/isq-metal-rebuilds on the isq-qol branch.

With this, no bundle without a Metal variant is referenced anywhere in the
repository any more.
Its spawn entry in official.json carries a diffuse that is 404 on every
Steam CDN, while the mesh next to it is fine. A Custom Model whose texture
fails to load renders pink, which is very likely part of what your Mac
tester reported alongside the shader problem -- and this half would show on
Windows too, on any cold cache.

The replacement is not invented: it is the diffuse the mod's own Graffiti
Token object already uses, same mesh, 42 KB of JPEG that answers today.
Both copies of official.json carried the dead one.
Correcting the previous commit, which was aimed at the wrong token. Fourteen
entries in official.json share one disc mesh -- Bane, Complete the Mission,
Condition, Graffiti, Incognito, Objective, Operative, Proton Charge, Shield,
Smoke, Victory, Wheel Mode -- and I read the dead diffuse as belonging to the
graffiti token because it sat next to that mesh. It is the operative token's,
and the previous commit would have made operative markers show graffiti art.

The right texture was in the mod already: Operative_Marker_Tokens.125746.json
carries the same mesh with a diffuse that answers, 162 KB of JPEG. Both
copies of official.json now use it, and the graffiti token keeps its own.
…g notched bases

On every base with notches the template's collider overlapped the base at
spawn; the template is locked, so the physics engine pushed the miniature
back instead, up to 0.73 mm on epic bases. The retuned bundle moves the
collider's tip from -35.62 to -33.50 mm, leaving 1.3 to 2.1 mm of
clearance on every base. The visual mesh is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The published materials render speeds 1 and 2 nearly identical (white and
a 0.745 grey). The retuned shared bundles paint them white, dark grey and
red, so the chosen speed reads at a glance. Purely cosmetic: the published
bundles with only the material color changed. Drop this commit if you
prefer the original look.

The colorTint fields next to the bundle links are decorative -- the tint
never reaches these bundles' materials -- so they are aligned with the
baked colors and commented as such, and the duplicated speed 1
sharedBundle line is dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Toggling the overlays walks the table and called clearRangeRulersOriginal
and clearCohesionRulerOriginal on objects holding a ruler -- names no
object script defines: every toggle threw a Lua error on each such token
(reported on the token, so the caller's pcall never surfaced it) and the
vanilla ruler was left on the table.

The cleanup now calls the functions that exist -- clearRangeRulers, which
every ruler-holding family gets from !/RangeRulers, and the models'
clearCohesionRuler -- and resets the ruler button flags the wipe leaves
stale in either direction (rangeOn on tokens, isqRangeOn on bomb carts,
rulerOn on order tokens), plus the hover hotkey's toggle identity in the
Global scope, so the next press always starts fresh.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nothing calls it -- the order token's cohesion button does its own
deterministic toggle with rulerOn, which is what this function was written
for -- and its body still calls clearCohesionRulerOriginal and
spawnCohesionRulerOriginal by name on the fig, names no fig script
defines: whoever wired it up later would inherit the silent-pcall bug the
overlay-toggle fix just removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The table-wide overlay mode lived only in a Lua global: every load -- and
every Ctrl+Z, which replays Global's startup with the snapshot's state --
silently flipped the table back to the mod's original overlays. The mode
now rides in the save blob next to the chess clock and welcome dialog
states, and load imposes it rather than toggling, the same rule that fixed
those two. Saves from before the key keep today's default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gCohesionTrigger was their only consumer and is gone; the range pair
stays, macModeToggle and the hover path still route through it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Order Token wires the two spawned templates to each other and to the
unit through setVar/setTable -- volatile script state that no save ever
carries. After a save, a load or an undo (which reloads the whole mod),
the copies still stand on the table but templateA/templateB/basePos are
nil: picking one up spams turnToCursor/positionTemplate errors and every
physics contact spams onCollisionStay. A fresh spawn is wired within its
first frame, a reloaded copy never is, so each copy now waits a second
after load and removes itself if the wiring never came.

Upstream bug, every platform, unrelated to the overlay module.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An assigned order token copies the selected unit's unitData into its
script globals for its own movement math, so the overlay hover hotkeys
took it for a unit and drew range bands or a cohesion ruler around the
token itself. Gate hovered objects on what they are instead: range draws
for unit figures (leaders carry miniGUIDs, minis carry leaderGUID),
tokens that declare a rangeKey (POI, objective, condition, Bane,
mission, smoke) and bomb carts (script-global baseSize); cohesion draws
for unit figures only, which is what vanilla already enforces through
its unitData check on every token. The vanilla path is left as it was.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ironsquadronfr-hub

Copy link
Copy Markdown
Author

Heads-up: rebased on current Mandos+MacFix and force-pushed. Two things changed since the first push:

  1. Serving strategy — the lookup table inside our module is gone and no binary is committed here any more: every repaired URL is a plain in-place swap in the file where it lives, so the whole diff is text.
  2. Eight more commits, detailed at the end of the description — notably: movement templates no longer throw attempt to index a nil value after a save/load/undo (upstream bug, every platform), the Iron Squadron overlay switch now survives saves and undos, and the hover hotkeys no longer draw overlays on order tokens.

Nothing changes in what we are asking: your test on Mac + Windows, then the Steam UGC upload when you are happy.

Martin Pourrat and others added 3 commits August 18, 2026 10:38
With slow or absent connectivity the vanilla XML is not up yet when the
toggle button builds itself: getXmlTable returns nothing and pushing the
empty result back logged "<UI.setXmlTable>: table was null" in Global.
Nothing to edit into means nothing to do -- and the initial build now
retries until the XML is there instead of firing once and missing the
menu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same volatility as the movement templates one commit back, other
family: the only reference to the vanilla maxMoveTemplate ring (and to
our overlay registry's projectors) lives in script state no save
carries, so a ring that made it into a save can never be cleared again
after a load or an undo. The Global's own load sweep already handles
this for rulers -- standbyTokens, last call of onload, destroys
"Cohesion Ruler" and "Range Ruler" by name -- but "Maximum Move"
was missing from its list. Add it there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
From a four-angle cleanup review of the series; no behavior change:

- macModeToggle: hoist the flag list out of the per-object loop, drop
  the pcall around setVar on an object whose getVar was just read bare,
  and fold the three getVar existence checks under one guard.
- IsqBridge: one shared isqVar probe instead of the pcall-around-getVar
  idiom written out three times; drop a nil-guard the pcalls already
  cover.
- Model Template B: point at Model Template A's zombie-guard comment
  instead of repeating it verbatim.
- IsqOverlays: the ISQ_ASSETS comment claimed a single repoint site,
  which the in-place URL swaps made false; say what repointing actually
  takes and why the literals are deliberate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The three SIL comments claimed silhouetteState is saved with the game;
it is not -- Unit_Leader and BombCarts already reset it on every load,
and silhouettes are attachments TTS never carries into a save, so a
load always starts with none up and the state false. Say so, and give
the POI token the same explicit reset its two siblings have (it relied
on nil being falsy). No behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant