first commit

This commit is contained in:
Beyhan Oğur
2026-09-03 04:45:36 +03:00
commit d5f52cc3d3
1038 changed files with 169579 additions and 0 deletions
@@ -0,0 +1,47 @@
---
kind: build_system
name: .NET Solution Build & CI Pipeline
category: build_system
scope:
- '**'
source_files:
- ClassicAssist.sln
- appveyor.yml
- azure-pipelines.yml
- .github/workflows/staging.yml
- .github/workflows/linux.yml
- ClassicAssist/ClassicAssist.csproj
- ClassicAssist.Avalonia/ClassicAssist.Avalonia.csproj
- ClassicAssist.Shared/ClassicAssist.Shared.csproj
---
The project uses a Visual Studio solution (ClassicAssist.sln) composed of multiple .NET projects built with MSBuild and the modern SDK-style .csproj format. The build system targets both Windows (WPF) and cross-platform (Avalonia) UI shells, sharing a netstandard2.0 core library.
Solution structure:
- ClassicAssist.Shared — shared runtime (netstandard2.0), consumed by all other projects; contains game data, networking, MVVM base classes, and macro command implementations.
- ClassicAssist — WPF client host targeting net48, references Shared plus native cuoapi.dll.
- ClassicAssist.Avalonia — Avalonia desktop shell targeting net48 and netcoreapp3.1; conditionally pulls Linux/macOS native assets when building under the Linux configuration.
- ClassicAssist.Launcher / ClassicAssist.Updater — standalone WPF helpers for launching ClassicUO clients and updating the app from GitHub releases.
- ClassicAssist.Tests — MSTest suite exercising agents, filters, macros, packets, etc.
- Tools/ExportCommands — documentation generator that reflects macro commands into Markdown files.
Configurations and target frameworks:
- Four configurations are defined across the solution: Debug, Release, Develop (adds DEVELOP constant), and Linux (enables Linux-specific packages like HarfBuzzSharp.NativeAssets.Linux).
- All binaries are emitted to Output/ at the repository root; the WPF/Avalonia outputs land in Output/net48/.
- Versioning is computed per-project via MSBuild expressions: 0.3.<days-since-2020-07-03>.<build-number>, giving an ever-increasing patch component without manual bumping.
CI pipelines:
- AppVeyor (appveyor.yml) — builds on Visual Studio 2019 against master, restores NuGet packages, builds the solution, runs ExportCommands.exe to regenerate docs, writes a MANIFEST.json with file hashes, pushes updated wiki Markdown to a separate GitHub wiki repo, then publishes the Output\net48 folder as a GitHub Release artifact tagged with the computed version.
- Azure Pipelines (azure-pipelines.yml) — triggered on the devel branch, restores NuGet, builds with MSBuild, runs VSTest, and publishes Output/net48 as a container artifact.
- GitHub Actions (.github/workflows/) — staging.yml builds the solution on windows-latest using MSBuild and runs tests against ClassicAssist.Tests.dll; linux.yml builds with dotnet build -c Linux on a matrix of macos-latest, ubuntu-latest, and windows-latest, uploading artifacts per OS.
Native dependencies and packaging:
- cuoapi.dll (and zlib32.dll / zlib64.dll) are shipped as checked-in native DLLs referenced via HintPath and copied to output.
- Avalonia's macOS/Linux native libraries are pulled from NuGet and copied into the output directory via a custom MSBuild CopyNativeLibs target when building the Avalonia project under the Linux configuration.
Conventions developers should follow:
- Add new configurations only through the existing Debug|Release|Develop|Linux set; avoid introducing new platform targets outside what CI already covers.
- Keep all build outputs under Output/ so CI artifact publishing paths remain valid.
- Use the SDK-style PackageReference pattern for NuGet dependencies rather than packages.config.
- When adding native libraries, reference them via HintPath and ensure they are copied to output or handled by a custom target.
- Do not manually edit version numbers — rely on the MSBuild date-based expression so AppVeyor can compute a unique tag.
@@ -0,0 +1,39 @@
---
kind: business_term
name: Business Glossary
category: business_term
scope:
- '**'
---
### ClassicUO
- Definition:The primary Ultima Online game client that hosts ClassicAssist as a plugin; serves as the main application process that loads and manages the assistant UI at runtime
- Aliases:CUO、Client
### Plugin
- Definition:A separately compiled assembly (ClassicAssist.Avalonia.exe) that ClassicUO loads dynamically at runtime using reflection; must implement the `Assistant.Engine.Install` entry point and follow specific loading conventions
- Aliases:Plugin Assembly
### Shared
- Definition:The core business logic library (ClassicAssist.Shared.dll) containing UO protocol handling, data models, and common functionality; loaded explicitly by the plugin before UI initialization
- Aliases:Shared Library、Core
### Launcher
- Definition:WPF-based helper application (ClassicAssist.Launcher.exe) for managing ClassicUO instances, shards, and plugin configuration; provides user interface for setup and management tasks
- Aliases:ClassicAssist Launcher
### Updater
- Definition:Standalone WPF application (ClassicAssist.Updater.exe) that handles automatic updates for ClassicAssist components by downloading from GitHub releases
- Aliases:ClassicAssist Updater
### Shard
- Definition:A configured Ultima Online server instance managed by the Launcher; includes connection details and profile settings for different game servers
- Aliases:Server、Game Server
### Agent
- Definition:Automated behavior module within ClassicAssist that performs repetitive tasks like autoloot, buying/selling, dressing, organizing inventory, or scavenging items
- Aliases:Automation Agent、Bot
### Macro
- Definition:Scripted sequence of actions and commands that can be executed automatically; supports both built-in commands and custom IronPython scripts
- Aliases:Macro Command、Script
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_wpf/agents/agents_tab_controls
title: Agents Tab Controls (WPF Views)
scope:
- ClassicAssist/UI/Views/Agents/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Flat collection of WPF `UserControl` subclasses in namespace `ClassicAssist.UI.Views.Agents`, each paired with a `.xaml` + code-behind file. Code-behinds are minimal stubs inheriting from `System.Windows.Controls.UserControl` and only calling `InitializeComponent()`, delegating all logic to ViewModels defined under `ClassicAssist.Shared.UI.ViewModels.Agents` (e.g. `AutolootViewModel`). The XAML files declare the view's data context via `<agents:AutolootViewModel />` and compose shared UI building blocks from `ClassicAssist.UI.Controls`, `ClassicAssist.UI.Misc` behaviors (`ClipboardBehaviour`, `ItemsControlSelectNewEntryBehaviour`, `ListViewMultiSelectionBehaviour`), value converters, and localized strings from `ClassicAssist.Shared.Resources`. Each tab is a self-contained presentation layer with no cross-tab dependencies; consumers assemble them at a higher level (the agents tab host).
@@ -0,0 +1,4 @@
- Code-behind classes are empty stubs that inherit from `UserControl` and call only `InitializeComponent()`; all state and commands live in the ViewModel.
- Each tab's XAML sets its `DataContext` inline by instantiating the matching ViewModel type from `ClassicAssist.Shared.UI.ViewModels.Agents` rather than using a DI container.
- UI text is sourced through `{x:Static resources:Strings.*}` from `ClassicAssist.Shared.Resources` instead of hard-coded strings.
- Reusable interactions are implemented as attached behaviors in `ClassicAssist.UI.Misc` (e.g. `ClipboardBehaviour`, `ItemsControlSelectNewEntryBehaviour`) applied via `i:Interaction.Behaviors`.
@@ -0,0 +1 @@
WPF UserControl views that present the individual agent tabs — autoloot, counters, dress, friends, organizer, scavenger, and vendor buy/sell — bound to their corresponding ViewModels.
@@ -0,0 +1 @@
WPF (`System.Windows.Controls`) with XAML markup, Microsoft.Xaml.Behaviors for `Interaction.Behaviors`, and MVVM binding against ViewModels in the separate `ClassicAssist.Shared` assembly.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_wpf/agents/autoloot
title: Autoloot Property Selection Dialogs (WPF)
scope:
- ClassicAssist/UI/Views/Autoloot/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Three sibling WPF `Window` views — `PropertySelectionWindow`, `CustomPropertiesWindow`, and `ClilocSelectionWindow` — each paired with a thin code-behind class inheriting from `System.Windows.Window`. The view-models live in the cross-cutting `ClassicAssist.Shared.UI.ViewModels.Autoloot` namespace (referenced via `xmlns:autoloot`) and are instantiated directly in XAML as `Window.DataContext`, so this module owns only presentation logic. All three windows share a uniform two-column layout (content on the left, action buttons on the right), load shared resources from `../../../Resources/Icons.xaml` and `ResourceDictionary.xaml`, bind localized UI text through `ClassicAssist.Shared.Resources.Strings`, and use `CloseOnClickBehaviour` attached via `System.Windows.Interactivity` to dismiss themselves after OK/Save actions. There is no internal sub-layering beyond the standard XAML/code-behind split.
@@ -0,0 +1,5 @@
- Each window pairs a `.xaml` with an empty code-behind class whose sole job is `InitializeComponent()` and inherits from `System.Windows.Window`.
- ViewModels are declared inline in XAML via `<autoloot:... />` and set as `Window.DataContext`, keeping code-behind free of binding setup.
- All user-facing text is pulled from `ClassicAssist.Shared.Resources.Strings` using `{x:Static ...}` rather than hardcoded literals.
- Dialog dismissal is handled uniformly by attaching `misc:CloseOnClickBehaviour` to OK/Save buttons instead of calling `Close()` in code.
- Every window merges the same shared resource dictionaries (`Icons.xaml`, `ResourceDictionary.xaml`) and applies `ThemeWindowBackgroundBrush` plus `Topmost="True"`.
@@ -0,0 +1 @@
WPF dialog windows that let players pick item properties for the autoloot feature, either by browsing item definitions or searching cliloc strings.
@@ -0,0 +1 @@
WPF (`System.Windows.Window`, `System.Windows.Interactivity`) with MVVM; localization via `ClassicAssist.Shared.Resources.Strings` and theme brushes from merged resource dictionaries.
@@ -0,0 +1,9 @@
schema_version: 1
module_path: classic_assist_wpf/agents
title: Agents UI (WPF Tab Views & Autoloot Dialogs)
scope:
- ClassicAssist/UI/Views/Agents/
- ClassicAssist/UI/Views/Autoloot/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Two sibling view folders share the same contract: every `*TabControl.xaml` in `Views/Agents/` instantiates a corresponding ViewModel from `ClassicAssist.Shared.UI.ViewModels.Agents` as its `DataContext`, while the `AutolootTabControl` additionally hosts the autoloot rule editor (items list + constraint grid) and opens the dialog windows under `Views/Autoloot/` (`ClilocSelectionWindow`, `CustomPropertiesWindow`, `PropertySelectionWindow`) through commands exposed on the same ViewModel. The code-behind of each tab control is a thin `InitializeComponent()` shell — all logic lives in the Shared ViewModels, so this module acts purely as the XAML binding layer between the game's Shared data models (`ClassicAssist.Data.Autoloot`, etc.) and the agents subsystem.
@@ -0,0 +1,3 @@
- Each `*TabControl.xaml.cs` is a one-liner constructor calling only `InitializeComponent()`, with no event handlers or logic.
- The XAML sets `DataContext` directly to a Shared ViewModel type (e.g. `<agents:AutolootViewModel />`) rather than relying on DI resolution at runtime.
- Cross-cutting interactions (clipboard, multi-select, new-entry insertion) are implemented as attached behaviors from `ClassicAssist.UI.Misc` bound via `i:Interaction.Behaviors` instead of code-behind.
@@ -0,0 +1 @@
WPF views that compose the Agents tab page and its property-selection dialogs, wiring each agent's UserControl to a Shared ViewModel via DataContext.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/agents_tab_controls
title: Agents Tab Panel (WPF)
scope:
- ClassicAssist/UI/Views/Agents/
- ClassicAssist/UI/Views/AgentsTabControl.xaml
- ClassicAssist/UI/Views/AgentsTabControl.xaml.cs
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Two-layer WPF composition: `AgentsTabControl.xaml` is a thin `UserControl` hosting a `TabControl` whose `TabItem`s are declared purely in XAML and each hosts one child view from the `ClassicAssist.UI.Views.Agents` namespace. The eight agent views (`Organizer`, `Dress`, `Counters`, `Friends`, `VendorBuy`, `VendorSell`, `Scavenger`, `Autoloot`) live as sibling `*.xaml` / `*.xaml.cs` pairs under `Agents/`; their code-behind classes are empty `partial` classes inheriting from `System.Windows.Controls.UserControl` with only an `InitializeComponent()` constructor, so all logic is expected to be supplied via data binding to view-models defined elsewhere. Header text for every tab is resolved through `resources:Strings.*` static resources, keeping UI text out of markup. Dependency direction is strictly inward: the parent tab control depends on the child agents; children do not reference the parent.
@@ -0,0 +1,3 @@
- Each agent feature ships as a paired `*.xaml` + `*.xaml.cs` where the code-behind is a minimal `partial class : UserControl` containing only `InitializeComponent()` in the constructor.
- Tab headers are localized by referencing `resources:Strings.<Key>` static XAML bindings rather than hard-coded strings.
- Child agent controls are referenced via a single `xmlns:agents="clr-namespace:ClassicAssist.UI.Views.Agents"` prefix and instantiated directly as XAML elements inside `TabItem` content.
@@ -0,0 +1 @@
WPF tabbed container that groups all player-agent management sub-panels — Organizer, Dress, Counters, Friends, Vendor Buy/Sell, Scavenger, and Autoloot — behind a single AgentsTabControl.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/autoloot_windows
title: Autoloot Property Selection Dialogs (WPF)
scope:
- ClassicAssist/UI/Views/Autoloot/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Three sibling WPF `Window` views — `PropertySelectionWindow`, `ClilocSelectionWindow`, and `CustomPropertiesWindow` — each paired with a minimal code-behind that only calls `InitializeComponent()`. All UI logic is delegated to view models in the cross-cutting `ClassicAssist.Shared.UI.ViewModels.Autoloot` namespace (`PropertySelectionViewModel`, `ClilocSelectionViewModel`, `CustomPropertiesViewModel`), which are instantiated directly in XAML via `<autoloot:... />` bindings. The `.xaml.cs` files depend on `System.Windows` (WPF), while the XAML markup additionally references shared resources (`ClassicAssist.Shared.Resources.Strings`, `ClassicAssist.UI.Misc.CloseOnClickBehaviour`, `ClassicAssist.UI.Resources.*`) and Expression Blend interactivity for button-close behavior. Dependency direction is strictly View → ViewModel; there is no controller or service layer inside this folder.
@@ -0,0 +1,4 @@
- Each window has a one-line constructor that only invokes `InitializeComponent()`; all command handling lives in the bound ViewModel rather than in code-behind.
- XAML sets `DataContext` inline via an `<autoloot:...ViewModel />` element so each window owns its own ViewModel instance instead of using DI.
- OK/Cancel buttons use `CloseOnClickBehaviour` from `ClassicAssist.UI.Misc` through `i:Interaction.Behaviors` instead of explicit click handlers.
- User-facing text is pulled from `ClassicAssist.Shared.Resources.Strings` via `{x:Static ...}` resource lookups, never hard-coded strings.
@@ -0,0 +1 @@
WPF dialog windows that let players pick autoloot properties by item, by cliloc key, or by defining custom property entries.
@@ -0,0 +1 @@
Windows Presentation Foundation (WPF) with XAML data binding, Expression Blend Interactivity (`i:Interaction.Behaviors`), and MVVM via view models defined in the shared `ClassicAssist.Shared` assembly.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/core_game_features
title: Classic Assist Game UI Panels (Chat, Skills, Hotkeys)
scope:
- ClassicAssist/UI/Views/ChatWindow.xaml
- ClassicAssist/UI/Views/SkillsTabControl.xaml
- ClassicAssist/UI/Views/HotkeysTabControl.xaml
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Three independent WPF views — a top-level `Window` (`ChatWindow.xaml`) and two reusable `UserControl`s (`SkillsTabControl.xaml`, `HotkeysTabControl.xaml`) — each declaring its own `DataContext` as an MVVM ViewModel from `ClassicAssist.UI.ViewModels` (e.g. `ChatViewModel`, `SkillsTabViewModel`, `HotkeysTabViewModel`). Views are purely declarative: they bind to ViewModel properties/commands via standard WPF bindings, use `ClassicAssist.UI.Misc.ValueConverters` for display logic, and delegate cross-cutting concerns to shared resources under `../../Resources/` (`DarkTheme.xaml`, `Icons.xaml`, `ResourceDictionary.xaml`). Interaction with non-visual state is routed through `ClassicAssist.UI.Misc` behaviors (`ItemsControlAutoScrollBehaviour`, `ComboBoxBehavior`, `BindableSelectedItemBehavior`) and `ClassicAssist.Misc.BindingProxy` to reach commands inside nested visual trees such as `ContextMenu`. The Chat window additionally hosts a custom title bar via `ClassicAssist.UI.Controls.CustomWindowTitleControl` and a right-hand user list split by a `GridSplitter`; the Skills panel uses a `ListView` with `GridView` columns backed by `SkillsGridViewColumn`, while the Hotkeys panel renders a hierarchical `TreeView` of `HotkeyEntry` items bound from `ClassicAssist.Data.Hotkeys`.
@@ -0,0 +1,5 @@
- Each XAML file instantiates its ViewModel directly in markup via `<... .DataContext><viewModels:...ViewModel /></... .DataContext>` rather than setting it in code-behind.
- Shared styling and icons are loaded by merging resource dictionaries from relative paths (`../../Resources/DarkTheme.xaml`, `../../Resources/Icons.xaml`, `../../Resources/ResourceDictionary.xaml`) inside the view's `UserControl.Resources` / `Window.Resources`.
- Commands inside detached visual trees (e.g. `ContextMenu`, `DataTemplate`) are invoked through a `misc:BindingProxy` keyed as `Proxy` so the binding source can reach the parent DataContext.
- UI interactions that need code-behind access are implemented as attached behaviors in `ClassicAssist.UI.Misc` (e.g. `ItemsControlAutoScrollBehaviour`, `ComboBoxBehavior`, `BindableSelectedItemBehavior`) wired via `i:Interaction.Behaviors`.
- Localized strings are referenced through `x:Static resources:Strings.<Key>` from `ClassicAssist.Shared.Resources` instead of hard-coded text.
@@ -0,0 +1 @@
WPF XAML view definitions for the in-game chat window and two tabbed panels that let players manage skills and hotkeys.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/utility_windows
title: Debug & Utility Dialog Windows
scope:
- ClassicAssist/UI/Views/DebugWindow.xaml
- ClassicAssist/UI/Views/GIFRecorderWindow.xaml
- ClassicAssist/UI/Views/HuePickerWindow.xaml
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,6 @@
Three standalone WPF `Window` views (no code-behind logic here) that each own their ViewModel via inline `<Window.DataContext>` instantiation:
- `DebugWindow.xaml` is a tabbed diagnostic shell (`TabControl`) hosting multiple sub-controls from the sibling `ClassicAssist.UI.Views.Debug` namespace (gumps, menus, buff icons, special moves, vendor buy, autoloot, properties, journal, assemblies), with its toolbar styling pulled in from `Debug/DebugToolBarStyle.xaml`. It binds to `ClassicAssist.Shared.UI.ViewModels.DebugViewModel`.
- `GIFRecorderWindow.xaml` is a transparent, borderless, always-on-top overlay window using `CustomWindowTitleControl` plus three embedded `DrawingImage` resources (wait/play/stop/save) and a `misc:BindingProxy` to reach the `GIFRecorderViewModel` commands from nested controls.
- `HuePickerWindow.xaml` is a filterable list of `HueEntryControl` items bound one-way to itself (`DataContext={RelativeSource Self}`), with OK/Cancel buttons wired to an MVVM command and closed via a shared `CloseOnClickBehaviour` attached through `System.Windows.Interactivity`.
All three follow the same dependency direction: XAML → `ClassicAssist.UI.Controls`, `ClassicAssist.UI.Misc`, `ClassicAssist.Shared.Resources.Strings`, and a dedicated ViewModel in either `ClassicAssist.UI.ViewModels` or `ClassicAssist.Shared.UI.ViewModels`; they do not import each other.
@@ -0,0 +1,4 @@
- Each window declares its ViewModel directly in XAML via `<Window.DataContext><viewModels:... /></Window.DataContext>` rather than setting it in code-behind.
- User-facing strings are resolved through `{x:Static resources:Strings.*}` from `ClassicAssist.Shared.Resources` instead of hard-coded literals.
- Reusable UI chrome (window title bar, hex dump, hue entry) is composed by plugging in custom controls from `ClassicAssist.UI.Controls` rather than building visuals inline.
- Cross-naming scope behavior (closing a dialog from a button inside a child control) is achieved via `misc:BindingProxy` + `ElementName` binding or `i:Interaction.Behaviors` with `CloseOnClickBehaviour`, keeping code-behind free.
@@ -0,0 +1 @@
WPF dialog windows providing in-game debugging, GIF recording, and hue selection utilities for ClassicAssist.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/entity_tools
title: Entity & Profile Inspector Dialogs (WPF)
scope:
- ClassicAssist/UI/Views/EntityCollectionViewer.xaml
- ClassicAssist/UI/Views/ObjectInpectorWindow.xaml
- ClassicAssist/UI/Views/NewProfileWindow.xaml
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Pure XAML view layer — each file is a `Window` subclass whose `DataContext` is instantiated inline in markup (`EntityCollectionViewerViewModel`, `ObjectInspectorViewModel`, `NewProfileViewModel`) rather than resolved through a DI container. The views depend on sibling ViewModels in `ClassicAssist.UI.ViewModels` / `ClassicAssist.Shared.UI.ViewModels`, shared resources in `ClassicAssist.Shared.Resources.Strings`, and reusable behaviors/styles from `ClassicAssist.UI.Misc` (e.g. `MultiSelectionBehaviour`, `ListViewDoubleClickBehaviour`, `CloseOnClickBehaviour`). `EntityCollectionViewer.xaml` composes the `EntityCollectionFilterControl` sub-view and merges its own `EntityCollectionViewerResourceDictionary.xaml` plus `Debug/DebugToolBarStyle.xaml`. `ObjectInspectorWindow.xaml` groups items by a `Category` property via a `CollectionViewSource.PropertyGroupDescription` and exposes an Expander-based group template. `NewProfileWindow.xaml` uses an `EnumMatchToBooleanConverter` to bind mutually exclusive radio buttons to a single enum property. All three follow MVVM with no code-behind logic visible here; interactivity is delegated to Expression Blend `i:Interaction.Behaviors` attached to controls.
@@ -0,0 +1,4 @@
- ViewModels are created directly in XAML via `<viewModels:... />` DataContext declarations instead of being injected or resolved at runtime.
- User-facing strings are pulled from `ClassicAssist.Shared.Resources.Strings` using `{x:Static resources:Strings.*}` bindings rather than hardcoded literals.
- Cross-cutting UI interactions (multi-select, double-click-to-open, close-on-click) are implemented as Expression Blend `i:Interaction.Behaviors` from `ClassicAssist.UI.Misc` and attached declaratively.
- Toolbar/menu actions drive commands on the ViewModel with `CommandParameter` carrying strongly-typed enum values (e.g. `EntityCollectionSortStyle`) instead of string tags.
@@ -0,0 +1 @@
Three WPF dialog windows that let users browse entity collections, inspect object properties in a grouped list, and create new profiles via a small confirmation form.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/filters_windows
title: Filter Configuration Dialogs (WPF)
scope:
- ClassicAssist/UI/Views/Filters/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,4 @@
Two independent WPF `Window` subclasses under the `ClassicAssist.UI.Views.Filters` namespace:
- `ClilocFilterConfigureWindow` follows MVVM: its XAML sets `DataContext` to a view model (`filters:ClilocFilterConfigureViewModel`) declared inline, while the code-behind is a minimal `InitializeComponent()` constructor. The UI composes a toolbar (save/remove), a `ListView` of configured Cliloc entries with an editable `Replacement` column via `controls:EditTextBlock`, and a lower pane that lets users search/filter available clilocs and add them.
- `RepeatedMessagesFilterConfigureWindow` uses self-binding (`DataContext="{Binding RelativeSource={RelativeSource Self}}")` and implements `INotifyPropertyChanged` directly on the window class, exposing a `MessageFilterOptions` object whose properties (`SendToJournal`, `MessageLimit`, `TimeLimit`, `BlockedTime`) are bound two-way to Xceed `IntegerUpDown` controls and a checkbox.
Both windows depend only on shared resources (`ClassicAssist.Shared.Resources.Strings`), custom controls (`ClassicAssist.UI.Controls`), value converters (`ClassicAssist.UI.Misc.ValueConverters`), and filter option models (`ClassicAssist.Data.Filters`). No cross-window communication — each dialog owns its own options instance.
@@ -0,0 +1,3 @@
- User-facing text is never hardcoded in XAML; all labels, headers, and tooltips are resolved through `x:Static resources:Strings.<Key>` from `ClassicAssist.Shared.Resources`.
- Dialogs expose their configuration state as a dedicated Options/ViewModel property and bind UI elements two-way to it, rather than reading/writing controls directly in code-behind.
- Command-driven actions (Save, Remove, Add, Close) are wired in XAML via `Command`/`CommandParameter` bindings or attached behaviors instead of Click event handlers.
@@ -0,0 +1 @@
WPF dialog windows for configuring ClassicAssist's in-game message filters, including Cliloc replacement and repeated-message suppression.
@@ -0,0 +1 @@
WPF (System.Windows) with Xceed WPF Toolkit (`xctk:IntegerUpDown`) for numeric input; Expression Blend Interactivity (`http://schemas.microsoft.com/expression/2010/interactivity`) used by `CloseOnClickBehaviour` to close dialogs without code-behind event handlers.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/macros_windows
title: Macros Editor & Command Browser (WPF)
scope:
- ClassicAssist/UI/Views/Macros/
- ClassicAssist/UI/Views/MacrosTabControl.xaml
- ClassicAssist/UI/Views/MacrosTabControl.xaml.cs
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,5 @@
Two WPF views driven by MVVM in the `ClassicAssist.Shared` assembly:
- `MacrosTabControl.xaml/.cs` is a `UserControl` hosting a left-side list of `MacroEntry` items (new/save/remove/record/play/stop/hotkey binding) and a right-side `ICSharpCode.AvalonEdit.TextEditor` bound to `SelectedItem.Macro`. The code-behind performs view-only initialization: loads the embedded `Python.Dark.xshd` highlighting, scans the executing assembly for public static methods decorated with `CommandsDisplayAttribute` under namespace `*Macros.Commands`, and wires AvalonEdit's completion window on text entry. A `GridSplitter` separates the two panes.
- `MacrosCommandWindow.xaml/.cs` is a standalone `Window` whose `DataContext` is `MacrosCommandViewModel`; it groups command items by `Category` via a `CollectionViewSource` and exposes an Insert button that pushes the selected command into the editor.
- Both views consume shared resources from `ClassicAssist.Shared` (`Strings`, `ShortcutKeys`, `MacroEntry`) and reuse custom controls/behaviors from `ClassicAssist.UI.Controls` and `ClassicAssist.UI.Misc` (e.g. `AvalonEditBehaviour`, `ItemsControlSelectNewEntryBehaviour`, `CloseOnClickBehaviour`).
- Dependency direction: Views → ViewModels (in `ClassicAssist.Shared`) → data model (`ClassicAssist.Data.Macros`); the code-behind only touches AvalonEdit plumbing and never owns business logic.
@@ -0,0 +1,4 @@
- View markup declares its ViewModel as `<xxx.DataContext>` inline and binds through `{Binding ...}` without any code-behind property wiring.
- Button/command bindings use `Mode=OneWay` for the `Command` property so the view cannot accidentally overwrite the VM's command reference.
- UI strings are sourced exclusively from `ClassicAssist.Shared.Resources.Strings` via `x:Static resources:Strings.Xxx` rather than hard-coded literals.
- AvalonEdit editor state is synchronized with the model through reusable `misc:AvalonEditBehaviour` / `misc:AvalonBindingBehaviour` attached behaviors instead of direct TextEditor property access in the VM.
@@ -0,0 +1 @@
WPF UI for browsing, editing, recording and executing ClassicAssist macros, plus a popup window that lists discoverable macro commands for insertion into scripts.
@@ -0,0 +1 @@
WPF + XAML with ICSharpCode.AvalonEdit for syntax-highlighted Python scripting; Expression Blend Interactivity (`i:`) for behaviors; dynamic discovery of macro commands via reflection over `*Macros.Commands` types annotated with `CommandsDisplayAttribute`.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui/app_shell_and_settings
title: Main Window Shell & Settings UI
scope:
- ClassicAssist/UI/Views/MainWindow.xaml
- ClassicAssist/UI/Views/AboutControl.xaml
- ClassicAssist/UI/Views/GeneralControl.xaml
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Three XAML views form the top-level UI: `MainWindow.xaml` is an `Avalonia.Window` (not WPF — despite the folder name) that instantiates `MainWindowViewModel`, attaches `LoadOptionsOnWindowLoaded` and `WindowResizeBehaviour` via `System.Windows.Interactivity.Behaviors`, and hosts a `TabControl` whose tabs compose sibling user-controls (`GeneralControl`, `OptionsTabControl`, `HotkeysTabControl`, `MacrosTabControl`, `SkillsTabControl`, `AgentsTabControl`, `browser:MacroBrowserControl`, `AboutControl`). Each tab control is a leaf `UserControl` whose `DataContext` is created inline in markup (e.g. `<viewModels:GeneralControlViewModel />`) from the shared `ClassicAssist.Shared` assembly; no code-behind wiring is used. Shared resources (icons, theme brushes) are pulled in by merging `../../Resources/Icons.xaml` and `../../Resources/ResourceDictionary.xaml` into each view's `Window.Resources` / `UserControl.Resources`. All text is localized through `ClassicAssist.Shared.Resources.Strings` static properties rather than hard-coded strings. The module depends outward on `ClassicAssist.UI.Controls`, `ClassicAssist.UI.ViewModels`, `ClassicAssist.UI.Misc`, `ClassicAssist.Browser`, and `ClassicAssist.Shared`; nothing inside this scope references other UI modules.
@@ -0,0 +1,5 @@
- Each view declares its ViewModel directly in XAML via `<viewModels:...ViewModel />` as `DataContext`, avoiding code-behind instantiation.
- Localized UI text is read exclusively through `{x:Static r:Strings.<Key>}` from `ClassicAssist.Shared.Resources`, never hard-coded literals.
- Shared icons and theme brushes are loaded by merging `../../Resources/Icons.xaml` and `../../Resources/ResourceDictionary.xaml` into each view's Resources dictionary.
- Complex interactions (window loading, resize handling, combo-box command binding) are implemented as `Interaction.Behaviors` in `ClassicAssist.UI.Misc` rather than event handlers in code-behind.
- Designer previewability is enabled per-control via `d:DesignInstance` / `d:DesignStyle` so views render with sample data at design time.
@@ -0,0 +1 @@
WPF shell hosting the ClassicAssist main window with a tabbed settings surface (General, Options, Hotkeys, Macros, Skills, Agents, Macro Browser, About) and its About/General view controls.
@@ -0,0 +1 @@
Avalonia UI (XAML + MVVM) targeting Windows, using `System.Windows.Interactivity` for behaviors and `ClassicAssist.Shared` for ViewModels and localized string resources.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_wpf/main_ui
title: Classic Assist WPF Game UI Layer
scope:
- ClassicAssist/UI/Views/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
The Views folder is a flat collection of XAML/XAML.cs pairs with no shared base class or MVVM framework — each file owns one window or control. Cross-cutting wiring happens at two levels: (1) `MainWindow.xaml` hosts an `AgentsTabControl` plus a `MacrosTabControl`, which in turn compose the sub-panels under `Agents/` (Organizer, Dress, Counters, Friends, VendorBuy/Sell, Scavenger, Autoloot); (2) top-level dialogs (`ChatWindow`, `DebugWindow`, `GIFRecorderWindow`, `HuePickerWindow`, `ObjectInpectorWindow`, `ActiveObjectsWindow`, `EntityCollectionViewer`, `NewProfileWindow`) are launched by the shell rather than embedded. All children depend on the same ClassicAssist core assemblies for data binding; there is no cross-view messaging contract inside this scope — communication flows through the application service layer outside Views.
@@ -0,0 +1,3 @@
- Every XAML view has a sibling `.xaml.cs` code-behind file; no separate ViewModel classes live under Views.
- Dialog-style windows derive from `RepositionableGumpWindow` to share drag-to-reposition behavior.
- Tabbed surfaces follow the pattern `<XxxTabControl>.xaml` hosting child panels via `<ContentPresenter>` or nested tabs.
@@ -0,0 +1 @@
WPF view layer that wires the main window shell, tabbed settings surface, and all in-game dialog windows for ClassicAssist's Ultima Online client.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/data_utils
title: Hotkey Commands, Macro Completion & Skill Comparer
scope:
- ClassicAssist/Data/Hotkeys/
- ClassicAssist/Data/Macros/
- ClassicAssist/Data/Skills/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,5 @@
Three independent leaf sub-packages with no internal dependencies on each other:
- `Hotkeys/Commands/` — each file is a concrete `HotkeyCommand` subclass decorated with `[HotkeyCommand(Name=..., Category=...)]`, registered by convention at startup; `Execute()` bridges game state (`ClassicAssist.Shared.UO.Commands`, `Engine.Items/Mobiles`) to WPF views in `ClassicAssist.UI.Views` (e.g. `EntityCollectionViewer`, `ChatWindow`, `GIFRecorderWindow`).
- `Macros/PythonCompletionData.cs` — implements `ICSharpCode.AvalonEdit.CodeCompletion.ICompletionData` so the macro editor can offer Python keyword completions.
- `Skills/SkillComparer.cs` — implements `System.Collections.IComparer` over `SkillEntry`, driven by `SkillsGridViewColumn.Enums` and `ListSortDirection`, used by the skills grid view.
Dependency direction is one-way: this module depends on `Shared`, `UI`, and `UO.Objects`; nothing here is depended on by Shared/UI.
@@ -0,0 +1,2 @@
- Hotkey actions are declared as classes deriving from `HotkeyCommand` and marked with the `[HotkeyCommand]` attribute carrying `Name` (and optional `Category`), with logic placed in an overridden `Execute()` method.
- WPF window launchers spin up a new background thread, set `ApartmentState.STA`, construct the window, call `Show()` / `ShowDialog()`, then pump the dispatcher via `Dispatcher.Run()`.
@@ -0,0 +1 @@
Small data-layer utilities: hotkey command implementations that open UI windows, a Python macro completion entry for AvalonEdit, and the skills grid sort comparer.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_wpf/browser
title: Macro Browser WPF Control
scope:
- ClassicAssist/Browser/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,8 @@
MVVM layering inside a single WPF UserControl:
- `MacroBrowserControl.xaml/.xaml.cs` is the view; it wires AvalonEdit syntax highlighting (Python.Dark.xshd) and opens hyperlinks via `Process.Start`.
- `MacroBrowserViewModel : BaseViewModel` owns all UI state (`ObservableCollection<string>` for Authors/Eras/Shards/Macros, `Category` filter), exposes `RelayCommand`/`RelayCommandAsync` actions, and drives data loading on construction by invoking a `_data` dictionary of `Func<Task<string[]>>` entries backed by the singleton `Database`.
- `Data/Database` is a thread-safe singleton wrapping an in-process LiteDB file (`macros.db`) with three collections — `repository`, `manifest`, `macros` — plus an `HttpClient` and `GitHubClient`. It fetches `metadata.json` from the repo's raw URL, caches it for 15 minutes (or until the master branch SHA changes), and lazily downloads macro source text keyed by name+SHA1.
- `Models/` holds LiteDB-persisted entities (`Macro`, `Manifest`, `Metadata`, `Repository`) plus a `Category` used as a hierarchical filter value.
- `Data/Filter` + `FilterType` model the shard/era/author/category predicate applied against the manifest before returning macro names.
- `Controls/ClearableComboBox` is a reusable ComboBox extension exposing a `ClearCommand` DP bound to the ViewModel's `ClearCommand`.
Dependency direction: View → ViewModel → Data/Models; Models are pure POCOs decorated with `[BsonId]`; no cross-dependencies between sibling subfolders except ViewModel depending on both Data and Models.
@@ -0,0 +1,5 @@
- UI state properties follow the `SetProperty(ref _field, value)` pattern inherited from `BaseViewModel`, with filter setters additionally calling `ApplyFilter()` to re-query the database.
- Commands are exposed as lazy-initialized read-only properties using `??=` with `new RelayCommand(...)` / `new RelayCommandAsync(...)`, guarded by a can-execute lambda over `SelectedItem`.
- Background work uses `Task.Run` followed by `.ContinueWith` or `_dispatcher.Invoke`/`_dispatcher.InvokeAsync` to marshal results back onto the UI thread before touching `ObservableCollection`s.
- Long-running async operations wrap their body in try/catch/finally that sets `Loading = false` and surfaces errors through `MessageBox.Show` with localized `Strings.Error`.
- LiteDB entities are plain POCOs annotated with `[BsonId]` and stored in collections created via `database.GetCollection<T>("name")` with `EnsureIndex(x => x.Id)`.
@@ -0,0 +1 @@
WPF user control that browses, filters, previews, and installs UO macros from the official ClassicAssist-Macros GitHub repository.
@@ -0,0 +1 @@
WPF with XAML code-behind, MVVM via `ClassicAssist.UI.ViewModels.BaseViewModel` and `RelayCommand`, AvalonEdit for macro preview syntax highlighting, LiteDB for local persistence (`macros.db`), Newtonsoft.Json for deserializing the remote `metadata.json`, and Octokit/GitHub REST API for commit-hash comparison.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/engine_support
title: WPF Engine Host & UI Abstractions
scope:
- ClassicAssist/Engine.cs
- ClassicAssist/WPFDispatcher.cs
- ClassicAssist/WPFUIInvoker.cs
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,6 @@
Three cooperating types form the WPF-specific host layer around `ClassicAssist.Shared.Engine`:
- `Engine.cs` (namespace `Assistant`) is the single entry point consumed by the plugin loader. `Install(PluginHeader*)` loads `ClassicAssist.Shared.dll`, subscribes to `AssemblyResolve` to resolve dependencies from `StartupPath` plus the runtime directory (with a special-case rename for `Colletions` → `System.Collections`), then spawns a background STA thread where it constructs `WPFDispatcher`/`WPFUIInvoker`, installs the shared engine via `SEngine.Install(plugin, new WPFMessageBoxProvider())`, hooks `Options.LoadEvent`/`SaveEvent` to serialize/deserialize every `BaseViewModel` implementing `ISettingProvider`, creates `MainWindow`, and runs `Dispatcher.Run()`.
- `WPFDispatcher.cs` implements the cross-platform `IDispatcher` interface by forwarding `Invoke`/`InvokeAsync` to `System.Windows.Threading.Dispatcher`.
- `WPFUIInvoker.cs` implements `IUIInvoker`: `Invoke`/`InvokeDialog<T>` reflectively locate a `Window` subclass by name in the executing assembly, construct it with optional ctor/data-context parameters, set `DataContext`, and show it on the dispatcher thread; `GetHueAsync` spins up its own STA thread to host `HuePickerWindow`; clipboard helpers call `System.Windows.Clipboard` directly.
Dependency direction: this module depends on `ClassicAssist.Shared` (for `SEngine`, `IDispatcher`, `IUIInvoker`, `WPFMessageBoxProvider`) and on the WPF UI project (`ClassicAssist.UI.Views`, `ClassicAssist.UI.ViewModels`, `ClassicAssist.Data`, `ClassicAssist.Misc`, `CUO_API`). The shared engine never references WPF — WPF concerns are isolated behind the two interfaces so the same core can be hosted elsewhere.
@@ -0,0 +1,3 @@
- Cross-thread UI access is always funneled through `IDispatcher.Invoke`/`InvokeAsync` rather than calling WPF APIs directly from background code.
- Window discovery uses string-typed names resolved via `Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == typeName && t.IsSubclassOf(typeof(Window)))`, throwing `ArgumentNullException` when not found.
- Optional constructor and data-context parameters are passed through as `object[]` arrays and materialized with `Activator.CreateInstance` at invocation time.
@@ -0,0 +1 @@
Hosts the ClassicAssist WPF application by bootstrapping the shared engine, wiring a WPF dispatcher and UI invoker, and running the main window on an STA thread.
@@ -0,0 +1 @@
WPF (`System.Windows.Threading.Dispatcher`, `System.Windows.Window`, `System.Windows.Clipboard`) as the concrete UI platform; reflection + `Activator.CreateInstance` for string-based window resolution; `TaskCompletionSource` to bridge STA dialog threads into async callers.
@@ -0,0 +1,9 @@
schema_version: 1
module_path: classic_assist_wpf/resources_assets
title: WPF Theme & Embedded Resources
scope:
- ClassicAssist/Resources/
- ClassicAssist/Properties/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,6 @@
Two parallel resource tracks coexist in the project:
- WPF XAML resources under `Resources/` are organised as a merged-dictionary chain: `ResourceDictionary.xaml` is the single entry point that merges `DarkTheme.xaml`, which in turn merges `Colours.xaml`. `Icons.xaml` holds all vector icons as `DrawingImage` entries keyed by name (e.g. `unlinkedIcon`, `PythonIcon`, `WebIcon`) and can be merged wherever needed; `LeftRightIconToggleButton.xaml` defines a reusable control template that references those icons. `Shrink.txt` is an embedded text asset.
- .NET ResX resources under `Properties/Resources.resx` + `Resources.Designer.cs` expose strongly-typed `System.Drawing.Bitmap` and string properties (`arrow_down`, `arrow_up`, `padlock`, `python`, `Shrink`) backed by `ResourceManager` lookups. The `.resx` file points at sibling PNGs via relative paths (`..\Resources\*.png`).
Dependency direction is one-way: `DarkTheme.xaml` → `Colours.xaml`; `ResourceDictionary.xaml` → `DarkTheme.xaml`; `Icons.xaml` is standalone and consumed by consumers via merge. No C# code lives in this module — it is purely declarative assets consumed by other assemblies.
@@ -0,0 +1 @@
WPF theme assets for ClassicAssist, providing a dark colour palette, per-control styles, inline DrawingImage icons, and .NET ResX-embedded bitmaps/text.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: classic_assist_wpf/ui_layer
title: WPF UI Controls, Behaviours & Value Converters
scope:
- ClassicAssist/UI/Controls/
- ClassicAssist/UI/Misc/
- ClassicAssist/UI/Models/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,6 @@
Three sibling sub-packages with a flat dependency direction (UI → Core/Misc, never the reverse):
- `Controls/` — self-contained `UserControl`s (`CustomWindowTitleControl`, `HueEntryControl`, `HexDumpControl`, `EditTextBlock`, `TextBoxKey`) plus a layout helper (`StretchyWrapPanel`). Each control exposes its bindable surface via `DependencyProperty` + CLR wrapper pairs.
- `Misc/` — behaviour/utility layer split into three groups: (1) `System.Windows.Interactivity.Behavior<T>` subclasses (`AvalonEditBehaviour`, `AutoScrollBehaviour`, `WindowResizeBehaviour`, `ItemsControlSelectNewEntryBehaviour`, …) that attach to existing WPF elements without subclassing; (2) `ValueConverters/` implementing `IValueConverter` for data-binding transformations, with `ValueConverterGroup` enabling chained conversion in XAML; (3) markup extensions (`EnumBindingSourceExtension`) and cross-cutting helpers (`BindingProxy`, `ClipboardBehaviour`, `WPFMessageBoxProvider` which implements `IMessageBoxProvider` from `ClassicAssist.Misc` to bridge WinForms `MessageBox` into the app's abstraction).
- `Models/` — tiny INotifyPropertyChanged view-model fragments (`EntityCollectionFilter`, `EntityCollectionSortStyle`) that encapsulate filter/sort state consumed by list/grid views.
No MVVM framework is used; binding relies on standard WPF `INotifyPropertyChanged` and `DependencyObject`. The only external runtime dependencies are `System.Windows.Interactivity` (for `Behavior<T>`) and `ICSharpCode.AvalonEdit` (for `TextEditor`), while higher-level abstractions like `IMessageBoxProvider` keep this leaf decoupled from concrete message-box implementations.
@@ -0,0 +1,4 @@
- Public bindable properties are exposed as `static readonly DependencyProperty` registered with `FrameworkPropertyMetadata(..., FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)` plus a thin CLR wrapper that calls `GetValue`/`SetValue`.
- Attached behaviours extend third-party controls by subclassing `System.Windows.Interactivity.Behavior<T>` and wiring/unwiring events inside `OnAttached`/`OnDetaching` rather than subclassing the target control.
- View-models implement `INotifyPropertyChanged` through a shared `SetProperty(ref T obj, T value, [CallerMemberName])` helper that also calls `CommandManager.InvalidateRequerySuggested()` to refresh bound commands.
- XAML-only helpers live as paired `.xaml` + `.xaml.cs` files where the code-behind contains only the `InitializeComponent` constructor and property wrappers, keeping logic in attached behaviours or converters.
@@ -0,0 +1 @@
Reusable WPF presentation primitives — custom controls, attached behaviours, markup extensions, value converters, and lightweight view-model helpers used by ClassicAssist's XAML views.
@@ -0,0 +1 @@
WPF with `System.Windows.Interactivity` for attached behaviours, `ICSharpCode.AvalonEdit` for code editing, and `System.Windows.Forms.MessageBox` (via `WPFMessageBoxProvider`) as the concrete message-box implementation.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_wpf
title: Classic Assist WPF Application Host
scope:
- ClassicAssist/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
The `Engine.Install` entry point is the single cross-child wiring point: it loads `ClassicAssist.Shared.dll`, registers a `WPFDispatcher`/`WPFUIInvoker` on `SEngine`, installs `WPFMessageBoxProvider`, subscribes to `Options.LoadEvent`/`SaveEvent` to persist every `BaseViewModel` implementing `ISettingProvider`, then creates `MainWindow` on an STA thread. `WPFUIInvoker` exposes `IUIInvoker.Invoke`/`InvokeDialog` so the shared engine can open any `Window` subclass by name with optional DataContext — this is how hotkey commands in `Data/Hotkeys/Commands` (e.g. `ShowChatWindowCommand`, `ShowGIFCaptureWindow`) and other engine code trigger UI across the `main_ui`, `agents`, and `browser` view modules. All XAML resources are merged centrally via `Resources/ResourceDictionary.xaml` (dark theme, icons, colours), consumed uniformly by every child's Views. The project is split into presentation concerns (`Browser`, `UI/*`, `Agents`, `Main Window`) plus thin data helpers (`Data/Hotkeys`, `Data/Macros`, `Data/Skills`) that depend only on the shared engine abstractions rather than each other.
@@ -0,0 +1 @@
WPF application host that boots the shared engine, wires STA-threaded UI primitives, and composes the main window shell from tabbed agent, macro, skills, and debug views.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: classic_assist_avalonia
title: ClassicAssist Avalonia Desktop UI
scope:
- ClassicAssist.Avalonia/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1 @@
Entry point `Program.Main` builds an `AppBuilder` configured with `UsePlatformDetect`, `LogToTrace`, and `UseReactiveUI`; `App.OnFrameworkInitializationCompleted` creates the root `MainWindow` with a `MainWindowViewModel` and wires it into `Assistant.Engine.MainWindow`. The module is loaded as a plugin by the core engine: `Engine.Install` (in the `Assistant` namespace) loads `ClassicAssist.Shared.dll` via `Assembly.LoadFile`, subscribes to `Options.SaveEvent`/`LoadEvent` to serialize/deserialize every `BaseViewModel` implementing `ISettingProvider`, then calls `LoadUI`. `LoadUI` branches on `SEngine.GetPlatformType()`: on Unix it starts the Avalonia app on a background thread; otherwise it calls `SetupWithoutStarting()` so the core can set `SEngine.Dispatcher = new AvaloniaDispatcher(Dispatcher.UIThread)` and `SEngine.UIInvoker = new AvaloniaUIInvoker(...)` before showing windows. `AvaloniaUIInvoker` implements `IUIInvoker` using reflection to construct `Window` subclasses by name, exposing `Invoke`, `InvokeDialog<T>`, `GetHueAsync`, and clipboard access through `IClipboard`. Views live under `Views/` (root windows plus `Agents/` tabbed sub-views), each paired with a `.axaml.cs` code-behind; reusable widgets are in `Controls/` (e.g. `GroupBox` extending `HeaderedContentControl` with a XAML theme); shared helpers (`Misc/`) provide behaviors, value converters, and the `AvaloniaMessageBoxProvider`. Dependencies flow one-way: this module depends on `ClassicAssist.Shared`, `ClassicAssist.Data`, `ClassicAssist.Misc`, and `ClassicAssist.UI.ViewModels`; the core never references Avalonia types directly — it only sees the `IDispatcher`/`IUIInvoker`/`IMessageBoxProvider` abstractions.
@@ -0,0 +1,4 @@
- Each view/window follows the `.axaml` + `.axaml.cs` split where the code-behind constructor simply calls `InitializeComponent()` and leaves logic to the ViewModel.
- Cross-thread UI marshalling goes exclusively through `SEngine.Dispatcher.Invoke` / `InvokeAsync` rather than direct `Dispatcher` usage in business code.
- User-facing dialogs are surfaced to the core via `IUIInvoker.Invoke`/`InvokeDialog<T>` by type-name reflection instead of direct window construction from non-UI assemblies.
- Persistent settings are implemented by having ViewModels implement `ISettingProvider` and registering handlers on `Options.SaveEvent`/`LoadEvent` to iterate `BaseViewModel.Instances`.
@@ -0,0 +1 @@
Avalonia-based desktop shell that hosts ClassicAssist's ReactiveUI MVVM views, custom controls, and cross-platform windowing/clipboard integration.
@@ -0,0 +1 @@
Avalonia UI with ReactiveUI for MVVM binding; embedded AvaloniaEdit binaries (`AvaloniaEdit.dll/.pdb`) ship with the package; Linux uses a dedicated background thread for the Avalonia loop while Windows initializes inline via `SetupWithoutStarting()`.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: launcher
title: ClassicAssist Launcher (WPF)
scope:
- ClassicAssist.Launcher/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,7 @@
- Single WPF WinExe project (`net48`, `UseWPF=true`) whose entry point is `App.xaml.cs`; it bootstraps Exceptionless telemetry before showing the main window.
- MVVM layout: `BaseViewModel` provides `INotifyPropertyChanged` plus two in-process `ICommand` implementations (`RelayCommand`, `RelayAsyncCommand`). Each screen has a XAML view + code-behind window paired with a ViewModel (`MainViewModel`, `OptionsViewModel`, `ShardsViewModel`).
- `MainViewModel` owns the application lifecycle: reads/writes `Launcher.json` via Newtonsoft.Json, manages `ClientPaths`/`DataPaths`/`Plugins` collections, resolves shard hostnames through `Utility.ResolveAddress`, and finally `Process.Start`s `ClassicUO.exe` with `-plugins`, `-ip`, `-port`, `-uopath`, `-encryption`, `-shard` arguments. On exit it persists state back to JSON.
- Shard configuration is split between built-in presets in the singleton `ShardManager` (double-checked-lock single instance) and user-defined entries persisted in `Launcher.json`; `ShardsWindow` / `ShardsViewModel` edit them.
- ClassicUO launch flags are driven by reflection over `ClassicOptions`: each property is annotated with `ClassicOptionAttribute` (argument name, default value, conditional include rules), which both `ReadClassicOptions` (load from JSON) and `BuildClassicOptions` (emit CLI args) consume.
- UI helpers live in `Behaviours/` (WPF Interactivity behaviours), `Controls/EditTextBlock` (custom control), and `ValueConverters/` (boolean/cell-width converters). Resources are localized via `.resx` files under `Properties/`.
- Dependency direction: Views → ViewModels → `ShardManager`/`Utility`/`ClassicOptions`; no cross-viewmodel references except when one window passes its ViewModel into another (e.g. `ShowOptionsWindow` constructs `OptionsViewModel` with shared `Plugins`/`ClassicOptions`).
@@ -0,0 +1,5 @@
- ViewModels inherit `BaseViewModel` and expose properties via `SetProperty(ref _field, value)` so that `CommandManager.InvalidateRequerySuggested` is automatically called on change.
- User actions are bound to `ICommand` properties implemented as lazy-initialized `RelayCommand`/`RelayCommandAsync` fields, with optional `canExecute` predicates passed at construction.
- ClassicUO command-line flags are declared declaratively on `ClassicOptions` properties using `[ClassicOption(...)]` attributes rather than hard-coded string literals, and consumed uniformly via reflection in `ReadClassicOptions`/`BuildClassicOptions`.
- Per-user configuration is serialized/deserialized manually against `Newtonsoft.Json.Linq.JObject`/`JArray` instead of using POCO serialization, with explicit field-by-field read/write paths in `MainViewModel.Closing` and the constructor.
- Singleton services (e.g. `ShardManager`) use double-checked locking with a static `_lock` object exposed through a `GetInstance()` factory method.
@@ -0,0 +1 @@
WPF launcher that configures and starts ClassicUO clients with per-shard settings, plugin loading, and an options dialog for ClassicUO command-line flags.
@@ -0,0 +1 @@
.NET Framework 4.8 WPF; Newtonsoft.Json for JSON persistence; Trinet.Core.IO.Ntfs for NTFS alternate data stream cleanup; System.Windows.Interactivity.WPF for XAML behaviours; Exceptionless for crash reporting.
@@ -0,0 +1,8 @@
schema_version: 1
module_path: shared_core/data_subsystems
title: Shared Game Data & Assistant Subsystems
scope:
- ClassicAssist.Shared/Data/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,9 @@
Organized as a flat `ClassicAssist.Shared/Data` tree where each feature area is its own sub-namespace (Hotkeys, Macros, Filters, Autoloot, Dress, Organizer, Scavenger, Skills, Spells, Abilities, Targeting, Chat, Vendors, Regions, BuffIcons, Counters, SpecialMoves, Commands, Friends) plus shared root types (`Options`, `AssistantOptions`, `JournalEntry`, `Language`, `Extension`, `Crypter`).
- Each subsystem follows a Manager + Entry pattern: a singleton `*Manager` class exposes an `ObservableCollectionEx<T>` of typed entries (e.g. `HotkeyManager.Items`, `MacroManager.Items`, `AutolootManager`, `DressManager`, `ScavengerManager`, `SkillManager`, `SpellManager`, `AbilitiesManager`, `BuffIconManager`, `CountersManager`, `VendorBuyAgentEntry`/`VendorSellAgentEntry`) and owns lifecycle / persistence.
- Hotkeys and Macros are the two execution engines: `HotkeyManager` reads SDL2 via P/Invoke to intercept keyboard/mouse events and dispatches `HotkeyEntry.Action`; `MacroManager` uses reflection to discover all `IMacroCommandParser` implementations at construction time, subscribes to `Engine.PacketReceivedEvent`/`PacketSentEvent` for macro recording, and runs `MacroEntry` instances on dedicated threads via `MacroInvoker`.
- Macro commands live in `Macros/Commands/` and are discovered by name; help text and categories come from resource strings consumed by `CommandsDisplayAttribute`.
- Filter pipeline is driven by the `IConfigurableFilter` interface with `Serialize`/`Deserialize`/`Configure`/`ResetOptions`, implemented by `ClilocFilter`, `LightLevelFilter`, `SeasonFilter`, `WeatherFilter`, `BardsMusicFilter`, `RepeatedMessagesFilter`, and `DynamicFilterEntry`.
- Static game data tables are embedded JSON files at the root (`Bodies.json`, `Spells.json`, `Weapons.json`, `Properties.json`, `Regions.json`, `MountID.json`, `Masteries.json`, `BuffIcons.json`) consumed by their respective managers.
- Profile persistence is centralized in `Options.cs`: `Options.Load`/`Save` emit `LoadEvent`/`SaveEvent` that individual subsystems subscribe to, and profiles are stored under a `Profiles/` directory next to the executable.
- Cross-cutting helpers: `Crypter` provides machine-name XOR encryption used by some persisted entries; `Extension` holds shared LINQ/string helpers.
@@ -0,0 +1,6 @@
- Each subsystem exposes a singleton manager accessed through a static `GetInstance()` method with double-checked locking, holding an `ObservableCollectionEx<T>` of entry objects.
- UI-bindable properties use a private backing field plus a `SetProperty(ref _field, value)` helper that calls `OnPropertyChanged`, decorated with `[NotifyPropertyChangedInvocator]` from `ClassicAssist.Annotations`.
- Persisted configuration entries implement a `Serialize()` returning a `JObject` and a `Deserialize(JToken)` constructor or method so they round-trip through `Newtonsoft.Json.Linq`.
- Pluggable command/filter types are discovered at runtime via reflection over `Assembly.GetExecutingAssembly().GetTypes()` filtered by a marker interface (`IMacroCommandParser`, `IConfigurableFilter`).
- Macro command metadata is attached with attributes (`CommandsDisplayAttribute`, `CommandsDisplayStringSeeAlsoAttribute`) whose values are resolved from localized resource strings rather than hard-coded.
- Cross-subsystem coordination goes through static events on `Options` (`LoadEvent`, `SaveEvent`) and `Engine` packet hooks (`PacketReceivedEvent`, `PacketSentEvent`) instead of direct coupling between managers.
@@ -0,0 +1 @@
Shared data layer providing hotkeys, macros, filters, autoloot, dress/organizer/scavenger agents, skills/spells/abilities, targeting, chat, vendors, and profile-based settings for the ClassicAssist client.
@@ -0,0 +1 @@
C# with Newtonsoft.Json (`JObject`/`JToken`) for profile and filter serialization; SDL2 via `[DllImport("SDL2.dll")]` for low-level key state polling; `System.ComponentModel.INotifyPropertyChanged` + `CallerMemberName` for UI-bound options; resource-driven localization via `ResourceManager` for macro command help.
@@ -0,0 +1,10 @@
schema_version: 1
module_path: shared_core/misc_helpers
title: Shared Primitives, Threading & Cross-Platform Utilities
scope:
- ClassicAssist.Shared/Misc/
- ClassicAssist.Shared/Helpers/
- ClassicAssist.Shared/Engine/
source_files: []
depends_on: []
related_to: []
@@ -0,0 +1,7 @@
Three sibling folders under ClassicAssist.Shared expose a flat set of reusable types consumed by both WPF and Avalonia UI projects:
- `Misc/` — the largest folder; holds cross-cutting primitives: key-code enums (`Keys`, `SDLKeys`) with an SDL↔WPF mapping table, display/formatting helpers (`DisplayFormatAttribute`, `HexFormatProvider`, `PropertyArrayFormatProvider`), `EntityComparers` implementing `IComparer<Entity>` for UO objects, threading primitives (`CircularBuffer<T>`, `ThreadQueue<T>`, `ThreadPriorityQueue<T>` backed by `Priority_Queue.SimplePriorityQueue`), interop (`NativeMethods.GetWindowRect`, `ExtensionMethods.DeleteObject` via gdi32/user32), and small utilities (`Utility`, `OffsetStopwatch`).
- `Helpers/Reflection.cs` — thin static helper that reads properties from types discovered through `Engine.ClassicAssembly`, used to resolve plugin/engine types at runtime.
- `Engine/` — partial-class extensions on the host `ClassicAssist.Shared.Engine` type: `Filters.cs` adds send/receive `PacketFilterInfo` registration methods (pre/post/incoming) against internal filter lists, and `PlatformDetector.cs` lazily detects Windows/Linux/macOS via `RuntimeInformation` and exposes it as `PlatformType`.
Dependency direction is one-way outward: this module depends on `ClassicAssist.Data`, `ClassicAssist.UO.Objects`, `ClassicAssist.UO.Network.PacketFilter`, and `ClassicAssist.Shared` (for `Engine` / `ClassicAssembly`), but nothing in those packages depends back on Misc/Helpers/Engine. The two provider interfaces (`ISettingProvider`, `IMessageBoxProvider`) plus their shared DTOs (`SharedWindow`, `MessageBoxResult`, etc.) are the only abstraction boundaries meant to be implemented by the WPF/Avalonia host projects.
@@ -0,0 +1,5 @@
- Cross-platform abstractions are exposed as interfaces (`ISettingProvider`, `IMessageBoxProvider`) with plain DTO structs/enums (`SharedWindow`, `MessageBoxResult`, `MessageBoxImage`, `MessageBoxButtons`) so WPF and Avalonia hosts can provide implementations without sharing UI code.
- Key input is modeled as parallel enum sets (`Keys`, `SDLKeys.SDL_Keycode`, `SDL_Scancode`) bridged by a static `INTERNAL_keyMap` dictionary and helper methods like `SDLKeyToKeys` rather than direct casting.
- Background work is dispatched through disposable queue wrappers (`ThreadQueue<T>`, `ThreadPriorityQueue<T>`) that own a background `Thread`, signal it via an `AutoResetEvent`, and terminate by enqueuing a sentinel `default` item before joining.
- P/Invoke and native resource handling are isolated in dedicated files (`NativeMethods.cs`, `ExtensionMethods.DeleteObject`) using `[DllImport]` + `[StructLayout(LayoutKind.Sequential)]` instead of scattered inline calls.
- Engine extension points are added as `partial class Engine` members in separate files (`Engine/Filters.cs`, `Engine/PlatformDetector.cs`) rather than modifying the core engine source.
@@ -0,0 +1 @@
Cross-cutting primitives (key codes, display formatting, entity comparers), thread-safe queues and buffers, P/Invoke helpers, reflection utilities, and shared Engine extension points for packet filters and platform detection.

Some files were not shown because too many files have changed in this diff Show More