├── TODO
├── API
├── package_icon.png
├── Properties
│ ├── AssemblyInfo.cs
│ └── Versioning.cs
├── API.csproj
├── SubscriptionWarning.cs
└── HarmonyHelper.cs
├── HarmonyMod
├── Resources
│ └── HarmonyLogo.png
├── Properties
│ ├── AssemblyInfo.cs
│ └── Versioning.cs
├── Patches
│ ├── PackageEntry_SetEntryPatch.cs
│ ├── PluginManager_OnDestroyPatch.cs
│ ├── UnityEngineDebug_LogException.cs
│ ├── UIView_ForwardExceptionPatch.cs
│ └── PluginManager_userModInstancePatch.cs
├── Source
│ ├── HarmonyModACLException.cs
│ ├── LoadingExtension.cs
│ ├── HarmonyModSupportException.cs
│ ├── AssemblyNameCompare.cs
│ ├── Installer.cs
│ ├── TextureResources.cs
│ ├── TypeExtensions.cs
│ ├── Harmony1StateTransfer.cs
│ ├── Handover.cs
│ └── Patcher.cs
└── HarmonyMod.csproj
├── nuget-repo
├── citiesharmony.api.1.0.3.nupkg
├── citiesharmony.api.1.0.4.nupkg
├── citiesharmony.api.1.0.5.nupkg
├── citiesharmony.api.1.0.6.nupkg
├── citiesharmony.api.2.0.0.nupkg
└── citiesharmony.api.2.0.0-rc.nupkg
├── PatchTooEarly_1_0_6
├── Class1.cs
└── PatchTooEarly_1_0_6.csproj
├── Compat-CitiesHarmony-Mod
├── Properties
│ ├── StrongName.pfx
│ └── AssemblyInfo.cs
├── Installer.cs
└── Compat-CitiesHarmony-Mod.csproj
├── BadMods
├── API-1.0.6
│ └── PatchTooEarly_1_0_6
│ │ ├── PatchTooEarly_1_0_6.csproj
│ │ └── PatchTooEarlyMod.cs
└── PatchTooEarly
│ ├── PatchTooEarly.csproj
│ └── PatchTooEarly.cs
├── .gitmodules
├── IAmAware
├── ModReportBase.cs
├── IAwareness0.csproj
├── Properties
│ └── AssemblyInfo.cs
└── IAmAware.cs
├── Test.Harmony
├── Test.Harmony.csproj
├── TesterMod.cs
└── HarmonyTests
│ ├── Specials.cs
│ ├── AttributePatchTest.cs
│ ├── ReturningStructs.cs
│ ├── ReturningStructMethods.cs
│ └── ACLTest.cs
├── ExampleMod
├── ExampleMod.csproj
├── ExampleMod.cs
└── Patcher.cs
├── .gitignore
├── README.md
└── MonoMod.Common.props
/TODO:
--------------------------------------------------------------------------------
1 | * Remove Hardcoded References paths C:\References
2 |
3 |
--------------------------------------------------------------------------------
/API/package_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/API/package_icon.png
--------------------------------------------------------------------------------
/HarmonyMod/Resources/HarmonyLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/HarmonyMod/Resources/HarmonyLogo.png
--------------------------------------------------------------------------------
/nuget-repo/citiesharmony.api.1.0.3.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/nuget-repo/citiesharmony.api.1.0.3.nupkg
--------------------------------------------------------------------------------
/nuget-repo/citiesharmony.api.1.0.4.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/nuget-repo/citiesharmony.api.1.0.4.nupkg
--------------------------------------------------------------------------------
/nuget-repo/citiesharmony.api.1.0.5.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/nuget-repo/citiesharmony.api.1.0.5.nupkg
--------------------------------------------------------------------------------
/nuget-repo/citiesharmony.api.1.0.6.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/nuget-repo/citiesharmony.api.1.0.6.nupkg
--------------------------------------------------------------------------------
/nuget-repo/citiesharmony.api.2.0.0.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/nuget-repo/citiesharmony.api.2.0.0.nupkg
--------------------------------------------------------------------------------
/PatchTooEarly_1_0_6/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace PatchTooEarly_1_0_6
4 | {
5 | public class Class1
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/nuget-repo/citiesharmony.api.2.0.0-rc.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/nuget-repo/citiesharmony.api.2.0.0-rc.nupkg
--------------------------------------------------------------------------------
/Compat-CitiesHarmony-Mod/Properties/StrongName.pfx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drok/Harmony-CitiesSkylines/HEAD/Compat-CitiesHarmony-Mod/Properties/StrongName.pfx
--------------------------------------------------------------------------------
/PatchTooEarly_1_0_6/PatchTooEarly_1_0_6.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/BadMods/API-1.0.6/PatchTooEarly_1_0_6/PatchTooEarly_1_0_6.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/API/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | [assembly: System.Reflection.AssemblyCompanyAttribute("Radu Hociung")]
2 | [assembly: System.Reflection.AssemblyCopyrightAttribute("Copyright 2021 Radu Hociung")]
3 | [assembly: System.Reflection.AssemblyDescriptionAttribute("Auto-installs and gives access to the Harmony Mod from the Steam Workshop")]
4 | [assembly: System.Reflection.AssemblyProductAttribute("Harmony.API")]
5 | [assembly: System.Reflection.AssemblyTitleAttribute("Lib.Harmony Helper for Cities: Skylines")]
6 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Harmony-v2"]
2 | path = Harmony-v2
3 | url = https://github.com/drok/Harmony
4 | branch = maintenance-v2
5 | [submodule "Compat-Harmony-2.0.0.9"]
6 | path = Compat-Harmony-2.0.0.9
7 | url = https://github.com/drok/Harmony
8 | branch = maintenance-2.0.0.9
9 | [submodule "Compat-Harmony-2.0.1.0"]
10 | path = Compat-Harmony-2.0.1.0
11 | url = https://github.com/drok/Harmony
12 | branch = maintenance-2.0.1.0
13 | [submodule "Compat-CitiesHarmony.Harmony-2.0.4.0"]
14 | path = Compat-CitiesHarmony.Harmony-2.0.4.0
15 | url = https://github.com/drok/Harmony
16 | branch = maintenance-CitiesHarmony.Harmony-2.0.4.0
17 | [submodule "MonoMod.Common"]
18 | path = MonoMod.Common
19 | url = https://github.com/drok/MonoMod.Common
20 | branch = maintenance-Harmony-CitiesSkylines
21 |
--------------------------------------------------------------------------------
/HarmonyMod/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: System.Reflection.AssemblyCompanyAttribute("Radu Hociung")]
4 | [assembly: System.Reflection.AssemblyProductAttribute("Modding Infrastructure")]
5 | [assembly: System.Reflection.AssemblyTitleAttribute("Harmony Mod for Cities Skylines")]
6 |
7 | /* Allow integration tests unrestricted access for testing */
8 | [assembly: InternalsVisibleTo("Test.Harmony, PublicKey=" +
9 | "00240000048000009400000006020000002400005253413100040000010001009d0f13cde5b126" +
10 | "c67d0c94873430cc171f8919863c6218a5bc1788a91caf6c197a851fdd4e5df5fe68726b5ca92a" +
11 | "cd2a47770cde3eb1538693a427a6c7591878b59dacc8fd24339f0e77f923ada3f80133f3a5b182" +
12 | "d7d04b16fb7bd02abff840b4b4ed9114463fef35c3437385205ebed7906a29ce6bd16a84e50129" +
13 | "8c8224ba")]
14 |
--------------------------------------------------------------------------------
/HarmonyMod/Patches/PackageEntry_SetEntryPatch.cs:
--------------------------------------------------------------------------------
1 | extern alias Harmony2;
2 | using Harmony2::HarmonyLib;
3 | using JetBrains.Annotations;
4 | using System;
5 |
6 | namespace HarmonyMod.MyPatches
7 | {
8 |
9 | [HarmonyPatch(typeof(PackageEntry), "SetEntry", new Type[] { typeof(EntryData), })]
10 | internal class PackageEntry_SetEntryPatch
11 | {
12 | ///
13 | /// when loading asset from a file, IAssetData.OnAssetLoaded() is called for all assets but the one that is loaded from the file.
14 | /// this postfix calls IAssetData.OnAssetLoaded() for asset loaded from file.
15 | ///
16 | [HarmonyPostfix]
17 | [UsedImplicitly]
18 | public static void Postfix(PackageEntry __instance, EntryData data)
19 | {
20 | (Mod.mainMod.userModInstance as Mod).report.OnContentManagerSetEntry(__instance, data.pluginInfo);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/HarmonyMod/Patches/PluginManager_OnDestroyPatch.cs:
--------------------------------------------------------------------------------
1 | extern alias Harmony2;
2 | using Harmony2::HarmonyLib;
3 | using JetBrains.Annotations;
4 | using System;
5 | using ColossalFramework.Plugins;
6 |
7 | namespace HarmonyMod.MyPatches
8 | {
9 |
10 | [HarmonyPatch(typeof(PluginManager), "OnDestroy", new Type[] {})]
11 | internal class PluginManager_OnDestroyPatch
12 | {
13 | [HarmonyPrefix]
14 | [UsedImplicitly]
15 | public static void Prefix(PluginManager __instance)
16 | {
17 | Mod.mainModInstance.OnPluginManagerDestroyStart();
18 | }
19 |
20 | [HarmonyFinalizer]
21 | [UsedImplicitly]
22 | public static Exception Finalizer(Exception __exception)
23 | {
24 | if (__exception is Exception)
25 | {
26 | UnityEngine.Debug.LogError($"[{Versioning.FULL_PACKAGE_NAME}] PluginManager.OnDestroy() threw {__exception.GetType().FullName}: {__exception.Message}\n{__exception.StackTrace}");
27 | }
28 | Mod.mainModInstance.OnPluginManagerDestroyDone();
29 | return null;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/IAmAware/ModReportBase.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Harmony for Cities Skylines
3 | * Copyright (C) 2021 Radu Hociung
4 | *
5 | * This program is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation; either version 2 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License along
16 | * with this program; if not, write to the Free Software Foundation, Inc.,
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 | */
19 |
20 | using System.Collections.Generic;
21 | using System.Reflection;
22 |
23 | namespace IAwareness
24 | {
25 | internal abstract class ModReportBase
26 | {
27 | public abstract HashSet missingAssemblies { get; }
28 | public abstract uint numProblems { get; }
29 | public abstract uint numProblemsCaused { get; }
30 | public abstract bool usesHarmony { get; }
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/HarmonyMod/Source/HarmonyModACLException.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Harmony for Cities Skylines
3 | * Copyright (C) 2021 Radu Hociung
4 | *
5 | * This program is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation; either version 2 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License along
16 | * with this program; if not, write to the Free Software Foundation, Inc.,
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 | */
19 |
20 | using System;
21 | using System.Collections.Generic;
22 | using System.Linq;
23 | using System.Text;
24 | using System.Reflection;
25 |
26 | namespace HarmonyMod
27 |
28 | {
29 | /// An exception caused by a user call to the library.
30 | ///
31 | [Serializable]
32 | public class HarmonyModACLException : Exception
33 | {
34 | public HarmonyModACLException(string message)
35 | : base(message)
36 | {
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Compat-CitiesHarmony-Mod/Installer.cs:
--------------------------------------------------------------------------------
1 | namespace CitiesHarmony
2 | {
3 | using HarmonyLib;
4 | using System.Diagnostics;
5 | using System.Reflection;
6 |
7 | public static class Installer {
8 |
9 | /* Installer.Run() is a legacy static method called by boformer's CitiesHarmony.API.HarmonyHelper,
10 | * various versions, so it must not be removed, for backward compatibility
11 | * If this method exists, it understands that the CitiesHarmony Mod is installed
12 | * */
13 | public static void Run() {
14 | var stack = new StackTrace();
15 | var lastCaller = stack.GetFrame(0).GetMethod();
16 | MethodBase caller = lastCaller;
17 | int assemblyDepth = 0;
18 | /* Search in the stack for the assembly that called
19 | * my caller(CitiesHarmony.API)
20 | */
21 | for (int i = 1; i < stack.FrameCount && assemblyDepth < 3; ++i)
22 | {
23 | caller = stack.GetFrame(i).GetMethod();
24 | if (lastCaller.DeclaringType.Assembly.FullName != caller.DeclaringType.Assembly.FullName)
25 | {
26 | lastCaller = caller;
27 | ++assemblyDepth;
28 | }
29 | }
30 |
31 | if (!Harmony.harmonyUsers.TryGetValue(caller.DeclaringType.Assembly.FullName, out var userStatus)) {
32 | Harmony.harmonyUsers[caller.DeclaringType.Assembly.FullName] = new Harmony.HarmonyUser() { instancesCreated = 0, checkBeforeUse = true, };
33 | }
34 | }
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/HarmonyMod/Source/LoadingExtension.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Harmony for Cities Skylines
3 | * Copyright (C) 2021 Radu Hociung
4 | *
5 | * This program is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation; either version 2 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License along
16 | * with this program; if not, write to the Free Software Foundation, Inc.,
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 | */
19 |
20 | using ICities;
21 |
22 | namespace HarmonyMod
23 | {
24 | class LoadingExtension : LoadingExtensionBase
25 | {
26 | public override void OnCreated(ILoading loading)
27 | {
28 | base.OnCreated(loading);
29 | #if TRACE
30 | UnityEngine.Debug.LogWarning($"[{Versioning.FULL_PACKAGE_NAME}] INFO: LoadingExtension.OnCreated(mode={loading.currentMode}, complete={loading.loadingComplete}");
31 | #endif
32 |
33 | }
34 |
35 | public override void OnReleased()
36 | {
37 | base.OnReleased();
38 | #if TRACE
39 | UnityEngine.Debug.LogWarning($"[{Versioning.FULL_PACKAGE_NAME}] INFO: LoadingExtension.OnReleased()");
40 | #endif
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Compat-CitiesHarmony-Mod/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Compat-CitiesHarmony-Mod")]
9 | [assembly: AssemblyDescription("Stub for Backward compatibility with boformer's CitiesHarmony mod")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Radu Hociung")]
12 | [assembly: AssemblyProduct("Compat-CitiesHarmony-Mod")]
13 | [assembly: AssemblyCopyright("Copyright Radu Hociung © 2021")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("d80afd7c-cb94-4696-b1f8-6255a5b07fb0")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/BadMods/PatchTooEarly/PatchTooEarly.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {EBC32B21-119F-4D23-9B31-61B4DEFFC16F}
4 | net35
5 | false
6 | ExampleMod
7 | 1.0.*
8 | false
9 |
10 |
11 |
12 | C:\References\Assembly-CSharp.dll
13 | False
14 |
15 |
16 | C:\References\ColossalManaged.dll
17 | False
18 |
19 |
20 | C:\References\ICities.dll
21 | False
22 |
23 |
24 | C:\References\UnityEngine.dll
25 | False
26 |
27 |
28 |
29 |
30 |
31 |
32 | none
33 | false
34 |
35 |
36 |
37 | $(LOCALAPPDATA)\Colossal Order\Cities_Skylines\Addons\Mods\$(ProjectName)\
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/API/Properties/Versioning.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | #if LABS || DEBUG
6 | [assembly: AssemblyVersion("0.0.0.0")]
7 | #else
8 | [assembly: AssemblyVersion("0.0.0.0")]
9 | [assembly: AssemblyFileVersion(CitiesHarmony.API.Versioning.MyFileVersion)]
10 | [assembly: AssemblyInformationalVersionAttribute(CitiesHarmony.API.Versioning.MyInformationalVersion)]
11 | #endif
12 |
13 | namespace CitiesHarmony.API
14 | {
15 | public static class Versioning
16 | {
17 | public const string PACKAGE_NAME = "Harmony.API";
18 | public const string MyFileVersion = "0.9.0";
19 | public const string MyInformationalVersion = MyFileVersion + POSTFIX;
20 | public const string FULL_PACKAGE_NAME = PACKAGE_NAME + " " + MyInformationalVersion;
21 | public const uint MyVersionNumber = 0x00090000;
22 |
23 | public struct Obsolescence
24 | {
25 | /* Versions at which various features will be disabled.
26 | * They should not be removed from the source code, because
27 | * they serve as documentation for how older versions worked.
28 | * The compiler with automatically remove the dead code.
29 | */
30 | }
31 |
32 | public static string VersionString(uint number)
33 | {
34 | return new System.Version(
35 | (int)((number >> 24) & 0xff),
36 | (int)((number >> 16) & 0xff),
37 | (int)((number >> 8) & 0xff),
38 | (int)(number & 0xff)).ToString();
39 | }
40 |
41 | public static bool IsObsolete(uint ver, string explanation)
42 | {
43 | bool obsolete = MyVersionNumber >= ver;
44 | if (!obsolete)
45 | {
46 | UnityEngine.Debug.LogWarning($"[{Versioning.FULL_PACKAGE_NAME}] WARNING '{explanation}' will be removed at release {VersionString(ver)}");
47 | }
48 | return obsolete;
49 | }
50 |
51 | #if DEBUG
52 | const string POSTFIX = "-DEBUG";
53 | #elif BETA
54 | const string POSTFIX = "-BETA";
55 | #else
56 | const string POSTFIX = "";
57 | #endif
58 | }
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/HarmonyMod/Patches/UnityEngineDebug_LogException.cs:
--------------------------------------------------------------------------------
1 | extern alias Harmony2;
2 | using Harmony2::HarmonyLib;
3 | using JetBrains.Annotations;
4 | using ColossalFramework.UI;
5 | using System;
6 |
7 | namespace HarmonyMod.MyPatches
8 | {
9 |
10 | [HarmonyPatch(typeof(UnityEngine.Debug), "LogException", new Type[] { typeof(Exception), })]
11 | [HarmonyPriority(Priority.First)]
12 | internal class UnityEngineDebug_LogException
13 | {
14 | ///
15 | /// when loading asset from a file, IAssetData.OnAssetLoaded() is called for all assets but the one that is loaded from the file.
16 | /// this postfix calls IAssetData.OnAssetLoaded() for asset loaded from file.
17 | ///
18 | [HarmonyPrefix]
19 | [UsedImplicitly]
20 | public static bool Prefix(Exception exception)
21 | {
22 | /* Return false if the Plugin report was successful
23 | * Allows up to 3 (MAX_EXCEPTION_PROMPTS_PER_MOD) ExceptionPanel popups per mod,
24 | * suppressing the rest.
25 | *
26 | * All exceptions are logged and reported in the Harmony report.
27 | */
28 | Mod.mainModInstance?.report.TryReportPlugin(exception);
29 | return true;
30 | }
31 | }
32 |
33 | [HarmonyPatch(typeof(UnityEngine.Debug), "LogException", new Type[] { typeof(Exception), typeof(UnityEngine.Object),})]
34 | [HarmonyPriority(Priority.First)]
35 | internal class UnityEngineDebug_LogException_withObject
36 | {
37 | ///
38 | /// when loading asset from a file, IAssetData.OnAssetLoaded() is called for all assets but the one that is loaded from the file.
39 | /// this postfix calls IAssetData.OnAssetLoaded() for asset loaded from file.
40 | ///
41 | [HarmonyPrefix]
42 | [UsedImplicitly]
43 | public static bool Prefix(Exception exception, UnityEngine.Object context)
44 | {
45 | /* Return false if the Plugin report was successful
46 | * Allows up to 3 (MAX_EXCEPTION_PROMPTS_PER_MOD) ExceptionPanel popups per mod,
47 | * suppressing the rest.
48 | *
49 | * All exceptions are logged and reported in the Harmony report.
50 | */
51 | Mod.mainModInstance?.report.TryReportPlugin(exception);
52 | return true;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Test.Harmony/Test.Harmony.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {47363338-1B57-467C-93B9-DD9FB293AF27}
4 | net35
5 | false
6 | Test.Harmony
7 | 1.0.*
8 | false
9 | 0.9.0;1.0.3;1.0.4;1.0.5;1.0.6;2.0.0
10 |
11 |
12 | false
13 | true
14 | Properties\StrongName.pfx
15 |
16 |
17 | false
18 | full
19 | true
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | C:\References\Assembly-CSharp.dll
32 | False
33 |
34 |
35 | C:\References\ColossalManaged.dll
36 | False
37 |
38 |
39 | C:\References\IAmAware-0.0.1.0.dll
40 |
41 |
42 | C:\References\ICities.dll
43 | False
44 |
45 |
46 | C:\References\UnityEngine.dll
47 | False
48 |
49 |
50 |
51 |
52 | $(LOCALAPPDATA)\Colossal Order\Cities_Skylines\Addons\Mods\$(ProjectName)-$(Configuration)\
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/BadMods/PatchTooEarly/PatchTooEarly.cs:
--------------------------------------------------------------------------------
1 | using CitiesHarmony.API;
2 | using HarmonyLib;
3 | using ICities;
4 | using System.Reflection;
5 |
6 | namespace ExampleMod {
7 | public class Mod : IUserMod {
8 | // You can add Harmony 2.0.0.9 as a dependency, but make sure that 0Harmony.dll is not copied to the output directory!
9 | // (0Harmony.dll is provided by CitiesHarmony workshop item)
10 |
11 | // Also make sure that HarmonyLib is not referenced in any way in your IUserMod implementation!
12 | // Instead, apply your patches from a separate static patcher class!
13 | // (otherwise it will fail to instantiate the type when CitiesHarmony is not installed)
14 |
15 | public string Name => "Harmony 2 Example";
16 | public string Description => "Patches SimulationManager.CreateRelay and LoadingManager.MetaDataLoaded";
17 |
18 | public void OnEnabled() {
19 | HarmonyHelper.DoOnHarmonyReady(() => Patcher.PatchAll());
20 | }
21 |
22 | public void OnDisabled() {
23 | if (HarmonyHelper.IsHarmonyInstalled) Patcher.UnpatchAll();
24 | }
25 | }
26 |
27 | public static class Patcher {
28 | private const string HarmonyId = "boformer.Harmony2Example";
29 |
30 | private static bool patched = false;
31 |
32 | public static void PatchAll() {
33 | if (patched) return;
34 |
35 | UnityEngine.Debug.Log("Harmony 2 Example: Patching...");
36 |
37 | patched = true;
38 |
39 | // Apply your patches here!
40 | // Harmony.DEBUG = true;
41 | var harmony = new Harmony("boformer.Harmony2Example");
42 | harmony.PatchAll(Assembly.GetExecutingAssembly());
43 | }
44 |
45 | public static void UnpatchAll() {
46 | if (!patched) return;
47 |
48 | var harmony = new Harmony(HarmonyId);
49 | harmony.UnpatchAll(HarmonyId);
50 |
51 | patched = false;
52 |
53 | UnityEngine.Debug.Log("Harmony 2 Example: Reverted...");
54 | }
55 | }
56 |
57 | // Random example patch
58 | [HarmonyPatch(typeof(SimulationManager), "CreateRelay")]
59 | public static class SimulationManagerCreateRelayPatch {
60 | public static void Prefix() {
61 | UnityEngine.Debug.Log("CreateRelay Prefix");
62 | }
63 | }
64 |
65 | // Random example patch
66 | [HarmonyPatch(typeof(LoadingManager), "MetaDataLoaded")]
67 | public static class LoadingManagerMetaDataLoadedPatch {
68 | public static void Prefix() {
69 | UnityEngine.Debug.Log("MetaDataLoaded Prefix");
70 | }
71 | }
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/BadMods/API-1.0.6/PatchTooEarly_1_0_6/PatchTooEarlyMod.cs:
--------------------------------------------------------------------------------
1 | using CitiesHarmony.API;
2 | using HarmonyLib;
3 | using ICities;
4 | using System.Reflection;
5 |
6 | namespace PatchTooEarly_1_0_6
7 | {
8 | public class PatchTooEarlyMod : IUserMod
9 | {
10 | // You can add Harmony 2.0.0.9 as a dependency, but make sure that 0Harmony.dll is not copied to the output directory!
11 | // (0Harmony.dll is provided by CitiesHarmony workshop item)
12 |
13 | // Also make sure that HarmonyLib is not referenced in any way in your IUserMod implementation!
14 | // Instead, apply your patches from a separate static patcher class!
15 | // (otherwise it will fail to instantiate the type when CitiesHarmony is not installed)
16 |
17 | public string Name => "Harmony 2 Example";
18 | public string Description => "Patches SimulationManager.CreateRelay and LoadingManager.MetaDataLoaded";
19 |
20 | public void OnEnabled()
21 | {
22 | HarmonyHelper.DoOnHarmonyReady(() => Patcher.PatchAll());
23 | }
24 |
25 | public void OnDisabled()
26 | {
27 | if (HarmonyHelper.IsHarmonyInstalled) Patcher.UnpatchAll();
28 | }
29 | }
30 |
31 | public static class Patcher
32 | {
33 | private const string HarmonyId = "boformer.Harmony2Example";
34 |
35 | private static bool patched = false;
36 |
37 | public static void PatchAll()
38 | {
39 | if (patched) return;
40 |
41 | UnityEngine.Debug.Log("Harmony 2 Example: Patching...");
42 |
43 | patched = true;
44 |
45 | // Apply your patches here!
46 | // Harmony.DEBUG = true;
47 | var harmony = new Harmony("boformer.Harmony2Example");
48 | harmony.PatchAll(Assembly.GetExecutingAssembly());
49 | }
50 |
51 | public static void UnpatchAll()
52 | {
53 | if (!patched) return;
54 |
55 | var harmony = new Harmony(HarmonyId);
56 | harmony.UnpatchAll(HarmonyId);
57 |
58 | patched = false;
59 |
60 | UnityEngine.Debug.Log("Harmony 2 Example: Reverted...");
61 | }
62 | }
63 |
64 | // Random example patch
65 | [HarmonyPatch(typeof(SimulationManager), "CreateRelay")]
66 | public static class SimulationManagerCreateRelayPatch
67 | {
68 | public static void Prefix()
69 | {
70 | UnityEngine.Debug.Log("CreateRelay Prefix");
71 | }
72 | }
73 |
74 | // Random example patch
75 | [HarmonyPatch(typeof(LoadingManager), "MetaDataLoaded")]
76 | public static class LoadingManagerMetaDataLoadedPatch
77 | {
78 | public static void Prefix()
79 | {
80 | UnityEngine.Debug.Log("MetaDataLoaded Prefix");
81 | }
82 | }
83 | }
84 |
85 |
--------------------------------------------------------------------------------
/HarmonyMod/Source/HarmonyModSupportException.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Harmony for Cities Skylines
3 | * Copyright (C) 2021 Radu Hociung
4 | *
5 | * This program is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation; either version 2 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License along
16 | * with this program; if not, write to the Free Software Foundation, Inc.,
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 | */
19 |
20 | using System;
21 | using System.Collections.Generic;
22 | using System.Linq;
23 | using System.Text;
24 | using System.Reflection;
25 |
26 | namespace HarmonyMod
27 |
28 | {
29 | /// An exception caused by a user call to the library.
30 | ///
31 | [Serializable]
32 | public class HarmonyModSupportException : Exception
33 | {
34 | public class UnsupportedAssembly
35 | {
36 | public UnsupportedAssembly(Assembly assembly, bool pullRequired)
37 | {
38 | this.assembly = assembly;
39 | isPullRequestRequired = pullRequired;
40 | }
41 |
42 | public bool isPullRequestRequired { get; private set; }
43 | public string Message
44 | {
45 | get
46 | {
47 | if (isPullRequestRequired)
48 | {
49 | return $"HarmonyLib version {assembly.GetName().Version} requires" +
50 | $" a compatibility patch. Submit it at https://github.com/drok/Harmony-CitiesSkylines/issues";
51 | } else
52 | {
53 | return $"HarmonyLib version {assembly.GetName().Version} is not supported by HarmonyMod yet. Please enter" +
54 | $" a feature request at https://github.com/drok/Harmony-CitiesSkylines/issues";
55 | }
56 | }
57 | }
58 | public Assembly assembly { get; private set; }
59 | }
60 | public List unsupportedAssemblies { get; private set; }
61 |
62 | /// The Harmony Instance that was in use when the exception occured
63 | ///
64 | internal HarmonyModSupportException(List assemblies)
65 | : base()
66 | {
67 | unsupportedAssemblies = assemblies;
68 | }
69 | public override string Message {
70 | get {
71 | string list = null;
72 |
73 | unsupportedAssemblies.ForEach((a) =>
74 | {
75 | list += (list is null ? "" : ", ") + a.assembly.GetName().Name + "[" + a.assembly.GetName().Version + "]";
76 | });
77 |
78 | return "Unsupported HarmonyLib implementations: " + list;
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Compat-CitiesHarmony-Mod/Compat-CitiesHarmony-Mod.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D80AFD7C-CB94-4696-B1F8-6255A5B07FB0}
8 | Library
9 | Properties
10 | Compat_CitiesHarmony_Mod
11 | CitiesHarmony
12 | v3.5
13 | 512
14 | true
15 | 7.3
16 | bin\$(Configuration)\
17 |
18 |
19 | false
20 | true
21 | Properties\StrongName.pfx
22 |
23 |
24 | false
25 | full
26 | true
27 |
28 |
29 | true
30 | none
31 | false
32 |
33 |
34 | bin\Beta\
35 | true
36 | AnyCPU
37 | 7.3
38 |
39 |
40 | false
41 | true
42 | Properties\StrongName.pfx
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {608c45f0-e72f-4436-b9ea-4d108b42313b}
59 | Harmony-v2
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/HarmonyMod/Source/AssemblyNameCompare.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Harmony for Cities Skylines
3 | * Copyright (C) 2021 Radu Hociung
4 | *
5 | * This program is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation; either version 2 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License along
16 | * with this program; if not, write to the Free Software Foundation, Inc.,
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 | */
19 |
20 | using System.Reflection;
21 | using System.Collections.Generic;
22 | using System.Linq;
23 |
24 | namespace HarmonyMod
25 | {
26 |
27 | internal class SameAssemblyName : IEqualityComparer
28 | {
29 |
30 | readonly bool m_compareVersion;
31 | readonly bool m_compareToken;
32 | readonly bool m_compareName;
33 | readonly bool m_compareCulture;
34 | readonly bool m_satisfyStrongName;
35 |
36 | public SameAssemblyName()
37 | {
38 | m_compareName = true;
39 | m_compareToken = true;
40 | m_compareVersion = true;
41 | m_satisfyStrongName = false;
42 | }
43 | public SameAssemblyName(bool compareVersion, bool compareToken, bool compareName, bool compareCulture)
44 | {
45 | m_compareVersion = compareVersion;
46 | m_compareToken = compareToken;
47 | m_compareName = compareName;
48 | m_compareCulture = compareCulture;
49 | m_satisfyStrongName = true;
50 | }
51 |
52 | public int GetHashCode(AssemblyName n)
53 | {
54 | int hashcode = n.Name.GetHashCode();
55 | int keyhash = n.GetPublicKeyToken() == null ? 0 : n.GetPublicKeyToken().ToString().GetHashCode();
56 | hashcode += keyhash;
57 | hashcode += n.Version.GetHashCode();
58 | return hashcode;
59 | }
60 | public bool Equals(AssemblyName a, AssemblyName b)
61 | {
62 | bool result = (!m_compareName || a.Name.Equals(b.Name)) &&
63 | (!m_compareVersion || a.Version.Equals(b.Version)) &&
64 | (!m_compareToken || (a.GetPublicKeyToken().Length == 0 && (!m_satisfyStrongName || b.GetPublicKeyToken().Length == 0)) ||
65 | (a.GetPublicKeyToken().SequenceEqual(b.GetPublicKeyToken()))) &&
66 | (!m_compareCulture || a.CultureInfo.Equals(b.CultureInfo));
67 | return result;
68 | }
69 |
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/HarmonyMod/Patches/UIView_ForwardExceptionPatch.cs:
--------------------------------------------------------------------------------
1 | extern alias Harmony2;
2 | using Harmony2::HarmonyLib;
3 | using JetBrains.Annotations;
4 | using ColossalFramework.UI;
5 | using System;
6 |
7 | namespace HarmonyMod.MyPatches
8 | {
9 |
10 | [HarmonyPatch(typeof(UIView), "ForwardException", new Type[] { typeof(Exception), })]
11 | [HarmonyPriority(Priority.First)]
12 | internal class UIView_ForwardExceptionPatch
13 | {
14 | ///
15 | /// when loading asset from a file, IAssetData.OnAssetLoaded() is called for all assets but the one that is loaded from the file.
16 | /// this postfix calls IAssetData.OnAssetLoaded() for asset loaded from file.
17 | ///
18 | [HarmonyPrefix]
19 | [UsedImplicitly]
20 | public static bool Prefix(Exception e)
21 | {
22 | /* Return false if the Plugin report was successful
23 | * Allows up to 3 (MAX_EXCEPTION_PROMPTS_PER_MOD) ExceptionPanel popups per mod,
24 | * suppressing the rest.
25 | *
26 | * All exceptions are logged and reported in the Harmony report.
27 | */
28 | return (Mod.mainMod.userModInstance as Mod).report.TryReportPlugin(e) <= Report.MAX_EXCEPTION_PROMPTS_PER_MOD;
29 | }
30 | }
31 |
32 | /* TODO: Hook UnityEngine.DebugLogHandler.LogException(), but it gets the same exceptions as UIView.
33 | * Also possibly hook Starter.OnDestroy (not declared) to capture logs to the last breath of the app.
34 | *
35 | * public void LogException(Exception exception, Object context)
36 | * {
37 | * DebugLogHandler.Internal_LogException(exception, context);
38 | * }
39 | *
40 | * NullReferenceException: Object reference not set to an instance of an object
41 | * at HarmonyMod.Mod.get_assemblyLocations () [0x00001] in U:\proj\skylines\CitiesHarmony\HarmonyMod\Source\Mod.cs:81
42 | * at HarmonyMod.Report.GetModList (Boolean showOnlyProblems) [0x001d3] in U:\proj\skylines\CitiesHarmony\HarmonyMod\Source\Report.cs:181
43 | * at HarmonyMod.Report.OutputReport (HarmonyMod.Mod self, Boolean final, System.String stepName) [0x00001] in U:\proj\skylines\CitiesHarmony\HarmonyMod\Source\Report.cs:277
44 | * at HarmonyMod.Mod.ICities.ILoadingExtension.OnReleased () [0x00001] in U:\proj\skylines\CitiesHarmony\HarmonyMod\Source\Mod.cs:275
45 | * at LoadingWrapper.OnLoadingExtensionsReleased () [0x00000] in :0
46 | * UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
47 | * UnityEngine.DebugLogHandler:LogException(Exception, Object)
48 | * UnityEngine.Logger:LogException(Exception, Object)
49 | * UnityEngine.Debug:LogException(Exception)
50 | * LoadingWrapper:OnLoadingExtensionsReleased()
51 | * LoadingWrapper:Release()
52 | * LoadingManager:ReleaseRelay()
53 | * LoadingManager:OnDestroy()
54 | *
55 | */
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/ExampleMod/ExampleMod.csproj:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 | {961A70CE-8B6C-4950-A845-E215F07D0CC2}
27 | net35
28 | false
29 | ExampleMod
30 | 1.0.*
31 | false
32 | Debug;Release
33 |
34 |
35 |
36 | C:\References\Assembly-CSharp.dll
37 | False
38 |
39 |
40 | C:\References\ColossalManaged.dll
41 | False
42 |
43 |
44 | C:\References\ICities.dll
45 | False
46 |
47 |
48 | C:\References\UnityEngine.dll
49 | False
50 |
51 |
52 |
53 |
54 |
55 |
56 | none
57 | false
58 |
59 |
60 |
61 | $(LOCALAPPDATA)\Colossal Order\Cities_Skylines\Addons\Mods\$(ProjectName)\
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/HarmonyMod/Patches/PluginManager_userModInstancePatch.cs:
--------------------------------------------------------------------------------
1 | extern alias Harmony2;
2 | using Harmony2::HarmonyLib;
3 | using JetBrains.Annotations;
4 | using System;
5 | using System.Linq;
6 | using System.Collections.Generic;
7 | using System.Reflection;
8 | using ColossalFramework.Plugins;
9 | using static ColossalFramework.Plugins.PluginManager;
10 |
11 |
12 | namespace HarmonyMod.MyPatches
13 | {
14 |
15 | [HarmonyPatch(typeof(PluginInfo), "get_userModInstance", new Type[] { })]
16 | internal class PluginManager_userModInstancePatch
17 | {
18 | [HarmonyPrefix]
19 | [UsedImplicitly]
20 | public static bool Prefix(PluginInfo __instance, ref object __result)
21 | {
22 | var inst = Traverse.Create(__instance);
23 | var userModInstance = inst.Field