Skip to content

Commit 4e7be09

Browse files
committed
Add safety checks to prevent crashes after missile launch
Add null checks throughout the widget to handle invalid units gracefully: - Check for valid positions before accessing unit data - Verify unit definitions and weapon definitions exist - Handle dead units and parent silos safely - Return early on nil values to prevent cascading failures Fixes crash that occurs after missile launches. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiaPfKSj8kGFjaUPyxYYHd
1 parent b84a180 commit 4e7be09

1 file changed

Lines changed: 64 additions & 21 deletions

File tree

LuaUI/Widgets/missle_command_center.lua

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -72,30 +72,39 @@ local function missle_class()
7272
end
7373

7474
function self:getNumberOfQueueLaunches(unit)
75-
local unitType = self.launchableTypes[Spring.GetUnitDefID(unit)]
76-
local numStockpiled = unitType.getStockpile(unit)
75+
local unitDefID = Spring.GetUnitDefID(unit)
76+
if not unitDefID then return 0 end
7777

78-
local numQueued = 0
79-
if (numStockpiled or 0) ~= 0 then
80-
local cmdQueue = Spring.GetUnitCommands(unit, numStockpiled);
78+
local unitType = self.launchableTypes[unitDefID]
79+
if not unitType then return 0 end
8180

82-
for _, cmd in ipairs(cmdQueue) do
83-
if cmd.id == unitType.launchCmd then numQueued = numQueued + 1 end
84-
end
81+
local numStockpiled = unitType.getStockpile(unit)
82+
if not numStockpiled or numStockpiled == 0 then return 0 end
83+
84+
local cmdQueue = Spring.GetUnitCommands(unit, numStockpiled)
85+
if not cmdQueue then return 0 end
8586

87+
local numQueued = 0
88+
for _, cmd in ipairs(cmdQueue) do
89+
if cmd and cmd.id == unitType.launchCmd then numQueued = numQueued + 1 end
8690
end
91+
8792
return numQueued
8893
end
8994

9095
function self:getCount()
9196
local count = 0
9297
for _, unit in ipairs(self:getOrderableUnits()) do
9398
if not Spring.GetUnitIsDead(unit) then
94-
local type = self.launchableTypes[Spring.GetUnitDefID(unit)]
95-
if type then
96-
count = count
97-
+ type.getStockpile(unit)
98-
- self:getNumberOfQueueLaunches(unit)
99+
local unitDefID = Spring.GetUnitDefID(unit)
100+
if unitDefID then
101+
local type = self.launchableTypes[unitDefID]
102+
if type then
103+
local stockpile = type.getStockpile(unit)
104+
if stockpile then
105+
count = count + stockpile - self:getNumberOfQueueLaunches(unit)
106+
end
107+
end
99108
end
100109
end
101110
end
@@ -242,12 +251,26 @@ local function missle_class()
242251
if not mx or not mz then return end
243252
local unit = self:getPerferedUnit{x = mx, z = mz}
244253
if not unit then return end
254+
245255
local ux, uy, uz = Spring.GetUnitPosition(unit)
246-
local dist = distance(mx, mz, ux, uz)
256+
if not ux then return end
247257

248-
local weaponDefId = self.launchableTypes[Spring.GetUnitDefID(unit)].weaponId
249-
local weaponDef = WeaponDefs[UnitDefs[Spring.GetUnitDefID(unit)].weapons[weaponDefId].weaponDef]
258+
local unitDefID = Spring.GetUnitDefID(unit)
259+
if not unitDefID then return end
250260

261+
local unitType = self.launchableTypes[unitDefID]
262+
if not unitType then return end
263+
264+
local unitDef = UnitDefs[unitDefID]
265+
if not unitDef or not unitDef.weapons then return end
266+
267+
local weapon = unitDef.weapons[unitType.weaponId]
268+
if not weapon then return end
269+
270+
local weaponDef = WeaponDefs[weapon.weaponDef]
271+
if not weaponDef then return end
272+
273+
local dist = distance(mx, mz, ux, uz)
251274
local range = weaponDef.range
252275
if dist > range * range then return end
253276

@@ -271,12 +294,16 @@ local function EOS_controller_class()
271294
launchCmd = CMD.ATTACK,
272295
weaponId = 1,
273296
getStockpile = function(unit)
274-
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
275297
if Spring.GetUnitIsDead(unit) then return 0 end
276298

299+
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
300+
if not silo or Spring.GetUnitIsDead(silo) then return 0 end
301+
277302
local x1, y1, z1 = Spring.GetUnitPosition(silo)
278303
local x2, y2, z2 = Spring.GetUnitPosition(unit)
279304

305+
if not x1 or not x2 then return 0 end
306+
280307
if distance3(x1, y1, z1, x2, y2, z2) > 600 then return 0 end
281308

282309
return 1
@@ -306,12 +333,16 @@ local function seismic_controller_class()
306333
launchCmd = CMD.ATTACK,
307334
weaponId = 1,
308335
getStockpile = function(unit)
309-
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
310336
if Spring.GetUnitIsDead(unit) then return 0 end
311337

338+
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
339+
if not silo or Spring.GetUnitIsDead(silo) then return 0 end
340+
312341
local x1, y1, z1 = Spring.GetUnitPosition(silo)
313342
local x2, y2, z2 = Spring.GetUnitPosition(unit)
314343

344+
if not x1 or not x2 then return 0 end
345+
315346
if distance3(x1, y1, z1, x2, y2, z2) > 600 then return 0 end
316347

317348
return 1
@@ -334,12 +365,16 @@ local function shockley_controller_class()
334365
launchCmd = CMD.ATTACK,
335366
weaponId = 1,
336367
getStockpile = function(unit)
337-
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
338368
if Spring.GetUnitIsDead(unit) then return 0 end
339369

370+
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
371+
if not silo or Spring.GetUnitIsDead(silo) then return 0 end
372+
340373
local x1, y1, z1 = Spring.GetUnitPosition(silo)
341374
local x2, y2, z2 = Spring.GetUnitPosition(unit)
342375

376+
if not x1 or not x2 then return 0 end
377+
343378
if distance3(x1, y1, z1, x2, y2, z2) > 600 then return 0 end
344379

345380
return 1
@@ -362,12 +397,16 @@ local function inferno_controller_class()
362397
launchCmd = CMD.ATTACK,
363398
weaponId = 1,
364399
getStockpile = function(unit)
365-
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
366400
if Spring.GetUnitIsDead(unit) then return 0 end
367401

402+
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
403+
if not silo or Spring.GetUnitIsDead(silo) then return 0 end
404+
368405
local x1, y1, z1 = Spring.GetUnitPosition(silo)
369406
local x2, y2, z2 = Spring.GetUnitPosition(unit)
370407

408+
if not x1 or not x2 then return 0 end
409+
371410
if distance3(x1, y1, z1, x2, y2, z2) > 600 then return 0 end
372411

373412
return 1
@@ -390,12 +429,16 @@ local function slow_missile_controller_class()
390429
launchCmd = CMD.ATTACK,
391430
weaponId = 1,
392431
getStockpile = function(unit)
393-
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
394432
if Spring.GetUnitIsDead(unit) then return 0 end
395433

434+
local silo = Spring.GetUnitRulesParam(unit, "missile_parentSilo")
435+
if not silo or Spring.GetUnitIsDead(silo) then return 0 end
436+
396437
local x1, y1, z1 = Spring.GetUnitPosition(silo)
397438
local x2, y2, z2 = Spring.GetUnitPosition(unit)
398439

440+
if not x1 or not x2 then return 0 end
441+
399442
if distance3(x1, y1, z1, x2, y2, z2) > 600 then return 0 end
400443

401444
return 1

0 commit comments

Comments
 (0)