Replace failing build step with restore + manifest validation. Full build requires /home/runner/.xlcore/dalamud/Hooks/dev/ which only exists on machines with Dalamud installed (XIVLauncher etc.). README documents how to enable build-CI in downstream repos.
Dalamud Plugin Template
A starting point for FFXIV/Dalamud plugins on the Hellion Forge.
Built on the official goatcorp Dalamud Sample Plugin (working code with goat-image demo, MainWindow with PlayerState/Lumina queries, ConfigWindow with movable-toggle), wrapped with Hellion-Forge conventions: custom-repo manifest, Forge-Auto-Announce workflow, version-bump checklist, MIT license.
The sample code is intentionally kept intact so the template builds and runs out of the box. Strip the goat demo when you implement your real plugin (see "Replacing the sample" below).
How to use this template
- Click "Use this template" at the top of the repository page on the Forge.
- Pick a name like
MyPluginand clone your new repo locally. - Find-and-replace
PluginNameTemplateeverywhere with your plugin's name (case-sensitive):git ls-files | xargs sed -i 's/PluginNameTemplate/MyPlugin/g' git mv PluginNameTemplate.csproj MyPlugin.csproj git mv PluginNameTemplate.yaml MyPlugin.yaml git mv PluginNameTemplate.sln MyPlugin.sln - Replace
images/icon.pngwith your plugin icon (512x512 PNG, transparent background). - Update
repo.jsonwith your realDownloadLinkInstallURLs once your CI publishes releases. - Strip the goat demo (or keep it for development reference — your call).
- Implement your plugin in
src/Plugin.csand friends.
After the rename, this README should be replaced with your plugin's actual README — the template-usage notes don't belong in your shipped plugin.
Replacing the sample
The template ships with the goat demo working end-to-end so you can verify your build setup before writing any code. To remove it cleanly:
- Empty the windows.
src/Windows/MainWindow.csandsrc/Windows/ConfigWindow.cs— replace the goat-image / config-toggle demos with your real UI. Keep the class structure (constructor signature,Draw()override). - Delete the goat asset.
Data/goat.pngand the<Content Include="Data\goat.png">block in<PluginName>.csproj. - Adjust services.
src/Plugin.csinjectsITextureProvider,IClientState,IPlayerState,IDataManager— only because the goat demo uses them. Drop the ones you don't need. - Rename
/pmycommand. Insrc/Plugin.cs, changeprivate const string CommandName = "/pmycommand"to your plugin's actual command and update theHelpMessage.
Project structure
.
├── .editorconfig Code style
├── .gitea/
│ ├── ISSUE_TEMPLATE/ Standard issue forms
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── forge-posts/v0.1.0.md Discord-announcement payload (Forge-Auto-Announce)
│ └── workflows/
│ ├── ci.yml Build verification on push/PR
│ └── forge-auto-announce.yml Discord announcement on tag
├── Data/
│ └── goat.png Sample asset for the working demo (replace or delete)
├── docs/CHANGELOG.md Long-form changelog
├── images/.gitkeep Plugin icon goes here (icon.png)
├── src/
│ ├── Plugin.cs IDalamudPlugin entry, WindowSystem, command handler
│ ├── Configuration.cs IPluginConfiguration
│ ├── packages.lock.json NuGet lockfile (Dalamud SDK manages this)
│ └── Windows/
│ ├── ConfigWindow.cs Movable-toggle demo
│ └── MainWindow.cs Goat + PlayerState/Lumina demo
├── PluginNameTemplate.csproj Dalamud-SDK 15.0.0 csproj
├── PluginNameTemplate.sln Solution file
├── PluginNameTemplate.yaml Dalamud manifest
├── repo.json Custom-repo manifest for testers
├── CHANGELOG.md Slim changelog (latest 2-4 versions)
├── CODEOWNERS Default reviewer
├── LICENSE MIT
└── README.md This file (replace before shipping)
Build
dotnet restore
dotnet build -c Release
DalamudPackager produces the .zip artifact under bin/Release/<PluginName>/latest.zip.
Note: Don't override the default DalamudPackager.targets from your csproj — it'll silently strip the icon and ImageUrls from your manifest. If you need custom packaging, do it via csproj properties only.
Versioning
Synchronized version fields (bump all at once):
<PluginName>.csproj→Version,AssemblyVersion,FileVersion<PluginName>.yaml→assembly_version+changelogrepo.json→AssemblyVersion,TestingAssemblyVersion, all 3DownloadLink*URLs,Description,ChangelogCHANGELOG.md(slim) anddocs/CHANGELOG.md(full) — keep the latest 2-4 versions in the slim copy.gitea/forge-posts/v<X.Y.Z>.md— Discord announcement payload
Sanity-check before tagging:
grep -rn "<old-version>" . | grep -v -E '(bin|obj|node_modules|.git/)'
The Forge-Auto-Announce workflow reads from the tagged tree, not main. If a fix-commit lands after the tag, force-push the tag.
Dalamud SDK version
This template targets Dalamud.NET.Sdk/15.0.0 and dalamud_api_level: 15 in the manifest. When the SDK bumps:
- Update
<Project Sdk="Dalamud.NET.Sdk/X.Y.Z">in the csproj - Update
dalamud_api_level: Xin the yaml - Check the SDK release notes for breaking API changes
- Run a clean build (
dotnet clean && dotnet build) and validate the goat demo still works
Testing
Service classes coupled to Dalamud (IPluginInterface, IDataManager, etc.) cannot be instantiated directly in xUnit because the Dalamud assembly isn't on the test AppDomain. Patterns that work:
- Pure helpers — extract logic into Dalamud-free classes, test those directly
- Constructor injection — pass utility dependencies in, mock them in tests
- External test repo — keep tests in a private side-repo if they need a richer harness
The default csproj has no test project. Add one when there's something to test.
License
MIT — see LICENSE. The base sample-code is from goatcorp's Dalamud SamplePlugin (AGPL-3.0 in upstream); the working translation here is shipped under MIT alongside the Hellion-specific scaffolding. If you ship a plugin that's a near-1:1 fork of the upstream sample, check the upstream license for your distribution.