fix: comprehensive null/invalid value handling in weapon selector

- Add detailed logging for skipped weapons
- Handle 'null', 'None', empty strings in decay/ammo values
- Safe Decimal conversion with try/except blocks
- Safe tooltip formatting for all weapon fields
- Safe preview update with error handling
This commit is contained in:
LemonNexus 2026-02-09 22:06:31 +00:00
parent f6ec57d2a2
commit 3ae25503d6
1 changed files with 97 additions and 44 deletions

View File

@ -111,29 +111,42 @@ class WeaponSelectorDialog(QDialog):
self._weapons = []
for w in all_weapons:
try:
# Validate that decay and ammo are valid numbers
decay_val = w.decay if w.decay is not None else 0
# NexusWeapon uses ammo_burn, not ammo
ammo_val = w.ammo_burn if w.ammo_burn is not None else 0
# Get raw values
decay_raw = w.decay
ammo_raw = w.ammo_burn
# Additional validation - skip if values are empty strings or 'null'
if decay_val == '' or decay_val == 'null':
decay_val = 0
if ammo_val == '' or ammo_val == 'null':
ammo_val = 0
# Log problematic values for debugging
if decay_raw is None or str(decay_raw).strip() == '':
logger.debug(f"Weapon {w.name}: decay is None or empty")
decay_raw = 0
if ammo_raw is None or str(ammo_raw).strip() == '':
logger.debug(f"Weapon {w.name}: ammo_burn is None or empty")
ammo_raw = 0
# Convert to string and strip
decay_str = str(decay_raw).strip()
ammo_str = str(ammo_raw).strip()
# Handle 'null' or 'None' strings
if decay_str.lower() in ('null', 'none', ''):
decay_str = '0'
if ammo_str.lower() in ('null', 'none', ''):
ammo_str = '0'
# Try to convert to Decimal
decay_val = Decimal(decay_str)
ammo_val = Decimal(ammo_str)
# Try to convert to Decimal to validate
Decimal(str(decay_val))
Decimal(str(ammo_val))
self._weapons.append(w)
except (InvalidOperation, ValueError, TypeError) as e:
# Skip weapons with invalid data
logger.debug(f"Skipping weapon {getattr(w, 'name', 'unknown')} due to invalid decay/ammo: {e}")
# Log the exact problem
logger.warning(f"Skipping weapon '{getattr(w, 'name', 'unknown')}': decay={getattr(w, 'decay', 'N/A')}, ammo_burn={getattr(w, 'ammo_burn', 'N/A')} - Error: {e}")
continue
# Sort by name
self._weapons.sort(key=lambda w: w.name.lower())
logger.info(f"Loaded {len(self._weapons)} valid weapons out of {len(all_weapons)} total")
self._populate_list(self._weapons)
except Exception as e:
logger.error(f"Failed to load weapons: {e}")
@ -145,32 +158,57 @@ class WeaponSelectorDialog(QDialog):
for weapon in weapons:
try:
# Get values with safe defaults
# Get values with safe defaults and validation
decay_raw = weapon.decay if weapon.decay is not None else 0
ammo_raw = weapon.ammo_burn if weapon.ammo_burn is not None else 0
# Ensure they're valid strings for Decimal conversion
decay_str = str(decay_raw).strip() if decay_raw else '0'
ammo_str = str(ammo_raw).strip() if ammo_raw else '0'
if decay_str.lower() in ('null', 'none', ''):
decay_str = '0'
if ammo_str.lower() in ('null', 'none', ''):
ammo_str = '0'
# Calculate cost per shot
decay_pec = Decimal(str(decay_raw))
ammo = Decimal(str(ammo_raw))
decay_pec = Decimal(decay_str)
ammo = Decimal(ammo_str)
cost_per_shot = (decay_pec / Decimal("100")) + (ammo * Decimal("0.0001"))
item = QListWidgetItem(f"{weapon.name} (💰 {cost_per_shot:.4f} PED)")
item.setData(Qt.ItemDataRole.UserRole, weapon)
# Tooltip
tooltip = (
f"Damage: {weapon.damage}\n"
f"Decay: {decay_raw} PEC\n"
f"Ammo: {ammo_raw}\n"
f"Range: {weapon.range_val}\n"
f"DPP: {weapon.dpp:.2f}" if weapon.dpp else "DPP: -"
)
item.setToolTip(tooltip)
# Safe tooltip formatting
try:
damage_str = str(weapon.damage) if weapon.damage is not None else "-"
range_str = str(weapon.range_val) if weapon.range_val is not None else "-"
# Format DPP safely
dpp_str = "-"
if weapon.dpp is not None:
try:
dpp_val = Decimal(str(weapon.dpp))
dpp_str = f"{dpp_val:.2f}"
except:
dpp_str = str(weapon.dpp)
tooltip = (
f"Damage: {damage_str}\n"
f"Decay: {decay_str} PEC\n"
f"Ammo: {ammo_str}\n"
f"Range: {range_str}\n"
f"DPP: {dpp_str}"
)
item.setToolTip(tooltip)
except Exception as tooltip_error:
logger.debug(f"Tooltip error for {weapon.name}: {tooltip_error}")
item.setToolTip(f"{weapon.name}")
self.weapon_list.addItem(item)
except Exception as e:
# Skip weapons that fail to process
logger.debug(f"Skipping weapon {getattr(weapon, 'name', 'unknown')}: {e}")
logger.warning(f"Skipping weapon in populate_list {getattr(weapon, 'name', 'unknown')}: {e}")
continue
def _on_search(self, text):
@ -191,23 +229,38 @@ class WeaponSelectorDialog(QDialog):
self.selected_weapon = weapon
# Get values with safe defaults
decay_raw = weapon.decay if weapon.decay is not None else 0
ammo_raw = weapon.ammo_burn if weapon.ammo_burn is not None else 0
# Calculate cost per shot
decay_pec = Decimal(str(decay_raw))
ammo = Decimal(str(ammo_raw))
cost_per_shot = (decay_pec / Decimal("100")) + (ammo * Decimal("0.0001"))
# Update preview
self.preview_name.setText(weapon.name)
self.preview_damage.setText(str(weapon.damage) if weapon.damage is not None else "-")
self.preview_decay.setText(f"{decay_raw} PEC")
self.preview_ammo.setText(str(ammo_raw))
self.preview_cost.setText(f"{cost_per_shot:.4f} PED")
self.ok_btn.setEnabled(True)
try:
# Get values with safe defaults and validation
decay_raw = weapon.decay if weapon.decay is not None else 0
ammo_raw = weapon.ammo_burn if weapon.ammo_burn is not None else 0
# Ensure they're valid strings for Decimal conversion
decay_str = str(decay_raw).strip() if decay_raw else '0'
ammo_str = str(ammo_raw).strip() if ammo_raw else '0'
if decay_str.lower() in ('null', 'none', ''):
decay_str = '0'
if ammo_str.lower() in ('null', 'none', ''):
ammo_str = '0'
# Calculate cost per shot
decay_pec = Decimal(decay_str)
ammo = Decimal(ammo_str)
cost_per_shot = (decay_pec / Decimal("100")) + (ammo * Decimal("0.0001"))
# Update preview with safe string conversion
self.preview_name.setText(str(weapon.name) if weapon.name else "Unknown")
self.preview_damage.setText(str(weapon.damage) if weapon.damage is not None else "-")
self.preview_decay.setText(f"{decay_str} PEC")
self.preview_ammo.setText(str(ammo_str))
self.preview_cost.setText(f"{cost_per_shot:.4f} PED")
self.ok_btn.setEnabled(True)
except Exception as e:
logger.error(f"Error updating preview for {getattr(weapon, 'name', 'unknown')}: {e}")
self.preview_name.setText(str(weapon.name) if weapon.name else "Unknown")
self.preview_cost.setText("Error")
self.ok_btn.setEnabled(False)
# Calculate cost per shot
decay_pec = Decimal(str(weapon.decay))