Files
K-C-Multiplayer/ModalManager.cs
devbeni e1d81baf60 Fix ModalManager crash when asset bundle is missing
Problem: When Main.TransitionTo() tried to show an error modal about
missing multiplayer UI, it triggered ModalManager's static constructor
which tried to instantiate modalUIPrefab. Since the asset bundle was
missing, modalUIPrefab was null, causing:

System.TypeInitializationException: The type initializer for
'KCM.ModalManager' threw an exception.
---> System.ArgumentException: The Object you want to instantiate is null.
  at KCM.ModalManager..cctor () [0x00017]

This created a catch-22: couldn't show error modal about missing UI
because the modal itself needed the missing UI.

Root cause:
ModalManager static constructor didn't check if modalUIPrefab was null
before trying to instantiate it. ShowModal() and HideModal() also
assumed modalInst was always initialized.

Solutions:

ModalManager static constructor (lines 25-30):
- Add null check for PrefabManager.modalUIPrefab
- If null, log warning and return early
- Set instantiated = true to prevent re-initialization
- Log success message when initialization works

ShowModal() method (lines 55-59):
- Check if modalInst is null before using it
- If null (couldn't initialize), just log the modal content
- Format: "MODAL (not shown - UI missing): Title - Message"
- Prevents crash and preserves the error message in logs

HideModal() method (lines 81-84):
- Add null check before calling SetActive
- Safe to call even if modal not initialized

Results:
 No crash when modal UI is missing
 Error messages still logged even if not shown visually
 Graceful degradation throughout the mod
 ModalManager can be safely called from anywhere
 Clear logging shows what modals would have appeared

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 21:28:18 +01:00

88 lines
2.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace KCM
{
public class ModalManager
{
static GameObject modalInst;
static bool instantiated = false;
static TMPro.TextMeshProUGUI tmpTitle;
static TMPro.TextMeshProUGUI tmpDescription;
static Button acceptButton;
static ModalManager()
{
if (!instantiated)
{
// Check if modal prefab is loaded
if (PrefabManager.modalUIPrefab == null)
{
Main.helper.Log("WARNING: ModalManager cannot initialize - modalUIPrefab is null (asset bundle missing)");
instantiated = true; // Prevent re-initialization attempts
return;
}
modalInst = GameObject.Instantiate(PrefabManager.modalUIPrefab, Constants.MainMenuUI_T);
modalInst.SetActive(false);
acceptButton = modalInst.transform.Find("Modal/Container/Button").GetComponent<UnityEngine.UI.Button>();
tmpTitle = modalInst.transform.Find("Modal/Container/Title").GetComponent<TextMeshProUGUI>();
tmpDescription = modalInst.transform.Find("Modal/Container/Description").GetComponent<TextMeshProUGUI>();
instantiated = true;
Main.helper.Log("ModalManager initialized successfully");
}
else
{
throw new Exception("ModalManager is a singleton and may only be instantiated once");
}
}
public static void ShowModal(string title, string message, string buttonText = "Okay", bool withButton = true, Action action = null)
{
// If modal couldn't be initialized (asset bundle missing), just log the message
if (modalInst == null)
{
Main.helper.Log($"MODAL (not shown - UI missing): {title} - {message}");
return;
}
tmpTitle.text = title;
tmpDescription.text = message;
acceptButton.gameObject.SetActive(withButton);
acceptButton.gameObject.GetComponentInChildren<TextMeshProUGUI>().text = buttonText;
acceptButton.onClick.RemoveAllListeners();
acceptButton.onClick.AddListener(() =>
{
modalInst.SetActive(false); // Clicked okay
action?.Invoke();
});
modalInst.SetActive(true);
}
public static void HideModal()
{
if (modalInst != null)
{
modalInst.SetActive(false);
}
}
}
}