Quick start
Install the .NET 9 SDK, copy examples/PluginStarter, and rename the project, source file, namespace, class, and manifest values. Your project should reference the API without copying it into the output:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\BlockGame.PluginApi\BlockGame.PluginApi.csproj"
Private="false" />
<None Include="plugin.yml" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
Declare plugin.yml
name: MyPlugin
version: 1.0.0
main: MyPlugin.dll
entrypoint: MyPlugin.MyPlugin
api-version: 1.6
# depend: [RequiredPlugin]
# softdepend: [OptionalPlugin]
| Key | Meaning |
|---|---|
name | Unique display name and data-directory name. |
version | Your plugin version, shown in server logs. |
main | Main assembly path relative to the manifest. |
entrypoint | Fully qualified public IGamePlugin type. |
api-version | Oldest API contract the plugin requires. |
depend | Plugins that must be installed and enabled first. |
softdepend | Optional integrations that influence load order when present. |
API versions use major-version compatibility: an API 1.6 server accepts plugins targeting 1.0 through 1.6, but not 2.0.
Implement the lifecycle
using BlockGame.PluginApi;
namespace MyPlugin;
public sealed class MyPlugin : GamePlugin
{
public override string Name => "MyPlugin";
public override string Version => "1.0.0";
public override void OnLoad()
{
// Read configuration and initialize plugin-owned state.
}
public override void OnEnable()
{
Server.Log("Enabled");
}
public override void OnDisable()
{
// Flush plugin-owned state.
}
}
Server and DataDirectory are available from OnLoad onward. Required dependencies enable first. Plugins disable in reverse enable order; listeners and scheduled tasks are removed automatically.
Register commands
Server.RegisterCommand(new PluginCommand(
"greet",
"Greets the command sender",
command => command.Reply($"Hello, {command.Player.Name}!"),
usage: "/greet",
permission: "myplugin.greet",
aliases: new[] { "hi" }));
Grant custom permission nodes in permissions.cfg. A command receives a stable player snapshot, parsed arguments, the raw command, and a reply callback.
Listen to typed events
Server.RegisterListener<BlockEditEventArgs>(e =>
{
if (e.Position.Y < 20)
{
e.Cancelled = true;
e.DenialMessage = "You cannot build below the arena.";
}
}, EventPriority.High);
Priorities run from Lowest through Monitor. Use Monitor only to observe. Set ignoreCancelled: true to skip an event already cancelled by an earlier listener.
Schedule server-thread work
Server.RunTask(() => Server.Broadcast("Next tick"));
Server.RunTaskLater(TimeSpan.FromSeconds(5), StartRound);
Server.RunTaskTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1), UpdateCountdown);
World and player callbacks run on the server tick thread. Keep them short; never block that thread on file, database, or network I/O.
Store plugin-owned data
Write configuration and persistent metadata beneath DataDirectory, not beside the installed DLL. Use stable PluginPlayer.Id values as player keys. Treat PluginPlayer objects as snapshots rather than live mutable state.
API 1.6 reference
| Area | Available capabilities |
|---|---|
| Lifecycle | OnLoad, OnEnable, OnDisable |
| Events | Join, quit, chat, tick, block edit, attack, damage, and death events |
| Players | Lookup, message, teleport, game mode, disguise, health, inventory, armor, and permissions |
| World | Block reads/writes, selections, bounded region capture and restore |
| Containers | Read, set, and clear item counts in authoritative chests |
| Persistence | Home chunks, claim enumeration, claim/release, chunk generation, and world saves |
| Utilities | Commands, broadcasts, prefixed logging, delayed and repeating tasks |
Use stable IDs from PluginBlocks and PluginItems. The API deliberately does not expose networking, rendering, server connection classes, or internal world types.
Minigame SDK
Round-based modes can reference BlockGame.PluginApi.Minigames and derive from MinigamePlugin. It activates only on a matching minigame backend and supplies player and announcement helpers. Ship BlockGame.PluginApi.Minigames.dll with plugins that use it.
Build, package, and test
powershell -NoProfile -ExecutionPolicy Bypass `
-File .\tools\package-plugin.ps1 `
.\examples\MyPlugin\MyPlugin.csproj
The packager validates the manifest and assembly, excludes debug symbols and the server-supplied API, and creates both a directory and ZIP beneath artifacts/plugins.
- Test against the oldest API version declared in your manifest.
- Install the packaged directory beneath
Plugins. - Restart the server and confirm the enabled message in its logs.
- Exercise disable and restart paths, missing configuration, and dependency failures before publishing.
Distribution rule: include your main DLL, plugin.yml, and private third-party dependencies. Do not ship BlockGame.PluginApi.dll.