Summary
The ANALYSIS tab reports:
1 CONFLICT: recipe smelter registered by Sandustry: Critical Matter and Platinum Industry and Atomic Age and Red Sand Production - the later one wins or throws
This is a false positive. The four mods each call api.structures.recipes.register("smelter", …) with distinct input elements — they attach recipes to the same vanilla machine, which is the supported case. There is no runtime collision.
Environment
- Sandustry 0.5.6, Mod Inspector 0.3.0, SandKit apiVersion 1
- Mods: Atomic Age (
3784544373), Sandustry: Critical Matter (3787956085), Red Sand Production (3788826976), Platinum Industry (3792673946)
Root cause
The scan in main.js:
{ kind: "recipe", regex: /\brecipes\s*\.\s*register\s*\(\s*["'`]([^"'`]+)["'`]/g },
captures the literal "smelter" — but smelter is the machine id, not a recipe id. The engine (recipes.register in the game bundle, ~@903849) keys the recipe table by machine and stores one recipe per input element; a second register for the same machine only replaces an existing recipe when the input matches:
// K(state, machineId, def): switch(machineId) -> condensers|steamDryers|synthesizers|snowmakers|smelters
let i = -1; for (let e = 0; e < a.length; e++) if (a[e].input === r.input) { i = e; break; }
i === -1 ? a.push(r) : a[i] = r;
Only those machine ids are legal keys for recipes.register; anything else throws TypeError at load. So renaming the literal would break every recipe involved — the mods cannot "fix" this from their side.
Actual inputs (no two overlap)
| Mod |
Smelter recipes (input → outputs) |
| Atomic Age (uolkx.atomic-age) |
sand → silicon, oil → sulfur, basalt → sulfur |
| Sandustry: Critical Matter (superman4eg.critical-matter) |
fuelPellet → liquidReactorFuel, moxBlend → liquidMoxFuel |
| Red Sand Production (sandustry-labs.red-sand-production) |
gold → liquidGold (intentionally retunes vanilla, 0.5 chance) |
| Platinum Industry (my3blkaht.platinum-industry) |
platinum → liquidPlatinum |
All four behave correctly together and are keyed by distinct inputs.
Suggested fix
Treat recipes attached to the known vanilla machine ids as shared-by-design, the same way hooks/events already are (SHARED_KINDS): keep them out of registryConflicts/countProblems and list them under the "SHARED BY DESIGN" info section. A real conflict only exists when the same machine and the same input both match — inputs are runtime element ids, so this could be checked against sandkit.mods.recipes instead of a source regex.
const SHARED_KINDS = new Set(["hook", "event"]);
+// recipes.register() is keyed off the *machine* id, not a recipe id - the
+// engine accepts exactly these machine types and stores one recipe per input
+// element. Several mods attaching distinct inputs to the same vanilla machine
+// (Smelter, Synthesizer, ...) is the normal case, so these can never collide at
+// runtime unless two mods pick the same input, which a source scan cannot see.
+const SHARED_RECIPE_MACHINES = new Set([
+ "condenser",
+ "steamDryer",
+ "synthesizer",
+ "snowmaker",
+ "smelter",
+]);
+const sharedByDesign = (kind, id) =>
+ SHARED_KINDS.has(kind) || (kind === "recipe" && SHARED_RECIPE_MACHINES.has(id));
for (const [kind, byId] of owners) {
for (const [id, modIds] of byId) {
if (modIds.length < 2) continue;
- (SHARED_KINDS.has(kind) ? sharedHooks : registryConflicts).push({ kind, id, modIds });
+ sharedByDesign(kind, id)
+ ? sharedHooks.push({ kind, id, modIds })
+ : registryConflicts.push({ kind, id, modIds });
}
}
And mirror it in the two "WHAT EACH MOD REGISTERS" filters:
- const registered = owned.filter((entry) => !SHARED_KINDS.has(entry.kind)).length;
+ const registered = owned.filter((entry) => !sharedByDesign(entry.kind, entry.id)).length;
- const owned = (analysis.perMod.get(mod.id) || []).filter((entry) => !SHARED_KINDS.has(entry.kind));
+ const owned = (analysis.perMod.get(mod.id) || []).filter((entry) => !sharedByDesign(entry.kind, entry.id));
Verified locally: patch passes node --check, and re-running the scanning logic over all four mods' sources drops countProblems to 0 (the entry moves to the shared-by-design bucket).
Reproduce
- Subscribe to the four mods listed above.
- Open Mod Inspector → ANALYSIS.
- Result:
1 CONFLICT (recipe smelter), while all smelter recipes (silicon, sulfur, liquid reactor fuel, liquid platinum, liquid gold) actually work in-game.
Summary
The ANALYSIS tab reports:
This is a false positive. The four mods each call
api.structures.recipes.register("smelter", …)with distinct input elements — they attach recipes to the same vanilla machine, which is the supported case. There is no runtime collision.Environment
3784544373), Sandustry: Critical Matter (3787956085), Red Sand Production (3788826976), Platinum Industry (3792673946)Root cause
The scan in
main.js:captures the literal
"smelter"— butsmelteris the machine id, not a recipe id. The engine (recipes.registerin the game bundle, ~@903849) keys the recipe table by machine and stores one recipe per input element; a secondregisterfor the same machine only replaces an existing recipe when the input matches:Only those machine ids are legal keys for
recipes.register; anything else throwsTypeErrorat load. So renaming the literal would break every recipe involved — the mods cannot "fix" this from their side.Actual inputs (no two overlap)
All four behave correctly together and are keyed by distinct inputs.
Suggested fix
Treat recipes attached to the known vanilla machine ids as shared-by-design, the same way hooks/events already are (
SHARED_KINDS): keep them out ofregistryConflicts/countProblemsand list them under the "SHARED BY DESIGN" info section. A real conflict only exists when the same machine and the same input both match — inputs are runtime element ids, so this could be checked againstsandkit.mods.recipesinstead of a source regex.And mirror it in the two "WHAT EACH MOD REGISTERS" filters:
Verified locally: patch passes
node --check, and re-running the scanning logic over all four mods' sources dropscountProblemsto 0 (the entry moves to the shared-by-design bucket).Reproduce
1 CONFLICT(recipe smelter), while all smelter recipes (silicon, sulfur, liquid reactor fuel, liquid platinum, liquid gold) actually work in-game.