Skip to content

Plugins

Macocian Alexandru Victor edited this page Apr 27, 2024 · 3 revisions

Daybreak Plugins

This page will contain a breakdown of what are Daybreak plugins, how to enable/disable plugins in Daybreak as well as to how to write a custom plugin.

Introduction

Daybreak plugins are executables that are loaded into the AppDomain of the running Daybreak launcher. They can contain any type of C# code as well as can import their own dependencies and libraries. Ultimately they can do anything that a C# dotnet application can do.

Note - Plugin dependency support is still pretty spotty. Future releases will eventually solve the plugin dependency importing

Plugins in Daybreak

Open Plugin Management View

Expand the Menu section and click on the Plugins subsection

image

Enable and disable a plugin

  1. Open the Plugin Management View
  2. Switch the toggle of the desired plugin

image

  1. Save the changes

image

  1. Restart Daybreak

image

Plugin details

Each entry in the Plugin Management View shows details about the detected plugin.

  1. The first line shows the name of the plugin
  2. The second line shows the full path of the plugin
  3. The ToggleSwitch shows the state of the plugin (enabled/disabled)

image

  1. The open file button opens the File Explorer at the path of the plugin

image

Coding your own plugin

This section contains details about the functionality of the Daybreak code and expects the reader to have some basic understanding of programming, OOP principles, dependency injection, inheritance as well as some basic level of C# and .net knowledge

Framework basics

Daybreak is written in WPF with some extensions to introduce a Dependency Injection engine and application lifetime hooks.

Quickstart Guide

As an example, check out SimplePlugin

The following guide is written for Visual Studio 2022. You can use any other text editor/IDE, but your steps will differ slightly

To create a new plugin:

  1. Clone the Daybreak repository (https://github.com/AlexMacocian/Daybreak.git)
  2. Create a new .net (preferably core or .net5+) library in Visual Studio anywhere on your PC
  3. Import Daybreak.csproj into your new Solution
  4. In your new .csproj, add a Project Reference to the Daybreak.csproj (Note: Don't copy the exact path you see in this picture. Modify it to match the path from your .csproj to Daybreak.csproj)

image

If successful, in Visual Studio, you should see Daybreak show up in the Dependencies tab

image

  1. Create a new class and inherit from PluginConfigurationBase (from Daybreak.Models.Plugins namespace)
public sealed class PluginConfiguration : PluginConfigurationBase
{
}
  1. Build the project
  2. Go to the output directory (usually it is in [Project path]/bin/[Debug|Release]/[Chosen .net version]/
  3. Copy your .dll(s) into Daybreak/Plugins/[Your plugin name]/
  4. Launch Daybreak

Configuring a plugin / Building the PluginConfigurationBase class

Plugins get access to almost the same setup as the main Daybreak binary. Since this code is still in alpha, please check PluginConfigurationBase.cs for the latest functionality.

Configuration overrides

Plugins get to pick which configuration steps to override. As of writing this document, the following overrides are exposed

  • RegisterResolvers
  • RegisterServices
  • RegisterViews
  • RegisterStartupActions
  • RegisterPostUpdateActions
  • RegisterDrawingModules
  • RegisterOptions
  • RegisterNotificationHandlers
  • RegisterMods
  • RegisterBrowserExtensions
  • RegisterLaunchArgumentHandlers

Plugins also get to use the following methods

  • RegisterLiteCollection
  • SetupLoggingAndMetrics
  • SetupDaybreakUserAgent
  • SetupChromeImpersonationUserAgent

Configuration overrides cont.

  1. RegisterResolvers provides a IServiceManager parameter. In this override, a plugin can do advanced operations such as setting up dependency resolvers (used in DI). Please check the ProjectConfiguration.cs for an example as to what you can do.
  2. RegisterServices provides a IServiceCollection parameter. This is the standard service collection used by most .net projects that implement DI. Here, a plugin can register its services and setup lifetimes for the services.
  3. RegisterViews provides a IViewProducer parameter. Here a plugin can register their own views (as a UserControl). Using IViewManager, the plugin can then later navigate to those views by calling viewManager.ShowView<TRegisteredViewType>()
  4. RegisterStartupActions provides a IStartupActionsProducer parameter. Here, the plugin can register a StartupActionBase that gets executed by Daybreak on startup.
  5. RegisterPostUpdateActions provides a IPostUpdateActionsProducer parameter. Here, the plugin can register a PostUpdateActionBase that gets executed by Daybreak after an update.
  6. RegisterDrawingModules provides a IDrawingModuleProducer parameter. Here, the plugin can register a DrawingModuleBase. The drawing module is used when drawing the minimap in FocusView.
  7. RegisterOptions provides an IOptionsProducer parameter. Here, the plugin can register options models that can later be retrieved using DI by requesting IOptions<TOption>, ILiveOptions<TOption>, IUpdateableOptions<TOption>, ILiveUpdateableOptions<TOption>. IOptions is a readonly value of the option that gets initialized once, meaning that if the options change during runtime, the value will not change unless requested again through DI. ILiveOptions requests the options value every time it is used in code, meaning that if the options change, the code will use the latest option. IUpdateableOptions provides a way for your code to update the options, but it won't always retrieve the latest value of the options. ILiveUpdateableOptions allows to update the options and will also always retrieve the latest value of the options.
  8. RegisterNotificationHandlers provides a INotificationHandlerProducer parameter. Here, the plugin can register a INotificationHandler. This handler will get called when a notification associated with this handler is being opened. Check INotificationService.NotifyInformation/NotifyError for details.
  9. RegisterMods provides a IModsManager parameter. Here, the plugin can register a IModService.
  10. RegisterBrowserExtensions provides a IBrowserExtensionsProducer. Here, the plugin can register a IBrowserExtension. Browser extensions allow the mod to set up Chrome extensions for the embedded browser.
  11. RegisterLaunchArgumentHandlers provides a IArgumentHandlerProducer. Here, the plugin can register a IArgumentHandler. Argument handlers specify an identifier and expected count of arguments. On startup, if provided arguments match the identifier and expected count, the handler will be called to process the command line arguments.

LiteCollections

Daybreak uses a LiteDB database for permanent storage. As an example, check out PriceHistoryDatabase.

To set up a lite collection for DI usage, use ProjectConfigurationBase.RegisterLiteCollection method to configure a ILiteCollection<TCollectionType>.

As type arguments, RegisterLiteCollection expects a TCollectionType that is the type of your model and a TOptionsType which is a class of type ICollectionOptions<TCollectionType>. Check out NotificationStorageOptions for an example of how the implemetnation of TOptionsType should look like.

RegisterLiteCollection will instruct the DI engine to build an ILiteCollection<TCollectionType> with a collection name specified in the TOptionsType class implementation.

Http Calls

It is strongly advised that you use the in-built http client implementation as it comes with hooks into the metrics and logging system. As such, once you follow the steps below for configuring and using the client, you'll get metrics on the average latency of your request in the Menu > Diagnostics > Metrics view as well as detailed logging of the request and responses in the Menu > Diagnostics > Logs view.

For an example, check out ProjectConfiguration.RegisterResolvers for the DI configuration, and BloogumClient for the actual usage of the HttpClient.

Daybreak provides a built-in way of handling http calls. In the RegisterResolvers override, setup your desired http client with

serviceManager
    .RegisterHttpClient<TService>()
        .WithMessageHandler(this.SetupLoggingAndMetrics<TService>)
        .WithDefaultRequestHeadersSetup(this.SetupDaybreakUserAgent)
        .Build()

Now that the DI is configured, in your service TService, simply take a dependency on IHttpClient<TService>.

Logging

By default, Daybreak comes with a ILogger<T> resolver implemented directly into the DI engine.

For an example on how to perform logging, check InternetCheckingService and the usage of ILogger<InternetCheckingService>.

To log messages, take a dependency on ILogger<TService> and simply use it to log information.

Your logs will be visible in Menu > Diagnostics > Logs view. You can also use Ctrl + F to search for key words through the logs.

Metrics

Currently, only metrics of type double are supported. Also, Daybreak doesn't currently store metrics across sessions. Once you close the application, your metrics will be lost

Daybreak exposes an IMetricsService through the DI engine.

Simply take a dependency on IMetricsService. Then, create your histogram by calling metricsService.CreateHistogram<double>. For an example, check out InternetCheckingService

To record metrics, call myHistogram.Record(value).

You can view your metrics in the Menu > Diagnostics > Metrics.

Notifications

Daybreak provides a service for emitting notifications to the user.

Currently, notifications have two levels, Information and Error.

To emit notifications, take a dependency on INotificationService and call notificationService.NotifyInformation or notificationService.NotifyError.

Notifications support custom handlers that get triggered when the user opens the notification. For an example, check out the TradeMessageNotificationHandler.

To link notifications to your notification handler, implement a TNotificationHandler of type INotificationHandler, and call notificationService.NotifyInformation<TNotificationHandler>. Daybreak will link your handler to your notification, so when the user opens your notification, it triggers your handler.

Image Cache

To improve performance, Daybreak implements an IImageCache that is configurable through the options. Simply take a dependency on IImageCache and call imageCache.GetImage(uri) to get an ImageSource of your image. Then you can use that to display your image in your views.

Guild Wars Integration

Daybreak integrates with Guild Wars through a memory scanner that scans your currently active Guild Wars instance. Check out Focus View for some examples of the capabilities of the scanner.

To use the scanner into your plugin, take a dependency on IGuildwarsMemoryCache. The refresh rate of the cache can be configured in the Menu > Settings > Memory Reader.

View Navigation

Views are controls of type UserControl.

Daybreak provides a centralized way of navigating through views by using IViewManager.

First, register your views in RegisterViews override.

Then, take a dependency on IViewManager and use viewManager.ShowView<TView>() to navigate to your view. If you want to pass some data to your view, call viewManager.ShowView<TView>(dataToPass). This data will be in the DataContext of your view.

Privilege Mode

If your plugin requires Daybreak to run in privileged mode (eg. Administrator rights), take a dependency on IPrivilegeManager.

Determine if Daybreak is already running in Privileged Mode by using privilegeManager.AdminPrivileges.

If you want to escalate Daybreak to administrator, call privilegeManager.RequestAdminPrivileges<TCancelView>(messageToUser, dataContextOfCancelView). In case the user declines the escalation, Daybreak will redirect the user to the TCancelView view. If you don't know what view to use, simply use LauncherView as a cancel view as this is the landing page of Daybreak.