Skip to content

01.01.99. Web Plug‐Ins ‐ SDK

Arley Pádua edited this page Sep 3, 2024 · 10 revisions

What is the plug-in SDK

Plug-in SDK is a package that you can use to develop your own plug-in for PKHeX.Web

Pre-requisites

Getting started

  1. Create a .NET project
# create a folder for your plugin
mkdir PlugIn.Demo

# change the directory
cd PlugIn.Demo

# create a razor class lib 
# (can also be a classlib if your plug-in doesn't need to render UI components)
dotnet new razorclasslib

# open VS Code
code .
  1. Configure your project file

Edit your project file (./PlugIn.Demo.csproj in this demo) adding the following lines:

<Project Sdk="Microsoft.NET.Sdk.Razor">

  <PropertyGroup>

    <!--add the following lines before the closing of /PropertyGroup-->
    <DebugType>portable</DebugType>
    <EmbedAllSources>true</EmbedAllSources>
    <PublishSingleFile>true</PublishSingleFile>
    <Version>1.0.0</Version>
    <!--until here-->
  </PropertyGroup>

</Project>

The rest you can leave as it is

  1. Add the SDK to your project

You can see the latest version here.

dotnet add package PKHeX.Web.Plugins

If all is good, you should a log stream that resembles the following:

  Determining projects to restore...
info : Adding PackageReference for package 'PKHeX.Web.Plugins' into project '...'.
info :   GET https://api.nuget.org/v3/registration5-gz-semver2/pkhex.web.plugins/index.json

... sequence omitted

info : PackageReference for package 'PKHeX.Web.Plugins' version '0.6.2' added to file './PlugIn.Demo.csproj'.
info : Writing assets file to disk. Path: /Users/Arley_SilvaDePadua/repos/personal/plugin-demo/obj/project.assets.json
log  : Restored /Users/Arley_SilvaDePadua/repos/personal/PlugIn.Demo/PlugIn.Demo.csproj (in 1.74 sec).
  1. Add your plug-in manifest

Add a new class having the name you like. I usually have the name of the plug-in, in this case: PlugInDemo.cs and make it implement the abstract class Settings like the following:

public class PlugInDemo : Settings
{
    public PlugInDemo() : base(Manifest)
    {
       // optionally define settings that may be useful to allow user to control
       // this["Some boolean setting"] = new SettingValue.BooleanValue(true);
       // this["Some integer setting"] = new SettingValue.IntegerValue(1);
       // this["Some string setting"] = new SettingValue.StringValue("a value");
       // this["Some file setting"] = new SettingValue.FileValue([], string.Empty);

       // optionally define the features you want to leave enabled by default
       // if none is set, everything will be disabled by default
       // EnabledByDefault<DemoAction>();
    }

    private static readonly PlugInManifest Manifest = new PlugInManifest(
        "Demo Plug-In",
        "Description of the plug-in");
}

This is the main class that you can use to specify the setting values and default enabled features. For more information about settings, click here.

  1. Add a feature onto your project

You can hook your plug-in up onto several actions within PKHeX.Web.

For the purposes of this example, we will add a button that will be visible when editing a Pokemon, by using the IPokemonEditAction hook:

public class DemoAction : IPokemonEditAction
{
    public string Label => "Demo Action";

    public string Description => "Demo action available when editing a pokemon";

    public Task<Outcome> OnActionRequested(Pokemon pokemon)
    {
        return Outcome.Notify(message: "Action executed",
            description: $"Action executed for pokemon: {pokemon.Species.Name}",
            type: Outcome.Notification.NotificationType.Info).Completed();
    }
}

This will simply add a button with label: Demo Action when editing any Pokemon, and when clicking it will show a notification to the user with the content: Action executed for pokemon: Pikachu

Check here for more hook options

  1. Publishing the package

All the steps above should be enough to write your own plug-in and make it pluggable to PKHeX.Web

Right now there's no formal process for publishing it, if you want to make it available, open an issue in this repository mentioning:

  • The description of the plug-in and what it does
  • The code repository

SDK Reference

Settings

Any plug-in compatible with PKHeX.Web should have exactly one class implementing the Settings class, and that will be used to determine all metadata and settings of the plug-in.

Manifest

The Settings class requires an instance of a PlugInManifest to be constructed which consists of metadata about the project:

public class PlugInDemo : Settings
{
    public PlugInDemo() : base(Manifest)
    {
    }

    private static readonly PlugInManifest Manifest = new PlugInManifest(
        PlugInName: "Demo Plug-In",
        Description: "Description of the plug-in", // optional
        ProjectUrl: "https://my-plug-in.com", // optional
        Information: "Some disclaimer to be shown in the plug-in settings page"); // optional
}

Configuration values

You may want to allow the user to change a few configuration values of your plug-in, and that's possible through the constructor:

public class PlugInDemo : Settings
{
    public PlugInDemo() : base(Manifest)
    {
       this["Some boolean setting"] = new SettingValue.BooleanValue(true);
       this["Some integer setting"] = new SettingValue.IntegerValue(1);
       this["Some string setting"] = new SettingValue.StringValue("a value");
       this["Some file setting"] = new SettingValue.FileValue([], string.Empty);
    }
}

Right now the only supported setting value types are: BooleanValue, IntegerValue, StringValue and FileValue

Persistence

All setting values are persisted in the local storage, with the exception of FileValue due to sizing limits its reference is still stored in the local storage, but the actual contents is stored in the IndexDb

Toggling hooks

All plug-in hooks defined in your project are disabled by default. If you want to have an action that's enabled by default, you have to intentionally specify that in the constructor:

public class PlugInDemo : Settings
{
    public PlugInDemo() : base(Manifest)
    {
       EnabledByDefault<MaxRareCandiesButton>();
    }
}

Plug-In Hooks

Plug-In hooks are the actual point of extension that's pluggable into PKHeX.Web, and there are several types that will help you extending the functionality that applies for your use case

Hooks

IRunOnPokemonChange

A hook that happens every time an attribute of a Pokemon is changed

IRunOnPokemonSave

A hook that happens every time one saves a Pokemon

IRunOnItemChanged

A hook that happens every time an item is added/removed/updated from your bag

IPokemonEditAction

Adds a button to the page when editing a Pokemon

IPokemonStatsEditAction

Adds a button to the 'Stats' tab when editing a Pokemon

IQuickAction

Adds a button to the home page of PKHeX.Web when a save is loaded

Outcomes

All plug-in actions should return an Outcome out of an action varying from several types:

Outcome.Void

Simply no outcome, it will just return the execution

public Task<Outcome> OnActionRequested(Pokemon pokemon) => Outcome.Void.Completed();

Outcome.Notification

Shows a pop-up notification when the action is finished:

public Task<Outcome> OnActionRequested(Pokemon pokemon) => Outcome.Notify(
  message: "Applied legality",
  description: $"{pokemon.Species.Name} made legal"
  type: Outcome.Notification.NotificationType.Info).Completed();

Outcome.PlugInPage

Allows to forward to a custom page defined in your project:

public Task<Outcome> OnActionRequested(Pokemon pokemon) => Outcome.Page<MyCustomPage>(
  path: "my-custom-page", 
  layout: Standard // optional parameter, options are: Standard or Empty
).Completed();

the example above forwards the user to the page under the path /my-custom-page and will use MyCustomPage as the content:

// MyCustomPage.razor

@inherits PkHexWebPlugInComponent

<div>This is a custom plug-in page</div>

Dependency-Injection

All plug-in hooks have their own sand-box dependency injection container, and are allowed to inject:

  • IGameProvider - allows to inspect currently aspect of the loaded game
  • Your plug-in settings class

Example:

public class MyQuickAction(
   IGameProvider gameProvider,
   PlugInDemo settings) : IQuickAction
{
    public string Label => "Demo action";
    public string Description => "Adds a button on the home page";
    public IDisable.DisableInfo DisabledInfo => IDisable.Enabled;
    
    public Task<Outcome> OnActionRequested()
    {
        if (gameProvider.IsLoaded && settings.GetBoolean("Can execute my action")) 
            return Outcome.Notify($"Executed for {gameProvider.LoadedGame.SaveVersion.Name}").Completed();

        return Outcome.Void.Completed();
    }
}