commit 9dbcc7d926c56054299b5b2740e0376eb360dfcf Author: Andrew Zambazos Date: Wed Jul 1 17:39:42 2026 +1200 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. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7e6c689 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba47615 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..00ce22b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ThirdParty/CommonLibF4"] + path = ThirdParty/CommonLibF4 + url = https://github.com/libxse/commonlibf4.git diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ac191aa --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4fe50f9 --- /dev/null +++ b/README.md @@ -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 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 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 }) +``` diff --git a/ThirdParty/.gitkeep b/ThirdParty/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/include/PluginName.h b/include/PluginName.h new file mode 100644 index 0000000..6d8b9bc --- /dev/null +++ b/include/PluginName.h @@ -0,0 +1,7 @@ +#pragma once + +namespace PluginName +{ + inline constexpr auto NAME = "F4SEPluginTemplate"; + inline constexpr auto VERSION = "0.1.0"; +} diff --git a/scripts/install-dev.ps1 b/scripts/install-dev.ps1 new file mode 100644 index 0000000..2973ba4 --- /dev/null +++ b/scripts/install-dev.ps1 @@ -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" diff --git a/scripts/package.ps1 b/scripts/package.ps1 new file mode 100644 index 0000000..169251c --- /dev/null +++ b/scripts/package.ps1 @@ -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" diff --git a/scripts/rename-template.ps1 b/scripts/rename-template.ps1 new file mode 100644 index 0000000..ddfa3a7 --- /dev/null +++ b/scripts/rename-template.ps1 @@ -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" diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..345b563 --- /dev/null +++ b/src/main.cpp @@ -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; +} diff --git a/src/pch.h b/src/pch.h new file mode 100644 index 0000000..aa15a38 --- /dev/null +++ b/src/pch.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include + +#include +#include + +using namespace std::literals; diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..3c89438 --- /dev/null +++ b/xmake.lua @@ -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)