Skip to content

Commit 13cd4d2

Browse files
committed
Avalonia UI shell. Based on TKMM.
ImGui was very fun to use but ultimately, it's very intensive for what it is. It showed 3 panes and sat at 20% CPU usage on my 13700F. I'm not sure if my code was just bad or if this is how ImGui is.
1 parent e4e66f3 commit 13cd4d2

29 files changed

+455
-1191
lines changed

.vscode/launch.json

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
{
22
"version": "0.2.0",
33
"configurations": [
4+
{
5+
// Use IntelliSense to find out which attributes exist for C# debugging
6+
// Use hover for the description of the existing attributes
7+
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
8+
"name": ".NET Core Launch (console)",
9+
"type": "coreclr",
10+
"request": "launch",
11+
"preLaunchTask": "build",
12+
// If you have changed target frameworks, make sure to update the program path.
13+
"program": "${workspaceFolder}/src/UI/bin/Debug/net8.0/Volte.UI.dll",
14+
"args": [],
15+
"cwd": "${workspaceFolder}/src/UI/bin/Debug/net8.0",
16+
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
17+
"console": "integratedTerminal",
18+
"stopAtEntry": false
19+
},
420
{
521
// Use IntelliSense to find out which attributes exist for C# debugging
622
// Use hover for the description of the existing attributes
@@ -11,7 +27,7 @@
1127
"preLaunchTask": "build",
1228
// If you have changed target frameworks, make sure to update the program path.
1329
"program": "${workspaceFolder}/src/Bot/bin/Debug/net8.0/Volte.dll",
14-
"args": ["--ui"],
30+
"args": [],
1531
"cwd": "${workspaceFolder}/src/Bot/bin/Debug/net8.0",
1632
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
1733
"console": "integratedTerminal",

Volte.sln

+9-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.28721.148
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volte", "src\Bot\Volte.csproj", "{5D4A85B0-1326-4CA2-A26C-D646D9579342}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volte.UI", "src\UI\Volte.UI.csproj", "{70624B23-1B94-4801-9536-DD4FE86E6671}"
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volte.UI", "src\UI\Volte.UI.csproj", "{BCD42127-89FE-44F3-AD2F-D224ED94B03A}"
99
EndProject
1010
Global
1111
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -23,14 +23,14 @@ Global
2323
{5D4A85B0-1326-4CA2-A26C-D646D9579342}.Release|Any CPU.Build.0 = Release|Any CPU
2424
{5D4A85B0-1326-4CA2-A26C-D646D9579342}.Release|x64.ActiveCfg = Release|x64
2525
{5D4A85B0-1326-4CA2-A26C-D646D9579342}.Release|x64.Build.0 = Release|x64
26-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Debug|Any CPU.Build.0 = Debug|Any CPU
28-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Debug|x64.ActiveCfg = Debug|Any CPU
29-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Debug|x64.Build.0 = Debug|Any CPU
30-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Release|Any CPU.ActiveCfg = Release|Any CPU
31-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Release|Any CPU.Build.0 = Release|Any CPU
32-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Release|x64.ActiveCfg = Release|Any CPU
33-
{70624B23-1B94-4801-9536-DD4FE86E6671}.Release|x64.Build.0 = Release|Any CPU
26+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Debug|x64.ActiveCfg = Debug|Any CPU
29+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Debug|x64.Build.0 = Debug|Any CPU
30+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Release|x64.ActiveCfg = Release|Any CPU
33+
{BCD42127-89FE-44F3-AD2F-D224ED94B03A}.Release|x64.Build.0 = Release|Any CPU
3434
EndGlobalSection
3535
GlobalSection(SolutionProperties) = preSolution
3636
HideSolutionNode = FALSE

src/Bot/Commands/Modules/BotOwner/UICommand.cs

-21
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Volte.Entities;
2+
3+
#nullable enable
4+
5+
public class VolteLogEventArgs
6+
{
7+
public required LogSeverity Severity;
8+
public required LogSource Source;
9+
public required string Message;
10+
public required string[] PrintedLines;
11+
public required Exception? Error;
12+
}

src/Bot/Core/VolteBot.cs

+1-73
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
1-
using System.IO;
2-
using ImGuiNET;
3-
using Silk.NET.Maths;
4-
using Silk.NET.OpenGL.Extensions.ImGui;
5-
using Silk.NET.Windowing;
6-
using SixLabors.ImageSharp;
7-
using SixLabors.ImageSharp.PixelFormats;
81
using Volte.Commands.Text.Modules;
9-
using Volte.UI;
10-
using Image = SixLabors.ImageSharp.Image;
112

123
namespace Volte;
134

145
public class VolteBot
156
{
167
public static Task StartAsync()
178
{
18-
Console.Title = DefaultWindowOptions.Title;
9+
Console.Title = $"Volte {Version.InformationVersion}";
1910
Console.CursorVisible = false;
2011
return new VolteBot().LoginAsync();
2112
}
@@ -39,9 +30,6 @@ private async Task LoginAsync()
3930

4031
ServiceProvider = new ServiceCollection().AddAllServices().BuildServiceProvider();
4132

42-
if (Program.CommandLineArguments.TryGetValue("ui", out var sizeStr))
43-
CreateUi(sizeStr);
44-
4533
_client = ServiceProvider.Get<DiscordSocketClient>();
4634
_cts = ServiceProvider.Get<CancellationTokenSource>();
4735

@@ -96,64 +84,4 @@ public static async Task ShutdownAsync(DiscordSocketClient client, ServiceProvid
9684

9785
Environment.Exit(0);
9886
}
99-
100-
// WindowOptions.Default with custom title and larger base window
101-
public static readonly WindowOptions DefaultWindowOptions = new(
102-
isVisible: true,
103-
position: new Vector2D<int>(50, 50),
104-
size: new Vector2D<int>(1600, 900),
105-
framesPerSecond: 0,
106-
updatesPerSecond: 0.0,
107-
api: GraphicsAPI.Default,
108-
title: $"Volte {Version.InformationVersion}",
109-
windowState: WindowState.Normal,
110-
windowBorder: WindowBorder.Resizable,
111-
isVSync: true,
112-
shouldSwapAutomatically: true,
113-
videoMode: VideoMode.Default
114-
);
115-
116-
private static void CreateUi(string sizeStr)
117-
{
118-
var uiParams = GetUiParams(sizeStr.TryParse<int>(out var fsz) ? fsz : 17);
119-
120-
if (UiManager.TryCreateUi(uiParams, out var uiStartError))
121-
{
122-
UiManager.AddView(new VolteUiView());
123-
UiManager.StartThread("Volte UI Thread");
124-
}
125-
else Error(LogSource.UI, $"Could not create UI: {uiStartError!.Message}");
126-
}
127-
128-
private static readonly string[] UiFontResourceKeys = [ "Regular", "Bold", "BoldItalic", "Italic" ];
129-
130-
public static UiManager.CreateParams GetUiParams(int fontSize)
131-
{
132-
unsafe
133-
{
134-
return new UiManager.CreateParams
135-
{
136-
WindowIcon = loadIcon(),
137-
WOptions = DefaultWindowOptions,
138-
Theme = Spectrum.Dark,
139-
OnConfigureIo = _ =>
140-
{
141-
UiFontResourceKeys.ForEach(key =>
142-
{
143-
using var embeddedFont = Assembly.GetExecutingAssembly().GetManifestResourceStream(key);
144-
if (embeddedFont != null)
145-
UiManager.LoadFontFromStream(embeddedFont, key, fontSize);
146-
});
147-
}
148-
};
149-
}
150-
151-
Image<Rgba32> loadIcon()
152-
{
153-
using var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("VolteIcon");
154-
return iconStream == null
155-
? null
156-
: Image.Load<Rgba32>(iconStream);
157-
}
158-
}
15987
}

src/Bot/Helpers/Event.cs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System.Collections.Immutable;
2+
3+
namespace Volte.Helpers;
4+
5+
internal class Event<T>
6+
where T : class
7+
{
8+
private readonly object _subLock = new();
9+
private ImmutableArray<T> _subscriptions = [];
10+
11+
public bool HasSubscribers
12+
{
13+
get
14+
{
15+
lock (_subLock)
16+
return _subscriptions.Length != 0;
17+
}
18+
}
19+
20+
public IReadOnlyList<T> Subscriptions
21+
{
22+
get
23+
{
24+
lock (_subLock)
25+
return _subscriptions;
26+
}
27+
}
28+
29+
public void Add(T subscriber)
30+
{
31+
Guard.Require(subscriber, nameof(subscriber));
32+
lock (_subLock)
33+
_subscriptions = _subscriptions.Add(subscriber);
34+
}
35+
36+
public void Remove(T subscriber)
37+
{
38+
Guard.Require(subscriber, nameof(subscriber));
39+
lock (_subLock)
40+
_subscriptions = _subscriptions.Remove(subscriber);
41+
}
42+
}
43+
44+
internal static class EventExtensions
45+
{
46+
public static void Call(this Event<Action> eventHandler)
47+
=> eventHandler.Subscriptions.ForEach(x => x());
48+
49+
public static void Call<T>(this Event<Action<T>> eventHandler,
50+
T arg
51+
) => eventHandler.Subscriptions.ForEach(x => x(arg));
52+
53+
public static void Call<T1, T2>(this Event<Action<T1, T2>> eventHandler,
54+
T1 arg1, T2 arg2
55+
) => eventHandler.Subscriptions.ForEach(x => x(arg1, arg2));
56+
57+
public static void Call<T1, T2, T3>(this Event<Action<T1, T2, T3>> eventHandler,
58+
T1 arg1, T2 arg2, T3 arg3
59+
) => eventHandler.Subscriptions.ForEach(x => x(arg1, arg2, arg3));
60+
61+
public static void Call<T1, T2, T3, T4>(this Event<Action<T1, T2, T3, T4>> eventHandler,
62+
T1 arg1, T2 arg2, T3 arg3, T4 arg4
63+
) => eventHandler.Subscriptions.ForEach(x => x(arg1, arg2, arg3, arg4));
64+
65+
public static void Call<T1, T2, T3, T4, T5>(this Event<Action<T1, T2, T3, T4, T5>> eventHandler,
66+
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5
67+
) => eventHandler.Subscriptions.ForEach(x => x(arg1, arg2, arg3, arg4, arg5));
68+
}

src/Bot/Helpers/Logger.Internal.cs

+23-9
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ internal static void LogFileRestartNotice()
5555
{
5656
if (_logFileNoticePrinted || !(Config.EnabledFeatures?.LogToFile ?? false)) return;
5757

58-
GetRelevantLogPath().AppendAllText($"{Side}RESTARTING{Side}\n");
58+
GetLogFilePath(DateTime.Now).AppendAllText($"{Side}RESTARTING{Side}\n");
5959

6060
_logFileNoticePrinted = true;
6161
}
@@ -113,10 +113,14 @@ private static void Execute<TData>(LogSeverity s, LogSource src, string message,
113113
if (e != null)
114114
{
115115
e.SentryCapture(scope =>
116-
scope.AddBreadcrumb("This exception might not have been thrown, and may not be important; it is merely being logged."));
117-
Append(Environment.NewLine + (e.Message.IsNullOrEmpty() ? "No message provided" : e.Message) +
118-
Environment.NewLine + e.StackTrace,
119-
Color.IndianRed, ref content);
116+
scope.AddBreadcrumb("This exception might not have been thrown, and may not be important; it is merely being logged.")
117+
);
118+
119+
Append(errorString(), Color.IndianRed, ref content);
120+
121+
string errorString()
122+
=> Environment.NewLine + (e.Message.IsNullOrEmpty() ? "No message provided" : e.Message) +
123+
Environment.NewLine + e.StackTrace;
120124
}
121125

122126
if (Environment.NewLine != content[^1].ToString())
@@ -126,12 +130,22 @@ private static void Execute<TData>(LogSeverity s, LogSource src, string message,
126130
}
127131

128132
if (Config.EnabledFeatures?.LogToFile ?? false)
129-
GetRelevantLogPath().AppendAllText(content.ToString().TrimEnd('\n').Append("\n"));
133+
GetLogFilePath(DateTime.Now).AppendAllText(content.ToString().TrimEnd('\n').Append("\n"));
134+
135+
if (!_logEventHandler.HasSubscribers) return;
136+
137+
_logEventHandler.Call(new VolteLogEventArgs
138+
{
139+
Severity = s,
140+
Source = src,
141+
Message = message,
142+
PrintedLines = content.ToString().TrimEnd('\n').Split('\n', StringSplitOptions.RemoveEmptyEntries),
143+
Error = e
144+
});
130145
}
131146

132-
private static FilePath GetLogFilePath(DateTime date) => new FilePath("logs") / string.Intern($"{date.Month}-{date.Day}-{date.Year}.log");
133-
134-
private static FilePath GetRelevantLogPath() => GetLogFilePath(DateTime.Now);
147+
private static FilePath GetLogFilePath(DateTime date)
148+
=> new FilePath("logs") / string.Intern($"{date.Year}-{date.Month}-{date.Day}.log");
135149

136150
private static void Append(string m, Color c)
137151
{

src/Bot/Helpers/Logger.cs

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
using System.IO;
22
using System.Runtime.CompilerServices;
3-
using Color = System.Drawing.Color;
4-
using Optional = Gommon.Optional;
53

64
namespace Volte.Helpers;
75

86
public static partial class Logger
97
{
8+
public static event Action<VolteLogEventArgs> LogEvent
9+
{
10+
add => _logEventHandler.Add(value);
11+
remove => _logEventHandler.Remove(value);
12+
}
13+
14+
private static readonly Event<Action<VolteLogEventArgs>> _logEventHandler = new();
15+
1016
public static bool IsDebugLoggingEnabled => Config.DebugEnabled || Version.IsDevelopment;
1117

1218
public static void HandleLogEvent(LogEventArgs args) =>
@@ -71,8 +77,9 @@ public static void Verbose<TData>(LogSource src, string message, InvocationInfo<
7177
/// This method calls <see cref="SentrySdk"/>'s CaptureException, so it is logged to Sentry.
7278
/// </summary>
7379
/// <param name="e">Exception to print.</param>
74-
public static void Error<TData>(Exception e, InvocationInfo<TData> caller)
75-
=> Execute(LogSeverity.Error, LogSource.Volte, string.Empty, e, caller);
80+
/// <param name="src">Source to print the message from.</param>
81+
public static void Error<TData>(Exception e, InvocationInfo<TData> caller, LogSource src = LogSource.Volte)
82+
=> Execute(LogSeverity.Error, src, string.Empty, e, caller);
7683

7784
#endregion
7885

src/Bot/Interactive/Paginator/ButtonPaginatorCallback.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Discord.Interactions;
2-
using Silk.NET.Input;
32
using Volte.Interactions;
43
using RunMode = Qmmands.RunMode;
54

src/Bot/Program.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ private static async Task Main(string[] args)
1212
if (output.Error is not InvalidOperationException)
1313
Error(output.Error);
1414

15-
CommandLineArguments = new ReadOnlyDictionary<string, string>(output.Parsed ?? new Dictionary<string, string>());
15+
await Main(output.Parsed);
16+
}
17+
18+
private static async Task Main(Dictionary<string, string> args)
19+
{
20+
CommandLineArguments = new ReadOnlyDictionary<string, string>(args ?? new Dictionary<string, string>());
1621
await VolteBot.StartAsync();
1722
}
1823
}

0 commit comments

Comments
 (0)