
Adds a "Mods" tab to Subnautica 2's settings menu where other mods register their own settings.. Required by mods that hook into it. Some settings can persist between sessions and apply live without reloading a save. Once installed, any supporting mod will populate the tab with i
This mod is hosted on Nexus Mods by JustChaldea. Clicking below will open the official mod page in a new tab.
Download on Nexus ModsYou will leave this site. Downloads and installs are handled by Nexus Mods.
Installation
Requires UE4SS for Subnautica 2, install that first.
Grab the latest SN2ModSettings-vX.Y.zip. With Vortex (or any mod manager), just install it. For manual install, extract into <Subnautica 2 install> Subnautica2 Binaries Win64 ue4ss Mods . Either way the mod ends up in ue4ss Mods SN2ModSettings .
For Linux/Proton/Steam Deck users, see this post.
On its own this adds nothing visible. The Mods tab only appears once at least one supported mod has registered settings.
For mod developers
Message me if your project uses ModSettings so I can link it at the bottom of the page! :D
SN2ModSettings adds a Mods tab to the in-game settings screen. Your mod drops a Lua manifest in SN2ModSettings/registrations/ and the library renders the UI using the same widget classes the vanilla menu uses. Sliders for numbers, toggles for booleans, keybinds for keys, with optional enabled_by gating so a master toggle can gray out its dependents.
Each registered mod gets a section with your display string as the header. Sections sort alphabetically by display (falling back to name), with the library's own "Mod Settings" pinned on top so the Check for updates toggle is always visible. The description field shows in the side panel whenever the user focuses a setting.
If your manifest includes version, the section header reads "<display> - vX.Y.Z". Add github and the library polls the GitHub Releases API once per session (cached 3 hours) and appends "- Update Available! vA.B.C" when there's a newer tag. Add nexus_id and the library auto-injects a "View on Nexus Mods" row at the bottom of your section.
User changes mirror to a UE4SS SharedVariable (in-memory, cross-mod) and to SN2ModSettings/saved/<your-mod>.lua for persistence. Your mod reads the SharedVariable in a LoopAsync and applies the change live.
UE4SS isolates each mod's _G, so globals and require can't share state between mods. SharedVariable is the cross-mod channel.
File layout
ue4ss/Mods/SN2ModSettings/
├── registrations/
│ └── YourMod.lua <- you write this at load time
└── saved/
└── YourMod.lua <- library writes user-adjusted values here
Writing your manifest
Pick a short, stable name. It's the filename for both your registration and saved values; renaming it after release orphans saved settings.
local MANIFEST_PATH = "./ue4ss/Mods/SN2ModSettings/registrations/MyMod.lua"
local function write_text(path, body)
local dir = path:match("(.*[/ ])")
os.execute('mkdir "' .. dir:gsub("/", " ") .. '" 2>nul')
local f = io.open(path, "w")
if not f then return false end
f:write(body); f:close()
return true
end
local manifest = [=[
return {
name = "MyMod",
display = "My Mod",
version = "1.0.0", -- optional: shown in section header
github = "you/MyMod-repo", -- optional: enables update check
nexus_id = "123", -- optional: enables Nexus button
settings = {
{ key="Enabled", title="Enable mod",
description="Master switch. When off, the sliders below gray out.",
type="toggle", default=true },
{ key="Intensity", title="Intensity (%)",
description="How strong the effect is. 100 = vanilla.",
type="slider", default=1.0, min=0.0, max=2.0, step=0.1,
format="percent", enabled_by="Enabled" },
{ key="FOV", title="Field of View",
description="Camera FOV in degrees.",
type="slider", default=90.0, min=60.0, max=120.0, step=1.0,
format="integer", enabled_by="Enabled" },
{ key="Hotkey", title="Toggle Hotkey",
description="Key pressed to toggle the effect.",
type="keybind", default="F" },
},
}
]=]
write_text(MANIFEST_PATH, manifest)
Top-level manifest keys:
• name: internal id and saved-file basename. Don't change it after release.[/*]
• display: section header shown in the Mods tab.[/*]
• version (optional, string): your mod's current version. Shown in the header and used as the baseline for update comparison.[/*]
• github (optional, string): "owner/repo" path for update checking. Tag comparison is integer-component lexicographic; stick to semver-ish tags.[/*]
• nexus_id (optional, string): your Nexus mod id, opens https://www.nexusmods.com/subnautica2/mods/<nexus_id> in the user's browser.[/*]
• settings: array of setting definitions.[/*]
Per-setting keys:
• key: identifier used as the field name in the saved file.[/*]
• title: label next to the widget. Falls back to key.[/*]
• description: shown in the side panel when the user focuses the setting.[/*]
• type: "slider", "toggle", or "keybind".[/*]
• default: number for sliders, boolean for toggles, FKey name string for keybinds (e.g. "N", "F5").[/*]
• enabled_by (optional): another setting's key in the same mod. When that one is falsy, this widget grays out.[/*]
Sliders also take min, max, step, and format:
• step: increment between values. Defaults to 0.1 for float/percent and 1.0 for integer.[/*]
• format: "float" (default, raw decimal), "integer" (rounded), or "percent" (value × 100 with % suffix).[/*]
Reading saved values
Each setting mirrors to a UE4SS SharedVariable keyed SN2ModSettings/<your-mod>/<setting-key> as a native Lua primitive. No serialization, no load(). The library seeds defaults at startup so the first read returns valid data.
local Config = {
Enabled = true, -- defaults; overwritten by SharedVariable reads
Intensity = 1.0,
}
local function LoadFromShared()
if not ModRef then return false end
local changed = false
for k in pairs(Config) do
local v = ModRef:GetSharedVariable("SN2ModSettings/MyMod/" .. k)
if v ~= nil and type(v) == type(Config[k]) and Config[k] ~= v then
Config[k] = v
changed = true
end
end
return changed
end
LoadFromShared()
LoopAsync(1000, function()
if not LoadFromShared() then return end
ExecuteInGameThread(function()
print("[MyMod] Config changed, reapplying")
-- apply Config.Enabled / Config.Intensity / etc.
end)
end)
The library also writes saved/<your-mod>.lua for persistence. Read the file directly if you want a debugging view, but SharedVariable is the cheap path for live values.
Keybinds
New in 1.1.1. type="keybind" renders a vanilla-styled binding row with a primary selector and an alternative selector, same widget the vanilla Controls tab uses. The user clicks a selector and presses any key; the captured FKey name is saved as a string ("N", "F5", "LeftMouseButton", etc.).
Each keybind setting gives you two saved entries:
• <key> - primary, defaults to your manifest default.[/*]
• <key>_Alt - alternative, defaults to "" (unset).[/*]
Both publish through the same SharedVariable channel as toggles and sliders.
Consuming the binding (dispatcher pattern): UE4SS doesn't expose UnregisterKeyBind, so you can't bind, unbind, and rebind in response to a UI change. Instead, register every key you might bind once at startup. Each handler reads config.Hotkey / config.Hotkey_Alt at call time and matches on the string name of the key it was registered for. Rebinding via the UI takes effect immediately, no restart. Per-keypress cost: a couple of SharedVariable reads and string compares.
local keyMap = {
A = Key.A, B = Key.B, C = Key.C, D = Key.D, E = Key.E,
F = Key.F, G = Key.G, H = Key.H, I = Key.I, J = Key.J,
K = Key.K, L = Key.L, M = Key.M, N = Key.N, O = Key.O,
P = Key.P, Q = Key.Q, R = Key.R, S = Key.S, T = Key.T,
U = Key.U, V = Key.V, W = Key.W, X = Key.X, Y = Key.Y, Z = Key.Z,
F1 = Key.F1, F2 = Key.F2, F3 = Key.F3, F4 = Key.F4,
F5 = Key.F5, F6 = Key.F6, F7 = Key.F7, F8 = Key.F8,
F9 = Key.F9, F10 = Key.F10, F11 = Key.F11, F12 = Key.F12,
}
for keyName, keyConst in pairs(keyMap) do
local captured_name = keyName
RegisterKeyBind(keyConst, function()
ExecuteInGameThread(function()
local pri = Config.Hotkey
local alt = Config.Hotkey_Alt
if captured_name == pri
or (alt ~= "" and captured_name == alt) then
DoTheThing()
end
end)
end)
end
If you don't need live rebind, you can read the saved key once at startup and RegisterKeyBind(Key[Config.Hotkey], handler) the old way. The user has to restart the game to apply a UI change in that case.
Notes:
• The selector shows the saved key via the NoKeySpecifiedText placeholder, so the user always sees their current binding even before they've captured anything in this session.[/*]
• Saved "" for the alt slot means "no alternative". Short-circuit on empty: alt ~= "" and captured_name == alt.[/*]
• To clear a binding the user rebinds to a different key. Saving an empty primary isn't supported (the freshly-constructed widget reads as unset too, and the library would silently wipe valid bindings on first render).[/*]
• Pick your default from the FKey name list: letters "A"-"Z", function keys "F1"-"F12", mouse buttons "LeftMouseButton"/"RightMouseButton"/"MiddleMouseButton", arrows "Left"/"Right"/"Up"/"Down", etc.[/*]
Supported widgets
slider, toggle, keybind. Planned but not yet wired up: combobox (dropdown), button (action trigger), radio (radio group). Vanilla SN2 uses all of those so the widget classes already exist.
Gotchas
• The library seeds a SharedVariable for every setting at startup using your default, so your in-Lua defaults only matter for the brief window between mod load and first SharedVariable read.[/*]
• Slider values come back as Lua numbers, toggles as booleans, keybinds as strings. Round on your side if you want an integer slider.[/*]
• enabled_by is UI only. It grays out the widget but doesn't change what your mod reads. Master toggle off means your mod has to gate its own behavior.[/*]
• The library writes the saved file the moment something changes, but applying the change is your mod's job. For GAS attributes that means the OnRep_<FieldName> aggregator-refresh trick; SN2-DrainRates has a reference.[/*]
• Don't rename your mod's name after release. It's the saved-file key and renaming orphans every user's saved settings.[/*]
Mods using Mod Settings:
Too Many Divers - Beyond 4 Player Co-op Unlocker
Comfort Tweaks - FoV - PDA pause
Configurable Flashlight
Cheat Toggles
No bugs reported yet. If you hit a problem, let the author know.
Be the first to rate this mod.
Sign in to leave a rating.
You might also like
No comments yet. Be the first to share your thoughts.