# Event-Driven Architecture **Referenced Files in This Document** - [Engine.cs](file://ClassicAssist.Avalonia/Engine.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [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) - [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) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs) - [MobileMoving.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileMoving.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) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [ChatViewModel.cs](file://ClassicAssist/Engine/ViewModels/ChatViewModel.cs) - [ThreadQueue.cs](file://ClassicAssist.Shared/Misc/ThreadQueue.cs) - [ThreadPriorityQueue.cs](file://ClassicAssist.Shared/Misc/ThreadPriorityQueue.cs) ## Table of Contents 1. [Introduction](#introduction) 2. [Project Structure](#project-structure) 3. [Core Components](#core-components) 4. [Architecture Overview](#architecture-overview) 5. [Detailed Component Analysis](#detailed-component-analysis) 6. [Dependency Analysis](#dependency-analysis) 7. [Performance Considerations](#performance-considerations) 8. [Troubleshooting Guide](#troubleshooting-guide) 9. [Conclusion](#conclusion) ## Introduction This document explains the event-driven architecture used by ClassicAssist.Avalonia to achieve loose coupling between network, engine, and UI layers. It focuses on how events are published and subscribed across components, how game state changes propagate via MVVM to update the UI, and how packet events flow from the network layer through the game engine to UI updates. It also covers custom event creation, routing, priority-based processing, thread safety for cross-thread handling, and performance optimization techniques for high-frequency events such as packet processing and entity updates. ## Project Structure The event-driven system spans multiple projects: - Shared networking and engine logic (Shared project) - Avalonia UI application (Avalonia project) - WPF legacy bridge (ClassicAssist project) - ViewModels and UI models (Shared UI folder) ```mermaid graph TB subgraph "Shared" Net["Network Layer
Packets, Handlers, Queues"] Eng["Game Engine
Entities, Collections"] UIModel["UI Models & ViewModels"] end subgraph "Avalonia" App["App & Engine Entry"] Disp["Avalonia Dispatcher"] end subgraph "WPF Bridge" WDisp["WPF Dispatcher"] end Net --> Eng Eng --> UIModel UIModel --> App App --> Disp App --> WDisp ``` [No sources needed since this diagram shows conceptual workflow, not actual code structure] ## Core Components - Network packet handlers and queues manage incoming/outgoing packets and route them into the engine. - Game engine entities expose state that is observed by view models. - View models implement MVVM and subscribe to engine state changes. - Dispatchers and UI invokers marshal UI updates onto the UI thread safely. Key responsibilities: - IncomingPacketHandlers and OutgoingPacketHandlers register and dispatch packet-specific logic. - ActionPacketQueue batches and prioritizes actions for efficient processing. - Entity and Mobile classes represent game objects; collections aggregate them. - BaseViewModel provides common MVVM infrastructure; MainWindowViewModel orchestrates UI state. - AvaloniaDispatcher and AvaloniaUIInvoker coordinate UI thread synchronization. **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) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.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) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.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) ## Architecture Overview The system uses an event-driven pipeline: - Network receives packets and publishes typed events via handlers. - Engine consumes these events to mutate entity state. - UI subscribes to state changes or explicit events and updates views via MVVM. - Cross-thread marshaling ensures UI updates occur on the UI thread. ```mermaid sequenceDiagram participant Net as "Network Layer" participant Hdl as "Packet Handlers" participant Q as "Action Packet Queue" participant Eng as "Game Engine" participant VM as "ViewModels" participant UI as "Avalonia UI" Net->>Hdl : "Incoming packet arrives" Hdl->>Q : "Enqueue action/event" Q-->>Eng : "Dequeue and process" Eng->>Eng : "Update Entities/Collections" Eng-->>VM : "State change notifications" VM->>UI : "Invoke UI updates on UI thread" UI-->>VM : "User interactions" VM->>Net : "Outgoing commands/packets" ``` **Diagram sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) - [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) ## Detailed Component Analysis ### Packet Event Handling Pipeline The pipeline transforms raw network packets into structured engine updates and UI reactions. ```mermaid flowchart TD Start(["Packet Received"]) --> Parse["Parse Packet Type"] Parse --> Route["Route to Handler"] Route --> Enqueue["Enqueue Action/Event"] Enqueue --> Process["Process Queue Item"] Process --> UpdateState["Update Engine State"] UpdateState --> Notify["Notify Subscribers"] Notify --> Marshal["Marshal to UI Thread"] Marshal --> End(["UI Updated"]) ``` **Diagram sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) **Section sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) ### Game State Propagation via MVVM - Engine entities expose properties and collections that reflect current game state. - View models observe these changes and raise property change notifications. - UI binds to view model properties; dispatcher ensures updates run on the UI thread. ```mermaid classDiagram class Entity { +Id +Properties +Update(...) } class Mobile { +Position +Status } class PlayerMobile { +IsLocal } class EntityCollection { +Add(...) +Remove(...) +FindById(...) } class BaseViewModel { +RaisePropertyChanged(...) } class MainWindowViewModel { +SelectedEntity +ChatMessages } Mobile --|> Entity PlayerMobile --|> Mobile EntityCollection --> Entity : "manages" MainWindowViewModel --> BaseViewModel : "extends" MainWindowViewModel --> EntityCollection : "observes" ``` **Diagram sources** - [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) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) **Section sources** - [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) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) ### Custom Event Creation and Routing To create a new event type: - Define a strongly-typed event payload class if needed. - Register a handler in the appropriate packet handlers collection. - Use the queue to enqueue the event/action with a priority if required. - Ensure subscribers (engine modules or view models) listen for the event. Routing considerations: - Centralized registration points simplify discovery and testing. - Priority-based queues allow critical events to be processed before less urgent ones. **Section sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) ### Priority-Based Event Processing High-priority events (e.g., combat-related updates) should be enqueued with higher priority to ensure timely processing. Lower-priority events (e.g., cosmetic updates) can be batched or deferred. ```mermaid flowchart TD A["Enqueue Event"] --> B{"Has Priority?"} B --> |Yes| C["Insert into Priority Queue"] B --> |No| D["Append to Standard Queue"] C --> E["Dequeue Highest Priority First"] D --> E E --> F["Process Event"] ``` **Diagram sources** - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) - [ThreadPriorityQueue.cs](file://ClassicAssist.Shared/Misc/ThreadPriorityQueue.cs) **Section sources** - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) - [ThreadPriorityQueue.cs](file://ClassicAssist.Shared/Misc/ThreadPriorityQueue.cs) ### Thread Safety and UI Synchronization Cross-thread event handling requires careful marshaling: - Use IDispatcher and IUIInvoker abstractions to schedule work on the UI thread. - Avoid direct UI updates from background threads. - Prefer batching UI updates to reduce churn during high-frequency events. ```mermaid sequenceDiagram participant BG as "Background Thread" participant Inv as "IUIInvoker" participant Disp as "IDispatcher" participant VM as "ViewModel" participant UI as "Avalonia UI" BG->>Inv : "Request UI update" Inv->>Disp : "Dispatch to UI thread" Disp-->>VM : "Invoke property change" VM-->>UI : "Render updated bindings" ``` **Diagram sources** - [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) **Section sources** - [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) ### High-Frequency Events: Packets and Entity Updates Common high-frequency events include mobile updates and movement packets. To optimize: - Coalesce rapid updates into fewer UI refreshes. - Use immutable snapshots where possible to avoid excessive allocations. - Apply filters to reduce unnecessary processing. ```mermaid flowchart TD Pkt["MobileUpdate/Moving Packet"] --> Filter["Apply Filters"] Filter --> Batch["Batch Updates"] Batch --> Apply["Apply to Entities"] Apply --> Throttle["Throttle UI Notifications"] Throttle --> Render["Render UI"] ``` **Diagram sources** - [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs) - [MobileMoving.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileMoving.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) **Section sources** - [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs) - [MobileMoving.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileMoving.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) ### Application Entry Points and Initialization The Avalonia application initializes core services, registers handlers, and starts the event loop. ```mermaid sequenceDiagram participant Main as "Program" participant App as "App" participant Eng as "Engine" participant Net as "Network" participant UI as "UI" Main->>App : "Build and Run" App->>Eng : "Initialize Engine" Eng->>Net : "Start Network" Net-->>Eng : "Incoming packets" Eng-->>UI : "State changes" UI-->>Eng : "User actions" ``` **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) **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) ## Dependency Analysis The following diagram highlights key dependencies among networking, engine, and UI components. ```mermaid graph TB IPH["IncomingPacketHandlers"] --> APQ["ActionPacketQueue"] OPH["OutgoingPacketHandlers"] --> APQ APQ --> ENG["Engine"] ENG --> ENT["Entity/Mobile/PlayerMobile"] ENG --> COL["EntityCollection"] COL --> VM["MainWindowViewModel"] VM --> UI["Avalonia UI"] UI --> INV["AvaloniaUIInvoker"] INV --> DISP["AvaloniaDispatcher"] ``` **Diagram sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.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) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) **Section sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.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) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) ## Performance Considerations - Batch and throttle UI updates to minimize layout passes. - Use priority queues for critical events and defer low-priority tasks. - Apply packet filters to reduce processing overhead. - Prefer immutable snapshots for large data sets to reduce GC pressure. - Avoid frequent allocations inside hot paths; reuse buffers when possible. - Coalesce rapid entity updates into single notifications. [No sources needed since this section provides general guidance] ## Troubleshooting Guide Common issues and resolutions: - UI not updating: Ensure UI invocations are dispatched on the UI thread using the dispatcher or UI invoker. - Stuttering during high-frequency events: Implement batching and throttling for UI updates. - Missed events: Verify handlers are registered and queues are being drained. - Deadlocks: Avoid holding locks while invoking UI updates; keep UI work minimal. **Section sources** - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) ## Conclusion ClassicAssist.Avalonia’s event-driven architecture enables loose coupling between network, engine, and UI layers. By leveraging typed packet handlers, priority queues, MVVM subscriptions, and robust UI thread marshaling, the system scales to handle high-frequency events efficiently. Following the patterns outlined here—custom event creation, careful routing, priority-based processing, and performance optimizations—will help maintain responsiveness and clarity as the application evolves.