A starter template for Skyrim Special Edition / Anniversary Edition SKSE plugins using CommonLibSSE-NG, CMake, and vcpkg.
Supports building on Linux (cross-compilation via clang-cl + xwin) and Windows (MSVC).
Mod manager (recommended):
- Install the requirements above.
- Install ExampleMod via your mod manager.
- Launch Skyrim via SKSE.
Manual:
- Install the requirements above.
- Copy
ExampleMod.dlltoData\SKSE\Plugins\. - Launch Skyrim via SKSE.
- Compatible with Skyrim SE and AE.
- No ESP/ESL required.
When loaded by Skyrim the plugin:
- Writes a log to
Data/SKSE/Plugins/ExampleMod.logvia spdlog. - Hooks
kDataLoaded(fires once all game data is loaded). - Prints to the in-game console (
~key):[ExampleMod] Loaded successfully!
- LLVM/Clang (provides
clang-cl,lld-link,llvm-lib,llvm-rc,llvm-mt) - xwin — downloads the real Windows SDK and MSVC CRT headers/libs
- Ninja
# Arch / CachyOS
sudo pacman -S clang lld llvm ninja
# Install xwin (requires Rust/cargo)
cargo install xwin
# Fetch Windows SDK + MSVC CRT headers to ~/.xwin (one-time, ~700 MB)
xwin splat --output ~/.xwinNote: On first configure,
cmake/toolchains/clang-cl-cross.cmakecreates TitleCase symlinks inside your xwin installation, e.g.:~/.xwin/sdk/lib/um/x86_64/Advapi32.lib -> advapi32.liblld-link is case-sensitive but CommonLibSSE-NG references libs with mixed-case names. The originals are untouched.
- Visual Studio 2022 with Desktop development with C++
Click "Use this template" on GitHub, or clone and re-initialise:
git clone https://github.com/your-org/your-mod.git
cd your-modLinux — run interactively or pass arguments directly:
./scripts/init.sh
# or:
./scripts/init.sh "AuthorName" "ModName"Windows (PowerShell):
.\scripts\init.ps1
# or:
.\scripts\init.ps1 "AuthorName" "ModName"This will:
- Replace mod name and author placeholders across all files
- Initialise git submodules (CommonLibSSE-NG + vcpkg)
- Bootstrap vcpkg
- Copy
.env.example→.envwith a reminder to fill in your paths
Edit the .env file created by the init script and set SKYRIM_MODS_FOLDER to your mod manager's staging folder:
# Vortex (Linux, Steam):
SKYRIM_MODS_FOLDER=$HOME/.local/share/Steam/steamapps/common/Vortex Mods/skyrimse
# MO2 (Linux):
# SKYRIM_MODS_FOLDER=$HOME/MO2/mods./scripts/build.sh
# or directly:
cmake --preset release-linux
cmake --build --preset release-linuxThe DLL lands in build/release-linux/ExampleMod.dll.
./scripts/deploy.sh
# or directly (deploy.sh sources .env for you; cmake does not):
source .env && cmake --workflow --preset deployThis configures, builds, and copies ExampleMod.dll + ExampleMod.pdb directly into:
$SKYRIM_MODS_FOLDER/ExampleMod/SKSE/Plugins/
Vortex will detect the new mod folder automatically. Enable it in Vortex, then launch Skyrim.
On subsequent builds (no config change needed):
./scripts/deploy.shcmake --preset release-windows
cmake --build --preset release-windowsThe DLL lands in build/msvc/Release/ExampleMod.dll.
Unit tests run as a native Windows executable via the test-windows preset:
cmake --preset test-windows
cmake --build --preset test-windows
ctest --preset test-windowsTests live in test/ and use Catch2. Only pure-logic code (no RE::/SKSE:: APIs) can be tested this way. See src/Utils.h and test/ExampleTests.cpp for the pattern.
Prerequisites:
go install github.com/evilmartians/lefthook@latestclang-format(part of LLVM — already required for development)clang-tidy(part of LLVM — already required for development)cmake-format(sudo pacman -S cmake-formaton Arch/CachyOS;pip install cmakelangelsewhere)shellcheck(sudo pacman -S shellcheckon Arch/CachyOS)
lefthook installCMake writes compile_commands.json to the build directory automatically.
Copy or symlink it to the project root so clangd picks it up:
# After configuring:
ln -sf build/release-linux/compile_commands.json compile_commands.jsonThe .clangd file already sets --target=x86_64-pc-windows-msvc so clangd
resolves Windows headers correctly on Linux.
Recommended Neovim plugins: nvim-lspconfig
with clangd, and clangd_extensions.nvim.
git submodule update --remote lib/commonlibsse-ng
git add lib/commonlibsse-ng
git commit -m "chore: update CommonLibSSE-NG submodule"The <!-- nexus:start/end --> block at the top of README.md is the source of truth for Requirements, Installation, and Compatibility. docs/nexus-page.md holds the rest of the BBCode page (overview, tagline, credits).
To update the Nexus page:
- Edit Requirements/Installation/Compatibility inside the
<!-- nexus:start/end -->block inREADME.md. - Edit overview, tagline, and credits directly in
docs/nexus-page.md.Do not edit the
<!-- generated:start/end -->block indocs/nexus-page.md— it is overwritten every time the script runs. - Generate the combined BBCode output:
python3 scripts/generate-nexus-page.py
# Or pipe straight to the clipboard:
python3 scripts/generate-nexus-page.py | xclip -selection clipboard # Linux
python3 scripts/generate-nexus-page.py | pbcopy # macOS- Paste the output into the Nexus Mods page editor.
| Workflow | Trigger | What it does |
|---|---|---|
setup.yml |
First push in a repo created from this template | Renames placeholders using the repo name, then self-deletes |
ci.yml |
PRs to main touching src/, test/, cmake/, vcpkg.json, CMakeLists.txt, CMakePresets.json |
clang-format (ubuntu) → test + build (windows, parallel) → clang-tidy (windows) |
release.yml |
Push of a v* tag |
Builds, packages via scripts/package.sh, publishes a GitHub Release with zip + PDB |
nexus-upload.yml |
Release published or manual workflow_dispatch |
Downloads release zip, generates cliff release notes, uploads to Nexus Mods |
lint.yml |
PRs touching scripts/ |
Runs shellcheck on shell scripts |
pr-title.yml |
PR opened/edited/reopened/synchronize | Checks PR title follows Conventional Commits (feat, fix, chore, refactor) |
nexus-upload.yml triggers automatically when a GitHub Release is published, or can be run manually via workflow_dispatch with a version input.
Prerequisites (one-time setup):
- Upload your first file manually via the Nexus Mods web UI — this creates the file group.
- Note the
file_group_idfrom the URL or mod manager. - Add to your repository:
- Secret
NEXUSMODS_API_KEY— your Nexus Mods API key (Settings → Secrets → Actions) - Variable
NEXUSMODS_FILE_GROUP_ID— the file group ID (Settings → Variables → Actions)
- Secret
MIT — see LICENSE.