commit 6f7cff954aee3ad1461cec9f69695e3039557839 Author: XShine555 Date: Wed Aug 26 23:01:38 2026 +0200 Add LabAPI plugin dotnet new template diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..79a1aa4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +end_of_line = crlf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.cs] +indent_style = space +indent_size = 4 +dotnet_sort_system_directives_first = true +csharp_new_line_before_open_brace = all +csharp_prefer_braces = true:suggestion + +# Naming and language conventions here reflect this project's own style; they +# do not by themselves enforce the "no unsolicited comments" / "English by +# default" rules in AGENTS.md / CLAUDE.md, which apply to any human or AI +# contributor regardless of what an analyzer can mechanically check. + +[*.{csproj,json,yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10f9606 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +obj/ +*.user +.vs/ diff --git a/.template.config/template.json b/.template.config/template.json new file mode 100644 index 0000000..df4da7c --- /dev/null +++ b/.template.config/template.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Your name / organization", + "classifications": [ "Common", "Library", "SCP:SL", "LabAPI", "Game Plugin" ], + "identity": "SCPSL.LabApi.PluginTemplate", + "name": "SCP: Secret Laboratory LabAPI Plugin", + "shortName": "labapi-plugin", + "tags": { + "language": "C#", + "type": "project" + }, + "sourceName": "LabApiPluginTemplate", + "preferNameDirectory": true, + "symbols": { + "LabApiVersion": { + "type": "parameter", + "datatype": "string", + "defaultValue": "1.1.7", + "replaces": "1.1.7", + "description": "Version of the Northwood.LabAPI NuGet package to install in the project." + }, + "PluginAuthor": { + "type": "parameter", + "datatype": "string", + "defaultValue": "YourName", + "replaces": "YourName", + "description": "Name used as the plugin's author (EntryPoint's Author property)." + }, + "skipRestore": { + "type": "parameter", + "datatype": "bool", + "description": "If set, skips running 'dotnet restore' automatically after creating the project.", + "defaultValue": "false" + } + }, + "primaryOutputs": [ + { "path": "LabApiPluginTemplate.csproj" } + ], + "postActions": [ + { + "condition": "(!skipRestore)", + "description": "Restores the NuGet packages required by this project (including LabAPI).", + "manualInstructions": [ + { "text": "Run 'dotnet restore'" } + ], + "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", + "continueOnError": true + } + ] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6b5dad1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,69 @@ +# AGENTS.md + +This repo is the source of a `dotnet new` template (shortName `labapi-plugin`) that +scaffolds SCP: Secret Laboratory LabAPI plugins. This file follows the +editor/vendor-agnostic [agents.md](https://agents.md) convention for AI coding tools +other than Claude Code (Cursor, Codex, etc.). **`CLAUDE.md` is this repo's primary, +detailed instructions file** — read it first if your tooling can. What follows is the +condensed subset any agent needs, kept in sync with `CLAUDE.md`. + +## This is template source, not a plugin + +This section applies only while `.template.config/` is present next to this file — +i.e. you're editing the template repo itself, not a plugin already generated from it. + +`.template.config/template.json` makes the dotnet templating engine do a **literal, +repo-wide string replacement** at generation time (not semantic renaming): this +folder's own name (`sourceName`) becomes whatever project name a consumer picks, the +placeholder author name in `EntryPoint.cs` becomes their `--PluginAuthor`, and the +LabAPI version pinned in the `.csproj`/`EntryPoint.cs` becomes their +`--LabApiVersion`. See `template.json` for the exact current literals — don't repeat +them verbatim in prose here, since this file ships inside every generated project and +an exact match would get silently rewritten too. Don't introduce an exact match of +one of those literals elsewhere in the repo unless you mean for it to be substituted; +add a new `template.json` symbol instead of hardcoding a magic string. `EntryPoint`/ +`Config` (the class names) are intentionally **not** substituted — only the namespace +tracks the project name. + +## Never invent LabAPI API surface + +Don't assume a LabAPI class/namespace/method/property/attribute exists. Verify it +against the restored `Northwood.LabAPI` package (`LabApi.dll`/`LabApi.xml` under +`%userprofile%\.nuget\packages\northwood.labapi\\lib\net48\`) or +https://github.com/northwood-studios/LabAPI first. If you can't verify a member, +say so instead of guessing. + +## Toolchain choices — don't change without being asked + +`TargetFramework` is `netstandard2.1` on purpose (LabAPI itself only ships +`net48`/`net481`), chosen so the plugin still loads under the game's Mono/.NET +Framework 4.8 host. `LangVersion` is pinned to `14.0`. The `.csproj` suppresses +exactly `NU1701` (scoped to the `Northwood.LabAPI` `PackageReference`) and `MSB3270` +(via `ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch=None`) because of that +netstandard2.1/net48-only-package combination — don't broaden either into a blanket +suppression or remove them. + +## Code style and language + +No `//`, `/* */`, or `///` comments unless the user explicitly asks for them in that +request; don't add comments restating self-explanatory code — `EntryPoint.cs`/ +`Config.cs` are already comment-free, follow the same standard for new files. All +text embedded in code (log/console messages, exceptions, config descriptions, +user-facing strings) defaults to English — e.g. `Logger.Info("Plugin initialized +successfully")` — except identifiers the user requested (project name, namespace, +class/method/property/variable names), which are never translated, and text the user +explicitly asks to be in another language. + +## Verifying a change + +No automated tests exist. Verify by generating a real project and building it: + +```bash +dotnet new uninstall . && dotnet new install . +dotnet new labapi-plugin -n VerifyPlugin -o ../_verify/VerifyPlugin +cd ../_verify/VerifyPlugin && dotnet build +``` + +Expect `Build succeeded. 0 Warning(s) 0 Error(s)`. See `CLAUDE.md` for the full +verification notes (cache invalidation after editing `template.json`, Windows cleanup +caveats). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0375e7a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,142 @@ +# CLAUDE.md + +This repo **is** a `dotnet new` project template (identity `SCPSL.LabApi.PluginTemplate`, +shortName `labapi-plugin`), not a runnable plugin. It scaffolds SCP: Secret Laboratory +LabAPI plugins. `AGENTS.md` carries a shorter, tool-agnostic version of the essentials +for non-Claude agents; this file is the authoritative one — keep both in sync when a +rule below changes. + +The next section only applies while you're editing **this template repository** +(i.e. a `.template.config/` folder is present next to this file). It does not apply +if you're instead looking at a plugin someone already generated from this template — +that folder never contains `.template.config/`, and its `EntryPoint.cs`/`Config.cs` +are ordinary project files at that point, not template scaffolding. + +## Repo is template *source* — read before touching any file + +`.template.config/template.json` sets this folder's name as the template's +`sourceName` and defines two text-replacement `symbols` (plus a third, `skipRestore`, +that only toggles the post-generation `dotnet restore` and replaces no text). The +dotnet templating engine does a literal, repo-wide **string replacement** at +generation time — not semantic renaming — driven by that config: + +- The folder/project name (`sourceName` in `template.json`, currently this repo's own + folder name) → whatever `-n`/`--name` the consumer passes to `dotnet new`. +- The placeholder value of the `PluginAuthor` symbol (the string currently sitting in + `EntryPoint.cs`'s `Author` property) → the consumer's `--PluginAuthor`. +- The placeholder value of the `LabApiVersion` symbol (the version string currently in + the `.csproj`'s `PackageReference` and in `EntryPoint.cs`'s `RequiredApiVersion`) → + the consumer's `--LabApiVersion`. + +Open `template.json` to see the exact current literal for each — don't hardcode them +again here or anywhere else in prose, because this file itself ships inside every +generated project and any exact match gets rewritten too (self-referential text like +"the project is called X" would silently turn into nonsense after generation). + +Consequences: +- Never introduce an exact match of one of those three literals elsewhere in the repo + unless you intend it to be substituted too. +- If you add a new thing that should be configurable at generation time, add a symbol + to `template.json` (`type: parameter`, `replaces: ""`) instead of + hardcoding a magic string. +- `EntryPoint` and `Config` (the class names) are **not** substituted and must stay + literally named that — LabAPI's `PluginLoader` discovers plugins by type + inheritance, not by name, and the file names `EntryPoint.cs`/`Config.cs` are a fixed + part of this template's contract. Only the *namespace* tracks the project name. + +## Repo layout (only what affects decisions) + +- `.template.config/template.json` — template identity, the 3 symbols above, + `primaryOutputs` (points at the project file using its pre-substitution literal + name — that's correct and expected, not a bug), and a `postActions` restore step + (the standard dotnet-templating "restore NuGet packages" action, id + `210D431B-...`; skipped in Visual Studio, which restores on its own). +- The `.csproj` at the repo root — the project file every generated plugin ships with. +- `EntryPoint.cs` / `Config.cs` — the scaffolded plugin (`Plugin`) and its + YAML-backed settings class. +- `.editorconfig` — formatting/naming only (indentation, brace style). It does **not** + enforce the comment/language rules below — those aren't analyzer-checkable. +- No `.sln`, no test project, no CI, no packaging (`.nupkg`) project, no git repo, and + no GitHub Copilot instructions file currently exist in this folder. Don't assume + any of them. + +## LabAPI contract (verified against northwood-studios/LabAPI source, not guessed) + +`Plugin` (`LabApi.Loader.Features.Plugins`) requires implementing: +`Name`, `Description`, `Author`, `RequiredApiVersion` (all abstract get-only) plus +`Enable()`/`Disable()`. Overridable but optional: `Version` (defaults to the +assembly's version), `Priority` (`LoadPriority`, defaults to `Medium`), +`ConfigFileName` (defaults to `"config.yml"`). `Config` is a plain `class` with a +public parameterless constructor and public get/set properties — the base class +(de)serializes it via YamlDotNet automatically; don't hand-roll config I/O. + +**Never invent a LabAPI class/namespace/method/property/attribute.** Before using one +you haven't already seen in this repo, check the restored package (`LabApi.dll` + +`LabApi.xml` under `%userprofile%\.nuget\packages\northwood.labapi\\lib\net48\`) +or https://github.com/northwood-studios/LabAPI. If you can't verify a member exists, +say so instead of guessing. No Skill/plugin specific to C#, .NET, MSBuild, NuGet, or +LabAPI exists in the Claude Code skill catalog as of this writing — check +`ToolSearch`/`SearchSkills` for one before editing in case that has changed, but don't +assume one exists. + +## Toolchain choices — don't "fix" these without being asked + +- The project's `TargetFramework` is `netstandard2.1` on purpose, even though + LabAPI's own docs and the `Northwood.LabAPI` NuGet package only target + `net48`/`net481`. This is a deliberate compatibility choice (compiled output still + loads under the game's Mono/.NET Framework 4.8 host) that trades away some + netstandard2.1 BCL/language surface .NET Framework can't actually run (e.g. default + interface members). Don't switch it to `net48`, don't add multi-targeting, unless + the user asks. +- `LangVersion` is pinned explicitly (currently `14.0`) — don't let it drift to + `latest`. +- The `.csproj` suppresses exactly two warnings, both scoped as narrowly as MSBuild + allows, because of the netstandard2.1/net48-only-package combination above: + `NU1701` (via a per-`PackageReference` ``, not a project-wide one) and + `MSB3270` (via `ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch=None`). Don't + broaden either into a blanket suppression and don't remove them without + understanding why (comments sit next to both in the `.csproj`). + +## Code style for anything you write here + +- No `//`, `/* */`, or `///` comments unless the user explicitly asks for + comments/documentation in that request. No comments restating self-explanatory code. + `EntryPoint.cs`/`Config.cs` are already comment-free — keep them that way and follow + the same standard for any new file. +- All text embedded in code defaults to **English**: string/log/console messages, + exception messages, `[Description]`/config text, any user-facing string. Example: + `Logger.Info("Plugin initialized successfully")`, not a Spanish equivalent. +- This never applies to identifiers the user picked — project name, namespace, + class/method/property/variable names stay exactly as requested (standard C# casing + rules aside). Honor an explicit request for a specific piece of text in another + language. + +## How to verify a change (no test suite exists — this is the actual check) + +There is no automated test project. Verify any template change by actually +generating a project from it and building that output. Replace `` below +with this repo's actual folder name (the value you'd see in `sourceName`): + +```bash +dotnet new uninstall . +dotnet new install . +dotnet new labapi-plugin -n VerifyPlugin -o ../_verify/VerifyPlugin +cd ../_verify/VerifyPlugin +dotnet build +``` + +Expect `Build succeeded. 0 Warning(s) 0 Error(s)`. If it isn't 0/0, the change broke +either the template's own warning suppressions or something in `EntryPoint.cs`/ +`Config.cs`/the `.csproj`. + +Notes: +- Re-run `dotnet new uninstall .` + `dotnet new install .` after **any** edit to + `.template.config/template.json` or the scaffolded files — the template cache + keys off the installed path and stale content otherwise gets regenerated. +- `dotnet build` also works directly inside `` (the placeholder project + is itself valid and compilable), but that only proves the *unsubstituted* + placeholder compiles — it does not exercise symbol substitution. Always additionally + verify through an actual `dotnet new` generation before calling a change done. +- On Windows, if cleaning up `../_verify` fails with "resource busy", run + `dotnet build-server shutdown` first, then retry. +- Delete the `../_verify` scratch output when done; it isn't part of the template. diff --git a/Config.cs b/Config.cs new file mode 100644 index 0000000..c6dbc89 --- /dev/null +++ b/Config.cs @@ -0,0 +1,10 @@ +using System.ComponentModel; + +namespace LabApiPluginTemplate +{ + public class Config + { + [Description("Whether the plugin is enabled.")] + public bool IsEnabled { get; set; } = true; + } +} diff --git a/EntryPoint.cs b/EntryPoint.cs new file mode 100644 index 0000000..093393a --- /dev/null +++ b/EntryPoint.cs @@ -0,0 +1,30 @@ +using System; +using LabApi.Features.Console; +using LabApi.Loader.Features.Plugins; +using LabApi.Loader.Features.Plugins.Enums; + +namespace LabApiPluginTemplate +{ + public class EntryPoint : Plugin + { + public override string Name => "LabApiPluginTemplate"; + + public override string Description => "TODO: describe what this plugin does."; + + public override string Author => "YourName"; + + public override Version RequiredApiVersion { get; } = new Version("1.1.7"); + + public override LoadPriority Priority => LoadPriority.Medium; + + public override void Enable() + { + Logger.Info($"{Name} has been enabled."); + } + + public override void Disable() + { + Logger.Info($"{Name} has been disabled."); + } + } +} diff --git a/LabApiPluginTemplate.csproj b/LabApiPluginTemplate.csproj new file mode 100644 index 0000000..5a446dd --- /dev/null +++ b/LabApiPluginTemplate.csproj @@ -0,0 +1,41 @@ + + + + + netstandard2.1 + + + 14.0 + + enable + disable + LabApiPluginTemplate + LabApiPluginTemplate + false + false + + + $(NoWarn);NU1701 + + + None + + + + + NU1701 + + + +