Skip to content

Refactors Autoloot property loading.#520

Merged
Reetus merged 2 commits intoautoloot-changes-allfrom
autoloot-ecv-load-main-additional-assemblies
Oct 12, 2025
Merged

Refactors Autoloot property loading.#520
Reetus merged 2 commits intoautoloot-changes-allfrom
autoloot-ecv-load-main-additional-assemblies

Conversation

@Reetus
Copy link
Owner

@Reetus Reetus commented Oct 10, 2025

Moves Autoloot property loading and assembly loading logic to AutolootManager. This change centralizes the property management, and removes redundant loading logic from the ECV and Autoloot View Models.

Summary by CodeRabbit

  • New Features

    • Added new autoloot filter options: layer-based and skill-bonus constraints.
    • Manager-driven loading of property definitions and external extensions at startup.
  • Changes

    • Removed the "Additional Assemblies" UI and related controls.
    • Simplified Entity Collection Viewer settings layout and filter initialisation flow.
  • Refactor

    • Centralised autoloot configuration and initialisation via a manager.
  • Chores

    • Updated license year to 2025.

Moves Autoloot property loading and assembly loading logic to AutolootManager.
This change centralizes the property management, and removes redundant loading logic from the ECV and Autoloot View Models.
@Reetus Reetus requested a review from Copilot October 10, 2025 07:18
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Centralises property and assembly initialisation into AutolootManager by adding LoadProperties and LoadAssemblies. Updates viewmodels to delegate loading to the manager, removes the Additional Assemblies UI and its code-behind, trims a DP case in a filter control, and updates license years.

Changes

Cohort / File(s) Summary
Autoloot manager: centralised loading
ClassicAssist/Data/Autoloot/AutolootManager.cs
Adds LoadProperties(ObservableCollection<PropertyEntry>) to load/merge Properties.json and Properties.Custom.json, register new predicate constraints (Layer, Skill_Bonus) and local JSON loader. Adds LoadAssemblies(ObservableCollection<PropertyEntry>) to load configured assemblies and invoke public static Initialize(ObservableCollection<PropertyEntry>). Adds new usings and updates license year.
ViewModels: delegate to AutolootManager
ClassicAssist/UI/ViewModels/Agents/AutolootViewModel.cs, ClassicAssist/UI/Views/ECV/Filter/EntityCollectionFilterViewModel.cs
Replace inline JSON/assembly loading with calls to AutolootManager.GetInstance().LoadProperties(...) and .LoadAssemblies(...). Remove in-class file/assembly loading helpers and the public Assemblies property from the ECV filter VM. Minor formatting and constructor flow adjustments.
Filter control: DP handling trimmed
ClassicAssist/UI/Views/ECV/Filter/EntityCollectionFilterControl.xaml.cs
Removes branch handling an ObservableCollection<Assembly> in the PropertyChangedCallback; now only handles ICommand case.
UI: remove Additional Assemblies feature
ClassicAssist/UI/Views/ECV/Settings/Controls/AdditionalAssembliesControl.xaml, ClassicAssist/UI/Views/ECV/Settings/Controls/AdditionalAssembliesControl.xaml.cs, ClassicAssist/UI/Views/ECV/EntityCollectionViewerSettingsWindow.xaml
Deletes AdditionalAssembliesControl XAML and code-behind, removes the Additional Assemblies GroupBox from the settings window, and adjusts grid rows/margins accordingly.
Licensing headers
...
Updated license timestamp/year to 2025 in touched files.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as UI (ECV / Autoloot)
  participant VM as ViewModel
  participant MGR as AutolootManager
  participant FS as FileSystem
  participant ASM as ExternalAssembly

  UI->>VM: Initialise
  VM->>MGR: GetInstance()
  VM->>MGR: LoadProperties(constraints)
  MGR->>FS: Read Properties.json / Properties.Custom.json
  FS-->>MGR: PropertyEntry[]
  MGR->>MGR: Merge entries + AddSorted + register Layer/Skill_Bonus predicates
  VM->>MGR: LoadAssemblies(constraints)
  MGR->>ASM: Load configured assemblies (paths from options)
  ASM-->>MGR: Assembly loaded
  MGR->>ASM: Invoke public static Initialize(constraints) if present
  ASM-->>MGR: Initialize invoked
  MGR-->>VM: Constraints populated
  VM-->>UI: Filters/properties ready
  note over UI,VM: Removed Additional Assemblies UI and direct VM-based assembly loading
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title succinctly describes the main change by indicating a refactor of Autoloot property loading, which aligns with the primary adjustments in the diff. It is concise, specific, and clearly communicates the focus of the pull request to reviewers.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch autoloot-ecv-load-main-additional-assemblies

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f82f85f and a70c420.

📒 Files selected for processing (1)
  • ClassicAssist/Data/Autoloot/AutolootManager.cs (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ClassicAssist/Data/Autoloot/AutolootManager.cs (6)
ClassicAssist/Data/Autoloot/PropertyEntry.cs (1)
  • PropertyEntry (8-78)
ClassicAssist/Data/AssistantOptions.cs (1)
  • AssistantOptions (19-353)
ClassicAssist/Data/Autoloot/AutolootHelpers.cs (6)
  • IEnumerable (24-33)
  • IEnumerable (40-86)
  • AutolootHelpers (11-182)
  • AutolootHelpers (16-20)
  • Operation (114-129)
  • Operation (131-144)
ClassicAssist.Shared/Misc/ExtensionMethods.cs (1)
  • AddSorted (35-50)
ClassicAssist/UO/Data/TileData.cs (1)
  • TileData (12-161)
ClassicAssist/UO/Objects/Property.cs (1)
  • Property (3-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR centralizes Autoloot property loading and assembly loading logic by moving it from individual view models to the AutolootManager. The refactoring eliminates code duplication and consolidates property management in a single location.

  • Removed duplicate property loading logic from ECV and Autoloot ViewModels
  • Added LoadProperties and LoadAssemblies methods to AutolootManager
  • Removed the AdditionalAssembliesControl UI components that were managing assemblies through the ECV interface

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
AdditionalAssembliesControl.xaml.cs Completely removed - assembly management UI no longer needed
AdditionalAssembliesControl.xaml Completely removed - assembly management UI no longer needed
EntityCollectionFilterViewModel.cs Replaced local property loading logic with AutolootManager calls
EntityCollectionFilterControl.xaml.cs Removed assembly collection handling code
EntityCollectionViewerSettingsWindow.xaml Removed AdditionalAssembliesControl from UI
AutolootViewModel.cs Replaced local property loading with AutolootManager calls and updated copyright
AutolootManager.cs Added LoadProperties and LoadAssemblies methods to centralize loading logic

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +74 to +83
Assembly asm = Assembly.LoadFrom( fileName );
IEnumerable<MethodInfo> initializeMethods = asm.GetTypes()
.Where( e => e.IsClass && e.IsPublic && e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) != null ).Select( e =>
e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) );

foreach ( MethodInfo initializeMethod in initializeMethods )
{
// ReSharper disable once CoVariantArrayConversion
initializeMethod?.Invoke( null, new[] { constraints } );
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading assemblies from file paths without validation poses security risks. Consider adding file existence checks and exception handling around Assembly.LoadFrom().

Suggested change
Assembly asm = Assembly.LoadFrom( fileName );
IEnumerable<MethodInfo> initializeMethods = asm.GetTypes()
.Where( e => e.IsClass && e.IsPublic && e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) != null ).Select( e =>
e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) );
foreach ( MethodInfo initializeMethod in initializeMethods )
{
// ReSharper disable once CoVariantArrayConversion
initializeMethod?.Invoke( null, new[] { constraints } );
if (!File.Exists(fileName))
{
// Optionally log or handle missing file
continue;
}
try
{
Assembly asm = Assembly.LoadFrom(fileName);
IEnumerable<MethodInfo> initializeMethods = asm.GetTypes()
.Where(e => e.IsClass && e.IsPublic && e.GetMethod("Initialize", BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof(ObservableCollection<PropertyEntry>) }, null) != null).Select(e =>
e.GetMethod("Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(ObservableCollection<PropertyEntry>) }, null));
foreach (MethodInfo initializeMethod in initializeMethods)
{
// ReSharper disable once CoVariantArrayConversion
initializeMethod?.Invoke(null, new[] { constraints });
}
}
catch (Exception ex)
{
// Optionally log the exception, e.g. Console.WriteLine or use a logging framework
// Console.WriteLine($"Failed to load assembly '{fileName}': {ex}");

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
ClassicAssist/Data/Autoloot/AutolootManager.cs (1)

160-174: Remove stray return; hoist local function for clarity

The unconditional return before the local LoadFile declaration is unnecessary and harms readability. Either remove the return or move LoadFile above its first use.

Apply this diff to remove the redundant return:

-            return;
-
             IEnumerable<PropertyEntry> LoadFile( string fileName )
             {
                 JsonSerializer serializer = new JsonSerializer();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 305d227 and f82f85f.

📒 Files selected for processing (7)
  • ClassicAssist/Data/Autoloot/AutolootManager.cs (2 hunks)
  • ClassicAssist/UI/ViewModels/Agents/AutolootViewModel.cs (21 hunks)
  • ClassicAssist/UI/Views/ECV/EntityCollectionViewerSettingsWindow.xaml (1 hunks)
  • ClassicAssist/UI/Views/ECV/Filter/EntityCollectionFilterControl.xaml.cs (0 hunks)
  • ClassicAssist/UI/Views/ECV/Filter/EntityCollectionFilterViewModel.cs (3 hunks)
  • ClassicAssist/UI/Views/ECV/Settings/Controls/AdditionalAssembliesControl.xaml (0 hunks)
  • ClassicAssist/UI/Views/ECV/Settings/Controls/AdditionalAssembliesControl.xaml.cs (0 hunks)
💤 Files with no reviewable changes (3)
  • ClassicAssist/UI/Views/ECV/Settings/Controls/AdditionalAssembliesControl.xaml.cs
  • ClassicAssist/UI/Views/ECV/Settings/Controls/AdditionalAssembliesControl.xaml
  • ClassicAssist/UI/Views/ECV/Filter/EntityCollectionFilterControl.xaml.cs
🧰 Additional context used
🧬 Code graph analysis (3)
ClassicAssist/Data/Autoloot/AutolootManager.cs (5)
ClassicAssist/Data/Autoloot/PropertyEntry.cs (1)
  • PropertyEntry (8-78)
ClassicAssist/Data/AssistantOptions.cs (1)
  • AssistantOptions (19-353)
ClassicAssist.Shared/Misc/ExtensionMethods.cs (1)
  • AddSorted (35-50)
ClassicAssist/UO/Data/TileData.cs (1)
  • TileData (12-161)
ClassicAssist/UO/Objects/Property.cs (1)
  • Property (3-19)
ClassicAssist/UI/Views/ECV/Filter/EntityCollectionFilterViewModel.cs (1)
ClassicAssist/Data/Autoloot/AutolootManager.cs (4)
  • AutolootManager (36-175)
  • AutolootManager (50-68)
  • LoadProperties (88-174)
  • LoadAssemblies (70-86)
ClassicAssist/UI/ViewModels/Agents/AutolootViewModel.cs (1)
ClassicAssist/Data/Autoloot/AutolootManager.cs (4)
  • AutolootManager (36-175)
  • AutolootManager (50-68)
  • LoadProperties (88-174)
  • LoadAssemblies (70-86)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
ClassicAssist/UI/Views/ECV/EntityCollectionViewerSettingsWindow.xaml (2)

56-59: Approve the layout simplification.

The removal of the second row definition and the margin adjustment to the Container Sets GroupBox are appropriate changes that align with the removal of the Additional Assemblies UI section. The reduced bottom margin (from 15 to 10) maintains visual consistency now that there's no second row below.


1-78: Additional Assemblies removed; confirm alternate management path

  • No AdditionalAssembl properties or XAML bindings remain in EntityCollectionViewerSettingsViewModel or ECV views.
  • Ensure additional assemblies can still be managed via AutolootManager or another interface.
ClassicAssist/UI/Views/ECV/Filter/EntityCollectionFilterViewModel.cs (1)

76-78: Centralised initialisation via AutolootManager looks good

Shifting property/assembly loading to AutolootManager simplifies this VM and reduces duplication. LGTM. Ensure the manager’s loading is exception‑safe as per the manager changes.

Also applies to: 120-122

Comment on lines +70 to +86
public void LoadAssemblies( ObservableCollection<PropertyEntry> constraints )
{
foreach ( string fileName in AssistantOptions.Assemblies )
{
Assembly asm = Assembly.LoadFrom( fileName );
IEnumerable<MethodInfo> initializeMethods = asm.GetTypes()
.Where( e => e.IsClass && e.IsPublic && e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null,
new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) != null ).Select( e =>
e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) );

foreach ( MethodInfo initializeMethod in initializeMethods )
{
// ReSharper disable once CoVariantArrayConversion
initializeMethod?.Invoke( null, new[] { constraints } );
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden assembly loading and avoid duplicate loads

Current code can crash initialisation and double‑load assemblies:

  • No exception handling for missing/corrupt assemblies or ReflectionTypeLoadException.
  • AssistantOptions.Load uses Assembly.LoadFile earlier; using LoadFrom here can load the same DLL again into a different context.

Recommend:

  • Reuse already loaded assemblies by Location; fall back to LoadFile to match prior usage.
  • Guard with File.Exists and wrap in try/catch.

Apply this diff:

-        public void LoadAssemblies( ObservableCollection<PropertyEntry> constraints )
-        {
-            foreach ( string fileName in AssistantOptions.Assemblies )
-            {
-                Assembly asm = Assembly.LoadFrom( fileName );
-                IEnumerable<MethodInfo> initializeMethods = asm.GetTypes()
-                    .Where( e => e.IsClass && e.IsPublic && e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null,
-                        new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) != null ).Select( e =>
-                        e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) );
-
-                foreach ( MethodInfo initializeMethod in initializeMethods )
-                {
-                    // ReSharper disable once CoVariantArrayConversion
-                    initializeMethod?.Invoke( null, new[] { constraints } );
-                }
-            }
-        }
+        public void LoadAssemblies( ObservableCollection<PropertyEntry> constraints )
+        {
+            foreach ( string fileName in AssistantOptions.Assemblies )
+            {
+                try
+                {
+                    if (string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName))
+                    {
+                        continue;
+                    }
+
+                    // Prefer reusing an existing load; fall back to LoadFile to match AssistantOptions.Load
+                    Assembly asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a =>
+                    {
+                        try { return string.Equals(a.Location, fileName, StringComparison.OrdinalIgnoreCase); }
+                        catch { return false; }
+                    }) ?? Assembly.LoadFile(fileName);
+
+                    IEnumerable<MethodInfo> initializeMethods;
+                    try
+                    {
+                        initializeMethods = asm.GetTypes()
+                            .Where( e => e.IsClass && e.IsPublic && e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null,
+                                new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) != null )
+                            .Select( e => e.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) );
+                    }
+                    catch (ReflectionTypeLoadException ex)
+                    {
+                        initializeMethods = ex.Types
+                            .Where( t => t != null && t.IsClass && t.IsPublic && t.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null,
+                                new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) != null )
+                            .Select( t => t.GetMethod( "Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof( ObservableCollection<PropertyEntry> ) }, null ) );
+                    }
+
+                    foreach ( MethodInfo initializeMethod in initializeMethods )
+                    {
+                        // ReSharper disable once CoVariantArrayConversion
+                        initializeMethod?.Invoke( null, new[] { constraints } );
+                    }
+                }
+                catch
+                {
+                    // ignored
+                }
+            }
+        }

Comment on lines +141 to +148
if (entry.Operator != AutolootOperator.NotPresent)
{
return properties
.Where(property => property.Arguments != null && property.Arguments.Length >= 1 &&
(property.Arguments[0].Equals(entry.Additional, StringComparison.CurrentCultureIgnoreCase) ||
string.IsNullOrEmpty(entry.Additional))).Any(property =>
AutolootHelpers.Operation(entry.Operator, Convert.ToInt32(property.Arguments[1]), entry.Value));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bounds bug in Skill_Bonus predicate (IndexOutOfRange risk)

Arguments length is checked for >= 1 but index 1 is accessed. This can throw when only one argument is present. Also, parsing should be guarded.

Apply this diff:

-                    if (entry.Operator != AutolootOperator.NotPresent)
-                    {
-                        return properties
-                            .Where(property => property.Arguments != null && property.Arguments.Length >= 1 &&
-                                                (property.Arguments[0].Equals(entry.Additional, StringComparison.CurrentCultureIgnoreCase) ||
-                                                  string.IsNullOrEmpty(entry.Additional))).Any(property =>
-                                AutolootHelpers.Operation(entry.Operator, Convert.ToInt32(property.Arguments[1]), entry.Value));
-                    }
+                    if (entry.Operator != AutolootOperator.NotPresent)
+                    {
+                        return properties
+                            .Where(property => property.Arguments != null && property.Arguments.Length >= 2 &&
+                                               (property.Arguments[0].Equals(entry.Additional, StringComparison.CurrentCultureIgnoreCase) ||
+                                                string.IsNullOrEmpty(entry.Additional)))
+                            .Any(property =>
+                                int.TryParse(property.Arguments[1], out var val) &&
+                                AutolootHelpers.Operation(entry.Operator, val, entry.Value));
+                    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (entry.Operator != AutolootOperator.NotPresent)
{
return properties
.Where(property => property.Arguments != null && property.Arguments.Length >= 1 &&
(property.Arguments[0].Equals(entry.Additional, StringComparison.CurrentCultureIgnoreCase) ||
string.IsNullOrEmpty(entry.Additional))).Any(property =>
AutolootHelpers.Operation(entry.Operator, Convert.ToInt32(property.Arguments[1]), entry.Value));
}
if (entry.Operator != AutolootOperator.NotPresent)
{
return properties
.Where(property => property.Arguments != null && property.Arguments.Length >= 2 &&
(property.Arguments[0].Equals(entry.Additional, StringComparison.CurrentCultureIgnoreCase) ||
string.IsNullOrEmpty(entry.Additional)))
.Any(property =>
int.TryParse(property.Arguments[1], out var val) &&
AutolootHelpers.Operation(entry.Operator, val, entry.Value));
}
🤖 Prompt for AI Agents
In ClassicAssist/Data/Autoloot/AutolootManager.cs around lines 141-148, the
predicate checks Arguments.Length >= 1 but then accesses Arguments[1], causing
an IndexOutOfRangeException and unguarded parsing; change the length check to >=
2, ensure you only evaluate the second argument when present, and use
int.TryParse to safely parse property.Arguments[1] (skip this property if
parsing fails) while preserving the existing Additional matching logic.

Comment on lines +103 to +112
_manager = AutolootManager.GetInstance();
_manager.LoadProperties( Constraints );
_manager.LoadAssemblies( Constraints );
_manager.GetEntries = () => _items.ToList();
_manager.CheckContainer = OnCorpseEvent;
_manager.CheckItems = CheckItems;
_manager.IsRunning = () => IsRunning;
_manager.MatchTextValue = () => MatchTextValue;
_manager.IsEnabled = () => Enabled;
_manager.SetEnabled = val => Enabled = val;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Singleton delegates capture this — leaks the ViewModel

Assigning instance-bound delegates to the long‑lived AutolootManager roots AutolootViewModel and prevents GC after the view closes. Provide a detach path or use weak references.

Minimal fix option:

  • Expose a DetachFromManager() that sets all manager delegates back to no‑ops or null and call it on view unload/disposing.
  • Or have AutolootManager accept handlers via a Register/Unregister API and store WeakReference targets.

Example using weak references (illustrative):

var weak = new WeakReference<AutolootViewModel>(this);
_manager.SetEnabled = val => { if (weak.TryGetTarget(out var vm)) vm.Enabled = val; };
_manager.IsEnabled  = () => weak.TryGetTarget(out var vm) && vm.Enabled;
// repeat for others...
🤖 Prompt for AI Agents
ClassicAssist/UI/ViewModels/Agents/AutolootViewModel.cs around lines 103-112
assigns instance-bound delegates to the singleton AutolootManager which captures
the ViewModel and prevents GC; fix by adding a DetachFromManager() method that
clears or replaces every manager delegate (GetEntries, CheckContainer,
CheckItems, IsRunning, MatchTextValue, IsEnabled, SetEnabled) with null/no-op
handlers and call DetachFromManager() from the view unload/dispose path, or
refactor to a Register/Unregister API on AutolootManager (store handlers there)
or wrap the ViewModel in WeakReference when assigning delegates and use
TryGetTarget checks so the delegates do not hold strong references to the
ViewModel.

@sonarqubecloud
Copy link

@Reetus Reetus changed the base branch from master to autoloot-changes-all October 12, 2025 01:29
@Reetus Reetus merged commit 75ae645 into autoloot-changes-all Oct 12, 2025
4 checks passed
@Reetus Reetus deleted the autoloot-ecv-load-main-additional-assemblies branch October 12, 2025 01:29
@coderabbitai coderabbitai bot mentioned this pull request Oct 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants