| Commit message (Collapse) | Author | Age | Files | Lines |
| | |
|
| |
|
|
|
|
|
|
|
| |
The builder image is pulled from GHCR and reused across CI runs, so
Dockerfile changes to its pip install line don't take effect until
docker.yml is manually triggered. Move the install into the CI build
step so PRs that add new python deps work without an image rebuild.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
After the first launch, doc/Stats -> overwrite/Stats (and siblings) are
deployed as directory symlinks. The next mappings() pass rglob'd
overwrite/ and emitted a Mapping for every nested entry — destinations
resolved back through the parent symlink to the source, so both
create_mapping's unlink+symlink_to and fuseconnector's
create_directory_symlink ended up operating on a path that physically
*was* the source, producing self-pointing symlinks and wiping the
directory contents on the next launch.
Emit one directory mapping per redirected top-level subdir instead. The
parent symlink covers everything underneath via normal path resolution,
so no nested mapping is generated and the alias condition can't arise.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Fluorine Manager launcher .desktop declared
MimeType=x-scheme-handler/nxm;x-scheme-handler/modl;, advertising itself
as a handler alongside the real mo2-nxm-handler.desktop. The launcher's
Exec line has no %u, so when xdg-desktop-portal launched it the URL was
dropped and MO2 came up with no args — which then hit the "instance
already running" path against the live primary. Drop the bad MimeType
from both the tarball template (data/icons/) and the .bin installer's
inline desktop entry in docker/build-inner.sh.
That alone fixes new installs, but xdg-desktop-portal remembers user
picks in its permission store: anyone who'd already selected Fluorine
Manager in the chooser dialog had it stuck as their always-use app
(count=3/3 = silent always-launch), and the bad pick survived removing
the MimeType. On startup the nxm handler now fires a DBus
DeletePermission against PermissionStore for com.fluorine.manager on
both schemes, so existing users self-heal on the next launch. The call
is fire-and-forget — a missing entry is the expected steady state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
processSocketData iterated socket->canReadLine() and emit'd nxmReceived
inside the loop. nxmReceived routes to OrganizerCore which can show a
modal dialog (the "Wrong Game" warning fires whenever the link's game
domain doesn't match the active instance). Modal dialogs spin the event
loop, and the loop processes the disconnected → deleteLater queued for
the same socket. The socket gets freed, then control returns to the
while loop and the next canReadLine() runs on a dangling pointer.
Repro hits cleanly when an nxm:// for a non-matching game arrives — the
"Wrong Game" dialog appears and as soon as the user dismisses it MO2
crashes with SIGSEGV. Read into a QStringList first, then emit after
the read loop ends so the socket is no longer touched once handlers run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Walked into this when a download manager click silently popped "An
instance of Mod Organizer is already running" with no MO2 actually
visible. State on disk: SysV shm segment from a prior MO2 still there
with nattch=0 (kernel won't reap until IPC_RMID, which only the dead
creator could call) plus the orphaned /tmp/mo-<key> socket file. The
stale-recovery path was attaching, finding no listener via
primaryAlive(), then trying to detach+create — which kept failing
because the corpse segment still occupied the key — and falling all the
way through to "re-attach as secondary". Result: the new launch flagged
itself ephemeral, forwardToPrimary() had nothing to send, and the user
got the ghost dialog.
The shm is only advisory; the unix socket listener is the real lock. If
primaryAlive() said no one's listening, we should run as primary even
when we can't reclaim the orphaned segment. Also re-probe on listen()
failure so a real race (another process becoming primary between our
probe and our listen) demotes us cleanly instead of leaving us as a
broken primary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
primaryAlive() opens a QLocalSocket then disconnects without writing
any payload to verify a primary is listening. The primary's
receiveMessage() saw the empty PeerClosedError connection and logged
"failed to receive data from secondary process" plus a popup, even
though the real NXM/shortcut message arrived on the next connection
and was processed correctly.
Treat zero-byte PeerClosedError/UnknownSocketError closes as the
expected probe pattern (debug log, no error). Other errors still
report loudly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Adds Executable::UseVfsBridge flag (off by default) and a "Use VFS
bridge (experimental)" checkbox in the executables editor next to
"Open in terminal" / "Use Proton". The flag plumbs through to
SpawnParameters::useVfsBridge and gates the FLUORINE_VFS_INDEX/DATA_DIR/
MOUNT env vars in spawn().
Without those env vars, the staged fluorine_vfs.dll still AppInit-loads
into every PE process in the prefix, but its hook_worker bails at
load_index() and installs zero hooks — equivalent to the bridge being
disabled. No separate gating is needed in protonlauncher.cpp because
its hid.dll-proxy staging and prewarm are already conditioned on
vfsBridgeIndexPresent(envVars).
Existing instances load with useVfsBridge=false (key absent from
ModOrganizer.ini; the loader treats absence as false). New executables
created via the plugin path also default off. Users who want the perf
win for a particular game tick the box; everyone else launches via the
correct, slower Wine + FUSE path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three bugs in the PE-side bridge collided to crash NSPBR ~54s after
launch with an AV in NPCWaterAIFix.dll. Root cause: the bridge said
STATUS_OBJECT_NAME_NOT_FOUND for files that exist on disk but are not
in the static index, masquerading as ground truth.
1. load_index() filtered every entry whose real_path didn't start
with '/'. That dropped every is_backing=true entry — for NSPBR,
the entire vanilla Skyrim base layer (Skyrim.esm, all base BSAs,
Video/BGS_Logo.bik). Now accept both forms; backing entries get
real_nt=NULL and exist only for query / negcache logic.
2. The "open negcache" comment claimed the index was authoritative
for the FUSE mount tree. With (1), it is not. Comment rewritten;
the comment about attr_cache_invalidate (which never existed) is
also corrected to point at attr_cache_mark_exists.
3. Hook_NtCreateFile and Hook_NtOpenFile returned ENOENT on
`!attr_cache_lookup() || !cexists` — the "no entry at all" case
got the same treatment as a confirmed-negative cache hit. Plugin
code that calls CreateFile without a preceding GetFileAttributes
would never reach Real_NtCreateFile. Now mirrors what
try_kernel32_attr_hit already did: only ENOENT on confirmed
negatives; pure cache miss falls through to Real_, which serves
correctly via Wine + FUSE.
Also includes the previously-uncommitted mount-scope fast gate so
out-of-mount paths (system DLLs, registry-backed objects, unrelated
drives) bypass the hook entirely with a single prefix compare on the
input string — no normalization, no cache lookup, no chance of a
wrong answer.
Verified by a self-contained 44-assertion C reproduction at
/home/luke/builds/fluorine-vfs-tests/test_negcache_logic.c (six
scenarios: backing-filter exclusion, first-open without pre-query,
pre-query masking, SKSE plugin probes, post-fix Skyrim recovery,
nt_path_to_key edge cases).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Bethesda's engine rewrites Plugins.txt as part of its shutdown sequence.
On a clean exit it preserves the active set; on a crash it can serialize
a partially cleared set — every plugin name still listed, but most
without their leading '*'. The previous syncPluginsBack code copied that
file back to the profile faithfully, after which refreshESPList +
savePluginList re-derived state and persisted the broken active list.
NSPBR observed this as 1170 active plugins collapsing to 54.
Two independent guards:
- afterRun() now skips the post-launch sync when exitCode != 0. Non-zero
is a strong "do not trust the prefix file" signal.
- syncPluginsBack() compares starred-line counts in candidate vs profile.
Refuses the copy when the candidate would drop the active count by
more than 30% relative AND more than 10 plugins absolute. Belt and
suspenders for cases where the engine catches its own crash and
exits 0 anyway.
Both guards apply regardless of the VFS bridge state and protect every
launch on every modlist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Linux QSharedMemory uses SysV segments that survive process crashes.
On the next launch, m_SharedMem.create() fails with AlreadyExists and
the constructor would either treat the corpse as a live primary
(blocking the user behind a ghost) or throw. The unix socket file is
similarly left behind and would make QLocalServer::listen() fail with
AddressInUseError if reclaim succeeded.
Now: probe the primary's listener via QLocalSocket::connectToServer
before believing the segment is alive. If the probe fails, detach and
re-create. If create then fails (another attached client keeps refcount
> 0), re-attach so the constructor returns in a usable secondary state
instead of silently no-op'ing every later sendMessage. Also call
QLocalServer::removeServer() before listen() to clear the orphaned
socket file when we did successfully reclaim.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Saves the merged VfsTree to ~/.local/share/fluorine/vfs_cache/ keyed by
data_dir + mods + overwrite. On a hit, mounting reads the tree from a
single binary file instead of re-walking every mod, which dominates
mount time on heavy modlists.
Validation: modlist.txt mtime+size, data/overwrite dir mtime, mod-dir
mtimes in priority order. Any drift invalidates. Session-scoped state
(extra-file injection, plugin timestamp stamping) is re-applied after
load and never cached.
rebuild() also refreshes the cache so the next cold mount hits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Issue #70: rename button was disabled for portable instances; the upstream
restriction assumed portable = MO2-binary dir, which doesn't apply here
(binary lives in ~/.local/share/fluorine/bin, instance dir is separate).
Allow rename when not active, and update the portable registry so the
renamed dir keeps showing up.
Issue #67: QLabel::setOpenExternalLinks(true) and friends route through
QDesktopServices::openUrl, which forks xdg-open with our bundled Qt env
inherited — silently fails. Install a global url handler that defers to
shell::Open, which already scrubs LD_LIBRARY_PATH/QT_PLUGIN_PATH.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Local stable builds picked up host CPU flags (AVX-512) and shipped
binaries that crashed on user CPUs without those instructions. CI runs
on generic GitHub-hosted x86-64 runners, so all release builds need to
go through it now.
- Trigger workflow on push of any v* tag in addition to main.
- Resolve channel from the ref: tags/v* -> stable, otherwise beta.
- Tarball name follows channel: fluorine-manager-<version>.tar.gz on
stable, fluorine-manager-beta.tar.gz on beta.
- Beta publish step gated on channel==beta to avoid double-publishing
when a tag also lives on main.
- New "Publish stable release" step creates/updates a release at the
tag, marks it --latest, and attaches the versioned tarball.
To cut v0.2.0: `git tag v0.2.0 && git push origin v0.2.0`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
Beta titlebar was "Fluorine Manager beta @ <hash>" — the version line
(0.1.x vs 0.2.x) was invisible. Switch to "<semver>-beta @ <hash>" so
the running build's major/minor is obvious without opening the About
dialog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
The wineboot -u deadlock workaround (waitforexitandrun + ntsync env nudge)
already lives in prefixsetuprunner, so the catch-all Proton 11 skip in
findSteamProtons is no longer needed. Users on stock Proton 11 are now
selectable alongside Proton 10 / Experimental / GE-Proton.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- OrganizerCore: add fluorineUpdater() getter + checkForSlrUpdates()
fired from startup checkForUpdates(); detached thread runs downloadSlr
when SLR already installed so steamrt4 BUILD_ID drift is picked up
without a launch-time stall.
- MainWindow: connect FluorineUpdater::updateAvailable to existing
updateAvailable() slot so the statusbar badge + actionUpdate light up;
on_actionUpdate_triggered routes to Settings -> Updates when a Fluorine
update is pending (the MO2 self-updater path is no-op'd).
- MainWindow launch path: drop !isSlrInstalled() short-circuit; fresh
installs still get the progress dialog, up-to-date checks rely on the
startup background pass.
- SettingsDialog: selectTabByLabel() so MainWindow can open Updates tab.
- Launcher: rm -rf update-staging/ once a new bundle has synced into
bin/, so the in-app updater doesn't accumulate stale extracts.
- CMakeLists.txt: 0.1.4 -> 0.2.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
m_Tokens on NXMAccessManager is std::optional<NexusOAuthTokens> (the
NexusOAuthTokens member belongs to ValidationAttempt). The new legacy
API key migration code accessed .accessToken/.apiKey directly on the
optional, breaking the build.
Use the optional's check + arrow access throughout, and re-check it
inside the network reply continuation since the user could clear
credentials before the request resolves.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Some plugins (Plugin Browser for MO2 and a number of older Python
plugins) read only the legacy `apiKey` field. The OAuth login flow
populates `accessToken` but never the legacy key, so those plugins fail
with "User API Key is missing" even after a successful sign-in.
Nexus' v1 `/users/validate.json` endpoint accepts the OAuth bearer token
and returns the user's personal API key in the `key` field. We hit it
once after `notifyTokens()` (fresh login) and once via the constructor
queued call (already-logged-in users on first launch with this build),
then persist the result through `GlobalSettings::setNexusApiKey()`.
The fetch is a no-op when an apiKey is already present (manual entry or
prior session) so it never overwrites a user-set value, and silently
fails on non-200 / missing field so we don't break the OAuth-only path
when Nexus eventually deprecates personal API keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Fedora 44 and Arch ship libfuse3.so.4; the static path list only checked
.so.3 / unversioned .so, so the sanity check warned "libfuse3 not found"
on systems where FUSE actually works.
Use dlopen() with RTLD_NOLOAD then RTLD_LAZY against libfuse3.so.{4,3,}
so the dynamic loader resolves through /etc/ld.so.cache and picks up any
SOVERSION the distro ships. Fallback path list extended with .so.4 for
sandboxes that block dlopen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Restore upstream MO2's exact key cases (gameName, installationFile,
nexusDescription, etc.) in readMeta/saveMeta/doInstall/createMod —
matches mod-shipped meta.ini cases, so on Linux Qt6's case-sensitive
QSettings IniFormat, setValue updates in place without producing a
duplicate key.
normalizeMetaIniCase() repurposed: instead of folding everything to
lowercase (which mismatched the upstream-style setValue calls and
caused the very dupes it was trying to fix), it now canonicalizes
known per-mod meta.ini keys to their upstream CamelCase, deduping
case-insensitive collisions per section. Cleans up dirty files
written during the 5d1fb29 -> 1f19892 window so they collapse to a
single canonical key per setting on first read.
Tests updated to assert canonical-CamelCase output.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
normalizeMetaIniCase folds keys to lowercase, but readMeta/saveMeta and
the install paths still used CamelCase ("installationFile", "gameName",
"nexusDescription", etc.). Qt6 IniFormat is case-sensitive on Linux, so
setValue under CamelCase wrote a NEW key alongside the lowercase entry
left by normalize, and value() reads with CamelCase missed the lowercase
key and fell back to defaults — saveMeta then persisted those defaults
under CamelCase. Net effect: every save grew meta.ini by ~12 duplicate
lines (e.g. installationFile= + installationfile=<real value>;
gameName=SkyrimSE + gamename=Skyrim).
Lowercased all per-mod meta.ini key names in modinforegular.cpp,
installationmanager.cpp, and organizercore.cpp. Section/group names
(installedFiles, Plugins, INI Tweaks) stay CamelCase because normalize
preserves section headers verbatim.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
PR #2374 (Nexus OAuth) added Qt6NetworkAuth dependency, breaking the
NixOS mobase build. Add qtnetworkauth to buildInputs and CMAKE_PREFIX_PATH.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Cherry-picked the two surviving improvements from stale branch
claude/fix-reported-issues-Iyt4s (issue #54). Rest of the branch's
content (Starfield data dir, My Games spam fix) was already
absorbed in main from a separate pass.
- Rename copy fallback: previously dropped the original rename
errno and returned generic ERROR_ACCESS_DENIED with no
context. Now bubbles "Rename failed: %1. Copy fallback failed: %2"
with both QFile::errorString() values so the log line actually
identifies what went wrong.
- formatSystemMessage: added an explicit comment block on why
the codebase's Windows error macros (5=ACCESS_DENIED) must not
be passed through strerror (5=EIO) on Linux.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We ship via portable tarball directory + .bin self-extracting installer
only; the AppImage path was unmaintained and pulling in linuxdeploy
tooling for a format we no longer publish.
Build:
- build.sh: drop appimage/all-includes-appimage modes (now: tarball,
installer, all=both, shell). Output listing tracks the directory.
- docker/Dockerfile: drop BUILD_APPIMAGE arg and the linuxdeploy
download/extract block.
- docker/build-inner.sh: drop build_appimage() (~120 lines), AppImage
arm of the BUILD_MODE switch, *.AppImage summary listing.
- docker/AppRun.sh: deleted.
- .gitignore: drop *.AppImage / squashfs-root / *.flatpak entries.
Runtime: no longer key off APPIMAGE/APPDIR env vars or AppRun-set
state. The fluorine-manager launcher script already exports
FLUORINE_ORIG_*, MO2_BASE_DIR, MO2_PLUGINS_DIR, MO2_LIBS_DIR,
MO2_PYTHON_DIR for the same purpose, so simplify:
- envshortcut: appImageOrBinary -> launcherOrBinary; bundledFluorineIcon
no longer probes APPDIR.
- nxmhandler: drop APPIMAGE-based wrapper path; use applicationFilePath.
- protonlauncher: rename cleanAppImageEnv -> cleanFluorineEnv, drop
APPIMAGE/APPDIR/OWD/ARGV0/APPIMAGE_ORIGINAL_EXEC/DESKTOPINTEGRATION
removals and .mount_Fluori pattern strip.
- prefixsetuprunner: same env-cleaning trim.
- utility, library.h, proxypython, mainwindow, moapplication, appconfig:
comment cleanup pointing at the launcher instead of AppRun.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
meta.ini: Qt6 QSettings IniFormat is case-sensitive on Linux. Pre-existing
CamelCase keys + lowercase setValue() left both casings in the file, so
mods grew duplicate entries on every install. Added MetaIniUtils::
normalizeMetaIniCase() to fold keys to lowercase and dedupe per-section
(keeping non-empty values) before any QSettings open in readMeta,
saveMeta, doInstall, and createMod.
SKSE Log Redirector: deployExternalMappings created per-file symlinks
under the redirected destination, so any new log file the game wrote
post-deploy ended up as a real file outside the mapping. For
isDirectory && createTarget mappings, now publish a single directory
symlink dst -> src when the destination is missing, an existing symlink,
or empty. Falls back to per-file symlinks when dst already has real
content (preserves user data). Cleanup removes the symlink, leaving no
files behind.
Tests under src/tests/ (gtest, opt-in via BUILD_TESTING):
- test_metainiutils: 6 tests covering no-op cases, case-only dedup,
multi-line continuations, per-section scoping, and end-to-end
verification that QSettings duplication is fixed.
- test_external_dir_mapping: 6 tests covering directory symlink deploy,
flow-through writes, idempotency, empty-dir replacement, real-file
fallback, and cleanup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Cherry-picked the three preview-parent fixes from #2368:
parentWidget() in ConflictsTab/FileTreeTab/ImagesTab returns the tab
widget itself, not the dialog — preview windows ended up parented to
the wrong widget. m_parent->parentWidget() returns the actual mod info
dialog so previews position and z-order correctly.
Skipped the Starfield blueprint feature from the same PR — requires
coordinated changes across uibase (add IPluginGame::blueprintPrefix)
and modorganizer-game_bethesda (Starfield override). See
project_upstream_pending_prs.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Adds metadata flags to the `download` subcommand and a corresponding
DownloadManager::startDownloadURLWithMeta() entry point that populates
ModRepositoryFileInfo before queueing.
Skipped the upstream settings.cpp hunk that re-registers the Windows
nxmhandler.exe binary for both nxm:// and modl:// — our nxmhandler_linux
already registers both MIME schemes in registerHandler().
Cherry-picked from upstream/dev/modl-handler against merge-base dc420a25.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
| |
Distinguish between -i (no value, prints current instance and exits)
and -i "" (launches the portable instance if one exists) by switching
the option parser from std::string to boost::optional<std::string>.
Cherry-picked from upstream 49da80c.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
When MO2 is launched by the nxm handler from a browser, CWD is whatever
the browser inherited (often /, $HOME, or the user's Desktop). Reset it
to the application directory so any code path relying on CWD behaves
the same as a normal launch.
Cherry-picked from upstream f80ad04. Comment expanded for the Linux
context (Qt resource lookup, relative QFile paths, QtWebEngine sandbox
helper) — upstream's note only covered QtWebEngineProcess on Windows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replaces legacy API-key flow with OAuth 2.0 PKCE (public client,
client_id "modorganizer2", overridable via MO2_NEXUS_CLIENT_ID env).
Backwards-compatible: stored API key is used as fallback if no OAuth
token is present.
Fixes the symptom users hit after Nexus deprecated personal API keys
for download endpoints — Settings showed "Connected." (key still
validates) but downloads/CDN list returned empty.
Cherry-picked from upstream/dev/oauth-graphql against merge-base
925bade3, with these adjustments for our Linux-only fork:
- Dockerfile: aqtinstall +qtnetworkauth module
- CMakeLists.txt: find_package + link Qt6::NetworkAuth
- Dropped Windows-only hunks: dlls.manifest.qt6{,debug}, pch.h
QWebSocket include
- Skipped src/CMakeLists.txt and .gitignore upstream hunks (their
layout differs from ours)
- Skipped organizer_en.ts (regenerable via lupdate)
- Skipped tutorials/tutorial_firststeps_settings.js (defer to the
tutorials-disable PR port)
- Kept our defensive default member initialisers in
nxmaccessmanager.h (m_Reply{nullptr}, m_Result{None}, etc.) and
`override` on createRequest()
- Resolved trivial conflicts in modlistviewactions.cpp by taking
upstream's NexusOAuthTokens API while retaining our [=, this]
capture style
- createinstancedialog.h, instancemanager.cpp: only meaningful PR
delta is a comment text change / unrelated drift; ours retained
OAuth callback uses local loopback http://127.0.0.1:28635/callback
served by QOAuthHttpServerReplyHandler (no firewall punch needed).
Token storage piggybacks the existing Linux QSettings INI credential
backend (~/.config/ModOrganizer/credentials.ini).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fixes #64: VFS now tracks the directories it creates outside the data
dir (external mappings + RootBuilder game-root deploys) and removes
them on unmount, but only if still empty. Pre-existing user dirs and
the game root itself are never touched. RootBuilder manifest gains a
"dirs" field so the cleanup survives a crashed session.
Also drops a Path-repr qDebug in bg3_file_mapper that crashed Qt's
logger on surrogate-escaped paths, and defaults QT_LOGGING_RULES to
default.debug=false in the launcher and AppRun (still overridable) so
the same class of plugin bug stays muted in shipped builds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Final auto-fix sweep that adds const to local variables and switches
range-for-by-value to range-for-by-const-ref where the element type is
non-trivially-copyable.
The auto-fixer also emitted ~15 invalid \`for (const T& const x : ...)\`
range loops where \`const-correctness\` and \`for-range-copy\` both fired
on the same line. Hand-fixed via sed: \`& const \` -> \`& \`.
Build verified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Sequentially applied a tighter set of safer checks:
- modernize-return-braced-init-list
- modernize-use-equals-default
- modernize-use-noexcept
- modernize-use-using
- modernize-raw-string-literal
- readability-enum-initial-value
- readability-make-member-function-const
- readability-convert-member-functions-to-static
- readability-redundant-member-init
- readability-container-contains
- readability-container-size-empty
One file (mainwindow.h) had to be hand-fixed: extractProgress was
auto-marked static by readability-convert-member-functions-to-static,
but it's bound via boost::bind(&MainWindow::extractProgress, this,
...) — making it static breaks the bind. Restored to a non-static
member.
Build verified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The first auto-fix run kept going in the background after the wait
loop returned (a podman-detached quirk), so it produced another batch
of fixes against ~52 more files that needed verification.
Two cases the auto-fixer can't fix safely on its own:
- DWORD m_exitCode{-1} -> static_cast<DWORD>(-1) on the brace init,
because -1 narrows to unsigned int.
- {EndorsedState::X} / {TrackedState::X} initializers had to be
qualified with their parent namespace (MOBase::) because the
identifier wasn't in scope at the point of in-class default-init.
Build verified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Ran sequentially (not parallel — concurrent fixes corrupt shared headers)
on src/src/ + libs/skse_log_redirector/. The check set:
- modernize-use-override
- modernize-use-nullptr
- modernize-use-default-member-init
- readability-redundant-member-init
- readability-container-contains
- readability-container-size-empty
Net effect: ~108 files updated, 425/426 lines changed (overrides added,
NULL/0 -> nullptr, member-init lists pruned where the same value is
already in the in-class default). Build verified after revert/re-apply.
Also disable readability-redundant-access-specifiers in .clang-tidy:
the check is confused by Qt's `private slots:` specifier and strips the
`private:` that follows it, breaking MOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`./lint.sh` runs clang-tidy inside the fluorine-builder Docker image so
the toolchain matches what compile_commands.json was generated with.
Strips -mno-direct-extern-access (gcc-only) before invoking clang and
skips browserview/browserdialog since QtWebEngine is Windows-only in
this fork.
Scope: src/src/ + libs/skse_log_redirector/ (everything we authored).
Vendored upstream libs follow their own coding standards and stay
out-of-scope.
Default-off the noisiest categories (fuchsia/google/llvm/altera,
pro-type-*, magic-numbers, named-parameter, function-cognitive-
complexity, identifier-length, etc.) so the surfaced warnings are
actionable.
Dockerfile: install clang-tidy alongside the build deps so the image
ships everything lint.sh needs.
Usage:
./build.sh tarball # build the image (one-time after this commit)
# and produce compile_commands.json
./lint.sh # lint everything
./lint.sh --fix # apply auto-fixes
./lint.sh src/src/foo.cpp # lint a single file
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Removes every #ifdef _WIN32 / #ifndef _WIN32 / Q_OS_WIN block from the
organizer source tree. The Linux build is the only target this fork
produces, so the dead Windows branches were just noise. Stubs for
Win32-shaped APIs that the upstream code references (FILETIME, HANDLE,
DWORD, etc.) live in shared/windows_compat.h so the surrounding source
keeps compiling without rewriting every signature.
Also:
- Reimplemented env::DirectoryWalker / env::forEachEntry / env::Module
/ env::Process / env::WindowsInfo as Linux-only (uname, /etc/os-release,
/proc, std::filesystem) since the Win32 branches that previously held
those implementations are gone.
- Reduced spawn.cpp / processrunner.cpp / env.cpp to their Linux paths;
dropped the helper:: namespace, Win-only waitForAllUSVFSProcesses-
WithLock(), Steam Win-registry probe, and Windows mini-dump path.
- Linux launcher uses BUILD_JOBS (default 4) so docker rebuilds during
the audit don't pin the host.
Build verified after every batch with ./build.sh tarball.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
These files were ported from upstream MO2 with the Windows code wrapped
in #ifdef _WIN32 and Linux equivalents in the #else branch. The Windows
branches never compile on this fork — drop them so the source matches
what's actually built.
- envwindows.cpp/.h: kept Linux uname()/os-release implementation; the
WindowsInfo::Version/Release fields drop their DWORD/uint32_t fork.
- envshell.cpp: deleted entirely (Linux body was empty — header
declares no-op stubs inline).
- envshell.h: drop Win32 ShellMenu declarations, keep Linux stubs.
- envmetrics.cpp: kept QScreen-based Linux Display/Metrics implementation.
- envshortcut.cpp: kept Linux .desktop shortcut writer + PE icon
extractor, drop Win32 IShellLink path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three FileMapper variants — Steam SSE, GOG, VR — that redirect
<docs>/My Games/<X> onto a sibling folder so SKSE writes logs to the
right place. Only one applies per game; all default off so the user
opts in to the matching variant.
Layout mirrors game_bethesda: shared static lib in src/common/ with the
QObject base (init/author/version/settings/enabledByDefault/mappings),
three plugin .so subdirs each adding Q_PLUGIN_METADATA + their own
name/description and destFolderName override. Q_PLUGIN_METADATA can
only appear once per .so, hence the split.
Paths preserved verbatim from the upstream .py — including the SSE
variant's "Skyrim.INI" sibling folder, which is intentional: a Skyrim
bug sometimes creates a directory of that name and the redirect makes
SKSE find logs in SSE's docs folder.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Setting UMU_ID="fluorine" triggered protonfixes' setup_mount_drives
(GE-Proton/utilities.py), which mounts X:=$HOME, U:=/media, V:=/run/media,
W:=/mnt every launch — undoing pruneExtraDrives. Wine's find_drive_nt_root
walks the path innermost-out and picks the first matching drive, so
X:=$HOME wins over Z:=/ for any path under home, producing X:\games\...
launches.
Empty UMU_ID:
- protonfixes utilities.py:38 `os.environ.get('UMU_ID','')` -> '' falsy
-> setup_mount_drives early-exits, no X/U/V/W created
- proton:1590 same falsy check -> 'gamedrive' not added to compat_config
-> setup_game_dir_drive's setup_dir_drive takes the elif branch and
actively removes any stale s: symlink
- proton:2340 `"UMU_ID" in os.environ` is still True for "" -> still
skips the c:\Program Files (x86)\Steam\steam.exe bridge for non-Steam
executables (the original reason we set UMU_ID)
Verified against installed Proton-GE proton script + utilities.py and
GloriousEggroll's confirmation in umu-launcher#634.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The version-sync step wiped ~/.local/share/fluorine/bin/ wholesale
before staging the new bundle in, which destroyed any user content
sitting alongside ours: portable instances (ModOrganizer.ini lives in
bin/ via MO2_BASE_DIR), custom plugins under bin/plugins/, etc.
Replace the wipe with a manifest-driven overlay: orphan top-level
entries we no longer ship get removed, then tar streams the manifested
entries from the extracted bundle on top of the live dir. Existing
directories are kept; user-added files inside our directories survive
because tar -x only writes paths it sees in the input stream.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
Blanking DISPLAY/WAYLAND_DISPLAY made xrandr exit 1 inside the
container, which Proton-GE 10-34's protonfixes treats as fatal during
wineboot -u. wineboot -u is unattended and won't pop UI, so leaking the
host display vars is safe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|