From d06dd9bf82040ad67a6f12fb92fb411770d63f28 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Sat, 25 Apr 2026 20:27:53 -0500 Subject: Detect native-Linux Stardew Valley, default executables to no-Proton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add IPluginGame::isNativeLinux() (virtual, default false) so a plugin can advertise that the discovered installation is a native Linux build. Bound through the Python plugin trampoline so basic_games subclasses can override it. Stardew Valley plugin: when the install dir contains the StardewValley launcher script and no Stardew Valley.exe, report isNativeLinux=true and return Linux-side binary names (StardewValley, StardewModdingAPI). Also fix GameDataPath to "Mods" — Linux ships the SMAPI mods dir with a capital M. ExecutablesList::getPluginExecutables: skip the UseProton flag when the plugin reports native Linux, so the spawn path falls into launchDirect() and the game runs without a Wine prefix. --- libs/basic_games/games/game_stardewvalley.py | 36 ++++++++++++++++++++-- .../src/mobase/wrappers/pyplugins.cpp | 1 + libs/plugin_python/src/mobase/wrappers/pyplugins.h | 4 +++ libs/uibase/include/uibase/iplugingame.h | 9 ++++++ src/src/executableslist.cpp | 11 ++++++- 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/libs/basic_games/games/game_stardewvalley.py b/libs/basic_games/games/game_stardewvalley.py index 6397b40..566a511 100644 --- a/libs/basic_games/games/game_stardewvalley.py +++ b/libs/basic_games/games/game_stardewvalley.py @@ -1,4 +1,6 @@ -from PyQt6.QtCore import QFileInfo +import sys + +from PyQt6.QtCore import QDir, QFileInfo import mobase @@ -21,6 +23,17 @@ class StardewValleyModDataChecker(mobase.ModDataChecker): return mobase.ModDataChecker.INVALID +def _has_native_linux_install(game_dir: QDir) -> bool: + # Native Linux Stardew ships a `StardewValley` shell-script launcher and + # has no `Stardew Valley.exe`. Use that to distinguish from a Windows + # install dropped under a Wine prefix. + if sys.platform != "linux": + return False + if QFileInfo(game_dir, "Stardew Valley.exe").exists(): + return False + return QFileInfo(game_dir, "StardewValley").exists() + + class StardewValleyGame(BasicGame): Name = "Stardew Valley Support Plugin" Author = "Syer10" @@ -33,7 +46,7 @@ class StardewValleyGame(BasicGame): GameSteamId = 413150 GameGogId = 1453375253 GameBinary = "Stardew Valley.exe" - GameDataPath = "mods" + GameDataPath = "Mods" GameDocumentsDirectory = "%DOCUMENTS%/StardewValley" GameSavesDirectory = "%GAME_DOCUMENTS%/Saves" GameSupportURL = ( @@ -46,7 +59,26 @@ class StardewValleyGame(BasicGame): self._register_feature(StardewValleyModDataChecker()) return True + def isNativeLinux(self) -> bool: + return _has_native_linux_install(self.gameDirectory()) + + def binaryName(self) -> str: + if self.isNativeLinux(): + return "StardewValley" + return super().binaryName() + def executables(self): + if self.isNativeLinux(): + return [ + mobase.ExecutableInfo( + "SMAPI", + QFileInfo(self.gameDirectory(), "StardewModdingAPI"), + ), + mobase.ExecutableInfo( + "Stardew Valley", + QFileInfo(self.gameDirectory(), "StardewValley"), + ), + ] return [ mobase.ExecutableInfo( "SMAPI", QFileInfo(self.gameDirectory(), "StardewModdingAPI.exe") diff --git a/libs/plugin_python/src/mobase/wrappers/pyplugins.cpp b/libs/plugin_python/src/mobase/wrappers/pyplugins.cpp index bcce0df..c744c7b 100644 --- a/libs/plugin_python/src/mobase/wrappers/pyplugins.cpp +++ b/libs/plugin_python/src/mobase/wrappers/pyplugins.cpp @@ -52,6 +52,7 @@ namespace mo2::python { "settings"_a) .def("listSaves", &IPluginGame::listSaves, "folder"_a) .def("isInstalled", &IPluginGame::isInstalled) + .def("isNativeLinux", &IPluginGame::isNativeLinux) .def("gameIcon", &IPluginGame::gameIcon) .def("gameDirectory", &IPluginGame::gameDirectory) .def("dataDirectory", &IPluginGame::dataDirectory) diff --git a/libs/plugin_python/src/mobase/wrappers/pyplugins.h b/libs/plugin_python/src/mobase/wrappers/pyplugins.h index 17f7ab5..ce9a911 100644 --- a/libs/plugin_python/src/mobase/wrappers/pyplugins.h +++ b/libs/plugin_python/src/mobase/wrappers/pyplugins.h @@ -385,6 +385,10 @@ namespace mo2::python { { PYBIND11_OVERRIDE_PURE(bool, IPluginGame, isInstalled, ); } + bool isNativeLinux() const override + { + PYBIND11_OVERRIDE(bool, IPluginGame, isNativeLinux, ); + } QIcon gameIcon() const override { PYBIND11_OVERRIDE_PURE(QIcon, IPluginGame, gameIcon, ); diff --git a/libs/uibase/include/uibase/iplugingame.h b/libs/uibase/include/uibase/iplugingame.h index 0fa2508..93ee09f 100644 --- a/libs/uibase/include/uibase/iplugingame.h +++ b/libs/uibase/include/uibase/iplugingame.h @@ -142,6 +142,15 @@ public: */ virtual bool isInstalled() const = 0; + /** + * this function may be called before init() + * + * @return true if the discovered installation is a native Linux build that + * should be launched directly without Proton/Wine. Default false: + * Linux ports of MO2 historically run every game under Proton. + */ + virtual bool isNativeLinux() const { return false; } + /** * this function may be called before init() * diff --git a/src/src/executableslist.cpp b/src/src/executableslist.cpp index f12a74c..459d25c 100644 --- a/src/src/executableslist.cpp +++ b/src/src/executableslist.cpp @@ -150,12 +150,21 @@ ExecutablesList::getPluginExecutables(MOBase::IPluginGame const* game) const std::vector v; + // Native Linux installs (e.g. native Stardew Valley) launch directly without + // Proton. Their executables default to UseProton=false so the user doesn't + // have to flip every entry by hand after instance creation. + const bool nativeLinux = game->isNativeLinux(); + for (const ExecutableInfo& info : game->executables()) { if (!info.isValid()) { continue; } - v.push_back({info, Executable::UseApplicationIcon | Executable::UseProton}); + Executable::Flags flags = Executable::UseApplicationIcon; + if (!nativeLinux) { + flags |= Executable::UseProton; + } + v.push_back({info, flags}); } const QFileInfo eppBin(QCoreApplication::applicationDirPath() + -- cgit v1.3.1