-
Notifications
You must be signed in to change notification settings - Fork 2
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.
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
Expand the Menu section and click on the Plugins subsection
- Open the Plugin Management View
- Switch the toggle of the desired plugin
- Save the changes
- Restart Daybreak
Each entry in the Plugin Management View shows details about the detected plugin.
- The first line shows the name of the plugin
- The second line shows the full path of the plugin
- The ToggleSwitch shows the state of the plugin (enabled/disabled)
- The open file button opens the File Explorer at the path of the 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
Daybreak is written in WPF with some extensions to introduce a Dependency Injection engine and application lifetime hooks.
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:
- Clone the Daybreak repository (https://github.com/AlexMacocian/Daybreak.git)
- Create a new .net (preferably core or .net5+) library in Visual Studio anywhere on your PC
- Import Daybreak.csproj into your new Solution
- 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)
If successful, in Visual Studio, you should see Daybreak show up in the Dependencies tab
- Create a new class and inherit from PluginConfigurationBase (from Daybreak.Models.Plugins namespace)
public sealed class PluginConfiguration : PluginConfigurationBase
{
}
- Build the project
- Go to the output directory (usually it is in [Project path]/bin/[Debug|Release]/[Chosen .net version]/
- Copy your .dll(s) into Daybreak/Plugins/[Your plugin name]/
- Launch Daybreak
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.
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
-
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. -
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. -
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 callingviewManager.ShowView<TRegisteredViewType>()
-
RegisterStartupActions
provides a IStartupActionsProducer parameter. Here, the plugin can register a StartupActionBase that gets executed by Daybreak on startup. -
RegisterPostUpdateActions
provides a IPostUpdateActionsProducer parameter. Here, the plugin can register a PostUpdateActionBase that gets executed by Daybreak after an update. -
RegisterDrawingModules
provides a IDrawingModuleProducer parameter. Here, the plugin can register a DrawingModuleBase. The drawing module is used when drawing the minimap in FocusView. -
RegisterOptions
provides an IOptionsProducer parameter. Here, the plugin can register options models that can later be retrieved using DI by requestingIOptions<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. -
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. -
RegisterMods
provides a IModsManager parameter. Here, the plugin can register a IModService. -
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. -
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.
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.
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>
.
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.
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.
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.
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.
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.
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.
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.