Skip to content
This repository has been archived by the owner on Mar 29, 2022. It is now read-only.

Commit

Permalink
add mod options menu
Browse files Browse the repository at this point in the history
add diagnostics capabilities to mod
  • Loading branch information
Tyler-IN committed Apr 4, 2020
1 parent cbd6063 commit 464d2d1
Show file tree
Hide file tree
Showing 8 changed files with 516 additions and 7 deletions.
26 changes: 26 additions & 0 deletions src/CommunityPatch/CommunityPatch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>True</CopyLocalLockFileAssemblies>
<MSBuildWarningsAsMessages>MSB3277</MSBuildWarningsAsMessages>
<Configurations>Release;Debug</Configurations>
<Platforms>X64</Platforms>
Expand All @@ -27,11 +28,36 @@
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\Native\bin\Win64_Shipping_Client\TaleWorlds.*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\SandBox\bin\Win64_Shipping_Client\TaleWorlds.*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\SandBoxCore\bin\Win64_Shipping_Client\*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\StoryMode\bin\Win64_Shipping_Client\*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>

<ItemGroup>
<None Include="..\..\SubModule.xml">
<Link>SubModule.xml</Link>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Fody" Version="6.1.1" />
<PackageReference Include="HardwareProviders.CPU.Standard" Version="2.0.1" />
<PackageReference Include="HardwareProviders.Standard" Version="2.0.1" />
<PackageReference Include="ModuleInit.Fody" Version="2.1.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" />
<PackageReference Include="Tomlyn" Version="0.1.2" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Text;
using HardwareProviders;
using Helpers;
using TaleWorlds.Core;
using TaleWorlds.Engine;
using TaleWorlds.InputSystem;
using TaleWorlds.Library;
using TaleWorlds.Localization;
using TaleWorlds.MountAndBlade;
using TaleWorlds.MountAndBlade.GauntletUI;

namespace CommunityPatch {

public partial class CommunityPatchSubModule {

public static void CopyDiagnosticsToClipboard() {
var sb = new StringBuilder();

try {
sb.AppendLine("Recorded Unhandled Exceptions:");
var i = 0;
foreach (var exc in RecordedUnhandledExceptions) {
var excStr = RecordedUnhandledExceptions.ToString();
sb.Append(" ").Append(++i).Append(". ").AppendLine(excStr.Replace("\n", "\n "));
}

if (i == 0)
sb.AppendLine(" None.");
}
catch (Exception ex) {
sb.Append(" *** ERROR: ").Append(ex.GetType().Name).Append(": ").AppendLine(ex.Message);
}

sb.AppendLine();

try {
sb.AppendLine("Recorded First Chance Exceptions:");
var i = 0;
foreach (var exc in RecordedFirstChanceExceptions) {
var excStr = RecordedFirstChanceExceptions.ToString();
sb.Append(" ").Append(++i).Append(". ").AppendLine(excStr.Replace("\n", "\n "));
}

if (RecordFirstChanceExceptions) {
if (i == 0)
sb.AppendLine(" None recorded.");
}
else {
sb.AppendLine(" Recording disabled.");
}
}
catch (Exception ex) {
sb.Append(" *** ERROR: ").Append(ex.GetType().Name).Append(": ").AppendLine(ex.Message);
}

sb.AppendLine();

try {
sb.AppendLine("Modules Information:");
var i = 0;
foreach (var mi in ModuleInfo.GetModules()) {
sb.Append(" ").Append(++i).Append(". ").Append(mi.Id).Append(" ").Append(mi.Version.ToString());
if (mi.IsSelected)
sb.Append(" *Selected*");
sb.AppendLine();
sb.Append(" ").Append(mi.Name);
if (!string.IsNullOrWhiteSpace(mi.Alias))
sb.Append(" (").Append(mi.Alias).Append(")");
if (mi.IsOfficial)
sb.Append(" *Official*");
sb.AppendLine();
if (mi.DependedModuleIds.Count <= 0)
continue;

sb.Append(" ").AppendLine("Dependencies:");
var j = 0;
foreach (var dep in mi.DependedModuleIds)
sb.Append(" ").Append(++j).Append(". ").AppendLine(dep);
}
}
catch (Exception ex) {
sb.Append(" *** ERROR: ").Append(ex.GetType().Name).Append(": ").AppendLine(ex.Message);
}

sb.AppendLine();

try {
sb.AppendLine("Loaded SubModules:");
var i = 0;
foreach (var sm in Module.CurrentModule.SubModules)
sb.Append(" ").Append(++i).Append(". ").AppendLine(sm.GetType().AssemblyQualifiedName);
}
catch (Exception ex) {
sb.Append(" *** ERROR: ").Append(ex.GetType().Name).Append(": ").AppendLine(ex.Message);
}

sb.AppendLine();

try {
sb.AppendLine("System Info:");
sb.Append(" ").AppendLine(Utilities.GetPCInfo().Replace("\n", "\n "));
sb.Append($" GPU Memory: {Utilities.GetGPUMemoryMB()}MB").AppendLine();
sb.Append($" GC Allocated: {GC.GetTotalMemory(false)}B").AppendLine();
sb.Append($" Engine Memory Used: {Utilities.GetCurrentCpuMemoryUsageMB()}MB").AppendLine();
sb.Append(" GC Latency Mode: ").Append(GCSettings.IsServerGC ? "Server " : "Client ").AppendLine(GCSettings.LatencyMode.ToString());
sb.AppendFormat($" GC LOH Compact Mode: {GCSettings.LargeObjectHeapCompactionMode}").AppendLine();
sb.AppendFormat($" Operating System: {RuntimeInformation.OSDescription}").AppendLine();
sb.AppendFormat($" Framework Compatibility: {RuntimeInformation.FrameworkDescription}").AppendLine();
}
catch (Exception ex) {
sb.Append(" *** ERROR: ").Append(ex.GetType().Name).Append(": ").AppendLine(ex.Message);
}

try {
var cpus = HardwareProviders.CPU.Cpu.Discover();

sb.AppendLine(" CPU Info:");
for (var i = 0; i < cpus.Length; ++i) {
var cpu = cpus[i];
var coreCount = cpu.CoreClocks.Length;
sb.Append(" ").Append(i + 1).Append(". ").Append(cpu.Name).Append(" with ").Append(coreCount).AppendLine(" cores:");
for (var c = 0; c < coreCount; ++c) {
sb.Append(" ").Append(c + 1).Append(". ").Append(cpu.CoreClocks[c].Value).Append("MHz ")
.Append(cpu.CoreTemperatures[c].Value).Append("°C");
sb.AppendLine();
}
}
}
catch (Exception ex) {
sb.Append(" *** ERROR: ").Append(ex.GetType().Name).Append(": ").AppendLine(ex.Message);
}

sb.AppendLine();

try {
Input.SetClipboardText(sb.ToString());
ShowMessage("Diagnostics copied to system clipboard.");
}
catch (Exception ex) {
ShowMessage($"Writing to system clipboard failed!\n{ex.GetType().Name}: {ex.Message}");
}
}

}

}
20 changes: 15 additions & 5 deletions src/CommunityPatch/CommunityPatchSubModule.Logging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ public partial class CommunityPatchSubModule {

[PublicAPI]
[Conditional("TRACE")]
public static void Error(Exception ex, FormattableString msg = null) {
Error(msg);
public static void Error(Exception ex, string msg = null) {
if (msg != null)
Error(msg);

var st = new StackTrace(ex, true);
var f = st.GetFrame(0);
Expand All @@ -24,12 +25,21 @@ public static void Error(Exception ex, FormattableString msg = null) {

[PublicAPI]
[Conditional("TRACE")]
public static void Error(FormattableString msg = null) {
public static void Error(Exception ex, FormattableString msg)
=> Error(ex, FormattableString.Invariant(msg));

[PublicAPI]
[Conditional("TRACE")]
public static void Error(FormattableString msg)
=> Error(FormattableString.Invariant(msg));

[PublicAPI]
[Conditional("TRACE")]
public static void Error(string msg = null) {
if (msg == null)
return;

var msgStr = FormattableString.Invariant(msg);
Debugger.Log(3, "CommunityPatch", msgStr);
Debugger.Log(3, "CommunityPatch", msg);
}

[PublicAPI]
Expand Down
119 changes: 117 additions & 2 deletions src/CommunityPatch/CommunityPatchSubModule.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using TaleWorlds.Core;
using TaleWorlds.Localization;
using TaleWorlds.MountAndBlade;
using Module = TaleWorlds.MountAndBlade.Module;

namespace CommunityPatch {

[PublicAPI]
public partial class CommunityPatchSubModule : MBSubModuleBase {

internal static readonly LinkedList<Exception> RecordedFirstChanceExceptions
= new LinkedList<Exception>();

internal static readonly LinkedList<Exception> RecordedUnhandledExceptions
= new LinkedList<Exception>();

internal static readonly OptionsFile Options = new OptionsFile("CommunityPatch.txt");

internal static bool DisableIntroVideo {
get => Options.Get<bool>(nameof(DisableIntroVideo));
set => Options.Set(nameof(DisableIntroVideo), value);
}

internal static bool RecordFirstChanceExceptions {
get => Options.Get<bool>(nameof(RecordFirstChanceExceptions));
set => Options.Set(nameof(RecordFirstChanceExceptions), value);
}

public override void BeginGameStart(Game game)
=> base.BeginGameStart(game);

public override bool DoLoading(Game game)
=> base.DoLoading(game);

protected override void OnBeforeInitialModuleScreenSetAsRoot() {
var module = Module.CurrentModule;

{
// remove the space option that DeveloperConsole module adds
var spaceOpt = module.GetInitialStateOptionWithId("Space");
if (spaceOpt != null) {
var opts = module.GetInitialStateOptions()
.Where(opt => opt != spaceOpt).ToArray();
module.ClearStateOptions();
foreach (var opt in opts)
module.AddInitialStateOption(opt);
}
}

if (DisableIntroVideo) {
try {
typeof(Module)
.GetField("_splashScreenPlayed", BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(module, true);
}
catch (Exception ex) {
Error(ex, "Couldn't disable intro video.");
}
}

base.OnBeforeInitialModuleScreenSetAsRoot();
}

protected override void OnSubModuleLoad() {
var module = Module.CurrentModule;
module.AddInitialStateOption(new InitialStateOption(
"ModOptions",
new TextObject("Mod Options"),
10001,
ShowModOptions,
false
));
base.OnSubModuleLoad();
}

private static void ShowMessage(string msg)
=> InformationManager.DisplayMessage(new InformationMessage(msg));

private void ShowModOptions()
=> InformationManager.ShowMultiSelectionInquiry(new MultiSelectionInquiryData(
"Mod Options",
"Community Patch Mod Options:",
new List<InquiryElement> {
new InquiryElement(
nameof(DisableIntroVideo),
DisableIntroVideo ? "Enable Intro Videos" : "Disable Intro Videos",
null
),
new InquiryElement(
nameof(RecordedFirstChanceExceptions),
DisableIntroVideo ? "Record First Chance Exceptions" : "Ignore First Chance Exceptions",
null
),
new InquiryElement(
nameof(CopyDiagnosticsToClipboard),
"Copy Diagnostics to Clipboard",
null
)
},
true,
true,
"Apply",
"Return",
list => {
var selected = (string) list[0].Identifier;
switch (selected) {
case nameof(DisableIntroVideo):
DisableIntroVideo = !DisableIntroVideo;
ShowMessage($"Intro Videos: {(DisableIntroVideo ? "Disabled" : "Enabled")}.");
Options.Save();
break;
case nameof(RecordFirstChanceExceptions):
RecordFirstChanceExceptions = !RecordFirstChanceExceptions;
ShowMessage($"Record FCEs: {(RecordFirstChanceExceptions ? "Disabled" : "Enabled")}.");
Options.Save();
break;
case nameof(CopyDiagnosticsToClipboard):
CopyDiagnosticsToClipboard();
break;
default:
throw new NotImplementedException(selected);
}
}, null));

public override void OnGameInitializationFinished(Game game) {
var patchType = typeof(IPatch);
var patches = new LinkedList<IPatch>();

Print("BeginGameStart");

foreach (var type in typeof(CommunityPatchSubModule).Assembly.GetTypes()) {
if (!patchType.IsAssignableFrom(type))
continue;
Expand Down
3 changes: 3 additions & 0 deletions src/CommunityPatch/FodyWeavers.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ModuleInit />
</Weavers>
Loading

0 comments on commit 464d2d1

Please sign in to comment.