# Plugin Architecture **Referenced Files in This Document** - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [HotkeyCommandAttribute.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommandAttribute.cs) - [HotkeyCommand.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommand.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [PacketFilter.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilter.cs) - [PacketFilterInfo.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilterInfo.cs) - [ClilocFilter.cs](file://ClassicAssist.Shared/Data/Filters/ClilocFilter.cs) - [RepeatedMessagesFilter.cs](file://ClassicAssist.Shared/Data/Filters/RepeatedMessagesFilter.cs) - [BardsMusicFilter.cs](file://ClassicAssist.Shared/Data/Filters/BardsMusicFilter.cs) - [LightLevelFilter.cs](file://ClassicAssist.Shared/Data/Filters/LightLevelFilter.cs) - [SeasonFilter.cs](file://ClassicAssist.Shared/Data/Filters/SeasonFilter.cs) - [WeatherFilter.cs](file://ClassicAssist.Shared/Data/Filters/WeatherFilter.cs) - [IConfigurableFilter.cs](file://ClassicAssist.Shared/Data/Filters/IConfigurableFilter.cs) - [FilterOptionsAttribute.cs](file://ClassicAssist.Shared/Data/Filters/FilterOptionsAttribute.cs) - [DynamicFilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/DynamicFilterEntry.cs) - [HotkeyEntry.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyEntry.cs) - [ShortcutKeys.cs](file://ClassicAssist.Shared/Data/Hotkeys/ShortcutKeys.cs) - [HotkeyChangedEventArgs.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyChangedEventArgs.cs) - [CommandsManager.cs](file://ClassicAssist.Shared/Data/Commands/CommandsManager.cs) - [MacroEntry.cs](file://ClassicAssist.Shared/Data/Macros/MacroEntry.cs) - [ParameterType.cs](file://ClassicAssist.Shared/Data/Macros/ParameterType.cs) - [SystemMessageTextWriter.cs](file://ClassicAssist.Shared/Data/Macros/SystemMessageTextWriter.cs) - [UseOnceList.cs](file://ClassicAssist.Shared/Data/Macros/UseOnceList.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) ## Table of Contents 1. [Introduction](#introduction) 2. [Project Structure](#project-structure) 3. [Core Components](#core-components) 4. [Architecture Overview](#architecture-overview) 5. [Detailed Component Analysis](#detailed-component-analysis) 6. [Dependency Analysis](#dependency-analysis) 7. [Performance Considerations](#performance-considerations) 8. [Troubleshooting Guide](#troubleshooting-guide) 9. [Conclusion](#conclusion) 10. [Appendices](#appendices) ## Introduction This document explains the plugin architecture and extensibility model in ClassicAssist.Avalonia. It focuses on how developers can extend functionality through: - Plugins discovered at runtime via attribute-based registration - Macro commands registered with attributes and invoked by a macro engine - Hotkey bindings that trigger commands or macros - A filter system for incoming and outgoing packets and UI messages It also covers lifecycle management, dependency injection patterns used across the system, security considerations when loading external assemblies, and practical guidelines for creating and distributing plugins. ## Project Structure The plugin-related features are implemented primarily in two layers: - Shared core (ClassicAssist.Shared): defines interfaces, attributes, managers, and built-in implementations for hotkeys, macros, filters, and packet filtering. - Avalonia host (ClassicAssist.Avalonia): bootstraps the application, initializes engines, and wires up platform-specific dispatching and UI invokers. ```mermaid graph TB subgraph "Host" Program["Program.cs"] App["App.axaml.cs"] EngineAva["Engine.cs (Avalonia)"] end subgraph "Shared Core" HotkeyMgr["HotkeyManager.cs"] MacroMgr["MacroManager.cs"] FiltersCore["Filters.cs"] InPktF["IncomingPacketFilters.cs"] OutPktF["OutgoingPacketFilters.cs"] CmdMgr["CommandsManager.cs"] end Program --> App App --> EngineAva EngineAva --> HotkeyMgr EngineAva --> MacroMgr EngineAva --> FiltersCore FiltersCore --> InPktF FiltersCore --> OutPktF EngineAva --> CmdMgr ``` **Diagram sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [CommandsManager.cs](file://ClassicAssist.Shared/Data/Commands/CommandsManager.cs) **Section sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) ## Core Components - Attribute-based discovery and registration: - Hotkey command attributes to register commands and bind them to keys. - Macro command attributes to expose callable functions to the macro language. - Filter attributes to configure UI options for filters. - Managers and engines: - Hotkey manager for binding and dispatching key combinations. - Macro manager and invoker for parsing and executing macro scripts. - Filter system for message and packet filtering. - Platform abstractions: - Dispatcher and UI invoker for cross-platform UI thread marshaling. Key responsibilities: - HotkeyManager: registers commands, binds shortcuts, raises events when keys are pressed. - MacroManager/MacroInvoker: discovers macro commands, parses parameters, executes commands safely. - Filters: centralizes filter registration and execution for both UI messages and network packets. - CommandsManager: manages top-level commands available to users and macros. **Section sources** - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [HotkeyCommandAttribute.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommandAttribute.cs) - [HotkeyCommand.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommand.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [CommandsManager.cs](file://ClassicAssist.Shared/Data/Commands/CommandsManager.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) ## Architecture Overview The extensibility model is centered around attribute-driven discovery and manager orchestration. At startup, the host initializes engines and scans loaded assemblies for types decorated with specific attributes. These types are then registered into their respective managers. Runtime invocation flows from user actions (hotkeys, macros, UI) through managers to concrete implementations. ```mermaid sequenceDiagram participant Host as "Host (Engine.cs)" participant Scanner as "Reflection Scanner" participant HKM as "HotkeyManager" participant MM as "MacroManager" participant FI as "Filters" participant Inv as "MacroInvoker" Host->>Scanner : "Scan assemblies for attributes" Scanner-->>HKM : "Register HotkeyCommandAttribute types" Scanner-->>MM : "Register Macro command types" Scanner-->>FI : "Register FilterEntry types" Host->>HKM : "Bind shortcuts" Host->>FI : "Enable filters" Note over Host,FI : "Runtime ready" User->>HKM : "Press shortcut" HKM-->>Inv : "Invoke associated macro/command" Inv->>MM : "Resolve and execute macro command" MM-->>Inv : "Result" Inv-->>User : "Effect applied" ``` **Diagram sources** - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) ## Detailed Component Analysis ### Attribute-Based Registration System The system uses attributes to mark classes and methods for automatic discovery and registration: - Hotkey commands: types implementing a base class or interface and decorated with an attribute that specifies metadata such as display name and default shortcut. - Macro commands: methods or classes annotated to be discoverable by the macro engine. - Filters: classes implementing a filter contract and optionally decorated with configuration attributes. Registration typically occurs during engine initialization by scanning assemblies for these attributes and calling manager APIs to add entries. ```mermaid classDiagram class HotkeyCommandAttribute { +string Name +string Shortcut } class HotkeyCommand { +Execute() } class HotkeyManager { +Register(command) +Bind(shortcut, command) +OnKeyPressed(event) } class MacroManager { +DiscoverCommands() +Register(command) } class MacroInvoker { +Invoke(name, args) } class FilterEntry { +Apply(message) bool } class Filters { +Register(filter) +Process(message) } HotkeyManager --> HotkeyCommand : "registers" HotkeyManager --> HotkeyCommandAttribute : "reads metadata" MacroManager --> MacroInvoker : "invokes" Filters --> FilterEntry : "applies" ``` **Diagram sources** - [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) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) **Section sources** - [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) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) ### Command Registration Process for Macro Commands and Hotkey Bindings - Macro command registration: - The macro manager scans for command definitions (methods or classes) marked with appropriate attributes. - Each command is parsed for parameter types and metadata, then stored in a registry accessible to the macro invoker. - Hotkey binding: - The hotkey manager reads attributes to determine default shortcuts and registers handlers. - When a key combination is detected, the manager invokes the corresponding action, which may call into the macro invoker to run a macro command. ```mermaid flowchart TD Start(["Startup"]) --> Scan["Scan assemblies for attributes"] Scan --> RegisterHK["Register Hotkey commands"] Scan --> RegisterMC["Register Macro commands"] RegisterHK --> BindShortcuts["Bind default shortcuts"] RegisterMC --> Ready["Engines ready"] BindShortcuts --> Ready Ready --> Press["User presses shortcut"] Press --> Dispatch["HotkeyManager dispatches"] Dispatch --> InvokeMacro["MacroInvoker executes macro command"] InvokeMacro --> End(["Done"]) ``` **Diagram sources** - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [HotkeyCommandAttribute.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommandAttribute.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) **Section sources** - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [HotkeyCommandAttribute.cs](file://ClassicAssist.Shared/Data/Hotkeys/Commands/HotkeyCommandAttribute.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) ### Filter System Architecture The filter system supports both UI message filtering and network packet filtering: - UI message filters implement a common entry type and are managed centrally. - Packet filters integrate with incoming and outgoing filter pipelines. - Some filters provide configuration UI via attributes. ```mermaid classDiagram class FilterEntry { +bool Apply(object message) } class DynamicFilterEntry { +object Options } class IConfigurableFilter { +ShowConfigureDialog() } class FilterOptionsAttribute { +string Title +string Description } class ClilocFilter class RepeatedMessagesFilter class BardsMusicFilter class LightLevelFilter class SeasonFilter class WeatherFilter class IncomingPacketFilters class OutgoingPacketFilters class PacketFilter class PacketFilterInfo FilterEntry <|-- DynamicFilterEntry IConfigurableFilter <|.. ClilocFilter IConfigurableFilter <|.. RepeatedMessagesFilter FilterOptionsAttribute ..> ClilocFilter FilterOptionsAttribute ..> RepeatedMessagesFilter IncomingPacketFilters --> PacketFilter : "uses" OutgoingPacketFilters --> PacketFilter : "uses" PacketFilter --> PacketFilterInfo : "configures" ``` **Diagram sources** - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [DynamicFilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/DynamicFilterEntry.cs) - [IConfigurableFilter.cs](file://ClassicAssist.Shared/Data/Filters/IConfigurableFilter.cs) - [FilterOptionsAttribute.cs](file://ClassicAssist.Shared/Data/Filters/FilterOptionsAttribute.cs) - [ClilocFilter.cs](file://ClassicAssist.Shared/Data/Filters/ClilocFilter.cs) - [RepeatedMessagesFilter.cs](file://ClassicAssist.Shared/Data/Filters/RepeatedMessagesFilter.cs) - [BardsMusicFilter.cs](file://ClassicAssist.Shared/Data/Filters/BardsMusicFilter.cs) - [LightLevelFilter.cs](file://ClassicAssist.Shared/Data/Filters/LightLevelFilter.cs) - [SeasonFilter.cs](file://ClassicAssist.Shared/Data/Filters/SeasonFilter.cs) - [WeatherFilter.cs](file://ClassicAssist.Shared/Data/Filters/WeatherFilter.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [PacketFilter.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilter.cs) - [PacketFilterInfo.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilterInfo.cs) **Section sources** - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [DynamicFilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/DynamicFilterEntry.cs) - [IConfigurableFilter.cs](file://ClassicAssist.Shared/Data/Filters/IConfigurableFilter.cs) - [FilterOptionsAttribute.cs](file://ClassicAssist.Shared/Data/Filters/FilterOptionsAttribute.cs) - [ClilocFilter.cs](file://ClassicAssist.Shared/Data/Filters/ClilocFilter.cs) - [RepeatedMessagesFilter.cs](file://ClassicAssist.Shared/Data/Filters/RepeatedMessagesFilter.cs) - [BardsMusicFilter.cs](file://ClassicAssist.Shared/Data/Filters/BardsMusicFilter.cs) - [LightLevelFilter.cs](file://ClassicAssist.Shared/Data/Filters/LightLevelFilter.cs) - [SeasonFilter.cs](file://ClassicAssist.Shared/Data/Filters/SeasonFilter.cs) - [WeatherFilter.cs](file://ClassicAssist.Shared/Data/Filters/WeatherFilter.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [PacketFilter.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilter.cs) - [PacketFilterInfo.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilterInfo.cs) ### Practical Examples #### Creating a Custom Macro Command Steps: - Define a method or class representing the command. - Annotate it with the appropriate macro command attribute so the macro manager can discover it. - Implement parameter handling using the defined parameter types. - Ensure thread safety if interacting with game state; use the dispatcher to marshal UI updates. References: - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [ParameterType.cs](file://ClassicAssist.Shared/Data/Macros/ParameterType.cs) - [SystemMessageTextWriter.cs](file://ClassicAssist.Shared/Data/Macros/SystemMessageTextWriter.cs) - [UseOnceList.cs](file://ClassicAssist.Shared/Data/Macros/UseOnceList.cs) **Section sources** - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [ParameterType.cs](file://ClassicAssist.Shared/Data/Macros/ParameterType.cs) - [SystemMessageTextWriter.cs](file://ClassicAssist.Shared/Data/Macros/SystemMessageTextWriter.cs) - [UseOnceList.cs](file://ClassicAssist.Shared/Data/Macros/UseOnceList.cs) #### Implementing a New Filter Steps: - Create a class implementing the filter entry contract. - Optionally implement the configurable filter interface and decorate with filter options attributes to enable configuration UI. - Register the filter during engine initialization or rely on attribute scanning. - Implement the filtering logic in the apply method. References: - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [IConfigurableFilter.cs](file://ClassicAssist.Shared/Data/Filters/IConfigurableFilter.cs) - [FilterOptionsAttribute.cs](file://ClassicAssist.Shared/Data/Filters/FilterOptionsAttribute.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) Example built-ins for reference: - [ClilocFilter.cs](file://ClassicAssist.Shared/Data/Filters/ClilocFilter.cs) - [RepeatedMessagesFilter.cs](file://ClassicAssist.Shared/Data/Filters/RepeatedMessagesFilter.cs) - [BardsMusicFilter.cs](file://ClassicAssist.Shared/Data/Filters/BardsMusicFilter.cs) - [LightLevelFilter.cs](file://ClassicAssist.Shared/Data/Filters/LightLevelFilter.cs) - [SeasonFilter.cs](file://ClassicAssist.Shared/Data/Filters/SeasonFilter.cs) - [WeatherFilter.cs](file://ClassicAssist.Shared/Data/Filters/WeatherFilter.cs) **Section sources** - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [IConfigurableFilter.cs](file://ClassicAssist.Shared/Data/Filters/IConfigurableFilter.cs) - [FilterOptionsAttribute.cs](file://ClassicAssist.Shared/Data/Filters/FilterOptionsAttribute.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [ClilocFilter.cs](file://ClassicAssist.Shared/Data/Filters/ClilocFilter.cs) - [RepeatedMessagesFilter.cs](file://ClassicAssist.Shared/Data/Filters/RepeatedMessagesFilter.cs) - [BardsMusicFilter.cs](file://ClassicAssist.Shared/Data/Filters/BardsMusicFilter.cs) - [LightLevelFilter.cs](file://ClassicAssist.Shared/Data/Filters/LightLevelFilter.cs) - [SeasonFilter.cs](file://ClassicAssist.Shared/Data/Filters/SeasonFilter.cs) - [WeatherFilter.cs](file://ClassicAssist.Shared/Data/Filters/WeatherFilter.cs) #### Extending the Hotkey System Steps: - Create a new hotkey command class and annotate it with the hotkey command attribute to specify metadata and default shortcut. - Ensure the command is discoverable by the reflection scanner during engine startup. - Bind additional shortcuts programmatically if needed. - Use the dispatcher to update UI elements safely. References: - [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) - [HotkeyEntry.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyEntry.cs) - [ShortcutKeys.cs](file://ClassicAssist.Shared/Data/Hotkeys/ShortcutKeys.cs) - [HotkeyChangedEventArgs.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyChangedEventArgs.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) **Section sources** - [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) - [HotkeyEntry.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyEntry.cs) - [ShortcutKeys.cs](file://ClassicAssist.Shared/Data/Hotkeys/ShortcutKeys.cs) - [HotkeyChangedEventArgs.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyChangedEventArgs.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) ### Plugin Lifecycle Management Lifecycle phases: - Discovery: scan assemblies for attribute-decorated types. - Registration: add discovered components to managers (hotkeys, macros, filters). - Initialization: perform any setup required by plugins. - Runtime: respond to user actions (hotkeys, macros) and process messages/packets through filters. - Shutdown: release resources and clean up registrations. Implementation anchors: - Host initialization and engine wiring: - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) **Section sources** - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) ### Dependency Injection Patterns While there is no explicit DI container, the codebase uses constructor and property injection via managers and services: - Managers hold references to shared services (e.g., dispatcher, UI invoker). - Filters and commands receive dependencies through constructors or properties. - The host wires up platform-specific implementations (dispatcher and UI invoker) before initializing managers. References: - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) **Section sources** - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) ### Security Considerations for Loaded Plugins - Isolation: load plugin assemblies in separate contexts where possible to limit impact. - Permissions: restrict access to sensitive APIs and file system operations. - Validation: verify signatures and allowlists for trusted plugins. - Sandboxing: avoid granting full trust to third-party code; prefer least privilege. - Error containment: catch exceptions in plugin code paths to prevent crashes. - Resource limits: throttle CPU and memory usage for plugin operations. [No sources needed since this section provides general guidance] ## Dependency Analysis High-level dependencies among plugin subsystems: ```mermaid graph LR HKAttr["HotkeyCommandAttribute.cs"] --> HKCmd["HotkeyCommand.cs"] HKCmd --> HKM["HotkeyManager.cs"] HKM --> HKEntry["HotkeyEntry.cs"] HKM --> HKChanged["HotkeyChangedEventArgs.cs"] HKM --> HKShortcut["ShortcutKeys.cs"] MCAttr["MacroManager.cs"] --> MCI["MacroInvoker.cs"] MCI --> Param["ParameterType.cs"] MCI --> SysMsg["SystemMessageTextWriter.cs"] MCI --> UseOnce["UseOnceList.cs"] FCore["Filters.cs"] --> FEntry["FilterEntry.cs"] FCore --> IFilt["IConfigurableFilter.cs"] FCore --> FOpt["FilterOptionsAttribute.cs"] FCore --> InPkt["IncomingPacketFilters.cs"] FCore --> OutPkt["OutgoingPacketFilters.cs"] InPkt --> PktF["PacketFilter.cs"] OutPkt --> PktF PktF --> PktInfo["PacketFilterInfo.cs"] ``` **Diagram sources** - [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) - [HotkeyEntry.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyEntry.cs) - [HotkeyChangedEventArgs.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyChangedEventArgs.cs) - [ShortcutKeys.cs](file://ClassicAssist.Shared/Data/Hotkeys/ShortcutKeys.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [ParameterType.cs](file://ClassicAssist.Shared/Data/Macros/ParameterType.cs) - [SystemMessageTextWriter.cs](file://ClassicAssist.Shared/Data/Macros/SystemMessageTextWriter.cs) - [UseOnceList.cs](file://ClassicAssist.Shared/Data/Macros/UseOnceList.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [FilterEntry.cs](file://ClassicAssist.Shared/Data/Filters/FilterEntry.cs) - [IConfigurableFilter.cs](file://ClassicAssist.Shared/Data/Filters/IConfigurableFilter.cs) - [FilterOptionsAttribute.cs](file://ClassicAssist.Shared/Data/Filters/FilterOptionsAttribute.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [PacketFilter.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilter.cs) - [PacketFilterInfo.cs](file://ClassicAssist.Shared/UO/Network/PacketFilter/PacketFilterInfo.cs) **Section sources** - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) ## Performance Considerations - Minimize reflection overhead by caching discovered types and metadata. - Avoid heavy work on the UI thread; use the dispatcher to offload tasks. - Batch filter processing and short-circuit early when possible. - Limit allocations in hot paths (packet filters, macro execution). - Use efficient data structures for large collections (e.g., entity lists). [No sources needed since this section provides general guidance] ## Troubleshooting Guide Common issues and resolutions: - Hotkeys not firing: - Verify attribute decoration and default shortcut values. - Check for conflicts between multiple bindings. - Ensure the hotkey manager is initialized and event listeners are active. - Macro commands not found: - Confirm the command is discoverable and properly attributed. - Validate parameter types match the macro invocation. - Inspect logs for errors during command resolution. - Filters not applying: - Ensure the filter is registered and enabled. - Review filter conditions and configuration options. - Test with minimal logic to isolate issues. - UI updates not appearing: - Marshal UI updates onto the correct thread using the dispatcher. - Avoid long-running operations on the UI thread. **Section sources** - [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs) - [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs) - [MacroInvoker.cs](file://ClassicAssist.Shared/Data/Macros/MacroInvoker.cs) - [Filters.cs](file://ClassicAssist.Shared/Engine/Filters.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) ## Conclusion ClassicAssist.Avalonia’s plugin architecture leverages attribute-based discovery and manager-driven orchestration to provide a flexible extensibility model. Developers can add macro commands, hotkey bindings, and filters with minimal boilerplate. By following the guidelines for lifecycle management, dependency injection, and security, you can build robust, maintainable plugins that integrate seamlessly with the host application. [No sources needed since this section summarizes without analyzing specific files] ## Appendices ### Guidelines for Plugin Development and Distribution - Naming and organization: - Use clear namespaces and assembly names for plugins. - Keep related commands and filters grouped logically. - Attributes and contracts: - Follow established attribute conventions for discovery. - Implement interfaces consistently to ensure compatibility. - Configuration: - Provide user-friendly titles and descriptions for filter options. - Persist settings securely and validate inputs. - Testing: - Unit test filter logic and macro commands. - Simulate hotkey events to verify bindings. - Distribution: - Package plugins as separate assemblies. - Include documentation and version information. - Provide installation instructions and compatibility notes. [No sources needed since this section provides general guidance]