From 53650e4bfe8bb7d455274e274e32fdabe87f9bdd Mon Sep 17 00:00:00 2001 From: "Nuno \"Omega Leo\" Diogo" Date: Tue, 23 Dec 2025 16:37:30 +0000 Subject: [PATCH] Dev changes (#5) * Fixed dependency * Updates to NeoDictionary * Changes to NeoDictionary * Started working on a Git implementation to integrate into Game Engines in the future * Added commit functionality to the git library * New changes * Added select for NeoDictionary.cs * Fixed issue with previous new method * Added a Benchmarking Tool to be used mostly in game engines * Added IndexOf to NeoDictionary.cs * Added new method for BenchmarkUtility.cs * added an instacedobject class * Added a new shuffle method for lists * fixed return type * Added new Changelog generator * Added new Game logic library to assist with game projects * Added Web Project * Added Nuget package configs * Add workflow for publishing game library NuGet package * Add README for OmegaLeo's Helper Library * Updated Website to reflect all libraries * Fixed missing library in Changelog * Added Contributing and Code of Conduct * Fixed web project * new documentation changes * fix packages * Fixed DocumentationHelper package dependencies * Added Serilog * Added logging * Fixed documentation * Added logging for cache * fixed documentation * Updated README.md * Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update OmegaLeo.HelperLib.Web/Controllers/HomeController.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update OmegaLeo.HelperLib.Web/Controllers/HomeController.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix error --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .gitignore | 11 +- .../OmegaLeo.HelperLib.Changelog.csproj | 10 +- ...OmegaLeo.HelperLib.ChangelogHandler.csproj | 5 + .../Program.cs | 1 + .../OmegaLeo.HelperLib.ConsoleApp.csproj | 4 + .../Extensions/ListExtensions.cs | 11 + .../Handlers/DelayedActionHandler.cs | 8 + .../OmegaLeo.HelperLib.Game.csproj | 1 + .../OmegaLeo.HelperLib.Git.csproj | 2 +- .../Controllers/HomeController.cs | 290 +++++++++++++++--- .../OmegaLeo.HelperLib.Web.csproj | 6 +- OmegaLeo.HelperLib.Web/Program.cs | 15 + .../Views/Home/Library.cshtml | 17 +- .../Views/Shared/_Layout.cshtml | 14 +- OmegaLeo.HelperLib.Web/appsettings.json | 3 +- OmegaLeo.HelperLib.Web/wwwroot/css/site.css | 18 ++ OmegaLeo.HelperLib.sln | 8 + OmegaLeo.HelperLib.sln.DotSettings.user | 3 + .../Helpers/BenchmarkUtility.cs | 34 ++ OmegaLeo.HelperLib/OmegaLeo.HelperLib.csproj | 2 +- README.md | 46 ++- 21 files changed, 458 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index add57be..cdb51ce 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,13 @@ bin/ obj/ /packages/ riderModule.iml -/_ReSharper.Caches/ \ No newline at end of file +/_ReSharper.Caches/ +/.vs +<<<<<<< Updated upstream +/OmegaLeo.HelperLib.Web +======= +/OmegaLeo.HelperLib.Web/.config +/OmegaLeo.HelperLib.Web/Properties/PublishProfiles +/OmegaLeo.HelperLib.Web/publish +>>>>>>> Stashed changes +/OmegaLeo.HelperLib.Web/OmegaLeo.HelperLib.Web.csproj.user diff --git a/OmegaLeo.HelperLib.Changelog/OmegaLeo.HelperLib.Changelog.csproj b/OmegaLeo.HelperLib.Changelog/OmegaLeo.HelperLib.Changelog.csproj index 866d986..67db24c 100644 --- a/OmegaLeo.HelperLib.Changelog/OmegaLeo.HelperLib.Changelog.csproj +++ b/OmegaLeo.HelperLib.Changelog/OmegaLeo.HelperLib.Changelog.csproj @@ -13,9 +13,13 @@ - - - + + + + + + + diff --git a/OmegaLeo.HelperLib.ChangelogHandler/OmegaLeo.HelperLib.ChangelogHandler.csproj b/OmegaLeo.HelperLib.ChangelogHandler/OmegaLeo.HelperLib.ChangelogHandler.csproj index e62e50e..762352a 100644 --- a/OmegaLeo.HelperLib.ChangelogHandler/OmegaLeo.HelperLib.ChangelogHandler.csproj +++ b/OmegaLeo.HelperLib.ChangelogHandler/OmegaLeo.HelperLib.ChangelogHandler.csproj @@ -9,7 +9,12 @@ + + + + + diff --git a/OmegaLeo.HelperLib.ChangelogHandler/Program.cs b/OmegaLeo.HelperLib.ChangelogHandler/Program.cs index 9c7dfa1..ec00c21 100644 --- a/OmegaLeo.HelperLib.ChangelogHandler/Program.cs +++ b/OmegaLeo.HelperLib.ChangelogHandler/Program.cs @@ -14,6 +14,7 @@ var changelogs = new Dictionary() { {Path.Join(root, "OmegaLeo.HelperLib.Changelog", "CHANGELOG.md"), typeof(Changelog).Assembly}, {Path.Join(root, "OmegaLeo.HelperLib", "CHANGELOG.md"), typeof(BenchmarkUtility).Assembly}, + {Path.Join(root, "OmegaLeo.HelperLib.Game", "CHANGELOG.md"), typeof(OmegaLeo.HelperLib.Game.Models.ConsoleCommand).Assembly}, }; foreach (var changelog in changelogs) diff --git a/OmegaLeo.HelperLib.ConsoleApp/OmegaLeo.HelperLib.ConsoleApp.csproj b/OmegaLeo.HelperLib.ConsoleApp/OmegaLeo.HelperLib.ConsoleApp.csproj index ca1ddff..9332042 100644 --- a/OmegaLeo.HelperLib.ConsoleApp/OmegaLeo.HelperLib.ConsoleApp.csproj +++ b/OmegaLeo.HelperLib.ConsoleApp/OmegaLeo.HelperLib.ConsoleApp.csproj @@ -13,4 +13,8 @@ + + + + diff --git a/OmegaLeo.HelperLib.Game/Extensions/ListExtensions.cs b/OmegaLeo.HelperLib.Game/Extensions/ListExtensions.cs index 6e90296..21dc8db 100644 --- a/OmegaLeo.HelperLib.Game/Extensions/ListExtensions.cs +++ b/OmegaLeo.HelperLib.Game/Extensions/ListExtensions.cs @@ -1,10 +1,21 @@ using System.Collections.Generic; +using NetFlow.DocumentationHelper.Library.Attributes; using OmegaLeo.HelperLib.Game.Models; namespace OmegaLeo.HelperLib.Game.Extensions { public static class ListExtensions { + [Documentation(nameof(Shuffle), + "Shuffles the elements of the list in place using the provided RandomNumberGenerator.", + null, + @"```csharp +var list = new List { 1, 2, 3, 4}; +var rng = new RandomNumberGenerator(); +// List before: [1, 2, 3, 4] +list.Shuffle(rng); +// List after: [3, 1, 4, 2] (example output) +```")] public static void Shuffle(this IList list, RandomNumberGenerator rng) { for (int i = list.Count - 1; i > 0; i--) diff --git a/OmegaLeo.HelperLib.Game/Handlers/DelayedActionHandler.cs b/OmegaLeo.HelperLib.Game/Handlers/DelayedActionHandler.cs index eb5b4b3..f59fc03 100644 --- a/OmegaLeo.HelperLib.Game/Handlers/DelayedActionHandler.cs +++ b/OmegaLeo.HelperLib.Game/Handlers/DelayedActionHandler.cs @@ -1,7 +1,12 @@ +using NetFlow.DocumentationHelper.Library.Attributes; using OmegaLeo.HelperLib.Game.Models; namespace OmegaLeo.HelperLib.Game.Handlers { + [Documentation(nameof(DelayedActionHandler), "Handler for managing delayed actions, allowing cancellation and status checking.", null, @"```csharp +var action = new DelayedAction(...); +var handler = new DelayedActionHandler(action); +```")] public class DelayedActionHandler { private readonly DelayedAction _action; @@ -11,6 +16,9 @@ namespace OmegaLeo.HelperLib.Game.Handlers _action = action; } + [Documentation(nameof(Cancel), "Cancels the delayed action if it has not yet executed.", null, @"```csharp +handler.Cancel(); +```")] public void Cancel() { if (_action != null) diff --git a/OmegaLeo.HelperLib.Game/OmegaLeo.HelperLib.Game.csproj b/OmegaLeo.HelperLib.Game/OmegaLeo.HelperLib.Game.csproj index ca5b493..4b83b9f 100644 --- a/OmegaLeo.HelperLib.Game/OmegaLeo.HelperLib.Game.csproj +++ b/OmegaLeo.HelperLib.Game/OmegaLeo.HelperLib.Game.csproj @@ -16,6 +16,7 @@ + diff --git a/OmegaLeo.HelperLib.Git/OmegaLeo.HelperLib.Git.csproj b/OmegaLeo.HelperLib.Git/OmegaLeo.HelperLib.Git.csproj index c149dcf..03d8ca0 100644 --- a/OmegaLeo.HelperLib.Git/OmegaLeo.HelperLib.Git.csproj +++ b/OmegaLeo.HelperLib.Git/OmegaLeo.HelperLib.Git.csproj @@ -13,7 +13,7 @@ - + diff --git a/OmegaLeo.HelperLib.Web/Controllers/HomeController.cs b/OmegaLeo.HelperLib.Web/Controllers/HomeController.cs index 9274ef3..225847a 100644 --- a/OmegaLeo.HelperLib.Web/Controllers/HomeController.cs +++ b/OmegaLeo.HelperLib.Web/Controllers/HomeController.cs @@ -1,9 +1,12 @@ // File: `OmegaLeo.HelperLib.Web/Controllers/HomeController.cs` + using System.Diagnostics; using System.Reflection; +using System.Runtime.ExceptionServices; using GameDevLibrary.Helpers; using Microsoft.AspNetCore.Mvc; using NetFlow.DocumentationHelper.Library.Helpers; +using NetFlow.DocumentationHelper.Library.Models; using OmegaLeo.HelperLib.Changelog.Tools; using OmegaLeo.HelperLib.Web.Models; @@ -25,31 +28,34 @@ public class HomeController : Controller if (_libraryCache != null) return; var root = GetSolutionRoot(); + _logger.LogInformation("Initializing libraries from base directory: {Root}", root); _libraryCache = new Dictionary { ["core"] = GenerateLibraryDocs( typeof(BenchmarkUtility).Assembly, - Path.Join(root, "OmegaLeo.HelperLib", "CHANGELOG.md"), + Path.Combine(root, "changelogs", "OmegaLeo.HelperLib.CHANGELOG.md"), "Core Library", "Core utilities and helper functions for general C# development", "OmegaLeo.HelperLib" ), ["changelog"] = GenerateLibraryDocs( typeof(Changelog.Models.Changelog).Assembly, - Path.Join(root, "OmegaLeo.HelperLib.Changelog", "CHANGELOG.md"), + Path.Combine(root, "changelogs", "OmegaLeo.HelperLib.Changelog.CHANGELOG.md"), "Changelog Library", "Tools for managing and generating changelogs", "OmegaLeo.HelperLib.Changelog" ), ["game"] = GenerateLibraryDocs( typeof(OmegaLeo.HelperLib.Game.Models.ConsoleCommand).Assembly, - Path.Join(root, "OmegaLeo.HelperLib.Game", "CHANGELOG.md"), + Path.Combine(root, "changelogs", "OmegaLeo.HelperLib.Game.CHANGELOG.md"), "Game Development Library", "Game development utilities for Unity and Godot", "OmegaLeo.HelperLib.Game" ) }; + + _logger.LogInformation("Initialized {Count} libraries", _libraryCache.Count); } public IActionResult Index() @@ -62,7 +68,18 @@ public class HomeController : Controller if (string.IsNullOrEmpty(id) || !_libraryCache.ContainsKey(id)) return NotFound(); - return View(_libraryCache[id]); + var cache = _libraryCache[id]; + + if (cache.Documentation.Any()) + { + _logger.LogInformation($"Found cache for {id}: {cache.Documentation.Count} entries"); + } + else + { + _logger.LogError($"Cache for {id} is empty!"); + } + + return View(cache); } public IActionResult Privacy() @@ -76,55 +93,258 @@ public class HomeController : Controller return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } - private LibraryDocumentationViewModel GenerateLibraryDocs(Assembly assembly, string changelogPath, string displayName, string description, string nugetPackageId) + private void LogDocumentationHelperInfo() { - var allDocumentation = DocumentationHelperTool.GenerateDocumentation(); - var assemblyName = assembly.GetName().Name; + try + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + var target = assemblies.FirstOrDefault(a => + a.GetName().Name.Equals("NetFlow.DocumentationHelper.Library", StringComparison.OrdinalIgnoreCase)); - var filteredDocumentation = allDocumentation - .Where(d => d.AssemblyName.Equals(assemblyName, StringComparison.OrdinalIgnoreCase)) - .OrderBy(d => d.ClassName) - .ToList(); + if (target == null) + { + _logger.LogWarning("NetFlow.DocumentationHelper.Library not loaded. Loaded assemblies ({Count}):", + assemblies.Length); + foreach (var a in assemblies.OrderBy(a => a.GetName().Name)) + { + _logger.LogInformation(" {Name} {Version} => {Location}", a.GetName().Name, a.GetName().Version, + SafeLocation(a)); + } - string changelogMarkdown = ""; + return; + } + + _logger.LogInformation("Found {Name} {Version} at {Location}", target.GetName().Name, + target.GetName().Version, SafeLocation(target)); + + Type attrType = null; + try + { + attrType = target.GetType("NetFlow.DocumentationHelper.Library.Attributes.DocumentationAttribute", + false, true); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to GetType for DocumentationAttribute in {Assembly}", + target.GetName().Name); + } + + if (attrType == null) + { + _logger.LogWarning("Type 'DocumentationAttribute' not found in {Assembly}.", target.GetName().Name); + } + else + { + var ctors = attrType.GetConstructors(BindingFlags.Public | BindingFlags.Instance | + BindingFlags.NonPublic); + if (!ctors.Any()) + _logger.LogWarning("No constructors found on DocumentationAttribute in {Assembly}.", + target.GetName().Name); + foreach (var ctor in ctors) + { + var sig = string.Join(", ", ctor.GetParameters().Select(p => p.ParameterType.Name + " " + p.Name)); + _logger.LogInformation("DocumentationAttribute ctor: ({Signature})", sig); + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to log DocumentationHelper assembly info."); + } + + static string SafeLocation(Assembly a) + { + try + { + return string.IsNullOrEmpty(a.Location) ? "" : a.Location; + } + catch + { + return ""; + } + } + } + + private void InspectDocumentationHelperMethod() + { + try + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + Type helperType = null; + foreach (var asm in assemblies) + { + try + { + var t = asm.GetTypes().FirstOrDefault(tt => + tt.Name.Equals("DocumentationHelperTool", StringComparison.OrdinalIgnoreCase) + || tt.FullName?.EndsWith(".DocumentationHelperTool") == true); + if (t != null) + { + helperType = t; + break; + } + } + catch (ReflectionTypeLoadException rtlex) + { + _logger.LogDebug(rtlex, "Could not enumerate types in {Assembly}", asm.GetName().Name); + foreach (var loaderEx in rtlex.LoaderExceptions ?? Array.Empty()) + _logger.LogDebug(loaderEx, "Loader exception for {Assembly}", asm.GetName().Name); + } + catch (Exception ex) + { + _logger.LogDebug(ex, "Failed enumerating types in {Assembly}", asm.GetName().Name); + } + } + + if (helperType == null) + { + _logger.LogWarning("DocumentationHelperTool type not found in loaded assemblies."); + return; + } + + _logger.LogInformation("DocumentationHelperTool found: {Type} in {Assembly} ({Location})", + helperType.FullName, helperType.Assembly.GetName().Name, SafeLocation(helperType.Assembly)); + + var method = helperType.GetMethod("GenerateDocumentation", + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + if (method == null) + { + _logger.LogWarning("GenerateDocumentation method not found on {Type}.", helperType.FullName); + } + else + { + var parameters = string.Join(", ", + method.GetParameters().Select(p => p.ParameterType.Name + " " + p.Name)); + _logger.LogInformation("Method: {Name} ({Parameters}) -> {ReturnType}", method.Name, parameters, + method.ReturnType.FullName); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to inspect DocumentationHelperTool type/method."); + } + + static string SafeLocation(Assembly a) + { + try + { + return string.IsNullOrEmpty(a.Location) ? "" : a.Location; + } + catch + { + return ""; + } + } + } + + private LibraryDocumentationViewModel GenerateLibraryDocs(Assembly assembly, string changelogPath, + string displayName, string description, string nugetPackageId) + { + _logger.LogInformation("=== Starting GenerateLibraryDocs for {Assembly} ===", assembly.GetName().Name); + _logger.LogInformation("Assembly location: {Location}", assembly.Location ?? ""); + _logger.LogInformation("Changelog path: {Path}", changelogPath); + _logger.LogInformation("Changelog exists: {Exists}", System.IO.File.Exists(changelogPath)); + + // Log types in assembly and their attributes + try + { + var types = assembly.GetTypes(); + _logger.LogInformation("Assembly {Name} contains {Count} types", assembly.GetName().Name, types.Length); + + var typesWithDocAttr = types.Where(t => + t.GetCustomAttributes().Any(a => + a.GetType().Name.Contains("Documentation", StringComparison.OrdinalIgnoreCase))) + .ToList(); + + _logger.LogInformation("Found {Count} types with DocumentationAttribute-like attributes", + typesWithDocAttr.Count); + + if (typesWithDocAttr.Any()) + { + foreach (var type in typesWithDocAttr.Take(5)) + { + _logger.LogInformation(" Type with doc attr: {TypeName}", type.FullName); + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to inspect assembly types"); + } + + LogDocumentationHelperInfo(); + + IEnumerable allDocumentation = Enumerable.Empty(); + try + { + // Pass the specific assembly to only scan that assembly + var result = DocumentationHelperTool.GenerateDocumentation(); + allDocumentation = result? + .Where(d => d.AssemblyName?.Equals(assembly.GetName().Name ,StringComparison.OrdinalIgnoreCase) == true) + .ToList() ?? Enumerable.Empty(); + + _logger.LogInformation("GenerateDocumentation returned {Count} items for {Assembly}", + allDocumentation.Count(), assembly.GetName().Name); + + if (!allDocumentation.Any()) + { + _logger.LogWarning( + "No documentation items generated for {Assembly}. Check if types have [Documentation] attribute applied.", + assembly.GetName().Name); + } + } + catch (MissingMethodException mmex) + { + _logger.LogError(mmex, + "Missing method when calling DocumentationHelperTool.GenerateDocumentation(). Possible assembly mismatch."); + InspectDocumentationHelperMethod(); + ExceptionDispatchInfo.Capture(mmex).Throw(); + } + catch (Exception ex) + { + _logger.LogError(ex, "DocumentationHelperTool.GenerateDocumentation threw an exception."); + InspectDocumentationHelperMethod(); + throw; + } + + var changelogMarkdown = string.Empty; if (System.IO.File.Exists(changelogPath)) { - changelogMarkdown = ChangelogTool.GetMarkdown(assembly); + try + { + ChangelogTool.ExtractChangelog(assembly); + changelogMarkdown = ChangelogTool.GetMarkdown(assembly); + _logger.LogInformation("Parsed changelog for {Assembly}", assembly.GetName().Name); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to parse changelog at {Path}", changelogPath); + } } - - string version = assembly.GetCustomAttribute()?.InformationalVersion - ?? assembly.GetName().Version?.ToString() - ?? "1.0.0"; - - if (version.Contains('+')) + else { - version = version.Substring(0, version.IndexOf('+')); + _logger.LogWarning("Changelog file not found at {Path}", changelogPath); } - return new LibraryDocumentationViewModel + var viewModel = new LibraryDocumentationViewModel { LibraryName = displayName, Description = description, - Version = version, NuGetPackageId = nugetPackageId, - Documentation = filteredDocumentation, - ChangelogMarkdown = changelogMarkdown + ChangelogMarkdown = changelogMarkdown, + Documentation = allDocumentation.ToList() }; + + _logger.LogInformation( + "=== Completed GenerateLibraryDocs for {Assembly}. Total documentation items: {Count} ===", + assembly.GetName().Name, viewModel.Documentation.Count); + + return viewModel; } static string GetSolutionRoot() { - string dir = AppContext.BaseDirectory; - - while (!string.IsNullOrEmpty(dir)) - { - if (Directory.GetFiles(dir, "*.sln").Length > 0) - return dir; - - dir = Directory.GetParent(dir)?.FullName; - } - - throw new Exception("Solution root not found."); + return AppContext.BaseDirectory; } } diff --git a/OmegaLeo.HelperLib.Web/OmegaLeo.HelperLib.Web.csproj b/OmegaLeo.HelperLib.Web/OmegaLeo.HelperLib.Web.csproj index d794c36..96a5f1f 100644 --- a/OmegaLeo.HelperLib.Web/OmegaLeo.HelperLib.Web.csproj +++ b/OmegaLeo.HelperLib.Web/OmegaLeo.HelperLib.Web.csproj @@ -15,7 +15,11 @@ - + + + + + diff --git a/OmegaLeo.HelperLib.Web/Program.cs b/OmegaLeo.HelperLib.Web/Program.cs index 5cd7c7e..2a48945 100644 --- a/OmegaLeo.HelperLib.Web/Program.cs +++ b/OmegaLeo.HelperLib.Web/Program.cs @@ -1,8 +1,23 @@ +using Serilog; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); +// Configure logging +// Configure Serilog +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .WriteTo.Console() + .WriteTo.File( + path: Path.Combine(AppContext.BaseDirectory, "logs", "documentation-.txt"), + rollingInterval: RollingInterval.Day, + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}") + .CreateLogger(); + +builder.Host.UseSerilog(); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/OmegaLeo.HelperLib.Web/Views/Home/Library.cshtml b/OmegaLeo.HelperLib.Web/Views/Home/Library.cshtml index 7666072..8e5d3b8 100644 --- a/OmegaLeo.HelperLib.Web/Views/Home/Library.cshtml +++ b/OmegaLeo.HelperLib.Web/Views/Home/Library.cshtml @@ -1,8 +1,16 @@ @* File: `OmegaLeo.HelperLib.Web/Views/Home/Library.cshtml` *@ +@using ColorCode.Styling +@using GameDevLibrary.Extensions +@using Markdig @model OmegaLeo.HelperLib.Web.Models.LibraryDocumentationViewModel @{ ViewData["Title"] = Model.LibraryName; var groupedByNamespace = Model.Documentation.GroupBy(d => d.AssemblyName).OrderBy(g => g.Key); + + var pipeline = new MarkdownPipelineBuilder() + .UseAdvancedExtensions() + .UseSyntaxHighlighting(StyleDictionary.DefaultDark) + .Build(); }
@@ -75,7 +83,14 @@ { @desc.Title - @Html.Raw(desc.Description) + @Html.Raw(desc.Description)
+ @if (desc.CodeExample.IsNotNullOrEmpty()) + { + Example:
+
+ @Html.Raw(Markdig.Markdown.ToHtml(desc.CodeExample, pipeline)) +
+ } } diff --git a/OmegaLeo.HelperLib.Web/Views/Shared/_Layout.cshtml b/OmegaLeo.HelperLib.Web/Views/Shared/_Layout.cshtml index d2eff25..3e42232 100644 --- a/OmegaLeo.HelperLib.Web/Views/Shared/_Layout.cshtml +++ b/OmegaLeo.HelperLib.Web/Views/Shared/_Layout.cshtml @@ -34,6 +34,14 @@ Game Dev + +
@@ -42,12 +50,6 @@ @RenderBody() -
-
- © @DateTime.Now.Year - OmegaLeo Helper Libraries -
-
- diff --git a/OmegaLeo.HelperLib.Web/appsettings.json b/OmegaLeo.HelperLib.Web/appsettings.json index 10f68b8..822760b 100644 --- a/OmegaLeo.HelperLib.Web/appsettings.json +++ b/OmegaLeo.HelperLib.Web/appsettings.json @@ -5,5 +5,6 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "https_port": 443 } diff --git a/OmegaLeo.HelperLib.Web/wwwroot/css/site.css b/OmegaLeo.HelperLib.Web/wwwroot/css/site.css index f8d98fc..9001678 100644 --- a/OmegaLeo.HelperLib.Web/wwwroot/css/site.css +++ b/OmegaLeo.HelperLib.Web/wwwroot/css/site.css @@ -19,4 +19,22 @@ html { body { margin-bottom: 60px; +} + +pre { + overflow: auto; + padding: 1rem; + background-color: #495057; + position: relative; + + color: #f2f2f2; +} + +code { + background-color: transparent !important; +} + +.example-code > div { + border-radius: 0.5rem !important; + background-color: #495057 !important; } \ No newline at end of file diff --git a/OmegaLeo.HelperLib.sln b/OmegaLeo.HelperLib.sln index 6026162..b3024e3 100644 --- a/OmegaLeo.HelperLib.sln +++ b/OmegaLeo.HelperLib.sln @@ -14,6 +14,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OmegaLeo.HelperLib.Web", "O EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OmegaLeo.HelperLib.Game", "OmegaLeo.HelperLib.Game\OmegaLeo.HelperLib.Game.csproj", "{488BC3EA-9B7A-4251-B802-22EB098D6494}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFiles", "SolutionFiles", "{00CF0543-37F3-41DE-B78F-8ADA6DE796ED}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md + CONTRIBUTING.md = CONTRIBUTING.md + LICENSE = LICENSE + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/OmegaLeo.HelperLib.sln.DotSettings.user b/OmegaLeo.HelperLib.sln.DotSettings.user index fd5e489..eb58d7e 100644 --- a/OmegaLeo.HelperLib.sln.DotSettings.user +++ b/OmegaLeo.HelperLib.sln.DotSettings.user @@ -1,6 +1,9 @@  + ForceIncluded + ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/OmegaLeo.HelperLib/Helpers/BenchmarkUtility.cs b/OmegaLeo.HelperLib/Helpers/BenchmarkUtility.cs index 6b2a1ff..1e33fe5 100644 --- a/OmegaLeo.HelperLib/Helpers/BenchmarkUtility.cs +++ b/OmegaLeo.HelperLib/Helpers/BenchmarkUtility.cs @@ -1,14 +1,22 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using NetFlow.DocumentationHelper.Library.Attributes; namespace GameDevLibrary.Helpers { + [Documentation(nameof(BenchmarkUtility), "Utility class for benchmarking code execution time.", null, @"```csharp +BenchmarkUtility.Start(""MyBenchmark""); +// Code to benchmark +BenchmarkUtility.Stop(""MyBenchmark""); +var results = BenchmarkUtility.GetResults(""MyBenchmark""); +```")] public class BenchmarkUtility { private static Dictionary> _benchmarks = new Dictionary>(); private static Dictionary _stopwatches = new Dictionary(); + [Documentation("GetStopwatch", "Retrieves or creates a Stopwatch instance for the given key.", null, null)] private static Stopwatch GetStopwatch(string key) { Stopwatch stopwatch; @@ -22,6 +30,14 @@ namespace GameDevLibrary.Helpers return stopwatch; } + [Documentation("Record", "Records the execution time of the provided action and returns the elapsed time in milliseconds.", null, @"```csharp +var time = BenchmarkUtility.Record(() => +{ + // Code to benchmark +}); + +Console.WriteLine($""Elapsed time: {time} ms""); +```")] public static long Record(Action actionToRecord) { var stopwatch = new Stopwatch(); @@ -33,6 +49,19 @@ namespace GameDevLibrary.Helpers return stopwatch.ElapsedMilliseconds; } + [Documentation("RecordAndSaveToResults", "Records the execution time of the provided action, saves it under the given key, and returns the elapsed time in milliseconds.", null, @"```csharp +var time = BenchmarkUtility.RecordAndSaveToResults(""MyBenchmark"", () => +{ + // Code to benchmark +}); + +Console.WriteLine($""Elapsed time: {time} ms""); + +var results = BenchmarkUtility.GetResults(""MyBenchmark""); + +var averageTime = results.Average(); +Console.WriteLine($""Average time: {averageTime} ms""); +```")] public static long RecordAndSaveToResults(string key, Action actionToRecord) { var stopwatch = new Stopwatch(); @@ -52,11 +81,13 @@ namespace GameDevLibrary.Helpers return stopwatch.ElapsedMilliseconds; } + [Documentation("Start", "Starts or restarts the stopwatch for the given key.", null, null)] public static void Start(string key) { GetStopwatch(key).Restart(); } + [Documentation("Stop", "Stops the stopwatch for the given key and records the elapsed time.", null, null)] public static void Stop(string key) { if (!_benchmarks.ContainsKey(key)) @@ -68,16 +99,19 @@ namespace GameDevLibrary.Helpers _benchmarks[key].Add(GetStopwatch(key).ElapsedMilliseconds); } + [Documentation("GetResults", "Retrieves the list of recorded times for the given key.", null, null)] public static List GetResults(string key) { return _benchmarks.ContainsKey(key) ? _benchmarks[key] : new List(); } + [Documentation("ClearResults", "Clears all recorded benchmark results.", null, null)] public static void ClearResults() { _benchmarks.Clear(); } + [Documentation("GetAllResults", "Retrieves all recorded benchmark results.", null, null)] public static Dictionary> GetAllResults() { return new Dictionary>(_benchmarks); diff --git a/OmegaLeo.HelperLib/OmegaLeo.HelperLib.csproj b/OmegaLeo.HelperLib/OmegaLeo.HelperLib.csproj index 52f6963..d9f20ce 100644 --- a/OmegaLeo.HelperLib/OmegaLeo.HelperLib.csproj +++ b/OmegaLeo.HelperLib/OmegaLeo.HelperLib.csproj @@ -14,7 +14,7 @@ - + diff --git a/README.md b/README.md index d9af6e6..8970ee1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,50 @@ # HelperLib -Library to help with C# projects and with game development using C# +Lightweight utility library for C\# projects and game development helpers. + +## Table of contents + +- [Overview](#overview) +- [Features](#features) +- [Install](#install) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [License](#license) +- [Socials](#socials) + +## Overview + +`HelperLib` contains small helpers and abstractions commonly useful in application and game development (helpers for collections, math, and utilities to keep game and engine code concise). + +## Features + +- Small, focused utilities for C\# +- Helpers that favor clarity and testability +- Game development specific helpers in the `OmegaLeo.HelperLib.Game` package +- Changelog generation utilities in the `OmegaLeo.HelperLib.Changelog` package +- Lightweight and easy to integrate into existing projects +- Well-documented and maintained +- Open source under the AGPL v3 License + +## Install + +Add the library to your project via NuGet: + +```bash +dotnet add package OmegaLeo.HelperLib +dotnet add package OmegaLeo.HelperLib.Game +dotnet add package OmegaLeo.HelperLib.Changelog +``` + +## Documentation +The documentation for this project can be found at [https://library.omegaleo.dev](https://library.omegaleo.dev). +This documentation is automatically generated from the code using the [NetFlow.DocumentationHelper.Library](https://github.com/omegaleo/Netflow.DocumentationHelper). + +## Contributing +Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines. + +## License +This project is licensed under the AGPL v3 License. See the [LICENSE](LICENSE) file for details. ## Socials [![Youtube](https://img.shields.io/youtube/channel/subscribers/UCsdYKC7EK4Z_cpgWt0YiO9Q?style=plastic&label=Youtube&color=red