-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Plugins development lol US
ASF INCLUDEZ SUPPORT 4 CUSTOM PLUGINS DAT CAN BE LOADD DURIN RUNTIME. PLUGINS ALLOW U 2 CUSTOMIZE ASF BEHAVIOUR, 4 EXAMPLE BY ADDIN CUSTOM COMMANDZ, CUSTOM TRADIN LOGIC OR WHOLE INTEGRASHUN WIF THIRD-PARTY SERVICEZ AN APIS.
This page describes ASF plugins from developers perspective - creating, maintaining, publishing and likewise. If you want to view user's perspective, move here instead.
Plugins are standard .NET libraries that define a class inheriting from common IPlugin
interface declared in ASF. You can develop plugins entirely independently of mainline ASF and reuse them in current and future ASF versions, as long as internal ASF API remains compatible. Plugin system used in ASF is based on System.Composition
, formerly known as Managed Extensibility Framework which allows ASF to discover and load your libraries during runtime.
We've prepared ASF-PluginTemplate for you, which you can use as a base for your plugin project. Using the template is not a requirement (as you can do everything from scratch), but we heavily recommend to pick it up as it can drastically kickstart your development and cut on time required to get all things right. Simply check out the README of the template and it'll guide you further. Regardless, we'll cover the basics below in case you wanted to start from scratch, or get to understand better the concepts used in the plugin template - you don't typically need to do any of that if you've decided to use our plugin template.
Your project should be a standard .NET library targetting appropriate framework of your target ASF version, as specified in the compilation section.
The project must reference main ArchiSteamFarm
assembly, either its prebuilt ArchiSteamFarm.dll
library that you've downloaded as part of the release, or the source project (e.g. if you decided to add ASF tree as a submodule). This will allow you to access and discover ASF structures, methods and properties, especially core IPlugin
interface which you'll need to inherit from in the next step. The project must also reference System.Composition.AttributedModel
at the minimum, which allows you to [Export]
your IPlugin
for ASF to use. IN ADDISHUN 2 DAT, U CUD WANTS/NED 2 REFERENCE OTHR COMMON LIBRARIEZ IN ORDR 2 INTERPRET TEH DATA STRUCTUREZ DAT R GIVEN 2 U IN SUM INTERFACEZ, BUT UNLES U NED THEM EXPLICITLY, DAT WILL BE ENOUGH 4 NAO.
If you did everything properly, your csproj
will be similar to below:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="C:\\Path\To\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
<!-- If building with downloaded DLL binary, use this instead of <ProjectReference> above -->
<!-- <Reference Include="ArchiSteamFarm" HintPath="C:\\Path\To\Downloaded\ArchiSteamFarm.dll" /> -->
</ItemGroup>
</Project>
From the code side, your plugin class must inherit from IPlugin
interface (either explicitly, or implicitly by inheriting from more specialized interface, such as IASF
) and [Export(typeof(IPlugin))]
in order to be recognized by ASF during runtime. TEH MOST BARE EXAMPLE DAT ACHIEVEZ DAT WUD BE TEH FOLLOWIN:
using System;
using System.Composition;
using System.Threading.Tasks;
using ArchiSteamFarm;
using ArchiSteamFarm.Plugins;
namespace YourNamespace.YourPluginName;
[Export(typeof(IPlugin))]
public sealed class YourPluginName : IPlugin {
public string Name => nameof(YourPluginName);
public Version Version => typeof(YourPluginName).Assembly.GetName().Version;
public Task OnLoaded() {
ASF.ArchiLogger.LogGenericInfo("Meow");
return Task.CompletedTask;
}
}
IN ORDR 2 MAK USE OV UR PLUGIN, U MUST FIRSTLY COMPILE IT. U CAN DO DAT EITHR FRUM UR IDE, OR FRUM WITHIN TEH ROOT DIRECTORY OV UR PROJECT VIA COMMAND:
# IF UR PROJECT IZ STANDALONE (NO NED 2 DEFINE ITZ NAYM SINCE IZ TEH ONLY WAN)
dotnet publish -c "Release" -o "out"
# IF UR PROJECT IZ PART OV ASF SOURCE TREE (2 AVOID COMPILIN UNNECESARY PARTS)
dotnet publish YourPluginName -c "Release" -o "out"
AFTERWARDZ, UR PLUGIN IZ READY 4 DEPLOYMENT. It's up to you how exactly you want to distribute and publish your plugin, but we recommend creating a zip archive where you'll put your compiled plugin together with its dependencies. This way user will simply need to unpack your zip archive into a standalone subdirectory inside their plugins
directory and do nothing else.
DIS AR TEH ONLY TEH MOST BASIC SCENARIO 2 GIT U STARTD. We have ExamplePlugin
project that shows you example interfaces and actions that you can do within your own plugin, including helpful comments. Feel free to take a look if you'd like to learn from a working code, or discover ArchiSteamFarm.Plugins
namespace yourself and refer to the included documentation for all available options. We'll also further elaborate on some core concepts below to explain them better.
If instead of example plugin you'd want to learn from real projects, there are several official plugins developed by us, e.g. ItemsMatcher
, MobileAuthenticator
or SteamTokenDumper
. In addition to that, there are also plugins developed by other developers, in our third-party section.
ASF, APART FRUM WUT U HAS ACCES 2 IN DA INTERFACEZ THEMSELVEZ, EXPOSEZ 2 U LOT OV ITZ INTERNAL APIS DAT U CAN MAK USE OV, IN ORDR 2 EXTEND TEH FUNCSHUNALITY. 4 EXAMPLE, IF UD LIEK 2 SEND SUM KIND OV NEW REQUEST 2 STEAM WEB, DEN U DO NOT NED 2 IMPLEMENT EVRYTHIN FRUM SCRATCH, ESPECIALLY DEALIN WIF ALL TEH ISSUEZ WEVE HAD 2 DEAL WIF BEFORE U. Simply use our Bot.ArchiWebHandler
which already exposes a lot of UrlWithSession()
methods for you to use, handling all the lower-level stuff such as authentication, session refresh or web limiting for you. Likewise, for sending web requests outside of Steam platform, you could use standard .NET HttpClient
class, but it's much better idea to use Bot.ArchiWebHandler.WebBrowser
that is available for you, which once again offers you a helpful hand, for example in regards to retrying failed requests.
We have a very open policy in terms of our API availability, so if you'd like to make use of something that ASF code already includes, simply open an issue and explain in it your planned use case of our ASF's internal API. WE MOST LIKELY WONT HAS ANYTHIN AGAINST, AS LONG AS UR USE CASE MAKEZ SENSE. This also includes all suggestions in regards to new IPlugin
interfaces that could make sense to be added in order to extend existing functionality.
Regardless of ASF API availability however, nothing is stopping you from e.g. including Discord.Net
library in your application and creating a bridge between your Discord bot and ASF commands, since your plugin can also have dependencies on its own. The possibilities are endless, and we made our best to give you as much freedom and flexibility as possible within your plugin, so there are no artificial limits on anything - your plugin is loaded into the main ASF process and can do anything that is realistically possible to do from within C# code.
IZ IMPORTANT 2 EMFASIZE DAT ASF IZ CONSUMR APPLICASHUN AN NOT TYPICAL LIBRARY WIF FIXD API SURFACE DAT U CAN DEPEND ON UNCONDISHUNALLY. This means that you can't assume that your plugin once compiled will keep working with all future ASF releases regardless, it's simply impossible if we want to keep developing the program further, and being unable to adapt to ever-ongoing Steam changes for the sake of backwards compatibility is just not appropriate for our case. DIS SHUD BE LOGICAL 4 U, BUT IZ IMPORTANT 2 HIGHLIGHT DAT FACT.
We'll do our best to keep public parts of ASF working and stable, but we'll not be afraid to break the compatibility if good enough reasons arise, following our deprecation policy in the process. This is especially important in regards to internal ASF structures that are exposed to you as part of ASF infrastructure (e.g. ArchiWebHandler
) which could be improved (and therefore rewritten) as part of ASF enhancements in one of the future versions. WELL DO R BEST 2 INFORM U APPROPRIATELY IN DA CHANGELOGS, AN INCLUDE APPROPRIATE WARNINGS DURIN RUNTIME BOUT OBSOLETE FEATUREZ. WE DO NOT INTEND 2 REWRITE EVRYTHIN 4 DA SAEK OV REWRITIN IT, SO U CAN BE FAIRLY SURE DAT TEH NEXT MINOR ASF VERSHUN WONT JUS SIMPLY DESTROY UR PLUGIN ENTIRELY ONLY CUZ IT HAS HIGHR VERSHUN NUMBR, BUT KEEPIN AN EYE ON CHANGELOGS AN OCCASHUNAL VERIFICASHUN IF EVRYTHIN WERKZ FINE IZ VRY GUD IDEA.
Your plugin will include at least two dependencies by default, ArchiSteamFarm
reference for internal API (IPlugin
at the minimum), and PackageReference
of System.Composition.AttributedModel
that is required for being recognized as ASF plugin to begin with ([Export]
clause). In addition to that, it may include more dependencies in regards to what you've decided to do in your plugin (e.g. Discord.Net
library if you've decided to integrate with Discord).
The output of your build will include your core YourPluginName.dll
library, as well as all the dependencies that you've referenced. Since you're developing a plugin to already-working program, you don't have to, and even shouldn't include dependencies that ASF already includes, for example ArchiSteamFarm
, SteamKit2
or AngleSharp
. STRIPPIN DOWN UR BUILD OFF DEPENDENCIEZ SHARD WIF ASF IZ NOT TEH ABSOLUTE REQUIREMENT 4 UR PLUGIN 2 WERK, BUT DOIN SO WILL DRAMATICALLY CUT TEH MEMS FOOTPRINT AN TEH SIZE OV UR PLUGIN, TOGETHR WIF INCREASIN TEH PERFORMANCE, DUE 2 TEH FACT DAT ASF WILL SHARE ITZ OWN DEPENDENCIEZ WIF U, AN WILL LOAD ONLY DOSE LIBRARIEZ DAT IT DOESNT KNOE BOUT ITSELF.
IN GENERAL, IT BE RECOMMENDD PRACTICE 2 INCLUDE ONLY DOSE LIBRARIEZ DAT ASF EITHR DOESNT INCLUDE, OR INCLUDEZ IN DA WRONG/INCOMPATIBLE VERSHUN. Examples of those would be obviously YourPluginName.dll
, but for example also Discord.Net.dll
if you decided to depend on it, as ASF doesn't include it itself. Bundling libraries that are shared with ASF can still make sense if you want to ensure API compatibility (e.g. being sure that AngleSharp
which you depend on in your plugin will always be in version X
and not the one that ASF ships with), but obviously doing that comes for a price of increased memory/size and worse performance, and therefore should be carefully evaluated.
If you know that the dependency which you need is included in ASF, you can mark it with IncludeAssets="compile"
as we showed you in the example csproj
above. DIS WILL TELL TEH COMPILR 2 AVOID PUBLISHIN REFERENCD LIBRARY ITSELF, AS ASF ALREADY INCLUDEZ DAT WAN. Likewise, notice that we reference the ASF project with ExcludeAssets="all" Private="false"
which works in a very similar way - telling the compiler to not produce any ASF files (as the user already has them). This applies only when referencing ASF project, since if you reference a dll
library, then you're not producing ASF files as part of your plugin.
If you're confused about above statement and you don't know better, check which dll
libraries are included in ASF-generic.zip
package and ensure that your plugin includes only those that are not part of it yet. This will be only YourPluginName.dll
for the most simple plugins. IF U GIT ANY ISSUEZ DURIN RUNTIME IN REGARDZ 2 SUM LIBRARIEZ, INCLUDE DOSE AFFECTD LIBRARIEZ AS WELL. IF ALL ELSE FAILS, U CAN ALWAYS DECIDE 2 BUNDLE EVRYTHIN.
NATIV DEPENDENCIEZ R GENERATD AS PART OV OS-SPECIFIC BUILDZ, AS THAR IZ NO .NET RUNTIME AVAILABLE ON TEH HOST AN ASF IZ RUNNIN THRU ITZ OWN .NET RUNTIME DAT IZ BUNDLD AS PART OV OS-SPECIFIC BUILD. IN ORDR 2 MINIMIZE TEH BUILD SIZE, ASF TRIMS ITZ NATIV DEPENDENCIEZ 2 INCLUDE ONLY TEH CODE DAT CAN BE POSIBLY REACHD WITHIN TEH PROGRAM, WHICH EFFECTIVELY CUTS TEH UNUSD PARTS OV TEH RUNTIME. This can create a potential problem for you in regards to your plugin, if suddenly you find out yourself in a situation where your plugin depends on some .NET feature that isn't being used in ASF, and therefore OS-specific builds can't execute it properly, usually throwing System.MissingMethodException
or System.Reflection.ReflectionTypeLoadException
in the process. As your plugin grows in size and becomes more and more complex, sooner or later you'll hit the surface that is not covered by our OS-specific build.
DIS AR TEH NEVR PROBLEM WIF GENERIC BUILDZ, CUZ DOSE R NEVR DEALIN WIF NATIV DEPENDENCIEZ IN DA FURST PLACE (AS THEY HAS COMPLETE WERKIN RUNTIME ON TEH HOST, EXECUTIN ASF). This is why it's a recommended practice to use your plugin in generic builds exclusively, but obviously that has its own downside of cutting your plugin from users that are running OS-specific builds of ASF. IF URE WONDERIN IF UR ISSUE IZ RELATD 2 NATIV DEPENDENCIEZ, U CAN ALSO USE DIS METHOD 4 VERIFICASHUN, LOAD UR PLUGIN IN ASF GENERIC BUILD AN C IF IT WERKZ. IF IT DOEZ, U HAS PLUGIN DEPENDENCIEZ COVERD, AN IZ TEH NATIV DEPENDENCIEZ CAUSIN ISSUEZ.
UNFORTUNATELY, WE HAD 2 MAK HARD CHOICE TWEEN PUBLISHIN WHOLE RUNTIME AS PART OV R OS-SPECIFIC BUILDZ, AN DECIDIN 2 CUT IT OUT OV UNUSD FEATUREZ, MAKIN TEH BUILD OVAR 80 MB SMALLR COMPARD 2 TEH FULL WAN. WEVE PICKD TEH SECOND OPSHUN, AN IZ UNFORTUNATELY IMPOSIBLE 4 U 2 INCLUDE TEH MISIN RUNTIME FEATUREZ TOGETHR WIF UR PLUGIN. If your project requires access to runtime features that are left out, you have to include full .NET runtime that you depend on, and that means running your plugin in generic
ASF flavour. U CANT RUN UR PLUGIN IN OS-SPECIFIC BUILDZ, AS DOSE BUILDZ R SIMPLY MISIN RUNTIME FEACHUR DAT U NED, AN .NET RUNTIME AS OV NAO IZ UNABLE 2 "MERGE" NATIV DEPENDENCY DAT U CUDVE PROVIDD WIF R OWN. PERHAPS ITLL IMPROOOV WAN DAI IN DA FUCHUR, BUT AS OV NAO IZ SIMPLY NOT POSIBLE.
ASFS OS-SPECIFIC BUILDZ INCLUDE TEH BARE MINIMUM OV ADDISHUNAL FUNCSHUNALITY WHICH IZ REQUIRD 2 RUN R OFFISHUL PLUGINS. APART OV DAT BEAN POSIBLE, DIS ALSO SLIGHTLY EXTENDZ TEH SURFACE 2 EXTRA DEPENDENCIEZ REQUIRD 4 DA MOST BASIC PLUGINS. THEREFORE NOT ALL PLUGINS WILL NED 2 WORRY BOUT NATIV DEPENDENCIEZ 2 BEGIN WIF - ONLY DOSE DAT GO BEYOND WUT ASF AN R OFFISHUL PLUGINS DIRECTLY NED. DIS AR TEH DUN AS AN EXTRA, SINCE IF WE NED 2 INCLUDE ADDISHUNAL NATIV DEPENDENCIEZ OURSELVEZ 4 R OWN USE CASEZ ANYWAY, WE CAN AS WELL SHIP THEM DIRECTLY WIF ASF, MAKIN THEM AVAILABLE, AN THEREFORE EASIR 2 COVR, ALSO 4 U. UNFORTUNATELY, DIS AR TEH NOT ALWAYS ENOUGH, AN AS UR PLUGIN GETS BIGGR AN MOAR COMPLEX, TEH LIKELIHOOD OV RUNNIN INTO TRIMMD FUNCSHUNALITY INCREASEZ. Therefore, we usually recommend you to run your custom plugins in generic
ASF flavour exclusively. U CAN STILL MANUALLY VERIFY DAT OS-SPECIFIC BUILD OV ASF HAS EVRYTHIN DAT UR PLUGIN REQUIREZ 4 ITZ FUNCSHUNALITY - BUT SINCE DAT CHANGEZ ON UR UPDATEZ AS WELL AS OURS, IT MITE BE TRICKY 2 MAINTAIN.
Sometimes it may be possible to "workaround" missing features by either using alternative options or reimplementing them in your plugin. This is however not always possible or viable, especially if the missing feature comes from third-party dependencies that you include as part of your plugin. You can always try to run your plugin in OS-specific build and attempt to make it work, but it might become too much hassle in the long-run, especially if you want from your code to just work, rather than fight with ASF surface.
ASF offers you two interfaces for implementing automatic updates in your plugin:
-
IGitHubPluginUpdates
providing you easy way to implement GitHub-based updates similar to general ASF update mechanism -
IPluginUpdates
providing you lower-level functionality that allows for custom update mechanism, if you require something more complex
The minimum checklist of things that are required for updates to work:
- You need to declare
RepositoryName
, which defines where the updates are pulled from. - You need to make use of tags and releases provided by GitHub. Tag must be in format parsable to a plugin version, e.g.
1.0.0.0
. -
Version
property of the plugin must match tag that it comes from. This means that binary available under tag1.2.3.4
must present itself as1.2.3.4
. - Each tag should have appropriate release available on GitHub with zip file release asset that includes your plugin binary files. The binary files should be available in the root directory within the zip file.
This will allow the mechanism in ASF to:
- Resolve current
Version
of your plugin, e.g.1.0.1
. - Use GitHub API to fetch latest
tag
available inRepositoryName
repo, e.g.1.0.2
. - Determine that
1.0.2
>1.0.1
, check release of1.0.2
tag to find.zip
file with the plugin update. - Download that
.zip
file, extract it, and put its content in the directory that includedYourPlugin.dll
before. - Restart ASF to load your plugin in
1.0.2
version.
Additional notes:
- Plugin updates require appropriate ASF config values, namely
PluginsUpdateMode
and/orPluginsUpdateList
. - Our plugin template already includes everything you need, simply rename the plugin to proper values, and it'll work out of the box.
- You can use combination of latest release as well as pre-releases, they'll be used as per
UpdateChannel
the user has defined. - There is
CanUpdate
boolean property you can override for disabling/enabling plugins update on your side, for example if you want to skip updates temporarily, or through any other reason. - It's possible to have more than one zip file in the release if you want to target different ASF versions. See
GetTargetReleaseAsset()
remarks, this allows you to have e.g.MyPlugin-V6.1.zip
as well asMyPlugin-V6.2.zip
and support both ASFV6.1.x.x
as well asV6.2.x.x
at once. - If the above is not sufficient to you and you require custom logic for picking the correct
.zip
file, you can overrideGetTargetReleaseAsset()
function and provide the artifact for ASF to use. - If your plugin needs to do some extra work before/after update, you can override
OnPluginUpdateProceeding()
and/orOnPluginUpdateFinished()
methods.
This interface allows you to implement custom logic for updates if by any reason IGithubPluginUpdates
is not sufficient to you, for example because you have tags that do not parse to versions, or because you're not using GitHub platform at all.
There is a single GetTargetReleaseURL()
function for you to override, which expects from you Uri?
of target plugin update location. ASF provides you some core variables that relate to the context the function was called with, but other than that, you're responsible to implement everything you need in that function and provide ASF the URL that should be used, or null
if the update is not available.
Other than that, it's similar to GitHub updates, where the URL should point to a .zip
file with the binary files available in the root directory. You also have OnPluginUpdateProceeding()
and OnPluginUpdateFinished()
methods available.
- π‘ HOME
- π§ CONFIGURASHUN
- π¬ FAQ
- βοΈ SETTIN UP (START HER)
- π₯ BAKGROUND GAMEZ REDEEMR
- π’ COMMANDZ
- π οΈ COMPATIBILITY
- 𧩠ITEMSMATCHERPLUGIN
- π MANAGEMENT
- β±οΈ PERFORMANCE
- π‘ REMOTE COMMUNICASHUN
- πͺ STEAM PPLZ SHARIN
- π TRADIN