28 KiB
Plugin Architecture
**Referenced Files in This Document** - [HotkeyCommandAttribute.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommandAttribute.cs) - [HotkeyCommand.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommand.cs) - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [Reflection.cs](file://ClassicAssist.Shared/Helpers/Reflection.cs) - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [PluginEntry.cs](file://ClassicAssist.Launcher/PluginEntry.cs) - [ManifestEntry.cs](file://ClassicAssist.Launcher/ManifestEntry.cs) - [ShardManager.cs](file://ClassicAssist.Launcher/ShardManager.cs) - [Browser.Models.Manifest.cs](file://ClassicAssist/Browser/Models/Manifest.cs) - [Browser.Models.Metadata.cs](file://ClassicAssist/Browser/Models/Metadata.cs) - [Browser.Data.Database.cs](file://ClassicAssist/Browser/Data/Database.cs) - [Browser.Data.Filter.cs](file://ClassicAssist/Browser/Data/Filter.cs) - [Browser.Data.FilterType.cs](file://ClassicAssist/Browser/Data/FilterType.cs) - [Browser.MacroBrowserControl.xaml.cs](file://ClassicAssist/Browser/MacroBrowserControl.xaml.cs) - [Browser.MacroBrowserViewModel.cs](file://ClassicAssist/Browser/MacroBrowserViewModel.cs)Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
Introduction
This document explains the plugin architecture and extension points for ClassicAssist.Avalonia, focusing on:
- Reflection-based command discovery
- Dynamic assembly loading and plugin lifecycle management
- Creating custom commands via HotkeyCommandAttribute
- Extending the macro system
- Plugin loading mechanism, dependency resolution, and version compatibility
- Security considerations, sandboxing, and isolation
- Distribution format, installation process, and updates
- Debugging, error handling, and performance monitoring for plugins
The goal is to provide both a high-level overview and detailed technical guidance for building robust plugins that integrate with hotkeys, macros, and UI components.
Project Structure
At a high level, the repository contains:
- Shared core types and managers (hotkeys, macros, reflection helpers)
- Avalonia application entry points and engine initialization
- Launcher utilities for plugin entries and manifests
- Browser features for browsing and managing macros and metadata
graph TB
subgraph "Shared Core"
HKAttr["HotkeyCommandAttribute"]
HKCmd["HotkeyCommand base"]
HKMgr["HotkeyManager"]
MacroInv["MacroInvoker"]
MacroMgr["MacroManager"]
Refl["Reflection Helpers"]
end
subgraph "Avalonia App"
Prog["Program"]
AppXaml["App.axaml.cs"]
EngineAva["Engine.cs (Avalonia)"]
end
subgraph "Launcher"
PluginEntry["PluginEntry"]
ManifestEntry["ManifestEntry"]
ShardMgr["ShardManager"]
end
subgraph "Browser"
DB["Database"]
Filter["Filter / FilterType"]
Meta["Metadata"]
Manifest["Manifest"]
MBVM["MacroBrowserViewModel"]
MBCtrl["MacroBrowserControl"]
end
Prog --> AppXaml --> EngineAva
EngineAva --> HKMgr
EngineAva --> MacroMgr
HKMgr --> HKAttr
HKMgr --> HKCmd
MacroMgr --> MacroInv
MacroInv --> Refl
ShardMgr --> PluginEntry
ShardMgr --> ManifestEntry
MBVM --> DB
MBVM --> Filter
MBVM --> Meta
MBVM --> Manifest
MBCtrl --> MBVM
Diagram sources
- Program.cs
- App.axaml.cs
- Engine.cs
- HotkeyManager.cs
- HotkeyCommandAttribute.cs
- HotkeyCommand.cs
- MacroManager.cs
- MacroInvoker.cs
- Reflection.cs
- PluginEntry.cs
- ManifestEntry.cs
- ShardManager.cs
- Browser.Data.Database.cs
- Browser.Data.Filter.cs
- Browser.Data.FilterType.cs
- Browser.Models.Metadata.cs
- Browser.Models.Manifest.cs
- Browser.MacroBrowserViewModel.cs
- Browser.MacroBrowserControl.xaml.cs
Section sources
- Program.cs
- App.axaml.cs
- Engine.cs
- HotkeyManager.cs
- HotkeyCommandAttribute.cs
- HotkeyCommand.cs
- MacroManager.cs
- MacroInvoker.cs
- Reflection.cs
- PluginEntry.cs
- ManifestEntry.cs
- ShardManager.cs
- Browser.Data.Database.cs
- Browser.Data.Filter.cs
- Browser.Data.FilterType.cs
- Browser.Models.Metadata.cs
- Browser.Models.Manifest.cs
- Browser.MacroBrowserViewModel.cs
- Browser.MacroBrowserControl.xaml.cs
Core Components
- Hotkey command discovery and registration
- Commands are discovered by scanning assemblies for classes implementing a specific base type and decorated with an attribute. The manager registers them into a central registry and binds them to configured shortcuts.
- Macro execution and extensibility
- Macros are parsed and executed through an invoker that resolves commands at runtime using reflection. A manager maintains macro definitions and provides APIs for invocation and control.
- Reflection helpers
- Utility methods for enumerating types, reading attributes, and constructing instances without hard-coded dependencies.
- Application engine and bootstrap
- The Avalonia app initializes subsystems including hotkey and macro managers, ensuring they are ready before user interaction.
Key responsibilities:
- HotkeyCommandAttribute: Declares metadata for discoverable commands (e.g., name, description).
- HotkeyCommand: Base class or interface for all hotkey commands.
- HotkeyManager: Scans loaded assemblies, discovers commands, and wires up shortcut handlers.
- MacroInvoker: Executes macro steps by resolving command implementations dynamically.
- MacroManager: Manages macro entries, persistence, and orchestration.
- Reflection: Provides safe and efficient reflection operations used across discovery and invocation.
Section sources
- HotkeyCommandAttribute.cs
- HotkeyCommand.cs
- HotkeyManager.cs
- MacroInvoker.cs
- MacroManager.cs
- Reflection.cs
- Engine.cs
Architecture Overview
The plugin architecture centers around dynamic discovery and late binding:
- Plugins ship as compiled assemblies placed in designated directories.
- At startup, the engine scans these assemblies for known patterns (attributes and base types).
- Discovered commands and macro extensions are registered into centralized managers.
- Runtime dispatch uses reflection to invoke command implementations based on configuration and user actions.
sequenceDiagram
participant Boot as "Program"
participant App as "App.axaml.cs"
participant Eng as "Engine.cs"
participant HKM as "HotkeyManager"
participant Mgr as "MacroManager"
participant Inv as "MacroInvoker"
participant Ref as "Reflection"
Boot->>App : Initialize Avalonia
App->>Eng : Create and configure engine
Eng->>HKM : Initialize hotkey subsystem
Eng->>Mgr : Initialize macro subsystem
HKM->>Ref : Enumerate types in loaded assemblies
HKM->>HKM : Discover commands via attributes/base types
Mgr->>Ref : Enumerate macro command types
Mgr->>Inv : Register resolvers and handlers
Note over HKM,Inv : Discovery complete; runtime dispatch ready
Diagram sources
Detailed Component Analysis
Hotkey Command System
Hotkey commands are implemented as classes that derive from a common base and are annotated with a metadata attribute. The hotkey manager performs discovery and registration.
classDiagram
class HotkeyCommand {
+Execute()
+CanExecute() bool
}
class HotkeyCommandAttribute {
+Name
+Description
}
class HotkeyManager {
+Initialize()
+Register(command)
+Unregister(command)
+OnShortcut(key)
}
HotkeyManager --> HotkeyCommand : "discovers and registers"
HotkeyCommand <.. HotkeyCommandAttribute : "decorated by"
- Discovery pattern:
- Scan assemblies for types implementing the base command type.
- Require the presence of the metadata attribute to include a command.
- Registration:
- Add discovered commands to a registry keyed by identifier.
- Bind configured shortcuts to command identifiers.
- Execution:
- On shortcut activation, resolve the command instance and call Execute.
Implementation references:
- Attribute definition and metadata fields
- Base command contract
- Manager initialization, discovery loop, and shortcut wiring
Diagram sources
Section sources
Macro System Extension Points
Macros are composed of steps that map to commands. The macro manager stores macro definitions, while the invoker resolves and executes commands at runtime.
sequenceDiagram
participant User as "User"
participant UI as "UI Layer"
participant MM as "MacroManager"
participant MI as "MacroInvoker"
participant Ref as "Reflection"
User->>UI : Run macro
UI->>MM : Invoke(macroId)
MM->>MI : Resolve steps and parameters
MI->>Ref : Find command implementation
MI->>MI : Execute step(s)
MI-->>MM : Result/status
MM-->>UI : Completion event
Key behaviors:
- Command resolution:
- Map macro step names to concrete implementations using reflection.
- Parameter binding:
- Parse and bind parameters to method signatures safely.
- Error propagation:
- Wrap exceptions during execution and report back to the caller.
References:
- Macro manager for storage and orchestration
- Invoker for resolution and execution
- Reflection helpers for safe type inspection
Diagram sources
Section sources
Reflection-Based Discovery Flow
Discovery is driven by scanning loaded assemblies for known patterns.
flowchart TD
Start(["Start Discovery"]) --> LoadAssemblies["Load plugin assemblies"]
LoadAssemblies --> EnumTypes["Enumerate public types"]
EnumTypes --> CheckBase{"Implements base command?"}
CheckBase --> |No| NextType["Next type"]
CheckBase --> |Yes| CheckAttr{"Has metadata attribute?"}
CheckAttr --> |No| NextType
CheckAttr --> |Yes| CreateInstance["Create instance"]
CreateInstance --> Register["Register in manager"]
Register --> NextType
NextType --> Done(["Done"])
Diagram sources
Section sources
Plugin Loading Mechanism and Lifecycle
Plugins are distributed as assemblies and loaded at runtime. The launcher and browser modules provide structures for manifest and plugin entries, while the engine coordinates initialization.
sequenceDiagram
participant Boot as "Program"
participant App as "App.axaml.cs"
participant Eng as "Engine.cs"
participant SM as "ShardManager"
participant PE as "PluginEntry"
participant ME as "ManifestEntry"
participant DB as "Database"
Boot->>App : Initialize
App->>Eng : Configure engine
Eng->>SM : Load plugin manifests
SM->>ME : Read manifest entries
SM->>PE : Build plugin entries
SM->>DB : Persist/update catalog
Eng->>Eng : Scan and load plugin assemblies
Eng->>Eng : Initialize discovered plugins
Lifecycle stages:
- Manifest parsing:
- Read metadata and dependency information.
- Assembly loading:
- Load plugin assemblies into the current context.
- Initialization:
- Call plugin entry points if provided.
- Teardown:
- Unregister commands and release resources on shutdown.
References:
- Plugin and manifest entry models
- Database integration for cataloging
- Engine initialization hooks
Diagram sources
- Program.cs
- App.axaml.cs
- Engine.cs
- ShardManager.cs
- PluginEntry.cs
- ManifestEntry.cs
- Browser.Data.Database.cs
Section sources
- Program.cs
- App.axaml.cs
- Engine.cs
- ShardManager.cs
- PluginEntry.cs
- ManifestEntry.cs
- Browser.Data.Database.cs
Version Compatibility and Dependency Resolution
Compatibility and dependencies are described via manifest and metadata structures. The loader validates versions and resolves required packages before loading.
flowchart TD
ReadManifest["Read ManifestEntry"] --> ValidateVersion["Validate target framework/version"]
ValidateVersion --> ResolveDeps["Resolve dependencies"]
ResolveDeps --> Missing{"Missing deps?"}
Missing --> |Yes| ReportError["Report missing dependencies"]
Missing --> |No| LoadAssembly["Load plugin assembly"]
LoadAssembly --> InitPlugin["Initialize plugin"]
InitPlugin --> Ready(["Ready"])
ReportError --> End(["Abort"])
References:
- Manifest and metadata models
- Loader logic for validation and resolution
Diagram sources
Section sources
UI Integration and Browsing
The browser component integrates with the macro system and database to present available content and manage metadata.
graph TB
VM["MacroBrowserViewModel"]
DB["Database"]
Filter["Filter / FilterType"]
Meta["Metadata"]
Manifest["Manifest"]
Ctrl["MacroBrowserControl"]
VM --> DB
VM --> Filter
VM --> Meta
VM --> Manifest
Ctrl --> VM
Responsibilities:
- Query and filter macro entries
- Display metadata and manifest details
- Provide user interactions to install/update content
Diagram sources
- Browser.MacroBrowserViewModel.cs
- Browser.MacroBrowserControl.xaml.cs
- Browser.Data.Database.cs
- Browser.Data.Filter.cs
- Browser.Data.FilterType.cs
- Browser.Models.Metadata.cs
- Browser.Models.Manifest.cs
Section sources
- Browser.MacroBrowserViewModel.cs
- Browser.MacroBrowserControl.xaml.cs
- Browser.Data.Database.cs
- Browser.Data.Filter.cs
- Browser.Data.FilterType.cs
- Browser.Models.Metadata.cs
- Browser.Models.Manifest.cs
Dependency Analysis
The following diagram shows key dependencies between core components involved in plugin discovery and execution.
graph LR
Program["Program"] --> AppXaml["App.axaml.cs"]
AppXaml --> EngineAva["Engine.cs"]
EngineAva --> HKM["HotkeyManager"]
EngineAva --> MacroMgr["MacroManager"]
HKM --> HKAttr["HotkeyCommandAttribute"]
HKM --> HKCmd["HotkeyCommand"]
MacroMgr --> MacroInv["MacroInvoker"]
MacroInv --> Refl["Reflection"]
ShardMgr["ShardManager"] --> PluginEntry["PluginEntry"]
ShardMgr --> ManifestEntry["ManifestEntry"]
MBVM["MacroBrowserViewModel"] --> DB["Database"]
MBVM --> Filter["Filter"]
MBVM --> Meta["Metadata"]
MBVM --> Manifest["Manifest"]
Diagram sources
- Program.cs
- App.axaml.cs
- Engine.cs
- HotkeyManager.cs
- HotkeyCommandAttribute.cs
- HotkeyCommand.cs
- MacroManager.cs
- MacroInvoker.cs
- Reflection.cs
- ShardManager.cs
- PluginEntry.cs
- ManifestEntry.cs
- Browser.MacroBrowserViewModel.cs
- Browser.Data.Database.cs
- Browser.Data.Filter.cs
- Browser.Models.Metadata.cs
- Browser.Models.Manifest.cs
Section sources
- Program.cs
- App.axaml.cs
- Engine.cs
- HotkeyManager.cs
- HotkeyCommandAttribute.cs
- HotkeyCommand.cs
- MacroManager.cs
- MacroInvoker.cs
- Reflection.cs
- ShardManager.cs
- PluginEntry.cs
- ManifestEntry.cs
- Browser.MacroBrowserViewModel.cs
- Browser.Data.Database.cs
- Browser.Data.Filter.cs
- Browser.Models.Metadata.cs
- Browser.Models.Manifest.cs
Performance Considerations
- Minimize reflection overhead:
- Cache discovered types and constructed delegates where possible.
- Avoid heavy work on the UI thread:
- Use background threads for discovery and I/O; marshal results back to the UI thread.
- Limit assembly scanning scope:
- Scan only known directories and avoid unnecessary assemblies.
- Batch operations:
- Group registrations and UI updates to reduce churn.
- Resource cleanup:
- Ensure plugins release handles and unsubscribe events during teardown.
[No sources needed since this section provides general guidance]
Troubleshooting Guide
Common issues and strategies:
- Missing dependencies:
- Validate manifest entries and ensure required packages are present before loading.
- Type not found:
- Confirm that command classes implement the expected base type and have the required attribute.
- Invocation errors:
- Inspect exception messages from the invoker and verify parameter binding.
- UI thread violations:
- Ensure cross-thread calls use the dispatcher or UI invoker.
- Logging and diagnostics:
- Centralize logging around discovery and execution paths to capture failures early.
Relevant areas to inspect:
- Hotkey manager discovery and registration
- Macro invoker resolution and execution
- Reflection helper usage
- Engine initialization sequence
Section sources
Conclusion
The ClassicAssist.Avalonia plugin architecture leverages reflection-based discovery and dynamic loading to enable extensible hotkey commands and macro systems. By adhering to the established contracts and leveraging the provided managers, developers can create robust plugins that integrate seamlessly with the host application. Proper attention to security, performance, and error handling ensures reliable operation and maintainability.
[No sources needed since this section summarizes without analyzing specific files]
Appendices
How to Create a Custom Hotkey Command
- Implement a class deriving from the base command type.
- Decorate the class with the metadata attribute to expose it to discovery.
- Place the compiled assembly in the plugin directory so the loader can find it.
- Verify registration by checking the hotkey manager’s registry after startup.
References:
Section sources
How to Extend the Macro System
- Define new macro commands by implementing the appropriate interfaces or classes recognized by the invoker.
- Ensure your command types are discoverable by the macro manager’s scanning routine.
- Test parameter binding and error handling within your command implementation.
References:
Section sources
Plugin Distribution Format and Installation
- Package your plugin as a .NET assembly compatible with the host framework.
- Include a manifest describing metadata, dependencies, and version constraints.
- Install by placing the assembly and manifest in the expected directories.
- Use the browser or launcher to validate and update plugins.
References:
- PluginEntry.cs
- ManifestEntry.cs
- Browser.Models.Manifest.cs
- Browser.Models.Metadata.cs
- Browser.Data.Database.cs
Section sources
- PluginEntry.cs
- ManifestEntry.cs
- Browser.Models.Manifest.cs
- Browser.Models.Metadata.cs
- Browser.Data.Database.cs
Security, Sandboxing, and Isolation
- Prefer least-privilege principles: restrict file and network access where possible.
- Validate inputs and sanitize outputs to prevent injection.
- Consider running plugins in isolated contexts if supported by the host environment.
- Monitor resource usage and enforce timeouts for long-running operations.
[No sources needed since this section provides general guidance]
Debugging, Error Handling, and Performance Monitoring
- Enable detailed logging around discovery and execution paths.
- Capture and log exceptions with stack traces for diagnostics.
- Use performance counters or timers to measure hot paths.
- Validate plugin versions and dependencies prior to loading to fail fast.
References:
Section sources