Files
ClassicAssist.Avalonia/.qoder/repowiki/en/content/Core Concepts/Plugin Architecture.md
T
Beyhan Oğur d5f52cc3d3 first commit
2026-09-03 04:45:36 +03:00

30 KiB
Raw Blame History

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
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion
  10. 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.
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

Section sources

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

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.

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

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.

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

Section sources

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.
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

Section sources

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.
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

Section sources

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:

Section sources

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:

Example built-ins for reference:

Section sources

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:

Section sources

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:

Section sources

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:

Section sources

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:

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

Section sources

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

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]