diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner.ruleset b/DuplicateAssemblyScanner/CodeAnalysis.ruleset
similarity index 93%
rename from DuplicateAssemblyScanner/DuplicateAssemblyScanner.ruleset
rename to DuplicateAssemblyScanner/CodeAnalysis.ruleset
index ce59629..f82ac1a 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner.ruleset
+++ b/DuplicateAssemblyScanner/CodeAnalysis.ruleset
@@ -1,5 +1,5 @@
-
+
@@ -39,4 +39,4 @@
-
\ No newline at end of file
+
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner.sln b/DuplicateAssemblyScanner/DuplicateAssemblyScanner.sln
index 30e3cc7..8d3dc29 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner.sln
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner.sln
@@ -5,6 +5,11 @@ VisualStudioVersion = 16.0.29806.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateAssemblyScanner", "DuplicateAssemblyScanner\DuplicateAssemblyScanner.csproj", "{EFBA373B-88B2-427A-8396-6F9D56C89F74}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E06C7F71-21C0-4D8E-920F-F8139B39A120}"
+ ProjectSection(SolutionItems) = preProject
+ VersionInfo.cs = VersionInfo.cs
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/DuplicateAssemblyScanner.csproj b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/DuplicateAssemblyScanner.csproj
index 621424b..9b6a5b1 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/DuplicateAssemblyScanner.csproj
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/DuplicateAssemblyScanner.csproj
@@ -11,7 +11,7 @@
DuplicateAssemblyScanner
v3.5
512
- true
+ false
latest
@@ -22,6 +22,7 @@
DEBUG;TRACE
prompt
4
+ ..\CodeAnalysis.ruleset
pdbonly
@@ -30,6 +31,7 @@
TRACE
prompt
4
+ ..\CodeAnalysis.ruleset
~/Library/Application Support/Steam/
@@ -72,19 +74,30 @@
+
+ Properties\VersionInfo.cs
+
+
+
+
+
+
+
+
+
set "DEPLOYDIR=$(LOCALAPPDATA)\Colossal Order\Cities_Skylines\Addons\Mods\$(TargetName)\"
del "%25DEPLOYDIR%25DuplicateAssemblyScanner.dll"
-xcopy /y "$(TargetDir)DuplicateAssemblyScanner.dll" "%25DEPLOYDIR%25"
+xcopy /y "$(TargetDir)DuplicateAssemblyScanner.dll" "%25DEPLOYDIR%25"
set DEPLOYDIR=
-
\ No newline at end of file
+
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Properties/AssemblyInfo.cs b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Properties/AssemblyInfo.cs
index 42a713f..24ae080 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Properties/AssemblyInfo.cs
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Properties/AssemblyInfo.cs
@@ -1,36 +1,6 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
+using System.Reflection;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("DuplicateAssemblyScanner")]
-[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("DuplicateAssemblyScanner")]
-[assembly: AssemblyCopyright("Copyright © 2020")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("efba373b-88b2-427a-8396-6f9d56c89f74")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/UserMod.cs b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/UserMod.cs
index 7df37c3..73b4690 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/UserMod.cs
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/UserMod.cs
@@ -1,32 +1,51 @@
namespace DuplicateAssemblyScanner {
- using ColossalFramework.UI;
using DuplicateAssemblyScanner.Util;
using ICities;
using JetBrains.Annotations;
- using UnityEngine.SceneManagement;
+ ///
+ /// The main mod class which the game instantiates when the mod is enabled.
+ ///
public class UserMod : IUserMod {
+ ///
+ /// Gets mod name shown in content manager and options screens.
+ /// Version defined in Solution Items > VersionInfo.cs file.
+ ///
[UsedImplicitly]
- public string Name => "DAS";
+ public string Name => $"DAS v{typeof(UserMod).Assembly.GetName().Version.ToString(3)}";
+ ///
+ /// Gets mod description shown in content manager.
+ ///
[UsedImplicitly]
public string Description => "Scans for duplicate assemblies in the app domain, which can cause bugs.";
+ ///
+ /// Called when mod is enabled.
+ ///
[UsedImplicitly]
public void OnEnabled() {
Log.Info("Enabled");
}
+ ///
+ /// Called when settings UI is required.
+ ///
+ ///
+ /// Helper for creating UI.
[UsedImplicitly]
public void OnSettingsUI(UIHelperBase helper) {
Log.Info("SettingsUI");
Settings.CreateUI(helper);
}
+ ///
+ /// Called when mod is disabled.
+ ///
[UsedImplicitly]
public void OnDisabled() {
Log.Info("Disabled");
}
}
-}
\ No newline at end of file
+}
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Assemblies.cs b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Assemblies.cs
index b6c94c6..c8debc1 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Assemblies.cs
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Assemblies.cs
@@ -8,6 +8,9 @@ namespace DuplicateAssemblyScanner.Util {
using System.Reflection;
using static ColossalFramework.Plugins.PluginManager;
+ ///
+ /// Scans for duplicate assemblies and, where found, attempts to work out which mods they are from.
+ ///
public class Assemblies {
///
@@ -47,7 +50,7 @@ public static Dictionary> Scan(out bool duplicatesFound) {
} else {
results.Add(name, new List() {
- { ver }
+ { ver },
});
}
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Log.cs b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Log.cs
index 432dd6b..b454af0 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Log.cs
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Log.cs
@@ -1,27 +1,73 @@
namespace DuplicateAssemblyScanner.Util {
using System.Diagnostics;
using System.IO;
+ using System.Reflection;
using UnityEngine;
+ ///
+ /// A simple logging class.
+ ///
+ /// When mod activates, it creates a log file in same location as `output_log.txt`.
+ /// Mac users: It will be in the Cities app contents.
+ ///
public class Log {
- public static readonly string LogFileName = "DuplicateAssemblyScanner.log";
- private enum LogLevel {
- Debug,
- Info,
- Error
- }
+ ///
+ /// File name for log file.
+ ///
+ public static readonly string LogFileName = $"{typeof(Log).Assembly.GetName().Name}.log";
+ ///
+ /// Full path and file name of log file.
+ ///
private static readonly string LogFilePath = Path.Combine(Application.dataPath, LogFileName);
+ ///
+ /// Set to true to include timestamp in log entries.
+ ///
+ private static readonly bool TimeStamp = false;
+
+ ///
+ /// Stopwatch used if is true.
+ ///
+ private static readonly Stopwatch Timer;
+
+ ///
+ /// Initializes static members of the class.
+ /// Resets log file on startup.
+ ///
static Log() {
try {
if (File.Exists(LogFilePath)) {
File.Delete(LogFilePath);
}
- } catch { }
+
+ if (TimeStamp) {
+ Timer = Stopwatch.StartNew();
+ }
+
+ AssemblyName details = typeof(Log).Assembly.GetName();
+ Info($"{details.Name} v{details.Version.ToString()}", true);
+ } catch {
+ // ignore
+ }
}
+ ///
+ /// Log levels. Also output in log file.
+ ///
+ private enum LogLevel {
+ Debug,
+ Info,
+ Error,
+ }
+
+ ///
+ /// Logs debug trace, only in DEBUG builds.
+ ///
+ ///
+ /// Log entry text.
+ /// If true will copy to the main game log file.
[Conditional("DEBUG")]
public static void Debug(string message, bool copyToGameLog = false) {
LogToFile(message, LogLevel.Debug);
@@ -30,6 +76,12 @@ public static void Debug(string message, bool copyToGameLog = false) {
}
}
+ ///
+ /// Logs info message.
+ ///
+ ///
+ /// Log entry text.
+ /// If true will copy to the main game log file.
public static void Info(string message, bool copyToGameLog = false) {
LogToFile(message, LogLevel.Info);
if (copyToGameLog) {
@@ -37,6 +89,12 @@ public static void Info(string message, bool copyToGameLog = false) {
}
}
+ ///
+ /// Logs error message and also outputs a stack trace.
+ ///
+ ///
+ /// Log entry text.
+ /// If true will copy to the main game log file.
public static void Error(string message, bool copyToGameLog = true) {
LogToFile(message, LogLevel.Error);
if (copyToGameLog) {
@@ -44,17 +102,31 @@ public static void Error(string message, bool copyToGameLog = true) {
}
}
- private static void LogToFile(string log, LogLevel level) {
+ ///
+ /// Write a message to log file.
+ ///
+ ///
+ /// Log entry text.
+ /// Logging level. If set to a stack trace will be appended.
+ private static void LogToFile(string message, LogLevel level) {
try {
using (StreamWriter w = File.AppendText(LogFilePath)) {
- w.Write("{0, -9}", $"[{level.ToString()}] ");
- w.WriteLine(log);
+ w.Write("{0, -8}", $"[{level.ToString()}] ");
+
+ if (TimeStamp) {
+ w.Write("{0, 15}", Timer.ElapsedTicks + " | ");
+ }
+
+ w.WriteLine(message);
+
if (level == LogLevel.Error) {
w.WriteLine(new StackTrace().ToString());
w.WriteLine();
}
}
- } catch { }
+ } catch {
+ // ignore
+ }
}
}
}
\ No newline at end of file
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Settings.cs b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Settings.cs
index b6da1cf..d483cb0 100644
--- a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Settings.cs
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/Util/Settings.cs
@@ -3,34 +3,20 @@ namespace DuplicateAssemblyScanner.Util {
using ICities;
using System.Collections.Generic;
+ ///
+ /// Generates the settings screen based on cached results from the assembly scanner.
+ ///
public class Settings {
///
/// Cache of assembly scan results.
///
- internal static Dictionary> cacheDictionary_;
+ private static Dictionary> cacheDictionary_;
///
/// Cache of whether duplicates were found by the scan.
///
- internal static bool cacheDuplicates_;
-
- ///
- /// Get (cached) results of assembly scan.
- ///
- ///
- /// Will be true if duplicates found.
- /// Dictionary of assembly lists keyed by assembly name.
- internal static Dictionary> CacheScanResults(out bool duplicatesFound) {
-
- if (cacheDictionary_ == null) {
- cacheDictionary_ = Assemblies.Scan(out bool issues);
- cacheDuplicates_ = issues;
- }
-
- duplicatesFound = cacheDuplicates_;
- return cacheDictionary_;
- }
+ private static bool cacheDuplicates_;
///
/// Generate the options screen listing all the duplicates (if found).
@@ -64,11 +50,34 @@ public static void CreateUI(UIHelperBase helper) {
checkbox.isEnabled = false;
}
}
-
}
}
}
- internal static void NoOp(bool _) { }
+ ///
+ /// A dummy click handler for checkboxes. Does nothing.
+ ///
+ ///
+ /// Ignored paramter.
+ internal static void NoOp(bool _) {
+ // do nothing
+ }
+
+ ///
+ /// Get (cached) results of assembly scan.
+ ///
+ ///
+ /// Will be true if duplicates found.
+ /// Dictionary of assembly lists keyed by assembly name.
+ private static Dictionary> CacheScanResults(out bool duplicatesFound) {
+
+ if (cacheDictionary_ == null) {
+ cacheDictionary_ = Assemblies.Scan(out bool issues);
+ cacheDuplicates_ = issues;
+ }
+
+ duplicatesFound = cacheDuplicates_;
+ return cacheDictionary_;
+ }
}
}
diff --git a/DuplicateAssemblyScanner/DuplicateAssemblyScanner/packages.config b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/packages.config
new file mode 100644
index 0000000..5011e19
--- /dev/null
+++ b/DuplicateAssemblyScanner/DuplicateAssemblyScanner/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/DuplicateAssemblyScanner/VersionInfo.cs b/DuplicateAssemblyScanner/VersionInfo.cs
new file mode 100644
index 0000000..a14c2dc
--- /dev/null
+++ b/DuplicateAssemblyScanner/VersionInfo.cs
@@ -0,0 +1,27 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// Assembly name - both must have same value
+[assembly: AssemblyTitle("DuplicateAssemblyScanner")]
+[assembly: AssemblyProduct("DuplicateAssemblyScanner")]
+
+// Description - can be empty string
+[assembly: AssemblyDescription("Scans for duplicate assemblies in the app domain, which can cause bugs.")]
+
+// Ownership information
+[assembly: AssemblyCompany("Cities Skylines Mods")]
+[assembly: AssemblyCopyright("MIT")]
+[assembly: AssemblyTrademark("")]
+
+// Culture information (usually leave blank)
+[assembly: AssemblyCulture("")]
+
+// Do not change.
+[assembly: ComVisible(false)]
+
+// Version information for an assembly consists of the following four values:
+// Major Version
+// Minor Version
+// Build Number
+// Revision (* = auto)
+[assembly: AssemblyVersion("1.1.0.*")]