Skip to content

API and customizations

Emanuele Manzione edited this page Jul 15, 2023 · 24 revisions

Use a different downloader

PATCH can use different downloaders. If you own the Basic version, you have access to the default FileDownloader. If you own the Plus version of PATCH, you will find additional downloaders: SmartDownloader, ChunkedDownloader and DropboxDownloader. You can switch between downloaders just by injecting it into the UpdatingContext, based on your needs.

FileDownloader

It is the default downloader for PATCH, the only one included in the Basic version. It is a sequential, single-threaded downloader. It downloads files one after another and follows a retry-based strategy if a download fails.

It does not need to be injected, it is used by default.

SmartDownloader (Plus only)

It is a high performance downloader that targets on downloading a lot of files in a reduced amount of time. Very useful for the first time downloading, when the whole application needs to be fully downloaded.

The downloading strategy it uses is the following:

  • files to download are ordered starting from the smallest one
  • X downloading threads are started (where X is your processors count - 1)
  • downloading threads use a work-stealing strategy to download files

This downloader also follows a retry-based strategy if a download fails.

You can inject it in this way:

_context = new UpdatingContext(settings, progress);
_context.Downloader = new SmartDownloader(_context);
_context.Downloader.DownloadComplete += DownloadComplete;

ChunkedDownloader (Plus only)

It is a high performance, chunk-based downloader that targets on downloading a lot of chunks in a reduced amount of time. Very useful for the first time downloading, when the whole application needs to be fully downloaded.

It uses the following downloading strategy:

  • files to download are ordered starting from the smallest one
  • X downloading threads are started (where X is by default 32, but you can customize that)
  • files to download are divided into chunks
  • downloading threads use a work-stealing strategy to download chunks

This downloader also follows a retry-based strategy if a download fails.

NOTE❗ 
Keep in mind that this downloader requires server-side support for the `HTTP Range` header (it performs partial downloads).

You can inject it in this way:

_context = new UpdatingContext(settings, progress);
_context.Downloader = new ChunkedDownloader(_context);
_context.Downloader.DownloadComplete += DownloadComplete;

DropboxDownloader (Plus only)

It uses the same strategy as the SmartDownloader, but for Dropbox.

You can inject it in this way:

_context = new UpdatingContext(settings, progress);
_context.Downloader = new DropboxDownloader(_context.FileSystem)
{
   Credentials = new NetworkCredential(string.Empty, "<your Dropbox Access Token>")
};
_updater.Downloader.DownloadComplete += DownloadComplete;

_networkChecker = new DropboxNetworkChecker()
{
   Credentials = _context.Downloader.Credentials
};

Note how both the Downloader and the NetworkChecker changed.

Use your own downloader

In both Basic and Plus versions you can also implement your own downloaders for your Launcher, if needed. Just implement IDownloader interface in your own class as following:

public sealed class MyDownloader : MHLab.Patch.Core.Client.IO.IDownloader
{
    // The code to implement IDownloader
}

then inject it into the Updater/Repairer/PatcherUpdater/UpdatingContext:

// This is taken from Launcher.cs, Initialize method.
_context= new UpdatingContext(settings, progress);
_context.Downloader = new MyDownloader();
_context.Downloader.DownloadComplete += DownloadComplete;

Also, note that some Downloaders must be paired with a custom NetworkChecker! Just implement INetworkChecker interface in your own class as following:

public sealed class MyNetworkChecker : MHLab.Patch.Core.Client.IO.INetworkChecker
{
    // The code to implement INetworkChecker
}

then use it into your Launcher!

Use your own logger

You can implement your own logger for your Launcher, if needed. Just implement ILogger interface in your own class as following:

public sealed class MyLogger : MHLab.Patch.Core.Loggers.ILogger
{
    // The code to implement ILogger
}

then inject it into the UpdatingContext:

// This is taken from Launcher.cs, Initialize method.
_context = new UpdatingContext(settings, progress);
_context.Logger = new MyLogger();

Checking if an update is available

Normally the Launcher sample script automatically starts the updating process, but for your convenience (and for customization purposes) some methods are exposed.

var repairer = new Repairer(_context);
repairer.IsRepairNeeded();
// It will return true if a repair is needed
var updater = new Updater(_context);
updater.IsUpdateAvailable();
// It will return true if a patch is available

Also, the UpdatingContext has the IsDirty property: you can check if it is set on true. If it is, a restart is recommended.

Warning

If you call those methods before the UpdatingContext's Initialize call, you will get an exception.

Inspecting UpdatingContext

You may be interested in knowing some information from the updating context, so you can show additional information or monitor what is happening under the hood.

_context.CurrentVersion; // It contains the current local version. Useful if you want to display the current version.
_context.PatchesPath; // It contains the shortest path for patches that needs to be applied to pull the latest version.
_context.BuildsIndex; // It contains the metadata for builds on your server.
_context.PatchesIndex; // It contains the metadata for patches on your server.
_context.ExistingFiles; // It contains the metadata of all local tracked files.
_context.CurrentBuildDefinition; // It contains the metadata of the current version.
_context.CurrentUpdaterDefinition; // It contains the metadata of the updater.

Warning

If you inspect those properties before the UpdatingContext's Initialize call, you will get wrong/empty data.

Update steps

You can queue your custom update steps in the PATCH's updating pipeline. The UpdatingContext exposes the RegisterUpdateStep method. It accepts an IUpdater (PatchUpdater, Repairer and Updater are all IUpdater and are all queued in the updating pipeline).

public sealed class MyUpdaterStep : IUpdater
{
   // The code to implement IUpdater
}

_context.RegisterUpdaterStep(new MyUpdaterStep());

You can find the sample usage in Launcher.cs and LauncherUpdater.cs, in Initialize method.

Remember that the order matters when you register an IUpdater: it is the same order they will execute. All steps are executed when you call _context.Update().

You can also register to the PerformedStep event on _context.Runner.PerformedStep. It is executed every time a step in the pipeline completes.

Settings Overriding customization

You can expand the settings you can override with the Settings Overriding functionality. You just need to create a class and inherit from SettingsOverride, then you can add your own properties. Then you just need to change the generic parameter passed to the UpdatingContext.OverrideSettings<TSettings>(Action<ILauncherSettings, TSettings> overrider) method in your Launcher initialization. An example will explain it better!

/* I create this new MySettingsOverride.cs */

public class MySettingsOverride : SettingsOverride
{
   public bool EnableMyCoolOption { get; set; }
}
/* MainWindow.xaml.cs@86 in Initialize() method.
If you are in Unity you can find it in Launcher.cs@19 and LauncherUpdater.cs@21 */

_context.OverrideSettings<MySettingsOverride>((originalSettings, settingsOverride) =>
{
   originalSettings.DebugMode = settingsOverride.DebugMode;
   originalSettings.PatcherUpdaterSafeMode = settingsOverride.PatcherUpdaterSafeMode;

   // Here you can access your EnableMyCoolOption property and do whatever you want with it.
   if (settingsOverride.EnableMyCoolOption)
   {
      // Do stuff...
   }
});