Compare commits

..

1 Commits

Author SHA1 Message Date
Jesse Plamondon-Willard abdbd1a90e add taskbar icon for Mono
This needs to be an embedded BMP file named Icon.bmp for Mono to recognise it.
2018-05-11 21:32:58 -04:00
867 changed files with 18207 additions and 55711 deletions

View File

@ -3,7 +3,7 @@ root: true
##########
## General formatting
## documentation: https://editorconfig.org
## documentation: http://editorconfig.org
##########
[*]
indent_style = space
@ -12,18 +12,12 @@ insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{csproj,nuspec,targets}]
indent_size = 2
[*.csproj]
charset = utf-8-bom
indent_size = 2
insert_final_newline = false
[README.txt]
end_of_line=crlf
[*.{command,sh}]
end_of_line=lf
[*.json]
indent_size = 2
##########
## C# formatting
@ -71,11 +65,4 @@ csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_inlined_variable_declaration = true:warning
# avoid superfluous braces
csharp_prefer_braces = false:hint
##########
## Column guidelines
## documentation: https://marketplace.visualstudio.com/items?itemName=PaulHarrington.EditorGuidelines
##########
[*.md]
guidelines = 100
csharp_prefer_braces = false:suggestion

6
.gitattributes vendored
View File

@ -1,6 +1,2 @@
# normalize line endings
# normalise line endings
* text=auto
README.txt text eol=crlf
*.command text eol=lf
*.sh text eol=lf

View File

@ -1,12 +0,0 @@
Do you want to...
* **Ask for help or report a bug?**
Please see 'get help' on [the SMAPI website](https://smapi.io) instead, don't create a GitHub
issue.
* **Submit a pull request?**
Pull requests are welcome! If you're submitting a new feature, it's best to discuss first to make
sure it'll be accepted. Feel free to come chat [on Discord](https://smapi.io/community).
Documenting your code and using the same formatting conventions is appreciated, but don't worry too
much about it. We'll fix up the code after we accept the pull request if needed.

3
.github/FUNDING.yml vendored
View File

@ -1,3 +0,0 @@
patreon: pathoschild
ko_fi: pathoschild
custom: https://www.paypal.me/pathoschild

View File

@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Troubleshooting guide for players
url: https://smapi.io/troubleshoot
about: See if your question is already answered first!
- name: Get help or discuss
url: https://smapi.io/help
about: Ask for help from the community, or join the Stardew Valley Discord to ask questions, report issues, or discuss with the SMAPI developer, players, and mod authors. The SMAPI developer is @Pathoschild#0001 on Discord.

View File

@ -1,37 +0,0 @@
---
name: Create a development task
about: DON'T DO THIS BEFORE READING. This is for specific changes to the code or technical bug reports. See below if something isn't working, you have questions or ideas, or you want to discuss something.
---
<!--
STOP!
Is this a specific development task? Don't create an issue if not!
See https://smapi.io/community if something isn't working, you have questions or ideas, or you want
to discuss something.
If you're absolutely sure it's a specific development task (e.g. a specific bug, not just
'something went wrong on my computer'), edit the template below.
-->
**Describe the bug**
A clear and concise description of what the bug is. Provide any other details you think might be relevant here.
**To Reproduce**
Exact steps which reproduce the bug, if possible. For example:
1. Load save '...'.
2. Walk to '....'.
3. Click '....'.
4. Error occurs.
**Log file**
Upload your SMAPI log to https://smapi.io/log and post a link here.
**Screenshots**
If applicable, add screenshots to help explain your problem.

3
.github/SUPPORT.md vendored
View File

@ -1,3 +0,0 @@
GitHub issues are only used for SMAPI development tasks.
To get help with SMAPI problems, see 'get help' on [the SMAPI website](https://smapi.io/) instead.

19
.gitignore vendored
View File

@ -11,7 +11,6 @@
[Oo]bj/
# Visual Studio cache/options
.config/
.vs/
# ReSharper
@ -19,9 +18,6 @@ _ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# Rider
.idea/
# NuGet packages
*.nupkg
**/packages/*
@ -30,18 +26,3 @@ _ReSharper*/
# sensitive files
appsettings.Development.json
# Azure generated files
src/SMAPI.Web/Properties/PublishProfiles/*.pubxml
src/SMAPI.Web/Properties/ServiceDependencies/* - Web Deploy/
# macOS
.DS_Store
# Loader Game Asserts
src/Loader/Assets/Content/
src/Loader/libs/
# VSHistory
**/.vshistory/
/build/StardewValleyAndroidStuff/

Binary file not shown.

View File

@ -0,0 +1,5 @@
using System.Reflection;
[assembly: AssemblyProduct("SMAPI")]
[assembly: AssemblyVersion("2.6.0")]
[assembly: AssemblyFileVersion("2.6.0")]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,78 +1,125 @@
<!--
This MSBuild file sets the common configuration and build scripts used by all the projects in this
repo. It imports the other MSBuild files as needed.
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- load dev settings -->
<Import Condition="$(OS) != 'Windows_NT' AND Exists('$(HOME)\stardewvalley.targets')" Project="$(HOME)\stardewvalley.targets" />
<Import Condition="$(OS) == 'Windows_NT' AND Exists('$(USERPROFILE)\stardewvalley.targets')" Project="$(USERPROFILE)\stardewvalley.targets" />
<!-- find game path -->
<PropertyGroup>
<!--set general build properties -->
<Version>3.18.4</Version>
<Product>SMAPI</Product>
<LangVersion>latest</LangVersion>
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
<DefineConstants Condition="$(OS) == 'Windows_NT' AND '$(BUILD_FOR_MOBILE)' == ''">$(DefineConstants);SMAPI_DEPRECATED;SMAPI_FOR_WINDOWS</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<!-- Linux paths -->
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/GOG Games/Stardew Valley/game</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.steam/steam/steamapps/common/Stardew Valley</GamePath>
<!-- Mac paths -->
<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>
<!-- Windows paths -->
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GOG Galaxy\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)') AND '$(OS)' == 'Windows_NT'">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32))</GamePath>
<GamePath Condition="!Exists('$(GamePath)') AND '$(OS)' == 'Windows_NT'">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32))</GamePath>
<!--embed symbols for error stack trace line numbers on Linux/macOS: https://github.com/dotnet/runtime/issues/39987-->
<DebugType>embedded</DebugType>
<!--enable nullable annotations, except in .NET Standard 2.0 where they aren't supported-->
<Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable>
<NoWarn Condition="'$(TargetFramework)' == 'netstandard2.0'">$(NoWarn);CS8632</NoWarn>
<!--set platform-->
<!--compile constants -->
<DefineConstants Condition="$(OS) == 'Windows_NT'">$(DefineConstants);SMAPI_FOR_WINDOWS</DefineConstants>
<CopyToGameFolder>false</CopyToGameFolder>
<!-- allow mods to be compiled as AnyCPU for compatibility with older platforms -->
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<!--
suppress warnings that don't apply, so it's easier to spot actual issues.
warning | builds | summary | rationale
┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
CS0436 | all | local type conflicts with imported type | SMAPI needs to use certain low-level code during very early compatibility checks, before it's safe to load any other DLLs.
CS0612 | deprecated | member is obsolete | internal references to deprecated code when deprecated code is enabled.
CS0618 | deprecated | member is obsolete (with message) | internal references to deprecated code when deprecated code is enabled.
CA1416 | all | platform code available on all platforms | Compiler doesn't recognize the #if constants used by SMAPI.
CS0809 | all | obsolete overload for non-obsolete member | This is deliberate to signal to mods that certain APIs are only implemented for the game and shouldn't be called by mods.
NU1701 | all | NuGet package targets older .NET version | All such packages are carefully tested to make sure they do work.
-->
<NoWarn Condition="$(DefineConstants.Contains(SMAPI_DEPRECATED))">$(NoWarn);CS0612;CS0618</NoWarn>
<NoWarn>$(NoWarn);CS0436;CA1416;CS0809;NU1701</NoWarn>
</PropertyGroup>
<ItemGroup Condition="$(COMPILE_WITH_PLUGIN) == 'True'">
<PackageReference Include="Microsoft.Net.Compilers" Version="3.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<!-- add common references -->
<ItemGroup>
<Reference Condition="'$(OS)' == 'Windows_NT'" Include="System.Management" />
</ItemGroup>
<!--find game folder-->
<Import Project="find-game-folder.targets" />
<!-- <Target Name="ValidateInstallPath" AfterTargets="BeforeBuild">-->
<!-- &lt;!&ndash; if game path is invalid, show one user-friendly error instead of a slew of reference errors &ndash;&gt;-->
<!-- <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." />-->
<!-- </Target>-->
<!-- add game references-->
<Choose>
<When Condition="'$(MSBuildProjectName)' == 'StardewModdingAPI' OR '$(MSBuildProjectName)' == 'StardewModdingAPI.Mods.ConsoleCommands' OR '$(MSBuildProjectName)' == 'StardewModdingAPI.Tests'">
<Choose>
<When Condition="$(OS) == 'Windows_NT'">
<ItemGroup>
<!--XNA framework-->
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
<Private>False</Private>
</Reference>
<!-- game DLLs -->
<Reference Include="Netcode">
<HintPath>$(GamePath)\Netcode.dll</HintPath>
<Private Condition="'$(MSBuildProjectName)' != 'StardewModdingAPI.Tests'">False</Private>
</Reference>
<Reference Include="Stardew Valley">
<HintPath>$(GamePath)\Stardew Valley.exe</HintPath>
<Private Condition="'$(MSBuildProjectName)' != 'StardewModdingAPI.Tests'">False</Private>
</Reference>
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
<HintPath>$(GamePath)\xTile.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<!-- MonoGame -->
<Reference Include="MonoGame.Framework">
<HintPath>$(GamePath)\MonoGame.Framework.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<!-- game DLLs -->
<Reference Include="StardewValley">
<HintPath>$(GamePath)\StardewValley.exe</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="xTile">
<HintPath>$(GamePath)\xTile.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
</Otherwise>
</Choose>
</When>
</Choose>
<!-- common build settings -->
<PropertyGroup>
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<!--deploy local files-->
<Import Project="deploy-local-smapi.targets" Condition="'$(CopyToGameFolder)' == 'true'" />
<!-- if game path is invalid, show one user-friendly error instead of a slew of reference errors -->
<Target Name="BeforeBuild">
<Error Condition="!Exists('$(GamePath)')" Text="Failed to find the game install path automatically; edit the *.csproj file and manually add a &lt;GamePath&gt; setting with the full directory path containing the Stardew Valley executable." />
</Target>
<!-- launch SMAPI through Visual Studio -->
<PropertyGroup Condition="'$(MSBuildProjectName)' == 'SMAPI'">
<!-- copy files into game directory and enable debugging (only in debug mode) -->
<Target Name="AfterBuild">
<CallTarget Targets="CopySMAPI;CopyDefaultMod" Condition="'$(Configuration)' == 'Debug'" />
</Target>
<Target Name="CopySMAPI" Condition="'$(MSBuildProjectName)' == 'StardewModdingAPI'">
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).config.json" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).metadata.json" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\StardewModdingAPI.Internal.dll" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.dll" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.dll" DestinationFolder="$(GamePath)" />
</Target>
<Target Name="CopyDefaultMod" Condition="'$(MSBuildProjectName)' == 'StardewModdingAPI.Mods.ConsoleCommands'">
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\Mods\ConsoleCommands" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\Mods\ConsoleCommands" Condition="Exists('$(TargetDir)\$(TargetName).pdb')" />
<Copy SourceFiles="$(TargetDir)\manifest.json" DestinationFolder="$(GamePath)\Mods\ConsoleCommands" />
</Target>
<!-- launch SMAPI on debug -->
<PropertyGroup Condition="$(Configuration) == 'Debug'">
<StartAction>Program</StartAction>
<StartProgram>$(GamePath)\StardewModdingAPI.exe</StartProgram>
<StartWorkingDirectory>$(GamePath)</StartWorkingDirectory>
</PropertyGroup>
<!-- Somehow this makes Visual Studio for macOS recognise the previous section. Nobody knows why. -->
<!-- Somehow this makes Visual Studio for Mac recognise the previous section. Nobody knows why. -->
<PropertyGroup Condition="'$(RunConfiguration)' == 'Default'" />
</Project>

View File

@ -1,13 +0,0 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--set properties -->
<PropertyGroup>
<!-- <BUILD_FOR_MOBILE></BUILD_FOR_MOBILE>-->
<BUILD_FOR_MOBILE>GOOGLE</BUILD_FOR_MOBILE>
<!-- <BUILD_FOR_MOBILE>GOOGLE_145</BUILD_FOR_MOBILE>-->
<!-- <BUILD_FOR_MOBILE>AMAZON</BUILD_FOR_MOBILE>-->
<!-- <BUILD_FOR_MOBILE>SAMSUNG</BUILD_FOR_MOBILE>-->
<!-- <BUILD_FOR_MOBILE>GOOGLE_LEGACY</BUILD_FOR_MOBILE>-->
<COMPILE_WITH_PLUGIN>False</COMPILE_WITH_PLUGIN>
<DefineConstants Condition="'$(BUILD_FOR_MOBILE)' == 'GOOGLE'">SMAPI_DEPRECATED;SMAPI_FOR_MOBILE;ANDROID_TARGET_GOOGLE</DefineConstants>
</PropertyGroup>
</Project>

View File

@ -1,77 +0,0 @@
<!--
This MSBuild file copies SMAPI and the bundled mods into the local Stardew Valley folder on build
to simplify testing. This just avoids needing to run the SMAPI installer each time.
This assumes `find-game-folder.targets` has already been imported and validated.
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopySmapiFiles" AfterTargets="AfterBuild">
<CallTarget Targets="CopySMAPI;CopyDefaultMods" />
</Target>
<Target Name="CopySMAPI" Condition="'$(MSBuildProjectName)' == 'SMAPI'">
<!-- SMAPI -->
<ItemGroup>
<TranslationFiles Include="$(TargetDir)\i18n\*.json" />
</ItemGroup>
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" Condition="$(OS) == 'Windows_NT'" />
<Copy SourceFiles="$(TargetDir)\$(TargetName)" DestinationFolder="$(GamePath)" Condition="$(OS) != 'Windows_NT'" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\SMAPI.config.json" DestinationFiles="$(GamePath)\smapi-internal\config.json" />
<Copy SourceFiles="$(TargetDir)\SMAPI.metadata.json" DestinationFiles="$(GamePath)\smapi-internal\metadata.json" />
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\TMXTile.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Pintail.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\smapi-internal\i18n" />
<!-- Harmony + dependencies -->
<Copy SourceFiles="$(TargetDir)\0Harmony.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\0Harmony.xml" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.Mdb.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.Pdb.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\MonoMod.Common.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<!-- FluentHttpClient + dependencies -->
<Copy SourceFiles="$(TargetDir)\Pathoschild.Http.Client.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\System.Net.Http.Formatting.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<!-- .NET dependencies -->
<Copy SourceFiles="$(TargetDir)\System.Management.dll" DestinationFolder="$(GamePath)\smapi-internal" Condition="$(OS) == 'Windows_NT'" />
<!-- Legacy .NET dependencies (remove in SMAPI 4.0.0) -->
<Copy SourceFiles="$(TargetDir)\System.Configuration.ConfigurationManager.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\System.Runtime.Caching.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\System.Security.Permissions.dll" DestinationFolder="$(GamePath)\smapi-internal" />
</Target>
<!-- .NET metadata files -->
<Target Name="CopyNetMetadata" Condition="'$(MSBuildProjectName)' == 'SMAPI.Installer'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\assets\runtimeconfig.json" DestinationFiles="$(GamePath)\StardewModdingAPI.runtimeconfig.json" />
<Copy SourceFiles="$(TargetDir)\assets\windows-exe-config.xml" DestinationFiles="$(GamePath)\StardewModdingAPI.exe.config" Condition="$(OS) == 'Windows_NT'" />
<Copy SourceFiles="$(GamePath)\Stardew Valley.deps.json" DestinationFiles="$(GamePath)\StardewModdingAPI.deps.json" Condition="!Exists('$(GamePath)\StardewModdingAPI.deps.json')" />
</Target>
<!-- bundled mods -->
<Target Name="CopyDefaultMods" Condition="'$(MSBuildProjectName)' == 'SMAPI.Mods.ConsoleCommands' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.ErrorHandler' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.SaveBackup'">
<ItemGroup>
<TranslationFiles Include="$(TargetDir)\i18n\*.json" />
</ItemGroup>
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" />
<Copy SourceFiles="$(TargetDir)\manifest.json" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" />
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" />
</Target>
<!-- toolkit -->
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
</Target>
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
</Target>
</Project>

View File

@ -1,66 +0,0 @@
<!--
This MSBuild file detects the Stardew Valley install path if possible, and sets the 'GamePath'
property.
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- import developer's custom path (if any) -->
<Import Condition="$(OS) != 'Windows_NT' AND Exists('$(HOME)\stardewvalley.targets')" Project="$(HOME)\stardewvalley.targets" />
<Import Condition="$(OS) == 'Windows_NT' AND Exists('$(USERPROFILE)\stardewvalley.targets')" Project="$(USERPROFILE)\stardewvalley.targets" />
<!-- find game path -->
<Choose>
<When Condition="$(OS) == 'Unix' OR $(OS) == 'OSX'">
<PropertyGroup>
<!-- Linux -->
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/GOG Games/Stardew Valley/game</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.steam/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>
<!-- macOS (may be 'Unix' or 'OSX') -->
<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>
</PropertyGroup>
</When>
<When Condition="$(OS) == 'Windows_NT'">
<PropertyGroup>
<!-- registry paths -->
<GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32))</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32))</GamePath>
<!-- derive from Steam library path -->
<_SteamLibraryPath>$([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Valve\Steam', 'SteamPath', null, RegistryView.Registry32))</_SteamLibraryPath>
<GamePath Condition="!Exists('$(GamePath)') AND '$(_SteamLibraryPath)' != ''">$(_SteamLibraryPath)\steamapps\common\Stardew Valley</GamePath>
<!-- GOG paths -->
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\GalaxyClient\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\GOG Galaxy\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\GOG Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GOG Galaxy\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GOG Games\Stardew Valley</GamePath>
<!-- Xbox app paths -->
<!--
The Xbox app saves the install path to the registry, but we can't use it here since it
saves the internal readonly path (like C:\Program Files\WindowsApps\Mutable\<package ID>)
instead of the mods-enabled path (like C:\Program Files\ModifiableWindowsApps\Stardew Valley).
Fortunately we can cheat a bit: players can customize the install drive, but they can't
change the install path on the drive.
-->
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">D:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">E:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">F:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">G:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">H:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<!-- Steam paths -->
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\Steam\steamapps\common\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath>
</PropertyGroup>
</When>
</Choose>
</Project>

View File

@ -0,0 +1,51 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
This build task is run from the installer project after all projects have been compiled, and
creates the build package in the bin\Packages folder.
-->
<Target Name="AfterBuild">
<PropertyGroup>
<CompiledSmapiPath>$(SolutionDir)\..\bin\$(Configuration)\SMAPI</CompiledSmapiPath>
<PackagePath>$(SolutionDir)\..\bin\Packaged</PackagePath>
<PackageInternalPath>$(PackagePath)\internal</PackageInternalPath>
</PropertyGroup>
<ItemGroup>
<CompiledMods Include="$(SolutionDir)\..\bin\$(Configuration)\Mods\**\*.*" />
</ItemGroup>
<!-- reset package directory -->
<RemoveDir Directories="$(PackagePath)" />
<!-- copy installer files -->
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(PackagePath)\install on Windows.exe" />
<Copy SourceFiles="$(TargetDir)\readme.txt" DestinationFiles="$(PackagePath)\README.txt" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(TargetDir)\unix-launcher.sh" DestinationFiles="$(PackageInternalPath)\Mono\StardewModdingAPI" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(TargetDir)\unix-install.sh" DestinationFiles="$(PackagePath)\install.sh" />
<!-- copy SMAPI files for Mono -->
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(PackageInternalPath)\Mono\install.exe" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Mono.Cecil.dll" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Newtonsoft.Json.dll" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.pdb" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.xml" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.config.json" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.metadata.json" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\System.Numerics.dll" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\System.Runtime.Caching.dll" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\steam_appid.txt" DestinationFolder="$(PackageInternalPath)\Mono" />
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="@(CompiledMods)" DestinationFolder="$(PackageInternalPath)\Mono\Mods\%(RecursiveDir)" />
<!-- copy SMAPI files for Windows -->
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Mono.Cecil.dll" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Newtonsoft.Json.dll" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.pdb" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.xml" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.config.json" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.metadata.json" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\steam_appid.txt" DestinationFolder="$(PackageInternalPath)\Windows" />
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="@(CompiledMods)" DestinationFolder="$(PackageInternalPath)\Windows\Mods\%(RecursiveDir)" />
</Target>
</Project>

View File

@ -0,0 +1,20 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
This build task is run from the ModBuildConfig project after it's been compiled, and copies the
package files to the bin\Pathoschild.Stardew.ModBuildConfig folder.
-->
<Target Name="AfterBuild">
<PropertyGroup>
<PackagePath>$(SolutionDir)\..\bin\Pathoschild.Stardew.ModBuildConfig</PackagePath>
</PropertyGroup>
<RemoveDir Directories="$(PackagePath)" />
<Copy SourceFiles="$(ProjectDir)/package.nuspec" DestinationFolder="$(PackagePath)" />
<Copy SourceFiles="$(ProjectDir)/build/smapi.targets" DestinationFiles="$(PackagePath)/build/Pathoschild.Stardew.ModBuildConfig.targets" />
<Copy SourceFiles="$(TargetDir)/StardewModdingAPI.ModBuildConfig.dll" DestinationFiles="$(PackagePath)/build/StardewModdingAPI.ModBuildConfig.dll" />
<Copy SourceFiles="$(SolutionDir)/SMAPI.ModBuildConfig.Analyzer/bin/netstandard1.3/StardewModdingAPI.ModBuildConfig.Analyzer.dll" DestinationFiles="$(PackagePath)/analyzers/dotnet/cs/StardewModdingAPI.ModBuildConfig.Analyzer.dll" />
<Copy SourceFiles="$(SolutionDir)/SMAPI.ModBuildConfig.Analyzer/tools/install.ps1" DestinationFiles="$(PackagePath)/tools/install.ps1" />
<Copy SourceFiles="$(SolutionDir)/SMAPI.ModBuildConfig.Analyzer/tools/uninstall.ps1" DestinationFiles="$(PackagePath)/tools/uninstall.ps1" />
</Target>
</Project>

View File

@ -1,212 +0,0 @@
#!/usr/bin/env bash
#
#
# This is the Bash equivalent of ../windows/prepare-install-package.ps1.
# When making changes, both scripts should be updated.
#
#
##########
## Fetch values
##########
# paths
gamePath="/home/pathoschild/Stardew Valley"
bundleModNames=("ConsoleCommands" "ErrorHandler" "SaveBackup")
# build configuration
buildConfig="Release"
folders=("linux" "macOS" "windows")
declare -A runtimes=(["linux"]="linux-x64" ["macOS"]="osx-x64" ["windows"]="win-x64")
declare -A msBuildPlatformNames=(["linux"]="Unix" ["macOS"]="OSX" ["windows"]="Windows_NT")
# version number
version="$1"
if [ $# -eq 0 ]; then
echo "SMAPI release version (like '4.0.0'):"
read version
fi
##########
## Move to SMAPI root
##########
cd "`dirname "$0"`/../.."
##########
## Clear old build files
##########
echo "Clearing old builds..."
echo "-------------------------------------------------"
for path in bin */**/bin */**/obj; do
echo "$path"
rm -rf $path
done
echo ""
##########
## Compile files
##########
. ${0%/*}/set-smapi-version.sh "$version"
for folder in ${folders[@]}; do
runtime=${runtimes[$folder]}
msbuildPlatformName=${msBuildPlatformNames[$folder]}
echo "Compiling SMAPI for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true
echo ""
echo ""
echo "Compiling installer for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Installer --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" -p:PublishTrimmed=True -p:TrimMode=Link --self-contained true
echo ""
echo ""
for modName in ${bundleModNames[@]}; do
echo "Compiling $modName for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false"
echo ""
echo ""
done
done
##########
## Prepare install package
##########
echo "Preparing install package..."
echo "-------------------------------------------------"
# init paths
installAssets="src/SMAPI.Installer/assets"
packagePath="bin/SMAPI installer"
packageDevPath="bin/SMAPI installer for developers"
# init structure
for folder in ${folders[@]}; do
mkdir "$packagePath/internal/$folder/bundle/smapi-internal" --parents
done
# copy base installer files
for name in "install on Linux.sh" "install on macOS.command" "install on Windows.bat" "README.txt"; do
cp "$installAssets/$name" "$packagePath"
done
# copy per-platform files
for folder in ${folders[@]}; do
runtime=${runtimes[$folder]}
# get paths
smapiBin="src/SMAPI/bin/$buildConfig/$runtime/publish"
internalPath="$packagePath/internal/$folder"
bundlePath="$internalPath/bundle"
# installer files
cp -r "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish"/* "$internalPath"
rm -rf "$internalPath/assets"
# runtime config for SMAPI
# This is identical to the one generated by the build, except that the min runtime version is
# set to 5.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of
# minor.
cp "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json"
# installer DLL config
if [ $folder == "windows" ]; then
cp "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config"
fi
# bundle root files
for name in "StardewModdingAPI" "StardewModdingAPI.dll" "StardewModdingAPI.xml" "steam_appid.txt"; do
if [ $name == "StardewModdingAPI" ] && [ $folder == "windows" ]; then
name="$name.exe"
fi
cp "$smapiBin/$name" "$bundlePath"
done
# bundle i18n
cp -r "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal
for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Pathoschild.Http.Client.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.xml" "System.Net.Http.Formatting.dll"; do
cp "$smapiBin/$name" "$bundlePath/smapi-internal"
done
cp "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json"
cp "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json"
if [ $folder == "linux" ] || [ $folder == "macOS" ]; then
cp "$installAssets/unix-launcher.sh" "$bundlePath"
else
cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
fi
# copy .NET dependencies
if [ $folder == "windows" ]; then
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
fi
# copy legacy .NET dependencies (remove in SMAPI 4.0.0)
cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal"
# copy bundled mods
for modName in ${bundleModNames[@]}; do
fromPath="src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
targetPath="$bundlePath/Mods/$modName"
mkdir "$targetPath" --parents
cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath"
if [ -d "$fromPath/i18n" ]; then
cp -r "$fromPath/i18n" "$targetPath"
fi
done
done
# mark scripts executable
for path in "install on Linux.sh" "install on macOS.command" "bundle/unix-launcher.sh"; do
if [ -f "$packagePath/$path" ]; then
chmod 755 "$packagePath/$path"
fi
done
# split into main + for-dev folders
cp -r "$packagePath" "$packageDevPath"
for folder in ${folders[@]}; do
# disable developer mode in main package
sed --in-place --expression="s/\"DeveloperMode\": true/\"DeveloperMode\": false/" "$packagePath/internal/$folder/bundle/smapi-internal/config.json"
# convert bundle folder into final 'install.dat' files
for path in "$packagePath/internal/$folder" "$packageDevPath/internal/$folder"; do
pushd "$path/bundle" > /dev/null
zip "install.dat" * --recurse-paths --quiet
popd > /dev/null
mv "$path/bundle/install.dat" "$path/install.dat"
rm -rf "$path/bundle"
done
done
##########
## Create release zips
##########
# rename folders
mv "$packagePath" "bin/SMAPI $version installer"
mv "$packageDevPath" "bin/SMAPI $version installer for developers"
# package files
pushd bin > /dev/null
zip -9 "SMAPI $version installer.zip" "SMAPI $version installer" --recurse-paths --quiet
zip -9 "SMAPI $version installer for developers.zip" "SMAPI $version installer for developers" --recurse-paths --quiet
popd > /dev/null
echo ""
echo "Done! Package created in $(pwd)/bin"

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
#
#
# This is the Bash equivalent of ../windows/set-smapi-version.ps1.
# When making changes, both scripts should be updated.
#
#
# get version number
version="$1"
if [ $# -eq 0 ]; then
echo "SMAPI release version (like '4.0.0'):"
read version
fi
# move to SMAPI root
cd "`dirname "$0"`/../.."
# apply changes
sed "s/<Version>.+<\/Version>/<Version>$version<\/Version>/" "build/common.targets" --in-place --regexp-extended
sed "s/RawApiVersion = \".+?\";/RawApiVersion = \"$version\";/" "src/SMAPI/Constants.cs" --in-place --regexp-extended
for modName in "ConsoleCommands" "ErrorHandler" "SaveBackup"; do
sed "s/\"(Version|MinimumApiVersion)\": \".+?\"/\"\1\": \"$version\"/g" "src/SMAPI.Mods.$modName/manifest.json" --in-place --regexp-extended
done

View File

@ -1,67 +0,0 @@
#!/usr/bin/env bash
##########
## Read config
##########
# get SMAPI version
version="$1"
if [ $# -eq 0 ]; then
echo "SMAPI release version (like '4.0.0'):"
read version
fi
# get Windows bin path
windowsBinPath="$2"
if [ $# -le 1 ]; then
echo "Windows compiled bin path:"
read windowsBinPath
fi
# installer internal folders
buildFolders=("linux" "macOS" "windows")
##########
## Finalize release package
##########
for folderName in "SMAPI $version installer" "SMAPI $version installer for developers"; do
# move files to Linux filesystem
echo "Preparing $folderName.zip..."
echo "-------------------------------------------------"
echo "copying '$windowsBinPath/$folderName' to Linux filesystem..."
cp -r "$windowsBinPath/$folderName" .
# fix permissions
echo "fixing permissions..."
find "$folderName" -type d -exec chmod 755 {} \;
find "$folderName" -type f -exec chmod 644 {} \;
find "$folderName" -name "*.sh" -exec chmod 755 {} \;
find "$folderName" -name "*.command" -exec chmod 755 {} \;
find "$folderName" -name "SMAPI.Installer" -exec chmod 755 {} \;
find "$folderName" -name "StardewModdingAPI" -exec chmod 755 {} \;
# convert bundle folder into final 'install.dat' files
for build in ${buildFolders[@]}; do
echo "packaging $folderName/internal/$build/install.dat..."
pushd "$folderName/internal/$build/bundle" > /dev/null
zip "install.dat" * --recurse-paths --quiet
mv install.dat ../
popd > /dev/null
rm -rf "$folderName/internal/$build/bundle"
done
# zip installer
echo "packaging installer..."
zip -9 "$folderName.zip" "$folderName" --recurse-paths --quiet
# move zip back to Windows bin path
echo "moving release zip to $windowsBinPath/$folderName.zip..."
mv "$folderName.zip" "$windowsBinPath"
rm -rf "$folderName"
echo ""
echo ""
done
echo "Done!"

View File

@ -1,11 +0,0 @@
function In-Place-Regex {
param (
[Parameter(Mandatory)][string]$Path,
[Parameter(Mandatory)][string]$Search,
[Parameter(Mandatory)][string]$Replace
)
$content = (Get-Content "$Path" -Encoding UTF8)
$content = ($content -replace "$Search", "$Replace")
[System.IO.File]::WriteAllLines((Get-Item "$Path").FullName, $content)
}

View File

@ -1,241 +0,0 @@
#
#
# This is the PowerShell equivalent of ../unix/prepare-install-package.sh, *except* that it doesn't
# set Linux permissions, create the install.dat files, or create the final zip (unless you specify
# --windows-only). Due to limitations in PowerShell, the final changes are handled by the
# windows/finalize-install-package.sh file in WSL.
#
# When making changes, make sure to update ../unix/prepare-install-package.ps1 too.
#
#
. "$PSScriptRoot/lib/in-place-regex.ps1"
##########
## Fetch values
##########
# paths
$gamePath = "C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley"
$bundleModNames = "ConsoleCommands", "ErrorHandler", "SaveBackup"
# build configuration
$buildConfig = "Release"
$folders = "linux", "macOS", "windows"
$runtimes = @{ linux = "linux-x64"; macOS = "osx-x64"; windows = "win-x64" }
$msBuildPlatformNames = @{ linux = "Unix"; macOS = "OSX"; windows = "Windows_NT" }
# version number
$version = $args[0]
if (!$version) {
$version = Read-Host "SMAPI release version (like '4.0.0')"
}
# Windows-only build
$windowsOnly = $false
foreach ($arg in $args) {
if ($arg -eq "--windows-only") {
$windowsOnly = $true
$folders = "windows"
$runtimes = @{ windows = "win-x64" }
$msBuildPlatformNames = @{ windows = "Windows_NT" }
}
}
##########
## Move to SMAPI root
##########
cd "$PSScriptRoot/../.."
##########
## Clear old build files
##########
echo "Clearing old builds..."
echo "-------------------------------------------------"
foreach ($path in (dir -Recurse -Include ('bin', 'obj'))) {
echo "$path"
rm -Recurse -Force "$path"
}
echo ""
##########
## Compile files
##########
. "$PSScriptRoot/set-smapi-version.ps1" "$version"
foreach ($folder in $folders) {
$runtime = $runtimes[$folder]
$msbuildPlatformName = $msBuildPlatformNames[$folder]
echo "Compiling SMAPI for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true
echo ""
echo ""
echo "Compiling installer for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Installer --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" -p:PublishTrimmed=True -p:TrimMode=Link --self-contained true
echo ""
echo ""
foreach ($modName in $bundleModNames) {
echo "Compiling $modName for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false"
echo ""
echo ""
}
}
##########
## Prepare install package
##########
echo "Preparing install package..."
echo "----------------------------"
# init paths
$installAssets = "src/SMAPI.Installer/assets"
$packagePath = "bin/SMAPI installer"
$packageDevPath = "bin/SMAPI installer for developers"
# init structure
foreach ($folder in $folders) {
mkdir "$packagePath/internal/$folder/bundle/smapi-internal" > $null
}
# copy base installer files
foreach ($name in @("install on Linux.sh", "install on macOS.command", "install on Windows.bat", "README.txt")) {
if ($windowsOnly -and ($name -eq "install on Linux.sh" -or $name -eq "install on macOS.command")) {
continue;
}
cp "$installAssets/$name" "$packagePath"
}
# copy per-platform files
foreach ($folder in $folders) {
$runtime = $runtimes[$folder]
# get paths
$smapiBin = "src/SMAPI/bin/$buildConfig/$runtime/publish"
$internalPath = "$packagePath/internal/$folder"
$bundlePath = "$internalPath/bundle"
# installer files
cp "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish/*" "$internalPath" -Recurse
rm -Recurse -Force "$internalPath/assets"
# runtime config for SMAPI
# This is identical to the one generated by the build, except that the min runtime version is
# set to 5.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of
# minor.
cp "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json"
# installer DLL config
if ($folder -eq "windows") {
cp "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config"
}
# bundle root files
foreach ($name in @("StardewModdingAPI", "StardewModdingAPI.dll", "StardewModdingAPI.xml", "steam_appid.txt")) {
if ($name -eq "StardewModdingAPI" -and $folder -eq "windows") {
$name = "$name.exe"
}
cp "$smapiBin/$name" "$bundlePath"
}
# bundle i18n
cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal
foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Pathoschild.Http.Client.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.xml", "System.Net.Http.Formatting.dll")) {
cp "$smapiBin/$name" "$bundlePath/smapi-internal"
}
if ($folder -eq "windows") {
cp "$smapiBin/VdfConverter.dll" "$bundlePath/smapi-internal"
}
cp "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json"
cp "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json"
if ($folder -eq "linux" -or $folder -eq "macOS") {
cp "$installAssets/unix-launcher.sh" "$bundlePath"
}
else {
cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
}
# copy .NET dependencies
if ($folder -eq "windows") {
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
}
# copy legacy .NET dependencies (remove in SMAPI 4.0.0)
cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal"
# copy bundled mods
foreach ($modName in $bundleModNames) {
$fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
$targetPath = "$bundlePath/Mods/$modName"
mkdir "$targetPath" > $null
cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath"
if (Test-Path "$fromPath/i18n" -PathType Container) {
cp -Recurse "$fromPath/i18n" "$targetPath"
}
}
}
# DISABLED: will be handled by Linux script
# mark scripts executable
#ForEach ($path in @("install on Linux.sh", "install on macOS.command", "bundle/unix-launcher.sh")) {
# if (Test-Path "$packagePath/$path" -PathType Leaf) {
# chmod 755 "$packagePath/$path"
# }
#}
# split into main + for-dev folders
cp -Recurse "$packagePath" "$packageDevPath"
foreach ($folder in $folders) {
# disable developer mode in main package
In-Place-Regex -Path "$packagePath/internal/$folder/bundle/smapi-internal/config.json" -Search "`"DeveloperMode`": true" -Replace "`"DeveloperMode`": false"
# convert bundle folder into final 'install.dat' files
if ($windowsOnly)
{
foreach ($path in @("$packagePath/internal/$folder", "$packageDevPath/internal/$folder"))
{
Compress-Archive -Path "$path/bundle/*" -CompressionLevel Optimal -DestinationPath "$path/install.zip"
mv "$path/install.zip" "$path/install.dat"
rm -Recurse -Force "$path/bundle"
}
}
}
###########
### Create release zips
###########
# rename folders
mv "$packagePath" "bin/SMAPI $version installer"
mv "$packageDevPath" "bin/SMAPI $version installer for developers"
# package files
if ($windowsOnly)
{
Compress-Archive -Path "bin/SMAPI $version installer" -DestinationPath "bin/SMAPI $version installer.zip" -CompressionLevel Optimal
Compress-Archive -Path "bin/SMAPI $version installer for developers" -DestinationPath "bin/SMAPI $version installer for developers.zip" -CompressionLevel Optimal
}
echo ""
echo "Done! See docs/technical/smapi.md to create the release zips."

View File

@ -1,25 +0,0 @@
#
#
# This is the PowerShell equivalent of ../unix/set-smapi-version.sh.
# When making changes, both scripts should be updated.
#
#
. "$PSScriptRoot\lib\in-place-regex.ps1"
# get version number
$version=$args[0]
if (!$version) {
$version = Read-Host "SMAPI release version (like '4.0.0')"
}
# move to SMAPI root
cd "$PSScriptRoot/../.."
# apply changes
In-Place-Regex -Path "build/common.targets" -Search "<Version>.+</Version>" -Replace "<Version>$version</Version>"
In-Place-Regex -Path "src/SMAPI/Constants.cs" -Search "RawApiVersion = `".+?`";" -Replace "RawApiVersion = `"$version`";"
ForEach ($modName in "ConsoleCommands","ErrorHandler","SaveBackup") {
In-Place-Regex -Path "src/SMAPI.Mods.$modName/manifest.json" -Search "`"(Version|MinimumApiVersion)`": `".+?`"" -Replace "`"`$1`": `"$version`""
}

19
docs/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,19 @@
Do you want to...
* **Ask for help using SMAPI?**
Please post a message in the [SMAPI support thread](http://community.playstarbound.com/threads/108375)
or [ask on Discord](https://stardewvalleywiki.com/Modding:Community#Discord), don't create a
GitHub issue.
* **Report a bug?**
Please post a message in the [SMAPI support thread](http://community.playstarbound.com/threads/108375)
or [ask on Discord](https://stardewvalleywiki.com/Modding:Community#Discord) instead, unless
you're sure it's a bug in SMAPI itself.
* **Submit a pull request?**
Pull requests are welcome! If you're submitting a new feature, it's best to discuss first to make
sure it'll be accepted. Feel free to come chat in [#modding on Discord](https://stardewvalleywiki.com/Modding:Community#Discord)
or post in the [SMAPI support thread](http://community.playstarbound.com/threads/108375).
Documenting your code and using the same formatting conventions is appreciated, but don't worry too
much about it. We'll fix up the code after we accept the pull request if needed.

View File

@ -1,6 +1,6 @@
**SMAPI** is an open-source modding framework and API for [Stardew Valley](https://stardewvalley.net/)
that lets you play the game with mods. It's safely installed alongside the game's executable, and
doesn't change any of your game files. It serves seven main purposes:
**SMAPI** is an open-source modding API for [Stardew Valley](http://stardewvalley.net/) that lets
you play the game with mods. It's safely installed alongside the game's executable, and doesn't
change any of your game files. It serves six main purposes:
1. **Load mods into the game.**
_SMAPI loads mods when the game is starting up so they can interact with it. (Code mods aren't
@ -10,70 +10,36 @@ doesn't change any of your game files. It serves seven main purposes:
_SMAPI provides APIs and events which let mods interact with the game in ways they otherwise
couldn't._
3. **Rewrite mods for compatibility.**
_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/macOS and Windows versions of
the game. In some cases it also rewrites code broken by a game update so the mod doesn't break._
3. **Rewrite mods for crossplatform compatibility.**
_SMAPI rewrites mods' compiled code before loading them so they work on Linux/Mac/Windows
without the mods needing to handle differences between the Linux/Mac and Windows versions of the
game._
5. **Intercept errors and automatically fix saves.**
_SMAPI intercepts errors, shows the error info in the SMAPI console, and in most cases
automatically recovers the game. That prevents mods from crashing the game, and makes it
possible to troubleshoot errors in the game itself that would otherwise show a generic 'program
has stopped working' type of message._
4. **Rewrite mods to update them.**
_SMAPI detects when a mod accesses part of the game that changed in a game update which affects
many mods, and rewrites the mod so it's compatible._
_SMAPI also automatically fixes save data in some cases when a load would crash, e.g. due to a
custom location or NPC mod that was removed._
5. **Intercept errors.**
_SMAPI intercepts errors that happen in the game, displays the error details in the console
window, and in most cases automatically recovers the game. This prevents mods from accidentally
crashing the game, and makes it possible to troubleshoot errors in the game itself that would
otherwise show a generic 'program has stopped working' type of message._
6. **Provide update checks.**
6. **Provide update checks.**
_SMAPI automatically checks for new versions of your installed mods, and notifies you when any
are available._
7. **Provide compatibility checks.**
_SMAPI automatically detects outdated or broken code in mods, and safely disables them before
they cause problems._
8. **Back up your save files.**
_SMAPI automatically creates a daily backup of your saves and keeps ten backups (via the bundled
Save Backup mod), in case something goes wrong._
## Documentation
Have questions? Come [ask the community](https://smapi.io/community) to get help from SMAPI
developers and other modders!
Have questions? Come [chat on Discord](https://discord.gg/KCJHWhX) with SMAPI developers and other
modders!
### For players
* [Player guide](https://stardewvalleywiki.com/Modding:Player_Guide)
### For modders
* [Modding documentation](https://smapi.io/docs)
* [Mod build configuration](technical/mod-package.md)
* [Modding documentation](https://stardewvalleywiki.com/Modding:Index)
* [Mod build configuration](mod-build-config.md)
* [Release notes](release-notes.md)
### For SMAPI developers
* [Technical docs](technical/smapi.md)
## Translating SMAPI
SMAPI rarely shows text in-game, so it only has a few translations. Contributions are welcome! See
[Modding:Translations](https://stardewvalleywiki.com/Modding:Translations) on the wiki for help
contributing translations.
locale | status
----------- | :----------------
default | ✓ [fully translated](../src/SMAPI/i18n/default.json)
Chinese | ✓ [fully translated](../src/SMAPI/i18n/zh.json)
French | ✓ [fully translated](../src/SMAPI/i18n/fr.json)
German | ✓ [fully translated](../src/SMAPI/i18n/de.json)
Hungarian | ✓ [fully translated](../src/SMAPI/i18n/hu.json)
Italian | ✓ [fully translated](../src/SMAPI/i18n/it.json)
Japanese | ✓ [fully translated](../src/SMAPI/i18n/ja.json)
Korean | ✓ [fully translated](../src/SMAPI/i18n/ko.json)
[Polish] | ✓ [fully translated](../src/SMAPI/i18n/pl.json)
Portuguese | ✓ [fully translated](../src/SMAPI/i18n/pt.json)
Russian | ✓ [fully translated](../src/SMAPI/i18n/ru.json)
Spanish | ✓ [fully translated](../src/SMAPI/i18n/es.json)
[Thai] | ✓ [fully translated](../src/SMAPI/i18n/th.json)
Turkish | ✓ [fully translated](../src/SMAPI/i18n/tr.json)
[Ukrainian] | ✓ [fully translated](../src/SMAPI/i18n/uk.json)
[Polish]: https://www.nexusmods.com/stardewvalley/mods/3616
[Thai]: https://www.nexusmods.com/stardewvalley/mods/7052
[Ukrainian]: https://www.nexusmods.com/stardewvalley/mods/8427
* [Technical docs](technical-docs.md)

5
docs/SUPPORT.md Normal file
View File

@ -0,0 +1,5 @@
GitHub issues are only used for SMAPI development tasks.
To get help with SMAPI problems, you can...
* [ask on Discord](https://stardewvalleywiki.com/Modding:Community#Discord);
* or post in the [SMAPI support thread](https://community.playstarbound.com/threads/108375).

View File

@ -1 +1,265 @@
[Documentation moved](technical/mod-package.md).
The **mod build package** is an open-source NuGet package which automates the MSBuild configuration
for SMAPI mods.
The package...
* detects your game install path;
* adds the assembly references you need (with automatic support for Linux/Mac/Windows);
* packages the mod into your `Mods` folder when you rebuild the code (configurable);
* configures Visual Studio to enable debugging into the code when the game is running (_Windows only_);
* adds C# analyzers to warn for Stardew Valley-specific issues.
## Contents
* [Install](#install)
* [Configure](#configure)
* [Code analysis warnings](#code-analysis-warnings)
* [Troubleshoot](#troubleshoot)
* [Release notes](#release-notes)
## Install
**When creating a new mod:**
1. Create an empty library project.
2. Reference the [`Pathoschild.Stardew.ModBuildConfig` NuGet package](https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig).
3. [Write your code](https://stardewvalleywiki.com/Modding:Creating_a_SMAPI_mod).
4. Compile on any platform.
**When migrating an existing mod:**
1. Remove any project references to `Microsoft.Xna.*`, `MonoGame`, Stardew Valley,
`StardewModdingAPI`, and `xTile`.
2. Reference the [`Pathoschild.Stardew.ModBuildConfig` NuGet package](https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig).
3. Compile on any platform.
## Configure
### Deploy files into the `Mods` folder
By default, your mod will be copied into the game's `Mods` folder (with a subfolder matching your
project name) when you rebuild the code. The package will automatically include your
`manifest.json`, any `i18n` files, and the build output.
To add custom files to the mod folder, just [add them to the build output](https://stackoverflow.com/a/10828462/262123).
(If your project references another mod, make sure the reference is [_not_ marked 'copy local'](https://msdn.microsoft.com/en-us/library/t1zz5y8c(v=vs.100).aspx).)
You can change the mod's folder name by adding this above the first `</PropertyGroup>` in your
`.csproj`:
```xml
<ModFolderName>YourModName</ModFolderName>
```
If you don't want to deploy the mod automatically, you can add this:
```xml
<EnableModDeploy>False</EnableModDeploy>
```
### Create release zip
By default, a zip file will be created in the build output when you rebuild the code. This zip file
contains all the files needed to share your mod in the recommended format for uploading to Nexus
Mods or other sites.
You can change the zipped folder name (and zip name) by adding this above the first
`</PropertyGroup>` in your `.csproj`:
```xml
<ModFolderName>YourModName</ModFolderName>
```
You can change the folder path where the zip is created like this:
```xml
<ModZipPath>$(SolutionDir)\_releases</ModZipPath>
```
Finally, you can disable the zip creation with this:
```xml
<EnableModZip>False</EnableModZip>
```
Or only create it in release builds with this:
```xml
<EnableModZip Condition="$(Configuration) != 'Release'">False</EnableModZip>
```
### Game path
The package usually detects where your game is installed automatically. If it can't find your game
or you have multiple installs, you can specify the path yourself. There's two ways to do that:
* **Option 1: global game path (recommended).**
_This will apply to every project that uses the package._
1. Get the full folder path containing the Stardew Valley executable.
2. Create this file:
platform | path
--------- | ----
Linux/Mac | `~/stardewvalley.targets`
Windows | `%USERPROFILE%\stardewvalley.targets`
3. Save the file with this content:
```xml
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<GamePath>PATH_HERE</GamePath>
</PropertyGroup>
</Project>
```
4. Replace `PATH_HERE` with your game path.
* **Option 2: path in the project file.**
_You'll need to do this for each project that uses the package._
1. Get the folder path containing the Stardew Valley `.exe` file.
2. Add this to your `.csproj` file under the `<Project` line:
```xml
<PropertyGroup>
<GamePath>PATH_HERE</GamePath>
</PropertyGroup>
```
3. Replace `PATH_HERE` with your custom game install path.
The configuration will check your custom path first, then fall back to the default paths (so it'll
still compile on a different computer).
### Unit test projects
**(upcoming in 2.1)**
You can use the package in unit test projects too. Its optional unit test mode...
1. disables deploying the project as a mod;
2. disables creating a release zip;
2. and copies the referenced DLLs into the build output for unit test frameworks.
To enable it, add this above the first `</PropertyGroup>` in your `.csproj`:
```xml
<ModUnitTests>True</ModUnitTests>
```
## Code warnings
### Overview
The NuGet package adds code warnings in Visual Studio specific to Stardew Valley. For example:
![](screenshots/code-analyzer-example.png)
You can hide the warnings using the warning ID (shown under 'code' in the Error List). See...
* [for specific code](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives/preprocessor-pragma-warning);
* for a method using this attribute:
```cs
[System.Diagnostics.CodeAnalysis.SuppressMessage("SMAPI.CommonErrors", "AvoidNetField")]
```
* for an entire project:
1. Expand the _References_ node for the project in Visual Studio.
2. Right-click on _Analyzers_ and choose _Open Active Rule Set_.
4. Expand _StardewModdingAPI.ModBuildConfig.Analyzer_ and uncheck the warnings you want to hide.
See below for help with each specific warning.
### Avoid implicit net field cast
Warning text:
> This implicitly converts '{{expression}}' from {{net type}} to {{other type}}, but
> {{net type}} has unintuitive implicit conversion rules. Consider comparing against the actual
> value instead to avoid bugs.
Stardew Valley uses net types (like `NetBool` and `NetInt`) to handle multiplayer sync. These types
can implicitly convert to their equivalent normal values (like `bool x = new NetBool()`), but their
conversion rules are unintuitive and error-prone. For example,
`item?.category == null && item?.category != null` can both be true at once, and
`building.indoors != null` can be true for a null value.
Suggested fix:
* Some net fields have an equivalent non-net property like `monster.Health` (`int`) instead of
`monster.health` (`NetInt`). The package will add a separate [AvoidNetField](#avoid-net-field) warning for
these. Use the suggested property instead.
* For a reference type (i.e. one that can contain `null`), you can use the `.Value` property:
```c#
if (building.indoors.Value == null)
```
Or convert the value before comparison:
```c#
GameLocation indoors = building.indoors;
if(indoors == null)
// ...
```
* For a value type (i.e. one that can't contain `null`), check if the object is null (if applicable)
and compare with `.Value`:
```cs
if (item != null && item.category.Value == 0)
```
### Avoid net field
Warning text:
> '{{expression}}' is a {{net type}} field; consider using the {{property name}} property instead.
Your code accesses a net field, which has some unusual behavior (see [AvoidImplicitNetFieldCast](#avoid-implicit-net-field-cast)).
This field has an equivalent non-net property that avoids those issues.
Suggested fix: access the suggested property name instead.
### Avoid obsolete field
Warning text:
> The '{{old field}}' field is obsolete and should be replaced with '{{new field}}'.
Your code accesses a field which is obsolete or no longer works. Use the suggested field instead.
## Troubleshoot
### "Failed to find the game install path"
That error means the package couldn't find your game. You can specify the game path yourself; see
_[Game path](#game-path)_ above.
## Release notes
### 2.1 alpha
* Added support for Stardew Valley 1.3.
* Added support for unit test projects.
* Added C# analyzers to warn about implicit conversions of Netcode fields in Stardew Valley 1.3.
### 2.0.2
* Fixed compatibility issue on Linux.
### 2.0.1
* Fixed mod deploy failing to create subfolders if they don't already exist.
### 2.0
* Added: mods are now copied into the `Mods` folder automatically (configurable).
* Added: release zips are now created automatically in your build output folder (configurable).
* Added: mod deploy and release zips now exclude Json.NET automatically, since it's provided by SMAPI.
* Added mod's version to release zip filename.
* Improved errors to simplify troubleshooting.
* Fixed release zip not having a mod folder.
* Fixed release zip failing if mod name contains characters that aren't valid in a filename.
### 1.7.1
* Fixed issue where i18n folders were flattened.
* The manifest/i18n files in the project now take precedence over those in the build output if both
are present.
### 1.7
* Added option to create release zips on build.
* Added reference to XNA's XACT library for audio-related mods.
### 1.6
* Added support for deploying mod files into `Mods` automatically.
* Added a build error if a game folder is found, but doesn't contain Stardew Valley or SMAPI.
### 1.5
* Added support for setting a custom game path globally.
* Added default GOG path on Mac.
### 1.4
* Fixed detection of non-default game paths on 32-bit Windows.
* Removed support for SilVerPLuM (discontinued).
* Removed support for overriding the target platform (no longer needed since SMAPI crossplatforms
mods automatically).
### 1.3
* Added support for non-default game paths on Windows.
### 1.2
* Exclude game binaries from mod build output.
### 1.1
* Added support for overriding the target platform.
### 1.0
* Initial release.
* Added support for detecting the game path automatically.
* Added support for injecting XNA/MonoGame references automatically based on the OS.
* Added support for mod builders like SilVerPLuM.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

255
docs/technical-docs.md Normal file
View File

@ -0,0 +1,255 @@
&larr; [README](README.md)
This file provides more technical documentation about SMAPI. If you only want to use or create
mods, this section isn't relevant to you; see the main README to use or create mods.
# Contents
* [SMAPI](#smapi)
* [Development](#development)
* [Compiling from source](#compiling-from-source)
* [Debugging a local build](#debugging-a-local-build)
* [Preparing a release](#preparing-a-release)
* [Customisation](#customisation)
* [Configuration file](#configuration-file)
* [Command-line arguments](#command-line-arguments)
* [Compile flags](#compile-flags)
* [SMAPI web services](#smapi-web-services)
* [Overview](#overview)
* [Log parser](#log-parser)
* [Mods API](#mods-api)
* [Development](#development-2)
* [Local development](#local-development)
* [Deploying to Amazon Beanstalk](#deploying-to-amazon-beanstalk)
* [Mod build config package](#mod-build-config-package)
# SMAPI
## Development
### Compiling from source
Using an official SMAPI release is recommended for most users.
SMAPI uses some C# 7 code, so you'll need at least
[Visual Studio 2017](https://www.visualstudio.com/vs/community/) on Windows,
[MonoDevelop 7.0](http://www.monodevelop.com/) on Linux,
[Visual Studio 2017 for Mac](https://www.visualstudio.com/vs/visual-studio-mac/), or an equivalent
IDE to compile it. It uses build configuration derived from the
[crossplatform mod config](https://github.com/Pathoschild/Stardew.ModBuildConfig#readme) to detect
your current OS automatically and load the correct references. Compile output will be placed in a
`bin` folder at the root of the git repository.
### Debugging a local build
Rebuilding the solution in debug mode will copy the SMAPI files into your game folder. Starting
the `StardewModdingAPI` project with debugging from Visual Studio (on Mac or Windows) will launch
SMAPI with the debugger attached, so you can intercept errors and step through the code being
executed. This doesn't work in MonoDevelop on Linux, unfortunately.
### Preparing a release
To prepare a crossplatform SMAPI release, you'll need to compile it on two platforms. See
[crossplatforming info](https://stardewvalleywiki.com/Modding:Creating_a_SMAPI_mod#Test_on_all_platforms)
on the wiki for the first-time setup.
1. Update the version number in `GlobalAssemblyInfo.cs` and `Constants::Version`. Make sure you use a
[semantic version](http://semver.org). Recommended format:
build type | format | example
:--------- | :-------------------------------- | :------
dev build | `<version>-alpha.<timestamp>` | `2.0-alpha.20171230`
prerelease | `<version>-prerelease.<ID>` | `2.0-prerelease.2`
release | `<version>` | `2.0`
2. In Windows:
1. Rebuild the solution in _Release_ mode.
2. Rename `bin/Packaged` to `SMAPI <version>` (e.g. `SMAPI 2.0`).
2. Transfer the `SMAPI <version>` folder to Linux or Mac.
_This adds the installer executable and Windows files. We'll do the rest in Linux or Mac,
since we need to set Unix file permissions that Windows won't save._
2. In Linux or Mac:
1. Rebuild the solution in _Release_ mode.
2. Copy `bin/internal/Packaged/Mono` into the `SMAPI <version>` folder.
3. If you did everything right so far, you should have a folder like this:
```
SMAPI-2.x/
install.exe
readme.txt
internal/
Mono/
Mods/*
Mono.Cecil.dll
Newtonsoft.Json.dll
StardewModdingAPI
StardewModdingAPI.config.json
StardewModdingAPI.Internal.dll
StardewModdingAPI.metadata.json
StardewModdingAPI.exe
StardewModdingAPI.pdb
StardewModdingAPI.xml
steam_appid.txt
System.Numerics.dll
System.Runtime.Caching.dll
System.ValueTuple.dll
Windows/
Mods/*
Mono.Cecil.dll
Newtonsoft.Json.dll
StardewModdingAPI.config.json
StardewModdingAPI.Internal.dll
StardewModdingAPI.metadata.json
StardewModdingAPI.exe
StardewModdingAPI.pdb
StardewModdingAPI.xml
System.ValueTuple.dll
steam_appid.txt
```
4. Open a terminal in the `SMAPI <version>` folder and run `chmod 755 internal/Mono/StardewModdingAPI`.
5. Copy & paste the `SMAPI <version>` folder as `SMAPI <version> for developers`.
6. In the `SMAPI <version>` folder...
* edit `internal/Mono/StardewModdingAPI.config.json` and
`internal/Windows/StardewModdingAPI.config.json` to disable developer mode;
* delete `internal/Windows/StardewModdingAPI.xml`.
7. Compress the two folders into `SMAPI <version>.zip` and `SMAPI <version> for developers.zip`.
## Customisation
### Configuration file
You can customise the SMAPI behaviour by editing the `StardewModdingAPI.config.json` file in your
game folder.
Basic fields:
field | purpose
----------------- | -------
`DeveloperMode` | Default `false` (except in _SMAPI for developers_ releases). Whether to enable features intended for mod developers (mainly more detailed console logging).
`CheckForUpdates` | Default `true`. Whether SMAPI should check for a newer version when you load the game. If a new version is available, a small message will appear in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.
`VerboseLogging` | Default `false`. Whether SMAPI should log more information about the game context.
`ModData` | Internal metadata about SMAPI mods. Changing this isn't recommended and may destabilise your game. See documentation in the file.
### Command-line arguments
The SMAPI installer recognises three command-line arguments:
argument | purpose
-------- | -------
`--install` | Preselects the install action, skipping the prompt asking what the user wants to do.
`--uninstall` | Preselects the uninstall action, skipping the prompt asking what the user wants to do.
`--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, but these are intended for internal use or testing and may
change without warning.
argument | purpose
-------- | -------
`--log-path "path"` | The relative or absolute path of the log file SMAPI should write.
`--no-terminal` | SMAPI won't write anything to the console window. (Messages will still be written to the log file.)
### Compile flags
SMAPI uses a small number of conditional compilation constants, which you can set by editing the
`<DefineConstants>` element in `StardewModdingAPI.csproj`. Supported constants:
flag | purpose
---- | -------
`SMAPI_FOR_WINDOWS` | Indicates that SMAPI is being compiled on Windows for players on Windows. Set automatically in `crossplatform.targets`.
# SMAPI web services
## Overview
The `StardewModdingAPI.Web` project provides an API and web UI hosted at `*.smapi.io`.
### Log parser
The log parser provides a web UI for uploading, parsing, and sharing SMAPI logs. The logs are
persisted in a compressed form to Pastebin.
The log parser lives at https://log.smapi.io.
### Mods API
The mods API provides version info for mods hosted by Chucklefish, GitHub, or Nexus Mods. It's used
by SMAPI to perform update checks. The `{version}` URL token is the version of SMAPI making the
request; it doesn't do anything currently, but lets us version breaking changes if needed.
Each mod is identified by a repository key and unique identifier (like `nexus:541`). The following
repositories are supported:
key | repository
------------- | ----------
`chucklefish` | A mod page on the [Chucklefish mod site](https://community.playstarbound.com/resources/categories/22), identified by the mod ID in the page URL.
`github` | A repository on [GitHub](https://github.com), identified by its owner and repository name (like `Zoryn4163/SMAPI-Mods`). This checks the version of the latest repository release.
`nexus` | A mod page on [Nexus Mods](https://www.nexusmods.com/stardewvalley), identified by the mod ID in the page URL.
The API accepts either `GET` or `POST` for convenience:
> ```
>GET https://api.smapi.io/v2.0/mods?modKeys=nexus:541,chucklefish:4228
>```
>```
>POST https://api.smapi.io/v2.0/mods
>{
> "ModKeys": [ "nexus:541", "chucklefish:4228" ]
>}
>```
It returns a response like this:
>```
>{
> "chucklefish:4228": {
> "name": "Entoarox Framework",
> "version": "1.8.0",
> "url": "https://community.playstarbound.com/resources/4228"
> },
> "nexus:541": {
> "name": "Lookup Anything",
> "version": "1.16",
> "url": "http://www.nexusmods.com/stardewvalley/mods/541"
> }
>}
>```
## Development
### Local development
`StardewModdingAPI.Web` is a regular ASP.NET MVC Core app, so you can just launch it from within
Visual Studio to run a local version.
There are two differences when it's run locally: all endpoints use HTTP instead of HTTPS, and the
subdomain portion becomes a route (e.g. `log.smapi.io` &rarr; `localhost:59482/log`).
Before running it locally, you need to enter your credentials in the `appsettings.Development.json`
file. See the next section for a description of each setting. This file is listed in `.gitignore`
to prevent accidentally committing credentials.
### Deploying to Amazon Beanstalk
The app can be deployed to a standard Amazon Beanstalk IIS environment. When creating the
environment, make sure to specify the following environment properties:
property name | description
------------------------------- | -----------------
`LogParser:PastebinDevKey` | The [Pastebin developer key](https://pastebin.com/api#1) used to authenticate with the Pastebin API.
`LogParser:PastebinUserKey` | The [Pastebin user key](https://pastebin.com/api#8) used to authenticate with the Pastebin API. Can be left blank to post anonymously.
`LogParser:SectionUrl` | The root URL of the log page, like `https://log.smapi.io/`.
`ModUpdateCheck:GitHubPassword` | The password with which to authenticate to GitHub when fetching release info.
`ModUpdateCheck:GitHubUsername` | The username with which to authenticate to GitHub when fetching release info.
## Mod build config package
### Overview
The mod build config package is a NuGet package that mods reference to automatically set up
references, configure the build, and add analyzers specific to Stardew Valley mods.
This involves three projects:
project | purpose
------------------------------------------------- | ----------------
`StardewModdingAPI.ModBuildConfig` | Configures the build (references, deploying the mod files, setting up debugging, etc).
`StardewModdingAPI.ModBuildConfig.Analyzer` | Adds C# analyzers which show code warnings in Visual Studio.
`StardewModdingAPI.ModBuildConfig.Analyzer.Tests` | Unit tests for the C# analyzers.
When the projects are built, the relevant files are copied into `bin/Pathoschild.Stardew.ModBuildConfig`.
### Preparing a build
To prepare a build of the NuGet package:
1. Install the [NuGet CLI](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools#nugetexe-cli).
1. Change the version and release notes in `package.nuspec`.
2. Rebuild the solution in _Release_ mode.
3. Open a terminal in the `bin/Pathoschild.Stardew.ModBuildConfig` package and run this command:
```bash
nuget.exe pack
```
That will create a `Pathoschild.Stardew.ModBuildConfig-<version>.nupkg` file in the same directory
which can be uploaded to NuGet or referenced directly.

View File

@ -1,618 +0,0 @@
&larr; [SMAPI](../README.md)
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, macOS, and Windows.
## Contents
* [Use](#use)
* [Features](#features)
* [Configure](#configure)
* [Code warnings](#code-warnings)
* [FAQs](#faqs)
* [How do I set the game path?](#custom-game-path)
* [How do I change which files are included in the mod deploy/zip?](#how-do-i-change-which-files-are-included-in-the-mod-deployzip)
* [Can I use the package for non-mod projects?](#can-i-use-the-package-for-non-mod-projects)
* [For SMAPI developers](#for-smapi-developers)
* [Release notes](#release-notes)
## Use
1. Create an empty library project.
2. Reference the [`Pathoschild.Stardew.ModBuildConfig` NuGet package](https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig).
3. [Write your code](https://stardewvalleywiki.com/Modding:Creating_a_SMAPI_mod).
4. Compile on any platform.
5. Run the game to play with your mod.
## Features
The package includes several features to simplify mod development (see [_configure_](#configure) to
change how these work):
* **Detect game path:**
The package automatically finds your game folder by scanning the default install paths and
Windows registry. It adds two MSBuild properties for use in your `.csproj` file if needed:
`$(GamePath)` and `$(GameModsPath)`.
* **Add assembly references:**
The package adds assembly references to MonoGame, SMAPI, Stardew Valley, and xTile. It
automatically adjusts depending on which OS 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.
* **Copy files into the `Mods` folder:**
The package automatically copies your mod's DLL and PDB files, `manifest.json`, [`i18n`
files](https://stardewvalleywiki.com/Modding:Translations) (if any), and the `assets` folder (if
any) into the `Mods` folder when you rebuild the code, with a subfolder matching the mod's project
name. That lets you try the mod in-game right after building it.
* **Create release zip:**
The package adds a zip file in your project's `bin` folder when you rebuild the code, in the
format recommended for uploading to mod sites like Nexus Mods. This includes the same files as
the previous feature.
* **Launch or debug mod:**
On Windows only, the package configures Visual Studio so you can launch the game and attach a
debugger using _Debug > Start Debugging_ or _Debug > Start Without Debugging_. This lets you [set
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
restart the game](https://docs.microsoft.com/en-us/visualstudio/debugger/edit-and-continue?view=vs-2019).
This is disabled on Linux/macOS due to limitations with the Mono wrapper.
* **Preconfigure common settings:**
The package automatically enables `.pdb` files (so error logs show line numbers to simplify
debugging), and enables support for the simplified SDK-style `.csproj` format.
* **Add code warnings:**
The package runs code analysis on your mod and raises warnings for some common errors or
pitfalls. See [_code warnings_](#code-warnings) for more info.
## Configure
### How to set options
You can configure the package by setting build properties, which are essentially tags like this:
```xml
<PropertyGroup>
<ModFolderName>CustomModName</ModFolderName>
<EnableModDeploy>false</EnableModDeploy>
</PropertyGroup>
```
There are two places you can put them:
* **Global properties** apply to every mod project you open on your computer. That's recommended
for properties you want to set for all mods (e.g. a custom game path). Here's where to put them:
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),
[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)).
2. Create a `stardewvalley.targets` file with this content:
```xml
<Project>
<PropertyGroup>
</PropertyGroup>
</Project>
```
3. Add the properties between the `<PropertyGroup>` and `</PropertyGroup>`.
* **Project properties** apply to a specific project. This is mainly useful for mod-specific
options like the mod name. Here's where to put them:
1. Open the folder containing your mod's source code.
2. Open the `.csproj` file in a text editor (Notepad is fine).
3. Add the properties between the first `<PropertyGroup>` and `</PropertyGroup>` tags you find.
**Note:** you can't use a property before it's defined. That mainly means that when setting
`GameModsPath`, you'll need to either specify `GamePath` manually or put the full path in
`GameModsPath`.
### Available properties
These are the options you can set:
<ul>
<li>Game properties:
<table>
<tr>
<th>property</th>
<th>effect</th>
</tr>
<tr>
<td><code>GamePath</code></td>
<td>
The absolute path to the Stardew Valley folder. This is auto-detected, so you usually don't need to
change it.
</td>
</tr>
<tr>
<td><code>GameModsPath</code></td>
<td>
The absolute path to the folder containing the game's installed mods (defaults to
`$(GamePath)/Mods`), used when deploying the mod files.
</td>
</tr>
<tr>
</table>
</li>
<li>Mod build properties:
<table>
<tr>
<th>property</th>
<th>effect</th>
</tr>
<tr>
<td><code>EnableHarmony</code></td>
<td>
Whether to add a reference to [Harmony](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Harmony)
(default `false`). This is only needed if you use Harmony.
</td>
</tr>
<tr>
<td><code>EnableModDeploy</code></td>
<td>
Whether to copy the mod files into your game's `Mods` folder (default `true`).
</td>
</tr>
<tr>
<td><code>EnableModZip</code></td>
<td>
Whether to create a release-ready `.zip` file in the mod project's `bin` folder (default `true`).
</td>
</tr>
<tr>
<td><code>ModFolderName</code></td>
<td>
The mod name for its folder under `Mods` and its release zip (defaults to the project name).
</td>
</tr>
<tr>
<td><code>ModZipPath</code></td>
<td>
The folder path where the release zip is created (defaults to the project's `bin` folder).
</td>
</tr>
</table>
</li>
<li>Specialized properties:
<table>
<tr>
<th>property</th>
<th>effect</th>
</tr>
<tr>
<td><code>BundleExtraAssemblies</code></td>
<td>
**Most mods should not change this option.**
By default (when this is _not_ enabled), only the mod files [normally considered part of the
mod](#Features) will be added to the release `.zip` and copied into the `Mods` folder (i.e.
"deployed"). That includes the assembly files (`*.dll`, `*.pdb`, and `*.xml`) for your mod project,
but any other DLLs won't be deployed.
Enabling this option will add _all_ dependencies to the build output, then deploy _some_ of them
depending on the comma-separated value(s) you set:
<table>
<tr>
<th>option</th>
<th>result</th>
</tr>
<tr>
<td><code>ThirdParty</code></td>
<td>
Assembly files which don't match any other category.
</td>
</tr>
<tr>
<td><code>System</code></td>
<td>
Assembly files whose names start with `Microsoft.*` or `System.*`.
</td>
</tr>
<tr>
<td><code>Game</code></td>
<td>
Assembly files which are part of MonoGame, SMAPI, or Stardew Valley.
</td>
</tr>
<tr>
<td><code>All</code></td>
<td>
Equivalent to `System, Game, ThirdParty`.
</td>
</tr>
</table>
Most mods should omit the option. Some mods may need `ThirdParty` if they bundle third-party DLLs
with their mod. The other options are mainly useful for unit tests.
When enabling this option, you should **manually review which files get deployed** and use the
`IgnoreModFilePaths` or `IgnoreModFilePatterns` options to exclude files as needed.
</td>
</tr>
<tr>
<td><code>EnableGameDebugging</code></td>
<td>
Whether to configure the project so you can launch or debug the game through the _Debug_ menu in
Visual Studio (default `true`). There's usually no reason to change this, unless it's a unit test
project.
</td>
</tr>
<tr>
<td><code>IgnoreModFilePaths</code></td>
<td>
A comma-delimited list of literal file paths to ignore, relative to the mod's `bin` folder. Paths
are case-sensitive, but path delimiters are normalized automatically. For example, this ignores a
set of tilesheets:
```xml
<IgnoreModFilePaths>assets/paths.png, assets/springobjects.png</IgnoreModFilePaths>
```
</td>
</tr>
<tr>
<td><code>IgnoreModFilePatterns</code></td>
<td>
A comma-delimited list of regex patterns matching files to ignore when deploying or zipping the mod
files (default empty). For crossplatform compatibility, you should replace path delimiters with `[/\\]`.
For example, this excludes all `.txt` and `.pdf` files, as well as the `assets/paths.png` file:
```xml
<IgnoreModFilePatterns>\.txt$, \.pdf$, assets[/\\]paths.png</IgnoreModFilePatterns>
```
</td>
</tr>
</table>
</li>
</ul>
## Code warnings
### Overview
The NuGet package adds code warnings in Visual Studio specific to Stardew Valley. For example:
![](screenshots/code-analyzer-example.png)
You can [hide the warnings](https://visualstudiomagazine.com/articles/2017/09/01/hide-compiler-warnings.aspx)
if needed using the warning ID (shown under 'code' in the Error List).
See below for help with specific warnings.
### Avoid implicit net field cast
Warning text:
> This implicitly converts '{{expression}}' from {{net type}} to {{other type}}, but
> {{net type}} has unintuitive implicit conversion rules. Consider comparing against the actual
> value instead to avoid bugs.
Stardew Valley uses net types (like `NetBool` and `NetInt`) to handle multiplayer sync. These types
can implicitly convert to their equivalent normal values (like `bool x = new NetBool()`), but their
conversion rules are unintuitive and error-prone. For example,
`item?.category == null && item?.category != null` can both be true at once, and
`building.indoors != null` can be true for a null value.
Suggested fix:
* Some net fields have an equivalent non-net property like `monster.Health` (`int`) instead of
`monster.health` (`NetInt`). The package will add a separate [AvoidNetField](#avoid-net-field) warning for
these. Use the suggested property instead.
* For a reference type (i.e. one that can contain `null`), you can use the `.Value` property:
```c#
if (building.indoors.Value == null)
```
Or convert the value before comparison:
```c#
GameLocation indoors = building.indoors;
if(indoors == null)
// ...
```
* For a value type (i.e. one that can't contain `null`), check if the object is null (if applicable)
and compare with `.Value`:
```cs
if (item != null && item.category.Value == 0)
```
### Avoid net field
Warning text:
> '{{expression}}' is a {{net type}} field; consider using the {{property name}} property instead.
Your code accesses a net field, which has some unusual behavior (see [AvoidImplicitNetFieldCast](#avoid-implicit-net-field-cast)).
This field has an equivalent non-net property that avoids those issues.
Suggested fix: access the suggested property name instead.
### Avoid obsolete field
Warning text:
> The '{{old field}}' field is obsolete and should be replaced with '{{new field}}'.
Your code accesses a field which is obsolete or no longer works. Use the suggested field instead.
### Wrong processor architecture
Warning text:
> The target platform should be set to 'Any CPU' for compatibility with both 32-bit and 64-bit
> versions of Stardew Valley (currently set to '{{current platform}}').
Mods can be used in either 32-bit or 64-bit mode. Your project's target platform isn't set to the
default 'Any CPU', so it won't work in both. You can fix it by [setting the target platform to
'Any CPU'](https://docs.microsoft.com/en-ca/visualstudio/ide/how-to-configure-projects-to-target-platforms).
## FAQs
### How do I set the game path?<span id="custom-game-path"></span>
The package detects where your game is installed automatically, so you usually don't need to set it
manually. If it can't find your game or you have multiple installs, you can specify the path
yourself.
To do that:
1. Get the full folder path containing the Stardew Valley executable.
2. See [_configure_](#configure) to add this property:
```xml
<PropertyGroup>
<GamePath>PATH_HERE</GamePath>
</PropertyGroup>
```
3. Replace `PATH_HERE` with your game's folder path (don't add quotes).
The configuration will check your custom path first, then fall back to the default paths (so it'll
still compile on a different computer).
### How do I change which files are included in the mod deploy/zip?
* For normal files, you can [add/remove them in the build output](https://stackoverflow.com/a/10828462/262123).
* For assembly files (`*.dll`, `*.exe`, `*.pdb`, or `*.xml`), see the
[`BundleExtraAssemblies` option](#configure).
* To exclude a file which the package copies by default, see the [`IgnoreModFilePaths` or
`IgnoreModFilePatterns` options](#configure).
### Can I use the package for non-mod projects?
Yep, this works in unit tests and framework projects too. Just disable the mod-related package
features (see [_configure_](#configure)):
```xml
<EnableGameDebugging>false</EnableGameDebugging>
<EnableModDeploy>false</EnableModDeploy>
<EnableModZip>false</EnableModZip>
```
To copy referenced DLLs into your build output for unit tests, add this too:
```xml
<BundleExtraAssemblies>All</BundleExtraAssemblies>
```
## For SMAPI developers
The mod build package consists of three projects:
project | purpose
------------------------------------------------- | ----------------
`StardewModdingAPI.ModBuildConfig` | Configures the build (references, deploying the mod files, setting up debugging, etc).
`StardewModdingAPI.ModBuildConfig.Analyzer` | Adds C# analyzers which show code warnings in Visual Studio.
`StardewModdingAPI.ModBuildConfig.Analyzer.Tests` | Unit tests for the C# analyzers.
The NuGet package is generated automatically in `StardewModdingAPI.ModBuildConfig`'s `bin` folder
when you compile it.
## Release notes
## 4.1.1
Released 24 June 2023 for SMAPI 3.13.0 or later.
* Replaced `.pdb` files with embedded symbols by default. This fixes logged errors not having line numbers on Linux/macOS.
### 4.1.0
Released 08 January 2023 for SMAPI 3.13.0 or later.
* Added `manifest.json` format validation on build (thanks to tylergibbs2!).
* Fixed game DLLs not excluded from the release zip when they're referenced explicitly but `BundleExtraAssemblies` isn't set.
### 4.0.2
Released 09 October 2022 for SMAPI 3.13.0 or later.
* Switched to the newer crossplatform `portable` debug symbols (thanks to lanturnalis!).
* Fixed `BundleExtraAssemblies` option being partly case-sensitive.
* Fixed `BundleExtraAssemblies` not applying `All` value to game assemblies.
### 4.0.1
Released 14 April 2022 for SMAPI 3.13.0 or later.
* Added detection for Xbox app game folders.
* Fixed "_conflicts between different versions of Microsoft.Win32.Registry_" warnings in recent SMAPI versions.
* Internal refactoring.
### 4.0.0
Released 30 November 2021 for SMAPI 3.13.0 or later.
* Updated for Stardew Valley 1.5.5 and SMAPI 3.13.0. (Older versions are no longer supported.)
* Added `IgnoreModFilePaths` option to ignore literal paths.
* Added `BundleExtraAssemblies` option to copy bundled DLLs into the mod zip/folder.
* Removed the `GameExecutableName` and `GameFramework` options (since they now have the same value
on all platforms).
* Removed the `CopyModReferencesToBuildOutput` option (superseded by `BundleExtraAssemblies`).
* Improved analyzer performance by enabling parallel execution.
**Migration guide for mod authors:**
1. See [_migrate to 64-bit_](https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows) and
[_migrate to Stardew Valley 1.5.5_](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5).
2. Possible changes in your `.csproj` or `.targets` files:
* Replace `$(GameExecutableName)` with `Stardew Valley`.
* Replace `$(GameFramework)` with `MonoGame` and remove any XNA Framework-specific logic.
* Replace `<CopyModReferencesToBuildOutput>true</CopyModReferencesToBuildOutput>` with
`<BundleExtraAssemblies>Game</BundleExtraAssemblies>`.
* If you need to bundle extra DLLs besides your mod DLL, see the [`BundleExtraAssemblies`
documentation](#configure).
### 3.3.0
Released 30 March 2021 for SMAPI 3.0.0 or later.
* Added a build warning when the mod isn't compiled for `Any CPU`.
* Added a `GameFramework` build property set to `MonoGame` or `Xna` based on the platform. This can
be overridden to change which framework it references.
* Added support for building mods against the 64-bit Linux version of the game on Windows.
* The package now suppresses the misleading 'processor architecture mismatch' warnings.
### 3.2.2
Released 23 September 2020 for SMAPI 3.0.0 or later.
* Reworked and streamlined how the package is compiled.
* Added [SMAPI-ModTranslationClassBuilder](https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder)
files to the ignore list.
### 3.2.1
Released 11 September 2020 for SMAPI 3.0.0 or later.
* Added more detailed logging.
* Fixed _path's format is not supported_ error when using default `Mods` path in 3.2.
### 3.2.0
Released 07 September 2020 for SMAPI 3.0.0 or later.
* Added option to change `Mods` folder path.
* Rewrote documentation to make it easier to read.
### 3.1.0
Released 01 February 2020 for SMAPI 3.0.0 or later.
* Added support for semantic versioning 2.0.
* `0Harmony.dll` is now ignored if the mod references Harmony directly (it's bundled with SMAPI).
### 3.0.0
Released 26 November 2019 for SMAPI 3.0.0 or later.
* Updated for SMAPI 3.0 and Stardew Valley 1.4.
* Added automatic support for `assets` folders.
* Added `$(GameExecutableName)` MSBuild variable.
* Added support for projects using the simplified `.csproj` format.
* Added option to disable game debugging config.
* Added `.pdb` files to builds by default (to enable line numbers in error stack traces).
* Added optional Harmony reference.
* Fixed `Newtonsoft.Json.pdb` included in release zips when Json.NET is referenced directly.
* Fixed `<IgnoreModFilePatterns>` not working for `i18n` files.
* Dropped support for older versions of SMAPI and Visual Studio.
* Migrated package icon to NuGet's new format.
### 2.2.0
Released 28 October 2018.
* Added support for SMAPI 2.8+ (still compatible with earlier versions).
* Added default game paths for 32-bit Windows.
* Fixed valid manifests marked invalid in some cases.
### 2.1.0
Released 27 July 2018.
* Added support for Stardew Valley 1.3.
* Added support for non-mod projects.
* Added C# analyzers to warn about implicit conversions of Netcode fields in Stardew Valley 1.3.
* Added option to ignore files by regex pattern.
* Added reference to new SMAPI DLL.
* Fixed some game paths not detected by NuGet package.
### 2.0.2
Released 01 November 2017.
* Fixed compatibility issue on Linux.
### 2.0.1
Released 11 October 2017.
* Fixed mod deploy failing to create subfolders if they don't already exist.
### 2.0.0
Released 11 October 2017.
* Added: mods are now copied into the `Mods` folder automatically (configurable).
* Added: release zips are now created automatically in your build output folder (configurable).
* Added: mod deploy and release zips now exclude Json.NET automatically, since it's provided by SMAPI.
* Added mod's version to release zip filename.
* Improved errors to simplify troubleshooting.
* Fixed release zip not having a mod folder.
* Fixed release zip failing if mod name contains characters that aren't valid in a filename.
### 1.7.1
Released 28 July 2017.
* Fixed issue where i18n folders were flattened.
* The manifest/i18n files in the project now take precedence over those in the build output if both
are present.
### 1.7.0
Released 28 July 2017.
* Added option to create release zips on build.
* Added reference to XNA's XACT library for audio-related mods.
### 1.6.2
Released 10 July 2017.
* Further improved crossplatform game path detection.
* Removed undocumented `GamePlatform` build property.
### 1.6.1
Released 09 July 2017.
* Improved crossplatform game path detection.
### 1.6.0
Released 05 June 2017.
* Added support for deploying mod files into `Mods` automatically.
* Added a build error if a game folder is found, but doesn't contain Stardew Valley or SMAPI.
### 1.5.0
Released 23 January 2017.
* Added support for setting a custom game path globally.
* Added default GOG path on macOS.
### 1.4.0
Released 11 January 2017.
* Fixed detection of non-default game paths on 32-bit Windows.
* Removed support for SilVerPLuM (discontinued).
* Removed support for overriding the target platform (no longer needed since SMAPI crossplatforms
mods automatically).
### 1.3.0
Released 31 December 2016.
* Added support for non-default game paths on Windows.
### 1.2.0
Released 24 October 2016.
* Exclude game binaries from mod build output.
### 1.1.0
Released 21 October 2016.
* Added support for overriding the target platform.
### 1.0.0
Released 21 October 2016.
* Initial release.
* Added support for detecting the game path automatically.
* Added support for injecting XNA/MonoGame references automatically based on the OS.
* Added support for mod builders like SilVerPLuM.

View File

@ -1,176 +0,0 @@
&larr; [README](../README.md)
This file provides more technical documentation about SMAPI. If you only want to use or create
mods, this section isn't relevant to you; see the main README to use or create mods.
This document is about SMAPI itself; see also [mod build package](mod-package.md) and
[web services](web.md).
# Contents
* [Customisation](#customisation)
* [Configuration file](#configuration-file)
* [Command-line arguments](#command-line-arguments)
* [Compile flags](#compile-flags)
* [Compile from source code](#compile-from-source-code)
* [Main project](#main-project)
* [Custom Harmony build](#custom-harmony-build)
* [Prepare a release](#prepare-a-release)
* [On any platform](#on-any-platform)
* [On Windows](#on-windows)
* [Release notes](#release-notes)
## Customisation
### Configuration file
You can customise some SMAPI behaviour by editing the `smapi-internal/config.json` file in your
game folder. See documentation in the file for more info.
### Command-line arguments
The SMAPI installer recognises three command-line arguments:
argument | purpose
-------- | -------
`--install` | Preselects the install action, skipping the prompt asking what the user wants to do.
`--uninstall` | Preselects the uninstall action, skipping the prompt asking what the user wants to do.
`--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.
`--no-prompt` | Don't let the installer wait for user input (e.g. for cases where it's being run by a script). If the installer is unable to continue without user input, it'll fail instead.
SMAPI itself recognises five arguments, but these are meant for internal use or testing, and might
change without warning. **On Linux/macOS**, command-line arguments won't work; see _environment
variables_ below instead.
argument | purpose
-------- | -------
`--developer-mode`<br />`--developer-mode-off` | Enable or disable features intended for mod developers. Currently this only makes `TRACE`-level messages appear in the console.
`--no-terminal` | SMAPI won't log anything to the console. On Linux/macOS only, this will also prevent the launch script from trying to open a terminal window. (Messages will still be written to the log file.)
`--use-current-shell` | On Linux/macOS only, the launch script won't try to open a terminal window. All console output will be sent to the shell running the launch script.
`--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
The above SMAPI arguments may not work on Linux/macOS due to the way the game launcher works. You
can set temporary environment variables instead. For example:
> SMAPI_MODS_PATH="Mods (multiplayer)" /path/to/StardewValley
environment variable | purpose
-------------------- | -------
`SMAPI_DEVELOPER_MODE` | Equivalent to `--developer-mode` and `--developer-mode-off` above. The value must be `true` or `false`.
`SMAPI_MODS_PATH` | Equivalent to `--mods-path` above.
`SMAPI_NO_TERMINAL` | Equivalent to `--no-terminal` above.
`SMAPI_USE_CURRENT_SHELL` | Equivalent to `--use-current-shell` above.
### Compile flags
SMAPI uses a small number of conditional compilation constants, which you can set by editing the
`<DefineConstants>` element in `SMAPI.csproj`. Supported constants:
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_DEPRECATED` | Whether to include deprecated code in the build.
## Compile from source code
### Main project
Using an official SMAPI release is recommended for most users, but you can compile from source
directly if needed. Just open the project in an IDE like [Visual
Studio](https://visualstudio.microsoft.com/vs/community/) or [Rider](https://www.jetbrains.com/rider/),
and build the `SMAPI` project. The project will automatically adjust the build settings for your
current OS and Stardew Valley install path.
Rebuilding the solution in debug mode will copy the SMAPI files into your game folder. Starting
the `SMAPI` project with debugging from Visual Studio or Rider should launch SMAPI with the
debugger attached, so you can intercept errors and step through the code being executed.
### Custom Harmony build
SMAPI uses [a custom build of Harmony 2.2.2](https://github.com/Pathoschild/Harmony#readme), which
is included in the `build` folder. To use a different build, just replace `0Harmony.dll` in that
folder before compiling.
## Prepare a release
### On any platform
**⚠ Ideally we'd have one set of instructions for all platforms. The instructions in this section
will produce a fully functional release for all supported platforms, _except_ that the application
icon for SMAPI on Windows will disappear due to [.NET runtime bug
3828](https://github.com/dotnet/runtime/issues/3828). Until that's fixed, see the _[on
Windows](#on-windows)_ section below to create a build that retains the icon.**
#### First-time setup
1. On Windows only:
1. [Install Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install).
2. Run `sudo apt update` in WSL to update the package list.
3. The rest of the instructions below should be run in WSL.
2. Install the required software:
1. Install the [.NET 5 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu).
_For Ubuntu-based systems, you can run `lsb_release -a` to get the Ubuntu version number._
2. [Install Steam](https://linuxconfig.org/how-to-install-steam-on-ubuntu-20-04-focal-fossa-linux).
3. Launch `steam` and install the game like usual.
4. Download and install your preferred IDE. For the [latest standalone Rider
version](https://www.jetbrains.com/help/rider/Installation_guide.html#prerequisites):
```sh
wget "<download url here>" -O rider-install.tar.gz
sudo tar -xzvf rider-install.tar.gz -C /opt
ln -s "/opt/JetBrains Rider-<version>/bin/rider.sh"
./rider.sh
```
3. Clone the SMAPI repo:
```sh
git clone https://github.com/Pathoschild/SMAPI.git
```
### Launch the game
1. Run these commands to start Steam:
```sh
export TERM=xterm
steam
```
2. Launch the game through the Steam UI.
### Prepare the release
1. Run `build/unix/prepare-install-package.sh VERSION_HERE` to create the release package in the
root `bin` folder.
Make sure you use a [semantic version](https://semver.org). Recommended format:
build type | format | example
:--------- | :----------------------- | :------
dev build | `<version>-alpha.<date>` | `4.0.0-alpha.20251230`
prerelease | `<version>-beta.<date>` | `4.0.0-beta.20251230`
release | `<version>` | `4.0.0`
### On Windows
#### First-time setup
1. Set up Windows Subsystem for Linux (WSL):
1. [Install WSL](https://docs.microsoft.com/en-us/windows/wsl/install).
2. Run `sudo apt update` in WSL to update the package list.
3. The rest of the instructions below should be run in WSL.
2. Install the required software:
1. Install the [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0).
2. Install [Stardew Valley](https://www.stardewvalley.net/).
3. Clone the SMAPI repo:
```sh
git clone https://github.com/Pathoschild/SMAPI.git
```
### Prepare the release
1. Run `build/windows/prepare-install-package.ps1 VERSION_HERE` in PowerShell to create the release
package folders in the root `bin` folder.
Make sure you use a [semantic version](https://semver.org). Recommended format:
build type | format | example
:--------- | :----------------------- | :------
dev build | `<version>-alpha.<date>` | `4.0.0-alpha.20251230`
prerelease | `<version>-beta.<date>` | `4.0.0-beta.20251230`
release | `<version>` | `4.0.0`
2. Launch WSL and run this script:
```bash
# edit to match the build created in steps 1
# In WSL, `/mnt/c/example` accesses `C:\example` on the Windows filesystem.
version="4.0.0"
binFolder="/mnt/e/source/_Stardew/SMAPI/bin"
build/windows/finalize-install-package.sh "$version" "$binFolder"
```
Note: to prepare a test Windows-only build, you can pass `--windows-only` in the first step and
skip the second one.
## Release notes
See [release notes](../release-notes.md).

View File

@ -1,388 +0,0 @@
&larr; [README](../README.md)
**SMAPI.Web** contains the code for the `smapi.io` website, including the mod compatibility list
and update check API.
## Contents
* [Log parser](#log-parser)
* [JSON validator](#json-validator)
* [Web API](#web-api)
* [Short URLs](#short-urls)
* [For SMAPI developers](#for-smapi-developers)
* [Local development](#local-development)
* [Production environment](#production-environment)
## Log parser
The log parser at https://smapi.io/log provides a web UI for uploading, parsing, and sharing SMAPI
logs.
The logs are saved in a compressed form to Amazon Blob storage for 30 days.
## JSON validator
### Overview
The JSON validator at https://smapi.io/json provides a web UI for uploading and sharing JSON files,
and validating them as plain JSON or against a predefined format like `manifest.json` or Content
Patcher's `content.json`.
The logs are saved in a compressed form to Amazon Blob storage for 30 days.
### Schema file format
Schema files are defined in `wwwroot/schemas` using the [JSON Schema](https://json-schema.org/)
format. The JSON validator UI recognises a superset of the standard fields to change output:
<dl>
<dt>Documentation URL</dt>
<dd>
The root schema may have a `@documentationURL` field, which is a web URL for the user
documentation:
```js
"@documentationUrl": "https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest"
```
If present, this is shown in the JSON validator UI.
</dd>
<dt>Error messages</dt>
<dd>
Any part of the schema can define an `@errorMessages` field, which overrides matching schema
errors. You can override by error code (recommended), or by error type and a regex pattern matched
against the error message (more fragile):
```js
// by error type
"pattern": "^[a-zA-Z0-9_.-]+\\.dll$",
"@errorMessages": {
"pattern": "Invalid value; must be a filename ending with .dll."
}
```
```js
// by error type + message pattern
"@errorMessages": {
"oneOf:valid against no schemas": "Missing required field: EntryDll or ContentPackFor.",
"oneOf:valid against more than one schema": "Can't specify both EntryDll or ContentPackFor, they're mutually exclusive."
}
```
Error messages may contain special tokens:
* The `@value` token is replaced with the error's value field. This is usually (but not always) the
original field value.
* When an error has child errors, by default they're flattened into one message:
```
line | field | error
---- | ---------- | -------------------------------------------------------------------------
4 | Changes[0] | JSON does not match schema from 'then'.
| | ==> Changes[0].ToArea.Y: Invalid type. Expected Integer but got String.
| | ==> Changes[0].ToArea: Missing required fields: Height.
```
If you set the message for an error to `$transparent`, the parent error is omitted entirely and
the child errors are shown instead:
```
line | field | error
---- | ------------------- | ----------------------------------------------
8 | Changes[0].ToArea.Y | Invalid type. Expected Integer but got String.
8 | Changes[0].ToArea | Missing required fields: Height.
```
The child errors themselves may be marked `$transparent`, etc. If an error has no child errors,
this override is ignored.
Validation errors for `then` blocks are transparent by default, unless overridden.
</dd>
</dl>
### Using a schema file directly
You can reference the validator schemas in your JSON file directly using the `$schema` field, for
text editors that support schema validation. For example:
```js
{
"$schema": "https://smapi.io/schemas/manifest.json",
"Name": "Some mod",
...
}
```
Available schemas:
format | schema URL
------ | ----------
[SMAPI: `manifest.json`](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest) | https://smapi.io/schemas/manifest.json
[SMAPI: translations (`i18n` folder)](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Translation) | https://smapi.io/schemas/i18n.json
[Content Patcher: `content.json`](https://github.com/Pathoschild/StardewMods/tree/develop/ContentPatcher#readme) | https://smapi.io/schemas/content-patcher.json
## Web API
### Overview
SMAPI provides a web API at `smapi.io/api` for use by SMAPI and external tools. The URL includes a
`{version}` token, which is the SMAPI version for backwards compatibility. This API is publicly
accessible but not officially released; it may change at any time.
### `/mods` endpoint
The API has one `/mods` endpoint. This crossreferences the mod against a variety of sources (e.g.
the wiki, Chucklefish, CurseForge, ModDrop, and Nexus) to provide metadata mainly intended for
update checks.
The API accepts a `POST` request with these fields:
<table>
<tr>
<th>field</th>
<th>summary</th>
</tr>
<tr>
<td><code>mods</code></td>
<td>
The mods for which to fetch metadata. Included fields:
field | summary
----- | -------
`id` | The unique ID in the mod's `manifest.json`. This is used to crossreference with the wiki, and to index mods in the response. If it's unknown (e.g. you just have an update key), you can use a unique fake ID like `FAKE.Nexus.2400`.
`updateKeys` | _(optional)_ [Update keys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest#Update_checks) which specify the mod pages to check, in addition to any mod pages linked to the `ID`.
`installedVersion` | _(optional)_ The installed version of the mod. If not specified, the API won't recommend an update.
`isBroken` | _(optional)_ Whether SMAPI failed to load the installed version of the mod, e.g. due to incompatibility. If true, the web API will be more permissive when recommending updates (e.g. allowing a stable → prerelease update).
</td>
</tr>
<tr>
<td><code>apiVersion</code></td>
<td>
_(optional)_ The installed version of SMAPI. If not specified, the API won't recommend an update.
</td>
</tr>
<tr>
<td><code>gameVersion</code></td>
<td>
_(optional)_ The installed version of Stardew Valley. This may be used to select updates.
</td>
</tr>
<tr>
<td><code>platform</code></td>
<td>
_(optional)_ The player's OS (`Android`, `Linux`, `Mac`, or `Windows`). This may be used to select updates.
</td>
</tr>
<tr>
<td><code>includeExtendedMetadata</code></td>
<td>
_(optional)_ Whether to include extra metadata that's not needed for SMAPI update checks, but which
may be useful to external tools.
</td>
</table>
Example request:
```js
POST https://smapi.io/api/v3.0/mods
{
"mods": [
{
"id": "Pathoschild.ContentPatcher",
"updateKeys": [ "nexus:1915" ],
"installedVersion": "1.9.2",
"isBroken": false
}
],
"apiVersion": "3.0.0",
"gameVersion": "1.4.0",
"platform": "Windows",
"includeExtendedMetadata": true
}
```
Response fields:
<table>
<tr>
<th>field</th>
<th>summary</th>
</tr>
<tr>
<td><code>id</code></td>
<td>
The mod ID you specified in the request.
</td>
</tr>
<tr>
<td><code>suggestedUpdate</code></td>
<td>
The update version recommended by the web API, if any. This is based on some internal rules (e.g.
it won't recommend a prerelease update if the player has a working stable version) and context
(e.g. whether the player is in the game beta channel). Choosing an update version yourself isn't
recommended, but you can set `includeExtendedMetadata: true` and check the `metadata` field if you
really want to do that.
</td>
</tr>
<tr>
<td><code>errors</code></td>
<td>
Human-readable errors that occurred fetching the version info (e.g. if a mod page has an invalid
version).
</td>
</tr>
<tr>
<td><code>metadata</code></td>
<td>
Extra metadata that's not needed for SMAPI update checks but which may be useful to external tools,
if you set `includeExtendedMetadata: true` in the request. Included fields:
field | summary
----- | -------
`id` | The known `manifest.json` unique IDs for this mod defined on the wiki, if any. That includes historical versions of the mod.
`name` | The normalised name for this mod based on the crossreferenced sites.
`nexusID` | The mod ID on [Nexus Mods](https://www.nexusmods.com/stardewvalley/), if any.
`chucklefishID` | The mod ID in the [Chucklefish mod repo](https://community.playstarbound.com/resources/categories/stardew-valley.22/), if any.
`curseForgeID` | The mod project ID on [CurseForge](https://www.curseforge.com/stardewvalley), if any.
`curseForgeKey` | The mod key on [CurseForge](https://www.curseforge.com/stardewvalley), if any. This is used in the mod page URL.
`modDropID` | The mod ID on [ModDrop](https://www.moddrop.com/stardew-valley), if any.
`gitHubRepo` | The GitHub repository containing the mod code, if any. Specified in the `Owner/Repo` form.
`customSourceUrl` | The custom URL to the mod code, if any. This is used for mods which aren't stored in a GitHub repo.
`customUrl` | The custom URL to the mod page, if any. This is used for mods which aren't stored on one of the standard mod sites covered by the ID fields.
`main` | The primary mod version, if any. This depends on the mod site, but it's typically either the version of the mod itself or of its latest non-optional download.
`optional` | The latest optional download version, if any.
`unofficial` | The version of the unofficial update defined on the wiki for this mod, if any.
`unofficialForBeta` | Equivalent to `unofficial`, but for beta versions of SMAPI or Stardew Valley.
`hasBetaInfo` | Whether there's an ongoing Stardew Valley or SMAPI beta which may affect update checks.
`compatibilityStatus` | The compatibility status for the mod for the stable version of the game, as defined on the wiki, if any. See [possible values](https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs).
`compatibilitySummary` | The human-readable summary of the mod's compatibility in HTML format, if any.
`brokeIn` | The SMAPI or Stardew Valley version that broke this mod, if any.
`betaCompatibilityStatus`<br />`betaCompatibilitySummary`<br />`betaBrokeIn` | Equivalent to the preceding fields, but for beta versions of SMAPI or Stardew Valley.
</td>
</tr>
</table>
Example response with `includeExtendedMetadata: false`:
```js
[
{
"id": "Pathoschild.ContentPatcher",
"suggestedUpdate": {
"version": "1.10.0",
"url": "https://www.nexusmods.com/stardewvalley/mods/1915"
},
"errors": []
}
]
```
Example response with `includeExtendedMetadata: true`:
```js
[
{
"id": "Pathoschild.ContentPatcher",
"suggestedUpdate": {
"version": "1.10.0",
"url": "https://www.nexusmods.com/stardewvalley/mods/1915"
},
"metadata": {
"id": [ "Pathoschild.ContentPatcher" ],
"name": "Content Patcher",
"nexusID": 1915,
"curseForgeID": 309243,
"curseForgeKey": "content-patcher",
"modDropID": 470174,
"gitHubRepo": "Pathoschild/StardewMods",
"main": {
"version": "1.10",
"url": "https://www.nexusmods.com/stardewvalley/mods/1915"
},
"hasBetaInfo": true,
"compatibilityStatus": "Ok",
"compatibilitySummary": "✓ use latest version."
},
"errors": []
}
]
```
## Short URLs
The SMAPI web services provides a few short URLs for convenience:
short url | → | target page
:-------- | - | :----------
[smapi.io/3.0](https://smapi.io/3.0) | → | [stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0)
[smapi.io/community](https://smapi.io/community) | → | [stardewvalleywiki.com/Modding:Community](https://stardewvalleywiki.com/Modding:Community)
[smapi.io/docs](https://smapi.io/docs) | → | [stardewvalleywiki.com/Modding:Index](https://stardewvalleywiki.com/Modding:Index)
[smapi.io/package](https://smapi.io/package) | → | [github.com/Pathoschild/SMAPI/blob/develop/docs/technical/mod-package.md](https://github.com/Pathoschild/SMAPI/blob/develop/docs/technical/mod-package.md)
[smapi.io/troubleshoot](https://smapi.io/troubleshoot) | → | [stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting](https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting)
[smapi.io/xnb](https://smapi.io/xnb) | → | [stardewvalleywiki.com/Modding:Using_XNB_mods](https://stardewvalleywiki.com/Modding:Using_XNB_mods)
## For SMAPI developers
### Local environment
A local environment lets you run a complete copy of the web project (including cache database) on
your machine, with no external dependencies aside from the actual mod sites.
1. Edit `appsettings.Development.json` and set these options:
property name | description
------------- | -----------
`NexusApiKey` | [Your Nexus API key](https://www.nexusmods.com/users/myaccount?tab=api#personal_key).
Optional settings:
property name | description
--------------------------- | -----------
`AzureBlobConnectionString` | The connection string for the Azure Blob storage account. Defaults to using the system's temporary file folder if not specified.
`GitHubUsername`<br />`GitHubPassword` | The GitHub credentials with which to query GitHub release info. Defaults to anonymous requests if not specified.
2. Launch `SMAPI.Web` from Visual Studio to run a local version of the site.
### Production environment
A production environment includes the web servers and cache database hosted online for public
access.
This section assumes you're creating a new environment on Azure, but the app isn't tied to any
Azure services. If you want to host it on a different site, you'll need to adjust the instructions
accordingly.
Initial setup:
1. Create an Azure Blob storage account for uploaded files.
2. Create an Azure App Services environment running the latest .NET on Linux or Windows.
3. Add these application settings in the new App Services environment:
property name | description
------------------------------- | -----------------
`ApiClients.AzureBlobConnectionString` | The connection string for the Azure Blob storage account created in step 2.
`ApiClients.GitHubUsername`<br />`ApiClients.GitHubPassword` | The login credentials for the GitHub account with which to fetch release info. If these are omitted, GitHub will impose much stricter rate limits.
`ApiClients:NexusApiKey` | The [Nexus API authentication key](https://github.com/Pathoschild/FluentNexus#init-a-client).
Optional settings:
property name | description
------------------------------- | -----------------
`BackgroundServices:Enabled` | Set to `true` to enable background processes like fetching data from the wiki, or false to disable them.
`Site:BetaEnabled` | Set to `true` to show a separate download button if there's a beta version of SMAPI in its GitHub releases.
`Site:BetaBlurb` | If `Site:BetaEnabled` is true and there's a beta version of SMAPI in its GitHub releases, this is shown on the beta download button as explanatory subtext.
`Site:SupporterList` | A list of Patreon supports to credit on the download page.
To deploy updates, just [redeploy the web project from Visual Studio](https://docs.microsoft.com/en-us/visualstudio/deployment/quickstart-deploy-to-azure).

View File

@ -1,19 +0,0 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

View File

@ -1,110 +0,0 @@
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{45D7D2FB-6B70-45D1-A595-6E289D6A3468}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Loader</RootNamespace>
<AssemblyName>Loader</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>True</AndroidApplication>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
<TargetFramework>net5.0-android</TargetFramework>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
<AndroidDexTool>d8</AndroidDexTool>
<!-- <AndroidLinkSkip>Mono.Android;MonoGame.Framework;mscorlib;System.Core;System;System.Drawing.Common;System.Numerics;System.Runtime.Serialization;System.Xml;System.Xml.Linq</AndroidLinkSkip>-->
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>False</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
<AndroidDexTool>d8</AndroidDexTool>
</PropertyGroup>
<ItemGroup>
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Resources\AboutResources.txt" />
<None Include="Properties\AndroidManifest.xml" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\icon.png" />
<AndroidResource Include="Resources\drawable\splash.png" />
<AndroidResource Include="Resources\drawable\splash_logos_crop.png" />
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
<AndroidResource Include="Resources\values\strings.xml" />
<AndroidResource Include="Resources\values\styles.xml" />
</ItemGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="Mono.Security" />
<ProjectReference Include="..\SMAPI\SMAPI.csproj" />
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap\ic_launcher_background.png" />
<AndroidResource Include="Resources\mipmap\ic_launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap\ic_launcher_round.png" />
<AndroidResource Include="Resources\xml\provider_paths.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\Content\**" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="156" android:versionName="1.5.6.31" android:installLocation="auto" package="com.zane.stardewvalley2" platformBuildVersionCode="31" platformBuildVersionName="9">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:label="SMAPI Stardew Valley"
android:icon="@mipmap/ic_launcher"
android:name="android.app.Application"
android:allowBackup="true"
android:resizeableActivity="false"
android:debuggable="true"
android:requestLegacyExternalStorage="true">
</application>
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
</manifest>

View File

@ -1,28 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Loader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Loader")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -1,44 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "R"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the R class would expose:
public class R {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
to reference the layout/main.xml file, or R.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

View File

@ -1,175 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Loader.Resource", IsApplication=true)]
namespace Loader
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.2.0.99")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
global::StardewModdingAPI.Resource.String.app_name = global::Loader.Resource.String.app_name;
global::StardewModdingAPI.Resource.String.hello = global::Loader.Resource.String.hello;
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Color
{
// aapt resource value: 0x7F010000
public const int colorAccent = 2130771968;
// aapt resource value: 0x7F010001
public const int colorPrimary = 2130771969;
// aapt resource value: 0x7F010002
public const int colorPrimaryDark = 2130771970;
// aapt resource value: 0x7F010003
public const int ic_launcher_background = 2130771971;
static Color()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Color()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F020000
public const int icon = 2130837504;
// aapt resource value: 0x7F020001
public const int splash = 2130837505;
// aapt resource value: 0x7F020002
public const int splash_logos_crop = 2130837506;
// aapt resource value: 0x7F020003
public const int splash_screen = 2130837507;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class Mipmap
{
// aapt resource value: 0x7F030000
public const int ic_launcher = 2130903040;
// aapt resource value: 0x7F030001
public const int ic_launcher_background = 2130903041;
// aapt resource value: 0x7F030002
public const int ic_launcher_foreground = 2130903042;
// aapt resource value: 0x7F030003
public const int ic_launcher_round = 2130903043;
static Mipmap()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Mipmap()
{
}
}
public partial class String
{
// aapt resource value: 0x7F040000
public const int action_settings = 2130968576;
// aapt resource value: 0x7F040001
public const int app_name = 2130968577;
// aapt resource value: 0x7F040002
public const int hello = 2130968578;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
public partial class Style
{
// aapt resource value: 0x7F050000
public const int Theme_Splash = 2131034112;
static Style()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Style()
{
}
}
public partial class Xml
{
// aapt resource value: 0x7F060000
public const int provider_paths = 2131099648;
static Xml()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Xml()
{
}
}
}
}
#pragma warning restore 1591

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#ffffff" />
</item>
<item>
<bitmap android:tileMode="disabled" android:gravity="center" android:src="@drawable/splash_logos_crop"/>
</item>
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#2c3e50</color>
<color name="colorPrimaryDark">#1B3147</color>
<color name="colorAccent">#3498db</color>
</resources>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#2C3E50</color>
</resources>

View File

@ -1,4 +0,0 @@
<resources>
<string name="app_name">Loader</string>
<string name="action_settings">Settings</string>
</resources>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Splash" parent="android:Theme">
<item name="android:windowBackground">@drawable/splash_screen</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>
</resources>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
<files-path name="files" path="." />
<internal-path name="internal_files" path="." />
</paths>

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
</packages>

View File

@ -1,42 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
#nullable enable
namespace System.Linq
{
public static class EnumerableExtension
{
/// <summary>Produces a sequence of tuples with elements from the two specified sequences.</summary>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <returns>A sequence of tuples with elements taken from the first and second sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second)
{
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second");
return ZipIterator<TFirst, TSecond>(first, second);
}
#nullable disable
private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(
IEnumerable<TFirst> first,
IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
{
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
yield return (e1.Current, e2.Current);
}
}
}
}
}

View File

@ -1,23 +0,0 @@
namespace System;
using Internal.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
public static class MobileMemoryExtension
{
/// <summary>Indicates whether a specified value is found in a read-only span. Values are compared using IEquatable{T}.Equals(T).</summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The value to search for.</param>
/// <typeparam name="T">The type of the span.</typeparam>
/// <returns>
/// <see langword="true" /> if found, <see langword="false" /> otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>
{
return span.IndexOf(value) >= 0;
}
}

View File

@ -1,34 +0,0 @@
namespace System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
public static class MobileTypeExtension
{
/// <summary>Determines whether the current type can be assigned to a variable of the specified <paramref name="targetType" />.</summary>
/// <param name="targetType">The type to compare with the current type.</param>
/// <returns>
/// <see langword="true" /> if any of the following conditions is true:
///
/// - The current instance and <paramref name="targetType" /> represent the same type.
///
/// - The current type is derived either directly or indirectly from <paramref name="targetType" />. The current type is derived directly from <paramref name="targetType" /> if it inherits from <paramref name="targetType" />; the current type is derived indirectly from <paramref name="targetType" /> if it inherits from a succession of one or more classes that inherit from <paramref name="targetType" />.
///
/// - <paramref name="targetType" /> is an interface that the current type implements.
///
/// - The current type is a generic type parameter, and <paramref name="targetType" /> represents one of the constraints of the current type.
///
/// - The current type represents a value type, and <paramref name="targetType" /> represents <c>Nullable&lt;c&gt;</c> (<c>Nullable(Of c)</c> in Visual Basic).
///
/// <see langword="false" /> if none of these conditions are true, or if <paramref name="targetType" /> or <see langword="this" /> is <see langword="null" />.</returns>
public static bool IsAssignableTo(this Type type, [NotNullWhen(true)] Type? targetType) => (object) targetType != null && targetType.IsAssignableFrom(type);
}

View File

@ -1,29 +0,0 @@
using System.Runtime.CompilerServices;
#nullable enable
namespace System.Collections.Generic
{
/// <summary>An <see cref="T:System.Collections.Generic.IEqualityComparer`1" /> that uses reference equality (<see cref="M:System.Object.ReferenceEquals(System.Object,System.Object)" />) instead of value equality (<see cref="M:System.Object.Equals(System.Object)" />) when comparing two object instances.</summary>
public sealed class ReferenceEqualityComparer : IEqualityComparer<object?>, IEqualityComparer
{
private ReferenceEqualityComparer()
{
}
/// <summary>Gets the singleton <see cref="T:System.Collections.Generic.ReferenceEqualityComparer" /> instance.</summary>
public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer();
/// <summary>Determines whether two object references refer to the same object instance.</summary>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
/// <returns>
/// <see langword="true" /> if both <paramref name="x" /> and <paramref name="y" /> refer to the same object instance or if both are <see langword="null" />; otherwise, <see langword="false" />.</returns>
public bool Equals(object? x, object? y) => x == y;
/// <summary>Returns a hash code for the specified object. The returned hash code is based on the object identity, not on the contents of the object.</summary>
/// <param name="obj">The object for which to retrieve the hash code.</param>
/// <returns>A hash code for the identity of <paramref name="obj" />.</returns>
public int GetHashCode(object? obj) => RuntimeHelpers.GetHashCode(obj);
}
}

View File

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{E5FE4C97-1A7C-4A97-9904-7351CA020C7F}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MobilePatch</RootNamespace>
<AssemblyName>MobilePatch</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
<TargetFramework>net5.0-android</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Mobile\EnumerableExtension.cs" />
<Compile Include="Mobile\MobileMemoryExtension.cs" />
<Compile Include="Mobile\MobileTypeExtension.cs" />
<Compile Include="Mobile\ReferenceEqualityComparer.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\AboutResources.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\strings.xml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\drawable\" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MobilePatch")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MobilePatch")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -1,44 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.axml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable/
icon.png
layout/
main.axml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "R"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the R class would expose:
public class R {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
to reference the layout/main.axml file, or R.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

View File

@ -1,59 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("MobilePatch.Resource", IsApplication=false)]
namespace MobilePatch
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.2.0.99")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class String
{
// aapt resource value: 0x7F010000
public static int app_name = 2130771968;
// aapt resource value: 0x7F010001
public static int hello = 2130771969;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
}
}
#pragma warning restore 1591

View File

@ -1,4 +0,0 @@
<resources>
<string name="hello">Hello World, Click Me!</string>
<string name="app_name">MobilePatch</string>
</resources>

View File

@ -9,4 +9,4 @@
/// <summary>Remove SMAPI from the game directory.</summary>
Uninstall
}
}
}

View File

@ -1,65 +0,0 @@
using System.IO;
using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Toolkit.Framework.GameScanning;
using StardewModdingAPI.Toolkit.Utilities;
namespace StardewModdingAPI.Installer.Framework
{
/// <summary>The installer context.</summary>
internal class InstallerContext
{
/*********
** Fields
*********/
/// <summary>The underlying toolkit game scanner.</summary>
private readonly GameScanner GameScanner = new();
/*********
** Accessors
*********/
/// <summary>The current OS.</summary>
public Platform Platform { get; }
/// <summary>The human-readable OS name and version.</summary>
public string PlatformName { get; }
/// <summary>Whether the installer is running on Windows.</summary>
public bool IsWindows => this.Platform == Platform.Windows;
/// <summary>Whether the installer is running on a Unix OS (including Linux or macOS).</summary>
public bool IsUnix => !this.IsWindows;
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
public InstallerContext()
{
this.Platform = EnvironmentUtility.DetectPlatform();
this.PlatformName = EnvironmentUtility.GetFriendlyPlatformName(this.Platform);
}
/// <summary>Get the installer's version number.</summary>
public ISemanticVersion GetInstallerVersion()
{
var raw = this.GetType().Assembly.GetName().Version!;
return new SemanticVersion(raw);
}
/// <summary>Get whether a folder seems to contain the game files.</summary>
/// <param name="dir">The folder to check.</param>
public bool LooksLikeGameFolder(DirectoryInfo dir)
{
return this.GameScanner.LooksLikeGameFolder(dir);
}
/// <summary>Get whether a folder seems to contain the game, and which version it contains if so.</summary>
/// <param name="dir">The folder to check.</param>
public GameFolderType GetGameFolderType(DirectoryInfo dir)
{
return this.GameScanner.GetGameFolderType(dir);
}
}
}

View File

@ -1,90 +0,0 @@
using System.IO;
using StardewModdingAPI.Toolkit.Framework;
namespace StardewModdingAPI.Installer.Framework
{
/// <summary>Manages paths for the SMAPI installer.</summary>
internal class InstallerPaths
{
/*********
** Accessors
*********/
/****
** Main folders
****/
/// <summary>The directory path containing the files to copy into the game folder.</summary>
public DirectoryInfo BundleDir { get; }
/// <summary>The directory containing the installed game.</summary>
public DirectoryInfo GameDir { get; }
/// <summary>The directory into which to install mods.</summary>
public DirectoryInfo ModsDir { get; }
/****
** Installer paths
****/
/// <summary>The full path to directory path containing the files to copy into the game folder.</summary>
public string BundlePath => this.BundleDir.FullName;
/// <summary>The full path to the backup API user settings folder, if applicable.</summary>
public string BundleApiUserConfigPath { get; }
/****
** Game paths
****/
/// <summary>The full path to the directory containing the installed game.</summary>
public string GamePath => this.GameDir.FullName;
/// <summary>The full path to the directory into which to install mods.</summary>
public string ModsPath => this.ModsDir.FullName;
/// <summary>The full path to SMAPI's internal configuration file.</summary>
public string ApiConfigPath { get; }
/// <summary>The full path to the user's config overrides file.</summary>
public string ApiUserConfigPath { get; }
/// <summary>The full path to the installed game DLL.</summary>
public string GameDllPath { get; }
/// <summary>The full path to the installed SMAPI executable file.</summary>
public string UnixSmapiExecutablePath { get; }
/// <summary>The full path to the vanilla game launch script on Linux/macOS.</summary>
public string VanillaLaunchScriptPath { get; }
/// <summary>The full path to the installed SMAPI launch script on Linux/macOS before it's renamed.</summary>
public string NewLaunchScriptPath { get; }
/// <summary>The full path to the backed up game launch script on Linux/macOS after SMAPI is installed.</summary>
public string BackupLaunchScriptPath { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="bundleDir">The directory path containing the files to copy into the game folder.</param>
/// <param name="gameDir">The directory path for the installed game.</param>
public InstallerPaths(DirectoryInfo bundleDir, DirectoryInfo gameDir)
{
// base paths
this.BundleDir = bundleDir;
this.GameDir = gameDir;
this.ModsDir = new DirectoryInfo(Path.Combine(gameDir.FullName, "Mods"));
this.GameDllPath = Path.Combine(gameDir.FullName, Constants.GameDllName);
// launch scripts
this.VanillaLaunchScriptPath = Path.Combine(gameDir.FullName, "StardewValley");
this.NewLaunchScriptPath = Path.Combine(gameDir.FullName, "unix-launcher.sh");
this.BackupLaunchScriptPath = Path.Combine(gameDir.FullName, "StardewValley-original");
this.UnixSmapiExecutablePath = Path.Combine(gameDir.FullName, "StardewModdingAPI");
// internal files
this.BundleApiUserConfigPath = Path.Combine(bundleDir.FullName, "smapi-internal", "config.user.json");
this.ApiConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.json");
this.ApiUserConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.user.json");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Threading;
namespace StardewModdingApi.Installer
namespace StardewModdingApi.Installer
{
/// <summary>The entry point for SMAPI's install and uninstall console app.</summary>
internal class Program
{
/*********
** Fields
*********/
/// <summary>The absolute path of the installer folder.</summary>
[SuppressMessage("ReSharper", "AssignNullToNotNullAttribute", Justification = "The assembly location is never null in this context.")]
private static readonly string InstallerPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
/// <summary>The absolute path of the folder containing the unzipped installer files.</summary>
private static readonly string ExtractedBundlePath = Path.Combine(Path.GetTempPath(), $"SMAPI-installer-{Guid.NewGuid():N}");
/// <summary>The absolute path for referenced assemblies.</summary>
private static readonly string InternalFilesPath = Path.Combine(Program.ExtractedBundlePath, "smapi-internal");
/*********
** Public methods
*********/
@ -30,73 +10,8 @@ namespace StardewModdingApi.Installer
/// <param name="args">The command line arguments.</param>
public static void Main(string[] args)
{
// find install bundle
FileInfo zipFile = new(Path.Combine(Program.InstallerPath, "install.dat"));
if (!zipFile.Exists)
{
Console.WriteLine($"Oops! Some of the installer files are missing; try re-downloading the installer. (Missing file: {zipFile.FullName})");
Console.ReadLine();
return;
}
// unzip bundle into temp folder
DirectoryInfo bundleDir = new(Program.ExtractedBundlePath);
Console.WriteLine("Extracting install files...");
ZipFile.ExtractToDirectory(zipFile.FullName, bundleDir.FullName);
// set up assembly resolution
AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
// launch installer
var installer = new InteractiveInstaller(bundleDir.FullName);
try
{
installer.Run(args);
}
catch (Exception ex)
{
Program.PrintErrorAndExit($"The installer failed with an unexpected exception.\nIf you need help fixing this error, see https://smapi.io/help\n\n{ex}");
}
}
/*********
** Private methods
*********/
/// <summary>Method called when assembly resolution fails, which may return a manually resolved assembly.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private static Assembly? CurrentDomain_AssemblyResolve(object? sender, ResolveEventArgs e)
{
try
{
AssemblyName name = new(e.Name);
foreach (FileInfo dll in new DirectoryInfo(Program.InternalFilesPath).EnumerateFiles("*.dll"))
{
if (name.Name != null && name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.OrdinalIgnoreCase))
return Assembly.LoadFrom(dll.FullName);
}
return null;
}
catch (Exception ex)
{
Console.WriteLine($"Error resolving assembly: {ex}");
return null;
}
}
/// <summary>Write an error directly to the console and exit.</summary>
/// <param name="message">The error message to display.</param>
private static void PrintErrorAndExit(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ResetColor();
Console.WriteLine("Game has ended. Press any key to exit.");
Thread.Sleep(100);
Console.ReadKey();
Environment.Exit(0);
var installer = new InteractiveInstaller();
installer.Run(args);
}
}
}

View File

@ -0,0 +1,4 @@
using System.Reflection;
[assembly: AssemblyTitle("SMAPI.Installer")]
[assembly: AssemblyDescription("The SMAPI installer for players.")]

View File

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>StardewModdingAPI.Installer</RootNamespace>
<Description>The SMAPI installer for players.</Description>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Exe</OutputType>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SMAPI.Toolkit\SMAPI.Toolkit.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="assets\*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<Import Project="..\SMAPI.Internal\SMAPI.Internal.projitems" Label="Shared" />
<Import Project="..\..\build\common.targets" />
</Project>

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{443DDF81-6AAF-420A-A610-3459F37E5575}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>StardewModdingAPI.Installer</RootNamespace>
<AssemblyName>StardewModdingAPI.Installer</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)\..\bin\Debug\Installer</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)\..\bin\Release\Installer</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\build\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Enums\ScriptAction.cs" />
<Compile Include="InteractiveInstaller.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="readme.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="unix-install.sh">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="unix-launcher.sh">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="..\SMAPI.Internal\SMAPI.Internal.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\common.targets" />
<Import Project="..\..\build\prepare-install-package.targets" />
</Project>

View File

@ -1,4 +0,0 @@
#!/usr/bin/env bash
cd "`dirname "$0"`"
internal/linux/SMAPI.Installer

View File

@ -1,43 +0,0 @@
@echo off
setlocal enabledelayedexpansion
SET installerDir="%~dp0"
REM make sure we're not running within a zip folder
REM The error level is usually 0 (install dir contains temp path), 1 (it doesn't), or 9009 (findstr doesn't exist due to a Windows issue).
REM If the command doesn't exist, just skip this check.
echo %installerDir% | findstr /C:"%TEMP%" 1>nul 2>null
if %ERRORLEVEL% EQU 0 (
echo Oops! It looks like you're running the installer from inside a zip file. Make sure you unzip the download first.
echo.
pause
exit
)
REM make sure an antivirus hasn't deleted the installer DLL
if not exist %installerDir%"internal\windows\SMAPI.Installer.dll" (
echo Oops! SMAPI is missing one of its files. Your antivirus might have deleted it.
echo Missing file: %installerDir%internal\windows\SMAPI.Installer.dll
echo.
pause
exit
)
if not exist %installerDir%"internal\windows\SMAPI.Installer.exe" (
echo Oops! SMAPI is missing one of its files. Your antivirus might have deleted it.
echo Missing file: %installerDir%internal\windows\SMAPI.Installer.exe
echo.
pause
exit
)
REM start installer
internal\windows\SMAPI.Installer.exe
REM keep window open if it failed
if %ERRORLEVEL% NEQ 0 (
echo.
echo Oops! The SMAPI installer seems to have failed. The error details may be shown above.
echo.
pause
exit
)

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
cd "`dirname "$0"`"
xattr -r -d com.apple.quarantine internal
internal/macOS/SMAPI.Installer

View File

@ -1,17 +0,0 @@
{
"runtimeOptions": {
"tfm": "net5.0",
"includedFrameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "5.0.0",
"rollForward": "latestMinor"
}
],
"configProperties": {
// disable tiered runtime JIT: https://github.com/dotnet/runtime/blob/main/docs/design/features/tiered-compilation.md
// This is disabled by the base game, and causes issues with Harmony patches.
"System.Runtime.TieredCompilation": false
}
}
}

View File

@ -1,169 +0,0 @@
#!/usr/bin/env bash
##########
## Initial setup
##########
# move to script's directory
cd "$(dirname "$0")" || exit $?
# Whether to avoid opening a separate terminal window, and avoid logging anything to the console.
# This isn't recommended since you won't see errors, warnings, and update alerts.
SKIP_TERMINAL=false
# Whether to avoid opening a separate terminal, but still send the usual log output to the console.
USE_CURRENT_SHELL=false
##########
## Read environment variables
##########
if [ "$SMAPI_NO_TERMINAL" == "true" ]; then
SKIP_TERMINAL=true
fi
if [ "$SMAPI_USE_CURRENT_SHELL" == "true" ]; then
USE_CURRENT_SHELL=true
fi
##########
## Read command-line arguments
##########
while [ "$#" -gt 0 ]; do
case "$1" in
--skip-terminal ) SKIP_TERMINAL=true; shift ;;
--use-current-shell ) USE_CURRENT_SHELL=true; shift ;;
-- ) shift; break ;;
* ) shift ;;
esac
done
if [ "$SKIP_TERMINAL" == "true" ]; then
USE_CURRENT_SHELL=true
fi
##########
## Open terminal if needed
##########
# on macOS, make sure we're running in a Terminal
# Besides letting the player see errors/warnings/alerts in the console, this is also needed because
# Steam messes with the PATH.
if [ "$(uname)" == "Darwin" ]; then
if [ ! -t 1 ]; then # not open in Terminal (https://stackoverflow.com/q/911168/262123)
# reopen in Terminal if needed
# https://stackoverflow.com/a/29511052/262123
if [ "$USE_CURRENT_SHELL" == "false" ]; then
echo "Reopening in the Terminal app..."
echo '#!/bin/sh' > /tmp/open-smapi-terminal.command
echo "\"$0\" $@ --use-current-shell" >> /tmp/open-smapi-terminal.command
chmod +x /tmp/open-smapi-terminal.command
cat /tmp/open-smapi-terminal.command
open -W /tmp/open-smapi-terminal.command
rm /tmp/open-smapi-terminal.command
exit 0
fi
fi
fi
##########
## Validate assumptions
##########
# script must be run from the game folder
if [ ! -f "Stardew Valley.dll" ]; then
printf "Oops! SMAPI must be placed in the Stardew Valley game folder.\nSee instructions: https://stardewvalleywiki.com/Modding:Player_Guide";
read -r
exit 1
fi
##########
## Launch SMAPI
##########
# macOS
if [ "$(uname)" == "Darwin" ]; then
./StardewModdingAPI "$@"
# Linux
else
# choose binary file to launch
LAUNCH_FILE="./StardewModdingAPI"
export LAUNCH_FILE
# run in terminal
if [ "$USE_CURRENT_SHELL" == "false" ]; then
# 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 wezterm; do
if command -v "$terminal" 2>/dev/null; then
export TERMINAL_NAME=$terminal
break;
fi
done
# find the true shell behind x-terminal-emulator
if [ "$TERMINAL_NAME" = "x-terminal-emulator" ]; then
TERMINAL_NAME="$(basename "$(readlink -f "$(command -v x-terminal-emulator)")")"
export TERMINAL_NAME
fi
# run in selected terminal and account for quirks
TERMINAL_PATH="$(command -v "$TERMINAL_NAME")"
export TERMINAL_PATH
if [ -x "$TERMINAL_PATH" ]; then
case $TERMINAL_NAME in
terminal|termite)
# consumes only one argument after -e
# options containing space characters are unsupported
exec "$TERMINAL_NAME" -e "env TERM=xterm $LAUNCH_FILE $@"
;;
xterm|konsole|alacritty)
# consumes all arguments after -e
exec "$TERMINAL_NAME" -e env TERM=xterm $LAUNCH_FILE "$@"
;;
terminator|xfce4-terminal|mate-terminal)
# consumes all arguments after -x
exec "$TERMINAL_NAME" -x env TERM=xterm $LAUNCH_FILE "$@"
;;
gnome-terminal)
# consumes all arguments after --
exec "$TERMINAL_NAME" -- env TERM=xterm $LAUNCH_FILE "$@"
;;
wezterm)
# consumes all arguments after start --. does not copy working directory automatically, needs --cwd for that.
exec "$TERMINAL_NAME" start --cwd "$(pwd)" -- env TERM=xterm $LAUNCH_FILE "$@"
;;
kitty)
# consumes all trailing arguments
exec "$TERMINAL_NAME" env TERM=xterm $LAUNCH_FILE "$@"
;;
*)
# 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
# explicitly run without terminal
elif [ "$SKIP_TERMINAL" == "true" ]; then
exec $LAUNCH_FILE --no-terminal "$@"
else
exec $LAUNCH_FILE "$@"
fi
fi

View File

@ -1,5 +0,0 @@
<configuration>
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
</configuration>

View File

@ -14,34 +14,25 @@
SMAPI lets you run Stardew Valley with mods. Don't forget to download mods separately.
Automated install
Player's guide
--------------------------------
See https://stardewvalleywiki.com/Modding:Player_Guide for help installing SMAPI, adding mods, etc.
See https://stardewvalleywiki.com/Modding:Player_Guide.
Manual install
--------------------------------
THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See the 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.
1. Unzip "internal/windows/install.dat" (on Windows) or "internal/unix/install.dat" (on Linux or
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
`StardewModdingAPI.exe` file should be right next to the game's executable.
3. Copy `Stardew Valley.deps.json` in the game folder, and rename the copy to
`StardewModdingAPI.deps.json`.
1. Download the latest version of SMAPI: https://github.com/Pathoschild/SMAPI/releases.
2. Unzip the .zip file somewhere (not in your game folder).
3. Copy the files from the "internal/Windows" folder (on Windows) or "internal/Mono" folder (on
Linux/Mac) into your game folder. The `StardewModdingAPI.exe` file should be right next to the
game's executable.
4.
- 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.
- Linux/macOS only: rename the "StardewValley" file (no extension) to "StardewValley-original", and
- Linux/Mac only: rename the "StardewValley" file (no extension) to "StardewValley-original", and
"StardewModdingAPI" (no extension) to "StardewValley". Now just launch the game as usual to
play with mods.
When installing on Linux or macOS:
- To configure the color scheme, edit the `smapi-internal/config.json` file and see instructions
there for the 'ColorScheme' setting.

View File

@ -0,0 +1,21 @@
#!/bin/bash
# Run the SMAPI installer through Mono on Linux or Mac.
# Move to script's directory
cd "`dirname "$0"`"
# get cross-distro version of POSIX command
COMMAND=""
if command -v command >/dev/null 2>&1; then
COMMAND="command -v"
elif type type >/dev/null 2>&1; then
COMMAND="type"
fi
# validate Mono & run installer
if $COMMAND mono >/dev/null 2>&1; then
mono internal/Mono/install.exe
else
echo "Oops! Looks like Mono isn't installed. Please install Mono from http://mono-project.com, reboot, and run this installer again."
read
fi

View File

@ -0,0 +1,95 @@
#!/bin/bash
# MonoKickstart Shell Script
# Written by Ethan "flibitijibibo" Lee
# Modified for StardewModdingAPI by Viz and Pathoschild
# Move to script's directory
cd "`dirname "$0"`"
# Get the system architecture
UNAME=`uname`
ARCH=`uname -m`
# MonoKickstart picks the right libfolder, so just execute the right binary.
if [ "$UNAME" == "Darwin" ]; then
# ... Except on OSX.
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:./osx/
# El Capitan is a total idiot and wipes this variable out, making the
# Steam overlay disappear. This sidesteps "System Integrity Protection"
# and resets the variable with Valve's own variable (they provided this
# fix by the way, thanks Valve!). Note that you will need to update your
# launch configuration to the script location, NOT just the app location
# (i.e. Kick.app/Contents/MacOS/Kick, not just Kick.app).
# -flibit
if [ "$STEAM_DYLD_INSERT_LIBRARIES" != "" ] && [ "$DYLD_INSERT_LIBRARIES" == "" ]; then
export DYLD_INSERT_LIBRARIES="$STEAM_DYLD_INSERT_LIBRARIES"
fi
# this was here before
ln -sf mcs.bin.osx mcs
# fix "DllNotFoundException: libgdiplus.dylib" errors when loading images in SMAPI
if [ -f libgdiplus.dylib ]; then
rm libgdiplus.dylib
fi
if [ -f /Library/Frameworks/Mono.framework/Versions/Current/lib/libgdiplus.dylib ]; then
ln -s /Library/Frameworks/Mono.framework/Versions/Current/lib/libgdiplus.dylib libgdiplus.dylib
fi
# launch SMAPI
cp StardewValley.bin.osx StardewModdingAPI.bin.osx
open -a Terminal ./StardewModdingAPI.bin.osx $@
else
# choose launcher
LAUNCHER=""
if [ "$ARCH" == "x86_64" ]; then
ln -sf mcs.bin.x86_64 mcs
cp StardewValley.bin.x86_64 StardewModdingAPI.bin.x86_64
LAUNCHER="./StardewModdingAPI.bin.x86_64 $@"
else
ln -sf mcs.bin.x86 mcs
cp StardewValley.bin.x86 StardewModdingAPI.bin.x86
LAUNCHER="./StardewModdingAPI.bin.x86 $@"
fi
# 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"
fi
# open SMAPI in terminal
if $COMMAND x-terminal-emulator 2>/dev/null; then
# Terminator converts -e to -x when used through x-terminal-emulator for some reason (per
# `man terminator`), which causes an "unable to find shell" error. If x-terminal-emulator
# is mapped to Terminator, invoke it directly instead.
if [[ "$(readlink -e $(which x-terminal-emulator))" == *"/terminator" ]]; then
terminator -e "$LAUNCHER"
else
x-terminal-emulator -e "$LAUNCHER"
fi
elif $COMMAND xterm 2>/dev/null; then
xterm -e "$LAUNCHER"
elif $COMMAND xfce4-terminal 2>/dev/null; then
xfce4-terminal -e "$LAUNCHER"
elif $COMMAND gnome-terminal 2>/dev/null; then
gnome-terminal -e "$LAUNCHER"
elif $COMMAND konsole 2>/dev/null; then
konsole -e "$LAUNCHER"
elif $COMMAND terminal 2>/dev/null; then
terminal -e "$LAUNCHER"
else
$LAUNCHER
fi
# some Linux users get error 127 (command not found) from the above block, even though
# `command -v` indicates the command is valid. As a fallback, launch SMAPI without a terminal when
# that happens and pass in an argument indicating SMAPI shouldn't try writing to the terminal
# (which can be slow if there is none).
if [ $? -eq 127 ]; then
$LAUNCHER --no-terminal
fi
fi

View File

@ -1,54 +0,0 @@
using System;
using System.Reflection;
using HarmonyLib;
namespace StardewModdingAPI.Internal.Patching
{
/// <summary>Provides base implementation logic for <see cref="IPatcher"/> instances.</summary>
internal abstract class BasePatcher : IPatcher
{
/*********
** Public methods
*********/
/// <inheritdoc />
public abstract void Apply(Harmony harmony, IMonitor monitor);
/*********
** Protected methods
*********/
/// <summary>Get a method and assert that it was found.</summary>
/// <typeparam name="TTarget">The type containing the method.</typeparam>
/// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param>
protected ConstructorInfo RequireConstructor<TTarget>(params Type[] parameters)
{
return PatchHelper.RequireConstructor<TTarget>(parameters);
}
/// <summary>Get a method and assert that it was found.</summary>
/// <typeparam name="TTarget">The type containing the method.</typeparam>
/// <param name="name">The method name.</param>
/// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param>
/// <param name="generics">The method generic types, or <c>null</c> if it's not generic.</param>
protected MethodInfo RequireMethod<TTarget>(string name, Type[]? parameters = null, Type[]? generics = null)
{
return PatchHelper.RequireMethod<TTarget>(name, parameters, generics);
}
/// <summary>Get a Harmony patch method on the current patcher instance.</summary>
/// <param name="name">The method name.</param>
/// <param name="priority">The patch priority to apply, usually specified using Harmony's <see cref="Priority"/> enum, or <c>null</c> to keep the default value.</param>
protected HarmonyMethod GetHarmonyMethod(string name, int? priority = null)
{
HarmonyMethod method = new(
AccessTools.Method(this.GetType(), name)
?? throw new InvalidOperationException($"Can't find patcher method {PatchHelper.GetMethodString(this.GetType(), name)}.")
);
if (priority.HasValue)
method.priority = priority.Value;
return method;
}
}
}

View File

@ -1,36 +0,0 @@
using System;
using HarmonyLib;
namespace StardewModdingAPI.Internal.Patching
{
/// <summary>Simplifies applying <see cref="IPatcher"/> instances to the game.</summary>
internal static class HarmonyPatcher
{
/*********
** Public methods
*********/
/// <summary>Apply the given Harmony patchers.</summary>
/// <param name="id">The mod ID applying the patchers.</param>
/// <param name="monitor">The monitor with which to log any errors.</param>
/// <param name="patchers">The patchers to apply.</param>
public static Harmony Apply(string id, IMonitor monitor, params IPatcher[] patchers)
{
Harmony harmony = new(id);
foreach (IPatcher patcher in patchers)
{
try
{
patcher.Apply(harmony, monitor);
}
catch (Exception ex)
{
monitor.Log($"Couldn't apply runtime patch '{patcher.GetType().Name}' to the game. Some SMAPI features may not work correctly. See log file for details.", LogLevel.Error);
monitor.Log($"Technical details:\n{ex.GetLogSummary()}");
}
}
return harmony;
}
}
}

View File

@ -1,16 +0,0 @@
using HarmonyLib;
namespace StardewModdingAPI.Internal.Patching
{
/// <summary>A set of Harmony patches to apply.</summary>
internal interface IPatcher
{
/*********
** Public methods
*********/
/// <summary>Apply the Harmony patches for this instance.</summary>
/// <param name="harmony">The Harmony instance.</param>
/// <param name="monitor">The monitor with which to log any errors.</param>
public void Apply(Harmony harmony, IMonitor monitor);
}
}

View File

@ -1,77 +0,0 @@
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;
namespace StardewModdingAPI.Internal.Patching
{
/// <summary>Provides utility methods for patching game code with Harmony.</summary>
internal static class PatchHelper
{
/*********
** Public methods
*********/
/// <summary>Get a constructor and assert that it was found.</summary>
/// <typeparam name="TTarget">The type containing the method.</typeparam>
/// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param>
/// <exception cref="InvalidOperationException">The type has no matching constructor.</exception>
public static ConstructorInfo RequireConstructor<TTarget>(Type[]? parameters = null)
{
return
AccessTools.Constructor(typeof(TTarget), parameters)
?? throw new InvalidOperationException($"Can't find constructor {PatchHelper.GetMethodString(typeof(TTarget), null, parameters)} to patch.");
}
/// <summary>Get a method and assert that it was found.</summary>
/// <typeparam name="TTarget">The type containing the method.</typeparam>
/// <param name="name">The method name.</param>
/// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param>
/// <param name="generics">The method generic types, or <c>null</c> if it's not generic.</param>
/// <exception cref="InvalidOperationException">The type has no matching method.</exception>
public static MethodInfo RequireMethod<TTarget>(string name, Type[]? parameters = null, Type[]? generics = null)
{
return
AccessTools.Method(typeof(TTarget), name, parameters, generics)
?? throw new InvalidOperationException($"Can't find method {PatchHelper.GetMethodString(typeof(TTarget), name, parameters, generics)} to patch.");
}
/// <summary>Get a human-readable representation of a method target.</summary>
/// <param name="type">The type containing the method.</param>
/// <param name="name">The method name, or <c>null</c> for a constructor.</param>
/// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param>
/// <param name="generics">The method generic types, or <c>null</c> if it's not generic.</param>
public static string GetMethodString(Type type, string? name, Type[]? parameters = null, Type[]? generics = null)
{
StringBuilder str = new();
// type
str.Append(type.FullName);
// method name (if not constructor)
if (name != null)
{
str.Append('.');
str.Append(name);
}
// generics
if (generics?.Any() == true)
{
str.Append('<');
str.Append(string.Join(", ", generics.Select(p => p.FullName)));
str.Append('>');
}
// parameters
if (parameters?.Any() == true)
{
str.Append('(');
str.Append(string.Join(", ", parameters.Select(p => p.FullName)));
str.Append(')');
}
return str.ToString();
}
}
}

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>6c16e948-3e5c-47a7-bf4b-07a7469a87a5</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>SMAPI.Internal.Patching</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)BasePatcher.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HarmonyPatcher.cs" />
<Compile Include="$(MSBuildThisFileDirectory)IPatcher.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PatchHelper.cs" />
</ItemGroup>
</Project>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>6c16e948-3e5c-47a7-bf4b-07a7469a87a5</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<Import Project="SMAPI.Internal.Patching.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

View File

@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
namespace StardewModdingAPI.Internal.ConsoleWriting
{
/// <summary>The console color scheme options.</summary>
internal class ColorSchemeConfig
{
/*********
** Accessors
*********/
/// <summary>The default color scheme ID to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</summary>
public MonitorColorScheme UseScheme { get; }
/// <summary>The available console color schemes.</summary>
public IDictionary<MonitorColorScheme, IDictionary<ConsoleLogLevel, ConsoleColor>> Schemes { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="useScheme">The default color scheme ID to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
/// <param name="schemes">The available console color schemes.</param>
public ColorSchemeConfig(MonitorColorScheme useScheme, IDictionary<MonitorColorScheme, IDictionary<ConsoleLogLevel, ConsoleColor>> schemes)
{
this.UseScheme = useScheme;
this.Schemes = schemes;
}
}
}

View File

@ -1,24 +1,19 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using StardewModdingAPI.Toolkit.Utilities;
namespace StardewModdingAPI.Internal.ConsoleWriting
{
/// <summary>Writes color-coded text to the console.</summary>
internal class ColorfulConsoleWriter : IConsoleWriter
/// <summary>Provides a wrapper for writing color-coded text to the console.</summary>
internal class ColorfulConsoleWriter
{
/*********
** Fields
** Properties
*********/
/// <summary>The console text color for each log level.</summary>
private readonly IDictionary<ConsoleLogLevel, ConsoleColor>? Colors;
private readonly IDictionary<ConsoleLogLevel, ConsoleColor> Colors;
/// <summary>Whether the current console supports color formatting.</summary>
#if !SMAPI_FOR_MOBILE
[MemberNotNullWhen(true, nameof(ColorfulConsoleWriter.Colors))]
#endif
private bool SupportsColor { get; }
private readonly bool SupportsColor;
/*********
@ -26,24 +21,11 @@ namespace StardewModdingAPI.Internal.ConsoleWriting
*********/
/// <summary>Construct an instance.</summary>
/// <param name="platform">The target platform.</param>
public ColorfulConsoleWriter(Platform platform)
: this(platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.AutoDetect)) { }
/// <summary>Construct an instance.</summary>
/// <param name="platform">The target platform.</param>
/// <param name="colorConfig">The colors to use for text written to the SMAPI console.</param>
public ColorfulConsoleWriter(Platform platform, ColorSchemeConfig colorConfig)
/// <param name="colorScheme">The console color scheme to use.</param>
public ColorfulConsoleWriter(Platform platform, MonitorColorScheme colorScheme)
{
if (colorConfig.UseScheme == MonitorColorScheme.None)
{
this.SupportsColor = false;
this.Colors = null;
}
else
{
this.SupportsColor = this.TestColorSupport();
this.Colors = this.GetConsoleColorScheme(platform, colorConfig);
}
this.SupportsColor = this.TestColorSupport();
this.Colors = this.GetConsoleColorScheme(platform, colorScheme);
}
/// <summary>Write a message line to the log.</summary>
@ -71,39 +53,6 @@ namespace StardewModdingAPI.Internal.ConsoleWriting
Console.WriteLine(message);
}
/// <summary>Get the default color scheme config for cases where it's not configurable (e.g. the installer).</summary>
/// <param name="useScheme">The default color scheme ID to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
/// <remarks>The colors here should be kept in sync with the SMAPI config file.</remarks>
public static ColorSchemeConfig GetDefaultColorSchemeConfig(MonitorColorScheme useScheme)
{
return new ColorSchemeConfig(
useScheme: useScheme,
schemes: new Dictionary<MonitorColorScheme, IDictionary<ConsoleLogLevel, ConsoleColor>>
{
[MonitorColorScheme.DarkBackground] = new Dictionary<ConsoleLogLevel, ConsoleColor>
{
[ConsoleLogLevel.Trace] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Debug] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Info] = ConsoleColor.White,
[ConsoleLogLevel.Warn] = ConsoleColor.Yellow,
[ConsoleLogLevel.Error] = ConsoleColor.Red,
[ConsoleLogLevel.Alert] = ConsoleColor.Magenta,
[ConsoleLogLevel.Success] = ConsoleColor.DarkGreen
},
[MonitorColorScheme.LightBackground] = new Dictionary<ConsoleLogLevel, ConsoleColor>
{
[ConsoleLogLevel.Trace] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Debug] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Info] = ConsoleColor.Black,
[ConsoleLogLevel.Warn] = ConsoleColor.DarkYellow,
[ConsoleLogLevel.Error] = ConsoleColor.Red,
[ConsoleLogLevel.Alert] = ConsoleColor.DarkMagenta,
[ConsoleLogLevel.Success] = ConsoleColor.DarkGreen
}
}
);
}
/*********
** Private methods
@ -124,22 +73,47 @@ namespace StardewModdingAPI.Internal.ConsoleWriting
/// <summary>Get the color scheme to use for the current console.</summary>
/// <param name="platform">The target platform.</param>
/// <param name="colorConfig">The colors to use for text written to the SMAPI console.</param>
private IDictionary<ConsoleLogLevel, ConsoleColor> GetConsoleColorScheme(Platform platform, ColorSchemeConfig colorConfig)
/// <param name="colorScheme">The console color scheme to use.</param>
private IDictionary<ConsoleLogLevel, ConsoleColor> GetConsoleColorScheme(Platform platform, MonitorColorScheme colorScheme)
{
// get color scheme ID
MonitorColorScheme schemeID = colorConfig.UseScheme;
if (schemeID == MonitorColorScheme.AutoDetect)
// auto detect color scheme
if (colorScheme == MonitorColorScheme.AutoDetect)
{
schemeID = platform == Platform.Mac
? MonitorColorScheme.LightBackground // macOS doesn't provide console background color info, but it's usually white.
colorScheme = platform == Platform.Mac
? MonitorColorScheme.LightBackground // MacOS doesn't provide console background color info, but it's usually white.
: ColorfulConsoleWriter.IsDark(Console.BackgroundColor) ? MonitorColorScheme.DarkBackground : MonitorColorScheme.LightBackground;
}
// get colors for scheme
return colorConfig.Schemes.TryGetValue(schemeID, out IDictionary<ConsoleLogLevel, ConsoleColor>? scheme)
? scheme
: throw new NotSupportedException($"Unknown color scheme '{schemeID}'.");
switch (colorScheme)
{
case MonitorColorScheme.DarkBackground:
return new Dictionary<ConsoleLogLevel, ConsoleColor>
{
[ConsoleLogLevel.Trace] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Debug] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Info] = ConsoleColor.White,
[ConsoleLogLevel.Warn] = ConsoleColor.Yellow,
[ConsoleLogLevel.Error] = ConsoleColor.Red,
[ConsoleLogLevel.Alert] = ConsoleColor.Magenta,
[ConsoleLogLevel.Success] = ConsoleColor.DarkGreen
};
case MonitorColorScheme.LightBackground:
return new Dictionary<ConsoleLogLevel, ConsoleColor>
{
[ConsoleLogLevel.Trace] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Debug] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Info] = ConsoleColor.Black,
[ConsoleLogLevel.Warn] = ConsoleColor.DarkYellow,
[ConsoleLogLevel.Error] = ConsoleColor.Red,
[ConsoleLogLevel.Alert] = ConsoleColor.DarkMagenta,
[ConsoleLogLevel.Success] = ConsoleColor.DarkGreen
};
default:
throw new NotSupportedException($"Unknown color scheme '{colorScheme}'.");
}
}
/// <summary>Get whether a console color should be considered dark, which is subjectively defined as 'white looks better than black on this text'.</summary>
@ -151,7 +125,7 @@ namespace StardewModdingAPI.Internal.ConsoleWriting
case ConsoleColor.Black:
case ConsoleColor.Blue:
case ConsoleColor.DarkBlue:
case ConsoleColor.DarkMagenta: // PowerShell
case ConsoleColor.DarkMagenta: // Powershell
case ConsoleColor.DarkRed:
case ConsoleColor.Red:
return true;

View File

@ -1,11 +0,0 @@
namespace StardewModdingAPI.Internal.ConsoleWriting
{
/// <summary>Writes text to the console.</summary>
internal interface IConsoleWriter
{
/// <summary>Write a message line to the log.</summary>
/// <param name="message">The message to log.</param>
/// <param name="level">The log level.</param>
void WriteLine(string message, ConsoleLogLevel level);
}
}

View File

@ -10,9 +10,6 @@ namespace StardewModdingAPI.Internal.ConsoleWriting
DarkBackground,
/// <summary>Use darker text colors that look better on a white or light background.</summary>
LightBackground,
/// <summary>Disable console color.</summary>
None
LightBackground
}
}

View File

@ -0,0 +1,112 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
#if SMAPI_FOR_WINDOWS
using System.Management;
#endif
using System.Runtime.InteropServices;
namespace StardewModdingAPI.Internal
{
/// <summary>Provides methods for fetching environment information.</summary>
internal static class EnvironmentUtility
{
/*********
** Properties
*********/
/// <summary>Get the OS name from the system uname command.</summary>
/// <param name="buffer">The buffer to fill with the resulting string.</param>
[DllImport("libc")]
static extern int uname(IntPtr buffer);
/*********
** Public methods
*********/
/// <summary>Detect the current OS.</summary>
public static Platform DetectPlatform()
{
switch (Environment.OSVersion.Platform)
{
case PlatformID.MacOSX:
return Platform.Mac;
case PlatformID.Unix:
return EnvironmentUtility.IsRunningMac()
? Platform.Mac
: Platform.Linux;
default:
return Platform.Windows;
}
}
/// <summary>Get the human-readable OS name and version.</summary>
/// <param name="platform">The current platform.</param>
[SuppressMessage("ReSharper", "EmptyGeneralCatchClause", Justification = "Error suppressed deliberately to fallback to default behaviour.")]
public static string GetFriendlyPlatformName(Platform platform)
{
#if SMAPI_FOR_WINDOWS
try
{
return new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem")
.Get()
.Cast<ManagementObject>()
.Select(entry => entry.GetPropertyValue("Caption").ToString())
.FirstOrDefault();
}
catch { }
#endif
return (platform == Platform.Mac ? "MacOS " : "") + Environment.OSVersion;
}
/// <summary>Get the name of the Stardew Valley executable.</summary>
/// <param name="platform">The current platform.</param>
public static string GetExecutableName(Platform platform)
{
return platform == Platform.Windows
? "Stardew Valley.exe"
: "StardewValley.exe";
}
/// <summary>Get whether the platform uses Mono.</summary>
/// <param name="platform">The current platform.</param>
public static bool IsMono(this Platform platform)
{
return platform == Platform.Linux || platform == Platform.Mac;
}
/*********
** Private methods
*********/
/// <summary>Detect whether the code is running on Mac.</summary>
/// <remarks>
/// This code is derived from the Mono project (see System.Windows.Forms/System.Windows.Forms/XplatUI.cs). It detects Mac by calling the
/// <c>uname</c> system command and checking the response, which is always 'Darwin' for MacOS.
/// </remarks>
private static bool IsRunningMac()
{
IntPtr buffer = IntPtr.Zero;
try
{
buffer = Marshal.AllocHGlobal(8192);
if (EnvironmentUtility.uname(buffer) == 0)
{
string os = Marshal.PtrToStringAnsi(buffer);
return os == "Darwin";
}
return false;
}
catch
{
return false; // default to Linux
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
}
}

View File

@ -1,67 +0,0 @@
using System;
using System.Reflection;
using System.Text.RegularExpressions;
namespace StardewModdingAPI.Internal
{
/// <summary>Provides extension methods for handling exceptions.</summary>
internal static class ExceptionHelper
{
/*********
** Public methods
*********/
/// <summary>Get a string representation of an exception suitable for writing to the error log.</summary>
/// <param name="exception">The error to summarize.</param>
public static string GetLogSummary(this Exception? exception)
{
try
{
string message;
switch (exception)
{
case TypeLoadException ex:
message = $"Failed loading type '{ex.TypeName}': {exception}";
break;
case ReflectionTypeLoadException ex:
string summary = ex.ToString();
foreach (Exception? childEx in ex.LoaderExceptions)
summary += $"\n\n{childEx?.GetLogSummary()}";
message = summary;
break;
default:
message = exception?.ToString() ?? $"<null exception>\n{Environment.StackTrace}";
break;
}
return ExceptionHelper.SimplifyExtensionMessage(message);
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed handling {exception?.GetType().FullName} (original message: {exception?.Message})", ex);
}
}
/// <summary>Simplify common patterns in exception log messages that don't convey useful info.</summary>
/// <param name="message">The log message to simplify.</param>
public static string SimplifyExtensionMessage(string message)
{
// remove namespace for core exception types
message = Regex.Replace(
message,
@"(?:StardewModdingAPI\.Framework\.Exceptions|Microsoft\.Xna\.Framework|System|System\.IO)\.([a-zA-Z]+Exception):",
"$1:"
);
// remove unneeded root build paths for SMAPI and Stardew Valley
message = message
.Replace(@"E:\source\_Stardew\SMAPI\src\", "")
.Replace(@"C:\GitlabRunner\builds\Gq5qA5P4\0\ConcernedApe\", "");
// remove placeholder info in Linux/macOS stack traces
return message
.Replace(@"<filename unknown>:0", "");
}
}
}

View File

@ -0,0 +1,56 @@
namespace StardewModdingAPI.Internal.Models
{
/// <summary>Generic metadata about a mod.</summary>
internal class ModInfoModel
{
/*********
** Accessors
*********/
/// <summary>The mod name.</summary>
public string Name { get; set; }
/// <summary>The semantic version for the mod's latest release.</summary>
public string Version { get; set; }
/// <summary>The semantic version for the mod's latest preview release, if available and different from <see cref="Version"/>.</summary>
public string PreviewVersion { get; set; }
/// <summary>The mod's web URL.</summary>
public string Url { get; set; }
/// <summary>The error message indicating why the mod is invalid (if applicable).</summary>
public string Error { get; set; }
/*********
** Public methods
*********/
/// <summary>Construct an empty instance.</summary>
public ModInfoModel()
{
// needed for JSON deserialising
}
/// <summary>Construct an instance.</summary>
/// <param name="name">The mod name.</param>
/// <param name="version">The semantic version for the mod's latest release.</param>
/// <param name="previewVersion">The semantic version for the mod's latest preview release, if available and different from <see cref="Version"/>.</param>
/// <param name="url">The mod's web URL.</param>
/// <param name="error">The error message indicating why the mod is invalid (if applicable).</param>
public ModInfoModel(string name, string version, string url, string previewVersion = null, string error = null)
{
this.Name = name;
this.Version = version;
this.PreviewVersion = previewVersion;
this.Url = url;
this.Error = error; // mainly initialised here for the JSON deserialiser
}
/// <summary>Construct an instance.</summary>
/// <param name="error">The error message indicating why the mod is invalid.</param>
public ModInfoModel(string error)
{
this.Error = error;
}
}
}

Some files were not shown because too many files have changed in this diff Show More