From fdec8d6b213942e5a2cd6fc13d6ce4af956ed31e Mon Sep 17 00:00:00 2001 From: Aubergine Date: Tue, 3 Mar 2020 03:14:43 +0000 Subject: [PATCH 1/2] Lifecycle part 1 - IUserMod Small first step splitting some stuff out of the IUserMod to get feedback as to whether the general approach is heading in right direction. --- TLM/TLM/Lifecycle.cs | 110 +++++++++++++++++++++++++ TLM/TLM/TLM.csproj | 6 +- TLM/TLM/Temp.cs | 75 +++++++++++++++++ TLM/TLM/TrafficManagerMod.cs | 76 ++++++++--------- TLM/TLM/UI/Localization/Translation.cs | 6 -- 5 files changed, 222 insertions(+), 51 deletions(-) create mode 100644 TLM/TLM/Lifecycle.cs create mode 100644 TLM/TLM/Temp.cs diff --git a/TLM/TLM/Lifecycle.cs b/TLM/TLM/Lifecycle.cs new file mode 100644 index 000000000..c1e7e69dd --- /dev/null +++ b/TLM/TLM/Lifecycle.cs @@ -0,0 +1,110 @@ +namespace TrafficManager { + using CSUtil.Commons; + using ICities; + using TrafficManager.State; + using TrafficManager.UI; + + /// + /// This class manages the lifecycle of TMPE mod. + /// + /// It also serves as an initial design draft for HarmonyLifecycle mod. + /// Code commenting is verbose; that would get moved to docs somwhere. + /// + public class Lifecycle { // : ILifecycle + + /// + /// The Lifecycle instance; may be null. + /// + private static Lifecycle instance_; + + /// + /// Gets the instance (creates one if necessary). + /// + public static Lifecycle Instance => instance_ ?? (instance_ = new Lifecycle()); + + /// + /// Called when the mod is enabled in one of the following ways: + /// + /// * Manually in Content Manager > Mods + /// * Already enabled when Cities.exe starts + /// * Auto-enabled upon subscription, by the Mod Autoenabler mod + /// * Hot reload of a dev build. + /// + /// + /// If true, the mod was enabled due to hot reload. + public void OnEnabled(bool hotLoad) { + if (hotLoad) { + Log.Info("HOT RELOAD"); + } else { + Temp.LogEnvironmentDetails(); + } + } + + /// + /// Called when locale needs updating: + /// + /// * Before OnSettings(), if not already called + /// * When user changes game language. + /// + /// Note that lanauge mods often use non-standard language codes such as: + /// + /// * jaex --> ja + /// * zh-cn --> zh + /// * kr --> ko. + /// + /// + /// A string representing the language code. + public void OnLocaleChange(string locale) { + Translation.HandleGameLocaleChange(); + } + + /// + /// Called when the game wants the mod to create its settings UI: + /// + /// * When mod is first enabled + /// * Each time a city is loaded. + /// + /// The parameter can be used to adapt your settings + /// screen depending on whether it's in-game or not. + /// + /// + /// The instance used to create the UI. + /// If true, the in-game settings screen should be created. + public void OnSettings(UIHelperBase helper, bool inGame) { + // todo: instead of `inGame` bool, should we use an enum (or whatever) to + // differentiate between in-game, in scneario, in editor, etc? + Options.MakeSettings(helper); + } + + /// + /// Called at an appropriate time to perform compatibility checks: + /// + /// * PluginManager has processed all mods + /// * Intro screens have completed; UIView is available + /// * App localisation services are available + /// * NOT loading, unloading or exiting + /// * NOT in game or editor. + /// + /// + /// Return true if environment is compatible, otherwise false. + public bool OnCompatibilityCheck() { + // todo: should we pass in game version as a `Version`? + return Temp.CheckCompatibility(); + } + + /// + /// Called when the mod is disabled in one of the following ways: + /// + /// * Manually in Content Manager > Mods + /// * Unsubscribed while enabled + /// * Hot reload of dev build causes hot unload of current build + /// * Game exit to desktop. + /// + /// + /// If true, the mod was enabled due to hot reload. + public void OnDisabled(bool hotUnload) { + Log.Info("TM:PE disabled."); + } + + } +} diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index 396552b8a..dad1e6cfb 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -1,4 +1,4 @@ - + @@ -143,12 +143,14 @@ + + @@ -525,7 +527,7 @@ - + diff --git a/TLM/TLM/Temp.cs b/TLM/TLM/Temp.cs new file mode 100644 index 000000000..d7a76b79d --- /dev/null +++ b/TLM/TLM/Temp.cs @@ -0,0 +1,75 @@ +namespace TrafficManager { + using CSUtil.Commons; + using System; + using System.Reflection; + using TrafficManager.Util; + + /// + /// This class is a temporary place to put a bunch of stuff until a better place is found for it. + /// + /// Much of this stuff will be replaced as part of PR #699. + /// + public class Temp { + + /// + /// Logs some info about TMPE build, mono version, etc. + /// + public static void LogEnvironmentDetails() { + LogBuildDetails(); + LogTmpeGuid(); + LogMonoVersion(); + } + + /// + /// Log TMPE build info and what game ver it expects. + /// + public static void LogBuildDetails() { + Log.InfoFormat( + "TM:PE enabled. Version {0}, Build {1} {2} for game version {3}.{4}.{5}-f{6}", + TrafficManagerMod.VersionString, + Assembly.GetExecutingAssembly().GetName().Version, + TrafficManagerMod.BRANCH, + TrafficManagerMod.GAME_VERSION_A, + TrafficManagerMod.GAME_VERSION_B, + TrafficManagerMod.GAME_VERSION_C, + TrafficManagerMod.GAME_VERSION_BUILD); + } + + /// + /// Log TMPE Guid. + /// + public static void LogTmpeGuid() { + Log.InfoFormat( + "Enabled TM:PE has GUID {0}", + Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId); + } + + /// + /// Log Mono version. + /// + public static void LogMonoVersion() { + // Log Mono version + Type monoRt = Type.GetType("Mono.Runtime"); + if (monoRt != null) { + MethodInfo displayName = monoRt.GetMethod( + "GetDisplayName", + BindingFlags.NonPublic | BindingFlags.Static); + if (displayName != null) { + Log.InfoFormat("Mono version: {0}", displayName.Invoke(null, null)); + } + } + } + + /// + /// Run compatibility checker. + /// + /// + /// Returns false if issues found, otherwise true. + public static bool CheckCompatibility() { + ModsCompatibilityChecker mcc = new ModsCompatibilityChecker(); + mcc.PerformModCheck(); + return true; // ideally this would return false if there are compatibility issues (#699 will sort that) + } + + } +} diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index a5525ceb8..a7a4412ed 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -48,73 +48,63 @@ public class TrafficManagerMod : IUserMod { internal static bool InGame() => SceneManager.GetActiveScene().name == "Game"; + internal static bool listeningToLocaleChange_ = false; + [UsedImplicitly] public void OnEnabled() { - Log.InfoFormat( - "TM:PE enabled. Version {0}, Build {1} {2} for game version {3}.{4}.{5}-f{6}", - VersionString, - Assembly.GetExecutingAssembly().GetName().Version, - BRANCH, - GAME_VERSION_A, - GAME_VERSION_B, - GAME_VERSION_C, - GAME_VERSION_BUILD); - Log.InfoFormat( - "Enabled TM:PE has GUID {0}", - Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId); - - // check for incompatible mods - if (UIView.GetAView() != null) { - // when TM:PE is enabled in content manager - CheckForIncompatibleMods(); - } else { - // or when game first loads if TM:PE was already enabled - LoadingManager.instance.m_introLoaded += CheckForIncompatibleMods; - } + Instance = this; + InGameHotReload = InGame(); - // Log Mono version - Type monoRt = Type.GetType("Mono.Runtime"); - if (monoRt != null) { - MethodInfo displayName = monoRt.GetMethod( - "GetDisplayName", - BindingFlags.NonPublic | BindingFlags.Static); - if (displayName != null) { - Log.InfoFormat("Mono version: {0}", displayName.Invoke(null, null)); + Lifecycle.Instance.OnEnabled(InGameHotReload); + + if (!InGameHotReload) { + // check for incompatible mods + if (UIView.GetAView() != null) { + // when TM:PE is enabled in content manager + Lifecycle.Instance.OnCompatibilityCheck(); + } else { + // or when game first loads if TM:PE was already enabled + LoadingManager.instance.m_introLoaded += CheckForIncompatibleMods; } } - - Instance = this; - InGameHotReload = InGame(); } [UsedImplicitly] public void OnDisabled() { - Log.Info("TM:PE disabled."); LoadingManager.instance.m_introLoaded -= CheckForIncompatibleMods; - LocaleManager.eventLocaleChanged -= Translation.HandleGameLocaleChange; - Translation.IsListeningToGameLocaleChanged = false; // is this necessary? + LocaleManager.eventLocaleChanged -= GameLocaleChanged; - if (InGame() && LoadingExtension.Instance != null) { + bool hotUnload = InGame() && LoadingExtension.Instance != null; + + if (hotUnload) { //Hot reload Unloading LoadingExtension.Instance.OnLevelUnloading(); LoadingExtension.Instance.OnReleased(); } + + Lifecycle.Instance.OnDisabled(hotUnload); + Instance = null; } [UsedImplicitly] public void OnSettingsUI(UIHelperBase helper) { - // Note: This bugs out if done in OnEnabled(), hence doing it here instead. - if (!Translation.IsListeningToGameLocaleChanged) { - Translation.IsListeningToGameLocaleChanged = true; - LocaleManager.eventLocaleChanged += new LocaleManager.LocaleChangedHandler(Translation.HandleGameLocaleChange); + if (!listeningToLocaleChange_) { + listeningToLocaleChange_ = true; + LocaleManager.eventLocaleChanged += new LocaleManager.LocaleChangedHandler(GameLocaleChanged); + GameLocaleChanged(); // call on first use } - Options.MakeSettings(helper); + + Lifecycle.Instance.OnSettings(helper, InGame()); + } + + private static void GameLocaleChanged() { + Lifecycle.Instance.OnLocaleChange(LocaleManager.instance.language); } private static void CheckForIncompatibleMods() { - ModsCompatibilityChecker mcc = new ModsCompatibilityChecker(); - mcc.PerformModCheck(); + LoadingManager.instance.m_introLoaded -= CheckForIncompatibleMods; + Lifecycle.Instance.OnCompatibilityCheck(); } } } diff --git a/TLM/TLM/UI/Localization/Translation.cs b/TLM/TLM/UI/Localization/Translation.cs index ba72f8c9a..a91f36dc5 100644 --- a/TLM/TLM/UI/Localization/Translation.cs +++ b/TLM/TLM/UI/Localization/Translation.cs @@ -98,12 +98,6 @@ public class Translation { public static Localization.LookupTable AICar => LoadingExtension.TranslationDatabase.aiCarLookup_; - /// - /// Gets or sets a value indicating whether we're currently listening to the event fired when user changes game langauge. - /// The event is hooked in and unhooked in . - /// - public static bool IsListeningToGameLocaleChanged { get; set; } = false; - /// /// Gets or sets a value indicating the current lanugage to use for translations. /// Note: Don't access directly, instead use . From c789443544d7006530ebfdabe34d6962a8caa502 Mon Sep 17 00:00:00 2001 From: Aubergine Date: Tue, 3 Mar 2020 13:14:12 +0000 Subject: [PATCH 2/2] move game version warning to temp class --- TLM/TLM/Lifecycle.cs | 2 + TLM/TLM/LoadingExtension.cs | 67 +-------------------------------- TLM/TLM/Temp.cs | 75 +++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 66 deletions(-) diff --git a/TLM/TLM/Lifecycle.cs b/TLM/TLM/Lifecycle.cs index c1e7e69dd..eeae5fc60 100644 --- a/TLM/TLM/Lifecycle.cs +++ b/TLM/TLM/Lifecycle.cs @@ -73,6 +73,7 @@ public void OnLocaleChange(string locale) { public void OnSettings(UIHelperBase helper, bool inGame) { // todo: instead of `inGame` bool, should we use an enum (or whatever) to // differentiate between in-game, in scneario, in editor, etc? + // or have separate methods, eg. OnMainSettings(), OnGameSettings(), OnEditorSettings()? Options.MakeSettings(helper); } @@ -92,6 +93,7 @@ public bool OnCompatibilityCheck() { return Temp.CheckCompatibility(); } + /// /// Called when the mod is disabled in one of the following ways: /// diff --git a/TLM/TLM/LoadingExtension.cs b/TLM/TLM/LoadingExtension.cs index cb7c9e4bb..9e5e36139 100644 --- a/TLM/TLM/LoadingExtension.cs +++ b/TLM/TLM/LoadingExtension.cs @@ -392,72 +392,7 @@ public override void OnLevelLoaded(LoadMode mode) { case SimulationManager.UpdateMode.NewGameFromMap: case SimulationManager.UpdateMode.NewGameFromScenario: case SimulationManager.UpdateMode.LoadGame: { - if (BuildConfig.applicationVersion != BuildConfig.VersionToString( - TrafficManagerMod.GAME_VERSION, - false)) - { - string[] majorVersionElms = BuildConfig.applicationVersion.Split('-'); - string[] versionElms = majorVersionElms[0].Split('.'); - uint versionA = Convert.ToUInt32(versionElms[0]); - uint versionB = Convert.ToUInt32(versionElms[1]); - uint versionC = Convert.ToUInt32(versionElms[2]); - - Log.Info($"Detected game version v{BuildConfig.applicationVersion}"); - - bool isModTooOld = TrafficManagerMod.GAME_VERSION_A < versionA || - (TrafficManagerMod.GAME_VERSION_A == versionA && - TrafficManagerMod.GAME_VERSION_B < versionB); - // || (TrafficManagerMod.GameVersionA == versionA - // && TrafficManagerMod.GameVersionB == versionB - // && TrafficManagerMod.GameVersionC < versionC); - - bool isModNewer = TrafficManagerMod.GAME_VERSION_A < versionA || - (TrafficManagerMod.GAME_VERSION_A == versionA && - TrafficManagerMod.GAME_VERSION_B > versionB); - // || (TrafficManagerMod.GameVersionA == versionA - // && TrafficManagerMod.GameVersionB == versionB - // && TrafficManagerMod.GameVersionC > versionC); - - if (isModTooOld) { - string msg = string.Format( - "Traffic Manager: President Edition detected that you are running " + - "a newer game version ({0}) than TM:PE has been built for ({1}). " + - "Please be aware that TM:PE has not been updated for the newest game " + - "version yet and thus it is very likely it will not work as expected.", - BuildConfig.applicationVersion, - BuildConfig.VersionToString(TrafficManagerMod.GAME_VERSION, false)); - - Log.Error(msg); - Singleton.instance.m_ThreadingWrapper.QueueMainThread( - () => { - UIView.library - .ShowModal("ExceptionPanel") - .SetMessage( - "TM:PE has not been updated yet", - msg, - false); - }); - } else if (isModNewer) { - string msg = string.Format( - "Traffic Manager: President Edition has been built for game version {0}. " + - "You are running game version {1}. Some features of TM:PE will not " + - "work with older game versions. Please let Steam update your game.", - BuildConfig.VersionToString(TrafficManagerMod.GAME_VERSION, false), - BuildConfig.applicationVersion); - - Log.Error(msg); - Singleton - .instance.m_ThreadingWrapper.QueueMainThread( - () => { - UIView.library - .ShowModal("ExceptionPanel") - .SetMessage( - "Your game should be updated", - msg, - false); - }); - } - } + Temp.CheckGameVersion(); IsGameLoaded = true; break; diff --git a/TLM/TLM/Temp.cs b/TLM/TLM/Temp.cs index d7a76b79d..87c354bc3 100644 --- a/TLM/TLM/Temp.cs +++ b/TLM/TLM/Temp.cs @@ -1,4 +1,6 @@ namespace TrafficManager { + using ColossalFramework; + using ColossalFramework.UI; using CSUtil.Commons; using System; using System.Reflection; @@ -71,5 +73,78 @@ public static bool CheckCompatibility() { return true; // ideally this would return false if there are compatibility issues (#699 will sort that) } + /// + /// Checks to see if game version is what TMPE expects, and if not warns users. + /// + /// This will be replaced as part of #699. + /// + public static void CheckGameVersion() { + if (BuildConfig.applicationVersion != BuildConfig.VersionToString( + TrafficManagerMod.GAME_VERSION, + false)) { + string[] majorVersionElms = BuildConfig.applicationVersion.Split('-'); + string[] versionElms = majorVersionElms[0].Split('.'); + uint versionA = Convert.ToUInt32(versionElms[0]); + uint versionB = Convert.ToUInt32(versionElms[1]); + uint versionC = Convert.ToUInt32(versionElms[2]); + + Log.Info($"Detected game version v{BuildConfig.applicationVersion}"); + + bool isModTooOld = TrafficManagerMod.GAME_VERSION_A < versionA || + (TrafficManagerMod.GAME_VERSION_A == versionA && + TrafficManagerMod.GAME_VERSION_B < versionB); + // || (TrafficManagerMod.GameVersionA == versionA + // && TrafficManagerMod.GameVersionB == versionB + // && TrafficManagerMod.GameVersionC < versionC); + + bool isModNewer = TrafficManagerMod.GAME_VERSION_A < versionA || + (TrafficManagerMod.GAME_VERSION_A == versionA && + TrafficManagerMod.GAME_VERSION_B > versionB); + // || (TrafficManagerMod.GameVersionA == versionA + // && TrafficManagerMod.GameVersionB == versionB + // && TrafficManagerMod.GameVersionC > versionC); + + if (isModTooOld) { + string msg = string.Format( + "Traffic Manager: President Edition detected that you are running " + + "a newer game version ({0}) than TM:PE has been built for ({1}). " + + "Please be aware that TM:PE has not been updated for the newest game " + + "version yet and thus it is very likely it will not work as expected.", + BuildConfig.applicationVersion, + BuildConfig.VersionToString(TrafficManagerMod.GAME_VERSION, false)); + + Log.Error(msg); + Singleton.instance.m_ThreadingWrapper.QueueMainThread( + () => { + UIView.library + .ShowModal("ExceptionPanel") + .SetMessage( + "TM:PE has not been updated yet", + msg, + false); + }); + } else if (isModNewer) { + string msg = string.Format( + "Traffic Manager: President Edition has been built for game version {0}. " + + "You are running game version {1}. Some features of TM:PE will not " + + "work with older game versions. Please let Steam update your game.", + BuildConfig.VersionToString(TrafficManagerMod.GAME_VERSION, false), + BuildConfig.applicationVersion); + + Log.Error(msg); + Singleton + .instance.m_ThreadingWrapper.QueueMainThread( + () => { + UIView.library + .ShowModal("ExceptionPanel") + .SetMessage( + "Your game should be updated", + msg, + false); + }); + } + } + } + } }