first commit
This commit is contained in:
@@ -0,0 +1,459 @@
|
||||
# Architecture Overview
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [README.md](file://README.md)
|
||||
- [ClassicAssist.Shared/Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [ClassicAssist.Avalonia/Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [ClassicAssist.Avalonia/App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [ClassicAssist.Launcher/MainViewModel.cs](file://ClassicAssist.Launcher/MainViewModel.cs)
|
||||
- [ClassicAssist.Shared/UO/Network/PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [ClassicAssist.Shared/UO/Objects/Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [ClassicAssist.Shared/Data/Macros/MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
- [ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [ClassicAssist.Avalonia/Views/MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [ClassicAssist.Avalonia/AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [ClassicAssist/WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [ClassicAssist.Shared/IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
</cite>
|
||||
|
||||
## 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)
|
||||
|
||||
## Introduction
|
||||
|
||||
ClassicAssist.Avalonia is a cross-platform Ultima Online game assistant application built using the Avalonia UI framework. The system follows a layered architecture pattern that separates concerns between the user interface layer, shared business logic, and platform-specific implementations. This design enables code reuse across multiple UI frameworks (WPF and Avalonia) while maintaining clean separation of responsibilities.
|
||||
|
||||
The application provides comprehensive game automation features including macro scripting, agent management, filtering systems, and real-time packet processing for Ultima Online gameplay enhancement.
|
||||
|
||||
## Project Structure
|
||||
|
||||
The ClassicAssist.Avalonia solution follows a modular architecture with clear separation between different functional areas:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "UI Layer"
|
||||
A[ClassicAssist.Avalonia]
|
||||
B[ClassicAssist.WPF]
|
||||
C[ClassicAssist.Launcher]
|
||||
end
|
||||
subgraph "Shared Core"
|
||||
D[ClassicAssist.Shared]
|
||||
E[ClassicAssist.Tests]
|
||||
end
|
||||
subgraph "External Dependencies"
|
||||
F[UO Game Engine]
|
||||
G[Plugin System]
|
||||
end
|
||||
A --> D
|
||||
B --> D
|
||||
C --> D
|
||||
D --> F
|
||||
D --> G
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Avalonia/Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [ClassicAssist.Shared/Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [ClassicAssist.Launcher/MainViewModel.cs](file://ClassicAssist.Launcher/MainViewModel.cs)
|
||||
|
||||
The project structure demonstrates a clean separation where:
|
||||
- **ClassicAssist.Shared**: Contains all business logic, data models, and core functionality
|
||||
- **ClassicAssist.Avalonia**: Avalonia-specific UI implementation
|
||||
- **ClassicAssist.WPF**: WPF-specific UI implementation
|
||||
- **ClassicAssist.Launcher**: Application launcher and configuration management
|
||||
|
||||
**Section sources**
|
||||
- [README.md](file://README.md)
|
||||
- [ClassicAssist.Avalonia/Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
|
||||
## Core Components
|
||||
|
||||
### Shared Library Architecture
|
||||
|
||||
The ClassicAssist.Shared library serves as the foundation containing all reusable business logic:
|
||||
|
||||
#### Network Processing Pipeline
|
||||
The network subsystem handles Ultima Online protocol communication through a sophisticated packet processing pipeline:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Game as "UO Game Client"
|
||||
participant Network as "PacketHandler"
|
||||
participant Filters as "PacketFilters"
|
||||
participant Handlers as "PacketHandlers"
|
||||
participant UI as "UI Layer"
|
||||
Game->>Network : Incoming Packet
|
||||
Network->>Filters : Apply Filters
|
||||
Filters-->>Network : Filtered Data
|
||||
Network->>Handlers : Route to Handler
|
||||
Handlers->>Handlers : Process Logic
|
||||
Handlers->>UI : Update State
|
||||
UI-->>Game : Response Actions
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Shared/UO/Network/PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [ClassicAssist.Shared/UO/Objects/Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
|
||||
#### Plugin-Based Extensibility Model
|
||||
|
||||
The system implements a robust plugin architecture allowing third-party extensions:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PluginBase {
|
||||
<<abstract>>
|
||||
+string Name
|
||||
+string Version
|
||||
+Initialize() void
|
||||
+Execute(context) object
|
||||
+Dispose() void
|
||||
}
|
||||
class MacroCommand {
|
||||
+string CommandName
|
||||
+string Description
|
||||
+Execute(parameters) object
|
||||
}
|
||||
class AgentPlugin {
|
||||
+AgentType Type
|
||||
+ConfigureDialog() Window
|
||||
+ProcessLogic() void
|
||||
}
|
||||
class FilterPlugin {
|
||||
+FilterCriteria Criteria
|
||||
+ApplyFilter(data) bool
|
||||
}
|
||||
PluginBase <|-- MacroCommand
|
||||
PluginBase <|-- AgentPlugin
|
||||
PluginBase <|-- FilterPlugin
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Shared/Data/Macros/MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
|
||||
**Section sources**
|
||||
- [ClassicAssist.Shared/Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [ClassicAssist.Shared/UO/Network/PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### High-Level System Architecture
|
||||
|
||||
The ClassicAssist.Avalonia system follows a multi-layered architecture pattern:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Presentation Layer"
|
||||
A[Avalonia UI]
|
||||
B[WPF UI]
|
||||
C[Launcher UI]
|
||||
end
|
||||
subgraph "Application Layer"
|
||||
D[Main Application Controller]
|
||||
E[Window Management]
|
||||
F[State Management]
|
||||
end
|
||||
subgraph "Business Logic Layer"
|
||||
G[Shared Business Logic]
|
||||
H[Game Engine Integration]
|
||||
I[Plugin Framework]
|
||||
end
|
||||
subgraph "Data Layer"
|
||||
J[Configuration Storage]
|
||||
K[Game Data Cache]
|
||||
L[Network Communication]
|
||||
end
|
||||
A --> D
|
||||
B --> D
|
||||
C --> D
|
||||
D --> G
|
||||
D --> H
|
||||
D --> I
|
||||
G --> J
|
||||
H --> K
|
||||
H --> L
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Avalonia/App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [ClassicAssist.Shared/Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
### Cross-Platform UI Abstraction
|
||||
|
||||
The system achieves cross-platform compatibility through abstraction layers:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class IDispatcher {
|
||||
<<interface>>
|
||||
+Invoke(action) void
|
||||
+BeginInvoke(action) void
|
||||
+Synchronize(context) void
|
||||
}
|
||||
class AvaloniaDispatcher {
|
||||
+Invoke(action) void
|
||||
+BeginInvoke(action) void
|
||||
+Synchronize(context) void
|
||||
}
|
||||
class WPFDispatcher {
|
||||
+Invoke(action) void
|
||||
+BeginInvoke(action) void
|
||||
+Synchronize(context) void
|
||||
}
|
||||
class IUIInvoker {
|
||||
<<interface>>
|
||||
+ShowMessageBox(message) void
|
||||
+OpenFileDialog() string
|
||||
+SaveFileDialog() string
|
||||
}
|
||||
class AvaloniaUIInvoker {
|
||||
+ShowMessageBox(message) void
|
||||
+OpenFileDialog() string
|
||||
+SaveFileDialog() string
|
||||
}
|
||||
class WPFUIInvoker {
|
||||
+ShowMessageBox(message) void
|
||||
+OpenFileDialog() string
|
||||
+SaveFileDialog() string
|
||||
}
|
||||
IDispatcher <|.. AvaloniaDispatcher
|
||||
IDispatcher <|.. WPFDispatcher
|
||||
IUIInvoker <|.. AvaloniaUIInvoker
|
||||
IUIInvoker <|.. WPFUIInvoker
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Shared/IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [ClassicAssist.Avalonia/AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [ClassicAssist/WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
|
||||
**Section sources**
|
||||
- [ClassicAssist.Avalonia/App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [ClassicAssist.Avalonia/AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### Main Application Flow
|
||||
|
||||
The application initialization and main flow follows a structured startup sequence:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Program as "Program.Main"
|
||||
participant App as "Avalonia Application"
|
||||
participant Engine as "Shared Engine"
|
||||
participant MainWindow as "Main Window"
|
||||
participant ViewModel as "Main Window ViewModel"
|
||||
Program->>App : Initialize Application
|
||||
App->>Engine : Load Shared Engine
|
||||
Engine->>Engine : Initialize Plugins
|
||||
Engine->>Engine : Setup Network
|
||||
App->>MainWindow : Create Main Window
|
||||
MainWindow->>ViewModel : Bind ViewModel
|
||||
ViewModel->>Engine : Request Initial Data
|
||||
Engine-->>ViewModel : Provide Data
|
||||
ViewModel-->>MainWindow : Update UI
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Avalonia/Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [ClassicAssist.Avalonia/App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [ClassicAssist.Shared/Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
### Data Flow Architecture
|
||||
|
||||
The system implements a unidirectional data flow pattern ensuring predictable state updates:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Network Packet] --> B[Packet Handler]
|
||||
B --> C{Filter Check}
|
||||
C --> |Pass| D[Business Logic Processor]
|
||||
C --> |Blocked| E[Discard]
|
||||
D --> F[State Manager]
|
||||
F --> G[ViewModel Update]
|
||||
G --> H[UI Binding]
|
||||
H --> I[Visual Update]
|
||||
J[User Action] --> K[Command Handler]
|
||||
K --> L[Business Logic]
|
||||
L --> M[Network Outgoing]
|
||||
M --> N[Game Server]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Shared/UO/Network/PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
|
||||
### Plugin System Implementation
|
||||
|
||||
The extensible plugin architecture supports multiple extension points:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PluginManager {
|
||||
-Dictionary~string,PluginBase~ plugins
|
||||
+LoadPlugins(path) void
|
||||
+GetPlugin(name) PluginBase
|
||||
+ExecutePlugin(name, context) object
|
||||
+UnloadPlugin(name) void
|
||||
}
|
||||
class PluginBase {
|
||||
<<abstract>>
|
||||
#string name
|
||||
#string version
|
||||
#string description
|
||||
+virtual Initialize() void
|
||||
+virtual Execute(context) object
|
||||
+virtual Dispose() void
|
||||
}
|
||||
class MacroPlugin {
|
||||
+MacroEntry[] macros
|
||||
+RegisterCommands() void
|
||||
+ExecuteMacro(name) void
|
||||
}
|
||||
class AgentPlugin {
|
||||
+IAgent[] agents
|
||||
+StartAgents() void
|
||||
+StopAgents() void
|
||||
}
|
||||
PluginManager --> PluginBase : manages
|
||||
PluginBase <|-- MacroPlugin
|
||||
PluginBase <|-- AgentPlugin
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Shared/Data/Macros/MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
|
||||
**Section sources**
|
||||
- [ClassicAssist.Avalonia/Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [ClassicAssist.Avalonia/Views/MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
|
||||
### Component Coupling and Cohesion
|
||||
|
||||
The system demonstrates high cohesion within modules and loose coupling between components:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "High Cohesion Modules"
|
||||
A[Network Module]
|
||||
B[UI Module]
|
||||
C[Plugin Module]
|
||||
D[Data Module]
|
||||
end
|
||||
subgraph "Low Coupling Interfaces"
|
||||
E[IDispatcher]
|
||||
F[IUIInvoker]
|
||||
G[IPlugin]
|
||||
H[IConfigProvider]
|
||||
end
|
||||
A --> E
|
||||
B --> F
|
||||
C --> G
|
||||
D --> H
|
||||
```
|
||||
|
||||
### External Dependencies Management
|
||||
|
||||
The system manages external dependencies through NuGet packages and assembly references:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "Core Dependencies"
|
||||
A[Avalonia Framework]
|
||||
B[Newtonsoft.Json]
|
||||
C[Microsoft.CSharp]
|
||||
end
|
||||
subgraph "Game Integration"
|
||||
D[UO Protocol Parser]
|
||||
E[Memory Access Libraries]
|
||||
end
|
||||
subgraph "UI Extensions"
|
||||
F[Avalonia.Controls]
|
||||
G[Avalonia.Themes]
|
||||
end
|
||||
A --> F
|
||||
A --> G
|
||||
B --> C
|
||||
D --> E
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [ClassicAssist.Avalonia/ClassicAssist.Avalonia.csproj](file://ClassicAssist.Avalonia/ClassicAssist.Avalonia.csproj)
|
||||
- [ClassicAssist.Shared/ClassicAssist.Shared.csproj](file://ClassicAssist.Shared/ClassicAssist.Shared.csproj)
|
||||
|
||||
**Section sources**
|
||||
- [ClassicAssist.Shared/Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [ClassicAssist.Avalonia/Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Memory Management and Optimization
|
||||
|
||||
The system implements several performance optimization strategies:
|
||||
|
||||
- **Object Pooling**: Reusable objects for frequently created instances
|
||||
- **Lazy Loading**: Deferred loading of heavy resources
|
||||
- **Background Processing**: Asynchronous operations for non-blocking UI
|
||||
- **Event Aggregation**: Efficient event handling patterns
|
||||
|
||||
### Threading Model
|
||||
|
||||
The application uses a single-threaded UI model with background workers:
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Idle
|
||||
Idle --> Initializing : "Application Start"
|
||||
Initializing --> Running : "Engine Ready"
|
||||
Running --> Paused : "Pause Request"
|
||||
Paused --> Running : "Resume Request"
|
||||
Running --> Stopping : "Shutdown"
|
||||
Stopping --> [*] : "Cleanup Complete"
|
||||
```
|
||||
|
||||
## Troubleshooting Guide
|
||||
|
||||
### Common Issues and Solutions
|
||||
|
||||
#### Network Connectivity Problems
|
||||
- Verify Ultima Online client connection status
|
||||
- Check firewall settings for port access
|
||||
- Validate packet filter configurations
|
||||
|
||||
#### UI Rendering Issues
|
||||
- Ensure proper dispatcher usage for thread safety
|
||||
- Verify Avalonia theme resource loading
|
||||
- Check for memory leaks in custom controls
|
||||
|
||||
#### Plugin Loading Failures
|
||||
- Validate plugin assembly signatures
|
||||
- Check dependency version compatibility
|
||||
- Review plugin manifest files
|
||||
|
||||
**Section sources**
|
||||
- [ClassicAssist.Shared/Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [ClassicAssist.Avalonia/App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
|
||||
## Conclusion
|
||||
|
||||
ClassicAssist.Avalonia demonstrates a well-architected cross-platform game assistant application that successfully balances code reuse, maintainability, and extensibility. The layered architecture with clear separation of concerns enables efficient development across multiple UI frameworks while providing a robust foundation for plugin-based extensions.
|
||||
|
||||
The system's design principles emphasize:
|
||||
- **Separation of Concerns**: Clear boundaries between UI, business logic, and data layers
|
||||
- **Cross-Platform Compatibility**: Abstracted interfaces enabling multiple UI implementations
|
||||
- **Extensibility**: Plugin architecture supporting third-party enhancements
|
||||
- **Performance**: Optimized data flow and memory management strategies
|
||||
|
||||
This architectural approach ensures long-term maintainability while providing a solid foundation for future feature additions and platform support expansion.
|
||||
@@ -0,0 +1,424 @@
|
||||
# Core Concepts
|
||||
|
||||
<cite>
|
||||
**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)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
- [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
- [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs)
|
||||
- [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs)
|
||||
</cite>
|
||||
|
||||
## 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)
|
||||
|
||||
## Introduction
|
||||
This document explains the core concepts and architecture of ClassicAssist.Avalonia, focusing on:
|
||||
- MVVM implementation across the Avalonia UI layer
|
||||
- The Engine class as the central orchestrator for lifecycle, events, and component initialization
|
||||
- Dispatcher pattern and UI invoker abstraction for cross-thread UI updates
|
||||
- Plugin architecture enabling extensibility via macro commands, agents, and filters
|
||||
- Network packet handling system and its relationship with game entities
|
||||
- Cross-platform considerations specific to Avalonia
|
||||
- Performance optimization strategies
|
||||
|
||||
## Project Structure
|
||||
The solution is organized into multiple projects:
|
||||
- ClassicAssist.Avalonia: Avalonia-based UI application entry point, platform-specific dispatcher and UI invoker, and views
|
||||
- ClassicAssist.Shared: Shared business logic, data models, network layer, macros, hotkeys, chat, and MVVM base classes
|
||||
- ClassicAssist (WPF): Legacy WPF UI project retained for compatibility
|
||||
- ClassicAssist.Launcher: Launcher application for shard selection and profile management
|
||||
- Tests and utilities under separate projects
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Avalonia UI"
|
||||
A_Program["Program.cs"]
|
||||
A_App["App.axaml.cs"]
|
||||
A_Engine["Engine.cs"]
|
||||
A_Dispatcher["AvaloniaDispatcher.cs"]
|
||||
A_Invoker["AvaloniaUIInvoker.cs"]
|
||||
A_MainView["Views/MainWindow.axaml.cs"]
|
||||
end
|
||||
subgraph "Shared Core"
|
||||
S_VM_Base["BaseViewModel.cs"]
|
||||
S_VM_Main["MainWindowViewModel.cs"]
|
||||
S_IDisp["IDispatcher.cs"]
|
||||
S_UIInv["IUIInvoker.cs"]
|
||||
S_MacroMgr["MacroManager.cs"]
|
||||
S_HotkeyMgr["HotkeyManager.cs"]
|
||||
S_ChatMgr["ChatManager.cs"]
|
||||
S_InPkt["IncomingPacketHandlers.cs"]
|
||||
S_OutPkt["OutgoingPacketHandlers.cs"]
|
||||
S_PktHdlr["PacketHandler.cs"]
|
||||
S_Entity["Entity.cs"]
|
||||
S_Mobile["Mobile.cs"]
|
||||
S_Item["Item.cs"]
|
||||
end
|
||||
A_Program --> A_App
|
||||
A_App --> A_Engine
|
||||
A_Engine --> A_Dispatcher
|
||||
A_Engine --> A_Invoker
|
||||
A_MainView --> S_VM_Main
|
||||
S_VM_Main --> S_VM_Base
|
||||
A_Engine --> S_MacroMgr
|
||||
A_Engine --> S_HotkeyMgr
|
||||
A_Engine --> S_ChatMgr
|
||||
A_Engine --> S_InPkt
|
||||
A_Engine --> S_OutPkt
|
||||
A_Engine --> S_PktHdlr
|
||||
S_InPkt --> S_Entity
|
||||
S_OutPkt --> S_Entity
|
||||
```
|
||||
|
||||
**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)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
- [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs)
|
||||
- [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.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
|
||||
- Engine (Avalonia): Central orchestrator that initializes subsystems, wires event handlers, manages lifecycle, and coordinates UI and network layers.
|
||||
- AvaloniaDispatcher: Implements IDispatcher for Avalonia, marshaling work onto the UI thread.
|
||||
- AvaloniaUIInvoker: Implements IUIInvoker to provide platform-independent UI operations (e.g., message boxes).
|
||||
- MVVM Layer: Views (Avalonia XAML/Code-behind) bind to ViewModels; ViewModels inherit from BaseViewModel and expose state and commands.
|
||||
- Network Layer: Packet handlers process incoming/outgoing packets and update shared entity collections.
|
||||
- Extensibility: MacroManager, HotkeyManager, and ChatManager provide extension points for commands, hotkeys, and chat integrations.
|
||||
|
||||
Key responsibilities:
|
||||
- Lifecycle: Application startup, configuration loading, plugin discovery, and graceful shutdown.
|
||||
- Event-driven communication: Decouples components via events and managers.
|
||||
- Cross-thread safety: Ensures UI updates occur on the UI thread using the dispatcher.
|
||||
- Platform abstraction: Uses interfaces for UI invocations to remain platform-neutral.
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
|
||||
## Architecture Overview
|
||||
The system follows a layered architecture:
|
||||
- Presentation Layer (Avalonia): Views and ViewModels implement MVVM.
|
||||
- Core Layer (Shared): Business logic, data models, network processing, and managers.
|
||||
- Infrastructure Layer: Platform-specific implementations for dispatcher and UI invoker.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as "App.axaml.cs"
|
||||
participant Engine as "Engine.cs"
|
||||
participant Disp as "AvaloniaDispatcher.cs"
|
||||
participant Inv as "AvaloniaUIInvoker.cs"
|
||||
participant VM as "MainWindowViewModel.cs"
|
||||
participant NetIn as "IncomingPacketHandlers.cs"
|
||||
participant NetOut as "OutgoingPacketHandlers.cs"
|
||||
participant Pkt as "PacketHandler.cs"
|
||||
participant Ent as "Entity.cs"
|
||||
App->>Engine : Initialize()
|
||||
Engine->>Disp : Register dispatcher
|
||||
Engine->>Inv : Register UI invoker
|
||||
Engine->>NetIn : Subscribe to incoming packets
|
||||
Engine->>NetOut : Subscribe to outgoing packets
|
||||
Engine->>VM : Create main window view model
|
||||
Note over Engine,VM : Bind View to ViewModel
|
||||
NetIn->>Pkt : Handle incoming packet
|
||||
Pkt->>Ent : Update entity state
|
||||
Pkt-->>VM : Notify changes (events or properties)
|
||||
VM->>Disp : Invoke UI update on UI thread
|
||||
VM->>Inv : Show dialog / message if needed
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### MVVM Implementation Across Avalonia UI
|
||||
- Views: Avalonia XAML files define layout and bindings; code-behind handles minimal UI logic.
|
||||
- ViewModels: Expose observable properties and commands; inherit from BaseViewModel to support change notification.
|
||||
- Data Binding: Views bind to ViewModels; updates propagate through property change notifications.
|
||||
- Commands: User actions are bound to commands in ViewModels, keeping UI logic testable and decoupled.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseViewModel {
|
||||
+NotifyPropertyChanged(propertyName)
|
||||
+RaiseEvent(eventArgs)
|
||||
}
|
||||
class MainWindowViewModel {
|
||||
+Title
|
||||
+OpenOptions()
|
||||
+CloseWindow()
|
||||
}
|
||||
class MainWindowView {
|
||||
+InitializeComponent()
|
||||
+DataContext
|
||||
}
|
||||
MainWindowViewModel --|> BaseViewModel : "inherits"
|
||||
MainWindowView --> MainWindowViewModel : "binds to"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
|
||||
**Section sources**
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
|
||||
### Engine Class: Central Orchestrator
|
||||
Responsibilities:
|
||||
- Initializes subsystems (network, macros, hotkeys, chat).
|
||||
- Registers event handlers for incoming/outgoing packets.
|
||||
- Creates and configures ViewModels and Views.
|
||||
- Manages application lifecycle and resource cleanup.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([Application Startup]) --> InitEngine["Engine.Initialize()"]
|
||||
InitEngine --> LoadConfig["Load Configuration"]
|
||||
LoadConfig --> RegisterDispatchers["Register Dispatcher & UI Invoker"]
|
||||
RegisterDispatchers --> SubscribeNetwork["Subscribe Packet Handlers"]
|
||||
SubscribeNetwork --> CreateUI["Create Main Window & ViewModel"]
|
||||
CreateUI --> RunLoop["Run Message Loop"]
|
||||
RunLoop --> Shutdown["Graceful Shutdown"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
|
||||
### Dispatcher Pattern and UI Invoker Abstraction
|
||||
- IDispatcher: Interface defining cross-thread execution primitives.
|
||||
- AvaloniaDispatcher: Concrete implementation for Avalonia, ensuring UI updates run on the UI thread.
|
||||
- IUIInvoker: Interface abstracting UI operations like showing dialogs.
|
||||
- AvaloniaUIInvoker: Concrete implementation providing Avalonia-specific UI calls.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class IDispatcher {
|
||||
+Invoke(action)
|
||||
+BeginInvoke(action)
|
||||
}
|
||||
class AvaloniaDispatcher {
|
||||
+Invoke(action)
|
||||
+BeginInvoke(action)
|
||||
}
|
||||
class IUIInvoker {
|
||||
+ShowMessage(title, message)
|
||||
+ShowDialog(window)
|
||||
}
|
||||
class AvaloniaUIInvoker {
|
||||
+ShowMessage(title, message)
|
||||
+ShowDialog(window)
|
||||
}
|
||||
AvaloniaDispatcher ..|> IDispatcher : "implements"
|
||||
AvaloniaUIInvoker ..|> IUIInvoker : "implements"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
### Plugin Architecture: Macros, Agents, and Filters
|
||||
- MacroManager: Loads and executes macro commands; supports dynamic command registration.
|
||||
- HotkeyManager: Binds keyboard shortcuts to commands; integrates with UI and background threads.
|
||||
- ChatManager: Provides chat integration and message routing.
|
||||
- Extensibility: New macro commands, agent behaviors, and filters can be added without modifying core logic.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as "User"
|
||||
participant HM as "HotkeyManager.cs"
|
||||
participant MM as "MacroManager.cs"
|
||||
participant CM as "ChatManager.cs"
|
||||
participant VM as "MainWindowViewModel.cs"
|
||||
User->>HM : Press hotkey
|
||||
HM->>MM : Execute macro command
|
||||
MM->>CM : Route chat action if needed
|
||||
MM-->>VM : Update UI state via events
|
||||
VM->>VM : Refresh bindings
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs)
|
||||
- [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
- [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
|
||||
**Section sources**
|
||||
- [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
- [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs)
|
||||
- [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs)
|
||||
|
||||
### Network Layer: Packet Handling and Game Entities
|
||||
- IncomingPacketHandlers: Processes server-to-client packets and updates entity collections.
|
||||
- OutgoingPacketHandlers: Handles client-to-server packets and triggers actions.
|
||||
- PacketHandler: Central registry and dispatch for packet types.
|
||||
- Entity Model: Base class for game objects; Mobile and Item extend it to represent characters and items.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
InPkt["IncomingPacketHandlers.cs"] --> PktHdlr["PacketHandler.cs"]
|
||||
PktHdlr --> UpdateEntities["Update Entity State"]
|
||||
UpdateEntities --> EntityModel["Entity.cs / Mobile.cs / Item.cs"]
|
||||
OutPkt["OutgoingPacketHandlers.cs"] --> PktHdlr
|
||||
PktHdlr --> SendAction["Send Action to Server"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
High-level dependencies:
|
||||
- Avalonia UI depends on Shared ViewModels and Managers.
|
||||
- Engine depends on Dispatcher and UI Invoker abstractions.
|
||||
- Network layer depends on Entity models and provides events to ViewModels.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A_Engine["Engine.cs"] --> A_Dispatcher["AvaloniaDispatcher.cs"]
|
||||
A_Engine --> A_Invoker["AvaloniaUIInvoker.cs"]
|
||||
A_Engine --> S_MacroMgr["MacroManager.cs"]
|
||||
A_Engine --> S_HotkeyMgr["HotkeyManager.cs"]
|
||||
A_Engine --> S_ChatMgr["ChatManager.cs"]
|
||||
A_Engine --> S_InPkt["IncomingPacketHandlers.cs"]
|
||||
A_Engine --> S_OutPkt["OutgoingPacketHandlers.cs"]
|
||||
S_InPkt --> S_Entity["Entity.cs"]
|
||||
S_OutPkt --> S_Entity
|
||||
A_MainView["MainWindow.axaml.cs"] --> S_VM_Main["MainWindowViewModel.cs"]
|
||||
S_VM_Main --> S_VM_Base["BaseViewModel.cs"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
- [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs)
|
||||
- [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [MacroManager.cs](file://ClassicAssist.Shared/Data/Macros/MacroManager.cs)
|
||||
- [HotkeyManager.cs](file://ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs)
|
||||
- [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
|
||||
## Performance Considerations
|
||||
- Minimize UI thread work: Use dispatcher to batch updates and avoid frequent property changes.
|
||||
- Efficient packet processing: Keep handlers lightweight; defer heavy computations to background tasks.
|
||||
- Entity collection updates: Coalesce updates to reduce binding churn.
|
||||
- Resource management: Dispose resources during shutdown; avoid memory leaks in event subscriptions.
|
||||
- Cross-platform rendering: Prefer vector graphics and reusable styles to improve performance on different platforms.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
Common issues and resolutions:
|
||||
- UI not updating: Ensure UI updates are dispatched on the UI thread using the dispatcher.
|
||||
- Cross-thread exceptions: Verify all UI invocations go through the UI invoker abstraction.
|
||||
- Packet handling delays: Check for blocking operations in packet handlers; offload to background threads where appropriate.
|
||||
- Macro command failures: Validate command registration and parameter parsing; inspect logs for errors.
|
||||
|
||||
**Section sources**
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
|
||||
## Conclusion
|
||||
ClassicAssist.Avalonia implements a robust MVVM architecture with clear separation between UI and core logic. The Engine orchestrates lifecycle and subsystems, while the dispatcher and UI invoker ensure cross-thread safety and platform independence. The network layer processes packets efficiently and updates shared entities, which are consumed by ViewModels for reactive UI updates. Extensibility is achieved through macro commands, hotkeys, and chat integrations. Following the outlined performance and troubleshooting practices will help maintain a responsive and reliable application across platforms.
|
||||
@@ -0,0 +1,339 @@
|
||||
# Engine Lifecycle Management
|
||||
|
||||
<cite>
|
||||
**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)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
</cite>
|
||||
|
||||
## 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)
|
||||
|
||||
## Introduction
|
||||
This document explains the lifecycle management of the Engine class in ClassicAssist.Avalonia, focusing on its role as the central orchestrator for application startup, subsystem initialization, and graceful shutdown. It also documents the event-driven communication model that coordinates network handlers, macro system, agents, and UI updates; the plugin loading mechanism used to discover and initialize extensions; and the thread management strategy for asynchronous operations and cross-thread UI updates. Finally, it provides examples of custom engine extension points and demonstrates how to integrate new features into the engine lifecycle.
|
||||
|
||||
## Project Structure
|
||||
The Avalonia-based entry point initializes the UI framework and then starts the Engine, which is responsible for bootstrapping core services, loading plugins, and managing runtime events. The shared Engine implementation defines the common lifecycle hooks and abstractions for dispatching work across threads and UI invocations.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Program["Program.cs<br/>Entry point"] --> App["App.axaml.cs<br/>Application bootstrap"]
|
||||
App --> EngineAvalonia["Engine.cs (Avalonia)<br/>Orchestrates startup/shutdown"]
|
||||
EngineAvalonia --> EngineShared["Engine.cs (Shared)<br/>Lifecycle hooks & contracts"]
|
||||
EngineAvalonia --> Dispatcher["AvaloniaDispatcher.cs<br/>IDispatcher impl"]
|
||||
EngineAvalonia --> UIInvoker["AvaloniaUIInvoker.cs<br/>IUIInvoker impl"]
|
||||
```
|
||||
|
||||
**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)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.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)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
## Core Components
|
||||
- Engine (Avalonia): Orchestrates application startup, initializes subsystems, loads plugins, and manages shutdown.
|
||||
- Engine (Shared): Defines lifecycle hooks and contracts for platform-specific implementations.
|
||||
- IDispatcher: Abstraction for scheduling work on a specific thread or context.
|
||||
- IUIInvoker: Abstraction for invoking UI updates safely from background threads.
|
||||
- AvaloniaDispatcher: Concrete dispatcher backed by Avalonia’s threading model.
|
||||
- AvaloniaUIInvoker: Concrete UI invoker that marshals calls onto the UI thread.
|
||||
|
||||
Key responsibilities:
|
||||
- Startup sequence: create services, configure logging, load configuration, initialize networking, macros, agents, and UI components.
|
||||
- Event-driven coordination: publish and subscribe to engine events to decouple subsystems.
|
||||
- Plugin discovery and initialization: scan assemblies, instantiate extensions, and register them with the engine.
|
||||
- Thread management: ensure background tasks run off the UI thread and UI updates are dispatched back to the UI thread.
|
||||
- Graceful shutdown: stop network loops, cancel timers, flush logs, release resources, and close UI windows.
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Architecture Overview
|
||||
The Engine acts as the central coordinator. It uses an event bus to communicate between components such as network handlers, macro system, agents, and UI. Subsystems register event handlers during initialization and react to engine events like Start, Stop, Connected, Disconnected, MacroExecuted, AgentStateChanged, etc.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Main as "Program"
|
||||
participant App as "App"
|
||||
participant Eng as "Engine (Avalonia)"
|
||||
participant Net as "Network Handler"
|
||||
participant Mac as "Macro System"
|
||||
participant Ag as "Agents"
|
||||
participant UI as "UI Layer"
|
||||
Main->>App : Initialize UI framework
|
||||
App->>Eng : Create and start Engine
|
||||
Eng->>Eng : Load configuration
|
||||
Eng->>Eng : Discover and load plugins
|
||||
Eng->>Net : Initialize network stack
|
||||
Eng->>Mac : Initialize macro engine
|
||||
Eng->>Ag : Initialize agent managers
|
||||
Eng->>UI : Initialize UI components
|
||||
Eng-->>Net : Event : Started
|
||||
Eng-->>Mac : Event : Started
|
||||
Eng-->>Ag : Event : Started
|
||||
Eng-->>UI : Event : Started
|
||||
Net-->>Eng : Event : Connected
|
||||
Eng-->>UI : Dispatch UI update via IUIInvoker
|
||||
Mac-->>Eng : Event : MacroExecuted
|
||||
Eng-->>Ag : Publish event to agents
|
||||
Ag-->>Eng : Event : StateChanged
|
||||
Eng-->>UI : Update UI state
|
||||
Eng->>Eng : Shutdown requested
|
||||
Eng-->>Net : Event : Stopping
|
||||
Eng-->>Mac : Event : Stopping
|
||||
Eng-->>Ag : Event : Stopping
|
||||
Eng-->>UI : Close windows and release resources
|
||||
Eng-->>Main : Exit
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### Engine Lifecycle Orchestration
|
||||
The Engine lifecycle follows a clear sequence:
|
||||
- Pre-start: parse arguments, set up logging, load profiles/configuration.
|
||||
- Start: initialize subsystems (network, macros, agents), load plugins, start event loop.
|
||||
- Running: handle events from subsystems, coordinate cross-thread UI updates, manage timers and queues.
|
||||
- Shutdown: signal stopping to all subsystems, wait for completion, release resources, exit cleanly.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["Engine.Start"]) --> PreStart["Pre-start: config, logging, profile"]
|
||||
PreStart --> InitSubsystems["Initialize subsystems<br/>Network, Macros, Agents"]
|
||||
InitSubsystems --> LoadPlugins["Discover and load plugins"]
|
||||
LoadPlugins --> StartEvents["Start event loop and listeners"]
|
||||
StartEvents --> Running["Running: process events"]
|
||||
Running --> ShutdownRequested{"Shutdown requested?"}
|
||||
ShutdownRequested --> |No| Running
|
||||
ShutdownRequested --> |Yes| SignalStop["Signal Stop to subsystems"]
|
||||
SignalStop --> Cleanup["Cleanup resources and UI"]
|
||||
Cleanup --> End(["Engine.Stop complete"])
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
### Event-Driven Communication Model
|
||||
The Engine exposes events that subsystems can subscribe to. Typical events include:
|
||||
- Application-level: Started, Stopping, Stopped
|
||||
- Network-level: Connected, Disconnected, PacketReceived
|
||||
- Macro-level: MacroExecuted, MacroError
|
||||
- Agent-level: AgentStateChanged, AgentActionPerformed
|
||||
- UI-level: UpdateRequested, WindowClosed
|
||||
|
||||
Subsystems publish events through the Engine’s event bus. For example, when the network handler connects, it raises a Connected event; the Engine publishes this to subscribers, including UI components that update status indicators.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Engine {
|
||||
+Event Started
|
||||
+Event Stopping
|
||||
+Event Stopped
|
||||
+Event Connected
|
||||
+Event Disconnected
|
||||
+Event MacroExecuted
|
||||
+Event AgentStateChanged
|
||||
+Publish(event, data)
|
||||
+Subscribe(event, handler)
|
||||
}
|
||||
class NetworkHandler {
|
||||
+Connect()
|
||||
+Disconnect()
|
||||
+OnConnected()
|
||||
+OnDisconnected()
|
||||
}
|
||||
class MacroSystem {
|
||||
+Execute(macro)
|
||||
+OnError(error)
|
||||
}
|
||||
class Agents {
|
||||
+HandleStateChange(state)
|
||||
}
|
||||
class UIUpdater {
|
||||
+UpdateStatus(status)
|
||||
+RefreshViews()
|
||||
}
|
||||
Engine <.. NetworkHandler : "publishes Connected/Disconnected"
|
||||
Engine <.. MacroSystem : "publishes MacroExecuted/MacroError"
|
||||
Engine <.. Agents : "publishes AgentStateChanged"
|
||||
Engine <.. UIUpdater : "publishes UI updates"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
### Plugin Loading Mechanism
|
||||
Plugin discovery and initialization follow these steps:
|
||||
- Scan configured directories for assemblies.
|
||||
- Identify types implementing known extension interfaces or attributes.
|
||||
- Instantiate plugin instances and call their Initialize method.
|
||||
- Register plugin-provided commands, filters, agents, or UI tabs with the Engine.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Scan["Scan assembly directories"] --> Identify["Identify plugin types"]
|
||||
Identify --> Instantiate["Instantiate plugin instances"]
|
||||
Instantiate --> Initialize["Call Initialize(pluginContext)"]
|
||||
Initialize --> Register["Register commands/filters/agents/UI"]
|
||||
Register --> Ready["Plugins ready"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
|
||||
### Thread Management Strategy
|
||||
Cross-thread concerns are handled via:
|
||||
- IDispatcher: schedules work on a target thread or context.
|
||||
- IUIInvoker: marshals UI updates onto the UI thread.
|
||||
- Background workers: network processing, macro execution, and agent logic run off the UI thread.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant BG as "Background Task"
|
||||
participant Eng as "Engine"
|
||||
participant Disp as "IDispatcher"
|
||||
participant UIInv as "IUIInvoker"
|
||||
participant UI as "UI Thread"
|
||||
BG->>Eng : Request UI update
|
||||
Eng->>UIInv : Invoke(updateCallback)
|
||||
UIInv->>Disp : ScheduleOnUIThread(callback)
|
||||
Disp-->>UI : Execute callback on UI thread
|
||||
UI-->>UIInv : Completed
|
||||
UIInv-->>Eng : Acknowledge
|
||||
Eng-->>BG : Continue processing
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
### Custom Engine Extension Points
|
||||
To integrate new features into the engine lifecycle:
|
||||
- Implement a plugin type recognized by the loader.
|
||||
- Provide an Initialize(context) method to register your feature.
|
||||
- Subscribe to relevant Engine events to react to lifecycle changes.
|
||||
- Use IUIInvoker for any UI updates from background threads.
|
||||
- Ensure your feature cleans up resources in response to Stopping events.
|
||||
|
||||
Example integration steps:
|
||||
- Add your assembly to the plugin directory.
|
||||
- Define a class implementing the expected interface or attribute.
|
||||
- In Initialize, register commands, filters, agents, or UI tabs.
|
||||
- Subscribe to Engine events for Connected/Disconnected, MacroExecuted, AgentStateChanged.
|
||||
- On Stopping, cancel timers, dispose resources, and detach event handlers.
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
The Engine depends on shared contracts and platform-specific implementations:
|
||||
- Shared contracts: IDispatcher, IUIInvoker, Engine base behavior.
|
||||
- Platform implementations: AvaloniaDispatcher, AvaloniaUIInvoker.
|
||||
- Subsystems: Network, Macros, Agents, UI.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
EngineAvalonia["Engine.cs (Avalonia)"] --> EngineShared["Engine.cs (Shared)"]
|
||||
EngineAvalonia --> IDisp["IDispatcher.cs"]
|
||||
EngineAvalonia --> IUI["IUIInvoker.cs"]
|
||||
EngineAvalonia --> AvalDisp["AvaloniaDispatcher.cs"]
|
||||
EngineAvalonia --> AvalUI["AvaloniaUIInvoker.cs"]
|
||||
EngineAvalonia --> Subsystems["Network, Macros, Agents, UI"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Performance Considerations
|
||||
- Avoid heavy work on the UI thread; always use IUIInvoker for UI updates.
|
||||
- Batch UI updates where possible to reduce redraw overhead.
|
||||
- Use efficient event subscriptions; unsubscribe during shutdown to prevent leaks.
|
||||
- Prefer asynchronous operations for network and file I/O.
|
||||
- Limit plugin scanning frequency; cache discovered plugins when appropriate.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
Common issues and resolutions:
|
||||
- UI not updating: ensure updates are invoked via IUIInvoker and scheduled on the UI thread using IDispatcher.
|
||||
- Plugins not loading: verify plugin assemblies are in the correct directory and implement the expected interfaces/attributes.
|
||||
- Events not firing: confirm subscriptions occur after Engine.Start and unsubscriptions occur before Engine.Stop.
|
||||
- Resource leaks: check that background tasks are cancelled and resources disposed during Stopping.
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
|
||||
## Conclusion
|
||||
The Engine in ClassicAssist.Avalonia serves as the central orchestrator for application lifecycle, coordinating subsystem initialization, event-driven communication, plugin loading, and graceful shutdown. By adhering to the provided extension points and thread management strategies, developers can integrate new features seamlessly while maintaining responsiveness and stability.
|
||||
|
||||
[No sources needed since this section summarizes without analyzing specific files]
|
||||
@@ -0,0 +1,382 @@
|
||||
# MVVM Pattern Implementation
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs)
|
||||
- [GeneralControl.axaml](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml)
|
||||
- [GeneralControl.axaml.cs](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
</cite>
|
||||
|
||||
## 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)
|
||||
|
||||
## Introduction
|
||||
This document explains how the Model-View-ViewModel (MVVM) pattern is implemented in ClassicAssist.Avalonia. It focuses on separation of concerns between Views (AXAML), ViewModels (C# classes), and Models (business logic), with emphasis on:
|
||||
- The BaseViewModel that provides common functionality such as property change notification and command binding
|
||||
- Data binding mechanisms and event handling patterns
|
||||
- Lifecycle management of ViewModels
|
||||
- Concrete examples from MainWindow and other key views
|
||||
- Testing strategies for ViewModels and best practices to maintain clean separation between presentation logic and business rules
|
||||
|
||||
## Project Structure
|
||||
The Avalonia UI layer follows a clear folder structure aligned with MVVM:
|
||||
- Views are defined in AXAML files under ClassicAssist.Avalonia/Views, with code-behind files (.axaml.cs) kept minimal
|
||||
- ViewModels live in ClassicAssist.Shared/UI/ViewModels and implement presentation logic and data binding
|
||||
- Models and shared services reside in ClassicAssist.Shared, including UI infrastructure like dispatchers and UI invokers
|
||||
- App initialization wires up the root ViewModel and ensures UI updates occur on the correct thread
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Avalonia UI Layer"
|
||||
A_MainWindow["MainWindow.axaml<br/>Code-behind: MainWindow.axaml.cs"]
|
||||
A_GeneralControl["GeneralControl.axaml<br/>Code-behind: GeneralControl.axaml.cs"]
|
||||
A_DialogWindow["DialogWindow.axaml<br/>Code-behind: DialogWindow.axaml.cs"]
|
||||
end
|
||||
subgraph "Shared UI Infrastructure"
|
||||
B_BaseViewModel["BaseViewModel.cs"]
|
||||
B_IDispatcher["IDispatcher.cs"]
|
||||
B_IUIInvoker["IUIInvoker.cs"]
|
||||
B_AvaloniaDispatcher["AvaloniaDispatcher.cs"]
|
||||
B_AvaloniaUIInvoker["AvaloniaUIInvoker.cs"]
|
||||
end
|
||||
subgraph "Shared Business Logic"
|
||||
C_Models["Models and Managers<br/>(ClassicAssist.Shared/Data/*)"]
|
||||
end
|
||||
A_MainWindow --> B_BaseViewModel
|
||||
A_GeneralControl --> B_BaseViewModel
|
||||
A_DialogWindow --> B_BaseViewModel
|
||||
B_BaseViewModel --> B_IDispatcher
|
||||
B_BaseViewModel --> B_IUIInvoker
|
||||
B_AvaloniaDispatcher --> B_IDispatcher
|
||||
B_AvaloniaUIInvoker --> B_IUIInvoker
|
||||
A_MainWindow --> C_Models
|
||||
A_GeneralControl --> C_Models
|
||||
A_DialogWindow --> C_Models
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [GeneralControl.axaml](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml)
|
||||
- [GeneralControl.axaml.cs](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml.cs)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
|
||||
## Core Components
|
||||
- BaseViewModel: Provides common MVVM utilities such as property change notification and command support. All ViewModels inherit from this base to ensure consistent behavior and reduce boilerplate.
|
||||
- MainWindowViewModel: Root ViewModel for the main window; orchestrates UI state and delegates business operations to models/managers.
|
||||
- DialogWindowViewModel: Lightweight ViewModel used by modal dialogs to expose properties and commands bound from AXAML.
|
||||
- GeneralControlViewModel: Example of a reusable control ViewModel demonstrating encapsulated presentation logic.
|
||||
- Dispatcher and UI Invoker abstractions: Ensure UI updates occur on the UI thread regardless of background work.
|
||||
|
||||
Key responsibilities:
|
||||
- Views (AXAML): Define layout, styling, and declarative bindings to ViewModel properties and commands. Code-behind should remain thin and avoid business logic.
|
||||
- ViewModels: Expose bindable properties and commands, handle user interactions, coordinate with models, and update UI state safely via dispatcher/invoker.
|
||||
- Models: Contain business rules, data access, and domain logic. They do not depend on UI frameworks.
|
||||
|
||||
**Section sources**
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Architecture Overview
|
||||
At runtime, the application initializes the UI framework, creates the root ViewModel, and sets it as the DataContext for the main view. ViewModels use the dispatcher to marshal UI updates onto the UI thread and invoke UI helpers through the UI invoker abstraction.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as "App.axaml.cs"
|
||||
participant Program as "Program.cs"
|
||||
participant MainWindow as "MainWindow.axaml"
|
||||
participant VM as "MainWindowViewModel"
|
||||
participant Dispatcher as "AvaloniaDispatcher"
|
||||
participant UIInvoker as "AvaloniaUIInvoker"
|
||||
Program->>App : Initialize UI framework
|
||||
App->>MainWindow : Create main window
|
||||
App->>VM : Instantiate root ViewModel
|
||||
App->>MainWindow : Set DataContext = VM
|
||||
MainWindow-->>VM : Bindings resolve properties/commands
|
||||
VM->>Dispatcher : ExecuteOnUIThread(action)
|
||||
Dispatcher-->>MainWindow : Invoke UI update
|
||||
VM->>UIInvoker : Show dialog / message
|
||||
UIInvoker-->>MainWindow : Present UI element
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### BaseViewModel
|
||||
BaseViewModel centralizes MVVM essentials:
|
||||
- Property change notification: Derived classes raise notifications when properties change so bindings update automatically.
|
||||
- Command support: Common command infrastructure enables binding user actions to methods or ICommand implementations.
|
||||
- Thread safety helpers: Integration with dispatcher/invoker to ensure UI updates happen on the UI thread.
|
||||
|
||||
Best practices:
|
||||
- Always raise property change notifications after updating backing fields.
|
||||
- Use commands for all user interactions exposed to AXAML.
|
||||
- Avoid direct UI references; rely on dispatcher and UI invoker abstractions.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseViewModel {
|
||||
+RaisePropertyChanged(propertyName)
|
||||
+ExecuteCommand(command)
|
||||
+EnsureUiThread(action)
|
||||
}
|
||||
class MainWindowViewModel {
|
||||
+Properties...
|
||||
+Commands...
|
||||
+Initialize()
|
||||
}
|
||||
class DialogWindowViewModel {
|
||||
+Title
|
||||
+Message
|
||||
+OkCommand
|
||||
+CancelCommand
|
||||
}
|
||||
class GeneralControlViewModel {
|
||||
+State
|
||||
+RefreshCommand
|
||||
}
|
||||
MainWindowViewModel --|> BaseViewModel
|
||||
DialogWindowViewModel --|> BaseViewModel
|
||||
GeneralControlViewModel --|> BaseViewModel
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs)
|
||||
|
||||
**Section sources**
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
|
||||
### MainWindow and MainWindowViewModel
|
||||
The main window demonstrates typical MVVM usage:
|
||||
- AXAML defines controls and binds their properties and commands to MainWindowViewModel members.
|
||||
- Code-behind remains minimal, typically only wiring up initial DataContext if needed.
|
||||
- MainWindowViewModel exposes properties for UI state and commands for user actions, coordinating with models/managers.
|
||||
|
||||
Data binding flow:
|
||||
- User interaction triggers a command in MainWindowViewModel.
|
||||
- ViewModel updates properties and raises notifications.
|
||||
- AXAML bindings reflect changes automatically.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant View as "MainWindow.axaml"
|
||||
participant VM as "MainWindowViewModel"
|
||||
participant Dispatcher as "AvaloniaDispatcher"
|
||||
View->>VM : Command invoked (e.g., "OpenSettings")
|
||||
VM->>VM : Perform action / update state
|
||||
VM->>Dispatcher : EnsureUiThread(updateUI)
|
||||
Dispatcher-->>View : Update bound properties
|
||||
View-->>User : Reflect new state
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
|
||||
**Section sources**
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
|
||||
### DialogWindow and DialogWindowViewModel
|
||||
Modal dialogs follow the same MVVM principles:
|
||||
- DialogWindow.axaml binds to DialogWindowViewModel properties (title, message) and commands (OK, Cancel).
|
||||
- DialogWindowViewModel encapsulates dialog-specific state and actions without referencing UI elements directly.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["Show Dialog"]) --> Bind["Bind Title/Message/Commands"]
|
||||
Bind --> UserAction{"User clicks OK or Cancel?"}
|
||||
UserAction --> |OK| Confirm["Confirm action in ViewModel"]
|
||||
UserAction --> |Cancel| Close["Close dialog"]
|
||||
Confirm --> UpdateUI["Update UI via dispatcher"]
|
||||
UpdateUI --> End(["Dialog Closed"])
|
||||
Close --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
|
||||
**Section sources**
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
|
||||
### GeneralControl and GeneralControlViewModel
|
||||
Reusable controls demonstrate encapsulation:
|
||||
- GeneralControl.axaml binds to GeneralControlViewModel properties and commands.
|
||||
- GeneralControlViewModel manages its own state and exposes a refresh command, keeping the control self-contained and testable.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class GeneralControl {
|
||||
<<AXAML>>
|
||||
+Bindings to ViewModel
|
||||
}
|
||||
class GeneralControlViewModel {
|
||||
+State
|
||||
+RefreshCommand
|
||||
+Initialize()
|
||||
}
|
||||
GeneralControl --> GeneralControlViewModel : "DataContext"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [GeneralControl.axaml](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml)
|
||||
- [GeneralControl.axaml.cs](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml.cs)
|
||||
- [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs)
|
||||
|
||||
**Section sources**
|
||||
- [GeneralControl.axaml](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml)
|
||||
- [GeneralControl.axaml.cs](file://ClassicAssist.Avalonia/Views/GeneralControl.axaml.cs)
|
||||
- [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs)
|
||||
|
||||
### Dispatcher and UI Invoker Abstractions
|
||||
To keep ViewModels UI-framework agnostic:
|
||||
- IDispatcher and IUIInvoker define contracts for executing code on the UI thread and invoking UI operations.
|
||||
- AvaloniaDispatcher and AvaloniaUIInvoker provide concrete implementations for Avalonia.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class IDispatcher {
|
||||
+ExecuteOnUIThread(action)
|
||||
}
|
||||
class IUIInvoker {
|
||||
+ShowDialog(dialogViewModel)
|
||||
+ShowMessage(message)
|
||||
}
|
||||
class AvaloniaDispatcher {
|
||||
+ExecuteOnUIThread(action)
|
||||
}
|
||||
class AvaloniaUIInvoker {
|
||||
+ShowDialog(dialogViewModel)
|
||||
+ShowMessage(message)
|
||||
}
|
||||
IDispatcher <|.. AvaloniaDispatcher
|
||||
IUIInvoker <|.. AvaloniaUIInvoker
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
ViewModels depend on:
|
||||
- BaseViewModel for common functionality
|
||||
- Dispatcher and UI invoker abstractions for cross-thread UI updates
|
||||
- Models/managers for business logic (not shown here but present in Shared/Data)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
VM_Main["MainWindowViewModel"] --> Base["BaseViewModel"]
|
||||
VM_Dialog["DialogWindowViewModel"] --> Base
|
||||
VM_General["GeneralControlViewModel"] --> Base
|
||||
Base --> Disp["IDispatcher"]
|
||||
Base --> Inv["IUIInvoker"]
|
||||
Disp --> DispImpl["AvaloniaDispatcher"]
|
||||
Inv --> InvImpl["AvaloniaUIInvoker"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Performance Considerations
|
||||
- Minimize heavy work on the UI thread; offload to background tasks and marshal results back using the dispatcher.
|
||||
- Use efficient collections and avoid unnecessary property updates; batch updates where possible.
|
||||
- Keep AXAML bindings simple and avoid complex converters in hot paths.
|
||||
- Reuse ViewModels and controls when appropriate to reduce allocation overhead.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
Common issues and resolutions:
|
||||
- UI not updating after property changes: Ensure property change notifications are raised correctly in BaseViewModel-derived classes.
|
||||
- Cross-thread exceptions: Always use the dispatcher to execute UI updates from background threads.
|
||||
- Commands not firing: Verify command bindings in AXAML and that commands are properly exposed and enabled.
|
||||
- Dialogs not showing: Use the UI invoker abstraction to present dialogs instead of direct UI calls.
|
||||
|
||||
**Section sources**
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Conclusion
|
||||
ClassicAssist.Avalonia implements MVVM cleanly by separating Views (AXAML), ViewModels (C#), and Models (business logic). BaseViewModel standardizes property change notification and command support, while dispatcher and UI invoker abstractions ensure thread-safe UI updates. MainWindow, DialogWindow, and GeneralControl illustrate proper MVVM usage, enabling testable and maintainable presentation logic. Following these patterns helps keep the UI responsive and the codebase organized.
|
||||
|
||||
[No sources needed since this section summarizes without analyzing specific files]
|
||||
@@ -0,0 +1,530 @@
|
||||
# Networking Fundamentals
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
- [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs)
|
||||
- [ActionPacketQueueItem.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueueItem.cs)
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
- [BasePacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/BasePacket.cs)
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
- [Packet.cs](file://ClassicAssist.Shared/UO/Objects/Packet.cs)
|
||||
</cite>
|
||||
|
||||
## 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 networking fundamentals of ClassicAssist.Avalonia with a focus on the Ultima Online packet protocol implementation. It covers how packets are parsed, serialized, and deserialized; how incoming and outgoing packets are routed through a handler system; how game entities (objects, mobiles, items) are created, updated, and destroyed based on network events; compression algorithms used for efficiency; error handling strategies; examples of custom packet handlers; extension points for new protocol features; performance considerations for high-frequency processing; and memory management strategies.
|
||||
|
||||
## Project Structure
|
||||
The networking layer is primarily implemented under ClassicAssist.Shared/UO/Network and ClassicAssist.Shared/UO/Data, with supporting object models under ClassicAssist.Shared/UO/Objects. Key areas include:
|
||||
- Packet I/O primitives: PacketReader and PacketWriter
|
||||
- Packet base types and concrete packet definitions
|
||||
- Incoming and outgoing packet routing via handler registries
|
||||
- Filtering and queuing mechanisms
|
||||
- Compression utilities
|
||||
- Entity lifecycle models and collections
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Networking Layer"
|
||||
PR["PacketReader"]
|
||||
PW["PacketWriter"]
|
||||
PH["PacketHandler"]
|
||||
IPH["IncomingPacketHandlers"]
|
||||
OPH["OutgoingPacketHandlers"]
|
||||
IF["IncomingPacketFilters"]
|
||||
OF["OutgoingPacketFilters"]
|
||||
APQ["ActionPacketQueue"]
|
||||
COMP["Compression"]
|
||||
end
|
||||
subgraph "Packets"
|
||||
BP["BasePacket"]
|
||||
MI["MobileIncoming"]
|
||||
MU["MobileUpdate"]
|
||||
RO["RemoveObject"]
|
||||
end
|
||||
subgraph "Objects"
|
||||
ENT["Entity"]
|
||||
ECOL["EntityCollection"]
|
||||
MOB["Mobile"]
|
||||
PMOB["PlayerMobile"]
|
||||
ITM["Item"]
|
||||
PKT["Packet"]
|
||||
end
|
||||
PR --> PH
|
||||
PH --> IPH
|
||||
PH --> IF
|
||||
PH --> APQ
|
||||
PW --> OPH
|
||||
PW --> OF
|
||||
PH --> COMP
|
||||
BP --> MI
|
||||
BP --> MU
|
||||
BP --> RO
|
||||
ENT --> ECOL
|
||||
MOB --> ENT
|
||||
PMOB --> MOB
|
||||
ITM --> ENT
|
||||
PKT --> BP
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
- [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs)
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [BasePacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/BasePacket.cs)
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
- [Packet.cs](file://ClassicAssist.Shared/UO/Objects/Packet.cs)
|
||||
|
||||
**Section sources**
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
- [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs)
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [BasePacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/BasePacket.cs)
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
- [Packet.cs](file://ClassicAssist.Shared/UO/Objects/Packet.cs)
|
||||
|
||||
## Core Components
|
||||
- Packet I/O primitives:
|
||||
- PacketReader provides methods to read UO-specific data types from streams or buffers.
|
||||
- PacketWriter provides methods to serialize UO-specific data types into streams or buffers.
|
||||
- Packet base model:
|
||||
- BasePacket defines common fields and serialization/deserialization hooks used by all concrete packets.
|
||||
- Concrete packets:
|
||||
- Examples include MobileIncoming, MobileUpdate, RemoveObject, which encapsulate specific UO message formats.
|
||||
- Handler registries:
|
||||
- IncomingPacketHandlers and OutgoingPacketHandlers maintain mappings between packet IDs and handler methods.
|
||||
- Packet routing:
|
||||
- PacketHandler orchestrates reading/writing, dispatching to filters and handlers, and managing queues.
|
||||
- Filters:
|
||||
- IncomingPacketFilters and OutgoingPacketFilters allow selective inspection, modification, or blocking of packets.
|
||||
- Queues:
|
||||
- ActionPacketQueue batches and orders actions derived from packets to ensure deterministic updates.
|
||||
- Compression:
|
||||
- Compression utilities implement UO’s packet compression scheme to reduce bandwidth usage.
|
||||
- Entities:
|
||||
- Entity, Mobile, PlayerMobile, Item, and their collections represent game objects whose lifecycle is driven by network events.
|
||||
|
||||
**Section sources**
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
- [BasePacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/BasePacket.cs)
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
- [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs)
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
|
||||
## Architecture Overview
|
||||
The networking architecture follows a pipeline pattern:
|
||||
- Incoming path:
|
||||
- Raw bytes are decompressed if needed, then read using PacketReader.
|
||||
- PacketHandler identifies the packet type and routes it through IncomingPacketFilters.
|
||||
- The appropriate IncomingPacketHandlers method is invoked to update state or enqueue actions.
|
||||
- Outgoing path:
|
||||
- Application code constructs packets using PacketWriter.
|
||||
- OutgoingPacketFilters inspect or modify packets before sending.
|
||||
- PacketHandler serializes and transmits packets, optionally compressing them.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Net as "Network Stream"
|
||||
participant Comp as "Compression"
|
||||
participant Reader as "PacketReader"
|
||||
participant PH as "PacketHandler"
|
||||
participant IF as "IncomingPacketFilters"
|
||||
participant IH as "IncomingPacketHandlers"
|
||||
participant Queue as "ActionPacketQueue"
|
||||
participant Coll as "EntityCollection"
|
||||
Net->>Comp : "Receive compressed bytes"
|
||||
Comp-->>Net : "Decompressed payload"
|
||||
Net->>Reader : "Read packet header"
|
||||
Reader->>PH : "Dispatch by packet ID"
|
||||
PH->>IF : "Apply incoming filters"
|
||||
IF-->>PH : "Filtered packet"
|
||||
PH->>IH : "Invoke registered handler"
|
||||
IH->>Coll : "Create/Update/Delete entities"
|
||||
IH->>Queue : "Enqueue follow-up actions"
|
||||
Queue-->>Coll : "Process queued actions"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### Packet Parsing, Serialization, and Deserialization
|
||||
- PacketReader and PacketWriter provide typed accessors for UO data formats (e.g., integers, strings, coordinates). They abstract low-level byte operations and ensure consistent parsing across the application.
|
||||
- BasePacket defines common fields and virtual methods for serialization/deserialization. Concrete packets override these methods to encode/decode their specific payloads.
|
||||
- Example flows:
|
||||
- Incoming: Decompress -> Read header -> Instantiate concrete packet -> Deserialize fields -> Route to handler.
|
||||
- Outgoing: Construct concrete packet -> Serialize fields -> Apply outgoing filters -> Compress -> Send.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["Start"]) --> ReadHeader["Read packet header"]
|
||||
ReadHeader --> Identify{"Identify packet type"}
|
||||
Identify --> |Known| CreatePacket["Instantiate concrete packet"]
|
||||
Identify --> |Unknown| LogWarn["Log warning and skip"]
|
||||
CreatePacket --> Deserialize["Deserialize fields"]
|
||||
Deserialize --> Route["Route to handler registry"]
|
||||
Route --> End(["End"])
|
||||
LogWarn --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [BasePacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/BasePacket.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
|
||||
**Section sources**
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
- [BasePacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/BasePacket.cs)
|
||||
|
||||
### Packet Handler System
|
||||
- IncomingPacketHandlers and OutgoingPacketHandlers maintain registration maps that associate packet IDs with handler methods.
|
||||
- PacketHandler centralizes dispatch logic, invoking filters and handlers in sequence.
|
||||
- Registration typically occurs during initialization, allowing modular addition of new packet support.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PacketHandler {
|
||||
+registerIncoming(id, handler)
|
||||
+registerOutgoing(id, handler)
|
||||
+dispatchIncoming(packet)
|
||||
+dispatchOutgoing(packet)
|
||||
}
|
||||
class IncomingPacketHandlers {
|
||||
+handlers : Map<int, Func>
|
||||
+invoke(id, packet)
|
||||
}
|
||||
class OutgoingPacketHandlers {
|
||||
+handlers : Map<int, Func>
|
||||
+invoke(id, packet)
|
||||
}
|
||||
class IncomingPacketFilters {
|
||||
+apply(packet) bool
|
||||
}
|
||||
class OutgoingPacketFilters {
|
||||
+apply(packet) bool
|
||||
}
|
||||
PacketHandler --> IncomingPacketHandlers : "uses"
|
||||
PacketHandler --> OutgoingPacketHandlers : "uses"
|
||||
PacketHandler --> IncomingPacketFilters : "applies"
|
||||
PacketHandler --> OutgoingPacketFilters : "applies"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
|
||||
**Section sources**
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
|
||||
### Entity Lifecycle Management
|
||||
- Entities are represented by Entity and its subclasses (Mobile, PlayerMobile, Item), managed by EntityCollection.
|
||||
- Network events drive lifecycle transitions:
|
||||
- Creation: Packets like MobileIncoming add new entities to collections.
|
||||
- Update: Packets like MobileUpdate modify entity properties (position, appearance, status).
|
||||
- Destruction: Packets like RemoveObject remove entities from collections and release resources.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant PH as "PacketHandler"
|
||||
participant IH as "IncomingPacketHandlers"
|
||||
participant Coll as "EntityCollection"
|
||||
participant Ent as "Entity"
|
||||
PH->>IH : "Handle MobileIncoming"
|
||||
IH->>Coll : "Add new Mobile/Item"
|
||||
Coll->>Ent : "Initialize entity state"
|
||||
PH->>IH : "Handle MobileUpdate"
|
||||
IH->>Ent : "Update properties"
|
||||
PH->>IH : "Handle RemoveObject"
|
||||
IH->>Coll : "Remove entity"
|
||||
Coll->>Ent : "Dispose resources"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
|
||||
**Section sources**
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
|
||||
### Compression Algorithms
|
||||
- Compression utilities implement UO’s packet compression scheme to minimize bandwidth usage.
|
||||
- Incoming packets may be decompressed before parsing; outgoing packets may be compressed before transmission.
|
||||
- Proper handling ensures compatibility with server expectations and avoids corruption.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
InStart(["Incoming Bytes"]) --> CheckFlag{"Compressed?"}
|
||||
CheckFlag --> |Yes| Decompress["Decompress payload"]
|
||||
CheckFlag --> |No| Parse["Parse directly"]
|
||||
Decompress --> Parse
|
||||
Parse --> End(["Parsed Packet"])
|
||||
OutStart(["Serialized Packet"]) --> Compress["Compress payload"]
|
||||
Compress --> OutEnd(["Send Compressed Bytes"])
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
|
||||
### Error Handling Strategies
|
||||
- Unknown packet IDs should be logged and skipped to prevent crashes.
|
||||
- Malformed packets should trigger validation failures and safe recovery paths.
|
||||
- Connection issues should be handled by retry logic, reconnection attempts, and graceful degradation.
|
||||
- Filters can block suspicious or malformed packets early in the pipeline.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
### Custom Packet Handlers and Extensibility
|
||||
- To add support for a new packet:
|
||||
- Define a concrete packet class extending BasePacket with serialization/deserialization logic.
|
||||
- Register an incoming handler in IncomingPacketHandlers mapping the packet ID to your method.
|
||||
- Optionally register an outgoing handler in OutgoingPacketHandlers for client-initiated messages.
|
||||
- Use filters in IncomingPacketFilters/OutgoingPacketFilters for inspection or modification.
|
||||
- Update entity collections or enqueue actions via ActionPacketQueue as needed.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Dev as "Developer Code"
|
||||
participant Reg as "IncomingPacketHandlers"
|
||||
participant PH as "PacketHandler"
|
||||
participant Coll as "EntityCollection"
|
||||
Dev->>Reg : "Register handler for new packet ID"
|
||||
PH->>Reg : "Lookup handler by ID"
|
||||
Reg-->>PH : "Handler method"
|
||||
PH->>Dev : "Invoke handler"
|
||||
Dev->>Coll : "Update/create entities"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
|
||||
### Performance Considerations and Memory Management
|
||||
- High-frequency packet processing:
|
||||
- Minimize allocations in hot paths; reuse buffers where possible.
|
||||
- Keep handler logic lightweight; defer heavy work to ActionPacketQueue.
|
||||
- Avoid excessive logging in tight loops; use sampling or thresholds.
|
||||
- Memory management:
|
||||
- Ensure entities dispose resources when removed to prevent leaks.
|
||||
- Use efficient collections in EntityCollection for fast lookups and removals.
|
||||
- Be mindful of string interning and large payload sizes.
|
||||
- Threading:
|
||||
- Separate network I/O threads from UI/update threads; marshal updates safely.
|
||||
- Use thread-safe queues and synchronization primitives.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Dependency Analysis
|
||||
The following diagram highlights key dependencies among networking components and object models.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
PH["PacketHandler"] --> IPH["IncomingPacketHandlers"]
|
||||
PH --> OPH["OutgoingPacketHandlers"]
|
||||
PH --> IF["IncomingPacketFilters"]
|
||||
PH --> OF["OutgoingPacketFilters"]
|
||||
PH --> APQ["ActionPacketQueue"]
|
||||
PH --> PR["PacketReader"]
|
||||
PH --> PW["PacketWriter"]
|
||||
PH --> COMP["Compression"]
|
||||
IPH --> MI["MobileIncoming"]
|
||||
IPH --> MU["MobileUpdate"]
|
||||
IPH --> RO["RemoveObject"]
|
||||
MI --> ENT["Entity"]
|
||||
MU --> ENT
|
||||
RO --> ENT
|
||||
ENT --> ECOL["EntityCollection"]
|
||||
ENT --> MOB["Mobile"]
|
||||
MOB --> PMOB["PlayerMobile"]
|
||||
ENT --> ITM["Item"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
- [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs)
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
|
||||
**Section sources**
|
||||
- [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs)
|
||||
- [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs)
|
||||
- [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs)
|
||||
- [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs)
|
||||
- [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs)
|
||||
- [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs)
|
||||
- [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs)
|
||||
- [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs)
|
||||
- [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs)
|
||||
- [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs)
|
||||
- [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs)
|
||||
- [RemoveObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/RemoveObject.cs)
|
||||
- [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs)
|
||||
- [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)
|
||||
- [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs)
|
||||
- [PlayerMobile.cs](file://ClassicAssist.Shared/UO/Objects/PlayerMobile.cs)
|
||||
- [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs)
|
||||
|
||||
## Performance Considerations
|
||||
- Batch updates:
|
||||
- Use ActionPacketQueue to coalesce multiple entity updates into fewer UI refreshes.
|
||||
- Efficient parsing:
|
||||
- Prefer direct reads over reflection-based deserialization.
|
||||
- Reduce GC pressure:
|
||||
- Reuse temporary buffers and avoid unnecessary string allocations.
|
||||
- Selective filtering:
|
||||
- Drop irrelevant packets early to save CPU cycles.
|
||||
- Profiling:
|
||||
- Monitor hot paths and identify bottlenecks in packet handlers.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
- Symptoms:
|
||||
- Missing entities or stale state: Verify incoming handlers for creation/update packets.
|
||||
- Crashes on unknown packets: Ensure unknown IDs are logged and skipped gracefully.
|
||||
- High CPU usage: Check for heavy operations in handlers; move to queue or background tasks.
|
||||
- Memory growth: Inspect entity disposal and collection cleanup.
|
||||
- Steps:
|
||||
- Enable detailed logging for packet IDs and sizes.
|
||||
- Validate packet lengths and field ranges before processing.
|
||||
- Test with filtered traffic to isolate problematic packets.
|
||||
- Review compression/decompression boundaries for errors.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Conclusion
|
||||
ClassicAssist.Avalonia implements a robust networking layer tailored to the Ultima Online protocol. By leveraging typed readers/writers, a centralized handler system, filters, queues, and compression, the application efficiently processes high-frequency network events while maintaining clear separation of concerns. Entity lifecycle management is driven by well-defined packet handlers, ensuring consistent state synchronization. Extensibility is straightforward through handler registration and filter application, and performance can be optimized by careful memory management and batching strategies.
|
||||
|
||||
## Appendices
|
||||
- Best practices:
|
||||
- Always validate packet headers and lengths.
|
||||
- Keep handlers idempotent and side-effect minimal.
|
||||
- Use filters for diagnostics and security checks.
|
||||
- Dispose entities promptly upon removal.
|
||||
- Extension checklist:
|
||||
- Define packet class with serialization/deserialization.
|
||||
- Register incoming/outgoing handlers.
|
||||
- Add filters if necessary.
|
||||
- Update entity collections or enqueue actions.
|
||||
- Test with real traffic and edge cases.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
@@ -0,0 +1,580 @@
|
||||
# Plugin Architecture
|
||||
|
||||
<cite>
|
||||
**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)
|
||||
</cite>
|
||||
|
||||
## 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]
|
||||
Reference in New Issue
Block a user