Merge branch 'develop' into stable
This commit is contained in:
commit
5d3d919d49
|
@ -1,17 +1,23 @@
|
||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
|
||||||
<Import Project="find-game-folder.targets" />
|
|
||||||
|
|
||||||
<!--set properties -->
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>3.9.5</Version>
|
<!--set general build properties -->
|
||||||
|
<Version>3.10.0</Version>
|
||||||
<Product>SMAPI</Product>
|
<Product>SMAPI</Product>
|
||||||
|
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
|
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
|
||||||
<DefineConstants Condition="$(OS) == 'Windows_NT'">$(DefineConstants);SMAPI_FOR_WINDOWS;SMAPI_FOR_XNA</DefineConstants>
|
|
||||||
|
<!--uncomment for 64-bit Stardew Valley on Windows-->
|
||||||
|
<!--<GamePath>D:\dev\SDV 64-bit\6125897</GamePath>
|
||||||
|
<DefineConstants>$(DefineConstants);SMAPI_FOR_WINDOWS_64BIT_HACK</DefineConstants>-->
|
||||||
|
|
||||||
|
<!--set platform-->
|
||||||
|
<DefineConstants Condition="$(OS) == 'Windows_NT'">$(DefineConstants);SMAPI_FOR_WINDOWS</DefineConstants>
|
||||||
|
<DefineConstants Condition="$(OS) == 'Windows_NT' AND !$(DefineConstants.Contains(SMAPI_FOR_WINDOWS_64BIT_HACK))">$(DefineConstants);SMAPI_FOR_XNA</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!--find game folder-->
|
||||||
|
<Import Project="find-game-folder.targets" />
|
||||||
|
|
||||||
<!-- if game path is invalid, show one user-friendly error instead of a slew of reference errors -->
|
<!-- if game path is invalid, show one user-friendly error instead of a slew of reference errors -->
|
||||||
<Target Name="ValidateInstallPath" AfterTargets="BeforeBuild">
|
<Target Name="ValidateInstallPath" AfterTargets="BeforeBuild">
|
||||||
<Error Condition="!Exists('$(GamePath)')" Text="Failed to find the game install path automatically. You can specify where to find it; see https://smapi.io/package/custom-game-path." />
|
<Error Condition="!Exists('$(GamePath)')" Text="Failed to find the game install path automatically. You can specify where to find it; see https://smapi.io/package/custom-game-path." />
|
||||||
|
@ -50,9 +56,9 @@
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit' AND $(TargetFramework) == 'net4.5'" AfterTargets="PostBuildEvent">
|
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit' AND $(TargetFramework) == 'net4.5'" AfterTargets="PostBuildEvent">
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces' AND $(TargetFramework) == 'net4.5'" AfterTargets="PostBuildEvent">
|
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces' AND $(TargetFramework) == 'net4.5'" AfterTargets="PostBuildEvent">
|
||||||
|
@ -74,7 +80,6 @@
|
||||||
<StartWorkingDirectory>$(GamePath)</StartWorkingDirectory>
|
<StartWorkingDirectory>$(GamePath)</StartWorkingDirectory>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Somehow this makes Visual Studio for Mac recognise the previous section. Nobody knows why. -->
|
<!-- Somehow this makes Visual Studio for macOS recognise the previous section. Nobody knows why. -->
|
||||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Default'" />
|
<PropertyGroup Condition="'$(RunConfiguration)' == 'Default'" />
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath>
|
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath>
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/Stardew Valley</GamePath>
|
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/Stardew Valley</GamePath>
|
||||||
|
|
||||||
<!-- Mac (may be 'Unix' or 'OSX') -->
|
<!-- macOS (may be 'Unix' or 'OSX') -->
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">/Applications/Stardew Valley.app/Contents/MacOS</GamePath>
|
<GamePath Condition="!Exists('$(GamePath)')">/Applications/Stardew Valley.app/Contents/MacOS</GamePath>
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS</GamePath>
|
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS</GamePath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
<!-- copy installer files -->
|
<!-- copy installer files -->
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on Linux.sh" />
|
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on Linux.sh" />
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on Mac.command" />
|
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on macOS.command" />
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\windows-install.bat" DestinationFiles="$(PackagePath)\install on Windows.bat" />
|
<Copy SourceFiles="$(TargetDir)\assets\windows-install.bat" DestinationFiles="$(PackagePath)\install on Windows.bat" />
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\README.txt" DestinationFiles="$(PackagePath)\README.txt" />
|
<Copy SourceFiles="$(TargetDir)\assets\README.txt" DestinationFiles="$(PackagePath)\README.txt" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(PackagePath)\internal\$(PlatformName)-install.exe" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(PackagePath)\internal\$(PlatformName)-install.exe" />
|
||||||
|
@ -74,13 +74,13 @@
|
||||||
<Copy SourceFiles="$(SaveBackupBin)\SaveBackup.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
<Copy SourceFiles="$(SaveBackupBin)\SaveBackup.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
||||||
<Copy SourceFiles="$(SaveBackupBin)\manifest.json" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
<Copy SourceFiles="$(SaveBackupBin)\manifest.json" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
||||||
|
|
||||||
<!-- fix errors on Linux/Mac (sample: https://smapi.io/log/mMdFUpgB) -->
|
<!-- fix errors on Linux/macOS (sample: https://smapi.io/log/mMdFUpgB) -->
|
||||||
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\System.Numerics.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\System.Numerics.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\System.Runtime.Caching.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\System.Runtime.Caching.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
|
|
||||||
<!-- fix Linux/Mac permissions -->
|
<!-- fix Linux/macOS permissions -->
|
||||||
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)\install on Linux.sh"" />
|
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)\install on Linux.sh"" />
|
||||||
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)\install on Mac.command"" />
|
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)\install on macOS.command"" />
|
||||||
|
|
||||||
<!-- finalise 'for developers' installer -->
|
<!-- finalise 'for developers' installer -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -11,9 +11,9 @@ doesn't change any of your game files. It serves seven main purposes:
|
||||||
couldn't._
|
couldn't._
|
||||||
|
|
||||||
3. **Rewrite mods for compatibility.**
|
3. **Rewrite mods for compatibility.**
|
||||||
_SMAPI rewrites mods' compiled code before loading them so they work on Linux/Mac/Windows
|
_SMAPI rewrites mods' compiled code before loading them so they work on Linux/macOS/Windows
|
||||||
without the mods needing to handle differences between the Linux/Mac and Windows versions of the
|
without the mods needing to handle differences between the Linux/macOS and Windows versions of
|
||||||
game. In some cases it also rewrites code broken by a game update so the mod doesn't break._
|
the game. In some cases it also rewrites code broken by a game update so the mod doesn't break._
|
||||||
|
|
||||||
5. **Intercept errors and automatically fix saves.**
|
5. **Intercept errors and automatically fix saves.**
|
||||||
_SMAPI intercepts errors, shows the error info in the SMAPI console, and in most cases
|
_SMAPI intercepts errors, shows the error info in the SMAPI console, and in most cases
|
||||||
|
|
|
@ -30,7 +30,7 @@ Released 13 September 2019 for Stardew Valley 1.3.36.
|
||||||
Released 23 April 2019 for Stardew Valley 1.3.36.
|
Released 23 April 2019 for Stardew Valley 1.3.36.
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* Fixed error when a custom map references certain vanilla tilesheets on Linux/Mac.
|
* Fixed error when a custom map references certain vanilla tilesheets on Linux/macOS.
|
||||||
* Fixed compatibility with some Linux distros.
|
* Fixed compatibility with some Linux distros.
|
||||||
|
|
||||||
## 2.11.1
|
## 2.11.1
|
||||||
|
@ -68,7 +68,7 @@ Released 09 January 2019 for Stardew Valley 1.3.32–33.
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* SMAPI now keeps the first save backup created for the day, instead of the last one.
|
* SMAPI now keeps the first save backup created for the day, instead of the last one.
|
||||||
* Fixed save backup for some Linux/Mac players. (When compression isn't available, SMAPI will now create uncompressed backups instead.)
|
* Fixed save backup for some Linux/macOS players. (When compression isn't available, SMAPI will now create uncompressed backups instead.)
|
||||||
* Fixed some common dependencies not linking to the mod page in 'missing mod' errors.
|
* Fixed some common dependencies not linking to the mod page in 'missing mod' errors.
|
||||||
* Fixed 'unknown mod' deprecation warnings showing a stack trace when developers mode not enabled.
|
* Fixed 'unknown mod' deprecation warnings showing a stack trace when developers mode not enabled.
|
||||||
* Fixed 'unknown mod' deprecation warnings when they occur in the Mod constructor.
|
* Fixed 'unknown mod' deprecation warnings when they occur in the Mod constructor.
|
||||||
|
@ -90,7 +90,7 @@ Released 09 January 2019 for Stardew Valley 1.3.32–33.
|
||||||
* Fixed 'unknown mod' deprecation warnings showing the wrong stack trace.
|
* Fixed 'unknown mod' deprecation warnings showing the wrong stack trace.
|
||||||
* Fixed `e.Cursor` in input events showing wrong grab tile when player using a controller moves without moving the viewpoint.
|
* Fixed `e.Cursor` in input events showing wrong grab tile when player using a controller moves without moving the viewpoint.
|
||||||
* Fixed incorrect 'bypassed safety checks' warning for mods using the new `Specialized.LoadStageChanged` event in 2.10.
|
* Fixed incorrect 'bypassed safety checks' warning for mods using the new `Specialized.LoadStageChanged` event in 2.10.
|
||||||
* Deprecated `EntryDll` values whose capitalization don't match the actual file. (This works on Windows, but causes errors for Linux/Mac players.)
|
* Deprecated `EntryDll` values whose capitalization don't match the actual file. (This works on Windows, but causes errors for Linux/macOS players.)
|
||||||
|
|
||||||
## 2.10.1
|
## 2.10.1
|
||||||
Released 30 December 2018 for Stardew Valley 1.3.32–33.
|
Released 30 December 2018 for Stardew Valley 1.3.32–33.
|
||||||
|
@ -183,7 +183,7 @@ Released 07 December 2018 for Stardew Valley 1.3.32.
|
||||||
## 2.8.2
|
## 2.8.2
|
||||||
Released 19 November 2018 for Stardew Valley 1.3.32.
|
Released 19 November 2018 for Stardew Valley 1.3.32.
|
||||||
|
|
||||||
* Fixed game crash in MacOS with SMAPI 2.8.
|
* Fixed game crash in macOS with SMAPI 2.8.
|
||||||
|
|
||||||
## 2.8.1
|
## 2.8.1
|
||||||
Released 19 November 2018 for Stardew Valley 1.3.32.
|
Released 19 November 2018 for Stardew Valley 1.3.32.
|
||||||
|
@ -205,7 +205,7 @@ Released 19 November 2018 for Stardew Valley 1.3.32.
|
||||||
* SMAPI now recommends a compatible SMAPI version if you have an older game version.
|
* SMAPI now recommends a compatible SMAPI version if you have an older game version.
|
||||||
* Improved various error messages to be more clear and intuitive.
|
* Improved various error messages to be more clear and intuitive.
|
||||||
* Improved compatibility with various Linux shells (thanks to lqdev!), and prefer xterm when available.
|
* Improved compatibility with various Linux shells (thanks to lqdev!), and prefer xterm when available.
|
||||||
* Fixed transparency issues on Linux/Mac for some mod images.
|
* Fixed transparency issues on Linux/macOS for some mod images.
|
||||||
* Fixed error when a mod manifest is corrupted.
|
* Fixed error when a mod manifest is corrupted.
|
||||||
* Fixed error when a mod adds an unnamed location.
|
* Fixed error when a mod adds an unnamed location.
|
||||||
* Fixed friendly error no longer shown when SMAPI isn't run from the game folder.
|
* Fixed friendly error no longer shown when SMAPI isn't run from the game folder.
|
||||||
|
@ -223,7 +223,7 @@ Released 19 November 2018 for Stardew Valley 1.3.32.
|
||||||
* The log parser now has a separate filter for game messages.
|
* The log parser now has a separate filter for game messages.
|
||||||
* The log parser now shows content pack authors (thanks to danvolchek!).
|
* The log parser now shows content pack authors (thanks to danvolchek!).
|
||||||
* Tweaked log parser UI (thanks to danvolchek!).
|
* Tweaked log parser UI (thanks to danvolchek!).
|
||||||
* Fixed log parser instructions for Mac.
|
* Fixed log parser instructions for macOS.
|
||||||
|
|
||||||
* For mod authors:
|
* For mod authors:
|
||||||
* Added [data API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Data) to store mod data in the save file or app data.
|
* Added [data API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Data) to store mod data in the save file or app data.
|
||||||
|
@ -267,7 +267,7 @@ Released 14 August 2018 for Stardew Valley 1.3.28.
|
||||||
* Improved how mod issues are listed in the console and log.
|
* Improved how mod issues are listed in the console and log.
|
||||||
* Revamped installer. It now...
|
* Revamped installer. It now...
|
||||||
* uses a new format that should be more intuitive;
|
* uses a new format that should be more intuitive;
|
||||||
* lets players on Linux/Mac choose the console color scheme (SMAPI will auto-detect it on Windows);
|
* lets players on Linux/macOS choose the console color scheme (SMAPI will auto-detect it on Windows);
|
||||||
* and validates requirements earlier.
|
* and validates requirements earlier.
|
||||||
* Fixed custom festival maps always using spring tilesheets.
|
* Fixed custom festival maps always using spring tilesheets.
|
||||||
* Fixed `player_add` command not recognising return scepter.
|
* Fixed `player_add` command not recognising return scepter.
|
||||||
|
@ -314,8 +314,8 @@ Released 01 August 2018 for Stardew Valley 1.3.27.
|
||||||
* Removed the `player_setlevel` and `player_setspeed` commands, which weren't implemented in a useful way. Use a mod like CJB Cheats Menu if you need those.
|
* Removed the `player_setlevel` and `player_setspeed` commands, which weren't implemented in a useful way. Use a mod like CJB Cheats Menu if you need those.
|
||||||
* Fixed `SEHException` errors for some players.
|
* Fixed `SEHException` errors for some players.
|
||||||
* Fixed performance issues for some players.
|
* Fixed performance issues for some players.
|
||||||
* Fixed default color scheme on Mac or in PowerShell (configurable via `StardewModdingAPI.config.json`).
|
* Fixed default color scheme on macOS or in PowerShell (configurable via `StardewModdingAPI.config.json`).
|
||||||
* Fixed installer error on Linux/Mac in some cases.
|
* Fixed installer error on Linux/macOS in some cases.
|
||||||
* Fixed installer not finding some game paths or showing duplicate paths.
|
* Fixed installer not finding some game paths or showing duplicate paths.
|
||||||
* Fixed installer not removing some SMAPI files.
|
* Fixed installer not removing some SMAPI files.
|
||||||
* Fixed launch issue for Linux players with some terminals. (Thanks to HanFox and kurumushi!)
|
* Fixed launch issue for Linux players with some terminals. (Thanks to HanFox and kurumushi!)
|
||||||
|
@ -345,7 +345,7 @@ Released 01 August 2018 for Stardew Valley 1.3.27.
|
||||||
* each event now provides a list of added/removed values;
|
* each event now provides a list of added/removed values;
|
||||||
* added buildings-changed event.
|
* added buildings-changed event.
|
||||||
* Added `Context.IsMultiplayer` and `Context.IsMainPlayer` flags.
|
* Added `Context.IsMultiplayer` and `Context.IsMainPlayer` flags.
|
||||||
* Added `Constants.TargetPlatform` which says whether the game is running on Linux, Mac, or Windows.
|
* Added `Constants.TargetPlatform` which says whether the game is running on Linux, macOS, or Windows.
|
||||||
* Added `semanticVersion.IsPrerelease()` method.
|
* Added `semanticVersion.IsPrerelease()` method.
|
||||||
* Added support for launching multiple instances transparently. This removes the former `--log-path` command-line argument.
|
* Added support for launching multiple instances transparently. This removes the former `--log-path` command-line argument.
|
||||||
* Added support for custom seasonal tilesheets when loading an unpacked `.tbin` map.
|
* Added support for custom seasonal tilesheets when loading an unpacked `.tbin` map.
|
||||||
|
@ -376,7 +376,7 @@ Released 01 August 2018 for Stardew Valley 1.3.27.
|
||||||
* Mod IDs should only contain letters, numbers, hyphens, dots, and underscores. That allows their use in many contexts like URLs. This restriction is now enforced. (In regex form: `^[a-zA-Z0-9_.-]+$`.)
|
* Mod IDs should only contain letters, numbers, hyphens, dots, and underscores. That allows their use in many contexts like URLs. This restriction is now enforced. (In regex form: `^[a-zA-Z0-9_.-]+$`.)
|
||||||
|
|
||||||
* For SMAPI developers:
|
* For SMAPI developers:
|
||||||
* Added more consistent crossplatform handling, including MacOS detection.
|
* Added more consistent crossplatform handling, including macOS detection.
|
||||||
* Added beta update channel.
|
* Added beta update channel.
|
||||||
* Added optional mod metadata to the web API (including Nexus info, wiki metadata, etc).
|
* Added optional mod metadata to the web API (including Nexus info, wiki metadata, etc).
|
||||||
* Added early prototype of SMAPI 3.0 events via `helper.Events`.
|
* Added early prototype of SMAPI 3.0 events via `helper.Events`.
|
||||||
|
@ -411,7 +411,7 @@ Released 26 March 2018 for Stardew Valley 1.2.30–1.2.33.
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* Fixed some textures not updated when a mod changes them.
|
* Fixed some textures not updated when a mod changes them.
|
||||||
* Fixed visual bug on Linux/Mac when mods overlay textures.
|
* Fixed visual bug on Linux/macOS when mods overlay textures.
|
||||||
* Fixed error when mods remove an asset editor/loader.
|
* Fixed error when mods remove an asset editor/loader.
|
||||||
* Fixed minimum game version incorrectly increased in SMAPI 2.5.3.
|
* Fixed minimum game version incorrectly increased in SMAPI 2.5.3.
|
||||||
|
|
||||||
|
@ -467,9 +467,9 @@ Released 24 February 2018 for Stardew Valley 1.2.30–1.2.33.
|
||||||
* **Added support for [content packs](https://stardewvalleywiki.com/Modding:Content_packs)**.
|
* **Added support for [content packs](https://stardewvalleywiki.com/Modding:Content_packs)**.
|
||||||
<small>_Content packs are collections of files for a SMAPI mod to load. These can be installed directly under `Mods` like a normal SMAPI mod, get automatic update and compatibility checks, and provide convenient APIs to the mods that read them._</small>
|
<small>_Content packs are collections of files for a SMAPI mod to load. These can be installed directly under `Mods` like a normal SMAPI mod, get automatic update and compatibility checks, and provide convenient APIs to the mods that read them._</small>
|
||||||
* Added mod detection for unhandled errors (so most errors now mention which mod caused them).
|
* Added mod detection for unhandled errors (so most errors now mention which mod caused them).
|
||||||
* Added install scripts for Linux/Mac (no more manual terminal commands!).
|
* Added install scripts for Linux/macOS (no more manual terminal commands!).
|
||||||
* Added the missing mod's name and URL to dependency errors.
|
* Added the missing mod's name and URL to dependency errors.
|
||||||
* Fixed uninstall script not reporting when done on Linux/Mac.
|
* Fixed uninstall script not reporting when done on Linux/macOS.
|
||||||
* Updated compatibility list and enabled update checks for more mods.
|
* Updated compatibility list and enabled update checks for more mods.
|
||||||
|
|
||||||
* For mod authors:
|
* For mod authors:
|
||||||
|
@ -524,7 +524,7 @@ Released 26 December 2017 for Stardew Valley 1.2.30–1.2.33.
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* Added a user-friendly [download page](https://smapi.io).
|
* Added a user-friendly [download page](https://smapi.io).
|
||||||
* Improved cryptic libgdiplus errors on Mac when Mono isn't installed.
|
* Improved cryptic libgdiplus errors on macOS when Mono isn't installed.
|
||||||
* Fixed mod UIs hidden when menu backgrounds are enabled.
|
* Fixed mod UIs hidden when menu backgrounds are enabled.
|
||||||
|
|
||||||
* For mod authors:
|
* For mod authors:
|
||||||
|
@ -545,9 +545,9 @@ Released 26 December 2017 for Stardew Valley 1.2.30–1.2.33.
|
||||||
Released 02 December 2017 for Stardew Valley 1.2.30–1.2.33.
|
Released 02 December 2017 for Stardew Valley 1.2.30–1.2.33.
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* Fixed error when a mod loads custom assets on Linux/Mac.
|
* Fixed error when a mod loads custom assets on Linux/macOS.
|
||||||
* Fixed error when checking for updates on Linux/Mac due to API HTTPS redirect.
|
* Fixed error when checking for updates on Linux/macOS due to API HTTPS redirect.
|
||||||
* Fixed error when Mac adds an `mcs` symlink to the installer package.
|
* Fixed error when macOS adds an `mcs` symlink to the installer package.
|
||||||
* Fixed `player_add` command not handling tool upgrade levels.
|
* Fixed `player_add` command not handling tool upgrade levels.
|
||||||
* Improved error when a mod has an invalid `EntryDLL` filename format.
|
* Improved error when a mod has an invalid `EntryDLL` filename format.
|
||||||
* Updated compatibility list.
|
* Updated compatibility list.
|
||||||
|
@ -651,7 +651,7 @@ For players:
|
||||||
* The console is now simpler and easier to read, and adjusts its colors to fit your terminal background color.
|
* The console is now simpler and easier to read, and adjusts its colors to fit your terminal background color.
|
||||||
* Renamed installer folder to avoid confusion.
|
* Renamed installer folder to avoid confusion.
|
||||||
* Updated compatibility list.
|
* Updated compatibility list.
|
||||||
* Fixed update check errors on Linux/Mac.
|
* Fixed update check errors on Linux/macOS.
|
||||||
* Fixed collection-changed errors during startup for some players.
|
* Fixed collection-changed errors during startup for some players.
|
||||||
|
|
||||||
For mod developers:
|
For mod developers:
|
||||||
|
@ -685,7 +685,7 @@ For SMAPI developers:
|
||||||
Released 09 September 2017 for Stardew Valley 1.2.30–1.2.33.
|
Released 09 September 2017 for Stardew Valley 1.2.30–1.2.33.
|
||||||
|
|
||||||
For players:
|
For players:
|
||||||
* Fixed errors when loading some custom maps on Linux/Mac or using XNB Loader.
|
* Fixed errors when loading some custom maps on Linux/macOS or using XNB Loader.
|
||||||
* Fixed errors in rare cases when a mod calculates an in-game date.
|
* Fixed errors in rare cases when a mod calculates an in-game date.
|
||||||
|
|
||||||
For mod authors:
|
For mod authors:
|
||||||
|
@ -772,7 +772,7 @@ For players:
|
||||||
* you have Stardew Valley 1.11 or earlier (which aren't compatible);
|
* you have Stardew Valley 1.11 or earlier (which aren't compatible);
|
||||||
* you run `install.exe` from within the downloaded zip file.
|
* you run `install.exe` from within the downloaded zip file.
|
||||||
* Fixed "unknown mod" deprecation warnings by improving how SMAPI detects the mod using the event.
|
* Fixed "unknown mod" deprecation warnings by improving how SMAPI detects the mod using the event.
|
||||||
* Fixed `libgdiplus.dylib` errors for some players on Mac.
|
* Fixed `libgdiplus.dylib` errors for some players on macOS.
|
||||||
* Fixed rare crash when window loses focus for a few players.
|
* Fixed rare crash when window loses focus for a few players.
|
||||||
* Bumped minimum game version to 1.2.30.
|
* Bumped minimum game version to 1.2.30.
|
||||||
* Updated mod compatibility list.
|
* Updated mod compatibility list.
|
||||||
|
@ -805,8 +805,8 @@ For players:
|
||||||
* SMAPI now recovers automatically from errors in the game loop when possible.
|
* SMAPI now recovers automatically from errors in the game loop when possible.
|
||||||
* SMAPI now remembers if your game crashed and offers help next time you launch it.
|
* SMAPI now remembers if your game crashed and offers help next time you launch it.
|
||||||
* Fixed installer sometimes finding redundant game paths.
|
* Fixed installer sometimes finding redundant game paths.
|
||||||
* Fixed save events not being raised after the first day on Linux/Mac.
|
* Fixed save events not being raised after the first day on Linux/macOS.
|
||||||
* Fixed error on Linux/Mac when a mod loads a PNG immediately after the save is loaded.
|
* Fixed error on Linux/macOS when a mod loads a PNG immediately after the save is loaded.
|
||||||
* Updated mod compatibility list for Stardew Valley 1.2.
|
* Updated mod compatibility list for Stardew Valley 1.2.
|
||||||
|
|
||||||
For mod developers:
|
For mod developers:
|
||||||
|
@ -826,15 +826,15 @@ Released 03 May 2017 for Stardew Valley 1.2.26–1.2.29.
|
||||||
For players:
|
For players:
|
||||||
* The installer now lets you choose the install path if you have multiple copies of the game, instead of using the first path found.
|
* The installer now lets you choose the install path if you have multiple copies of the game, instead of using the first path found.
|
||||||
* Fixed mod draw errors breaking the game.
|
* Fixed mod draw errors breaking the game.
|
||||||
* Fixed mods on Linux/Mac no longer working after the game saves.
|
* Fixed mods on Linux/macOS no longer working after the game saves.
|
||||||
* Fixed `libgdiplus.dylib` errors on Mac when mods read PNG files.
|
* Fixed `libgdiplus.dylib` errors on macOS when mods read PNG files.
|
||||||
* Adopted pufferchick.
|
* Adopted pufferchick.
|
||||||
|
|
||||||
For mod developers:
|
For mod developers:
|
||||||
* Unknown mod manifest fields are now stored in `IManifest::ExtraFields`.
|
* Unknown mod manifest fields are now stored in `IManifest::ExtraFields`.
|
||||||
* The content API now defaults to `ContentSource.ModFolder`.
|
* The content API now defaults to `ContentSource.ModFolder`.
|
||||||
* Fixed content API error when loading a PNG during early game init (e.g. in mod's `Entry`).
|
* Fixed content API error when loading a PNG during early game init (e.g. in mod's `Entry`).
|
||||||
* Fixed content API error when loading an XNB from the mod folder on Mac.
|
* Fixed content API error when loading an XNB from the mod folder on macOS.
|
||||||
|
|
||||||
## 1.11
|
## 1.11
|
||||||
Released 30 April 2017 for Stardew Valley 1.2.26.
|
Released 30 April 2017 for Stardew Valley 1.2.26.
|
||||||
|
@ -888,7 +888,7 @@ For players:
|
||||||
* Fixed the game-needs-an-update error not pausing before exit.
|
* Fixed the game-needs-an-update error not pausing before exit.
|
||||||
* Fixed installer errors for some players when deleting files.
|
* Fixed installer errors for some players when deleting files.
|
||||||
* Fixed installer not ignoring potential game folders that don't contain a Stardew Valley exe.
|
* Fixed installer not ignoring potential game folders that don't contain a Stardew Valley exe.
|
||||||
* Fixed installer not recognising Linux/Mac paths starting with `~/` or containing an escaped space.
|
* Fixed installer not recognising Linux/macOS paths starting with `~/` or containing an escaped space.
|
||||||
* Fixed TrainerMod letting you add invalid items which may crash the game.
|
* Fixed TrainerMod letting you add invalid items which may crash the game.
|
||||||
* Fixed TrainerMod's `world_downminelevel` command not working.
|
* Fixed TrainerMod's `world_downminelevel` command not working.
|
||||||
* Fixed rare issue where mod dependencies would override SMAPI dependencies and cause unpredictable bugs.
|
* Fixed rare issue where mod dependencies would override SMAPI dependencies and cause unpredictable bugs.
|
||||||
|
@ -912,7 +912,7 @@ For mod developers:
|
||||||
* Removed the experimental `IConfigFile`.
|
* Removed the experimental `IConfigFile`.
|
||||||
|
|
||||||
For SMAPI developers:
|
For SMAPI developers:
|
||||||
* Added support for debugging SMAPI on Linux/Mac if supported by the editor.
|
* Added support for debugging SMAPI on Linux/macOS if supported by the editor.
|
||||||
|
|
||||||
## 1.8
|
## 1.8
|
||||||
Released 04 February 2017 for Stardew Valley 1.1–1.11.
|
Released 04 February 2017 for Stardew Valley 1.1–1.11.
|
||||||
|
@ -1004,7 +1004,7 @@ For players:
|
||||||
* Improved installer wording to reduce confusion.
|
* Improved installer wording to reduce confusion.
|
||||||
* Fixed the installer not removing TrainerMod from appdata if it's already in the game mods directory.
|
* Fixed the installer not removing TrainerMod from appdata if it's already in the game mods directory.
|
||||||
* Fixed the installer not moving mods out of appdata if the game isn't installed on the same Windows partition.
|
* Fixed the installer not moving mods out of appdata if the game isn't installed on the same Windows partition.
|
||||||
* Fixed the SMAPI console not being shown on Linux and Mac.
|
* Fixed the SMAPI console not being shown on Linux and macOS.
|
||||||
|
|
||||||
For developers:
|
For developers:
|
||||||
* Added a reflection API (via `helper.Reflection`) that simplifies robust access to the game's private fields and methods.
|
* Added a reflection API (via `helper.Reflection`) that simplifies robust access to the game's private fields and methods.
|
||||||
|
@ -1016,7 +1016,7 @@ For developers:
|
||||||
Released 04 December 2016 for Stardew Valley 1.1–1.11.
|
Released 04 December 2016 for Stardew Valley 1.1–1.11.
|
||||||
|
|
||||||
For players:
|
For players:
|
||||||
* You can now run most mods on any platform (e.g. run Windows mods on Linux/Mac).
|
* You can now run most mods on any platform (e.g. run Windows mods on Linux/macOS).
|
||||||
* Fixed the normal uninstaller not removing files added by the 'SMAPI for developers' installer.
|
* Fixed the normal uninstaller not removing files added by the 'SMAPI for developers' installer.
|
||||||
|
|
||||||
## 1.2
|
## 1.2
|
||||||
|
@ -1063,7 +1063,7 @@ For developers:
|
||||||
Released 11 November 2016 for Stardew Valley 1.1–1.11.
|
Released 11 November 2016 for Stardew Valley 1.1–1.11.
|
||||||
|
|
||||||
For players:
|
For players:
|
||||||
* Added support for Linux and Mac.
|
* Added support for Linux and macOS.
|
||||||
* Added installer to automate adding & removing SMAPI.
|
* Added installer to automate adding & removing SMAPI.
|
||||||
* Added background update check on launch.
|
* Added background update check on launch.
|
||||||
* Fixed missing `steam_appid.txt` file.
|
* Fixed missing `steam_appid.txt` file.
|
||||||
|
|
|
@ -7,6 +7,31 @@
|
||||||
* Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info).
|
* Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 3.10
|
||||||
|
Released 03 May 2021 for Stardew Valley 1.5.4 or later. See [release highlights](https://www.patreon.com/posts/50764911).
|
||||||
|
|
||||||
|
* For players:
|
||||||
|
* Added full support for the [unofficial 64-bit Stardew Valley patch](https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows), which removes memory limits. The installer detects which version of SMAPI you need, and SMAPI shows update alerts for Stardew64Installer if applicable.
|
||||||
|
* Added smarter grouping for skipped mods, so it's easier to see root dependencies to update first.
|
||||||
|
* Added crash recovery when the game can't update a map's seasonal tilesheets _(in Error Handler)_. SMAPI will log an error and keep the previous tilesheets in that case.
|
||||||
|
* Added installer option to enter a custom game path even if it detected a game folder.
|
||||||
|
* `*.ico` files are now ignored when scanning for mods.
|
||||||
|
* Fixed error for non-English players after returning to title, reloading, and entering town with a completed movie theater.
|
||||||
|
* Fixed `world_clear` console command not removing resource clumps outside the farm and secret woods.
|
||||||
|
* Fixed error running SMAPI in a strict sandbox on Linux (thanks to kuesji!).
|
||||||
|
* Fixed `StardewModdingAPI.bin.osx` on macOS overwritten with an identical file on launch which would reset file permissions (thanks to 007wayne!).
|
||||||
|
* Fixed inconsistent spelling/style for 'macOS'.
|
||||||
|
|
||||||
|
* For modders:
|
||||||
|
* Added support for [ignoring local map tilesheet files when loading a map](https://stardewvalleywiki.com/Modding:Maps#Local_copy_of_a_vanilla_tilesheet).
|
||||||
|
* Added asset propagation for `Data\Concessions`.
|
||||||
|
* Added SMAPI version and bitness to the console title before startup to simplify troubleshooting.
|
||||||
|
* If a map loads a tilesheet path with no file extension, SMAPI now automatically links it to a `.png` version in the map folder if possible.
|
||||||
|
* Improved error-handling during asset propagation.
|
||||||
|
* Fixed `Context.IsMainPlayer` returning true for a farmhand in split-screen mode before the screen is initialized.
|
||||||
|
* Fixed error when editing bundle data while a split-screen player is joining.
|
||||||
|
* Fixed update subkeys not working in file descriptions for Nexus mods marked as adult content.
|
||||||
|
|
||||||
## 3.9.5
|
## 3.9.5
|
||||||
Released 21 March 2021 for Stardew Valley 1.5.4 or later.
|
Released 21 March 2021 for Stardew Valley 1.5.4 or later.
|
||||||
|
|
||||||
|
@ -236,7 +261,7 @@ Released 03 October 2020 for Stardew Valley 1.4.1 or later.
|
||||||
Released 16 September 2020 for Stardew Valley 1.4.1 or later.
|
Released 16 September 2020 for Stardew Valley 1.4.1 or later.
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* Fixed errors on Linux/Mac due to content packs with incorrect filename case.
|
* Fixed errors on Linux/macOS due to content packs with incorrect filename case.
|
||||||
* Fixed map rendering crash due to conflict between SMAPI and PyTK.
|
* Fixed map rendering crash due to conflict between SMAPI and PyTK.
|
||||||
* Fixed error in heuristically-rewritten mods in rare cases (thanks to collaboration with ZaneYork!).
|
* Fixed error in heuristically-rewritten mods in rare cases (thanks to collaboration with ZaneYork!).
|
||||||
|
|
||||||
|
@ -280,7 +305,7 @@ Released 07 September 2020 for Stardew Valley 1.4.1 or later. See [release highl
|
||||||
* For mod authors:
|
* For mod authors:
|
||||||
* Added `PathUtilities` to simplify working with file/asset names.
|
* Added `PathUtilities` to simplify working with file/asset names.
|
||||||
* You can now read/write `SDate` values to JSON (e.g. for `config.json`, network mod messages, etc).
|
* You can now read/write `SDate` values to JSON (e.g. for `config.json`, network mod messages, etc).
|
||||||
* Fixed asset propagation not updating title menu buttons immediately on Linux/Mac.
|
* Fixed asset propagation not updating title menu buttons immediately on Linux/macOS.
|
||||||
|
|
||||||
* For the web UI:
|
* For the web UI:
|
||||||
* Updated the JSON validator/schema for Content Patcher 1.16 and 1.17.
|
* Updated the JSON validator/schema for Content Patcher 1.16 and 1.17.
|
||||||
|
@ -325,7 +350,7 @@ Released 20 June 2020 for Stardew Valley 1.4.1 or later. See [release highlights
|
||||||
* Added experimental option to reduce startup time when loading mod DLLs (thanks to ZaneYork!). Enable `RewriteInParallel` in the `smapi-internal/config.json` to try it.
|
* Added experimental option to reduce startup time when loading mod DLLs (thanks to ZaneYork!). Enable `RewriteInParallel` in the `smapi-internal/config.json` to try it.
|
||||||
* Reduced processing time when a mod loads many unpacked images (thanks to Entoarox!).
|
* Reduced processing time when a mod loads many unpacked images (thanks to Entoarox!).
|
||||||
* Mod load warnings are now listed alphabetically.
|
* Mod load warnings are now listed alphabetically.
|
||||||
* MacOS files starting with `._` are now ignored and can no longer cause skipped mods.
|
* macOS files starting with `._` are now ignored and can no longer cause skipped mods.
|
||||||
* Simplified paranoid warning logs and reduced their log level.
|
* Simplified paranoid warning logs and reduced their log level.
|
||||||
* Fixed black maps on Android for mods which use `.tmx` files.
|
* Fixed black maps on Android for mods which use `.tmx` files.
|
||||||
* Fixed `BadImageFormatException` error detection.
|
* Fixed `BadImageFormatException` error detection.
|
||||||
|
@ -381,7 +406,7 @@ Released 27 April 2020 for Stardew Valley 1.4.1 or later. See [release highlight
|
||||||
* Added `SDate` fields/methods: `SeasonIndex`, `FromDaysSinceStart`, `FromWorldDate`, `ToWorldDate`, and `ToLocaleString` (thanks to kdau!).
|
* Added `SDate` fields/methods: `SeasonIndex`, `FromDaysSinceStart`, `FromWorldDate`, `ToWorldDate`, and `ToLocaleString` (thanks to kdau!).
|
||||||
* Added `SDate` translations taken from the Lookup Anything mod.¹
|
* Added `SDate` translations taken from the Lookup Anything mod.¹
|
||||||
* Fixed asset propagation for certain maps loaded through temporary content managers. This notably fixes unreliable patches to the farmhouse and town maps.
|
* Fixed asset propagation for certain maps loaded through temporary content managers. This notably fixes unreliable patches to the farmhouse and town maps.
|
||||||
* Fixed asset propagation on Linux/Mac for monster sprites, NPC dialogue, and NPC schedules.
|
* Fixed asset propagation on Linux/macOS for monster sprites, NPC dialogue, and NPC schedules.
|
||||||
* Fixed asset propagation for NPC dialogue sometimes causing a spouse to skip marriage dialogue or not allow kisses.
|
* Fixed asset propagation for NPC dialogue sometimes causing a spouse to skip marriage dialogue or not allow kisses.
|
||||||
|
|
||||||
¹ Date format translations were taken from the Lookup Anything mod; thanks to translators FixThisPlz (improved Russian), LeecanIt (added Italian), pomepome (added Japanese), S2SKY (added Korean), Sasara (added German), SteaNN (added Russian), ThomasGabrielDelavault (added Spanish), VincentRoth (added French), Yllelder (improved Spanish), and yuwenlan (added Chinese). Some translations for Korean, Hungarian, and Turkish were derived from the game translations.
|
¹ Date format translations were taken from the Lookup Anything mod; thanks to translators FixThisPlz (improved Russian), LeecanIt (added Italian), pomepome (added Japanese), S2SKY (added Korean), Sasara (added German), SteaNN (added Russian), ThomasGabrielDelavault (added Spanish), VincentRoth (added French), Yllelder (improved Spanish), and yuwenlan (added Chinese). Some translations for Korean, Hungarian, and Turkish were derived from the game translations.
|
||||||
|
@ -397,7 +422,7 @@ Released 24 March 2020 for Stardew Valley 1.4.1 or later.
|
||||||
Released 22 March 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/35161371).
|
Released 22 March 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/35161371).
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* Fixed semi-transparency issues on Linux/Mac in recent versions of Mono (e.g. pink shadows).
|
* Fixed semi-transparency issues on Linux/macOS in recent versions of Mono (e.g. pink shadows).
|
||||||
* Fixed `player_add` command error if you have broken XNB mods.
|
* Fixed `player_add` command error if you have broken XNB mods.
|
||||||
* Removed invalid-location check now handled by the game.
|
* Removed invalid-location check now handled by the game.
|
||||||
* Updated translations. Thanks to Annosz (added Hungarian)!
|
* Updated translations. Thanks to Annosz (added Hungarian)!
|
||||||
|
@ -439,7 +464,7 @@ Released 22 February 2020 for Stardew Valley 1.4.1 or later. See [release highli
|
||||||
* Updated translations. Thanks to xCarloC (added Italian)!
|
* Updated translations. Thanks to xCarloC (added Italian)!
|
||||||
|
|
||||||
* For the Save Backup mod:
|
* For the Save Backup mod:
|
||||||
* Fixed warning on MacOS when you have no saves yet.
|
* Fixed warning on macOS when you have no saves yet.
|
||||||
* Reduced log messages.
|
* Reduced log messages.
|
||||||
|
|
||||||
* For the web UI:
|
* For the web UI:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
← [SMAPI](../README.md)
|
← [SMAPI](../README.md)
|
||||||
|
|
||||||
The **mod build package** is an open-source NuGet package which automates the MSBuild configuration
|
The **mod build package** is an open-source NuGet package which automates the MSBuild configuration
|
||||||
for SMAPI mods and related tools. The package is fully compatible with Linux, Mac, and Windows.
|
for SMAPI mods and related tools. The package is fully compatible with Linux, macOS, and Windows.
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
* [Use](#use)
|
* [Use](#use)
|
||||||
|
@ -33,7 +33,7 @@ change how these work):
|
||||||
|
|
||||||
* **Add assembly references:**
|
* **Add assembly references:**
|
||||||
The package adds assembly references to SMAPI, Stardew Valley, xTile, and the game framework
|
The package adds assembly references to SMAPI, Stardew Valley, xTile, and the game framework
|
||||||
(MonoGame on Linux/Mac, XNA Framework on Windows). It automatically adjusts depending on which OS
|
(MonoGame on Linux/macOS, XNA Framework on Windows). It automatically adjusts depending on which OS
|
||||||
you're compiling it on. If you use [Harmony](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Harmony),
|
you're compiling it on. If you use [Harmony](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Harmony),
|
||||||
it can optionally add a reference to that too.
|
it can optionally add a reference to that too.
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ change how these work):
|
||||||
breakpoints](https://docs.microsoft.com/en-us/visualstudio/debugger/using-breakpoints?view=vs-2019)
|
breakpoints](https://docs.microsoft.com/en-us/visualstudio/debugger/using-breakpoints?view=vs-2019)
|
||||||
in your code while the game is running, or [make simple changes to the mod code without needing to
|
in your code while the game is running, or [make simple changes to the mod code without needing to
|
||||||
restart the game](https://docs.microsoft.com/en-us/visualstudio/debugger/edit-and-continue?view=vs-2019).
|
restart the game](https://docs.microsoft.com/en-us/visualstudio/debugger/edit-and-continue?view=vs-2019).
|
||||||
This is disabled on Linux/Mac due to limitations with the Mono wrapper.
|
This is disabled on Linux/macOS due to limitations with the Mono wrapper.
|
||||||
|
|
||||||
* **Preconfigure common settings:**
|
* **Preconfigure common settings:**
|
||||||
The package automatically enables `.pdb` files (so error logs show line numbers to simplify
|
The package automatically enables `.pdb` files (so error logs show line numbers to simplify
|
||||||
|
@ -82,7 +82,7 @@ There are two places you can put them:
|
||||||
|
|
||||||
1. Open the home folder on your computer (see instructions for
|
1. Open the home folder on your computer (see instructions for
|
||||||
[Linux](https://superuser.com/questions/409218/where-is-my-users-home-folder-in-ubuntu),
|
[Linux](https://superuser.com/questions/409218/where-is-my-users-home-folder-in-ubuntu),
|
||||||
[MacOS](https://www.cnet.com/how-to/how-to-find-your-macs-home-folder-and-add-it-to-finder/),
|
[macOS](https://www.cnet.com/how-to/how-to-find-your-macs-home-folder-and-add-it-to-finder/),
|
||||||
or [Windows](https://www.computerhope.com/issues/ch000109.htm)).
|
or [Windows](https://www.computerhope.com/issues/ch000109.htm)).
|
||||||
2. Create a `stardewvalley.targets` file with this content:
|
2. Create a `stardewvalley.targets` file with this content:
|
||||||
```xml
|
```xml
|
||||||
|
@ -132,7 +132,7 @@ The absolute path to the folder containing the game's installed mods (defaults t
|
||||||
<td><code>GameExecutableName</code></td>
|
<td><code>GameExecutableName</code></td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
The filename for the game's executable (i.e. `StardewValley.exe` on Linux/Mac or
|
The filename for the game's executable (i.e. `StardewValley.exe` on Linux/macOS or
|
||||||
`Stardew Valley.exe` on Windows). This is auto-detected, and you should almost never change this.
|
`Stardew Valley.exe` on Windows). This is auto-detected, and you should almost never change this.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
@ -484,7 +484,7 @@ Released 05 June 2017.
|
||||||
Released 23 January 2017.
|
Released 23 January 2017.
|
||||||
|
|
||||||
* Added support for setting a custom game path globally.
|
* Added support for setting a custom game path globally.
|
||||||
* Added default GOG path on Mac.
|
* Added default GOG path on macOS.
|
||||||
|
|
||||||
### 1.4
|
### 1.4
|
||||||
Released 11 January 2017.
|
Released 11 January 2017.
|
||||||
|
|
|
@ -33,7 +33,7 @@ argument | purpose
|
||||||
`--game-path "path"` | Specifies the full path to the folder containing the Stardew Valley executable, skipping automatic detection and any prompt to choose a path. If the path is not valid, the installer displays an error.
|
`--game-path "path"` | Specifies the full path to the folder containing the Stardew Valley executable, skipping automatic detection and any prompt to choose a path. If the path is not valid, the installer displays an error.
|
||||||
|
|
||||||
SMAPI itself recognises two arguments **on Windows only**, but these are intended for internal use
|
SMAPI itself recognises two arguments **on Windows only**, but these are intended for internal use
|
||||||
or testing and may change without warning. On Linux/Mac, see _environment variables_ below.
|
or testing and may change without warning. On Linux/macOS, see _environment variables_ below.
|
||||||
|
|
||||||
argument | purpose
|
argument | purpose
|
||||||
-------- | -------
|
-------- | -------
|
||||||
|
@ -41,7 +41,7 @@ argument | purpose
|
||||||
`--mods-path` | The path to search for mods, if not the standard `Mods` folder. This can be a path relative to the game folder (like `--mods-path "Mods (test)"`) or an absolute path.
|
`--mods-path` | The path to search for mods, if not the standard `Mods` folder. This can be a path relative to the game folder (like `--mods-path "Mods (test)"`) or an absolute path.
|
||||||
|
|
||||||
### Environment variables
|
### Environment variables
|
||||||
The above SMAPI arguments don't work on Linux/Mac due to the way the game launcher works. You can
|
The above SMAPI arguments don't work on Linux/macOS due to the way the game launcher works. You can
|
||||||
set temporary environment variables instead. For example:
|
set temporary environment variables instead. For example:
|
||||||
> SMAPI_MODS_PATH="Mods (multiplayer)" /path/to/StardewValley
|
> SMAPI_MODS_PATH="Mods (multiplayer)" /path/to/StardewValley
|
||||||
|
|
||||||
|
@ -56,8 +56,9 @@ SMAPI uses a small number of conditional compilation constants, which you can se
|
||||||
|
|
||||||
flag | purpose
|
flag | purpose
|
||||||
---- | -------
|
---- | -------
|
||||||
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled for Windows; if not set, the code assumes Linux/MacOS. Set automatically in `common.targets`.
|
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled for Windows; if not set, the code assumes Linux/macOS. Set automatically in `common.targets`.
|
||||||
`SMAPI_FOR_XNA` | Whether SMAPI is being compiled for XNA Framework; if not set, the code assumes MonoGame. Set automatically in `common.targets` with the same value as `SMAPI_FOR_WINDOWS`.
|
`SMAPI_FOR_WINDOWS_64BIT_HACK` | Whether SMAPI is being [compiled for Windows with a 64-bit Linux version of the game](https://github.com/Pathoschild/SMAPI/issues/767). This is highly specialized and shouldn't be used in most cases. False by default.
|
||||||
|
`SMAPI_FOR_XNA` | Whether SMAPI is being compiled for XNA Framework; if not set, the code assumes MonoGame. Set automatically in `common.targets` with the same value as `SMAPI_FOR_WINDOWS` (unless `SMAPI_FOR_WINDOWS_64BIT_HACK` is set).
|
||||||
`HARMONY_2` | Whether to enable experimental Harmony 2.0 support and rewrite existing Harmony 1._x_ mods for compatibility. Note that you need to replace `build/0Harmony.dll` with a Harmony 2.0 build (or switch to a package reference) to use this flag.
|
`HARMONY_2` | Whether to enable experimental Harmony 2.0 support and rewrite existing Harmony 1._x_ mods for compatibility. Note that you need to replace `build/0Harmony.dll` with a Harmony 2.0 build (or switch to a package reference) to use this flag.
|
||||||
|
|
||||||
## For SMAPI developers
|
## For SMAPI developers
|
||||||
|
@ -72,7 +73,7 @@ placed in a `bin` folder at the root of the Git repository.
|
||||||
|
|
||||||
### Debugging a local build
|
### Debugging a local build
|
||||||
Rebuilding the solution in debug mode will copy the SMAPI files into your game folder. Starting
|
Rebuilding the solution in debug mode will copy the SMAPI files into your game folder. Starting
|
||||||
the `SMAPI` project with debugging from Visual Studio (on Mac or Windows) will launch SMAPI with
|
the `SMAPI` project with debugging from Visual Studio (on macOS or Windows) will launch SMAPI with
|
||||||
the debugger attached, so you can intercept errors and step through the code being executed. That
|
the debugger attached, so you can intercept errors and step through the code being executed. That
|
||||||
doesn't work in MonoDevelop on Linux, unfortunately.
|
doesn't work in MonoDevelop on Linux, unfortunately.
|
||||||
|
|
||||||
|
@ -81,7 +82,9 @@ To prepare a crossplatform SMAPI release, you'll need to compile it on two platf
|
||||||
[crossplatforming info](https://stardewvalleywiki.com/Modding:Modder_Guide/Test_and_Troubleshoot#Testing_on_all_platforms)
|
[crossplatforming info](https://stardewvalleywiki.com/Modding:Modder_Guide/Test_and_Troubleshoot#Testing_on_all_platforms)
|
||||||
on the wiki for the first-time setup.
|
on the wiki for the first-time setup.
|
||||||
|
|
||||||
1. Update the version numbers in `build/common.targets`, `Constants`, and the `manifest.json` for
|
1. [Install a separate 64-bit version of Stardew Valley](https://github.com/Steviegt6/Stardew64Installer#readme)
|
||||||
|
on Windows.
|
||||||
|
2. Update the version numbers in `build/common.targets`, `Constants`, and the `manifest.json` for
|
||||||
bundled mods. Make sure you use a [semantic version](https://semver.org). Recommended format:
|
bundled mods. Make sure you use a [semantic version](https://semver.org). Recommended format:
|
||||||
|
|
||||||
build type | format | example
|
build type | format | example
|
||||||
|
@ -89,12 +92,15 @@ on the wiki for the first-time setup.
|
||||||
dev build | `<version>-alpha.<date>` | `3.0.0-alpha.20171230`
|
dev build | `<version>-alpha.<date>` | `3.0.0-alpha.20171230`
|
||||||
prerelease | `<version>-beta.<date>` | `3.0.0-beta.20171230`
|
prerelease | `<version>-beta.<date>` | `3.0.0-beta.20171230`
|
||||||
release | `<version>` | `3.0.0`
|
release | `<version>` | `3.0.0`
|
||||||
|
3. In Windows:
|
||||||
2. In Windows:
|
|
||||||
1. Rebuild the solution with the _release_ solution configuration.
|
1. Rebuild the solution with the _release_ solution configuration.
|
||||||
2. Copy `bin/SMAPI installer` and `bin/SMAPI installer for developers` to Linux/Mac.
|
2. Back up the `bin/SMAPI installer` and `bin/SMAPI installer for developers` folders.
|
||||||
|
3. Edit `common.targets` and uncomment the Stardew Valley 64-bit section at the top.
|
||||||
3. In Linux/Mac:
|
4. Rebuild the solution again.
|
||||||
|
5. Rename the compiled `StardewModdingAPI.exe` file to `StardewModdingAPI-x64.exe`, and copy it
|
||||||
|
into the `windows-install.dat` files from step ii.
|
||||||
|
6. Copy the folders from step ii to Linux/MacOS.
|
||||||
|
4. In Linux/macOS:
|
||||||
1. Rebuild the solution with the _release_ solution configuration.
|
1. Rebuild the solution with the _release_ solution configuration.
|
||||||
2. Add the `windows-install.*` files from Windows to the `bin/SMAPI installer` and
|
2. Add the `windows-install.*` files from Windows to the `bin/SMAPI installer` and
|
||||||
`bin/SMAPI installer for developers` folders compiled on Linux.
|
`bin/SMAPI installer for developers` folders compiled on Linux.
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
/// <summary>Whether the installer is running on Windows.</summary>
|
/// <summary>Whether the installer is running on Windows.</summary>
|
||||||
public bool IsWindows => this.Platform == Platform.Windows;
|
public bool IsWindows => this.Platform == Platform.Windows;
|
||||||
|
|
||||||
/// <summary>Whether the installer is running on a Unix OS (including Linux or MacOS).</summary>
|
/// <summary>Whether the installer is running on a Unix OS (including Linux or macOS).</summary>
|
||||||
public bool IsUnix => !this.IsWindows;
|
public bool IsUnix => !this.IsWindows;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,16 +44,16 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
/// <summary>The full path to the user's config overrides file.</summary>
|
/// <summary>The full path to the user's config overrides file.</summary>
|
||||||
public string ApiUserConfigPath { get; }
|
public string ApiUserConfigPath { get; }
|
||||||
|
|
||||||
/// <summary>The full path to the installed SMAPI executable file.</summary>
|
/// <summary>The full path to the installed game executable file.</summary>
|
||||||
public string ExecutablePath { get; }
|
public string ExecutablePath { get; private set; }
|
||||||
|
|
||||||
/// <summary>The full path to the vanilla game launcher on Linux/Mac.</summary>
|
/// <summary>The full path to the vanilla game launcher on Linux/macOS.</summary>
|
||||||
public string UnixLauncherPath { get; }
|
public string UnixLauncherPath { get; }
|
||||||
|
|
||||||
/// <summary>The full path to the installed SMAPI launcher on Linux/Mac before it's renamed.</summary>
|
/// <summary>The full path to the installed SMAPI launcher on Linux/macOS before it's renamed.</summary>
|
||||||
public string UnixSmapiLauncherPath { get; }
|
public string UnixSmapiLauncherPath { get; }
|
||||||
|
|
||||||
/// <summary>The full path to the vanilla game launcher on Linux/Mac after SMAPI is installed.</summary>
|
/// <summary>The full path to the vanilla game launcher on Linux/macOS after SMAPI is installed.</summary>
|
||||||
public string UnixBackupLauncherPath { get; }
|
public string UnixBackupLauncherPath { get; }
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,5 +79,12 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
this.ApiConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.json");
|
this.ApiConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.json");
|
||||||
this.ApiUserConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.user.json");
|
this.ApiUserConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.user.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Override the filename for the <see cref="ExecutablePath"/>.</summary>
|
||||||
|
/// <param name="filename">the file name.</param>
|
||||||
|
public void SetExecutableFileName(string filename)
|
||||||
|
{
|
||||||
|
this.ExecutablePath = Path.Combine(this.GamePath, filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -10,9 +11,6 @@ using StardewModdingAPI.Internal.ConsoleWriting;
|
||||||
using StardewModdingAPI.Toolkit;
|
using StardewModdingAPI.Toolkit;
|
||||||
using StardewModdingAPI.Toolkit.Framework.ModScanning;
|
using StardewModdingAPI.Toolkit.Framework.ModScanning;
|
||||||
using StardewModdingAPI.Toolkit.Utilities;
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
#if !SMAPI_FOR_WINDOWS
|
|
||||||
using System.Diagnostics;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace StardewModdingApi.Installer
|
namespace StardewModdingApi.Installer
|
||||||
{
|
{
|
||||||
|
@ -40,11 +38,11 @@ namespace StardewModdingApi.Installer
|
||||||
string GetInstallPath(string path) => Path.Combine(installDir.FullName, path);
|
string GetInstallPath(string path) => Path.Combine(installDir.FullName, path);
|
||||||
|
|
||||||
// current files
|
// current files
|
||||||
yield return GetInstallPath("libgdiplus.dylib"); // Linux/Mac only
|
yield return GetInstallPath("libgdiplus.dylib"); // Linux/macOS only
|
||||||
yield return GetInstallPath("StardewModdingAPI"); // Linux/Mac only
|
yield return GetInstallPath("StardewModdingAPI"); // Linux/macOS only
|
||||||
yield return GetInstallPath("StardewModdingAPI.exe");
|
yield return GetInstallPath("StardewModdingAPI.exe");
|
||||||
yield return GetInstallPath("StardewModdingAPI.exe.config");
|
yield return GetInstallPath("StardewModdingAPI.exe.config");
|
||||||
yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // Linux/Mac only
|
yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // Linux/macOS only
|
||||||
yield return GetInstallPath("StardewModdingAPI.pdb"); // Windows only
|
yield return GetInstallPath("StardewModdingAPI.pdb"); // Windows only
|
||||||
yield return GetInstallPath("StardewModdingAPI.xml");
|
yield return GetInstallPath("StardewModdingAPI.xml");
|
||||||
yield return GetInstallPath("smapi-internal");
|
yield return GetInstallPath("smapi-internal");
|
||||||
|
@ -106,13 +104,13 @@ namespace StardewModdingApi.Installer
|
||||||
/// 2. Ask the user whether to install or uninstall.
|
/// 2. Ask the user whether to install or uninstall.
|
||||||
///
|
///
|
||||||
/// Uninstall logic:
|
/// Uninstall logic:
|
||||||
/// 1. On Linux/Mac: if a backup of the launcher exists, delete the launcher and restore the backup.
|
/// 1. On Linux/macOS: if a backup of the launcher exists, delete the launcher and restore the backup.
|
||||||
/// 2. Delete all files and folders in the game directory matching one of the values returned by <see cref="GetUninstallPaths"/>.
|
/// 2. Delete all files and folders in the game directory matching one of the values returned by <see cref="GetUninstallPaths"/>.
|
||||||
///
|
///
|
||||||
/// Install flow:
|
/// Install flow:
|
||||||
/// 1. Run the uninstall flow.
|
/// 1. Run the uninstall flow.
|
||||||
/// 2. Copy the SMAPI files from package/Windows or package/Mono into the game directory.
|
/// 2. Copy the SMAPI files from package/Windows or package/Mono into the game directory.
|
||||||
/// 3. On Linux/Mac: back up the game launcher and replace it with the SMAPI launcher. (This isn't possible on Windows, so the user needs to configure it manually.)
|
/// 3. On Linux/macOS: back up the game launcher and replace it with the SMAPI launcher. (This isn't possible on Windows, so the user needs to configure it manually.)
|
||||||
/// 4. Create the 'Mods' directory.
|
/// 4. Create the 'Mods' directory.
|
||||||
/// 5. Copy the bundled mods into the 'Mods' directory (deleting any existing versions).
|
/// 5. Copy the bundled mods into the 'Mods' directory (deleting any existing versions).
|
||||||
/// 6. Move any mods from app data into game's mods directory.
|
/// 6. Move any mods from app data into game's mods directory.
|
||||||
|
@ -143,7 +141,7 @@ namespace StardewModdingApi.Installer
|
||||||
#else
|
#else
|
||||||
if (context.IsWindows)
|
if (context.IsWindows)
|
||||||
{
|
{
|
||||||
this.PrintError($"This is the installer for Linux/Mac. Run the 'install on Windows.exe' file instead.");
|
this.PrintError($"This is the installer for Linux/macOS. Run the 'install on Windows.exe' file instead.");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +194,7 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Step 2: choose a theme (can't auto-detect on Linux/Mac)
|
** Step 2: choose a theme (can't auto-detect on Linux/macOS)
|
||||||
*********/
|
*********/
|
||||||
MonitorColorScheme scheme = MonitorColorScheme.AutoDetect;
|
MonitorColorScheme scheme = MonitorColorScheme.AutoDetect;
|
||||||
if (context.IsUnix)
|
if (context.IsUnix)
|
||||||
|
@ -260,7 +258,6 @@ namespace StardewModdingApi.Installer
|
||||||
** collect details
|
** collect details
|
||||||
****/
|
****/
|
||||||
// get game path
|
// get game path
|
||||||
this.PrintInfo("Where is your game folder?");
|
|
||||||
DirectoryInfo installDir = this.InteractivelyGetInstallPath(toolkit, context, gamePathArg);
|
DirectoryInfo installDir = this.InteractivelyGetInstallPath(toolkit, context, gamePathArg);
|
||||||
if (installDir == null)
|
if (installDir == null)
|
||||||
{
|
{
|
||||||
|
@ -277,7 +274,20 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Step 4: validate assumptions
|
** Step 4: detect 64-bit Stardew Valley
|
||||||
|
*********/
|
||||||
|
// detect 64-bit mode
|
||||||
|
bool isWindows64Bit = false;
|
||||||
|
if (context.Platform == Platform.Windows)
|
||||||
|
{
|
||||||
|
FileInfo linuxExecutable = new FileInfo(Path.Combine(paths.GamePath, "StardewValley.exe"));
|
||||||
|
isWindows64Bit = linuxExecutable.Exists && this.Is64Bit(linuxExecutable.FullName);
|
||||||
|
if (isWindows64Bit)
|
||||||
|
paths.SetExecutableFileName(linuxExecutable.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Step 5: validate assumptions
|
||||||
*********/
|
*********/
|
||||||
// executable exists
|
// executable exists
|
||||||
if (!File.Exists(paths.ExecutablePath))
|
if (!File.Exists(paths.ExecutablePath))
|
||||||
|
@ -300,7 +310,7 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Step 5: ask what to do
|
** Step 6: ask what to do
|
||||||
*********/
|
*********/
|
||||||
ScriptAction action;
|
ScriptAction action;
|
||||||
{
|
{
|
||||||
|
@ -308,7 +318,7 @@ namespace StardewModdingApi.Installer
|
||||||
** print header
|
** print header
|
||||||
****/
|
****/
|
||||||
this.PrintInfo("Hi there! I'll help you install or remove SMAPI. Just one question first.");
|
this.PrintInfo("Hi there! I'll help you install or remove SMAPI. Just one question first.");
|
||||||
this.PrintDebug($"Game path: {paths.GamePath}");
|
this.PrintDebug($"Game path: {paths.GamePath}{(context.IsWindows ? $" [{(isWindows64Bit ? "64-bit" : "32-bit")}]" : "")}");
|
||||||
this.PrintDebug($"Color scheme: {this.GetDisplayText(scheme)}");
|
this.PrintDebug($"Color scheme: {this.GetDisplayText(scheme)}");
|
||||||
this.PrintDebug("----------------------------------------------------------------------------");
|
this.PrintDebug("----------------------------------------------------------------------------");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
@ -346,14 +356,14 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Step 6: apply
|
** Step 7: apply
|
||||||
*********/
|
*********/
|
||||||
{
|
{
|
||||||
/****
|
/****
|
||||||
** print header
|
** print header
|
||||||
****/
|
****/
|
||||||
this.PrintInfo($"That's all I need! I'll {action.ToString().ToLower()} SMAPI now.");
|
this.PrintInfo($"That's all I need! I'll {action.ToString().ToLower()} SMAPI now.");
|
||||||
this.PrintDebug($"Game path: {paths.GamePath}");
|
this.PrintDebug($"Game path: {paths.GamePath}{(context.IsWindows ? $" [{(isWindows64Bit ? "64-bit" : "32-bit")}]" : "")}");
|
||||||
this.PrintDebug($"Color scheme: {this.GetDisplayText(scheme)}");
|
this.PrintDebug($"Color scheme: {this.GetDisplayText(scheme)}");
|
||||||
this.PrintDebug("----------------------------------------------------------------------------");
|
this.PrintDebug("----------------------------------------------------------------------------");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
@ -414,6 +424,27 @@ namespace StardewModdingApi.Installer
|
||||||
this.RecursiveCopy(sourceEntry, paths.GameDir);
|
this.RecursiveCopy(sourceEntry, paths.GameDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isWindows64Bit)
|
||||||
|
{
|
||||||
|
this.PrintDebug("Making SMAPI 64-bit...");
|
||||||
|
FileInfo x64Executable = new FileInfo(Path.Combine(paths.BundleDir.FullName, "StardewModdingAPI-x64.exe"));
|
||||||
|
if (x64Executable.Exists)
|
||||||
|
{
|
||||||
|
string targetName = "StardewModdingAPI.exe";
|
||||||
|
this.InteractivelyDelete(Path.Combine(paths.GameDir.FullName, targetName));
|
||||||
|
this.InteractivelyDelete(Path.Combine(paths.GameDir.FullName, x64Executable.Name));
|
||||||
|
|
||||||
|
this.RecursiveCopy(x64Executable, paths.GameDir);
|
||||||
|
File.Move(Path.Combine(paths.GamePath, x64Executable.Name), Path.Combine(paths.GamePath, targetName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.PrintError($"Oops! Could not find the required '{x64Executable.Name}' installer file. SMAPI was unable to install correctly.");
|
||||||
|
Console.ReadLine();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// replace mod launcher (if possible)
|
// replace mod launcher (if possible)
|
||||||
if (context.IsUnix)
|
if (context.IsUnix)
|
||||||
{
|
{
|
||||||
|
@ -433,8 +464,6 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
// mark file executable
|
// mark file executable
|
||||||
// (MSBuild doesn't keep permission flags for files zipped in a build task.)
|
// (MSBuild doesn't keep permission flags for files zipped in a build task.)
|
||||||
// (Note: exclude from Windows build because antivirus apps can flag the process start code as suspicious.)
|
|
||||||
#if !SMAPI_FOR_WINDOWS
|
|
||||||
new Process
|
new Process
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo
|
StartInfo = new ProcessStartInfo
|
||||||
|
@ -444,7 +473,6 @@ namespace StardewModdingApi.Installer
|
||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
}
|
}
|
||||||
}.Start();
|
}.Start();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create mods directory (if needed)
|
// create mods directory (if needed)
|
||||||
|
@ -540,6 +568,13 @@ namespace StardewModdingApi.Installer
|
||||||
/*********
|
/*********
|
||||||
** Private methods
|
** Private methods
|
||||||
*********/
|
*********/
|
||||||
|
/// <summary>Get whether an executable is 64-bit.</summary>
|
||||||
|
/// <param name="executablePath">The absolute path to the executable file.</param>
|
||||||
|
private bool Is64Bit(string executablePath)
|
||||||
|
{
|
||||||
|
return AssemblyName.GetAssemblyName(executablePath).ProcessorArchitecture != ProcessorArchitecture.X86;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Get the display text for a color scheme.</summary>
|
/// <summary>Get the display text for a color scheme.</summary>
|
||||||
/// <param name="scheme">The color scheme.</param>
|
/// <param name="scheme">The color scheme.</param>
|
||||||
private string GetDisplayText(MonitorColorScheme scheme)
|
private string GetDisplayText(MonitorColorScheme scheme)
|
||||||
|
@ -676,56 +711,44 @@ namespace StardewModdingApi.Installer
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
// use game folder which contains the installer, if any
|
// let user choose detected path
|
||||||
{
|
DirectoryInfo[] defaultPaths = this.DetectGameFolders(toolkit, context).ToArray();
|
||||||
DirectoryInfo curPath = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
|
|
||||||
while (curPath?.Parent != null) // must be in a folder (not at the root)
|
|
||||||
{
|
|
||||||
if (context.LooksLikeGameFolder(curPath))
|
|
||||||
return curPath;
|
|
||||||
|
|
||||||
curPath = curPath.Parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use an installed path
|
|
||||||
DirectoryInfo[] defaultPaths = toolkit.GetGameFolders().ToArray();
|
|
||||||
if (defaultPaths.Any())
|
if (defaultPaths.Any())
|
||||||
{
|
{
|
||||||
// only one path
|
this.PrintInfo("Where do you want to add or remove SMAPI?");
|
||||||
if (defaultPaths.Length == 1)
|
|
||||||
return defaultPaths.First();
|
|
||||||
|
|
||||||
// let user choose path
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
this.PrintInfo("Found multiple copies of the game:");
|
|
||||||
for (int i = 0; i < defaultPaths.Length; i++)
|
for (int i = 0; i < defaultPaths.Length; i++)
|
||||||
this.PrintInfo($"[{i + 1}] {defaultPaths[i].FullName}");
|
this.PrintInfo($"[{i + 1}] {defaultPaths[i].FullName}");
|
||||||
|
this.PrintInfo($"[{defaultPaths.Length + 1}] Enter a custom game path.");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
string[] validOptions = Enumerable.Range(1, defaultPaths.Length).Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray();
|
string[] validOptions = Enumerable.Range(1, defaultPaths.Length + 1).Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray();
|
||||||
string choice = this.InteractivelyChoose("Where do you want to add/remove SMAPI? Type the number next to your choice, then press enter.", validOptions);
|
string choice = this.InteractivelyChoose("Type the number next to your choice, then press enter.", validOptions);
|
||||||
int index = int.Parse(choice, CultureInfo.InvariantCulture) - 1;
|
int index = int.Parse(choice, CultureInfo.InvariantCulture) - 1;
|
||||||
return defaultPaths[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ask user
|
if (index < defaultPaths.Length)
|
||||||
this.PrintInfo("Oops, couldn't find the game automatically.");
|
return defaultPaths[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.PrintInfo("Oops, couldn't find the game automatically.");
|
||||||
|
|
||||||
|
// let user enter manual path
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// get path from user
|
// get path from user
|
||||||
|
Console.WriteLine();
|
||||||
this.PrintInfo($"Type the file path to the game directory (the one containing '{context.ExecutableName}'), then press enter.");
|
this.PrintInfo($"Type the file path to the game directory (the one containing '{context.ExecutableName}'), then press enter.");
|
||||||
string path = Console.ReadLine()?.Trim();
|
string path = Console.ReadLine()?.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
{
|
{
|
||||||
this.PrintInfo(" You must specify a directory path to continue.");
|
this.PrintWarning("You must specify a directory path to continue.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize path
|
// normalize path
|
||||||
path = context.IsWindows
|
path = context.IsWindows
|
||||||
? path.Replace("\"", "") // in Windows, quotes are used to escape spaces and aren't part of the file path
|
? path.Replace("\"", "") // in Windows, quotes are used to escape spaces and aren't part of the file path
|
||||||
: path.Replace("\\ ", " "); // in Linux/Mac, spaces in paths may be escaped if copied from the command line
|
: path.Replace("\\ ", " "); // in Linux/macOS, spaces in paths may be escaped if copied from the command line
|
||||||
if (path.StartsWith("~/"))
|
if (path.StartsWith("~/"))
|
||||||
{
|
{
|
||||||
string home = Environment.GetEnvironmentVariable("HOME") ?? Environment.GetEnvironmentVariable("USERPROFILE");
|
string home = Environment.GetEnvironmentVariable("HOME") ?? Environment.GetEnvironmentVariable("USERPROFILE");
|
||||||
|
@ -740,12 +763,12 @@ namespace StardewModdingApi.Installer
|
||||||
// validate path
|
// validate path
|
||||||
if (!directory.Exists)
|
if (!directory.Exists)
|
||||||
{
|
{
|
||||||
this.PrintInfo(" That directory doesn't seem to exist.");
|
this.PrintWarning("That directory doesn't seem to exist.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!context.LooksLikeGameFolder(directory))
|
if (!context.LooksLikeGameFolder(directory))
|
||||||
{
|
{
|
||||||
this.PrintInfo(" That directory doesn't contain a Stardew Valley executable.");
|
this.PrintWarning("That directory doesn't contain a Stardew Valley executable.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,6 +778,37 @@ namespace StardewModdingApi.Installer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the possible game paths to update.</summary>
|
||||||
|
/// <param name="toolkit">The mod toolkit.</param>
|
||||||
|
/// <param name="context">The installer context.</param>
|
||||||
|
private IEnumerable<DirectoryInfo> DetectGameFolders(ModToolkit toolkit, InstallerContext context)
|
||||||
|
{
|
||||||
|
HashSet<string> foundPaths = new HashSet<string>();
|
||||||
|
|
||||||
|
// game folder which contains the installer, if any
|
||||||
|
{
|
||||||
|
DirectoryInfo curPath = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
|
||||||
|
while (curPath?.Parent != null) // must be in a folder (not at the root)
|
||||||
|
{
|
||||||
|
if (context.LooksLikeGameFolder(curPath))
|
||||||
|
{
|
||||||
|
foundPaths.Add(curPath.FullName);
|
||||||
|
yield return curPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
curPath = curPath.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// game paths detected by toolkit
|
||||||
|
foreach (DirectoryInfo dir in toolkit.GetGameFolders())
|
||||||
|
{
|
||||||
|
if (foundPaths.Add(dir.FullName))
|
||||||
|
yield return dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Interactively move mods out of the appdata directory.</summary>
|
/// <summary>Interactively move mods out of the appdata directory.</summary>
|
||||||
/// <param name="properModsDir">The directory which should contain all mods.</param>
|
/// <param name="properModsDir">The directory which should contain all mods.</param>
|
||||||
/// <param name="packagedModsDir">The installer directory containing packaged mods.</param>
|
/// <param name="packagedModsDir">The installer directory containing packaged mods.</param>
|
||||||
|
@ -845,7 +899,7 @@ namespace StardewModdingApi.Installer
|
||||||
switch (entry.Name)
|
switch (entry.Name)
|
||||||
{
|
{
|
||||||
case "mcs":
|
case "mcs":
|
||||||
return false; // ignore Mac symlink
|
return false; // ignore macOS symlink
|
||||||
case "Mods":
|
case "Mods":
|
||||||
return false; // Mods folder handled separately
|
return false; // Mods folder handled separately
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -24,19 +24,20 @@ Manual install
|
||||||
THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See instructions above instead.
|
THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See instructions above instead.
|
||||||
If you really want to install SMAPI manually, here's how.
|
If you really want to install SMAPI manually, here's how.
|
||||||
|
|
||||||
1. Unzip "internal/windows-install.dat" (on Windows) or "internal/unix-install.dat" (on Linux/Mac).
|
1. Unzip "internal/windows-install.dat" (on Windows) or "internal/unix-install.dat" (on
|
||||||
You can change '.dat' to '.zip', it's just a normal zip file renamed to prevent confusion.
|
Linux/macOS). You can change '.dat' to '.zip', it's just a normal zip file renamed to prevent
|
||||||
|
confusion.
|
||||||
2. Copy the files from the folder you just unzipped into your game folder. The
|
2. Copy the files from the folder you just unzipped into your game folder. The
|
||||||
`StardewModdingAPI.exe` file should be right next to the game's executable.
|
`StardewModdingAPI.exe` file should be right next to the game's executable.
|
||||||
3.
|
3.
|
||||||
- Windows only: if you use Steam, see the install guide above to enable achievements and
|
- Windows only: if you use Steam, see the install guide above to enable achievements and
|
||||||
overlay. Otherwise, just run StardewModdingAPI.exe in your game folder to play with mods.
|
overlay. Otherwise, just run StardewModdingAPI.exe in your game folder to play with mods.
|
||||||
|
|
||||||
- Linux/Mac only: rename the "StardewValley" file (no extension) to "StardewValley-original", and
|
- Linux/macOS only: rename the "StardewValley" file (no extension) to "StardewValley-original", and
|
||||||
"StardewModdingAPI" (no extension) to "StardewValley". Now just launch the game as usual to
|
"StardewModdingAPI" (no extension) to "StardewValley". Now just launch the game as usual to
|
||||||
play with mods.
|
play with mods.
|
||||||
|
|
||||||
When installing on Linux or Mac:
|
When installing on Linux or macOS:
|
||||||
- Make sure Mono is installed (normally the installer checks for you). While it's not required,
|
- Make sure Mono is installed (normally the installer checks for you). While it's not required,
|
||||||
many mods won't work correctly without it. (Specifically, mods which load PNG images may crash or
|
many mods won't work correctly without it. (Specifically, mods which load PNG images may crash or
|
||||||
freeze the game.)
|
freeze the game.)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Run the SMAPI installer through Mono on Linux or Mac.
|
# Run the SMAPI installer through Mono on Linux or macOS.
|
||||||
|
|
||||||
# Move to script's directory
|
# Move to script's directory
|
||||||
cd "`dirname "$0"`"
|
cd "`dirname "$0"`"
|
||||||
|
|
|
@ -37,73 +37,86 @@ if [ "$UNAME" == "Darwin" ]; then
|
||||||
ln -s /Library/Frameworks/Mono.framework/Versions/Current/lib/libgdiplus.dylib libgdiplus.dylib
|
ln -s /Library/Frameworks/Mono.framework/Versions/Current/lib/libgdiplus.dylib libgdiplus.dylib
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# create bin file
|
||||||
|
# Note: don't overwrite if it's identical, to avoid resetting permission flags
|
||||||
|
if [ ! -x StardewModdingAPI.bin.osx ] || ! cmp StardewValley.bin.osx StardewModdingAPI.bin.osx >/dev/null 2>&1; then
|
||||||
|
cp -p StardewValley.bin.osx StardewModdingAPI.bin.osx
|
||||||
|
fi
|
||||||
|
|
||||||
# launch SMAPI
|
# launch SMAPI
|
||||||
cp StardewValley.bin.osx StardewModdingAPI.bin.osx
|
|
||||||
open -a Terminal ./StardewModdingAPI.bin.osx "$@"
|
open -a Terminal ./StardewModdingAPI.bin.osx "$@"
|
||||||
else
|
else
|
||||||
# choose launcher
|
# choose binary file to launch
|
||||||
LAUNCHER=""
|
LAUNCH_FILE=""
|
||||||
if [ "$ARCH" == "x86_64" ]; then
|
if [ "$ARCH" == "x86_64" ]; then
|
||||||
ln -sf mcs.bin.x86_64 mcs
|
ln -sf mcs.bin.x86_64 mcs
|
||||||
cp StardewValley.bin.x86_64 StardewModdingAPI.bin.x86_64
|
cp StardewValley.bin.x86_64 StardewModdingAPI.bin.x86_64
|
||||||
LAUNCHER="./StardewModdingAPI.bin.x86_64"
|
LAUNCH_FILE="./StardewModdingAPI.bin.x86_64"
|
||||||
else
|
else
|
||||||
ln -sf mcs.bin.x86 mcs
|
ln -sf mcs.bin.x86 mcs
|
||||||
cp StardewValley.bin.x86 StardewModdingAPI.bin.x86
|
cp StardewValley.bin.x86 StardewModdingAPI.bin.x86
|
||||||
LAUNCHER="./StardewModdingAPI.bin.x86"
|
LAUNCH_FILE="./StardewModdingAPI.bin.x86"
|
||||||
fi
|
|
||||||
export LAUNCHER
|
|
||||||
|
|
||||||
# get cross-distro version of POSIX command
|
|
||||||
COMMAND=""
|
|
||||||
if command -v command 2>/dev/null; then
|
|
||||||
COMMAND="command -v"
|
|
||||||
elif type type 2>/dev/null; then
|
|
||||||
COMMAND="type -p"
|
|
||||||
fi
|
fi
|
||||||
|
export LAUNCH_FILE
|
||||||
|
|
||||||
# select terminal (prefer xterm for best compatibility, then known supported terminals)
|
# select terminal (prefer xterm for best compatibility, then known supported terminals)
|
||||||
for terminal in xterm gnome-terminal kitty terminator xfce4-terminal konsole terminal termite alacritty mate-terminal x-terminal-emulator; do
|
for terminal in xterm gnome-terminal kitty terminator xfce4-terminal konsole terminal termite alacritty mate-terminal x-terminal-emulator; do
|
||||||
if $COMMAND "$terminal" 2>/dev/null; then
|
if command -v "$terminal" 2>/dev/null; then
|
||||||
export LAUNCHTERM=$terminal
|
export TERMINAL_NAME=$terminal
|
||||||
break;
|
break;
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# find the true shell behind x-terminal-emulator
|
# find the true shell behind x-terminal-emulator
|
||||||
if [ "$LAUNCHTERM" = "x-terminal-emulator" ]; then
|
if [ "$TERMINAL_NAME" = "x-terminal-emulator" ]; then
|
||||||
export LAUNCHTERM="$(basename "$(readlink -f $(COMMAND x-terminal-emulator))")"
|
export TERMINAL_NAME="$(basename "$(readlink -f $(command -v x-terminal-emulator))")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# run in selected terminal and account for quirks
|
# run in selected terminal and account for quirks
|
||||||
case $LAUNCHTERM in
|
export TERMINAL_PATH="$(command -v $TERMINAL_NAME)"
|
||||||
terminal|termite)
|
if [ -x $TERMINAL_PATH ]; then
|
||||||
# LAUNCHTERM consumes only one argument after -e
|
case $TERMINAL_NAME in
|
||||||
# options containing space characters are unsupported
|
terminal|termite)
|
||||||
exec $LAUNCHTERM -e "env TERM=xterm $LAUNCHER $@"
|
# consumes only one argument after -e
|
||||||
;;
|
# options containing space characters are unsupported
|
||||||
xterm|konsole|alacritty)
|
exec $TERMINAL_NAME -e "env TERM=xterm $LAUNCH_FILE $@"
|
||||||
# LAUNCHTERM consumes all arguments after -e
|
;;
|
||||||
exec $LAUNCHTERM -e env TERM=xterm $LAUNCHER "$@"
|
|
||||||
;;
|
xterm|konsole|alacritty)
|
||||||
terminator|xfce4-terminal|mate-terminal)
|
# consumes all arguments after -e
|
||||||
# LAUNCHTERM consumes all arguments after -x
|
exec $TERMINAL_NAME -e env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
exec $LAUNCHTERM -x env TERM=xterm $LAUNCHER "$@"
|
;;
|
||||||
;;
|
|
||||||
gnome-terminal)
|
terminator|xfce4-terminal|mate-terminal)
|
||||||
# LAUNCHTERM consumes all arguments after --
|
# consumes all arguments after -x
|
||||||
exec $LAUNCHTERM -- env TERM=xterm $LAUNCHER "$@"
|
exec $TERMINAL_NAME -x env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
;;
|
;;
|
||||||
kitty)
|
|
||||||
# LAUNCHTERM consumes all trailing arguments
|
gnome-terminal)
|
||||||
exec $LAUNCHTERM env TERM=xterm $LAUNCHER "$@"
|
# consumes all arguments after --
|
||||||
;;
|
exec $TERMINAL_NAME -- env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
*)
|
;;
|
||||||
# If we don't know the terminal, just try to run it in the current shell.
|
|
||||||
env TERM=xterm $LAUNCHER "$@"
|
kitty)
|
||||||
# if THAT fails, launch with no output
|
# consumes all trailing arguments
|
||||||
if [ $? -eq 127 ]; then
|
exec $TERMINAL_NAME env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
exec $LAUNCHER --no-terminal "$@"
|
;;
|
||||||
fi
|
|
||||||
esac
|
*)
|
||||||
|
# If we don't know the terminal, just try to run it in the current shell.
|
||||||
|
# If THAT fails, launch with no output.
|
||||||
|
env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
|
if [ $? -eq 127 ]; then
|
||||||
|
exec $LAUNCH_FILE --no-terminal "$@"
|
||||||
|
fi
|
||||||
|
esac
|
||||||
|
|
||||||
|
## terminal isn't executable; fallback to current shell or no terminal
|
||||||
|
else
|
||||||
|
echo "The '$TERMINAL_NAME' terminal isn't executable. SMAPI might be running in a sandbox or the system might be misconfigured? Falling back to current shell."
|
||||||
|
env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
|
if [ $? -eq 127 ]; then
|
||||||
|
exec $LAUNCH_FILE --no-terminal "$@"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -129,7 +129,7 @@ namespace StardewModdingAPI.Internal.ConsoleWriting
|
||||||
if (schemeID == MonitorColorScheme.AutoDetect)
|
if (schemeID == MonitorColorScheme.AutoDetect)
|
||||||
{
|
{
|
||||||
schemeID = platform == Platform.Mac
|
schemeID = platform == Platform.Mac
|
||||||
? MonitorColorScheme.LightBackground // MacOS doesn't provide console background color info, but it's usually white.
|
? MonitorColorScheme.LightBackground // macOS doesn't provide console background color info, but it's usually white.
|
||||||
: ColorfulConsoleWriter.IsDark(Console.BackgroundColor) ? MonitorColorScheme.DarkBackground : MonitorColorScheme.LightBackground;
|
: ColorfulConsoleWriter.IsDark(Console.BackgroundColor) ? MonitorColorScheme.DarkBackground : MonitorColorScheme.LightBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.10.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.10.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0">
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
using StardewValley.Locations;
|
using StardewValley.Locations;
|
||||||
|
@ -224,18 +223,17 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
|
||||||
{
|
{
|
||||||
int removed = 0;
|
int removed = 0;
|
||||||
|
|
||||||
// get resource clumps
|
foreach (var clump in location.resourceClumps.Where(shouldRemove).ToArray())
|
||||||
IList<ResourceClump> resourceClumps =
|
|
||||||
(location as Farm)?.resourceClumps
|
|
||||||
?? (IList<ResourceClump>)(location as Woods)?.stumps
|
|
||||||
?? new List<ResourceClump>();
|
|
||||||
|
|
||||||
// remove matching clumps
|
|
||||||
foreach (var clump in resourceClumps.ToArray())
|
|
||||||
{
|
{
|
||||||
if (shouldRemove(clump))
|
location.resourceClumps.Remove(clump);
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location is Woods woods)
|
||||||
|
{
|
||||||
|
foreach (ResourceClump clump in woods.stumps.Where(shouldRemove).ToArray())
|
||||||
{
|
{
|
||||||
resourceClumps.Remove(clump);
|
woods.stumps.Remove(clump);
|
||||||
removed++;
|
removed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<!-- Windows only -->
|
<!-- Windows only -->
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
||||||
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
|
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" Condition="!$(DefineConstants.Contains(SMAPI_FOR_WINDOWS_64BIT_HACK))" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Game framework -->
|
<!-- Game framework -->
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"Name": "Console Commands",
|
"Name": "Console Commands",
|
||||||
"Author": "SMAPI",
|
"Author": "SMAPI",
|
||||||
"Version": "3.9.5",
|
"Version": "3.10.0",
|
||||||
"Description": "Adds SMAPI console commands that let you manipulate the game.",
|
"Description": "Adds SMAPI console commands that let you manipulate the game.",
|
||||||
"UniqueID": "SMAPI.ConsoleCommands",
|
"UniqueID": "SMAPI.ConsoleCommands",
|
||||||
"EntryDll": "ConsoleCommands.dll",
|
"EntryDll": "ConsoleCommands.dll",
|
||||||
"MinimumApiVersion": "3.9.5"
|
"MinimumApiVersion": "3.10.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,11 @@ using Harmony;
|
||||||
#endif
|
#endif
|
||||||
using StardewModdingAPI.Framework.Patching;
|
using StardewModdingAPI.Framework.Patching;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
using xTile;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Mods.ErrorHandler.Patches
|
namespace StardewModdingAPI.Mods.ErrorHandler.Patches
|
||||||
{
|
{
|
||||||
/// <summary>A Harmony patch for <see cref="GameLocation.checkEventPrecondition"/> which intercepts invalid preconditions and logs an error instead of crashing.</summary>
|
/// <summary>Harmony patches for <see cref="GameLocation.checkEventPrecondition"/> and <see cref="GameLocation.updateSeasonalTileSheets"/> which intercept errors instead of crashing.</summary>
|
||||||
/// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks>
|
/// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony and methods are named for clarity.")]
|
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony and methods are named for clarity.")]
|
||||||
[SuppressMessage("ReSharper", "IdentifierTypo", Justification = "Argument names are defined by Harmony and methods are named for clarity.")]
|
[SuppressMessage("ReSharper", "IdentifierTypo", Justification = "Argument names are defined by Harmony and methods are named for clarity.")]
|
||||||
|
@ -39,17 +40,25 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
|
||||||
public void Apply(Harmony harmony)
|
public void Apply(Harmony harmony)
|
||||||
{
|
{
|
||||||
harmony.Patch(
|
harmony.Patch(
|
||||||
original: AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"),
|
original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.checkEventPrecondition)),
|
||||||
finalizer: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Finalize_GameLocation_CheckEventPrecondition))
|
finalizer: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Finalize_GameLocation_CheckEventPrecondition))
|
||||||
);
|
);
|
||||||
|
harmony.Patch(
|
||||||
|
original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.updateSeasonalTileSheets)),
|
||||||
|
finalizer: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Before_GameLocation_UpdateSeasonalTileSheets))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public void Apply(HarmonyInstance harmony)
|
public void Apply(HarmonyInstance harmony)
|
||||||
{
|
{
|
||||||
harmony.Patch(
|
harmony.Patch(
|
||||||
original: AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"),
|
original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.checkEventPrecondition)),
|
||||||
prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_CheckEventPrecondition))
|
prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_CheckEventPrecondition))
|
||||||
);
|
);
|
||||||
|
harmony.Patch(
|
||||||
|
original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.updateSeasonalTileSheets)),
|
||||||
|
prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_UpdateSeasonalTileSheets))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -74,7 +83,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/// <summary>The method to call instead of GameLocation.checkEventPrecondition.</summary>
|
/// <summary>The method to call instead of <see cref="GameLocation.checkEventPrecondition"/>.</summary>
|
||||||
/// <param name="__instance">The instance being patched.</param>
|
/// <param name="__instance">The instance being patched.</param>
|
||||||
/// <param name="__result">The return value of the original method.</param>
|
/// <param name="__result">The return value of the original method.</param>
|
||||||
/// <param name="precondition">The precondition to be parsed.</param>
|
/// <param name="precondition">The precondition to be parsed.</param>
|
||||||
|
@ -103,5 +112,47 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if HARMONY_2
|
||||||
|
/// <summary>The method to call instead of <see cref="GameLocation.updateSeasonalTileSheets"/>.</summary>
|
||||||
|
/// <param name="__instance">The instance being patched.</param>
|
||||||
|
/// <param name="map">The map whose tilesheets to update.</param>
|
||||||
|
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
|
||||||
|
/// <returns>Returns the exception to throw, if any.</returns>
|
||||||
|
private static Exception Before_GameLocation_UpdateSeasonalTileSheets(GameLocation __instance, Map map, Exception __exception)
|
||||||
|
{
|
||||||
|
if (__exception != null)
|
||||||
|
GameLocationPatches.MonitorForGame.Log($"Failed updating seasonal tilesheets for location '{__instance.NameOrUniqueName}': \n{__exception}", LogLevel.Error);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/// <summary>The method to call instead of <see cref="GameLocation.updateSeasonalTileSheets"/>.</summary>
|
||||||
|
/// <param name="__instance">The instance being patched.</param>
|
||||||
|
/// <param name="map">The map whose tilesheets to update.</param>
|
||||||
|
/// <param name="__originalMethod">The method being wrapped.</param>
|
||||||
|
/// <returns>Returns whether to execute the original method.</returns>
|
||||||
|
private static bool Before_GameLocation_UpdateSeasonalTileSheets(GameLocation __instance, Map map, MethodInfo __originalMethod)
|
||||||
|
{
|
||||||
|
const string key = nameof(GameLocationPatches.Before_GameLocation_UpdateSeasonalTileSheets);
|
||||||
|
if (!PatchHelper.StartIntercept(key))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
__originalMethod.Invoke(__instance, new object[] { map });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (TargetInvocationException ex)
|
||||||
|
{
|
||||||
|
GameLocationPatches.MonitorForGame.Log($"Failed updating seasonal tilesheets for location '{__instance.NameOrUniqueName}'. Technical details:\n{ex.InnerException}", LogLevel.Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
PatchHelper.StopIntercept(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,12 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
|
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
|
||||||
|
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Windows only -->
|
<!-- Windows only -->
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
||||||
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
|
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" Condition="!$(DefineConstants.Contains(SMAPI_FOR_WINDOWS_64BIT_HACK))" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Game framework -->
|
<!-- Game framework -->
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"Name": "Error Handler",
|
"Name": "Error Handler",
|
||||||
"Author": "SMAPI",
|
"Author": "SMAPI",
|
||||||
"Version": "3.9.5",
|
"Version": "3.10.0",
|
||||||
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
|
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
|
||||||
"UniqueID": "SMAPI.ErrorHandler",
|
"UniqueID": "SMAPI.ErrorHandler",
|
||||||
"EntryDll": "ErrorHandler.dll",
|
"EntryDll": "ErrorHandler.dll",
|
||||||
"MinimumApiVersion": "3.9.5"
|
"MinimumApiVersion": "3.10.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Constants.TargetPlatform == GamePlatform.Mac)
|
if (Constants.TargetPlatform == GamePlatform.Mac)
|
||||||
this.CompressUsingMacProcess(sourcePath, destination); // due to limitations with the bundled Mono on Mac, we can't reference System.IO.Compression
|
this.CompressUsingMacProcess(sourcePath, destination); // due to limitations with the bundled Mono on macOS, we can't reference System.IO.Compression
|
||||||
else
|
else
|
||||||
this.CompressUsingNetFramework(sourcePath, destination);
|
this.CompressUsingNetFramework(sourcePath, destination);
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
|
||||||
createFromDirectory.Invoke(null, new object[] { sourcePath, destination.FullName, CompressionLevel.Fastest, false });
|
createFromDirectory.Invoke(null, new object[] { sourcePath, destination.FullName, CompressionLevel.Fastest, false });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Create a zip using a process command on MacOS.</summary>
|
/// <summary>Create a zip using a process command on macOS.</summary>
|
||||||
/// <param name="sourcePath">The file or directory path to zip.</param>
|
/// <param name="sourcePath">The file or directory path to zip.</param>
|
||||||
/// <param name="destination">The destination file to create.</param>
|
/// <param name="destination">The destination file to create.</param>
|
||||||
private void CompressUsingMacProcess(string sourcePath, FileInfo destination)
|
private void CompressUsingMacProcess(string sourcePath, FileInfo destination)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"Name": "Save Backup",
|
"Name": "Save Backup",
|
||||||
"Author": "SMAPI",
|
"Author": "SMAPI",
|
||||||
"Version": "3.9.5",
|
"Version": "3.10.0",
|
||||||
"Description": "Automatically backs up all your saves once per day into its folder.",
|
"Description": "Automatically backs up all your saves once per day into its folder.",
|
||||||
"UniqueID": "SMAPI.SaveBackup",
|
"UniqueID": "SMAPI.SaveBackup",
|
||||||
"EntryDll": "SaveBackup.dll",
|
"EntryDll": "SaveBackup.dll",
|
||||||
"MinimumApiVersion": "3.9.5"
|
"MinimumApiVersion": "3.10.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Moq" Version="4.15.1" />
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -251,7 +251,7 @@ namespace SMAPI.Tests.Utilities
|
||||||
[TestCase(
|
[TestCase(
|
||||||
@"~/parent",
|
@"~/parent",
|
||||||
@"~/PARENT/child",
|
@"~/PARENT/child",
|
||||||
ExpectedResult = @"child" // note: incorrect on Linux and sometimes MacOS, but not worth the complexity of detecting whether the filesystem is case-sensitive for SMAPI's purposes
|
ExpectedResult = @"child" // note: incorrect on Linux and sometimes macOS, but not worth the complexity of detecting whether the filesystem is case-sensitive for SMAPI's purposes
|
||||||
)]
|
)]
|
||||||
#endif
|
#endif
|
||||||
public string GetRelativePath(string sourceDir, string targetPath)
|
public string GetRelativePath(string sourceDir, string targetPath)
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
|
||||||
/// <param name="content">The body content to post.</param>
|
/// <param name="content">The body content to post.</param>
|
||||||
private TResult Post<TBody, TResult>(string url, TBody content)
|
private TResult Post<TBody, TResult>(string url, TBody content)
|
||||||
{
|
{
|
||||||
// note: avoid HttpClient for Mac compatibility
|
// note: avoid HttpClient for macOS compatibility
|
||||||
using WebClient client = new WebClient();
|
using WebClient client = new WebClient();
|
||||||
|
|
||||||
Uri fullUrl = new Uri(this.BaseUrl, url);
|
Uri fullUrl = new Uri(this.BaseUrl, url);
|
||||||
|
|
|
@ -20,9 +20,6 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
/// <summary>The current OS.</summary>
|
/// <summary>The current OS.</summary>
|
||||||
private readonly Platform Platform;
|
private readonly Platform Platform;
|
||||||
|
|
||||||
/// <summary>The name of the Stardew Valley executable.</summary>
|
|
||||||
private readonly string ExecutableName;
|
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
|
@ -31,7 +28,6 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
public GameScanner()
|
public GameScanner()
|
||||||
{
|
{
|
||||||
this.Platform = EnvironmentUtility.DetectPlatform();
|
this.Platform = EnvironmentUtility.DetectPlatform();
|
||||||
this.ExecutableName = EnvironmentUtility.GetExecutableName(this.Platform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Find all valid Stardew Valley install folders.</summary>
|
/// <summary>Find all valid Stardew Valley install folders.</summary>
|
||||||
|
@ -58,7 +54,12 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
/// <param name="dir">The folder to check.</param>
|
/// <param name="dir">The folder to check.</param>
|
||||||
public bool LooksLikeGameFolder(DirectoryInfo dir)
|
public bool LooksLikeGameFolder(DirectoryInfo dir)
|
||||||
{
|
{
|
||||||
return dir.Exists && dir.EnumerateFiles(this.ExecutableName).Any();
|
return
|
||||||
|
dir.Exists
|
||||||
|
&& (
|
||||||
|
dir.EnumerateFiles("StardewValley.exe").Any()
|
||||||
|
|| dir.EnumerateFiles("Stardew Valley.exe").Any()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
? $"{home}/.steam/steam/steamapps/common/Stardew Valley"
|
? $"{home}/.steam/steam/steamapps/common/Stardew Valley"
|
||||||
: $"{home}/.local/share/Steam/steamapps/common/Stardew Valley";
|
: $"{home}/.local/share/Steam/steamapps/common/Stardew Valley";
|
||||||
|
|
||||||
// Mac
|
// macOS
|
||||||
yield return "/Applications/Stardew Valley.app/Contents/MacOS";
|
yield return "/Applications/Stardew Valley.app/Contents/MacOS";
|
||||||
yield return $"{home}/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS";
|
yield return $"{home}/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS";
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace StardewModdingAPI.Toolkit.Framework
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(Platform.Mac):
|
case nameof(Platform.Mac):
|
||||||
name = $"MacOS {name}";
|
name = $"macOS {name}";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
|
@ -124,10 +124,10 @@ namespace StardewModdingAPI.Toolkit.Framework
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Detect whether the code is running on Mac.</summary>
|
/// <summary>Detect whether the code is running on macOS.</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This code is derived from the Mono project (see System.Windows.Forms/System.Windows.Forms/XplatUI.cs). It detects Mac by calling the
|
/// This code is derived from the Mono project (see System.Windows.Forms/System.Windows.Forms/XplatUI.cs). It detects macOS by calling the
|
||||||
/// <c>uname</c> system command and checking the response, which is always 'Darwin' for MacOS.
|
/// <c>uname</c> system command and checking the response, which is always 'Darwin' for macOS.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private static bool IsRunningMac()
|
private static bool IsRunningMac()
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
|
||||||
/// <summary>The mod patches the game in a way that may impact stability.</summary>
|
/// <summary>The mod patches the game in a way that may impact stability.</summary>
|
||||||
PatchesGame = 4,
|
PatchesGame = 4,
|
||||||
|
|
||||||
/// <summary>The mod uses the <c>dynamic</c> keyword which won't work on Linux/Mac.</summary>
|
/// <summary>The mod uses the <c>dynamic</c> keyword which won't work on Linux/macOS.</summary>
|
||||||
UsesDynamic = 8,
|
UsesDynamic = 8,
|
||||||
|
|
||||||
/// <summary>The mod references specialized 'unvalidated update tick' events which may impact stability.</summary>
|
/// <summary>The mod references specialized 'unvalidated update tick' events which may impact stability.</summary>
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
private readonly HashSet<Regex> IgnoreFilesystemNames = new HashSet<Regex>
|
private readonly HashSet<Regex> IgnoreFilesystemNames = new HashSet<Regex>
|
||||||
{
|
{
|
||||||
new Regex(@"^__folder_managed_by_vortex$", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Vortex mod manager
|
new Regex(@"^__folder_managed_by_vortex$", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Vortex mod manager
|
||||||
new Regex(@"(?:^\._|^\.DS_Store$|^__MACOSX$|^mcs$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // MacOS
|
new Regex(@"(?:^\._|^\.DS_Store$|^__MACOSX$|^mcs$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // macOS
|
||||||
new Regex(@"^(?:desktop\.ini|Thumbs\.db)$", RegexOptions.Compiled | RegexOptions.IgnoreCase) // Windows
|
new Regex(@"^(?:desktop\.ini|Thumbs\.db)$", RegexOptions.Compiled | RegexOptions.IgnoreCase) // Windows
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
// images
|
// images
|
||||||
".bmp",
|
".bmp",
|
||||||
".gif",
|
".gif",
|
||||||
|
".ico",
|
||||||
".jpeg",
|
".jpeg",
|
||||||
".jpg",
|
".jpg",
|
||||||
".png",
|
".png",
|
||||||
|
@ -136,7 +137,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
return new ModFolder(root, searchFolder, ModType.Xnb, null, ModParseError.XnbMod, "it's not a SMAPI mod (see https://smapi.io/xnb for info).");
|
return new ModFolder(root, searchFolder, ModType.Xnb, null, ModParseError.XnbMod, "it's not a SMAPI mod (see https://smapi.io/xnb for info).");
|
||||||
|
|
||||||
// SMAPI installer
|
// SMAPI installer
|
||||||
if (relevantFiles.Any(p => p.Name == "install on Linux.sh" || p.Name == "install on Mac.command" || p.Name == "install on Windows.bat"))
|
if (relevantFiles.Any(p => p.Name == "install on Linux.sh" || p.Name == "install on macOS.command" || p.Name == "install on Windows.bat"))
|
||||||
return new ModFolder(root, searchFolder, ModType.Invalid, null, ModParseError.ManifestMissing, "the SMAPI installer isn't a mod (you can delete this folder after running the installer file).");
|
return new ModFolder(root, searchFolder, ModType.Invalid, null, ModParseError.ManifestMissing, "the SMAPI installer isn't a mod (you can delete this folder after running the installer file).");
|
||||||
|
|
||||||
// not a mod?
|
// not a mod?
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
<Import Project="..\..\build\common.targets" />
|
<Import Project="..\..\build\common.targets" />
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.28" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.0.0" />
|
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.0" />
|
||||||
<PackageReference Include="System.Management" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT'" />
|
<PackageReference Include="System.Management" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT'" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT' AND '$(TargetFramework)' == 'netstandard2.0'" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT' AND '$(TargetFramework)' == 'netstandard2.0'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
/// <summary>The Linux version of the game.</summary>
|
/// <summary>The Linux version of the game.</summary>
|
||||||
Linux,
|
Linux,
|
||||||
|
|
||||||
/// <summary>The Mac version of the game.</summary>
|
/// <summary>The macOS version of the game.</summary>
|
||||||
Mac,
|
Mac,
|
||||||
|
|
||||||
/// <summary>The Windows version of the game.</summary>
|
/// <summary>The Windows version of the game.</summary>
|
||||||
|
|
|
@ -186,7 +186,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Nexus
|
||||||
Version = SemanticVersion.TryParse(mod.Version, out ISemanticVersion version) ? version?.ToString() : mod.Version,
|
Version = SemanticVersion.TryParse(mod.Version, out ISemanticVersion version) ? version?.ToString() : mod.Version,
|
||||||
Url = this.GetModUrl(id),
|
Url = this.GetModUrl(id),
|
||||||
Downloads = files.Files
|
Downloads = files.Files
|
||||||
.Select(file => (IModDownload)new GenericModDownload(file.Name, null, file.FileVersion))
|
.Select(file => (IModDownload)new GenericModDownload(file.Name, file.Description, file.FileVersion))
|
||||||
.ToArray()
|
.ToArray()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,17 +13,17 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.7.0" />
|
<PackageReference Include="Azure.Storage.Blobs" Version="12.8.3" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.17" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.22" />
|
||||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
|
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.28" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core" Version="2.9.9" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
|
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" />
|
||||||
<PackageReference Include="Markdig" Version="0.22.0" />
|
<PackageReference Include="Markdig" Version="0.24.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.8" />
|
||||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.13" />
|
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||||
<PackageReference Include="Pathoschild.FluentNexus" Version="1.0.1" />
|
<PackageReference Include="Pathoschild.FluentNexus" Version="1.0.2" />
|
||||||
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.0.0" />
|
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" />
|
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" />
|
||||||
|
|
|
@ -231,7 +231,7 @@ namespace StardewModdingAPI.Web
|
||||||
: null
|
: null
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// redirect to HTTPS (except API for Linux/Mac Mono compatibility)
|
// redirect to HTTPS (except API for Linux/macOS Mono compatibility)
|
||||||
.Add(
|
.Add(
|
||||||
new RedirectToHttpsRule(except: req => req.Host.Host == "localhost" || req.Path.StartsWithSegments("/api"))
|
new RedirectToHttpsRule(except: req => req.Host.Host == "localhost" || req.Path.StartsWithSegments("/api"))
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</h1>
|
</h1>
|
||||||
<div id="blurb">
|
<div id="blurb">
|
||||||
<p>The mod loader for Stardew Valley.</p>
|
<p>The mod loader for Stardew Valley.</p>
|
||||||
<p>Compatible with GOG/Steam achievements and Linux/Mac/Windows, uninstall anytime, and there's a friendly community if you need help.</p>
|
<p>Compatible with GOG/Steam achievements and Linux/macOS/Windows, uninstall anytime, and there's a friendly community if you need help.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="call-to-action">
|
<div id="call-to-action">
|
||||||
|
|
|
@ -120,7 +120,7 @@ else if (Model.ParsedLog?.IsValid == true)
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
<div data-os="@Platform.Mac">
|
<div data-os="@Platform.Mac">
|
||||||
On Mac:
|
On macOS:
|
||||||
<ol>
|
<ol>
|
||||||
<li>Open the Finder app.</li>
|
<li>Open the Finder app.</li>
|
||||||
<li>Click <em>Go</em> at the top, then <em>Go to Folder</em>.</li>
|
<li>Click <em>Go</em> at the top, then <em>Go to Folder</em>.</li>
|
||||||
|
|
|
@ -109,7 +109,7 @@
|
||||||
|
|
||||||
"Rubydew": {
|
"Rubydew": {
|
||||||
"ID": "bwdy.rubydew",
|
"ID": "bwdy.rubydew",
|
||||||
"SuppressWarnings": "UsesDynamic", // mod explicitly loads DLLs for Linux/Mac compatibility
|
"SuppressWarnings": "UsesDynamic", // mod explicitly loads DLLs for Linux/macOS compatibility
|
||||||
"Default | UpdateKey": "Nexus:3656"
|
"Default | UpdateKey": "Nexus:3656"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
"title": "Format version",
|
"title": "Format version",
|
||||||
"description": "The format version. You should always use the latest version to enable the latest features and avoid obsolete behavior.",
|
"description": "The format version. You should always use the latest version to enable the latest features and avoid obsolete behavior.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "1.21.0",
|
"const": "1.22.0",
|
||||||
"@errorMessages": {
|
"@errorMessages": {
|
||||||
"const": "Incorrect value '@value'. This should be set to the latest format version, currently '1.21.0'."
|
"const": "Incorrect value '@value'. This should be set to the latest format version, currently '1.22.0'."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConfigSchema": {
|
"ConfigSchema": {
|
||||||
|
|
|
@ -38,6 +38,14 @@ namespace StardewModdingAPI
|
||||||
/// <summary>The target game platform.</summary>
|
/// <summary>The target game platform.</summary>
|
||||||
internal static GamePlatform Platform { get; } = (GamePlatform)Enum.Parse(typeof(GamePlatform), LowLevelEnvironmentUtility.DetectPlatform());
|
internal static GamePlatform Platform { get; } = (GamePlatform)Enum.Parse(typeof(GamePlatform), LowLevelEnvironmentUtility.DetectPlatform());
|
||||||
|
|
||||||
|
/// <summary>Whether SMAPI is being compiled for Windows with a 64-bit Linux version of the game. This is highly specialized and shouldn't be used in most cases.</summary>
|
||||||
|
internal static bool IsWindows64BitHack { get; } =
|
||||||
|
#if SMAPI_FOR_WINDOWS_64BIT_HACK
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>The game framework running the game.</summary>
|
/// <summary>The game framework running the game.</summary>
|
||||||
internal static GameFramework GameFramework { get; } =
|
internal static GameFramework GameFramework { get; } =
|
||||||
#if SMAPI_FOR_XNA
|
#if SMAPI_FOR_XNA
|
||||||
|
@ -47,10 +55,13 @@ namespace StardewModdingAPI
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// <summary>The game's assembly name.</summary>
|
/// <summary>The game's assembly name.</summary>
|
||||||
internal static string GameAssemblyName => EarlyConstants.Platform == GamePlatform.Windows ? "Stardew Valley" : "StardewValley";
|
internal static string GameAssemblyName => EarlyConstants.Platform == GamePlatform.Windows && !EarlyConstants.IsWindows64BitHack ? "Stardew Valley" : "StardewValley";
|
||||||
|
|
||||||
/// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary>
|
/// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary>
|
||||||
internal static int? LogScreenId { get; set; }
|
internal static int? LogScreenId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>SMAPI's current raw semantic version.</summary>
|
||||||
|
internal static string RawApiVersion = "3.10.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Contains SMAPI's constants and assumptions.</summary>
|
/// <summary>Contains SMAPI's constants and assumptions.</summary>
|
||||||
|
@ -63,7 +74,7 @@ namespace StardewModdingAPI
|
||||||
** Public
|
** Public
|
||||||
****/
|
****/
|
||||||
/// <summary>SMAPI's current semantic version.</summary>
|
/// <summary>SMAPI's current semantic version.</summary>
|
||||||
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.9.5");
|
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion(EarlyConstants.RawApiVersion);
|
||||||
|
|
||||||
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
||||||
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.4");
|
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.4");
|
||||||
|
@ -231,33 +242,27 @@ namespace StardewModdingAPI
|
||||||
targetAssemblies.Add(typeof(StardewModdingAPI.IManifest).Assembly);
|
targetAssemblies.Add(typeof(StardewModdingAPI.IManifest).Assembly);
|
||||||
|
|
||||||
// get changes for platform
|
// get changes for platform
|
||||||
switch (targetPlatform)
|
if (Constants.Platform != Platform.Windows || EarlyConstants.IsWindows64BitHack)
|
||||||
{
|
{
|
||||||
case Platform.Linux:
|
removeAssemblyReferences.AddRange(new[]
|
||||||
case Platform.Mac:
|
{
|
||||||
removeAssemblyReferences.AddRange(new[]
|
"Netcode",
|
||||||
{
|
"Stardew Valley"
|
||||||
"Netcode",
|
});
|
||||||
"Stardew Valley"
|
targetAssemblies.Add(
|
||||||
});
|
typeof(StardewValley.Game1).Assembly // note: includes Netcode types on Linux/macOS
|
||||||
targetAssemblies.Add(
|
);
|
||||||
typeof(StardewValley.Game1).Assembly // note: includes Netcode types on Linux/Mac
|
}
|
||||||
);
|
else
|
||||||
break;
|
{
|
||||||
|
removeAssemblyReferences.Add(
|
||||||
case Platform.Windows:
|
"StardewValley"
|
||||||
removeAssemblyReferences.Add(
|
);
|
||||||
"StardewValley"
|
targetAssemblies.AddRange(new[]
|
||||||
);
|
{
|
||||||
targetAssemblies.AddRange(new[]
|
typeof(Netcode.NetBool).Assembly,
|
||||||
{
|
typeof(StardewValley.Game1).Assembly
|
||||||
typeof(Netcode.NetBool).Assembly,
|
});
|
||||||
typeof(StardewValley.Game1).Assembly
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new InvalidOperationException($"Unknown target platform '{targetPlatform}'.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get changes for game framework
|
// get changes for game framework
|
||||||
|
@ -295,6 +300,21 @@ namespace StardewModdingAPI
|
||||||
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray());
|
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get whether the game assembly was patched by Stardew64Installer.</summary>
|
||||||
|
/// <param name="version">The version of Stardew64Installer which was applied to the game assembly, if any.</param>
|
||||||
|
internal static bool IsPatchedByStardew64Installer(out ISemanticVersion version)
|
||||||
|
{
|
||||||
|
PropertyInfo property = typeof(Game1).GetProperty("Stardew64InstallerVersion");
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
version = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
version = new SemanticVersion((string)property.GetValue(null));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Private methods
|
** Private methods
|
||||||
|
|
|
@ -86,7 +86,7 @@ namespace StardewModdingAPI
|
||||||
public static bool HasRemotePlayers => Context.IsMultiplayer && !Game1.hasLocalClientsOnly;
|
public static bool HasRemotePlayers => Context.IsMultiplayer && !Game1.hasLocalClientsOnly;
|
||||||
|
|
||||||
/// <summary>Whether the current player is the main player. This is always true in single-player, and true when hosting in multiplayer.</summary>
|
/// <summary>Whether the current player is the main player. This is always true in single-player, and true when hosting in multiplayer.</summary>
|
||||||
public static bool IsMainPlayer => Game1.IsMasterGame && !(TitleMenu.subMenu is FarmhandMenu);
|
public static bool IsMainPlayer => Game1.IsMasterGame && Context.ScreenId == 0 && !(TitleMenu.subMenu is FarmhandMenu);
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
** Fields
|
** Fields
|
||||||
*********/
|
*********/
|
||||||
/// <summary>The minimum value to consider non-transparent.</summary>
|
/// <summary>The minimum value to consider non-transparent.</summary>
|
||||||
/// <remarks>On Linux/Mac, fully transparent pixels may have an alpha up to 4 for some reason.</remarks>
|
/// <remarks>On Linux/macOS, fully transparent pixels may have an alpha up to 4 for some reason.</remarks>
|
||||||
private const byte MinOpacity = 5;
|
private const byte MinOpacity = 5;
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
// premultiplied by the content pipeline. The formula is derived from
|
// premultiplied by the content pipeline. The formula is derived from
|
||||||
// https://blogs.msdn.microsoft.com/shawnhar/2009/11/06/premultiplied-alpha/.
|
// https://blogs.msdn.microsoft.com/shawnhar/2009/11/06/premultiplied-alpha/.
|
||||||
// Note: don't use named arguments here since they're different between
|
// Note: don't use named arguments here since they're different between
|
||||||
// Linux/Mac and Windows.
|
// Linux/macOS and Windows.
|
||||||
float alphaBelow = 1 - (above.A / 255f);
|
float alphaBelow = 1 - (above.A / 255f);
|
||||||
newData[i] = new Color(
|
newData[i] = new Color(
|
||||||
(int)(above.R + (below.R * alphaBelow)), // r
|
(int)(above.R + (below.R * alphaBelow)), // r
|
||||||
|
|
|
@ -57,6 +57,8 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
IReflectedMethod method = reflection.GetMethod(typeof(TitleContainer), "GetCleanPath");
|
IReflectedMethod method = reflection.GetMethod(typeof(TitleContainer), "GetCleanPath");
|
||||||
this.NormalizeAssetNameForPlatform = path => method.Invoke<string>(path);
|
this.NormalizeAssetNameForPlatform = path => method.Invoke<string>(path);
|
||||||
}
|
}
|
||||||
|
else if (EarlyConstants.IsWindows64BitHack)
|
||||||
|
this.NormalizeAssetNameForPlatform = PathUtilities.NormalizePath;
|
||||||
else
|
else
|
||||||
this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic
|
this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ namespace StardewModdingAPI.Framework
|
||||||
);
|
);
|
||||||
this.ContentManagers.Add(contentManagerForAssetPropagation);
|
this.ContentManagers.Add(contentManagerForAssetPropagation);
|
||||||
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
|
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
|
||||||
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, reflection, aggressiveMemoryOptimizations);
|
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary>
|
/// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary>
|
||||||
|
|
|
@ -34,6 +34,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <summary>The language code for language-agnostic mod assets.</summary>
|
/// <summary>The language code for language-agnostic mod assets.</summary>
|
||||||
private readonly LanguageCode DefaultLanguage = Constants.DefaultLanguage;
|
private readonly LanguageCode DefaultLanguage = Constants.DefaultLanguage;
|
||||||
|
|
||||||
|
/// <summary>If a map tilesheet's image source has no file extensions, the file extensions to check for in the local mod folder.</summary>
|
||||||
|
private readonly string[] LocalTilesheetExtensions = { ".png", ".xnb" };
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
|
@ -215,11 +218,17 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
FileInfo file = new FileInfo(Path.Combine(this.FullRootDirectory, path));
|
FileInfo file = new FileInfo(Path.Combine(this.FullRootDirectory, path));
|
||||||
|
|
||||||
// try with default extension
|
// try with default extension
|
||||||
if (!file.Exists && file.Extension.ToLower() != ".xnb")
|
if (!file.Exists && file.Extension == string.Empty)
|
||||||
{
|
{
|
||||||
FileInfo result = new FileInfo(file.FullName + ".xnb");
|
foreach (string extension in this.LocalTilesheetExtensions)
|
||||||
if (result.Exists)
|
{
|
||||||
file = result;
|
FileInfo result = new FileInfo(file.FullName + extension);
|
||||||
|
if (result.Exists)
|
||||||
|
{
|
||||||
|
file = result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
|
@ -259,6 +268,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
string relativeMapFolder = Path.GetDirectoryName(relativeMapPath) ?? ""; // folder path containing the map, relative to the mod folder
|
string relativeMapFolder = Path.GetDirectoryName(relativeMapPath) ?? ""; // folder path containing the map, relative to the mod folder
|
||||||
|
|
||||||
// fix tilesheets
|
// fix tilesheets
|
||||||
|
this.Monitor.VerboseLog($"Fixing tilesheet paths for map '{relativeMapPath}' from mod '{this.ModName}'...");
|
||||||
foreach (TileSheet tilesheet in map.TileSheets)
|
foreach (TileSheet tilesheet in map.TileSheets)
|
||||||
{
|
{
|
||||||
// get image source
|
// get image source
|
||||||
|
@ -280,6 +290,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
if (!this.TryGetTilesheetAssetName(relativeMapFolder, imageSource, out string assetName, out string error))
|
if (!this.TryGetTilesheetAssetName(relativeMapFolder, imageSource, out string assetName, out string error))
|
||||||
throw new SContentLoadException($"{errorPrefix} {error}");
|
throw new SContentLoadException($"{errorPrefix} {error}");
|
||||||
|
|
||||||
|
if (assetName != tilesheet.ImageSource)
|
||||||
|
this.Monitor.VerboseLog($" Mapped tilesheet '{tilesheet.ImageSource}' to '{assetName}'.");
|
||||||
|
|
||||||
tilesheet.ImageSource = assetName;
|
tilesheet.ImageSource = assetName;
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (!(ex is SContentLoadException))
|
catch (Exception ex) when (!(ex is SContentLoadException))
|
||||||
|
@ -308,6 +321,15 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// special case: local filenames starting with a dot should be ignored
|
||||||
|
// For example, this lets mod authors have a '.spring_town.png' file in their map folder so it can be
|
||||||
|
// opened in Tiled, while still mapping it to the vanilla 'Maps/spring_town' asset at runtime.
|
||||||
|
{
|
||||||
|
string filename = Path.GetFileName(relativePath);
|
||||||
|
if (filename.StartsWith("."))
|
||||||
|
relativePath = Path.Combine(Path.GetDirectoryName(relativePath) ?? "", filename.TrimStart('.'));
|
||||||
|
}
|
||||||
|
|
||||||
// get relative to map file
|
// get relative to map file
|
||||||
{
|
{
|
||||||
string localKey = Path.Combine(modRelativeMapFolder, relativePath);
|
string localKey = Path.Combine(modRelativeMapFolder, relativePath);
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
|
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
|
||||||
private readonly JsonHelper JsonHelper;
|
private readonly JsonHelper JsonHelper;
|
||||||
|
|
||||||
/// <summary>A cache of case-insensitive => exact relative paths within the content pack, for case-insensitive file lookups on Linux/Mac.</summary>
|
/// <summary>A cache of case-insensitive => exact relative paths within the content pack, for case-insensitive file lookups on Linux/macOS.</summary>
|
||||||
private readonly IDictionary<string, string> RelativePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
private readonly IDictionary<string, string> RelativePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,11 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <param name="validOnly">Only return valid update keys.</param>
|
/// <param name="validOnly">Only return valid update keys.</param>
|
||||||
IEnumerable<UpdateKey> GetUpdateKeys(bool validOnly = true);
|
IEnumerable<UpdateKey> GetUpdateKeys(bool validOnly = true);
|
||||||
|
|
||||||
|
/// <summary>Get whether the given mod ID must be installed to load this mod.</summary>
|
||||||
|
/// <param name="modId">The mod ID to check.</param>
|
||||||
|
/// <param name="includeOptional">Whether to include optional dependencies.</param>
|
||||||
|
bool HasRequiredModId(string modId, bool includeOptional);
|
||||||
|
|
||||||
/// <summary>Get the mod IDs that must be installed to load this mod.</summary>
|
/// <summary>Get the mod IDs that must be installed to load this mod.</summary>
|
||||||
/// <param name="includeOptional">Whether to include optional dependencies.</param>
|
/// <param name="includeOptional">Whether to include optional dependencies.</param>
|
||||||
IEnumerable<string> GetRequiredModIds(bool includeOptional = false);
|
IEnumerable<string> GetRequiredModIds(bool includeOptional = false);
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
public void WriteLine(string message)
|
public void WriteLine(string message)
|
||||||
{
|
{
|
||||||
// always use Windows-style line endings for convenience
|
// always use Windows-style line endings for convenience
|
||||||
// (Linux/Mac editors are fine with them, Windows editors often require them)
|
// (Linux/macOS editors are fine with them, Windows editors often require them)
|
||||||
this.Stream.Write(message + "\r\n");
|
this.Stream.Write(message + "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -283,13 +283,16 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
/// <param name="customSettings">The custom SMAPI settings.</param>
|
/// <param name="customSettings">The custom SMAPI settings.</param>
|
||||||
public void LogIntro(string modsPath, IDictionary<string, object> customSettings)
|
public void LogIntro(string modsPath, IDictionary<string, object> customSettings)
|
||||||
{
|
{
|
||||||
// get platform label
|
// log platform & patches
|
||||||
string platformLabel = EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform);
|
{
|
||||||
if ((Constants.GameFramework == GameFramework.Xna) != (Constants.Platform == Platform.Windows))
|
this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info);
|
||||||
platformLabel += $" with {Constants.GameFramework}";
|
|
||||||
|
|
||||||
// init logging
|
string[] patchLabels = this.GetPatchLabels().ToArray();
|
||||||
this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {platformLabel}", LogLevel.Info);
|
if (patchLabels.Any())
|
||||||
|
this.Monitor.Log($"Detected custom version: {string.Join(", ", patchLabels)}", LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// log basic info
|
||||||
this.Monitor.Log($"Mods go here: {modsPath}", LogLevel.Info);
|
this.Monitor.Log($"Mods go here: {modsPath}", LogLevel.Info);
|
||||||
if (modsPath != Constants.DefaultModsPath)
|
if (modsPath != Constants.DefaultModsPath)
|
||||||
this.Monitor.Log("(Using custom --mods-path argument.)");
|
this.Monitor.Log("(Using custom --mods-path argument.)");
|
||||||
|
@ -406,6 +409,20 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
gameMonitor.Log(message, level);
|
gameMonitor.Log(message, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get human-readable labels to log for detected SMAPI and Stardew Valley customizations.</summary>
|
||||||
|
private IEnumerable<string> GetPatchLabels()
|
||||||
|
{
|
||||||
|
// custom game framework
|
||||||
|
if (EarlyConstants.IsWindows64BitHack)
|
||||||
|
yield return $"running 64-bit SMAPI with {Constants.GameFramework}";
|
||||||
|
else if ((Constants.GameFramework == GameFramework.Xna) != (Constants.Platform == Platform.Windows))
|
||||||
|
yield return $"running {Constants.GameFramework}";
|
||||||
|
|
||||||
|
// patched by Stardew64Installer
|
||||||
|
if (Constants.IsPatchedByStardew64Installer(out ISemanticVersion patchedByVersion))
|
||||||
|
yield return $"patched by Stardew64Installer {patchedByVersion}";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Write a summary of mod warnings to the console and log.</summary>
|
/// <summary>Write a summary of mod warnings to the console and log.</summary>
|
||||||
/// <param name="mods">The loaded mods.</param>
|
/// <param name="mods">The loaded mods.</param>
|
||||||
/// <param name="skippedMods">The mods which could not be loaded.</param>
|
/// <param name="skippedMods">The mods which could not be loaded.</param>
|
||||||
|
@ -426,67 +443,38 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
// log skipped mods
|
// log skipped mods
|
||||||
if (skippedMods.Any())
|
if (skippedMods.Any())
|
||||||
{
|
{
|
||||||
// get logging logic
|
var loggedDuplicateIds = new HashSet<string>();
|
||||||
HashSet<string> loggedDuplicateIds = new HashSet<string>();
|
|
||||||
void LogSkippedMod(IModMetadata mod)
|
|
||||||
{
|
|
||||||
string message = $" - {mod.DisplayName}{(mod.Manifest?.Version != null ? " " + mod.Manifest.Version.ToString() : "")} because {mod.Error}";
|
|
||||||
|
|
||||||
// handle duplicate mods
|
|
||||||
// (log first duplicate only, don't show redundant version)
|
|
||||||
if (mod.FailReason == ModFailReason.Duplicate && mod.HasManifest())
|
|
||||||
{
|
|
||||||
if (!loggedDuplicateIds.Add(mod.Manifest.UniqueID))
|
|
||||||
return; // already logged
|
|
||||||
|
|
||||||
message = $" - {mod.DisplayName} because {mod.Error}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// log message
|
|
||||||
this.Monitor.Log(message, LogLevel.Error);
|
|
||||||
if (mod.ErrorDetails != null)
|
|
||||||
this.Monitor.Log($" ({mod.ErrorDetails})");
|
|
||||||
}
|
|
||||||
|
|
||||||
// group mods
|
|
||||||
List<IModMetadata> skippedDependencies = new List<IModMetadata>();
|
|
||||||
List<IModMetadata> otherSkippedMods = new List<IModMetadata>();
|
|
||||||
{
|
|
||||||
// track broken dependencies
|
|
||||||
HashSet<string> skippedDependencyIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
HashSet<string> skippedModIds = new HashSet<string>(from mod in skippedMods where mod.HasID() select mod.Manifest.UniqueID, StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (IModMetadata mod in skippedMods)
|
|
||||||
{
|
|
||||||
foreach (string requiredId in skippedModIds.Intersect(mod.GetRequiredModIds()))
|
|
||||||
skippedDependencyIds.Add(requiredId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect mod groups
|
|
||||||
foreach (IModMetadata mod in skippedMods)
|
|
||||||
{
|
|
||||||
if (mod.HasID() && skippedDependencyIds.Contains(mod.Manifest.UniqueID))
|
|
||||||
skippedDependencies.Add(mod);
|
|
||||||
else
|
|
||||||
otherSkippedMods.Add(mod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// log skipped mods
|
|
||||||
this.Monitor.Log(" Skipped mods", LogLevel.Error);
|
this.Monitor.Log(" Skipped mods", LogLevel.Error);
|
||||||
this.Monitor.Log(" " + "".PadRight(50, '-'), LogLevel.Error);
|
this.Monitor.Log(" " + "".PadRight(50, '-'), LogLevel.Error);
|
||||||
this.Monitor.Log(" These mods could not be added to your game.", LogLevel.Error);
|
this.Monitor.Log(" These mods could not be added to your game.", LogLevel.Error);
|
||||||
this.Monitor.Newline();
|
this.Monitor.Newline();
|
||||||
|
foreach (var list in this.GroupFailedModsByPriority(skippedMods))
|
||||||
if (skippedDependencies.Any())
|
|
||||||
{
|
{
|
||||||
foreach (IModMetadata mod in skippedDependencies.OrderBy(p => p.DisplayName))
|
if (list.Any())
|
||||||
LogSkippedMod(mod);
|
{
|
||||||
this.Monitor.Newline();
|
foreach (IModMetadata mod in list.OrderBy(p => p.DisplayName))
|
||||||
}
|
{
|
||||||
|
string message = $" - {mod.DisplayName}{(" " + mod.Manifest?.Version?.ToString()).TrimEnd()} because {mod.Error}";
|
||||||
|
|
||||||
foreach (IModMetadata mod in otherSkippedMods.OrderBy(p => p.DisplayName))
|
// duplicate mod: log first one only, don't show redundant version
|
||||||
LogSkippedMod(mod);
|
if (mod.FailReason == ModFailReason.Duplicate && mod.HasManifest())
|
||||||
this.Monitor.Newline();
|
{
|
||||||
|
if (loggedDuplicateIds.Add(mod.Manifest.UniqueID))
|
||||||
|
continue; // already logged
|
||||||
|
|
||||||
|
message = $" - {mod.DisplayName} because {mod.Error}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// log message
|
||||||
|
this.Monitor.Log(message, LogLevel.Error);
|
||||||
|
if (mod.ErrorDetails != null)
|
||||||
|
this.Monitor.Log($" ({mod.ErrorDetails})");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Monitor.Newline();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// log warnings
|
// log warnings
|
||||||
|
@ -553,11 +541,97 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
|
|
||||||
// not crossplatform
|
// not crossplatform
|
||||||
this.LogModWarningGroup(modsWithWarnings, ModWarning.UsesDynamic, LogLevel.Debug, "Not crossplatform",
|
this.LogModWarningGroup(modsWithWarnings, ModWarning.UsesDynamic, LogLevel.Debug, "Not crossplatform",
|
||||||
"These mods use the 'dynamic' keyword, and won't work on Linux/Mac."
|
"These mods use the 'dynamic' keyword, and won't work on Linux/macOS."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Group failed mods by the priority players should update them, where mods in earlier groups are more likely to fix multiple mods.</summary>
|
||||||
|
/// <param name="failedMods">The failed mods to group.</param>
|
||||||
|
private IEnumerable<IList<IModMetadata>> GroupFailedModsByPriority(IList<IModMetadata> failedMods)
|
||||||
|
{
|
||||||
|
var failedOthers = failedMods.ToList();
|
||||||
|
var skippedModIds = new HashSet<string>(from mod in failedMods where mod.HasID() select mod.Manifest.UniqueID, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// group B: dependencies which failed
|
||||||
|
var failedOtherDependencies = new List<IModMetadata>();
|
||||||
|
{
|
||||||
|
// get failed dependency IDs
|
||||||
|
var skippedDependencyIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (IModMetadata mod in failedMods)
|
||||||
|
{
|
||||||
|
foreach (string requiredId in skippedModIds.Intersect(mod.GetRequiredModIds()))
|
||||||
|
skippedDependencyIds.Add(requiredId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// group matching mods
|
||||||
|
this.FilterThrough(
|
||||||
|
fromList: failedOthers,
|
||||||
|
toList: failedOtherDependencies,
|
||||||
|
match: mod => mod.HasID() && skippedDependencyIds.Contains(mod.Manifest.UniqueID)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// group A: failed root dependencies which other dependencies need
|
||||||
|
var failedRootDependencies = new List<IModMetadata>();
|
||||||
|
{
|
||||||
|
var skippedDependencyIds = new HashSet<string>(failedOtherDependencies.Select(p => p.Manifest.UniqueID));
|
||||||
|
this.FilterThrough(
|
||||||
|
fromList: failedOtherDependencies,
|
||||||
|
toList: failedRootDependencies,
|
||||||
|
match: mod =>
|
||||||
|
{
|
||||||
|
// has no failed dependency
|
||||||
|
foreach (string requiredId in mod.GetRequiredModIds())
|
||||||
|
{
|
||||||
|
if (skippedDependencyIds.Contains(requiredId))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// another dependency depends on this mod
|
||||||
|
bool isDependedOn = false;
|
||||||
|
foreach (IModMetadata other in failedOtherDependencies)
|
||||||
|
{
|
||||||
|
if (other.HasRequiredModId(mod.Manifest.UniqueID, includeOptional: false))
|
||||||
|
{
|
||||||
|
isDependedOn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDependedOn;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return groups
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
failedRootDependencies,
|
||||||
|
failedOtherDependencies,
|
||||||
|
failedOthers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Filter matching items from one list and add them to the other.</summary>
|
||||||
|
/// <typeparam name="TItem">The list item type.</typeparam>
|
||||||
|
/// <param name="fromList">The list to filter.</param>
|
||||||
|
/// <param name="toList">The list to which to add filtered items.</param>
|
||||||
|
/// <param name="match">Matches items to filter through.</param>
|
||||||
|
private void FilterThrough<TItem>(IList<TItem> fromList, IList<TItem> toList, Func<TItem, bool> match)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fromList.Count; i++)
|
||||||
|
{
|
||||||
|
TItem item = fromList[i];
|
||||||
|
if (match(item))
|
||||||
|
{
|
||||||
|
toList.Add(item);
|
||||||
|
fromList.RemoveAt(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Write a mod warning group to the console and log.</summary>
|
/// <summary>Write a mod warning group to the console and log.</summary>
|
||||||
/// <param name="mods">The mods to search.</param>
|
/// <param name="mods">The mods to search.</param>
|
||||||
/// <param name="match">Matches mods to include in the warning group.</param>
|
/// <param name="match">Matches mods to include in the warning group.</param>
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
/// <summary>The instruction is compatible, but affects the save serializer in a way that may make saves unloadable without the mod.</summary>
|
/// <summary>The instruction is compatible, but affects the save serializer in a way that may make saves unloadable without the mod.</summary>
|
||||||
DetectedSaveSerializer,
|
DetectedSaveSerializer,
|
||||||
|
|
||||||
/// <summary>The instruction is compatible, but uses the <c>dynamic</c> keyword which won't work on Linux/Mac.</summary>
|
/// <summary>The instruction is compatible, but uses the <c>dynamic</c> keyword which won't work on Linux/macOS.</summary>
|
||||||
DetectedDynamic,
|
DetectedDynamic,
|
||||||
|
|
||||||
/// <summary>The instruction is compatible, but references <see cref="ISpecializedEvents.UnvalidatedUpdateTicking"/> or <see cref="ISpecializedEvents.UnvalidatedUpdateTicked"/> which may impact stability.</summary>
|
/// <summary>The instruction is compatible, but references <see cref="ISpecializedEvents.UnvalidatedUpdateTicking"/> or <see cref="ISpecializedEvents.UnvalidatedUpdateTicked"/> which may impact stability.</summary>
|
||||||
|
|
|
@ -19,6 +19,9 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
/// <summary>The non-error issues with the mod, including warnings suppressed by the data record.</summary>
|
/// <summary>The non-error issues with the mod, including warnings suppressed by the data record.</summary>
|
||||||
private ModWarning ActualWarnings = ModWarning.None;
|
private ModWarning ActualWarnings = ModWarning.None;
|
||||||
|
|
||||||
|
/// <summary>The mod IDs which are listed as a requirement by this mod. The value for each pair indicates whether the dependency is required (i.e. not an optional dependency).</summary>
|
||||||
|
private readonly Lazy<IDictionary<string, bool>> Dependencies;
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Accessors
|
** Accessors
|
||||||
|
@ -100,6 +103,8 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
this.Manifest = manifest;
|
this.Manifest = manifest;
|
||||||
this.DataRecord = dataRecord;
|
this.DataRecord = dataRecord;
|
||||||
this.IsIgnored = isIgnored;
|
this.IsIgnored = isIgnored;
|
||||||
|
|
||||||
|
this.Dependencies = new Lazy<IDictionary<string, bool>>(this.ExtractDependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -198,24 +203,22 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool HasRequiredModId(string modId, bool includeOptional)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
this.Dependencies.Value.TryGetValue(modId, out bool isRequired)
|
||||||
|
&& (includeOptional || isRequired);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<string> GetRequiredModIds(bool includeOptional = false)
|
public IEnumerable<string> GetRequiredModIds(bool includeOptional = false)
|
||||||
{
|
{
|
||||||
HashSet<string> required = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
foreach (var pair in this.Dependencies.Value)
|
||||||
|
|
||||||
// yield dependencies
|
|
||||||
if (this.Manifest?.Dependencies != null)
|
|
||||||
{
|
{
|
||||||
foreach (var entry in this.Manifest?.Dependencies)
|
if (includeOptional || pair.Value)
|
||||||
{
|
yield return pair.Key;
|
||||||
if ((entry.IsRequired || includeOptional) && required.Add(entry.UniqueID))
|
|
||||||
yield return entry.UniqueID;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// yield content pack parent
|
|
||||||
if (this.Manifest?.ContentPackFor?.UniqueID != null && required.Add(this.Manifest.ContentPackFor.UniqueID))
|
|
||||||
yield return this.Manifest.ContentPackFor.UniqueID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -237,5 +240,29 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
string rootFolderName = Path.GetFileName(this.RootPath) ?? "";
|
string rootFolderName = Path.GetFileName(this.RootPath) ?? "";
|
||||||
return Path.Combine(rootFolderName, this.RelativeDirectoryPath);
|
return Path.Combine(rootFolderName, this.RelativeDirectoryPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Private methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Extract mod IDs from the manifest that must be installed to load this mod.</summary>
|
||||||
|
/// <returns>Returns a dictionary of mod ID => is required (i.e. not an optional dependency).</returns>
|
||||||
|
public IDictionary<string, bool> ExtractDependencies()
|
||||||
|
{
|
||||||
|
var ids = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// yield dependencies
|
||||||
|
if (this.Manifest?.Dependencies != null)
|
||||||
|
{
|
||||||
|
foreach (var entry in this.Manifest?.Dependencies)
|
||||||
|
ids[entry.UniqueID] = entry.IsRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
// yield content pack parent
|
||||||
|
if (this.Manifest?.ContentPackFor?.UniqueID != null)
|
||||||
|
ids[this.Manifest.ContentPackFor.UniqueID] = true;
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
||||||
{
|
{
|
||||||
/// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility between Linux/Mac or Windows.</summary>
|
/// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility between Linux/macOS or Windows.</summary>
|
||||||
/// <remarks>This is public to support SMAPI rewriting and should not be referenced directly by mods.</remarks>
|
/// <remarks>This is public to support SMAPI rewriting and should not be referenced directly by mods.</remarks>
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")]
|
[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")]
|
||||||
[SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/Mac.")]
|
[SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/macOS.")]
|
||||||
[SuppressMessage("ReSharper", "CS1591", Justification = "Documentation not needed for facade classes.")]
|
[SuppressMessage("ReSharper", "CS1591", Justification = "Documentation not needed for facade classes.")]
|
||||||
public class SpriteBatchFacade : SpriteBatch
|
public class SpriteBatchFacade : SpriteBatch
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,6 +52,9 @@ namespace StardewModdingAPI.Framework.Models
|
||||||
/// <summary>SMAPI's GitHub project name, used to perform update checks.</summary>
|
/// <summary>SMAPI's GitHub project name, used to perform update checks.</summary>
|
||||||
public string GitHubProjectName { get; set; }
|
public string GitHubProjectName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Stardew64Installer's GitHub project name, used to perform update checks.</summary>
|
||||||
|
public string Stardew64InstallerGitHubProjectName { get; set; }
|
||||||
|
|
||||||
/// <summary>The base URL for SMAPI's web API, used to perform update checks.</summary>
|
/// <summary>The base URL for SMAPI's web API, used to perform update checks.</summary>
|
||||||
public string WebApiBaseUrl { get; set; }
|
public string WebApiBaseUrl { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,7 @@ namespace StardewModdingAPI.Framework
|
||||||
#if SMAPI_FOR_WINDOWS
|
#if SMAPI_FOR_WINDOWS
|
||||||
if (Constants.Platform != Platform.Windows)
|
if (Constants.Platform != Platform.Windows)
|
||||||
{
|
{
|
||||||
this.Monitor.Log("Oops! You're running Windows, but this version of SMAPI is for Linux or Mac. Please reinstall SMAPI to fix this.", LogLevel.Error);
|
this.Monitor.Log("Oops! You're running Windows, but this version of SMAPI is for Linux or macOS. Please reinstall SMAPI to fix this.", LogLevel.Error);
|
||||||
this.LogManager.PressAnyKeyToExit();
|
this.LogManager.PressAnyKeyToExit();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -263,10 +263,7 @@ namespace StardewModdingAPI.Framework
|
||||||
});
|
});
|
||||||
|
|
||||||
// set window titles
|
// set window titles
|
||||||
this.SetWindowTitles(
|
this.UpdateWindowTitles();
|
||||||
game: $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}",
|
|
||||||
smapi: $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -280,10 +277,7 @@ namespace StardewModdingAPI.Framework
|
||||||
this.LogManager.LogSettingsHeader(this.Settings);
|
this.LogManager.LogSettingsHeader(this.Settings);
|
||||||
|
|
||||||
// set window titles
|
// set window titles
|
||||||
this.SetWindowTitles(
|
this.UpdateWindowTitles();
|
||||||
game: $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}",
|
|
||||||
smapi: $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// start game
|
// start game
|
||||||
this.Monitor.Log("Starting game...", LogLevel.Debug);
|
this.Monitor.Log("Starting game...", LogLevel.Debug);
|
||||||
|
@ -387,11 +381,7 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
|
|
||||||
// update window titles
|
// update window titles
|
||||||
int modsLoaded = this.ModRegistry.GetAll().Count();
|
this.UpdateWindowTitles();
|
||||||
this.SetWindowTitles(
|
|
||||||
game: $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods",
|
|
||||||
smapi: $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion} with {modsLoaded} mods"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Raised after the game finishes initializing.</summary>
|
/// <summary>Raised after the game finishes initializing.</summary>
|
||||||
|
@ -419,7 +409,7 @@ namespace StardewModdingAPI.Framework
|
||||||
Game1.mapDisplayDevice = new SDisplayDevice(Game1.content, Game1.game1.GraphicsDevice);
|
Game1.mapDisplayDevice = new SDisplayDevice(Game1.content, Game1.game1.GraphicsDevice);
|
||||||
|
|
||||||
// log GPU info
|
// log GPU info
|
||||||
#if SMAPI_FOR_WINDOWS
|
#if SMAPI_FOR_WINDOWS && !SMAPI_FOR_WINDOWS_64BIT_HACK
|
||||||
this.Monitor.Log($"Running on GPU: {Game1.game1.GraphicsDevice?.Adapter?.Description ?? "<unknown>"}");
|
this.Monitor.Log($"Running on GPU: {Game1.game1.GraphicsDevice?.Adapter?.Description ?? "<unknown>"}");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1238,13 +1228,23 @@ namespace StardewModdingAPI.Framework
|
||||||
return !issuesFound;
|
return !issuesFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Set the window titles for the game and console windows.</summary>
|
/// <summary>Set the titles for the game and console windows.</summary>
|
||||||
/// <param name="game">The game window text.</param>
|
private void UpdateWindowTitles()
|
||||||
/// <param name="smapi">The SMAPI window text.</param>
|
|
||||||
private void SetWindowTitles(string game, string smapi)
|
|
||||||
{
|
{
|
||||||
this.Game.Window.Title = game;
|
string smapiVersion = $"{Constants.ApiVersion}{(EarlyConstants.IsWindows64BitHack ? " [64-bit]" : "")}";
|
||||||
this.LogManager.SetConsoleTitle(smapi);
|
|
||||||
|
string consoleTitle = $"SMAPI {smapiVersion} - running Stardew Valley {Constants.GameVersion}";
|
||||||
|
string gameTitle = $"Stardew Valley {Constants.GameVersion} - running SMAPI {smapiVersion}";
|
||||||
|
|
||||||
|
if (this.ModRegistry.AreAllModsLoaded)
|
||||||
|
{
|
||||||
|
int modsLoaded = this.ModRegistry.GetAll().Count();
|
||||||
|
consoleTitle += $" with {modsLoaded} mods";
|
||||||
|
gameTitle += $" with {modsLoaded} mods";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Game.Window.Title = gameTitle;
|
||||||
|
this.LogManager.SetConsoleTitle(consoleTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Asynchronously check for a new version of SMAPI and any installed mods, and print alerts to the console if an update is available.</summary>
|
/// <summary>Asynchronously check for a new version of SMAPI and any installed mods, and print alerts to the console if an update is available.</summary>
|
||||||
|
@ -1259,7 +1259,7 @@ namespace StardewModdingAPI.Framework
|
||||||
// create client
|
// create client
|
||||||
string url = this.Settings.WebApiBaseUrl;
|
string url = this.Settings.WebApiBaseUrl;
|
||||||
#if !SMAPI_FOR_WINDOWS
|
#if !SMAPI_FOR_WINDOWS
|
||||||
url = url.Replace("https://", "http://"); // workaround for OpenSSL issues with the game's bundled Mono on Linux/Mac
|
url = url.Replace("https://", "http://"); // workaround for OpenSSL issues with the game's bundled Mono on Linux/macOS
|
||||||
#endif
|
#endif
|
||||||
WebApiClient client = new WebApiClient(url, Constants.ApiVersion);
|
WebApiClient client = new WebApiClient(url, Constants.ApiVersion);
|
||||||
this.Monitor.Log("Checking for updates...");
|
this.Monitor.Log("Checking for updates...");
|
||||||
|
@ -1302,6 +1302,41 @@ namespace StardewModdingAPI.Framework
|
||||||
this.LogManager.WriteUpdateMarker(updateFound.ToString(), updateUrl);
|
this.LogManager.WriteUpdateMarker(updateFound.ToString(), updateUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check Stardew64Installer version
|
||||||
|
if (Constants.IsPatchedByStardew64Installer(out ISemanticVersion patchedByVersion))
|
||||||
|
{
|
||||||
|
ISemanticVersion updateFound = null;
|
||||||
|
string updateUrl = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// fetch update check
|
||||||
|
ModEntryModel response = client.GetModInfo(new[] { new ModSearchEntryModel("Steviegt6.Stardew64Installer", patchedByVersion, new[] { $"GitHub:{this.Settings.Stardew64InstallerGitHubProjectName}" }) }, apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform).Single().Value;
|
||||||
|
updateFound = response.SuggestedUpdate?.Version;
|
||||||
|
updateUrl = response.SuggestedUpdate?.Url ?? Constants.HomePageUrl;
|
||||||
|
|
||||||
|
// log message
|
||||||
|
if (updateFound != null)
|
||||||
|
this.Monitor.Log($"You can update Stardew64Installer to {updateFound}: {updateUrl}", LogLevel.Alert);
|
||||||
|
else
|
||||||
|
this.Monitor.Log(" Stardew64Installer okay.");
|
||||||
|
|
||||||
|
// show errors
|
||||||
|
if (response.Errors.Any())
|
||||||
|
{
|
||||||
|
this.Monitor.Log("Couldn't check for a new version of Stardew64Installer. This won't affect your game, but you may not be notified of new versions if this keeps happening.", LogLevel.Warn);
|
||||||
|
this.Monitor.Log($"Error: {string.Join("\n", response.Errors)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.Monitor.Log("Couldn't check for a new version of Stardew64Installer. This won't affect your game, but you won't be notified of new versions if this keeps happening.", LogLevel.Warn);
|
||||||
|
this.Monitor.Log(ex is WebException && ex.InnerException == null
|
||||||
|
? $"Error: {ex.Message}"
|
||||||
|
: $"Error: {ex.GetLogSummary()}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check mod versions
|
// check mod versions
|
||||||
if (mods.Any())
|
if (mods.Any())
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Serialization
|
||||||
{
|
{
|
||||||
/// <summary>Handles deserialization of <see cref="Color"/> for crossplatform compatibility.</summary>
|
/// <summary>Handles deserialization of <see cref="Color"/> for crossplatform compatibility.</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// - Linux/Mac format: { "B": 76, "G": 51, "R": 25, "A": 102 }
|
/// - Linux/macOS format: { "B": 76, "G": 51, "R": 25, "A": 102 }
|
||||||
/// - Windows format: "26, 51, 76, 102"
|
/// - Windows format: "26, 51, 76, 102"
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
internal class ColorConverter : SimpleReadOnlyConverter<Color>
|
internal class ColorConverter : SimpleReadOnlyConverter<Color>
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Serialization
|
||||||
{
|
{
|
||||||
/// <summary>Handles deserialization of <see cref="Point"/> for crossplatform compatibility.</summary>
|
/// <summary>Handles deserialization of <see cref="Point"/> for crossplatform compatibility.</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// - Linux/Mac format: { "X": 1, "Y": 2 }
|
/// - Linux/macOS format: { "X": 1, "Y": 2 }
|
||||||
/// - Windows format: "1, 2"
|
/// - Windows format: "1, 2"
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
internal class PointConverter : SimpleReadOnlyConverter<Point>
|
internal class PointConverter : SimpleReadOnlyConverter<Point>
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.Serialization
|
||||||
{
|
{
|
||||||
/// <summary>Handles deserialization of <see cref="Rectangle"/> for crossplatform compatibility.</summary>
|
/// <summary>Handles deserialization of <see cref="Rectangle"/> for crossplatform compatibility.</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// - Linux/Mac format: { "X": 1, "Y": 2, "Width": 3, "Height": 4 }
|
/// - Linux/macOS format: { "X": 1, "Y": 2, "Width": 3, "Height": 4 }
|
||||||
/// - Windows format: "{X:1 Y:2 Width:3 Height:4}"
|
/// - Windows format: "{X:1 Y:2 Width:3 Height:4}"
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
internal class RectangleConverter : SimpleReadOnlyConverter<Rectangle>
|
internal class RectangleConverter : SimpleReadOnlyConverter<Rectangle>
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Serialization
|
||||||
{
|
{
|
||||||
/// <summary>Handles deserialization of <see cref="Vector2"/> for crossplatform compatibility.</summary>
|
/// <summary>Handles deserialization of <see cref="Vector2"/> for crossplatform compatibility.</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// - Linux/Mac format: { "X": 1, "Y": 2 }
|
/// - Linux/macOS format: { "X": 1, "Y": 2 }
|
||||||
/// - Windows format: "1, 2"
|
/// - Windows format: "1, 2"
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
internal class Vector2Converter : SimpleReadOnlyConverter<Vector2>
|
internal class Vector2Converter : SimpleReadOnlyConverter<Vector2>
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace StardewModdingAPI
|
||||||
/// <summary>The Linux version of the game.</summary>
|
/// <summary>The Linux version of the game.</summary>
|
||||||
Linux = Platform.Linux,
|
Linux = Platform.Linux,
|
||||||
|
|
||||||
/// <summary>The Mac version of the game.</summary>
|
/// <summary>The macOS version of the game.</summary>
|
||||||
Mac = Platform.Mac,
|
Mac = Platform.Mac,
|
||||||
|
|
||||||
/// <summary>The Windows version of the game.</summary>
|
/// <summary>The Windows version of the game.</summary>
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using Netcode;
|
using Netcode;
|
||||||
|
using StardewModdingAPI.Framework;
|
||||||
using StardewModdingAPI.Framework.ContentManagers;
|
using StardewModdingAPI.Framework.ContentManagers;
|
||||||
using StardewModdingAPI.Framework.Reflection;
|
using StardewModdingAPI.Framework.Reflection;
|
||||||
using StardewModdingAPI.Toolkit.Utilities;
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
|
@ -36,15 +37,18 @@ namespace StardewModdingAPI.Metadata
|
||||||
/// <summary>An internal content manager used only for asset propagation. See remarks on <see cref="GameContentManagerForAssetPropagation"/>.</summary>
|
/// <summary>An internal content manager used only for asset propagation. See remarks on <see cref="GameContentManagerForAssetPropagation"/>.</summary>
|
||||||
private readonly GameContentManagerForAssetPropagation DisposableContentManager;
|
private readonly GameContentManagerForAssetPropagation DisposableContentManager;
|
||||||
|
|
||||||
|
/// <summary>Writes messages to the console.</summary>
|
||||||
|
private readonly IMonitor Monitor;
|
||||||
|
|
||||||
|
/// <summary>Simplifies access to private game code.</summary>
|
||||||
|
private readonly Reflector Reflection;
|
||||||
|
|
||||||
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
|
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
|
||||||
private readonly bool AggressiveMemoryOptimizations;
|
private readonly bool AggressiveMemoryOptimizations;
|
||||||
|
|
||||||
/// <summary>Normalizes an asset key to match the cache key and assert that it's valid.</summary>
|
/// <summary>Normalizes an asset key to match the cache key and assert that it's valid.</summary>
|
||||||
private readonly Func<string, string> AssertAndNormalizeAssetName;
|
private readonly Func<string, string> AssertAndNormalizeAssetName;
|
||||||
|
|
||||||
/// <summary>Simplifies access to private game code.</summary>
|
|
||||||
private readonly Reflector Reflection;
|
|
||||||
|
|
||||||
/// <summary>Optimized bucket categories for batch reloading assets.</summary>
|
/// <summary>Optimized bucket categories for batch reloading assets.</summary>
|
||||||
private enum AssetBucket
|
private enum AssetBucket
|
||||||
{
|
{
|
||||||
|
@ -65,12 +69,14 @@ namespace StardewModdingAPI.Metadata
|
||||||
/// <summary>Initialize the core asset data.</summary>
|
/// <summary>Initialize the core asset data.</summary>
|
||||||
/// <param name="mainContent">The main content manager through which to reload assets.</param>
|
/// <param name="mainContent">The main content manager through which to reload assets.</param>
|
||||||
/// <param name="disposableContent">An internal content manager used only for asset propagation.</param>
|
/// <param name="disposableContent">An internal content manager used only for asset propagation.</param>
|
||||||
|
/// <param name="monitor">Writes messages to the console.</param>
|
||||||
/// <param name="reflection">Simplifies access to private code.</param>
|
/// <param name="reflection">Simplifies access to private code.</param>
|
||||||
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
|
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
|
||||||
public CoreAssetPropagator(LocalizedContentManager mainContent, GameContentManagerForAssetPropagation disposableContent, Reflector reflection, bool aggressiveMemoryOptimizations)
|
public CoreAssetPropagator(LocalizedContentManager mainContent, GameContentManagerForAssetPropagation disposableContent, IMonitor monitor, Reflector reflection, bool aggressiveMemoryOptimizations)
|
||||||
{
|
{
|
||||||
this.MainContentManager = mainContent;
|
this.MainContentManager = mainContent;
|
||||||
this.DisposableContentManager = disposableContent;
|
this.DisposableContentManager = disposableContent;
|
||||||
|
this.Monitor = monitor;
|
||||||
this.Reflection = reflection;
|
this.Reflection = reflection;
|
||||||
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
|
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
|
||||||
|
|
||||||
|
@ -116,7 +122,17 @@ namespace StardewModdingAPI.Metadata
|
||||||
default:
|
default:
|
||||||
foreach (var entry in bucket)
|
foreach (var entry in bucket)
|
||||||
{
|
{
|
||||||
bool changed = this.PropagateOther(entry.Key, entry.Value, ignoreWorld, out bool curChangedMapWarps);
|
bool changed = false;
|
||||||
|
bool curChangedMapWarps = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
changed = this.PropagateOther(entry.Key, entry.Value, ignoreWorld, out curChangedMapWarps);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.Monitor.Log($"An error occurred while propagating asset changes. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||||
|
}
|
||||||
|
|
||||||
propagatedAssets[entry.Key] = changed;
|
propagatedAssets[entry.Key] = changed;
|
||||||
updatedNpcWarps = updatedNpcWarps || curChangedMapWarps;
|
updatedNpcWarps = updatedNpcWarps || curChangedMapWarps;
|
||||||
}
|
}
|
||||||
|
@ -258,6 +274,7 @@ namespace StardewModdingAPI.Metadata
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case "data\\bundles": // NetWorldState constructor
|
case "data\\bundles": // NetWorldState constructor
|
||||||
|
if (Context.IsMainPlayer && Game1.netWorldState != null)
|
||||||
{
|
{
|
||||||
var bundles = this.Reflection.GetField<NetBundles>(Game1.netWorldState.Value, "bundles").GetValue();
|
var bundles = this.Reflection.GetField<NetBundles>(Game1.netWorldState.Value, "bundles").GetValue();
|
||||||
var rewards = this.Reflection.GetField<NetIntDictionary<bool, NetBool>>(Game1.netWorldState.Value, "bundleRewards").GetValue();
|
var rewards = this.Reflection.GetField<NetIntDictionary<bool, NetBool>>(Game1.netWorldState.Value, "bundleRewards").GetValue();
|
||||||
|
@ -286,6 +303,10 @@ namespace StardewModdingAPI.Metadata
|
||||||
Game1.clothingInformation = content.Load<Dictionary<int, string>>(key);
|
Game1.clothingInformation = content.Load<Dictionary<int, string>>(key);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case "data\\concessions": // MovieTheater.GetConcessions
|
||||||
|
MovieTheater.ClearCachedLocalizedData();
|
||||||
|
return true;
|
||||||
|
|
||||||
case "data\\concessiontastes": // MovieTheater.GetConcessionTasteForCharacter
|
case "data\\concessiontastes": // MovieTheater.GetConcessionTasteForCharacter
|
||||||
this.Reflection
|
this.Reflection
|
||||||
.GetField<List<ConcessionTaste>>(typeof(MovieTheater), "_concessionTastes")
|
.GetField<List<ConcessionTaste>>(typeof(MovieTheater), "_concessionTastes")
|
||||||
|
@ -306,16 +327,9 @@ namespace StardewModdingAPI.Metadata
|
||||||
case "data\\hairdata": // Farmer.GetHairStyleMetadataFile
|
case "data\\hairdata": // Farmer.GetHairStyleMetadataFile
|
||||||
return this.ReloadHairData();
|
return this.ReloadHairData();
|
||||||
|
|
||||||
case "data\\moviesreactions": // MovieTheater.GetMovieReactions
|
|
||||||
this.Reflection
|
|
||||||
.GetField<List<MovieCharacterReaction>>(typeof(MovieTheater), "_genericReactions")
|
|
||||||
.SetValue(content.Load<List<MovieCharacterReaction>>(key));
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case "data\\movies": // MovieTheater.GetMovieData
|
case "data\\movies": // MovieTheater.GetMovieData
|
||||||
this.Reflection
|
case "data\\moviesreactions": // MovieTheater.GetMovieReactions
|
||||||
.GetField<Dictionary<string, MovieData>>(typeof(MovieTheater), "_movieData")
|
MovieTheater.ClearCachedLocalizedData();
|
||||||
.SetValue(content.Load<Dictionary<string, MovieData>>(key));
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case "data\\npcdispositions": // NPC constructor
|
case "data\\npcdispositions": // NPC constructor
|
||||||
|
|
|
@ -24,6 +24,8 @@ namespace StardewModdingAPI
|
||||||
/// <param name="args">The command-line arguments.</param>
|
/// <param name="args">The command-line arguments.</param>
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
Console.Title = $"SMAPI {EarlyConstants.RawApiVersion}{(EarlyConstants.IsWindows64BitHack ? " 64-bit" : "")} - {Console.Title}";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
|
AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
|
||||||
|
@ -159,7 +161,7 @@ namespace StardewModdingAPI
|
||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
Program.PressAnyKeyToExit(showMessage: true);
|
Program.PressAnyKeyToExit(showMessage: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,9 +69,14 @@ copy all the settings, or you may cause bugs due to overridden changes in future
|
||||||
*/
|
*/
|
||||||
"GitHubProjectName": "Pathoschild/SMAPI",
|
"GitHubProjectName": "Pathoschild/SMAPI",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stardew64Installer's GitHub project name, used to perform update checks.
|
||||||
|
*/
|
||||||
|
"Stardew64InstallerGitHubProjectName": "Steviegt6/Stardew64Installer",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base URL for SMAPI's web API, used to perform update checks.
|
* The base URL for SMAPI's web API, used to perform update checks.
|
||||||
* Note: the protocol will be changed to http:// on Linux/Mac due to OpenSSL issues with the
|
* Note: the protocol will be changed to http:// on Linux/macOS due to OpenSSL issues with the
|
||||||
* game's bundled Mono.
|
* game's bundled Mono.
|
||||||
*/
|
*/
|
||||||
"WebApiBaseUrl": "https://smapi.io/api/",
|
"WebApiBaseUrl": "https://smapi.io/api/",
|
||||||
|
@ -85,7 +90,7 @@ copy all the settings, or you may cause bugs due to overridden changes in future
|
||||||
* The colors to use for text written to the SMAPI console.
|
* The colors to use for text written to the SMAPI console.
|
||||||
*
|
*
|
||||||
* The possible values for 'UseScheme' are:
|
* The possible values for 'UseScheme' are:
|
||||||
* - AutoDetect: SMAPI will assume a light background on Mac, and detect the background color
|
* - AutoDetect: SMAPI will assume a light background on macOS, and detect the background color
|
||||||
* automatically on Linux or Windows.
|
* automatically on Linux or Windows.
|
||||||
* - LightBackground: use darker text colors that look better on a white or light background.
|
* - LightBackground: use darker text colors that look better on a white or light background.
|
||||||
* - DarkBackground: use lighter text colors that look better on a black or dark background.
|
* - DarkBackground: use lighter text colors that look better on a black or dark background.
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
|
|
||||||
<Import Project="..\..\build\common.targets" />
|
<Import Project="..\..\build\common.targets" />
|
||||||
|
|
||||||
|
<PropertyGroup Condition="$(DefineConstants.Contains(SMAPI_FOR_WINDOWS_64BIT_HACK))">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LargeAddressAware" Version="1.0.5" />
|
<PackageReference Include="LargeAddressAware" Version="1.0.5" />
|
||||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||||
|
@ -34,7 +38,7 @@
|
||||||
|
|
||||||
<!-- Windows only -->
|
<!-- Windows only -->
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
||||||
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
|
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" Condition="!$(DefineConstants.Contains(SMAPI_FOR_WINDOWS_64BIT_HACK))" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue