diff --git a/TLM/TLM/LoadingExtension.cs b/TLM/TLM/LoadingExtension.cs index 837768df2..f4abbfa78 100644 --- a/TLM/TLM/LoadingExtension.cs +++ b/TLM/TLM/LoadingExtension.cs @@ -16,23 +16,27 @@ namespace TrafficManager { using UnityEngine.SceneManagement; using Util; using Object = UnityEngine.Object; + using System.Linq; [UsedImplicitly] public class LoadingExtension : LoadingExtensionBase { static LoadingExtension() { TranslationDatabase.LoadAllTranslations(); + RegisterCustomManagers(); } - public LoadingExtension() { - } - - internal static LoadingExtension Instance = null; + static FastList GetSimManagers() => + typeof(SimulationManager) + .GetField("m_managers", BindingFlags.Static | BindingFlags.NonPublic) + ?.GetValue(null) + as FastList + ?? throw new Exception("could not get SimulationManager.m_managers"); - FastList simManager => - typeof(SimulationManager).GetField("m_managers", BindingFlags.Static | BindingFlags.NonPublic) - ?.GetValue(null) as FastList; + internal static AppMode? AppMode => SimulationManager.instance.m_ManagersWrapper.loading.currentMode; - internal static AppMode? AppMode => Instance?.loadingManager?.currentMode; + public static SimulationManager.UpdateMode UpdateMode => SimulationManager.instance.m_metaData.m_updateMode; + public static LoadMode Mode => (LoadMode)UpdateMode; + public static string Scene => SceneManager.GetActiveScene().name; /// /// determines whether Game mode as oppose to edit mode (eg asset editor). @@ -46,37 +50,15 @@ public LoadingExtension() { /// public static Translation TranslationDatabase = new Translation(); - public static UITransportDemand TransportDemandUI { get; private set; } - public static List RegisteredManagers { get; private set; } public static bool IsGameLoaded { get; private set; } - public static bool IsPathManagerReplaced { - get; private set; - } - - public override void OnCreated(ILoading loading) { - Log._Debug("LoadingExtension.OnCreated() called"); - - // SelfDestruct.DestructOldInstances(this); - base.OnCreated(loading); - if (IsGameLoaded) { - // When another mod is detected, OnCreated is called again for god - or CS team - knows what reason! - Log._Debug("Hot reload of another mod detected. Skipping LoadingExtension.OnCreated() ..."); - return; - } - InGameUtil.Instantiate(); + public static bool IsPathManagerReplaced { get; private set; } + private static void RegisterCustomManagers() { RegisteredManagers = new List(); - CustomPathManager = new CustomPathManager(); - - RegisterCustomManagers(); - - Instance = this; - } - private void RegisterCustomManagers() { // TODO represent data dependencies differently RegisteredManagers.Add(ExtNodeManager.Instance); RegisteredManagers.Add(ExtSegmentManager.Instance); @@ -107,24 +89,19 @@ private void RegisterCustomManagers() { RegisteredManagers.Add(JunctionRestrictionsManager.Instance); } - public override void OnReleased() { - TrafficManagerMod.Instance.InGameHotReload = false; - Instance = null; - base.OnReleased(); - } + public override void OnLevelUnloading() => Unload(); + + public override void OnLevelLoaded(LoadMode mode) => Load(); + + public static void Unload() { + Log.Info("LoadingExtension.Unload()"); - public override void OnLevelUnloading() { - Log.Info("OnLevelUnloading"); - base.OnLevelUnloading(); if (IsPathManagerReplaced) { CustomPathManager._instance.WaitForAllPaths(); } try { - var reverseManagers = new List(RegisteredManagers); - reverseManagers.Reverse(); - - foreach (ICustomManager manager in reverseManagers) { + foreach (ICustomManager manager in RegisteredManagers.AsEnumerable().Reverse()) { Log.Info($"OnLevelUnloading: {manager.GetType().Name}"); manager.OnLevelUnloading(); } @@ -132,24 +109,12 @@ public override void OnLevelUnloading() { Flags.OnLevelUnloading(); GlobalConfig.OnLevelUnloading(); - var gameObject = UIView.GetAView().gameObject; - - void Destroy() where T : MonoBehaviour { - Object obj = (Object)gameObject.GetComponent(); - if (obj != null) { - Object.Destroy(obj); - } - } - - Destroy(); - Destroy(); - Destroy(); - - //It's MonoBehaviour - comparing to null is wrong - if (TransportDemandUI) { - Object.Destroy(TransportDemandUI); - TransportDemandUI = null; - } + var uiviewGO = UIView.GetAView().gameObject; + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); Log.Info("Removing Controls from UI."); if (ModUI.Instance != null) { @@ -164,18 +129,21 @@ void Destroy() where T : MonoBehaviour { // ignored - prevents collision with other mods } - Patcher.Instance?.Uninstall(); + Patcher.Uninstall(); IsGameLoaded = false; + TrafficManagerMod.InGameHotReload = false; } - public override void OnLevelLoaded(LoadMode mode) { - SimulationManager.UpdateMode updateMode = SimulationManager.instance.m_metaData.m_updateMode; - string scene = SceneManager.GetActiveScene().name; - Log.Info($"OnLevelLoaded({mode}) called. updateMode={updateMode}, scene={scene}"); + public static void Load() { + Log.Info($"LoadingExtension.Load() called. {Mode} called. updateMode={UpdateMode}, scene={Scene}"); - if (scene == "ThemeEditor") + if(Scene == "ThemeEditor") return; + InGameUtil.Instantiate(); + + RegisterCustomManagers(); + IsGameLoaded = false; if (BuildConfig.applicationVersion != BuildConfig.VersionToString( @@ -247,7 +215,7 @@ public override void OnLevelLoaded(LoadMode mode) { IsGameLoaded = true; //it will replace stock PathManager or already Replaced before HotReload - if (!IsPathManagerReplaced || TrafficManagerMod.Instance.InGameHotReload) { + if (!IsPathManagerReplaced || TrafficManagerMod.InGameHotReload) { try { Log.Info("Pathfinder Compatible. Setting up CustomPathManager and SimManager."); FieldInfo pathManagerInstance = typeof(Singleton).GetField( @@ -278,17 +246,14 @@ public override void OnLevelLoaded(LoadMode mode) { pathManagerInstance.SetValue(null, CustomPathManager); - Log._Debug("Getting Current SimulationManager"); - var simManager = this.simManager; - if (simManager == null) { - throw new Exception("simManager is null"); - } + Log._Debug("Getting Current SimulationManagers"); + var simManagers = GetSimManagers(); Log._Debug("Removing Stock PathManager"); - simManager.Remove(stockPathManager); + simManagers.Remove(stockPathManager); Log._Debug("Adding Custom PathManager"); - simManager.Add(CustomPathManager); + simManagers.Add(CustomPathManager); Object.Destroy(stockPathManager, 10f); @@ -318,22 +283,14 @@ public override void OnLevelLoaded(LoadMode mode) { ModUI.OnLevelLoaded(); if (PlayMode) { - // Init transport demand UI - if (TransportDemandUI == null) { - UIView uiView = UIView.GetAView(); - TransportDemandUI = (UITransportDemand)uiView.AddUIComponent(typeof(UITransportDemand)); - } - - // add "remove vehicle" button - UIView.GetAView().gameObject.AddComponent(); - - // add "remove citizen instance" button - UIView.GetAView().gameObject.AddComponent(); - - UIView.GetAView().gameObject.AddComponent(); + UIView uiView = UIView.GetAView(); + uiView.AddUIComponent(typeof(UITransportDemand)); + uiView.gameObject.AddComponent(); + uiView.gameObject.AddComponent(); + uiView.gameObject.AddComponent(); } - Patcher.Create().Install(); + Patcher.Install(); // Log.Info("Fixing non-created nodes with problems..."); // FixNonCreatedNodeProblems(); diff --git a/TLM/TLM/Patcher.cs b/TLM/TLM/Patcher.cs index 1445ac2d4..349768ae7 100644 --- a/TLM/TLM/Patcher.cs +++ b/TLM/TLM/Patcher.cs @@ -9,20 +9,10 @@ namespace TrafficManager { using TrafficManager.RedirectionFramework; using TrafficManager.Util; - public class Patcher { - public static Patcher Instance { get; private set; } - - public static Patcher Create() => Instance = new Patcher(); - + public static class Patcher { private const string HARMONY_ID = "de.viathinksoft.tmpe"; - private bool initialized_ = false; - - public void Install() { - if (initialized_) { - return; - } - + public static void Install() { Log.Info("Init detours"); bool fail = false; @@ -31,10 +21,8 @@ public void Install() { Harmony.DEBUG = true; #endif // Harmony attribute-driven patching - Log.Info($"Performing Harmony attribute-driven patching"); - var harmony = new Harmony(HARMONY_ID); - Shortcuts.Assert(harmony != null, "HarmonyInst!=null"); - harmony.PatchAll(); + Log.Info($"Performing Harmony attribute-driven patches"); + new Harmony(HARMONY_ID).PatchAll(); Log.Info($"Harmony attribute-driven patching successfull!"); } catch (Exception e) { @@ -56,36 +44,24 @@ public void Install() { } if (fail) { - Log.Info("Detours failed"); - Singleton.instance.m_ThreadingWrapper.QueueMainThread( - () => { - UIView.library - .ShowModal("ExceptionPanel") - .SetMessage( - "TM:PE failed to load", - "Traffic Manager: President Edition failed to load. You can " + - "continue playing but it's NOT recommended. Traffic Manager will " + - "not work as expected.", - true); - }); + Log.Info("patcher failed"); + UIView.library + .ShowModal("ExceptionPanel") + .SetMessage( + "TM:PE failed to load", + "Traffic Manager: President Edition failed to load. You can " + + "continue playing but it's NOT recommended. Traffic Manager will " + + "not work as expected.", + true); } else { - Log.Info("Detours successful"); + Log.Info("TMPE patches installed successfully"); } - - initialized_ = true; } - public void Uninstall() { - if (!initialized_) { - return; - } - - var harmony = new Harmony(HARMONY_ID); - Shortcuts.Assert(harmony != null, "HarmonyInst!=null"); - harmony.UnpatchAll(HARMONY_ID); - - initialized_ = false; - Log.Info("Reverting detours finished."); + public static void Uninstall() { + new Harmony(HARMONY_ID).UnpatchAll(HARMONY_ID); + AssemblyRedirector.Revert(); + Log.Info("TMPE patches uninstalled."); } } } diff --git a/TLM/TLM/State/Options.cs b/TLM/TLM/State/Options.cs index a981ea7ea..8495fb5c5 100644 --- a/TLM/TLM/State/Options.cs +++ b/TLM/TLM/State/Options.cs @@ -132,7 +132,7 @@ internal static void RebuildMenu() { } } - public static void MakeSettings(UIHelperBase helper) { + public static void MakeSettings(UIHelper helper) { ExtUITabstrip tabStrip = ExtUITabstrip.Create(helper); OptionsGeneralTab.MakeSettings_General(tabStrip); OptionsGameplayTab.MakeSettings_Gameplay(tabStrip); diff --git a/TLM/TLM/State/SerializableDataExtension.cs b/TLM/TLM/State/SerializableDataExtension.cs index d8747dce7..4ebc9f9f4 100644 --- a/TLM/TLM/State/SerializableDataExtension.cs +++ b/TLM/TLM/State/SerializableDataExtension.cs @@ -15,28 +15,14 @@ public class SerializableDataExtension { private const string DATA_ID = "TrafficManager_v1.0"; - private static ISerializableData _serializableData; + private static ISerializableData SerializableData => SimulationManager.instance.m_SerializableDataWrapper; private static Configuration _configuration; public static bool StateLoading; - public override void OnCreated(ISerializableData serializableData) { - Log._Debug("SerializableDataExtension.OnCreated() called"); - if(LoadingExtension.IsGameLoaded) { - Log._Debug("Hot reload of another mod detected. Skipping SerializableDataExtension.OnCreated() ..."); - return; - } - _serializableData = serializableData; - - if (TrafficManagerMod.Instance.InGameHotReload) { - Log._Debug("HOT RELOAD ..."); - OnLoadData(); - LoadingExtension.Instance.OnLevelLoaded(LoadMode.LoadGame); - } - } - - public override void OnReleased() { } + public override void OnLoadData() => Load(); + public override void OnSaveData() => Save(); - public override void OnLoadData() { + public static void Load() { Log.Info("Loading Traffic Manager: PE Data"); StateLoading = true; bool loadingSucceeded = true; @@ -64,7 +50,7 @@ public override void OnLoadData() { Log.Info("Initialization done. Loading mod data now."); try { - byte[] data = _serializableData.LoadData(DATA_ID); + byte[] data = SerializableData.LoadData(DATA_ID); DeserializeData(data); } catch (Exception e) { @@ -74,7 +60,7 @@ public override void OnLoadData() { // load options try { - byte[] options = _serializableData.LoadData("TMPE_Options"); + byte[] options = SerializableData.LoadData("TMPE_Options"); if (options != null) { if (!OptionsManager.Instance.LoadData(options)) { loadingSucceeded = false; @@ -288,7 +274,7 @@ private static void LoadDataState(out bool error) { } } - public override void OnSaveData() { + public static void Save() { bool success = true; // try { @@ -369,7 +355,7 @@ public override void OnSaveData() { try { // save options - _serializableData.SaveData("TMPE_Options", OptionsManager.Instance.SaveData(ref success)); + SerializableData.SaveData("TMPE_Options", OptionsManager.Instance.SaveData(ref success)); } catch (Exception ex) { Log.Error("Unexpected error while saving options: " + ex.Message); success = false; @@ -382,7 +368,7 @@ public override void OnSaveData() { binaryFormatter.Serialize(memoryStream, configuration); memoryStream.Position = 0; Log.Info($"Save data byte length {memoryStream.Length}"); - _serializableData.SaveData(DATA_ID, memoryStream.ToArray()); + SerializableData.SaveData(DATA_ID, memoryStream.ToArray()); } catch (Exception ex) { Log.Error("Unexpected error while saving data: " + ex); success = false; diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index 9c71d199b..69b5ccadf 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -49,14 +49,12 @@ public TrafficManagerMod() { public string Description => "Manage your city's traffic"; - internal static TrafficManagerMod Instance = null; - - internal bool InGameHotReload { get; set; } = false; + internal static bool InGameHotReload { get; set; } = false; /// /// determines if simulation is inside game/editor. useful to detect hot-reload. /// - internal static bool InGame() => + internal static bool InGameOrEditor() => SceneManager.GetActiveScene().name != "IntroScreen" && SceneManager.GetActiveScene().name != "Startup"; @@ -97,11 +95,15 @@ public void OnEnabled() { Log._Debug("Scene is " + SceneManager.GetActiveScene().name); - Instance = this; - InGameHotReload = InGame(); + InGameHotReload = InGameOrEditor(); HarmonyHelper.EnsureHarmonyInstalled(); + if(InGameHotReload) { + SerializableDataExtension.Load(); + LoadingExtension.Load(); + } + #if DEBUG const bool installHarmonyASAP = false; // set true for fast testing if (installHarmonyASAP) @@ -116,16 +118,14 @@ public void OnDisabled() { LocaleManager.eventLocaleChanged -= Translation.HandleGameLocaleChange; Translation.IsListeningToGameLocaleChanged = false; // is this necessary? - if (InGame() && LoadingExtension.Instance != null) { - //Hot reload Unloading - LoadingExtension.Instance.OnLevelUnloading(); - LoadingExtension.Instance.OnReleased(); + if (InGameOrEditor() && LoadingExtension.IsGameLoaded) { + //Hot Unload + LoadingExtension.Unload(); } - Instance = null; } [UsedImplicitly] - public void OnSettingsUI(UIHelperBase helper) { + public void OnSettingsUI(UIHelper helper) { // Note: This bugs out if done in OnEnabled(), hence doing it here instead. if (!Translation.IsListeningToGameLocaleChanged) { Translation.IsListeningToGameLocaleChanged = true; diff --git a/TLM/TLM/UI/Helpers/ExtUITabStrip.cs b/TLM/TLM/UI/Helpers/ExtUITabStrip.cs index a5a46b7ff..3665965d5 100644 --- a/TLM/TLM/UI/Helpers/ExtUITabStrip.cs +++ b/TLM/TLM/UI/Helpers/ExtUITabStrip.cs @@ -104,9 +104,8 @@ public UIHelper AddTabPage(string name, bool scrollBars = true) { return panelHelper; } - public static ExtUITabstrip Create(UIHelperBase helperBase) { - UIHelper actualHelper = helperBase as UIHelper; - UIComponent optionsContainer = actualHelper.self as UIComponent; + public static ExtUITabstrip Create(UIHelper helper) { + UIComponent optionsContainer = helper.self as UIComponent; float orgOptsContainerWidth = optionsContainer.height; float orgOptsContainerHeight = optionsContainer.width; diff --git a/TLM/TLM/UI/MainMenu/MainMenuWindow.cs b/TLM/TLM/UI/MainMenu/MainMenuWindow.cs index 6c9528594..f44421d65 100644 --- a/TLM/TLM/UI/MainMenu/MainMenuWindow.cs +++ b/TLM/TLM/UI/MainMenu/MainMenuWindow.cs @@ -372,8 +372,7 @@ private void SetupControls_DebugLabels(UiBuilder builder, } } - // Hot Reload version label (debug only) - if (TrafficManagerMod.Instance.InGameHotReload) { + if (TrafficManagerMod.InGameHotReload) { // Hot Reload version label (debug only) string text = $"HOT RELOAD {Assembly.GetExecutingAssembly().GetName().Version}"; using (var hotReloadB = builder.Label(text)) {