Add F4SE plugin template

Create a minimal Fallout 4 F4SE/CommonLibF4 starter repo with build, packaging, install, and rename scripts, plus initial plugin source, headers, and xmake configuration.
This commit is contained in:
2026-07-01 17:39:42 +12:00
commit 9dbcc7d926
13 changed files with 417 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = crlf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{cpp,h,hpp}]
indent_style = tab
indent_size = 4
[*.lua]
indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false
+35
View File
@@ -0,0 +1,35 @@
# IDE / editor
.cache/
.idea/
.vs/
.vscode/.cache/
# xmake
.xmake/
build*/
vs*/
compile_commands.json
# Visual Studio generated files
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
# Build output
*.obj
*.pdb
*.ilk
*.dll
*.lib
*.exp
*.exe
*.zip
# Local config
local.json
dev-config.json
# OS junk
.DS_Store
Thumbs.db
+3
View File
@@ -0,0 +1,3 @@
[submodule "ThirdParty/CommonLibF4"]
path = ThirdParty/CommonLibF4
url = https://github.com/libxse/commonlibf4.git
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 YourName
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+199
View File
@@ -0,0 +1,199 @@
# F4SEPluginTemplate
Minimal reusable Fallout 4 F4SE plugin template using CommonLibF4.
This template is intentionally lightweight. Add project-specific dependencies such as Ultralight, GameNetworkingSockets, ImGui, or custom SDKs inside each actual mod repo instead of adding them here.
## Requirements
- Windows
- Visual Studio 2022 with C++ tools, or another MSVC/Clang-CL C++23 toolchain
- xmake 3.0.0 or newer
- Git
- Fallout 4 Script Extender installed for testing
## Create a new mod repo from this template
```powershell
git clone <your-template-repo-url> MyNewF4SEMod
cd MyNewF4SEMod
git submodule update --init --recursive
.\scripts\rename-template.ps1 -NewName "MyNewF4SEMod" -Author "YourName"
```
Then commit the renamed version as the starting point for the new mod.
## Build
```powershell
xmake build
```
The built plugin is copied to:
```text
package/F4SE/Plugins/
```
## Generate Visual Studio project
```powershell
xmake project -k vsxmake
```
## Package for mod managers
```powershell
.\scripts\package.ps1 -OutputName "MyNewF4SEMod"
```
This creates a zip with the expected layout:
```text
F4SE/Plugins/MyNewF4SEMod.dll
F4SE/Plugins/MyNewF4SEMod.pdb
```
## Dev install to Fallout 4
```powershell
.\scripts\install-dev.ps1 -Fallout4Path "C:\Program Files (x86)\Steam\steamapps\common\Fallout 4"
```
## Repo layout
```text
src/ C++ plugin source
include/ Public/internal headers
ThirdParty/CommonLibF4/ CommonLibF4 submodule
package/ Staged mod-manager package output
scripts/ Helper scripts
```
## Optional Modules
This template intentionally keeps the base plugin minimal. Only add extra third-party modules when a specific mod needs them.
The recommended convention is to place optional dependencies in `ThirdParty/`:
```text
ThirdParty/
├─ CommonLibF4/
├─ xbyak/
├─ Ultralight-SDK/
├─ framework-F4-Conversion/
├─ spdlog/
└─ tomlplusplus/
```
### Xbyak
Use Xbyak when the plugin needs low-level hook helpers, runtime assembly generation, trampoline work, or custom patching logic. Xbyak is a header-only x86/x64 JIT assembler library.
```powershell
git submodule add https://github.com/herumi/xbyak.git ThirdParty/xbyak
git submodule update --init --recursive
```
Example `xmake.lua` include:
```lua
add_includedirs("ThirdParty/xbyak", { public = true })
```
### Ultralight SDK
Use Ultralight when the mod needs HTML/CSS/JavaScript-driven UI, such as a custom browser, server browser, launcher-like interface, or rich overlay. Ultralight is a GPU-accelerated HTML renderer with C and C++ support.
```powershell
git submodule add <your-ultralight-sdk-repo-url> ThirdParty/Ultralight-SDK
git submodule update --init --recursive
```
Example `xmake.lua` include:
```lua
add_includedirs("ThirdParty/Ultralight-SDK/include", { public = true })
```
Linking Ultralight usually requires additional `.lib` files and runtime `.dll` files. Keep those project-specific rather than adding them to the base template.
### framework-F4-Conversion
Use `framework-F4-Conversion` only for mods that depend on NomadsReach's Fallout 4 conversion/UI framework. This should not be required for normal F4SE plugins.
```powershell
git submodule add https://github.com/NomadsReach/framework-F4-Conversion.git ThirdParty/framework-F4-Conversion
git submodule update --init --recursive
```
Example `xmake.lua` include:
```lua
add_includedirs("ThirdParty/framework-F4-Conversion/include", { public = true })
```
Only wire this into the build once the mod actually uses it.
### spdlog
Use `spdlog` if the plugin needs more advanced logging than the default template logging setup, such as rotating logs, multiple sinks, custom formatting, or async logging.
```powershell
git submodule add https://github.com/gabime/spdlog.git ThirdParty/spdlog
git submodule update --init --recursive
```
Example `xmake.lua` include:
```lua
add_includedirs("ThirdParty/spdlog/include", { public = true })
```
### toml++
Use `toml++` if the plugin needs structured configuration files, for example:
```text
Data/F4SE/Plugins/MyPlugin.toml
```
```powershell
git submodule add https://github.com/marzer/tomlplusplus.git ThirdParty/tomlplusplus
git submodule update --init --recursive
```
Example `xmake.lua` include:
```lua
add_includedirs("ThirdParty/tomlplusplus/include", { public = true })
```
### SimpleIni
Use SimpleIni for very small `.ini` configuration files. This is useful when the plugin only needs a few basic settings and a TOML parser would be overkill.
```powershell
git submodule add https://github.com/brofield/simpleini.git ThirdParty/SimpleIni
git submodule update --init --recursive
```
Example `xmake.lua` include:
```lua
add_includedirs("ThirdParty/SimpleIni", { public = true })
```
### fmt
Use `fmt` if the plugin needs standalone formatting utilities. Some logging libraries already include or depend on `fmt`, so avoid adding it separately unless the project directly needs it.
```powershell
git submodule add https://github.com/fmtlib/fmt.git ThirdParty/fmt
git submodule update --init --recursive
```
Example `xmake.lua` include:
```lua
add_includedirs("ThirdParty/fmt/include", { public = true })
```
View File
+7
View File
@@ -0,0 +1,7 @@
#pragma once
namespace PluginName
{
inline constexpr auto NAME = "F4SEPluginTemplate";
inline constexpr auto VERSION = "0.1.0";
}
+15
View File
@@ -0,0 +1,15 @@
param(
[Parameter(Mandatory=$true)]
[string]$Fallout4Path
)
$ErrorActionPreference = "Stop"
$root = Split-Path -Parent $PSScriptRoot
$plugins = Join-Path $root "package\F4SE\Plugins"
$target = Join-Path $Fallout4Path "Data\F4SE\Plugins"
New-Item -ItemType Directory -Force -Path $target | Out-Null
Copy-Item -Path (Join-Path $plugins "*") -Destination $target -Force
Write-Host "Installed plugin files to $target"
+16
View File
@@ -0,0 +1,16 @@
param(
[string]$OutputName = "F4SEPluginTemplate"
)
$ErrorActionPreference = "Stop"
$root = Split-Path -Parent $PSScriptRoot
$packageDir = Join-Path $root "package"
$outFile = Join-Path $root "$OutputName.zip"
if (Test-Path $outFile) {
Remove-Item $outFile -Force
}
Compress-Archive -Path (Join-Path $packageDir "*") -DestinationPath $outFile
Write-Host "Created $outFile"
+23
View File
@@ -0,0 +1,23 @@
param(
[Parameter(Mandatory=$true)]
[string]$NewName,
[string]$Author = "YourName"
)
$ErrorActionPreference = "Stop"
$root = Split-Path -Parent $PSScriptRoot
$files = Get-ChildItem -Path $root -Recurse -File | Where-Object {
$_.FullName -notmatch "\\\.git\\" -and
$_.FullName -notmatch "\\lib\\commonlibf4\\"
}
foreach ($file in $files) {
$content = Get-Content $file.FullName -Raw
$content = $content.Replace("F4SEPluginTemplate", $NewName)
$content = $content.Replace("YourName", $Author)
Set-Content -Path $file.FullName -Value $content -NoNewline
}
Write-Host "Renamed template references to $NewName"
+33
View File
@@ -0,0 +1,33 @@
#include "pch.h"
namespace
{
void MessageHandler(F4SE::MessagingInterface::Message* a_message)
{
if (!a_message) {
return;
}
switch (a_message->type) {
case F4SE::MessagingInterface::kGameLoaded:
REX::INFO("Game loaded");
break;
default:
break;
}
}
}
F4SE_PLUGIN_LOAD(const F4SE::LoadInterface* a_f4se)
{
F4SE::Init(a_f4se);
REX::INFO("F4SEPluginTemplate loaded");
const auto messaging = F4SE::GetMessagingInterface();
if (messaging) {
messaging->RegisterListener(MessageHandler);
}
return true;
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <RE/Fallout.h>
#include <F4SE/F4SE.h>
#include <REX/REX.h>
#include <string_view>
#include <utility>
using namespace std::literals;
+37
View File
@@ -0,0 +1,37 @@
-- F4SEPluginTemplate
-- Minimal reusable Fallout 4 F4SE plugin template using CommonLibF4.
includes("ThirdParty/CommonLibF4")
set_project("F4SEPluginTemplate")
set_version("0.1.0")
set_license("MIT")
set_languages("c++23")
set_warnings("allextra")
add_rules("mode.debug", "mode.releasedbg")
add_rules("plugin.vsxmake.autoupdate")
target("F4SEPluginTemplate")
add_rules("commonlibf4.plugin", {
name = "F4SEPluginTemplate",
author = "YourName",
description = "Fallout 4 F4SE plugin template using CommonLibF4"
})
add_files("src/**.cpp")
add_headerfiles("src/**.h")
add_headerfiles("include/**.h")
add_includedirs("src", "include")
set_pcxxheader("src/pch.h")
after_build(function (target)
local outdir = path.join(os.projectdir(), "package", "F4SE", "Plugins")
os.mkdir(outdir)
os.cp(target:targetfile(), outdir)
local pdb = path.join(target:targetdir(), target:basename() .. ".pdb")
if os.isfile(pdb) then
os.cp(pdb, outdir)
end
end)