Skip to content
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
57a03f3
wip - refactoring
originalfoo Feb 13, 2020
ca5b9af
use string builder for logging in loop
originalfoo Feb 13, 2020
d1fefff
Now compiles, showing signs of life
originalfoo Feb 13, 2020
e9a399d
cleaned some cruft
originalfoo Feb 14, 2020
2c64dbb
tinkering with assembly scanner
originalfoo Feb 14, 2020
a757c28
DLC scanner added
originalfoo Feb 14, 2020
f472130
Tidy up, also added Match Day to DLC scanner
originalfoo Feb 14, 2020
7d4ec20
Trying to extract version/branch info from assemblies
originalfoo Feb 15, 2020
680ed97
Partially working reflection
originalfoo Feb 15, 2020
b679300
Almost completely working reflection!
originalfoo Feb 15, 2020
70001e1
Retreiving version and branch, but very crufty code
originalfoo Feb 15, 2020
f1f8948
Version and branch inspection working nicely now
originalfoo Feb 15, 2020
a81d7e3
Missing variable declaration in example
originalfoo Feb 15, 2020
385ddfd
mid-way through a refactor
originalfoo Feb 18, 2020
ea093f9
mid-refactor
originalfoo Feb 20, 2020
2d2dfd8
Should be able to resume launcher auto-continue (not tested yet)
originalfoo Feb 20, 2020
963c434
Merge branch 'master' into 697-608-439-compatibility-checker
originalfoo Feb 21, 2020
8d6b87f
Tested the auto-continue feature - works great (thanks krzychu!)
originalfoo Feb 21, 2020
a287def
Refactor and cleanup
originalfoo Feb 21, 2020
0e31dc8
mid-refactor of UI (still horrible broken mess)
originalfoo Feb 21, 2020
a488325
more refactoring
originalfoo Feb 23, 2020
062c393
Tweaks to mod checker
originalfoo Feb 25, 2020
d31a3c1
Add error logging
originalfoo Feb 25, 2020
5046b24
Minor tweaks
originalfoo Feb 26, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TLM/CSUtil.CameraControl
2 changes: 1 addition & 1 deletion TLM/OptionsFramework
5 changes: 2 additions & 3 deletions TLM/SharedAssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using System.Reflection;
using System.Runtime.CompilerServices;
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: AssemblyCompany("")]
[assembly: AssemblyCompany("https://TMPE.me")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
Expand All @@ -20,4 +19,4 @@
// Minor Version
// Build Number
// Revision
[assembly: AssemblyVersion("11.1.0.*")]
[assembly: AssemblyVersion("11.1.1.*")]
110 changes: 110 additions & 0 deletions TLM/TLM/Compatibility/Check/Assemblies.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
namespace TrafficManager.Compatibility.Check {
using ColossalFramework;
using ColossalFramework.Plugins;
using CSUtil.Commons;
using ICities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using static ColossalFramework.Plugins.PluginManager;
using TrafficManager.Util;

public class Assemblies {

/// <summary>
/// Default build string if we can't determine LABS, STABLE, or DEBUG.
/// </summary>
internal const string OBSOLETE = "OBSOLETE";

internal const string STABLE = "STABLE";

internal const string BROKEN = "BROKEN";

internal const string TMMOD = "TrafficManager.TrafficManagerMod";

internal static readonly Version VersionedByAssembly;

internal static readonly Version LinuxFanVersion;

/// <summary>
/// Initializes static members of the <see cref="Assemblies"/> class.
/// </summary>
static Assemblies() {
VersionedByAssembly = new Version(11, 1, 0);
LinuxFanVersion = new Version(10, 20);
}

public static bool Verify(/*out Dictionary<Assembly,Guid> results*/) {

Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly asm in assemblies) {
AssemblyName details = asm.GetName();
if (details.Name.Contains("TrafficManager")) {

try {
//Log.Info("--------------------------- extracting ver info ---------------");

if (ExtractVersionDetails(asm, out Version ver, out string build)) {
Log.InfoFormat(
"Assembly: {0} v{1} {2}",
details.Name,
ver.Build == -1 ? ver.ToString(2) : ver.ToString(3),
build);
}

} catch (Exception e) {
Log.Info("loop failed -----------");
Log.Error(e.ToString());
}
}
}
return true; // to do
}

internal static bool ExtractVersionDetails(Assembly asm, out Version ver, out string branch) {

ver = asm.GetName().Version;
branch = OBSOLETE;

Type type = asm.GetType(TMMOD);
object instance = Activator.CreateInstance(type);

if (ver < VersionedByAssembly) {
try {
if (MemberValue.TryGetMemberValue<string>(type, instance, "Version", out string dirty)) {
//Log.Info("Raw string: " + dirty);

// clean the raw string in to something that resembles a verison number
string clean = Regex.Match(dirty, @"[0-9]+(?:\.[0-9]+)+").Value;
//Log.Info("clean string: " + clean);

// parse in to Version instance
ver = new Version(clean);
}
}
catch {
Log.Warning("Unable to retrieve or parse 'Version' member");
}
}

try {
if (MemberValue.TryGetMemberValue<string>(type, instance, "BRANCH", out string val)) {
branch = val;
} else if (ver == LinuxFanVersion) { // temporary
branch = STABLE;
}
}
catch {
Log.Warning("Unable to retrieve or parse 'BRANCH' member");
}

(instance as IDisposable)?.Dispose();

return true;
}

}
}
50 changes: 50 additions & 0 deletions TLM/TLM/Compatibility/Check/DLCs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
namespace TrafficManager.Compatibility.Check {
using ColossalFramework.PlatformServices;
using CSUtil.Commons;
using System.Collections.Generic;
using System.Text;

/// <summary>
/// Scans for transit-affecting DLCs.
/// </summary>
public class DLCs {

/// <summary>
/// Scan for DLCs and log whether they are installed or not.
/// </summary>
public static void Verify() {

try {
Dictionary<uint, string> DLCs = new Dictionary<uint, string>() {
{ (uint)SteamHelper.DLC.AfterDarkDLC, "After Dark" },
{ (uint)SteamHelper.DLC.InMotionDLC, "Mass Transit" },
{ (uint)SteamHelper.DLC.SnowFallDLC, "Snowfall" },
{ (uint)SteamHelper.DLC.NaturalDisastersDLC, "Natural Disasters" },
{ (uint)SteamHelper.DLC.ParksDLC, "Park Life" },
{ (uint)SteamHelper.DLC.IndustryDLC, "Industries" },
{ (uint)SteamHelper.DLC.GreenCitiesDLC, "Green Cities" },
{ (uint)SteamHelper.DLC.Football, "Match Day" },
};

StringBuilder sb = new StringBuilder(500);

sb.Append("Transit-affecting DLCs [*] = Installed:\n\n");

string formatStr = " {0} {1}\n";
string strAsterisk = "*";
string strSpace = " ";

foreach (KeyValuePair<uint, string> dlc in DLCs) {
sb.AppendFormat(
formatStr,
PlatformService.IsDlcInstalled(dlc.Key) ? strAsterisk : strSpace,
dlc.Value);
}

Log.Info(sb.ToString());
}
catch {
}
}
}
}
168 changes: 168 additions & 0 deletions TLM/TLM/Compatibility/Check/Mods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
namespace TrafficManager.Compatibility.Check {
using ColossalFramework.Plugins;
using ColossalFramework;
using CSUtil.Commons;
using static ColossalFramework.Plugins.PluginManager;
using System;
using System.Collections.Generic;
using System.Text;
using TrafficManager.Compatibility.Enum;
using TrafficManager.Compatibility.Struct;
using TrafficManager.State;

/// <summary>
/// Scans for known incompatible mods as defined by <see cref="IncompatibleMods.List"/>.
/// </summary>
public class Mods {

// Strings for log entries
internal const string LOG_ENTRY_FORMAT = "{0} {1} {2} {3}\n";
internal const string MARKER_ENABLED = "*";
internal const string MARKER_BLANK = " ";
internal const string MARKER_TMPE = ">";
internal const string MARKER_CRITICAL = "C";
internal const string MARKER_MAJOR = "M";
internal const string MARKER_MINOR = "m";

internal const string LOCAL_MOD_STR = "(local)";
internal const string BUNDLED_MOD_STR = "(bundled)";

/// <summary>
/// Scans installed mods (local and workshop) looking for known incompatibilities.
/// </summary>
///
/// <param name="results">A dictionary issues found (will be empty if no issues).</param>
/// <param name="critical">Number of critical incompatibilities.</param>
/// <param name="major">Number of major incompatibilities.</param>
/// <param name="minor">Number of minor incompatibilities.</param>
/// <param name="tmpe">Number of non-obsolete TM:PE mods.</param>
///
/// <returns>Returns <c>true</c> if incompatible mods detected, otherwise <c>false</c>.</returns>
public static bool Verify(
out Dictionary<PluginInfo, ModDescriptor> results,
out int minor,
out int major,
out int critical,
out int tmpe) {

results = new Dictionary<PluginInfo, ModDescriptor>();

// current verification state
bool verified = true;

// check minor severity incompatibilities?
bool scanMinor = GlobalConfig.Instance.Main.ScanForKnownIncompatibleModsAtStartup;

// check disabled mods? note: Critical incompatibilities are always processed
bool scanDisabled = !GlobalConfig.Instance.Main.IgnoreDisabledMods;

// batch all logging in to a single log message
// 6000 chars is roughly 120 mods worth of logging
StringBuilder log = new StringBuilder(6000);

log.AppendFormat(
"Compatibility.Check.Mods.Verify() scanMinor={0}, scanDisabled={1}\n\n",
scanMinor,
scanDisabled);

// Variables for log file entries
string logWorkshopId;
string logIncompatible;

// problem counters
minor = major = critical = tmpe = 0;

PluginManager manager = Singleton<PluginManager>.instance;

List<PluginInfo> mods = new List<PluginInfo>(manager.modCount);

mods.AddRange(manager.GetPluginsInfo()); // normal mods
mods.AddRange(manager.GetCameraPluginInfos()); // camera scripts

// iterate plugins
foreach (PluginInfo mod in mods) {

try {
// Generate descriptor for the mod
ModDescriptor descriptor = mod;

// String to log for workshop id
logWorkshopId = mod.isBuiltin
? BUNDLED_MOD_STR
: descriptor.ModIsLocal
? LOCAL_MOD_STR
: descriptor.ModWorkshopId.ToString();

switch (descriptor.Incompatibility) {

case Severity.Critical:
logIncompatible = MARKER_CRITICAL;
verified = false;
++critical;
results.Add(mod, descriptor);
break;

case Severity.Major:
logIncompatible = MARKER_MAJOR;
if (mod.isEnabled || scanDisabled) {
verified = false;
++major;
results.Add(mod, descriptor);
}
break;

case Severity.Minor:
logIncompatible = MARKER_MINOR;
if (scanMinor && (mod.isEnabled || scanDisabled)) {
verified = false;
++minor;
results.Add(mod, descriptor);
}
break;

case Severity.TMPE:
logIncompatible = MARKER_TMPE;
++tmpe;
if (descriptor.AssemblyGuid != CompatibilityManager.SelfGuid) {
verified = false;
results.Add(mod, descriptor);
}
break;

default:
case Severity.None:
logIncompatible = MARKER_BLANK;
break;
}

log.AppendFormat(
LOG_ENTRY_FORMAT,
logIncompatible,
mod.isEnabled ? MARKER_ENABLED : MARKER_BLANK,
logWorkshopId.PadRight(12),
descriptor.ModName);

} catch (Exception e) {
Log.ErrorFormat(
"Error scanning {0}:\n{1}",
mod.modPath,
e.ToString());
}

} // foreach

log.AppendFormat(
"\n{0} Mod(s), *{1} enabled: {2} [C]ritical, {3} [M]ajor, {4} [m]inor, {5} > TM:PE\n",
manager.modCount,
manager.enabledModCount,
critical,
major,
minor,
tmpe);

Log.Info(log.ToString());

return verified;
}
}
}
29 changes: 29 additions & 0 deletions TLM/TLM/Compatibility/Check/Versions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace TrafficManager.Compatibility.Check {
using CSUtil.Commons;
using System;

/// <summary>
/// Checks version equality with added logging.
/// </summary>
public class Versions {

/// <summary>
/// Verifies that expected version matches actual verison.
///
/// Versions are matched on Major.minor.build. Revision is ignored.
/// </summary>
///
/// <param name="expected">The version you are expecting.</param>
/// <param name="actual">The actual version.</param>
///
/// <returns>Returns <c>true</c> if versions match, otherwise <c>false</c>.</returns>
public static bool Verify(Version expected, Version actual) {
Log.InfoFormat(
"Compatibility.Check.Versions.Verify({0}, {1})",
expected.ToString(3),
actual.ToString(3));

return expected == actual;
}
}
}
Loading