Merge branch 'develop' into stable
This commit is contained in:
commit
3342502993
|
@ -11,6 +11,7 @@
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
|
|
||||||
# Visual Studio cache/options
|
# Visual Studio cache/options
|
||||||
|
.config/
|
||||||
.vs/
|
.vs/
|
||||||
|
|
||||||
# ReSharper
|
# ReSharper
|
||||||
|
@ -32,3 +33,4 @@ appsettings.Development.json
|
||||||
|
|
||||||
# Azure generated files
|
# Azure generated files
|
||||||
src/SMAPI.Web/Properties/PublishProfiles/*.pubxml
|
src/SMAPI.Web/Properties/PublishProfiles/*.pubxml
|
||||||
|
src/SMAPI.Web/Properties/ServiceDependencies/* - Web Deploy/
|
||||||
|
|
Binary file not shown.
|
@ -1,13 +1,13 @@
|
||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!--set general build properties -->
|
<!--set general build properties -->
|
||||||
<Version>3.12.8</Version>
|
<Version>3.13.0</Version>
|
||||||
<Product>SMAPI</Product>
|
<Product>SMAPI</Product>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
|
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
|
||||||
|
|
||||||
<!--set platform-->
|
<!--set platform-->
|
||||||
<DefineConstants Condition="$(OS) == 'Windows_NT'">$(DefineConstants);SMAPI_FOR_WINDOWS;SMAPI_FOR_XNA</DefineConstants>
|
<DefineConstants Condition="$(OS) == 'Windows_NT'">$(DefineConstants);SMAPI_FOR_WINDOWS</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!--find game folder-->
|
<!--find game folder-->
|
||||||
|
@ -27,20 +27,31 @@
|
||||||
<TranslationFiles Include="$(TargetDir)\i18n\*.json" />
|
<TranslationFiles Include="$(TargetDir)\i18n\*.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" />
|
<!-- SMAPI -->
|
||||||
|
<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).pdb" DestinationFolder="$(GamePath)" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" />
|
||||||
<Copy SourceFiles="$(TargetDir)\SMAPI.config.json" DestinationFiles="$(GamePath)\smapi-internal\config.json" />
|
<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)\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="@(TranslationFiles)" DestinationFolder="$(GamePath)\smapi-internal\i18n" />
|
||||||
|
|
||||||
|
<!-- Harmony + dependencies -->
|
||||||
<Copy SourceFiles="$(TargetDir)\0Harmony.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\0Harmony.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\0Harmony.xml" 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.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.Mdb.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)\Mono.Cecil.Pdb.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\MonoMod.Common.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\MonoMod.Common.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
|
||||||
<Copy SourceFiles="$(TargetDir)\TMXTile.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
<!-- .NET dependencies -->
|
||||||
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\smapi-internal\i18n" />
|
<Copy SourceFiles="$(TargetDir)\System.Configuration.ConfigurationManager.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\System.Management.dll" DestinationFolder="$(GamePath)\smapi-internal" Condition="$(OS) == 'Windows_NT'" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\System.Runtime.Caching.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\System.Security.Permissions.dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="CopyDefaultMods" Condition="'$(MSBuildProjectName)' == 'SMAPI.Mods.ConsoleCommands' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.ErrorHandler' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.SaveBackup'">
|
<Target Name="CopyDefaultMods" Condition="'$(MSBuildProjectName)' == 'SMAPI.Mods.ConsoleCommands' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.ErrorHandler' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.SaveBackup'">
|
||||||
|
@ -54,13 +65,13 @@
|
||||||
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" />
|
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit' AND $(TargetFramework) == 'net452'" AfterTargets="PostBuildEvent">
|
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit'" AfterTargets="PostBuildEvent">
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces' AND $(TargetFramework) == 'net452'" AfterTargets="PostBuildEvent">
|
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces'" AfterTargets="PostBuildEvent">
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
|
||||||
|
|
|
@ -41,14 +41,4 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</When>
|
</When>
|
||||||
</Choose>
|
</Choose>
|
||||||
|
|
||||||
<!-- set game metadata -->
|
|
||||||
<PropertyGroup>
|
|
||||||
<!--standard executable name-->
|
|
||||||
<GameExecutableName>Stardew Valley</GameExecutableName>
|
|
||||||
<GameExecutableName Condition="$(OS) != 'Windows_NT'">StardewValley</GameExecutableName>
|
|
||||||
|
|
||||||
<!--Linux install on Windows (for 64-bit hack)-->
|
|
||||||
<GameExecutableName Condition="$(OS) == 'Windows_NT' AND !Exists('$(GamePath)\$(GameExecutableName).exe') AND Exists('$(GamePath)\StardewValley.exe')">StardewValley</GameExecutableName>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<OutRootPath>$(SolutionDir)\..\bin</OutRootPath>
|
<OutRootPath>$(SolutionDir)\..\bin</OutRootPath>
|
||||||
|
|
||||||
<SmapiBin>$(BuildRootPath)\SMAPI\bin\$(Configuration)</SmapiBin>
|
<SmapiBin>$(BuildRootPath)\SMAPI\bin\$(Configuration)</SmapiBin>
|
||||||
<ToolkitBin>$(BuildRootPath)\SMAPI.Toolkit\bin\$(Configuration)\net452</ToolkitBin>
|
<ToolkitBin>$(BuildRootPath)\SMAPI.Toolkit\bin\$(Configuration)\net5.0</ToolkitBin>
|
||||||
<ConsoleCommandsBin>$(BuildRootPath)\SMAPI.Mods.ConsoleCommands\bin\$(Configuration)</ConsoleCommandsBin>
|
<ConsoleCommandsBin>$(BuildRootPath)\SMAPI.Mods.ConsoleCommands\bin\$(Configuration)</ConsoleCommandsBin>
|
||||||
<ErrorHandlerBin>$(BuildRootPath)\SMAPI.Mods.ErrorHandler\bin\$(Configuration)</ErrorHandlerBin>
|
<ErrorHandlerBin>$(BuildRootPath)\SMAPI.Mods.ErrorHandler\bin\$(Configuration)</ErrorHandlerBin>
|
||||||
<SaveBackupBin>$(BuildRootPath)\SMAPI.Mods.SaveBackup\bin\$(Configuration)</SaveBackupBin>
|
<SaveBackupBin>$(BuildRootPath)\SMAPI.Mods.SaveBackup\bin\$(Configuration)</SaveBackupBin>
|
||||||
|
@ -35,12 +35,16 @@
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on Linux.sh" />
|
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on Linux.sh" />
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on macOS.command" />
|
<Copy SourceFiles="$(TargetDir)\assets\unix-install.sh" DestinationFiles="$(PackagePath)\install on macOS.command" />
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\windows-install.bat" DestinationFiles="$(PackagePath)\install on Windows.bat" />
|
<Copy SourceFiles="$(TargetDir)\assets\windows-install.bat" DestinationFiles="$(PackagePath)\install on Windows.bat" />
|
||||||
<Copy SourceFiles="$(TargetDir)\assets\README.txt" DestinationFiles="$(PackagePath)\README.txt" />
|
<Copy SourceFiles="$(TargetDir)\assets\README.txt" DestinationFolder="$(PackagePath)" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(PackagePath)\internal\$(PlatformName)-install.exe" />
|
<Copy SourceFiles="$(TargetDir)\assets\windows-exe-config.xml" DestinationFiles="$(PackagePath)\internal\$(PlatformName)\install.exe.config" Condition="$(PlatformName) == 'windows'" />
|
||||||
<Copy Condition="$(PlatformName) == 'windows'" SourceFiles="$(TargetDir)\assets\windows-exe-config.xml" DestinationFiles="$(PackagePath)\internal\$(PlatformName)-install.exe.config" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(PackagePath)\internal\$(PlatformName)" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).runtimeconfig.json" DestinationFolder="$(PackagePath)\internal\$(PlatformName)" />
|
||||||
|
|
||||||
<!--copy bundle files-->
|
<!--copy bundle files-->
|
||||||
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI.exe" DestinationFolder="$(PackagePath)\bundle" />
|
<Copy SourceFiles="$(TargetDir)\assets\runtimeconfig.$(PlatformName).json" DestinationFiles="$(PackagePath)\bundle\StardewModdingAPI.runtimeconfig.json" />
|
||||||
|
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI.dll" DestinationFolder="$(PackagePath)\bundle" />
|
||||||
|
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI.exe" DestinationFolder="$(PackagePath)\bundle" Condition="$(PlatformName) == 'windows'" />
|
||||||
|
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI" DestinationFolder="$(PackagePath)\bundle" Condition="$(PlatformName) != 'windows'" />
|
||||||
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI.pdb" DestinationFolder="$(PackagePath)\bundle" />
|
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI.pdb" DestinationFolder="$(PackagePath)\bundle" />
|
||||||
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI.xml" DestinationFolder="$(PackagePath)\bundle" />
|
<Copy SourceFiles="$(SmapiBin)\StardewModdingAPI.xml" DestinationFolder="$(PackagePath)\bundle" />
|
||||||
<Copy SourceFiles="$(SmapiBin)\steam_appid.txt" DestinationFolder="$(PackagePath)\bundle" />
|
<Copy SourceFiles="$(SmapiBin)\steam_appid.txt" DestinationFolder="$(PackagePath)\bundle" />
|
||||||
|
@ -61,11 +65,16 @@
|
||||||
<Copy SourceFiles="$(ToolkitBin)\SMAPI.Toolkit.CoreInterfaces.pdb" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
<Copy SourceFiles="$(ToolkitBin)\SMAPI.Toolkit.CoreInterfaces.pdb" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
<Copy SourceFiles="$(ToolkitBin)\SMAPI.Toolkit.CoreInterfaces.xml" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
<Copy SourceFiles="$(ToolkitBin)\SMAPI.Toolkit.CoreInterfaces.xml" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(PackagePath)\bundle\smapi-internal\i18n" />
|
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(PackagePath)\bundle\smapi-internal\i18n" />
|
||||||
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\unix-launcher.sh" DestinationFiles="$(PackagePath)\bundle\StardewModdingAPI" />
|
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\unix-launcher.sh" DestinationFolder="$(PackagePath)\bundle" />
|
||||||
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(SmapiBin)\System.Numerics.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
|
||||||
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(SmapiBin)\System.Runtime.Caching.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(SmapiBin)\System.Runtime.Caching.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
<Copy Condition="$(PlatformName) == 'windows'" SourceFiles="$(TargetDir)\assets\windows-exe-config.xml" DestinationFiles="$(PackagePath)\bundle\StardewModdingAPI.exe.config" />
|
<Copy Condition="$(PlatformName) == 'windows'" SourceFiles="$(TargetDir)\assets\windows-exe-config.xml" DestinationFiles="$(PackagePath)\bundle\StardewModdingAPI.exe.config" />
|
||||||
|
|
||||||
|
<!-- copy .NET dependencies -->
|
||||||
|
<Copy SourceFiles="$(SmapiBin)\System.Configuration.ConfigurationManager.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
|
<Copy SourceFiles="$(SmapiBin)\System.Management.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" Condition="$(PlatformName) == 'windows'" />
|
||||||
|
<Copy SourceFiles="$(SmapiBin)\System.Runtime.Caching.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
|
<Copy SourceFiles="$(SmapiBin)\System.Security.Permissions.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
||||||
|
|
||||||
<!--copy bundled mods-->
|
<!--copy bundled mods-->
|
||||||
<Copy SourceFiles="$(ConsoleCommandsBin)\ConsoleCommands.dll" DestinationFolder="$(PackagePath)\bundle\Mods\ConsoleCommands" />
|
<Copy SourceFiles="$(ConsoleCommandsBin)\ConsoleCommands.dll" DestinationFolder="$(PackagePath)\bundle\Mods\ConsoleCommands" />
|
||||||
<Copy SourceFiles="$(ConsoleCommandsBin)\ConsoleCommands.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\ConsoleCommands" />
|
<Copy SourceFiles="$(ConsoleCommandsBin)\ConsoleCommands.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\ConsoleCommands" />
|
||||||
|
@ -78,63 +87,33 @@
|
||||||
<Copy SourceFiles="$(SaveBackupBin)\SaveBackup.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
<Copy SourceFiles="$(SaveBackupBin)\SaveBackup.pdb" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
||||||
<Copy SourceFiles="$(SaveBackupBin)\manifest.json" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
<Copy SourceFiles="$(SaveBackupBin)\manifest.json" DestinationFolder="$(PackagePath)\bundle\Mods\SaveBackup" />
|
||||||
|
|
||||||
<!-- fix errors on Linux/macOS (sample: https://smapi.io/log/mMdFUpgB) -->
|
|
||||||
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\System.Numerics.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
|
||||||
<Copy Condition="$(PlatformName) == 'unix'" SourceFiles="$(TargetDir)\assets\System.Runtime.Caching.dll" DestinationFolder="$(PackagePath)\bundle\smapi-internal" />
|
|
||||||
|
|
||||||
<!-- fix Linux/macOS permissions -->
|
<!-- fix Linux/macOS permissions -->
|
||||||
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)\install on Linux.sh"" />
|
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)/install on Linux.sh"" />
|
||||||
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)\install on macOS.command"" />
|
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)/install on macOS.command"" />
|
||||||
|
<Exec Condition="$(PlatformName) == 'unix'" Command="chmod 755 "$(PackagePath)/bundle/unix-launcher.sh"" />
|
||||||
|
|
||||||
<!-- finalise 'for developers' installer -->
|
<!-- finalise 'for developers' installer -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageFiles Include="$(PackagePath)\**\*.*" />
|
<PackageFiles Include="$(PackagePath)\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Copy SourceFiles="@(PackageFiles)" DestinationFolder="$(PackageDevPath)\%(RecursiveDir)" />
|
<Copy SourceFiles="@(PackageFiles)" DestinationFolder="$(PackageDevPath)\%(RecursiveDir)" />
|
||||||
<ZipDirectory FromDirPath="$(PackageDevPath)\bundle" ToFilePath="$(PackageDevPath)\internal\$(PlatformName)-install.dat" />
|
<ZipDirectory SourceDirectory="$(PackageDevPath)\bundle" DestinationFile="$(PackageDevPath)\internal\$(PlatformName)\install.dat" />
|
||||||
<RemoveDir Directories="$(PackageDevPath)\bundle" />
|
<RemoveDir Directories="$(PackageDevPath)\bundle" />
|
||||||
|
|
||||||
<!-- finalise normal installer -->
|
<!-- finalise normal installer -->
|
||||||
<ReplaceFileText FilePath="$(PackagePath)\bundle\smapi-internal\config.json" Search=""DeveloperMode": true" Replace=""DeveloperMode": false" />
|
<ReplaceFileText FilePath="$(PackagePath)\bundle\smapi-internal\config.json" Search=""DeveloperMode": true" Replace=""DeveloperMode": false" />
|
||||||
<ZipDirectory FromDirPath="$(PackagePath)\bundle" ToFilePath="$(PackagePath)\internal\$(PlatformName)-install.dat" />
|
<ZipDirectory SourceDirectory="$(PackagePath)\bundle" DestinationFile="$(PackagePath)\internal\$(PlatformName)\install.dat" />
|
||||||
<RemoveDir Directories="$(PackagePath)\bundle" />
|
<RemoveDir Directories="$(PackagePath)\bundle" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<!-- Create a zip file with the contents of a given folder path. Derived from https://stackoverflow.com/a/38127938/262123. -->
|
|
||||||
<UsingTask TaskName="ZipDirectory" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
|
|
||||||
<ParameterGroup>
|
|
||||||
<FromDirPath ParameterType="System.String" Required="true" />
|
|
||||||
<ToFilePath ParameterType="System.String" Required="true" />
|
|
||||||
</ParameterGroup>
|
|
||||||
<Task>
|
|
||||||
<Reference Include="System.IO.Compression.FileSystem" />
|
|
||||||
<Using Namespace="System.IO.Compression" />
|
|
||||||
<Code Type="Fragment" Language="cs">
|
|
||||||
<![CDATA[
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ZipFile.CreateFromDirectory(FromDirPath, ToFilePath);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
Log.LogErrorFromException(ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
]]>
|
|
||||||
</Code>
|
|
||||||
</Task>
|
|
||||||
</UsingTask>
|
|
||||||
|
|
||||||
<!-- Replace text in a file based on a regex pattern. Derived from https://stackoverflow.com/a/22571621/262123. -->
|
<!-- Replace text in a file based on a regex pattern. Derived from https://stackoverflow.com/a/22571621/262123. -->
|
||||||
<UsingTask TaskName="ReplaceFileText" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
<UsingTask TaskName="ReplaceFileText" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
||||||
<ParameterGroup>
|
<ParameterGroup>
|
||||||
<FilePath ParameterType="System.String" Required="true" />
|
<FilePath ParameterType="System.String" Required="true" />
|
||||||
<Search ParameterType="System.String" Required="true" />
|
<Search ParameterType="System.String" Required="true" />
|
||||||
<Replace ParameterType="System.String" Required="true" />
|
<Replace ParameterType="System.String" Required="true" />
|
||||||
</ParameterGroup>
|
</ParameterGroup>
|
||||||
<Task>
|
<Task>
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Using Namespace="System" />
|
<Using Namespace="System" />
|
||||||
<Using Namespace="System.IO" />
|
<Using Namespace="System.IO" />
|
||||||
<Using Namespace="System.Text.RegularExpressions" />
|
<Using Namespace="System.Text.RegularExpressions" />
|
||||||
|
|
|
@ -1,17 +1,44 @@
|
||||||
← [README](README.md)
|
← [README](README.md)
|
||||||
|
|
||||||
# Release notes
|
# Release notes
|
||||||
|
## 3.13.0
|
||||||
|
Released 30 November 2021 for Stardew Valley 1.5.5 or later.
|
||||||
|
|
||||||
|
* For players:
|
||||||
|
* Updated for Stardew Valley 1.5.5.
|
||||||
|
* Added `set_farm_type` [console command](https://stardewvalleywiki.com/Modding:Console_commands#Console_commands) to change the current farm type.
|
||||||
|
* Fixed installer window closing immediately if the installer crashed.
|
||||||
|
* Updated compatibility list.
|
||||||
|
|
||||||
|
* For mod authors:
|
||||||
|
* Migrated to 64-bit MonoGame and .NET 5 on all platforms (see [migration guide for mod authors](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5)).
|
||||||
|
* Added support for [map overlays via `asset.AsMap().PatchMap`](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Content#Edit_a_map).
|
||||||
|
* Added support for loading BmFont `.fnt` files for [custom languages](https://stardewvalleywiki.com/Modding:Custom_languages) through the [content API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Content).
|
||||||
|
|
||||||
|
* For the web UI:
|
||||||
|
* Updated the JSON validator/schema for Content Patcher 1.24.0.
|
||||||
|
|
||||||
|
**Update note for players with older systems:**
|
||||||
|
The game now has two branches: the _main branch_ which you'll get by default, and an optional
|
||||||
|
[_compatibility branch_ for older systems](https://www.stardewvalley.net/compatibility/). The two
|
||||||
|
branches have identical content, but use [different technologies](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5#Game_compatibility_branch).
|
||||||
|
|
||||||
|
Unfortunately **SMAPI only supports the main branch of the game**. There are formidable difficulties
|
||||||
|
across all mods in supporting all three variations, 32-bit imposes significant restrictions on what
|
||||||
|
mods can do, and the [Steam hardware stats](https://store.steampowered.com/hwsurvey) show that 99.69%
|
||||||
|
of players now have 64-bit.
|
||||||
|
|
||||||
## 3.12.8
|
## 3.12.8
|
||||||
Released 18 October 2021 for Stardew Valley 1.5.4.
|
Released 18 October 2021 for Stardew Valley 1.5.4.
|
||||||
|
|
||||||
* For players:
|
* For players:
|
||||||
* Fixed mod edits to the farmhouse shifting the player down one tile in some cases.
|
* Fixed mod edits to the farmhouse shifting the player down one tile in some cases.
|
||||||
|
* Fixed map tile rotations/flips not working for farmhands in split-screen mode.
|
||||||
* Improved translations. Thanks to ellipszist (added Thai) and Zangorr (added Polish)!
|
* Improved translations. Thanks to ellipszist (added Thai) and Zangorr (added Polish)!
|
||||||
_These are custom languages which require Stardew Valley 1.5.5 and the [Polish](https://www.nexusmods.com/stardewvalley/mods/3616) or [Thai](https://www.nexusmods.com/stardewvalley/mods/7052) mod._
|
_These are custom languages which require Stardew Valley 1.5.5 and the [Polish](https://www.nexusmods.com/stardewvalley/mods/3616) or [Thai](https://www.nexusmods.com/stardewvalley/mods/7052) mod._
|
||||||
|
|
||||||
* For mod authors:
|
* For mod authors:
|
||||||
* SMAPI now intercepts dictionary duplicate-key errors and adds the key to the error message to simplify troubleshooting. (Due to Harmony limitations, this only works for the dictionary types used by the game.)
|
* SMAPI now intercepts dictionary duplicate-key errors and adds the key to the error message to simplify troubleshooting. (Due to Harmony limitations, this only works for the dictionary types used by the game.)
|
||||||
* Fixed map tile rotations/flips not working for farmhands in split-screen mode.
|
|
||||||
* Fixed barn/coop exit warps being reset when you edit their interior map.
|
* Fixed barn/coop exit warps being reset when you edit their interior map.
|
||||||
|
|
||||||
* For the web UI:
|
* For the web UI:
|
||||||
|
|
|
@ -29,20 +29,19 @@ change how these work):
|
||||||
* **Detect game path:**
|
* **Detect game path:**
|
||||||
The package automatically finds your game folder by scanning the default install paths and
|
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:
|
Windows registry. It adds two MSBuild properties for use in your `.csproj` file if needed:
|
||||||
`$(GamePath)` and `$(GameExecutableName)`.
|
`$(GamePath)` and `$(GameModsPath)`.
|
||||||
|
|
||||||
* **Add assembly references:**
|
* **Add assembly references:**
|
||||||
The package adds assembly references to SMAPI, Stardew Valley, xTile, and the game framework
|
The package adds assembly references to MonoGame, SMAPI, Stardew Valley, and xTile. It
|
||||||
(MonoGame on Linux/macOS, XNA Framework on Windows). It automatically adjusts depending on which OS
|
automatically adjusts depending on which OS you're compiling it on. If you use
|
||||||
you're compiling it on. If you use [Harmony](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Harmony),
|
[Harmony](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Harmony), it can optionally add
|
||||||
it can optionally add a reference to that too.
|
a reference to that too.
|
||||||
|
|
||||||
* **Copy files into the `Mods` folder:**
|
* **Copy files into the `Mods` folder:**
|
||||||
The package automatically copies your mod's DLL and PDB files, `manifest.json`, [`i18n`
|
The package automatically copies your mod's DLL and PDB files, `manifest.json`, [`i18n`
|
||||||
files](https://stardewvalleywiki.com/Modding:Translations) (if any), the `assets` folder (if
|
files](https://stardewvalleywiki.com/Modding:Translations) (if any), and the `assets` folder (if
|
||||||
any), and [build output](https://stackoverflow.com/a/10828462/262123) into your game's `Mods`
|
any) into the `Mods` folder when you rebuild the code, with a subfolder matching the mod's project
|
||||||
folder when you rebuild the code, with a subfolder matching the mod's project name. That lets you
|
name. That lets you try the mod in-game right after building it.
|
||||||
try the mod in-game right after building it.
|
|
||||||
|
|
||||||
* **Create release zip:**
|
* **Create release zip:**
|
||||||
The package adds a zip file in your project's `bin` folder when you rebuild the code, in the
|
The package adds a zip file in your project's `bin` folder when you rebuild the code, in the
|
||||||
|
@ -129,23 +128,6 @@ The absolute path to the folder containing the game's installed mods (defaults t
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>GameExecutableName</code></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The filename for the game's executable (i.e. `StardewValley.exe` on Linux/macOS or
|
|
||||||
`Stardew Valley.exe` on Windows). This is auto-detected, and you should almost never change this.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>GameFramework</code></td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The game framework for which the mod is being compiled (one of `Xna` or `MonoGame`). This is
|
|
||||||
auto-detected based on the platform, and you should almost never change this.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -206,11 +188,63 @@ The folder path where the release zip is created (defaults to the project's `bin
|
||||||
<th>effect</th>
|
<th>effect</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>CopyModReferencesToBuildOutput</code></td>
|
<td><code>BundleExtraAssemblies</code></td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Whether to copy game and framework DLLs into the mod folder (default `false`). This is useful for
|
**Most mods should not change this option.**
|
||||||
unit test projects, but not needed for mods that'll be run through SMAPI.
|
|
||||||
|
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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -222,6 +256,20 @@ Whether to configure the project so you can launch or debug the game through the
|
||||||
Visual Studio (default `true`). There's usually no reason to change this, unless it's a unit test
|
Visual Studio (default `true`). There's usually no reason to change this, unless it's a unit test
|
||||||
project.
|
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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -330,16 +378,15 @@ The configuration will check your custom path first, then fall back to the defau
|
||||||
still compile on a different computer).
|
still compile on a different computer).
|
||||||
|
|
||||||
### How do I change which files are included in the mod deploy/zip?
|
### How do I change which files are included in the mod deploy/zip?
|
||||||
For custom files, you can [add/remove them in the build output](https://stackoverflow.com/a/10828462/262123).
|
* For normal files, you can [add/remove them in the build output](https://stackoverflow.com/a/10828462/262123).
|
||||||
(If your project references another mod, make sure the reference is [_not_ marked 'copy
|
* For assembly files (`*.dll`, `*.exe`, `*.pdb`, or `*.xml`), see the
|
||||||
local'](https://msdn.microsoft.com/en-us/library/t1zz5y8c(v=vs.100).aspx).)
|
[`BundleExtraAssemblies` option](#configure).
|
||||||
|
* To exclude a file which the package copies by default, see the [`IgnoreModFilePaths` or
|
||||||
To exclude a file the package copies by default, see `IgnoreModFilePatterns` under
|
`IgnoreModFilePatterns` options](#configure).
|
||||||
[_configure_](#configure).
|
|
||||||
|
|
||||||
### Can I use the package for non-mod projects?
|
### Can I use the package for non-mod projects?
|
||||||
You can use the package in non-mod projects too (e.g. unit tests or framework DLLs). Just disable
|
Yep, this works in unit tests and framework projects too. Just disable the mod-related package
|
||||||
the mod-related package features (see [_configure_](#configure)):
|
features (see [_configure_](#configure)):
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<EnableGameDebugging>false</EnableGameDebugging>
|
<EnableGameDebugging>false</EnableGameDebugging>
|
||||||
|
@ -347,9 +394,9 @@ the mod-related package features (see [_configure_](#configure)):
|
||||||
<EnableModZip>false</EnableModZip>
|
<EnableModZip>false</EnableModZip>
|
||||||
```
|
```
|
||||||
|
|
||||||
If you need to copy the referenced DLLs into your build output, add this too:
|
To copy referenced DLLs into your build output for unit tests, add this too:
|
||||||
```xml
|
```xml
|
||||||
<CopyModReferencesToBuildOutput>true</CopyModReferencesToBuildOutput>
|
<BundleExtraAssemblies>All</BundleExtraAssemblies>
|
||||||
```
|
```
|
||||||
|
|
||||||
## For SMAPI developers
|
## For SMAPI developers
|
||||||
|
@ -366,13 +413,31 @@ when you compile it.
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
## Upcoming release
|
## Upcoming release
|
||||||
|
* 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.
|
* 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
|
## 3.3.0
|
||||||
Released 30 March 2021.
|
Released 30 March 2021.
|
||||||
|
|
||||||
* Added a build warning when the mod isn't compiled for `Any CPU`.
|
* 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 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.
|
* 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.
|
* The package now suppresses the misleading 'processor architecture mismatch' warnings.
|
||||||
|
|
||||||
|
@ -380,7 +445,8 @@ Released 30 March 2021.
|
||||||
Released 23 September 2020.
|
Released 23 September 2020.
|
||||||
|
|
||||||
* Reworked and streamlined how the package is compiled.
|
* Reworked and streamlined how the package is compiled.
|
||||||
* Added [SMAPI-ModTranslationClassBuilder](https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder) files to the ignore list.
|
* Added [SMAPI-ModTranslationClassBuilder](https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder)
|
||||||
|
files to the ignore list.
|
||||||
|
|
||||||
### 3.2.1
|
### 3.2.1
|
||||||
Released 11 September 2020.
|
Released 11 September 2020.
|
||||||
|
|
|
@ -57,7 +57,6 @@ SMAPI uses a small number of conditional compilation constants, which you can se
|
||||||
flag | purpose
|
flag | purpose
|
||||||
---- | -------
|
---- | -------
|
||||||
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled for Windows; if not set, the code assumes Linux/macOS. Set automatically in `common.targets`.
|
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled for Windows; if not set, the code assumes Linux/macOS. Set automatically in `common.targets`.
|
||||||
`SMAPI_FOR_XNA` | Whether SMAPI is being compiled for XNA Framework; if not set, the code assumes MonoGame. Set automatically in `common.targets` with the same value as `SMAPI_FOR_WINDOWS`.
|
|
||||||
|
|
||||||
## For SMAPI developers
|
## For SMAPI developers
|
||||||
### Compiling from source
|
### Compiling from source
|
||||||
|
|
|
@ -367,7 +367,7 @@ accordingly.
|
||||||
Initial setup:
|
Initial setup:
|
||||||
|
|
||||||
1. Create an Azure Blob storage account for uploaded files.
|
1. Create an Azure Blob storage account for uploaded files.
|
||||||
2. Create an Azure App Services environment running the latest .NET Core on Linux or Windows.
|
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:
|
3. Add these application settings in the new App Services environment:
|
||||||
|
|
||||||
property name | description
|
property name | description
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.Win32;
|
|
||||||
using StardewModdingAPI.Toolkit;
|
using StardewModdingAPI.Toolkit;
|
||||||
using StardewModdingAPI.Toolkit.Framework.GameScanning;
|
using StardewModdingAPI.Toolkit.Framework.GameScanning;
|
||||||
using StardewModdingAPI.Toolkit.Utilities;
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
|
@ -13,9 +11,6 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
/*********
|
/*********
|
||||||
** Fields
|
** Fields
|
||||||
*********/
|
*********/
|
||||||
/// <summary>The <see cref="Environment.OSVersion"/> value that represents Windows 7.</summary>
|
|
||||||
private readonly Version Windows7Version = new Version(6, 1);
|
|
||||||
|
|
||||||
/// <summary>The underlying toolkit game scanner.</summary>
|
/// <summary>The underlying toolkit game scanner.</summary>
|
||||||
private readonly GameScanner GameScanner = new GameScanner();
|
private readonly GameScanner GameScanner = new GameScanner();
|
||||||
|
|
||||||
|
@ -29,9 +24,6 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
/// <summary>The human-readable OS name and version.</summary>
|
/// <summary>The human-readable OS name and version.</summary>
|
||||||
public string PlatformName { get; }
|
public string PlatformName { get; }
|
||||||
|
|
||||||
/// <summary>The name of the Stardew Valley executable.</summary>
|
|
||||||
public string ExecutableName { get; }
|
|
||||||
|
|
||||||
/// <summary>Whether the installer is running on Windows.</summary>
|
/// <summary>Whether the installer is running on Windows.</summary>
|
||||||
public bool IsWindows => this.Platform == Platform.Windows;
|
public bool IsWindows => this.Platform == Platform.Windows;
|
||||||
|
|
||||||
|
@ -47,7 +39,6 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
{
|
{
|
||||||
this.Platform = EnvironmentUtility.DetectPlatform();
|
this.Platform = EnvironmentUtility.DetectPlatform();
|
||||||
this.PlatformName = EnvironmentUtility.GetFriendlyPlatformName(this.Platform);
|
this.PlatformName = EnvironmentUtility.GetFriendlyPlatformName(this.Platform);
|
||||||
this.ExecutableName = EnvironmentUtility.GetExecutableName(this.Platform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the installer's version number.</summary>
|
/// <summary>Get the installer's version number.</summary>
|
||||||
|
@ -57,42 +48,6 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
return new SemanticVersion(raw);
|
return new SemanticVersion(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get whether the current system has .NET Framework 4.5 or later installed. This only applies on Windows.</summary>
|
|
||||||
/// <exception cref="NotSupportedException">The current platform is not Windows.</exception>
|
|
||||||
public bool HasNetFramework45()
|
|
||||||
{
|
|
||||||
switch (this.Platform)
|
|
||||||
{
|
|
||||||
case Platform.Windows:
|
|
||||||
using (RegistryKey versionKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"))
|
|
||||||
return versionKey?.GetValue("Release") != null; // .NET Framework 4.5+
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException("The installed .NET Framework version can only be checked on Windows.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get whether the current system has XNA Framework installed. This only applies on Windows.</summary>
|
|
||||||
/// <exception cref="NotSupportedException">The current platform is not Windows.</exception>
|
|
||||||
public bool HasXna()
|
|
||||||
{
|
|
||||||
switch (this.Platform)
|
|
||||||
{
|
|
||||||
case Platform.Windows:
|
|
||||||
using (RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(@"SOFTWARE\Microsoft\XNA\Framework"))
|
|
||||||
return key != null; // XNA Framework 4.0+
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException("The installed XNA Framework version can only be checked on Windows.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Whether the current OS supports newer versions of .NET Framework.</summary>
|
|
||||||
public bool CanInstallLatestNetFramework()
|
|
||||||
{
|
|
||||||
return Environment.OSVersion.Version >= this.Windows7Version; // Windows 7+
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get whether a folder seems to contain the game files.</summary>
|
/// <summary>Get whether a folder seems to contain the game files.</summary>
|
||||||
/// <param name="dir">The folder to check.</param>
|
/// <param name="dir">The folder to check.</param>
|
||||||
public bool LooksLikeGameFolder(DirectoryInfo dir)
|
public bool LooksLikeGameFolder(DirectoryInfo dir)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using StardewModdingAPI.Toolkit.Framework;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Installer.Framework
|
namespace StardewModdingAPI.Installer.Framework
|
||||||
{
|
{
|
||||||
|
@ -44,17 +45,20 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
/// <summary>The full path to the user's config overrides file.</summary>
|
/// <summary>The full path to the user's config overrides file.</summary>
|
||||||
public string ApiUserConfigPath { get; }
|
public string ApiUserConfigPath { get; }
|
||||||
|
|
||||||
/// <summary>The full path to the installed game executable file.</summary>
|
/// <summary>The full path to the installed game DLL.</summary>
|
||||||
public string ExecutablePath { get; private set; }
|
public string GameDllPath { get; }
|
||||||
|
|
||||||
/// <summary>The full path to the vanilla game launcher on Linux/macOS.</summary>
|
/// <summary>The full path to the installed SMAPI executable file.</summary>
|
||||||
public string UnixLauncherPath { get; }
|
public string UnixSmapiExecutablePath { get; }
|
||||||
|
|
||||||
/// <summary>The full path to the installed SMAPI launcher on Linux/macOS before it's renamed.</summary>
|
/// <summary>The full path to the vanilla game launch script on Linux/macOS.</summary>
|
||||||
public string UnixSmapiLauncherPath { get; }
|
public string VanillaLaunchScriptPath { get; }
|
||||||
|
|
||||||
/// <summary>The full path to the vanilla game launcher on Linux/macOS after SMAPI is installed.</summary>
|
/// <summary>The full path to the installed SMAPI launch script on Linux/macOS before it's renamed.</summary>
|
||||||
public string UnixBackupLauncherPath { get; }
|
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; }
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
|
@ -63,28 +67,24 @@ namespace StardewModdingAPI.Installer.Framework
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
/// <param name="bundleDir">The directory path containing the files to copy into the game folder.</param>
|
/// <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>
|
/// <param name="gameDir">The directory path for the installed game.</param>
|
||||||
/// <param name="gameExecutableName">The name of the game's executable file for the current platform.</param>
|
public InstallerPaths(DirectoryInfo bundleDir, DirectoryInfo gameDir)
|
||||||
public InstallerPaths(DirectoryInfo bundleDir, DirectoryInfo gameDir, string gameExecutableName)
|
|
||||||
{
|
{
|
||||||
|
// base paths
|
||||||
this.BundleDir = bundleDir;
|
this.BundleDir = bundleDir;
|
||||||
this.GameDir = gameDir;
|
this.GameDir = gameDir;
|
||||||
this.ModsDir = new DirectoryInfo(Path.Combine(gameDir.FullName, "Mods"));
|
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.BundleApiUserConfigPath = Path.Combine(bundleDir.FullName, "smapi-internal", "config.user.json");
|
||||||
|
|
||||||
this.ExecutablePath = Path.Combine(gameDir.FullName, gameExecutableName);
|
|
||||||
this.UnixLauncherPath = Path.Combine(gameDir.FullName, "StardewValley");
|
|
||||||
this.UnixSmapiLauncherPath = Path.Combine(gameDir.FullName, "StardewModdingAPI");
|
|
||||||
this.UnixBackupLauncherPath = Path.Combine(gameDir.FullName, "StardewValley-original");
|
|
||||||
this.ApiConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.json");
|
this.ApiConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.json");
|
||||||
this.ApiUserConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.user.json");
|
this.ApiUserConfigPath = Path.Combine(gameDir.FullName, "smapi-internal", "config.user.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Override the filename for the <see cref="ExecutablePath"/>.</summary>
|
|
||||||
/// <param name="filename">the file name.</param>
|
|
||||||
public void SetExecutableFileName(string filename)
|
|
||||||
{
|
|
||||||
this.ExecutablePath = Path.Combine(this.GamePath, filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,18 +39,19 @@ namespace StardewModdingApi.Installer
|
||||||
string GetInstallPath(string path) => Path.Combine(installDir.FullName, path);
|
string GetInstallPath(string path) => Path.Combine(installDir.FullName, path);
|
||||||
|
|
||||||
// current files
|
// current files
|
||||||
yield return GetInstallPath("libgdiplus.dylib"); // Linux/macOS only
|
|
||||||
yield return GetInstallPath("StardewModdingAPI"); // Linux/macOS only
|
yield return GetInstallPath("StardewModdingAPI"); // Linux/macOS only
|
||||||
|
yield return GetInstallPath("StardewModdingAPI.dll");
|
||||||
yield return GetInstallPath("StardewModdingAPI.exe");
|
yield return GetInstallPath("StardewModdingAPI.exe");
|
||||||
yield return GetInstallPath("StardewModdingAPI.exe.config");
|
yield return GetInstallPath("StardewModdingAPI.exe.config");
|
||||||
yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // Linux/macOS only
|
yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // Linux/macOS only
|
||||||
yield return GetInstallPath("StardewModdingAPI.pdb"); // Windows only
|
yield return GetInstallPath("StardewModdingAPI.pdb"); // Windows only
|
||||||
|
yield return GetInstallPath("StardewModdingAPI.runtimeconfig.json");
|
||||||
yield return GetInstallPath("StardewModdingAPI.xml");
|
yield return GetInstallPath("StardewModdingAPI.xml");
|
||||||
yield return GetInstallPath("StardewModdingAPI-x64.exe"); // not normally added to game folder, but may be mistakenly added by a manual install
|
|
||||||
yield return GetInstallPath("smapi-internal");
|
yield return GetInstallPath("smapi-internal");
|
||||||
yield return GetInstallPath("steam_appid.txt");
|
yield return GetInstallPath("steam_appid.txt");
|
||||||
|
|
||||||
// obsolete
|
// obsolete
|
||||||
|
yield return GetInstallPath("libgdiplus.dylib"); // before 3.13 (macOS only)
|
||||||
yield return GetInstallPath(Path.Combine("Mods", ".cache")); // 1.3-1.4
|
yield return GetInstallPath(Path.Combine("Mods", ".cache")); // 1.3-1.4
|
||||||
yield return GetInstallPath(Path.Combine("Mods", "TrainerMod")); // *–2.0 (renamed to ConsoleCommands)
|
yield return GetInstallPath(Path.Combine("Mods", "TrainerMod")); // *–2.0 (renamed to ConsoleCommands)
|
||||||
yield return GetInstallPath("Mono.Cecil.Rocks.dll"); // 1.3–1.8
|
yield return GetInstallPath("Mono.Cecil.Rocks.dll"); // 1.3–1.8
|
||||||
|
@ -70,9 +71,7 @@ namespace StardewModdingApi.Installer
|
||||||
yield return GetInstallPath("StardewModdingAPI.Toolkit.CoreInterfaces.dll"); // moved in 2.8
|
yield return GetInstallPath("StardewModdingAPI.Toolkit.CoreInterfaces.dll"); // moved in 2.8
|
||||||
yield return GetInstallPath("StardewModdingAPI.Toolkit.CoreInterfaces.pdb"); // moved in 2.8
|
yield return GetInstallPath("StardewModdingAPI.Toolkit.CoreInterfaces.pdb"); // moved in 2.8
|
||||||
yield return GetInstallPath("StardewModdingAPI.Toolkit.CoreInterfaces.xml"); // moved in 2.8
|
yield return GetInstallPath("StardewModdingAPI.Toolkit.CoreInterfaces.xml"); // moved in 2.8
|
||||||
yield return GetInstallPath("System.Numerics.dll"); // moved in 2.8
|
yield return GetInstallPath("StardewModdingAPI-x64.exe"); // before 3.13
|
||||||
yield return GetInstallPath("System.Runtime.Caching.dll"); // moved in 2.8
|
|
||||||
yield return GetInstallPath("System.ValueTuple.dll"); // moved in 2.8
|
|
||||||
|
|
||||||
if (modsDir.Exists)
|
if (modsDir.Exists)
|
||||||
{
|
{
|
||||||
|
@ -149,30 +148,6 @@ namespace StardewModdingApi.Installer
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/****
|
|
||||||
** Check Windows dependencies
|
|
||||||
****/
|
|
||||||
if (context.IsWindows)
|
|
||||||
{
|
|
||||||
// .NET Framework 4.5+
|
|
||||||
if (!context.HasNetFramework45())
|
|
||||||
{
|
|
||||||
this.PrintError(context.CanInstallLatestNetFramework()
|
|
||||||
? "Please install the latest version of .NET Framework before installing SMAPI."
|
|
||||||
: "Please install .NET Framework 4.5 before installing SMAPI."
|
|
||||||
);
|
|
||||||
this.PrintError("See the download page at https://www.microsoft.com/net/download/framework for details.");
|
|
||||||
Console.ReadLine();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!context.HasXna())
|
|
||||||
{
|
|
||||||
this.PrintError("You don't seem to have XNA Framework installed. Please run the game at least once before installing SMAPI, so it can perform its first-time setup.");
|
|
||||||
Console.ReadLine();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/****
|
/****
|
||||||
** read command-line arguments
|
** read command-line arguments
|
||||||
****/
|
****/
|
||||||
|
@ -270,51 +245,20 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
// get folders
|
// get folders
|
||||||
DirectoryInfo bundleDir = new DirectoryInfo(this.BundlePath);
|
DirectoryInfo bundleDir = new DirectoryInfo(this.BundlePath);
|
||||||
paths = new InstallerPaths(bundleDir, installDir, context.ExecutableName);
|
paths = new InstallerPaths(bundleDir, installDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Step 4: validate assumptions
|
** Step 4: validate assumptions
|
||||||
*********/
|
*********/
|
||||||
// not 64-bit on Windows
|
|
||||||
if (context.Platform == Platform.Windows)
|
|
||||||
{
|
|
||||||
FileInfo linuxExecutable = new FileInfo(Path.Combine(paths.GamePath, "StardewValley.exe"));
|
|
||||||
if (linuxExecutable.Exists && this.Is64Bit(linuxExecutable.FullName))
|
|
||||||
{
|
|
||||||
this.PrintError("Oops! The detected game install path seems to be unofficial 64-bit mode, which is no longer supported. You can update to Stardew Valley 1.5.5 or later instead. See https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows for more info.");
|
|
||||||
Console.ReadLine();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// executable exists
|
// executable exists
|
||||||
if (!File.Exists(paths.ExecutablePath))
|
if (!File.Exists(paths.GameDllPath))
|
||||||
{
|
{
|
||||||
this.PrintError("The detected game install path doesn't contain a Stardew Valley executable.");
|
this.PrintError("The detected game install path doesn't contain a Stardew Valley executable.");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not Stardew Valley 1.5.5+
|
|
||||||
if (File.Exists(Path.Combine(paths.GamePath, "Stardew Valley.dll")))
|
|
||||||
{
|
|
||||||
this.PrintError("Oops! The detected game install path seems to be Stardew Valley 1.5.5 or later, but this version of SMAPI is only compatible up to Stardew Valley 1.5.4. Please check for a newer version of SMAPI: https://smapi.io.");
|
|
||||||
Console.ReadLine();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// game folder doesn't contain paths beyond the max limit
|
|
||||||
{
|
|
||||||
string[] tooLongPaths = PathUtilities.GetTooLongPaths(Path.Combine(paths.GamePath, "Mods")).ToArray();
|
|
||||||
if (tooLongPaths.Any())
|
|
||||||
{
|
|
||||||
this.PrintError($"SMAPI can't install to the detected game folder, because some of its files exceed the maximum {context.Platform} path length.\nIf you need help fixing this error, see https://smapi.io/help\n\nAffected paths:\n {string.Join("\n ", tooLongPaths)}");
|
|
||||||
Console.ReadLine();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
|
|
||||||
|
|
||||||
|
@ -387,11 +331,11 @@ namespace StardewModdingApi.Installer
|
||||||
** Always uninstall old files
|
** Always uninstall old files
|
||||||
****/
|
****/
|
||||||
// restore game launcher
|
// restore game launcher
|
||||||
if (context.IsUnix && File.Exists(paths.UnixBackupLauncherPath))
|
if (context.IsUnix && File.Exists(paths.BackupLaunchScriptPath))
|
||||||
{
|
{
|
||||||
this.PrintDebug("Removing SMAPI launcher...");
|
this.PrintDebug("Removing SMAPI launcher...");
|
||||||
this.InteractivelyDelete(paths.UnixLauncherPath);
|
this.InteractivelyDelete(paths.VanillaLaunchScriptPath);
|
||||||
File.Move(paths.UnixBackupLauncherPath, paths.UnixLauncherPath);
|
File.Move(paths.BackupLaunchScriptPath, paths.VanillaLaunchScriptPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove old files
|
// remove old files
|
||||||
|
@ -439,29 +383,40 @@ namespace StardewModdingApi.Installer
|
||||||
this.PrintDebug("Safely replacing game launcher...");
|
this.PrintDebug("Safely replacing game launcher...");
|
||||||
|
|
||||||
// back up & remove current launcher
|
// back up & remove current launcher
|
||||||
if (File.Exists(paths.UnixLauncherPath))
|
if (File.Exists(paths.VanillaLaunchScriptPath))
|
||||||
{
|
{
|
||||||
if (!File.Exists(paths.UnixBackupLauncherPath))
|
if (!File.Exists(paths.BackupLaunchScriptPath))
|
||||||
File.Move(paths.UnixLauncherPath, paths.UnixBackupLauncherPath);
|
File.Move(paths.VanillaLaunchScriptPath, paths.BackupLaunchScriptPath);
|
||||||
else
|
else
|
||||||
this.InteractivelyDelete(paths.UnixLauncherPath);
|
this.InteractivelyDelete(paths.VanillaLaunchScriptPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new launcher
|
// add new launcher
|
||||||
File.Move(paths.UnixSmapiLauncherPath, paths.UnixLauncherPath);
|
File.Move(paths.NewLaunchScriptPath, paths.VanillaLaunchScriptPath);
|
||||||
|
|
||||||
// mark file executable
|
// mark files executable
|
||||||
// (MSBuild doesn't keep permission flags for files zipped in a build task.)
|
// (MSBuild doesn't keep permission flags for files zipped in a build task.)
|
||||||
|
foreach (string path in new[] { paths.VanillaLaunchScriptPath, paths.UnixSmapiExecutablePath })
|
||||||
|
{
|
||||||
new Process
|
new Process
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo
|
StartInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = "chmod",
|
FileName = "chmod",
|
||||||
Arguments = $"755 \"{paths.UnixLauncherPath}\"",
|
Arguments = $"755 \"{path}\"",
|
||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
}
|
}
|
||||||
}.Start();
|
}.Start();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the game's deps.json file
|
||||||
|
// (This is needed to resolve native DLLs like libSkiaSharp.)
|
||||||
|
File.Copy(
|
||||||
|
sourceFileName: Path.Combine(paths.GamePath, "Stardew Valley.deps.json"),
|
||||||
|
destFileName: Path.Combine(paths.GamePath, "StardewModdingAPI.deps.json"),
|
||||||
|
overwrite: true
|
||||||
|
);
|
||||||
|
|
||||||
// create mods directory (if needed)
|
// create mods directory (if needed)
|
||||||
if (!paths.ModsDir.Exists)
|
if (!paths.ModsDir.Exists)
|
||||||
|
@ -527,7 +482,7 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Step 6: final instructions
|
** Step 7: final instructions
|
||||||
*********/
|
*********/
|
||||||
if (context.IsWindows)
|
if (context.IsWindows)
|
||||||
{
|
{
|
||||||
|
@ -556,13 +511,6 @@ namespace StardewModdingApi.Installer
|
||||||
/*********
|
/*********
|
||||||
** Private methods
|
** Private methods
|
||||||
*********/
|
*********/
|
||||||
/// <summary>Get whether an executable is 64-bit.</summary>
|
|
||||||
/// <param name="executablePath">The absolute path to the executable file.</param>
|
|
||||||
private bool Is64Bit(string executablePath)
|
|
||||||
{
|
|
||||||
return LowLevelEnvironmentUtility.Is64BitAssembly(executablePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get the display text for a color scheme.</summary>
|
/// <summary>Get the display text for a color scheme.</summary>
|
||||||
/// <param name="scheme">The color scheme.</param>
|
/// <param name="scheme">The color scheme.</param>
|
||||||
private string GetDisplayText(MonitorColorScheme scheme)
|
private string GetDisplayText(MonitorColorScheme scheme)
|
||||||
|
@ -725,7 +673,7 @@ namespace StardewModdingApi.Installer
|
||||||
{
|
{
|
||||||
// get path from user
|
// get path from user
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
this.PrintInfo($"Type the file path to the game directory (the one containing '{context.ExecutableName}'), then press enter.");
|
this.PrintInfo($"Type the file path to the game directory (the one containing '{Constants.GameDllName}'), then press enter.");
|
||||||
string path = Console.ReadLine()?.Trim();
|
string path = Console.ReadLine()?.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,8 +31,7 @@ namespace StardewModdingApi.Installer
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
// find install bundle
|
// find install bundle
|
||||||
PlatformID platform = Environment.OSVersion.Platform;
|
FileInfo zipFile = new FileInfo(Path.Combine(Program.InstallerPath, "install.dat"));
|
||||||
FileInfo zipFile = new FileInfo(Path.Combine(Program.InstallerPath, $"{(platform == PlatformID.Win32NT ? "windows" : "unix")}-install.dat"));
|
|
||||||
if (!zipFile.Exists)
|
if (!zipFile.Exists)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Oops! Some of the installer files are missing; try re-downloading the installer. (Missing file: {zipFile.FullName})");
|
Console.WriteLine($"Oops! Some of the installer files are missing; try re-downloading the installer. (Missing file: {zipFile.FullName})");
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<RootNamespace>StardewModdingAPI.Installer</RootNamespace>
|
<RootNamespace>StardewModdingAPI.Installer</RootNamespace>
|
||||||
<Description>The SMAPI installer for players.</Description>
|
<Description>The SMAPI installer for players.</Description>
|
||||||
<TargetFramework>net452</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -24,7 +24,7 @@ Manual install
|
||||||
THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See instructions above instead.
|
THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See instructions above instead.
|
||||||
If you really want to install SMAPI manually, here's how.
|
If you really want to install SMAPI manually, here's how.
|
||||||
|
|
||||||
1. Unzip "internal/windows-install.dat" (on Windows) or "internal/unix-install.dat" (on
|
1. Unzip "internal/windows/install.dat" (on Windows) or "internal/unix/install.dat" (on
|
||||||
Linux/macOS). You can change '.dat' to '.zip', it's just a normal zip file renamed to prevent
|
Linux/macOS). You can change '.dat' to '.zip', it's just a normal zip file renamed to prevent
|
||||||
confusion.
|
confusion.
|
||||||
2. Copy the files from the folder you just unzipped into your game folder. The
|
2. Copy the files from the folder you just unzipped into your game folder. The
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"runtimeOptions": {
|
||||||
|
"tfm": "net5.0",
|
||||||
|
"includedFrameworks": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App",
|
||||||
|
"version": "5.0.7"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configProperties": {
|
||||||
|
"System.Runtime.TieredCompilation": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"runtimeOptions": {
|
||||||
|
"tfm": "net5.0",
|
||||||
|
"framework": {
|
||||||
|
"name": "Microsoft.NETCore.App",
|
||||||
|
"version": "5.0.0"
|
||||||
|
},
|
||||||
|
"configProperties": {
|
||||||
|
"System.Runtime.TieredCompilation": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +1,14 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Run the SMAPI installer through Mono on Linux or macOS.
|
|
||||||
|
|
||||||
# Move to script's directory
|
# Move to script's directory
|
||||||
cd "`dirname "$0"`"
|
cd "`dirname "$0"`"
|
||||||
|
|
||||||
# get cross-distro version of POSIX command
|
# make sure .NET 5 is installed
|
||||||
COMMAND=""
|
if ! command -v dotnet >/dev/null 2>&1; then
|
||||||
if command -v command >/dev/null 2>&1; then
|
echo "Oops! You must have .NET 5 installed to use SMAPI: https://dotnet.microsoft.com/download";
|
||||||
COMMAND="command -v"
|
|
||||||
elif type type >/dev/null 2>&1; then
|
|
||||||
COMMAND="type"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if $TERM is not set to xterm, mono will bail out when attempting to write to the console.
|
|
||||||
export TERM=xterm
|
|
||||||
|
|
||||||
# validate Mono & run installer
|
|
||||||
if $COMMAND mono >/dev/null 2>&1; then
|
|
||||||
mono internal/unix-install.exe
|
|
||||||
else
|
|
||||||
echo "Oops! Looks like Mono isn't installed. Please install Mono from https://mono-project.com, reboot, and run this installer again."
|
|
||||||
read
|
read
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# run installer
|
||||||
|
dotnet internal/unix/SMAPI.Installer.dll
|
||||||
|
|
|
@ -1,51 +1,19 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# MonoKickstart Shell Script
|
|
||||||
# Written by Ethan "flibitijibibo" Lee
|
|
||||||
# Modified for SMAPI by various contributors
|
|
||||||
|
|
||||||
# Move to script's directory
|
##########
|
||||||
|
## Initial setup
|
||||||
|
##########
|
||||||
|
# move to script's directory
|
||||||
cd "$(dirname "$0")" || exit $?
|
cd "$(dirname "$0")" || exit $?
|
||||||
|
|
||||||
# Get the system architecture
|
|
||||||
UNAME=$(uname)
|
|
||||||
ARCH=$(uname -m)
|
|
||||||
|
|
||||||
# MonoKickstart picks the right libfolder, so just execute the right binary.
|
##########
|
||||||
if [ "$UNAME" == "Darwin" ]; then
|
## Open terminal if needed
|
||||||
# ... Except on OSX.
|
##########
|
||||||
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:./osx/
|
# 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
|
||||||
# El Capitan is a total idiot and wipes this variable out, making the
|
# Steam messes with the PATH.
|
||||||
# Steam overlay disappear. This sidesteps "System Integrity Protection"
|
if [ "$(uname)" == "Darwin" ]; then
|
||||||
# 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
|
|
||||||
|
|
||||||
# create bin file
|
|
||||||
# Note: don't overwrite if it's identical, to avoid resetting permission flags
|
|
||||||
if [ ! -x StardewModdingAPI.bin.osx ] || ! cmp StardewValley.bin.osx StardewModdingAPI.bin.osx >/dev/null 2>&1; then
|
|
||||||
cp -p StardewValley.bin.osx StardewModdingAPI.bin.osx
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make sure we're running in Terminal (so the user can see errors/warnings/update alerts).
|
|
||||||
# Previously we would just use `open -a Terminal` to launch the .bin.osx file, but that
|
|
||||||
# doesn't let us set environment variables.
|
|
||||||
if [ ! -t 1 ]; then # https://stackoverflow.com/q/911168/262123
|
if [ ! -t 1 ]; then # https://stackoverflow.com/q/911168/262123
|
||||||
# sanity check to make sure we don't have an infinite loop of opening windows
|
# sanity check to make sure we don't have an infinite loop of opening windows
|
||||||
SKIP_TERMINAL=false
|
SKIP_TERMINAL=false
|
||||||
|
@ -68,21 +36,38 @@ if [ "$UNAME" == "Darwin" ]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# launch SMAPI
|
|
||||||
LC_ALL="C" ./StardewModdingAPI.bin.osx "$@"
|
##########
|
||||||
|
## Validate assumptions
|
||||||
|
##########
|
||||||
|
# script must be run from the game folder
|
||||||
|
if [ ! -f "Stardew Valley.dll" ]; then
|
||||||
|
echo "Oops! SMAPI must be placed in the Stardew Valley game folder.\nSee instructions: https://stardewvalleywiki.com/Modding:Player_Guide";
|
||||||
|
read
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# .NET 5 must be installed
|
||||||
|
if ! command -v dotnet >/dev/null 2>&1; then
|
||||||
|
echo "Oops! You must have .NET 5 installed to use SMAPI: https://dotnet.microsoft.com/download";
|
||||||
|
read
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
## Launch SMAPI
|
||||||
|
##########
|
||||||
|
# macOS
|
||||||
|
if [ "$(uname)" == "Darwin" ]; then
|
||||||
|
dotnet StardewModdingAPI.dll "$@"
|
||||||
|
|
||||||
|
# Linux
|
||||||
else
|
else
|
||||||
# choose binary file to launch
|
# choose binary file to launch
|
||||||
LAUNCH_FILE=""
|
LAUNCH_FILE="./StardewModdingAPI"
|
||||||
if [ "$ARCH" == "x86_64" ]; then
|
|
||||||
ln -sf mcs.bin.x86_64 mcs
|
|
||||||
cp StardewValley.bin.x86_64 StardewModdingAPI.bin.x86_64
|
|
||||||
LAUNCH_FILE="./StardewModdingAPI.bin.x86_64"
|
|
||||||
else
|
|
||||||
ln -sf mcs.bin.x86 mcs
|
|
||||||
cp StardewValley.bin.x86 StardewModdingAPI.bin.x86
|
|
||||||
LAUNCH_FILE="./StardewModdingAPI.bin.x86"
|
|
||||||
fi
|
|
||||||
export LAUNCH_FILE
|
export LAUNCH_FILE
|
||||||
|
|
||||||
# select terminal (prefer xterm for best compatibility, then known supported terminals)
|
# select terminal (prefer xterm for best compatibility, then known supported terminals)
|
||||||
|
@ -105,44 +90,44 @@ else
|
||||||
terminal|termite)
|
terminal|termite)
|
||||||
# consumes only one argument after -e
|
# consumes only one argument after -e
|
||||||
# options containing space characters are unsupported
|
# options containing space characters are unsupported
|
||||||
exec $TERMINAL_NAME -e "env TERM=xterm LC_ALL=\"C\" $LAUNCH_FILE $@"
|
exec $TERMINAL_NAME -e "env TERM=xterm $LAUNCH_FILE $@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
xterm|konsole|alacritty)
|
xterm|konsole|alacritty)
|
||||||
# consumes all arguments after -e
|
# consumes all arguments after -e
|
||||||
exec $TERMINAL_NAME -e env TERM=xterm LC_ALL="C" $LAUNCH_FILE "$@"
|
exec $TERMINAL_NAME -e env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
terminator|xfce4-terminal|mate-terminal)
|
terminator|xfce4-terminal|mate-terminal)
|
||||||
# consumes all arguments after -x
|
# consumes all arguments after -x
|
||||||
exec $TERMINAL_NAME -x env TERM=xterm LC_ALL="C" $LAUNCH_FILE "$@"
|
exec $TERMINAL_NAME -x env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
gnome-terminal)
|
gnome-terminal)
|
||||||
# consumes all arguments after --
|
# consumes all arguments after --
|
||||||
exec $TERMINAL_NAME -- env TERM=xterm LC_ALL="C" $LAUNCH_FILE "$@"
|
exec $TERMINAL_NAME -- env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
kitty)
|
kitty)
|
||||||
# consumes all trailing arguments
|
# consumes all trailing arguments
|
||||||
exec $TERMINAL_NAME env TERM=xterm LC_ALL="C" $LAUNCH_FILE "$@"
|
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 we don't know the terminal, just try to run it in the current shell.
|
||||||
# If THAT fails, launch with no output.
|
# If THAT fails, launch with no output.
|
||||||
env TERM=xterm LC_ALL="C" $LAUNCH_FILE "$@"
|
env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
if [ $? -eq 127 ]; then
|
if [ $? -eq 127 ]; then
|
||||||
exec LC_ALL="C" $LAUNCH_FILE --no-terminal "$@"
|
exec $LAUNCH_FILE --no-terminal "$@"
|
||||||
fi
|
fi
|
||||||
esac
|
esac
|
||||||
|
|
||||||
## terminal isn't executable; fallback to current shell or no terminal
|
## terminal isn't executable; fallback to current shell or no terminal
|
||||||
else
|
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."
|
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 LC_ALL="C" $LAUNCH_FILE "$@"
|
env TERM=xterm $LAUNCH_FILE "$@"
|
||||||
if [ $? -eq 127 ]; then
|
if [ $? -eq 127 ]; then
|
||||||
exec LC_ALL="C" $LAUNCH_FILE --no-terminal "$@"
|
exec $LAUNCH_FILE --no-terminal "$@"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,8 +1,49 @@
|
||||||
@echo off
|
@echo off
|
||||||
echo "%~dp0" | findstr /C:"%TEMP%" 1>nul
|
|
||||||
if not errorlevel 1 (
|
SET installerDir=%~dp0
|
||||||
|
|
||||||
|
REM make sure we're not running within a zip folder
|
||||||
|
echo %installerDir% | findstr /C:"%TEMP%" 1>nul
|
||||||
|
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 Oops! It looks like you're running the installer from inside a zip file. Make sure you unzip the download first.
|
||||||
|
echo.
|
||||||
pause
|
pause
|
||||||
) else (
|
exit
|
||||||
start /WAIT /B internal\windows-install.exe
|
)
|
||||||
|
|
||||||
|
REM make sure .NET 5 is installed
|
||||||
|
WHERE dotnet /q
|
||||||
|
if %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo Oops! You must have .NET 5 ^(desktop x64^) installed to use SMAPI: https://dotnet.microsoft.com/download/dotnet/5.0/runtime
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit
|
||||||
|
)
|
||||||
|
dotnet --info | findstr /C:"Microsoft.WindowsDesktop.App 5." 1>nul
|
||||||
|
if %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo Oops! You must have .NET 5 ^(desktop x64^) installed to use SMAPI: https://dotnet.microsoft.com/download/dotnet/5.0/runtime
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
REM start installer
|
||||||
|
dotnet internal\windows\SMAPI.Installer.dll
|
||||||
|
|
||||||
|
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
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.10.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.10.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0">
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -20,5 +16,4 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Import Project="..\..\build\common.targets" />
|
<Import Project="..\..\build\common.targets" />
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.10.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.10.0" />
|
||||||
<PackageReference Update="NETStandard.Library" PrivateAssets="all" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -8,6 +8,7 @@ using System.Text.RegularExpressions;
|
||||||
using Microsoft.Build.Framework;
|
using Microsoft.Build.Framework;
|
||||||
using Microsoft.Build.Utilities;
|
using Microsoft.Build.Utilities;
|
||||||
using StardewModdingAPI.ModBuildConfig.Framework;
|
using StardewModdingAPI.ModBuildConfig.Framework;
|
||||||
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
|
|
||||||
namespace StardewModdingAPI.ModBuildConfig
|
namespace StardewModdingAPI.ModBuildConfig
|
||||||
{
|
{
|
||||||
|
@ -17,6 +18,10 @@ namespace StardewModdingAPI.ModBuildConfig
|
||||||
/*********
|
/*********
|
||||||
** Accessors
|
** Accessors
|
||||||
*********/
|
*********/
|
||||||
|
/// <summary>The name (without extension or path) of the current mod's DLL.</summary>
|
||||||
|
[Required]
|
||||||
|
public string ModDllName { get; set; }
|
||||||
|
|
||||||
/// <summary>The name of the mod folder.</summary>
|
/// <summary>The name of the mod folder.</summary>
|
||||||
[Required]
|
[Required]
|
||||||
public string ModFolderName { get; set; }
|
public string ModFolderName { get; set; }
|
||||||
|
@ -45,9 +50,15 @@ namespace StardewModdingAPI.ModBuildConfig
|
||||||
[Required]
|
[Required]
|
||||||
public bool EnableModZip { get; set; }
|
public bool EnableModZip { get; set; }
|
||||||
|
|
||||||
/// <summary>Custom comma-separated regex patterns matching files to ignore when deploying or zipping the mod.</summary>
|
/// <summary>A comma-separated list of regex patterns matching files to ignore when deploying or zipping the mod.</summary>
|
||||||
public string IgnoreModFilePatterns { get; set; }
|
public string IgnoreModFilePatterns { get; set; }
|
||||||
|
|
||||||
|
/// <summary>A comma-separated list of relative file paths to ignore when deploying or zipping the mod.</summary>
|
||||||
|
public string IgnoreModFilePaths { get; set; }
|
||||||
|
|
||||||
|
/// <summary>A comma-separated list of <see cref="ExtraAssemblyTypes"/> values which indicate which extra DLLs to bundle.</summary>
|
||||||
|
public string BundleExtraAssemblies { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
|
@ -69,11 +80,15 @@ namespace StardewModdingAPI.ModBuildConfig
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// parse extra DLLs to bundle
|
||||||
|
ExtraAssemblyTypes bundleAssemblyTypes = this.GetExtraAssembliesToBundleOption();
|
||||||
|
|
||||||
// parse ignore patterns
|
// parse ignore patterns
|
||||||
|
string[] ignoreFilePaths = this.GetCustomIgnoreFilePaths().ToArray();
|
||||||
Regex[] ignoreFilePatterns = this.GetCustomIgnorePatterns().ToArray();
|
Regex[] ignoreFilePatterns = this.GetCustomIgnorePatterns().ToArray();
|
||||||
|
|
||||||
// get mod info
|
// get mod info
|
||||||
ModFileManager package = new ModFileManager(this.ProjectDir, this.TargetDir, ignoreFilePatterns, validateRequiredModFiles: this.EnableModDeploy || this.EnableModZip);
|
ModFileManager package = new ModFileManager(this.ProjectDir, this.TargetDir, ignoreFilePaths, ignoreFilePatterns, bundleAssemblyTypes, this.ModDllName, validateRequiredModFiles: this.EnableModDeploy || this.EnableModZip);
|
||||||
|
|
||||||
// deploy mod files
|
// deploy mod files
|
||||||
if (this.EnableModDeploy)
|
if (this.EnableModDeploy)
|
||||||
|
@ -134,6 +149,28 @@ namespace StardewModdingAPI.ModBuildConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Parse the extra assembly types which should be bundled with the mod.</summary>
|
||||||
|
private ExtraAssemblyTypes GetExtraAssembliesToBundleOption()
|
||||||
|
{
|
||||||
|
ExtraAssemblyTypes flags = ExtraAssemblyTypes.None;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(this.BundleExtraAssemblies))
|
||||||
|
{
|
||||||
|
foreach (string raw in this.BundleExtraAssemblies.Split(','))
|
||||||
|
{
|
||||||
|
if (!Enum.TryParse(raw, out ExtraAssemblyTypes type))
|
||||||
|
{
|
||||||
|
this.Log.LogWarning($"[mod build package] Ignored invalid <{nameof(this.BundleExtraAssemblies)}> value '{raw}', expected one of '{string.Join("', '", Enum.GetNames(typeof(ExtraAssemblyTypes)))}'.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags |= type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Get the custom ignore patterns provided by the user.</summary>
|
/// <summary>Get the custom ignore patterns provided by the user.</summary>
|
||||||
private IEnumerable<Regex> GetCustomIgnorePatterns()
|
private IEnumerable<Regex> GetCustomIgnorePatterns()
|
||||||
{
|
{
|
||||||
|
@ -157,6 +194,29 @@ namespace StardewModdingAPI.ModBuildConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the custom relative file paths provided by the user to ignore.</summary>
|
||||||
|
private IEnumerable<string> GetCustomIgnoreFilePaths()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(this.IgnoreModFilePaths))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (string raw in this.IgnoreModFilePaths.Split(','))
|
||||||
|
{
|
||||||
|
string path;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
path = PathUtilities.NormalizePath(raw);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.Log.LogWarning($"[mod build package] Ignored invalid <{nameof(this.IgnoreModFilePaths)}> path {raw}:\n{ex}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Copy the mod files into the game's mod folder.</summary>
|
/// <summary>Copy the mod files into the game's mod folder.</summary>
|
||||||
/// <param name="files">The files to include.</param>
|
/// <param name="files">The files to include.</param>
|
||||||
/// <param name="modFolderPath">The folder path to create with the mod files.</param>
|
/// <param name="modFolderPath">The folder path to create with the mod files.</param>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.ModBuildConfig.Framework
|
||||||
|
{
|
||||||
|
/// <summary>An extra assembly type for the <see cref="DeployModTask.BundleExtraAssemblies"/> field.</summary>
|
||||||
|
[Flags]
|
||||||
|
internal enum ExtraAssemblyTypes
|
||||||
|
{
|
||||||
|
/// <summary>Don't include extra assemblies.</summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>Assembly files which are part of MonoGame, SMAPI, or Stardew Valley.</summary>
|
||||||
|
Game = 1,
|
||||||
|
|
||||||
|
/// <summary>Assembly files whose names start with <c>Microsoft.*</c> or <c>System.*</c>.</summary>
|
||||||
|
System = 2,
|
||||||
|
|
||||||
|
/// <summary>Assembly files which don't match any other category.</summary>
|
||||||
|
ThirdParty = 4
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,45 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
|
||||||
/// <summary>The files that are part of the package.</summary>
|
/// <summary>The files that are part of the package.</summary>
|
||||||
private readonly IDictionary<string, FileInfo> Files;
|
private readonly IDictionary<string, FileInfo> Files;
|
||||||
|
|
||||||
|
/// <summary>The file extensions used by assembly files.</summary>
|
||||||
|
private readonly ISet<string> AssemblyFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
".dll",
|
||||||
|
".exe",
|
||||||
|
".pdb",
|
||||||
|
".xml"
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>The DLLs which match the <see cref="ExtraAssemblyTypes.Game"/> type.</summary>
|
||||||
|
private readonly ISet<string> GameDllNames = new HashSet<string>
|
||||||
|
{
|
||||||
|
// SMAPI
|
||||||
|
"0Harmony",
|
||||||
|
"Mono.Cecil",
|
||||||
|
"Mono.Cecil.Mdb",
|
||||||
|
"Mono.Cecil.Pdb",
|
||||||
|
"MonoMod.Common",
|
||||||
|
"Newtonsoft.Json",
|
||||||
|
"StardewModdingAPI",
|
||||||
|
"SMAPI.Toolkit",
|
||||||
|
"SMAPI.Toolkit.CoreInterfaces",
|
||||||
|
"TMXTile",
|
||||||
|
|
||||||
|
// game + framework
|
||||||
|
"BmFont",
|
||||||
|
"FAudio-CS",
|
||||||
|
"GalaxyCSharp",
|
||||||
|
"GalaxyCSharpGlue",
|
||||||
|
"Lidgren.Network",
|
||||||
|
"MonoGame.Framework",
|
||||||
|
"SkiaSharp",
|
||||||
|
"Stardew Valley",
|
||||||
|
"StardewValley.GameData",
|
||||||
|
"Steamworks.NET",
|
||||||
|
"TextCopy",
|
||||||
|
"xTile"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
|
@ -28,10 +67,13 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
/// <param name="projectDir">The folder containing the project files.</param>
|
/// <param name="projectDir">The folder containing the project files.</param>
|
||||||
/// <param name="targetDir">The folder containing the build output.</param>
|
/// <param name="targetDir">The folder containing the build output.</param>
|
||||||
|
/// <param name="ignoreFilePaths">The custom relative file paths provided by the user to ignore.</param>
|
||||||
/// <param name="ignoreFilePatterns">Custom regex patterns matching files to ignore when deploying or zipping the mod.</param>
|
/// <param name="ignoreFilePatterns">Custom regex patterns matching files to ignore when deploying or zipping the mod.</param>
|
||||||
|
/// <param name="bundleAssemblyTypes">The extra assembly types which should be bundled with the mod.</param>
|
||||||
|
/// <param name="modDllName">The name (without extension or path) for the current mod's DLL.</param>
|
||||||
/// <param name="validateRequiredModFiles">Whether to validate that required mod files like the manifest are present.</param>
|
/// <param name="validateRequiredModFiles">Whether to validate that required mod files like the manifest are present.</param>
|
||||||
/// <exception cref="UserErrorException">The mod package isn't valid.</exception>
|
/// <exception cref="UserErrorException">The mod package isn't valid.</exception>
|
||||||
public ModFileManager(string projectDir, string targetDir, Regex[] ignoreFilePatterns, bool validateRequiredModFiles)
|
public ModFileManager(string projectDir, string targetDir, string[] ignoreFilePaths, Regex[] ignoreFilePatterns, ExtraAssemblyTypes bundleAssemblyTypes, string modDllName, bool validateRequiredModFiles)
|
||||||
{
|
{
|
||||||
this.Files = new Dictionary<string, FileInfo>(StringComparer.OrdinalIgnoreCase);
|
this.Files = new Dictionary<string, FileInfo>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
@ -47,7 +89,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
|
||||||
string relativePath = entry.Item1;
|
string relativePath = entry.Item1;
|
||||||
FileInfo file = entry.Item2;
|
FileInfo file = entry.Item2;
|
||||||
|
|
||||||
if (!this.ShouldIgnore(file, relativePath, ignoreFilePatterns))
|
if (!this.ShouldIgnore(file, relativePath, ignoreFilePaths, ignoreFilePatterns, bundleAssemblyTypes, modDllName))
|
||||||
this.Files[relativePath] = file;
|
this.Files[relativePath] = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,36 +191,72 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
|
||||||
/// <summary>Get whether a build output file should be ignored.</summary>
|
/// <summary>Get whether a build output file should be ignored.</summary>
|
||||||
/// <param name="file">The file to check.</param>
|
/// <param name="file">The file to check.</param>
|
||||||
/// <param name="relativePath">The file's relative path in the package.</param>
|
/// <param name="relativePath">The file's relative path in the package.</param>
|
||||||
|
/// <param name="ignoreFilePaths">The custom relative file paths provided by the user to ignore.</param>
|
||||||
/// <param name="ignoreFilePatterns">Custom regex patterns matching files to ignore when deploying or zipping the mod.</param>
|
/// <param name="ignoreFilePatterns">Custom regex patterns matching files to ignore when deploying or zipping the mod.</param>
|
||||||
private bool ShouldIgnore(FileInfo file, string relativePath, Regex[] ignoreFilePatterns)
|
/// <param name="bundleAssemblyTypes">The extra assembly types which should be bundled with the mod.</param>
|
||||||
|
/// <param name="modDllName">The name (without extension or path) for the current mod's DLL.</param>
|
||||||
|
private bool ShouldIgnore(FileInfo file, string relativePath, string[] ignoreFilePaths, Regex[] ignoreFilePatterns, ExtraAssemblyTypes bundleAssemblyTypes, string modDllName)
|
||||||
{
|
{
|
||||||
return
|
// apply custom patterns
|
||||||
|
if (ignoreFilePaths.Any(p => p == relativePath) || ignoreFilePatterns.Any(p => p.IsMatch(relativePath)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// ignore unneeded files
|
||||||
|
{
|
||||||
|
bool shouldIgnore =
|
||||||
// release zips
|
// release zips
|
||||||
this.EqualsInvariant(file.Extension, ".zip")
|
this.EqualsInvariant(file.Extension, ".zip")
|
||||||
|
|
||||||
// Harmony (bundled into SMAPI)
|
// *.deps.json (only SMAPI's top-level one is used)
|
||||||
|| this.EqualsInvariant(file.Name, "0Harmony.dll")
|
|| file.Name.EndsWith(".deps.json")
|
||||||
|
|
||||||
// Json.NET (bundled into SMAPI)
|
|
||||||
|| this.EqualsInvariant(file.Name, "Newtonsoft.Json.dll")
|
|
||||||
|| this.EqualsInvariant(file.Name, "Newtonsoft.Json.pdb")
|
|
||||||
|| this.EqualsInvariant(file.Name, "Newtonsoft.Json.xml")
|
|
||||||
|
|
||||||
// mod translation class builder (not used at runtime)
|
|
||||||
|| this.EqualsInvariant(file.Name, "Pathoschild.Stardew.ModTranslationClassBuilder.dll")
|
|
||||||
|| this.EqualsInvariant(file.Name, "Pathoschild.Stardew.ModTranslationClassBuilder.pdb")
|
|
||||||
|| this.EqualsInvariant(file.Name, "Pathoschild.Stardew.ModTranslationClassBuilder.xml")
|
|
||||||
|
|
||||||
// code analysis files
|
// code analysis files
|
||||||
|| file.Name.EndsWith(".CodeAnalysisLog.xml", StringComparison.OrdinalIgnoreCase)
|
|| file.Name.EndsWith(".CodeAnalysisLog.xml", StringComparison.OrdinalIgnoreCase)
|
||||||
|| file.Name.EndsWith(".lastcodeanalysissucceeded", StringComparison.OrdinalIgnoreCase)
|
|| file.Name.EndsWith(".lastcodeanalysissucceeded", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|
||||||
|
// translation class builder (not used at runtime)
|
||||||
|
|| (
|
||||||
|
file.Name.StartsWith("Pathoschild.Stardew.ModTranslationClassBuilder")
|
||||||
|
&& this.AssemblyFileExtensions.Contains(file.Extension)
|
||||||
|
)
|
||||||
|
|
||||||
// OS metadata files
|
// OS metadata files
|
||||||
|| this.EqualsInvariant(file.Name, ".DS_Store")
|
|| this.EqualsInvariant(file.Name, ".DS_Store")
|
||||||
|| this.EqualsInvariant(file.Name, "Thumbs.db")
|
|| this.EqualsInvariant(file.Name, "Thumbs.db");
|
||||||
|
if (shouldIgnore)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// custom ignore patterns
|
// check for bundled assembly types
|
||||||
|| ignoreFilePatterns.Any(p => p.IsMatch(relativePath));
|
// When bundleAssemblyTypes is set, *all* dependencies are copied into the build output but only those which match the given assembly types should be bundled.
|
||||||
|
if (bundleAssemblyTypes != ExtraAssemblyTypes.None)
|
||||||
|
{
|
||||||
|
var type = this.GetExtraAssemblyType(file, modDllName);
|
||||||
|
if (type != ExtraAssemblyTypes.None && !bundleAssemblyTypes.HasFlag(type))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the extra assembly type for a file, assuming that the user specified one or more extra types to bundle.</summary>
|
||||||
|
/// <param name="file">The file to check.</param>
|
||||||
|
/// <param name="modDllName">The name (without extension or path) for the current mod's DLL.</param>
|
||||||
|
private ExtraAssemblyTypes GetExtraAssemblyType(FileInfo file, string modDllName)
|
||||||
|
{
|
||||||
|
string baseName = Path.GetFileNameWithoutExtension(file.Name);
|
||||||
|
string extension = file.Extension;
|
||||||
|
|
||||||
|
if (baseName == modDllName || !this.AssemblyFileExtensions.Contains(extension))
|
||||||
|
return ExtraAssemblyTypes.None;
|
||||||
|
|
||||||
|
if (this.GameDllNames.Contains(baseName))
|
||||||
|
return ExtraAssemblyTypes.Game;
|
||||||
|
|
||||||
|
if (baseName.StartsWith("System.", StringComparison.OrdinalIgnoreCase) || baseName.StartsWith("Microsoft.", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ExtraAssemblyTypes.System;
|
||||||
|
|
||||||
|
return ExtraAssemblyTypes.ThirdParty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get whether a string is equal to another case-insensitively.</summary>
|
/// <summary>Get whether a string is equal to another case-insensitively.</summary>
|
||||||
|
|
|
@ -2,28 +2,28 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!--build-->
|
<!--build-->
|
||||||
<RootNamespace>StardewModdingAPI.ModBuildConfig</RootNamespace>
|
<RootNamespace>StardewModdingAPI.ModBuildConfig</RootNamespace>
|
||||||
<TargetFramework>net452</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
|
||||||
<!--NuGet package-->
|
<!--NuGet package-->
|
||||||
<PackageId>Pathoschild.Stardew.ModBuildConfig</PackageId>
|
<PackageId>Pathoschild.Stardew.ModBuildConfig</PackageId>
|
||||||
<Title>Build package for SMAPI mods</Title>
|
<Title>Build package for SMAPI mods</Title>
|
||||||
<Version>3.3.0</Version>
|
<Version>4.0.0</Version>
|
||||||
<Authors>Pathoschild</Authors>
|
<Authors>Pathoschild</Authors>
|
||||||
<Description>Automates the build configuration for crossplatform Stardew Valley SMAPI mods. For SMAPI 3.0 or later.</Description>
|
<Description>Automates the build configuration for crossplatform Stardew Valley SMAPI mods. For SMAPI 3.13.0 or later.</Description>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<PackageIcon>images/icon.png</PackageIcon>
|
<PackageIcon>images/icon.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://smapi.io/package/readme</PackageProjectUrl>
|
<PackageProjectUrl>https://smapi.io/package/readme</PackageProjectUrl>
|
||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
|
||||||
|
<!--copy dependency DLLs to bin folder so we can include them in package -->
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.Build" />
|
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.10" />
|
||||||
<Reference Include="Microsoft.Build.Framework" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<Reference Include="Microsoft.Build.Utilities.v4.0" />
|
|
||||||
<Reference Include="System.IO.Compression" />
|
|
||||||
<Reference Include="System.Web.Extensions" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
|
||||||
<!-- recognise XNA Framework DLLs in the GAC (only affects mods using new csproj format) -->
|
<!-- don't create the 'refs' folder (which isn't useful for mods) -->
|
||||||
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
|
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||||
|
|
||||||
<!-- suppress processor architecture mismatch warning (mods should be compiled in 'Any CPU' so they work in both 32-bit and 64-bit mode) -->
|
<!-- suppress processor architecture mismatch warning (mods should be compiled in 'Any CPU' so they work in both 32-bit and 64-bit mode) -->
|
||||||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||||
|
@ -26,10 +26,10 @@
|
||||||
<EnableModZip Condition="'$(EnableModZip)' == ''">true</EnableModZip>
|
<EnableModZip Condition="'$(EnableModZip)' == ''">true</EnableModZip>
|
||||||
<EnableHarmony Condition="'$(EnableHarmony)' == ''">false</EnableHarmony>
|
<EnableHarmony Condition="'$(EnableHarmony)' == ''">false</EnableHarmony>
|
||||||
<EnableGameDebugging Condition="'$(EnableGameDebugging)' == ''">true</EnableGameDebugging>
|
<EnableGameDebugging Condition="'$(EnableGameDebugging)' == ''">true</EnableGameDebugging>
|
||||||
<CopyModReferencesToBuildOutput Condition="'$(CopyModReferencesToBuildOutput)' == '' OR ('$(CopyModReferencesToBuildOutput)' != 'true' AND '$(CopyModReferencesToBuildOutput)' != 'false')">false</CopyModReferencesToBuildOutput>
|
<BundleExtraAssemblies Condition="'$(BundleExtraAssemblies)' == ''"></BundleExtraAssemblies>
|
||||||
|
|
||||||
<GameFramework Condition="'$(GameFramework)' == '' AND '$(OS)' == 'Windows_NT'">Xna</GameFramework>
|
<!-- coppy referenced DLLs into build output -->
|
||||||
<GameFramework Condition="'$(GameFramework)' == ''">MonoGame</GameFramework>
|
<CopyLocalLockFileAssemblies Condition="$(BundleExtraAssemblies.Contains('ThirdParty')) OR $(BundleExtraAssemblies.Contains('Game')) OR $(BundleExtraAssemblies.Contains('System')) OR $(BundleExtraAssemblies.Contains('All'))">true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(OS)' == 'Windows_NT' AND '$(EnableGameDebugging)' == 'true'">
|
<PropertyGroup Condition="'$(OS)' == 'Windows_NT' AND '$(EnableGameDebugging)' == 'true'">
|
||||||
|
@ -43,37 +43,20 @@
|
||||||
<!--*********************************************
|
<!--*********************************************
|
||||||
** Add assembly references
|
** Add assembly references
|
||||||
**********************************************-->
|
**********************************************-->
|
||||||
<!-- common -->
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="$(CopyModReferencesToBuildOutput)" />
|
<!-- game -->
|
||||||
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="$(CopyModReferencesToBuildOutput)" />
|
<Reference Include="Stardew Valley" HintPath="$(GamePath)\Stardew Valley.dll" Private="$(BundleExtraAssemblies.Contains('Game'))" />
|
||||||
<Reference Include="StardewModdingAPI" HintPath="$(GamePath)\StardewModdingAPI.exe" Private="$(CopyModReferencesToBuildOutput)" />
|
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="$(BundleExtraAssemblies.Contains('Game'))" />
|
||||||
<Reference Include="SMAPI.Toolkit.CoreInterfaces" HintPath="$(GamePath)\smapi-internal\SMAPI.Toolkit.CoreInterfaces.dll" Private="$(CopyModReferencesToBuildOutput)" />
|
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="$(BundleExtraAssemblies.Contains('Game'))" />
|
||||||
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="$(CopyModReferencesToBuildOutput)" />
|
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="$(BundleExtraAssemblies.Contains('Game'))" />
|
||||||
<Reference Include="0Harmony" Condition="'$(EnableHarmony)' == 'true'" HintPath="$(GamePath)\smapi-internal\0Harmony.dll" Private="$(CopyModReferencesToBuildOutput)" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Windows only -->
|
<!-- SMAPI -->
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
<Reference Include="StardewModdingAPI" HintPath="$(GamePath)\StardewModdingAPI.dll" Private="$(BundleExtraAssemblies.Contains('Game'))" />
|
||||||
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="$(CopyModReferencesToBuildOutput)" />
|
<Reference Include="SMAPI.Toolkit.CoreInterfaces" HintPath="$(GamePath)\smapi-internal\SMAPI.Toolkit.CoreInterfaces.dll" Private="$(BundleExtraAssemblies.Contains('Game'))" />
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Game framework -->
|
<!-- Harmony -->
|
||||||
<Choose>
|
<Reference Include="0Harmony" Condition="'$(EnableHarmony)' == 'true'" HintPath="$(GamePath)\smapi-internal\0Harmony.dll" Private="$(BundleExtraAssemblies.Contains('Game'))" />
|
||||||
<When Condition="'$(GameFramework)' == 'Xna'">
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</When>
|
|
||||||
<Otherwise>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="$(CopyModReferencesToBuildOutput)" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Otherwise>
|
|
||||||
</Choose>
|
|
||||||
|
|
||||||
|
|
||||||
<!--*********************************************
|
<!--*********************************************
|
||||||
|
@ -85,8 +68,8 @@
|
||||||
|
|
||||||
<!-- invalid game path -->
|
<!-- invalid game path -->
|
||||||
<Error Condition="!Exists('$(GamePath)')" Text="The mod build package can't find your game folder. You can specify where to find it; see https://smapi.io/package/custom-game-path." ContinueOnError="false" />
|
<Error Condition="!Exists('$(GamePath)')" Text="The mod build package can't find your game folder. You can specify where to find it; see https://smapi.io/package/custom-game-path." ContinueOnError="false" />
|
||||||
<Error Condition="!Exists('$(GamePath)\$(GameExecutableName).exe')" Text="The mod build package found a game folder at $(GamePath), but it doesn't contain the $(GameExecutableName) file. If this folder is invalid, delete it and the package will autodetect another game install path." ContinueOnError="false" />
|
<Error Condition="!Exists('$(GamePath)\Stardew Valley.dll')" Text="The mod build package found a game folder at $(GamePath), but it doesn't contain the Stardew Valley file. If this folder is invalid, delete it and the package will autodetect another game install path." ContinueOnError="false" />
|
||||||
<Error Condition="!Exists('$(GamePath)\StardewModdingAPI.exe')" Text="The mod build package found a game folder at $(GamePath), but it doesn't contain SMAPI. You need to install SMAPI before building the mod." ContinueOnError="false" />
|
<Error Condition="!Exists('$(GamePath)\StardewModdingAPI.dll')" Text="The mod build package found a game folder at $(GamePath), but it doesn't contain SMAPI. You need to install SMAPI before building the mod." ContinueOnError="false" />
|
||||||
|
|
||||||
<!-- invalid target architecture (note: internal value is 'AnyCPU', value shown in Visual Studio is 'Any CPU') -->
|
<!-- invalid target architecture (note: internal value is 'AnyCPU', value shown in Visual Studio is 'Any CPU') -->
|
||||||
<Warning Condition="'$(Platform)' != 'AnyCPU'" 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 '$(Platform)'). See https://smapi.io/package/wrong-processor-architecture for details." HelpLink="https://smapi.io/package/wrong-processor-architecture" />
|
<Warning Condition="'$(Platform)' != 'AnyCPU'" 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 '$(Platform)'). See https://smapi.io/package/wrong-processor-architecture for details." HelpLink="https://smapi.io/package/wrong-processor-architecture" />
|
||||||
|
@ -98,6 +81,7 @@
|
||||||
**********************************************-->
|
**********************************************-->
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
<DeployModTask
|
<DeployModTask
|
||||||
|
ModDllName="$(TargetName)"
|
||||||
ModFolderName="$(ModFolderName)"
|
ModFolderName="$(ModFolderName)"
|
||||||
ModZipPath="$(ModZipPath)"
|
ModZipPath="$(ModZipPath)"
|
||||||
|
|
||||||
|
@ -108,6 +92,9 @@
|
||||||
TargetDir="$(TargetDir)"
|
TargetDir="$(TargetDir)"
|
||||||
GameModsDir="$(GameModsPath)"
|
GameModsDir="$(GameModsPath)"
|
||||||
IgnoreModFilePatterns="$(IgnoreModFilePatterns)"
|
IgnoreModFilePatterns="$(IgnoreModFilePatterns)"
|
||||||
|
IgnoreModFilePaths="$(IgnoreModFilePaths)"
|
||||||
|
|
||||||
|
BundleExtraAssemblies="$(BundleExtraAssemblies)"
|
||||||
/>
|
/>
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using StardewValley;
|
||||||
|
using StardewValley.GameData;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
|
||||||
|
{
|
||||||
|
/// <summary>A command which changes the player's farm type.</summary>
|
||||||
|
internal class SetFarmTypeCommand : ConsoleCommand
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
public SetFarmTypeCommand()
|
||||||
|
: base("set_farm_type", "Sets the current player's farm type.\n\nUsage: set_farm_type <farm type>\n- farm type: the farm type to set. Enter `set_farm_type list` for a list of available farm types.") { }
|
||||||
|
|
||||||
|
/// <summary>Handle the command.</summary>
|
||||||
|
/// <param name="monitor">Writes messages to the console and log file.</param>
|
||||||
|
/// <param name="command">The command name.</param>
|
||||||
|
/// <param name="args">The command arguments.</param>
|
||||||
|
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
|
||||||
|
{
|
||||||
|
// validate
|
||||||
|
if (!Context.IsWorldReady)
|
||||||
|
{
|
||||||
|
monitor.Log("You must load a save to use this command.", LogLevel.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse arguments
|
||||||
|
if (!args.TryGet(0, "farm type", out string farmType))
|
||||||
|
return;
|
||||||
|
bool isVanillaId = int.TryParse(farmType, out int vanillaId) && vanillaId is (>= 0 and < Farm.layout_max);
|
||||||
|
|
||||||
|
// handle argument
|
||||||
|
if (farmType == "list")
|
||||||
|
this.HandleList(monitor);
|
||||||
|
else if (isVanillaId)
|
||||||
|
this.HandleVanillaFarmType(vanillaId, monitor);
|
||||||
|
else
|
||||||
|
this.HandleCustomFarmType(farmType, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Private methods
|
||||||
|
*********/
|
||||||
|
/****
|
||||||
|
** Handlers
|
||||||
|
****/
|
||||||
|
/// <summary>Print a list of available farm types.</summary>
|
||||||
|
/// <param name="monitor">Writes messages to the console and log file.</param>
|
||||||
|
private void HandleList(IMonitor monitor)
|
||||||
|
{
|
||||||
|
StringBuilder result = new();
|
||||||
|
|
||||||
|
// list vanilla types
|
||||||
|
result.AppendLine("The farm type can be one of these vanilla types:");
|
||||||
|
foreach (var type in this.GetVanillaFarmTypes())
|
||||||
|
result.AppendLine($" - {type.Key} ({type.Value})");
|
||||||
|
result.AppendLine();
|
||||||
|
|
||||||
|
// list custom types
|
||||||
|
{
|
||||||
|
var customTypes = this.GetCustomFarmTypes();
|
||||||
|
if (customTypes.Any())
|
||||||
|
{
|
||||||
|
result.AppendLine("Or one of these custom farm types:");
|
||||||
|
foreach (var type in customTypes.Values.OrderBy(p => p.ID))
|
||||||
|
result.AppendLine($" - {type.ID} ({this.GetCustomName(type)})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result.AppendLine("Or a custom farm type (though none is loaded currently).");
|
||||||
|
}
|
||||||
|
|
||||||
|
// print
|
||||||
|
monitor.Log(result.ToString(), LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Set a vanilla farm type.</summary>
|
||||||
|
/// <param name="type">The farm type.</param>
|
||||||
|
/// <param name="monitor">Writes messages to the console and log file.</param>
|
||||||
|
private void HandleVanillaFarmType(int type, IMonitor monitor)
|
||||||
|
{
|
||||||
|
if (Game1.whichFarm == type)
|
||||||
|
{
|
||||||
|
monitor.Log($"Your current farm is already set to {type} ({this.GetVanillaName(type)}).", LogLevel.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SetFarmType(type, null);
|
||||||
|
this.PrintSuccess(monitor, $"{type} ({this.GetVanillaName(type)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Set a custom farm type.</summary>
|
||||||
|
/// <param name="id">The farm type ID.</param>
|
||||||
|
/// <param name="monitor">Writes messages to the console and log file.</param>
|
||||||
|
private void HandleCustomFarmType(string id, IMonitor monitor)
|
||||||
|
{
|
||||||
|
if (Game1.whichModFarm?.ID == id)
|
||||||
|
{
|
||||||
|
monitor.Log($"Your current farm is already set to {id} ({this.GetCustomName(Game1.whichModFarm)}).", LogLevel.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.GetCustomFarmTypes().TryGetValue(id, out ModFarmType customFarmType))
|
||||||
|
{
|
||||||
|
monitor.Log($"Invalid farm type '{id}'. Enter `help set_farm_type` for more info.", LogLevel.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SetFarmType(Farm.mod_layout, customFarmType);
|
||||||
|
this.PrintSuccess(monitor, $"{id} ({this.GetCustomName(customFarmType)})");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Change the farm type.</summary>
|
||||||
|
/// <param name="type">The farm type ID.</param>
|
||||||
|
/// <param name="customFarmData">The custom farm type data, if applicable.</param>
|
||||||
|
private void SetFarmType(int type, ModFarmType customFarmData)
|
||||||
|
{
|
||||||
|
// set flags
|
||||||
|
Game1.whichFarm = type;
|
||||||
|
Game1.whichModFarm = customFarmData;
|
||||||
|
|
||||||
|
// update farm map
|
||||||
|
Farm farm = Game1.getFarm();
|
||||||
|
farm.mapPath.Value = $@"Maps\{Farm.getMapNameFromTypeInt(Game1.whichFarm)}";
|
||||||
|
farm.reloadMap();
|
||||||
|
|
||||||
|
// clear spouse area cache to avoid errors
|
||||||
|
FieldInfo cacheField = farm.GetType().GetField("_baseSpouseAreaTiles", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||||
|
if (cacheField == null)
|
||||||
|
throw new InvalidOperationException("Failed to access '_baseSpouseAreaTiles' field to clear spouse area cache.");
|
||||||
|
if (cacheField.GetValue(farm) is not IDictionary cache)
|
||||||
|
throw new InvalidOperationException($"The farm's '_baseSpouseAreaTiles' field didn't match the expected {nameof(IDictionary)} type.");
|
||||||
|
cache.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintSuccess(IMonitor monitor, string label)
|
||||||
|
{
|
||||||
|
StringBuilder result = new();
|
||||||
|
result.AppendLine($"Your current farm has been converted to {label}. Saving and reloading is recommended to make sure everything is updated for the change.");
|
||||||
|
result.AppendLine();
|
||||||
|
result.AppendLine("This doesn't move items that are out of bounds on the new map. If you need to clean up, you can...");
|
||||||
|
result.AppendLine(" - temporarily switch back to the previous farm type;");
|
||||||
|
result.AppendLine(" - or use a mod like Noclip Mode: https://www.nexusmods.com/stardewvalley/mods/3900 ;");
|
||||||
|
result.AppendLine(" - or use the world_clear console command (enter `help world_clear` for details).");
|
||||||
|
|
||||||
|
monitor.Log(result.ToString(), LogLevel.Warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
** Vanilla farm types
|
||||||
|
****/
|
||||||
|
/// <summary>Get the display name for a vanilla farm type.</summary>
|
||||||
|
/// <param name="type">The farm type.</param>
|
||||||
|
private string GetVanillaName(int type)
|
||||||
|
{
|
||||||
|
string translationKey = type switch
|
||||||
|
{
|
||||||
|
Farm.default_layout => "Character_FarmStandard",
|
||||||
|
Farm.riverlands_layout => "Character_FarmFishing",
|
||||||
|
Farm.forest_layout => "Character_FarmForaging",
|
||||||
|
Farm.mountains_layout => "Character_FarmMining",
|
||||||
|
Farm.combat_layout => "Character_FarmCombat",
|
||||||
|
Farm.fourCorners_layout => "Character_FarmFourCorners",
|
||||||
|
Farm.beach_layout => "Character_FarmBeach",
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
return translationKey != null
|
||||||
|
? Game1.content.LoadString(@$"Strings\UI:{translationKey}").Split('_')[0]
|
||||||
|
: type.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the available vanilla farm types by ID.</summary>
|
||||||
|
private IDictionary<int, string> GetVanillaFarmTypes()
|
||||||
|
{
|
||||||
|
IDictionary<int, string> farmTypes = new Dictionary<int, string>();
|
||||||
|
|
||||||
|
foreach (int id in Enumerable.Range(0, Farm.layout_max))
|
||||||
|
farmTypes[id] = this.GetVanillaName(id);
|
||||||
|
|
||||||
|
return farmTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
** Custom farm types
|
||||||
|
****/
|
||||||
|
/// <summary>Get the display name for a custom farm type.</summary>
|
||||||
|
/// <param name="farmType">The custom farm type.</param>
|
||||||
|
private string GetCustomName(ModFarmType farmType)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(farmType?.TooltipStringPath))
|
||||||
|
return farmType?.ID;
|
||||||
|
|
||||||
|
return Game1.content.LoadString(farmType.TooltipStringPath)?.Split('_')[0] ?? farmType.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the available custom farm types by ID.</summary>
|
||||||
|
private IDictionary<string, ModFarmType> GetCustomFarmTypes()
|
||||||
|
{
|
||||||
|
IDictionary<string, ModFarmType> farmTypes = new Dictionary<string, ModFarmType>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (ModFarmType farmType in Game1.content.Load<List<ModFarmType>>("Data\\AdditionalFarms"))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(farmType.ID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
farmTypes[farmType.ID] = farmType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return farmTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>ConsoleCommands</AssemblyName>
|
<AssemblyName>ConsoleCommands</AssemblyName>
|
||||||
<RootNamespace>StardewModdingAPI.Mods.ConsoleCommands</RootNamespace>
|
<RootNamespace>StardewModdingAPI.Mods.ConsoleCommands</RootNamespace>
|
||||||
<TargetFramework>net452</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -12,32 +12,12 @@
|
||||||
<ProjectReference Include="..\SMAPI\SMAPI.csproj" Private="False" />
|
<ProjectReference Include="..\SMAPI\SMAPI.csproj" Private="False" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
|
|
||||||
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Windows only -->
|
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
|
||||||
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Game framework -->
|
|
||||||
<Choose>
|
|
||||||
<When Condition="$(DefineConstants.Contains(SMAPI_FOR_XNA))">
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
</When>
|
|
||||||
<Otherwise>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
|
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
|
||||||
|
<Reference Include="Stardew Valley" HintPath="$(GamePath)\Stardew Valley.dll" Private="False" />
|
||||||
|
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
|
||||||
|
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Otherwise>
|
|
||||||
</Choose>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"Name": "Console Commands",
|
"Name": "Console Commands",
|
||||||
"Author": "SMAPI",
|
"Author": "SMAPI",
|
||||||
"Version": "3.12.8",
|
"Version": "3.13.0",
|
||||||
"Description": "Adds SMAPI console commands that let you manipulate the game.",
|
"Description": "Adds SMAPI console commands that let you manipulate the game.",
|
||||||
"UniqueID": "SMAPI.ConsoleCommands",
|
"UniqueID": "SMAPI.ConsoleCommands",
|
||||||
"EntryDll": "ConsoleCommands.dll",
|
"EntryDll": "ConsoleCommands.dll",
|
||||||
"MinimumApiVersion": "3.12.8"
|
"MinimumApiVersion": "3.13.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
|
||||||
public override void Apply(Harmony harmony, IMonitor monitor)
|
public override void Apply(Harmony harmony, IMonitor monitor)
|
||||||
{
|
{
|
||||||
harmony.Patch(
|
harmony.Patch(
|
||||||
original: Constants.GameFramework == GameFramework.Xna
|
original: this.RequireMethod<SpriteBatch>("CheckValid", new[] { typeof(Texture2D) }),
|
||||||
? this.RequireMethod<SpriteBatch>("InternalDraw")
|
|
||||||
: this.RequireMethod<SpriteBatch>("CheckValid", new[] { typeof(Texture2D) }),
|
|
||||||
postfix: this.GetHarmonyMethod(nameof(SpriteBatchPatcher.After_CheckValid))
|
postfix: this.GetHarmonyMethod(nameof(SpriteBatchPatcher.After_CheckValid))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,13 +28,8 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
|
||||||
/*********
|
/*********
|
||||||
** Private methods
|
** Private methods
|
||||||
*********/
|
*********/
|
||||||
#if SMAPI_FOR_XNA
|
|
||||||
/// <summary>The method to call after <see cref="SpriteBatch.InternalDraw"/>.</summary>
|
|
||||||
/// <param name="texture">The texture to validate.</param>
|
|
||||||
#else
|
|
||||||
/// <summary>The method to call after <see cref="SpriteBatch.CheckValid"/>.</summary>
|
/// <summary>The method to call after <see cref="SpriteBatch.CheckValid"/>.</summary>
|
||||||
/// <param name="texture">The texture to validate.</param>
|
/// <param name="texture">The texture to validate.</param>
|
||||||
#endif
|
|
||||||
private static void After_CheckValid(Texture2D texture)
|
private static void After_CheckValid(Texture2D texture)
|
||||||
{
|
{
|
||||||
if (texture?.IsDisposed == true)
|
if (texture?.IsDisposed == true)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>ErrorHandler</AssemblyName>
|
<AssemblyName>ErrorHandler</AssemblyName>
|
||||||
<RootNamespace>StardewModdingAPI.Mods.ErrorHandler</RootNamespace>
|
<RootNamespace>StardewModdingAPI.Mods.ErrorHandler</RootNamespace>
|
||||||
<TargetFramework>net452</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -14,33 +14,12 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
|
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
|
||||||
|
<Reference Include="Stardew Valley" HintPath="$(GamePath)\Stardew Valley.dll" Private="False" />
|
||||||
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
|
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
|
||||||
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
|
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Windows only -->
|
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
|
||||||
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Game framework -->
|
|
||||||
<Choose>
|
|
||||||
<When Condition="$(DefineConstants.Contains(SMAPI_FOR_XNA))">
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
</When>
|
|
||||||
<Otherwise>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Otherwise>
|
|
||||||
</Choose>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="i18n\*.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Update="i18n\*.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
<None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"Name": "Error Handler",
|
"Name": "Error Handler",
|
||||||
"Author": "SMAPI",
|
"Author": "SMAPI",
|
||||||
"Version": "3.12.8",
|
"Version": "3.13.0",
|
||||||
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
|
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
|
||||||
"UniqueID": "SMAPI.ErrorHandler",
|
"UniqueID": "SMAPI.ErrorHandler",
|
||||||
"EntryDll": "ErrorHandler.dll",
|
"EntryDll": "ErrorHandler.dll",
|
||||||
"MinimumApiVersion": "3.12.8"
|
"MinimumApiVersion": "3.13.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>SaveBackup</AssemblyName>
|
<AssemblyName>SaveBackup</AssemblyName>
|
||||||
<RootNamespace>StardewModdingAPI.Mods.SaveBackup</RootNamespace>
|
<RootNamespace>StardewModdingAPI.Mods.SaveBackup</RootNamespace>
|
||||||
<TargetFramework>net452</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
|
<Reference Include="Stardew Valley" HintPath="$(GamePath)\Stardew Valley.dll" Private="False" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"Name": "Save Backup",
|
"Name": "Save Backup",
|
||||||
"Author": "SMAPI",
|
"Author": "SMAPI",
|
||||||
"Version": "3.12.8",
|
"Version": "3.13.0",
|
||||||
"Description": "Automatically backs up all your saves once per day into its folder.",
|
"Description": "Automatically backs up all your saves once per day into its folder.",
|
||||||
"UniqueID": "SMAPI.SaveBackup",
|
"UniqueID": "SMAPI.SaveBackup",
|
||||||
"EntryDll": "SaveBackup.dll",
|
"EntryDll": "SaveBackup.dll",
|
||||||
"MinimumApiVersion": "3.12.8"
|
"MinimumApiVersion": "3.13.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>SMAPI.Tests</AssemblyName>
|
<AssemblyName>SMAPI.Tests</AssemblyName>
|
||||||
<RootNamespace>SMAPI.Tests</RootNamespace>
|
<RootNamespace>SMAPI.Tests</RootNamespace>
|
||||||
<TargetFramework>net452</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -16,16 +16,14 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||||
<PackageReference Include="Moq" Version="4.16.1" />
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="$(GameExecutableName)">
|
<Reference Include="Stardew Valley" HintPath="$(GamePath)\Stardew Valley.dll" Private="True" />
|
||||||
<HintPath>$(GamePath)\$(GameExecutableName).exe</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -182,18 +182,14 @@ namespace SMAPI.Tests.Utilities
|
||||||
[TestCaseSource(nameof(PathUtilitiesTests.SamplePaths))]
|
[TestCaseSource(nameof(PathUtilitiesTests.SamplePaths))]
|
||||||
public void NormalizeAssetName(SamplePath path)
|
public void NormalizeAssetName(SamplePath path)
|
||||||
{
|
{
|
||||||
if (Path.IsPathRooted(path.OriginalPath) || path.OriginalPath.StartsWith("/") || path.OriginalPath.StartsWith("\\"))
|
if (Path.IsPathRooted(path.OriginalPath) || path.OriginalPath.StartsWith('/') || path.OriginalPath.StartsWith('\\'))
|
||||||
Assert.Ignore("Absolute paths can't be used as asset names.");
|
Assert.Ignore("Absolute paths can't be used as asset names.");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
string normalized = PathUtilities.NormalizeAssetName(path.OriginalPath);
|
string normalized = PathUtilities.NormalizeAssetName(path.OriginalPath);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
#if SMAPI_FOR_WINDOWS
|
Assert.AreEqual(path.NormalizedOnUnix, normalized); // MonoGame uses the Linux format
|
||||||
Assert.AreEqual(path.NormalizedOnWindows, normalized);
|
|
||||||
#else
|
|
||||||
Assert.AreEqual(path.NormalizedOnUnix, normalized);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****
|
/****
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<RootNamespace>StardewModdingAPI</RootNamespace>
|
<RootNamespace>StardewModdingAPI</RootNamespace>
|
||||||
<Description>Provides toolkit interfaces which are available to SMAPI mods.</Description>
|
<Description>Provides toolkit interfaces which are available to SMAPI mods.</Description>
|
||||||
<TargetFrameworks>net452;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>net5.0; netstandard2.0</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace StardewModdingAPI.Toolkit.Framework
|
||||||
|
{
|
||||||
|
/// <summary>Contains the SMAPI installer's constants and assumptions.</summary>
|
||||||
|
internal static class Constants
|
||||||
|
{
|
||||||
|
/// <summary>The name of the game's main DLL, used to detect game folders.</summary>
|
||||||
|
public const string GameDllName = "Stardew Valley.dll";
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,10 +56,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
dir.Exists
|
dir.Exists
|
||||||
&& (
|
&& dir.EnumerateFiles("Stardew Valley.dll").Any();
|
||||||
dir.EnumerateFiles("StardewValley.exe").Any()
|
|
||||||
|| dir.EnumerateFiles("Stardew Valley.exe").Any()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -80,15 +80,6 @@ namespace StardewModdingAPI.Toolkit.Framework
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the name of the Stardew Valley executable.</summary>
|
|
||||||
/// <param name="platform">The current platform.</param>
|
|
||||||
public static string GetExecutableName(string platform)
|
|
||||||
{
|
|
||||||
return platform == nameof(Platform.Windows)
|
|
||||||
? "Stardew Valley.exe"
|
|
||||||
: "StardewValley.exe";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get whether an executable is 64-bit.</summary>
|
/// <summary>Get whether an executable is 64-bit.</summary>
|
||||||
/// <param name="path">The absolute path to the assembly file.</param>
|
/// <param name="path">The absolute path to the assembly file.</param>
|
||||||
public static bool Is64BitAssembly(string path)
|
public static bool Is64BitAssembly(string path)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<RootNamespace>StardewModdingAPI.Toolkit</RootNamespace>
|
<RootNamespace>StardewModdingAPI.Toolkit</RootNamespace>
|
||||||
<Description>A library which encapsulates mod-handling logic for mod managers and tools. Not intended for use by mods.</Description>
|
<Description>A library which encapsulates mod-handling logic for mod managers and tools. Not intended for use by mods.</Description>
|
||||||
<TargetFrameworks>net452;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>net5.0; netstandard2.0</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.0" />
|
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.0" />
|
||||||
<PackageReference Include="System.Management" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT'" />
|
<PackageReference Include="System.Management" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT' AND '$(TargetFramework)' == 'netstandard2.0'" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -40,13 +40,6 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
return LowLevelEnvironmentUtility.GetFriendlyPlatformName(platform.ToString());
|
return LowLevelEnvironmentUtility.GetFriendlyPlatformName(platform.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the name of the Stardew Valley executable.</summary>
|
|
||||||
/// <param name="platform">The current platform.</param>
|
|
||||||
public static string GetExecutableName(Platform platform)
|
|
||||||
{
|
|
||||||
return LowLevelEnvironmentUtility.GetExecutableName(platform.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get whether an executable is 64-bit.</summary>
|
/// <summary>Get whether an executable is 64-bit.</summary>
|
||||||
/// <param name="path">The absolute path to the assembly file.</param>
|
/// <param name="path">The absolute path to the assembly file.</param>
|
||||||
public static bool Is64BitAssembly(string path)
|
public static bool Is64BitAssembly(string path)
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Toolkit.Utilities
|
namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
|
@ -42,5 +44,16 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
if (entry.Exists)
|
if (entry.Exists)
|
||||||
throw new IOException($"Timed out trying to delete {entry.FullName}");
|
throw new IOException($"Timed out trying to delete {entry.FullName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the MD5 hash for a file.</summary>
|
||||||
|
/// <param name="absolutePath">The absolute file path.</param>
|
||||||
|
public static string GetFileHash(string absolutePath)
|
||||||
|
{
|
||||||
|
using FileStream stream = File.OpenRead(absolutePath);
|
||||||
|
using MD5 md5 = MD5.Create();
|
||||||
|
|
||||||
|
byte[] hash = md5.ComputeHash(stream);
|
||||||
|
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -27,7 +26,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
public static readonly char PreferredPathSeparator = Path.DirectorySeparatorChar;
|
public static readonly char PreferredPathSeparator = Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
/// <summary>The preferred directory separator character in an asset key.</summary>
|
/// <summary>The preferred directory separator character in an asset key.</summary>
|
||||||
public static readonly char PreferredAssetSeparator = PathUtilities.PreferredPathSeparator;
|
public static readonly char PreferredAssetSeparator = '/';
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
|
@ -88,14 +87,18 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
/// <summary>Get a directory or file path relative to a given source path. If no relative path is possible (e.g. the paths are on different drives), an absolute path is returned.</summary>
|
/// <summary>Get a directory or file path relative to a given source path. If no relative path is possible (e.g. the paths are on different drives), an absolute path is returned.</summary>
|
||||||
/// <param name="sourceDir">The source folder path.</param>
|
/// <param name="sourceDir">The source folder path.</param>
|
||||||
/// <param name="targetPath">The target folder or file path.</param>
|
/// <param name="targetPath">The target folder or file path.</param>
|
||||||
/// <remarks>
|
|
||||||
///
|
|
||||||
/// NOTE: this is a heuristic implementation that works in the cases SMAPI needs it for, but it doesn't handle all edge cases (e.g. case-sensitivity on Linux, or traversing between UNC paths on Windows). This should be replaced with the more comprehensive <c>Path.GetRelativePath</c> if the game ever migrates to .NET Core.
|
|
||||||
///
|
|
||||||
/// </remarks>
|
|
||||||
[Pure]
|
[Pure]
|
||||||
public static string GetRelativePath(string sourceDir, string targetPath)
|
public static string GetRelativePath(string sourceDir, string targetPath)
|
||||||
{
|
{
|
||||||
|
#if NET5_0
|
||||||
|
return Path.GetRelativePath(sourceDir, targetPath);
|
||||||
|
#else
|
||||||
|
// NOTE:
|
||||||
|
// this is a heuristic implementation that works in the cases SMAPI needs it for, but it
|
||||||
|
// doesn't handle all edge cases (e.g. case-sensitivity on Linux, or traversing between
|
||||||
|
// UNC paths on Windows). SMAPI and mods will use the more robust .NET 5 version anyway
|
||||||
|
// though, this is only for compatibility with the mod build package.
|
||||||
|
|
||||||
// convert to URIs
|
// convert to URIs
|
||||||
Uri from = new Uri(sourceDir.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
|
Uri from = new Uri(sourceDir.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
|
||||||
Uri to = new Uri(targetPath.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
|
Uri to = new Uri(targetPath.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
|
||||||
|
@ -123,6 +126,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
}
|
}
|
||||||
|
|
||||||
return relative;
|
return relative;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
|
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
|
||||||
|
@ -145,32 +149,5 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
{
|
{
|
||||||
return !Regex.IsMatch(str, "[^a-z0-9_.-]", RegexOptions.IgnoreCase);
|
return !Regex.IsMatch(str, "[^a-z0-9_.-]", RegexOptions.IgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the paths which exceed the OS length limit.</summary>
|
|
||||||
/// <param name="rootPath">The root path to search.</param>
|
|
||||||
internal static IEnumerable<string> GetTooLongPaths(string rootPath)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(rootPath))
|
|
||||||
return new string[0];
|
|
||||||
|
|
||||||
return Directory
|
|
||||||
.EnumerateFileSystemEntries(rootPath, "*.*", SearchOption.AllDirectories)
|
|
||||||
.Where(PathUtilities.IsPathTooLong);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get whether a file or directory path exceeds the OS path length limit.</summary>
|
|
||||||
/// <param name="path">The path to test.</param>
|
|
||||||
internal static bool IsPathTooLong(string path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_ = Path.GetFullPath(path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (PathTooLongException)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,21 +57,16 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
{
|
{
|
||||||
// choose versions
|
// choose versions
|
||||||
ReleaseVersion[] versions = await this.GetReleaseVersionsAsync();
|
ReleaseVersion[] versions = await this.GetReleaseVersionsAsync();
|
||||||
ReleaseVersion stableVersion = versions.LastOrDefault(version => !version.IsBeta && !version.IsForDevs);
|
ReleaseVersion stableVersion = versions.LastOrDefault(version => !version.IsForDevs);
|
||||||
ReleaseVersion stableVersionForDevs = versions.LastOrDefault(version => !version.IsBeta && version.IsForDevs);
|
ReleaseVersion stableVersionForDevs = versions.LastOrDefault(version => version.IsForDevs);
|
||||||
ReleaseVersion betaVersion = versions.LastOrDefault(version => version.IsBeta && !version.IsForDevs);
|
|
||||||
ReleaseVersion betaVersionForDevs = versions.LastOrDefault(version => version.IsBeta && version.IsForDevs);
|
|
||||||
|
|
||||||
// render view
|
// render view
|
||||||
IndexVersionModel stableVersionModel = stableVersion != null
|
IndexVersionModel stableVersionModel = stableVersion != null
|
||||||
? new IndexVersionModel(stableVersion.Version.ToString(), stableVersion.Release.Body, stableVersion.Asset.DownloadUrl, stableVersionForDevs?.Asset.DownloadUrl)
|
? new IndexVersionModel(stableVersion.Version.ToString(), stableVersion.Release.Body, stableVersion.Asset.DownloadUrl, stableVersionForDevs?.Asset.DownloadUrl)
|
||||||
: new IndexVersionModel("unknown", "", "https://github.com/Pathoschild/SMAPI/releases", null); // just in case something goes wrong)
|
: new IndexVersionModel("unknown", "", "https://github.com/Pathoschild/SMAPI/releases", null); // just in case something goes wrong
|
||||||
IndexVersionModel betaVersionModel = betaVersion != null && this.SiteConfig.BetaEnabled
|
|
||||||
? new IndexVersionModel(betaVersion.Version.ToString(), betaVersion.Release.Body, betaVersion.Asset.DownloadUrl, betaVersionForDevs?.Asset.DownloadUrl)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// render view
|
// render view
|
||||||
var model = new IndexModel(stableVersionModel, betaVersionModel, this.SiteConfig.BetaBlurb, this.SiteConfig.SupporterList);
|
var model = new IndexModel(stableVersionModel, this.SiteConfig.OtherBlurb, this.SiteConfig.SupporterList);
|
||||||
return this.View(model);
|
return this.View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,27 +88,12 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
{
|
{
|
||||||
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
|
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
|
||||||
|
|
||||||
// get latest release (whether preview or stable)
|
// get latest stable release
|
||||||
GitRelease stableRelease = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true);
|
GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false);
|
||||||
|
|
||||||
// split stable/prerelease if applicable
|
// strip 'noinclude' blocks from release description
|
||||||
GitRelease betaRelease = null;
|
if (release != null)
|
||||||
if (stableRelease.IsPrerelease)
|
|
||||||
{
|
{
|
||||||
GitRelease result = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false);
|
|
||||||
if (result != null)
|
|
||||||
{
|
|
||||||
betaRelease = stableRelease;
|
|
||||||
stableRelease = result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip 'noinclude' blocks from release descriptions
|
|
||||||
foreach (GitRelease release in new[] { stableRelease, betaRelease })
|
|
||||||
{
|
|
||||||
if (release == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
HtmlDocument doc = new HtmlDocument();
|
HtmlDocument doc = new HtmlDocument();
|
||||||
doc.LoadHtml(release.Body);
|
doc.LoadHtml(release.Body);
|
||||||
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//*[@class='noinclude']")?.ToArray() ?? new HtmlNode[0])
|
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//*[@class='noinclude']")?.ToArray() ?? new HtmlNode[0])
|
||||||
|
@ -122,10 +102,8 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
// get versions
|
// get versions
|
||||||
ReleaseVersion[] stableVersions = this.ParseReleaseVersions(stableRelease).ToArray();
|
return this
|
||||||
ReleaseVersion[] betaVersions = this.ParseReleaseVersions(betaRelease).ToArray();
|
.ParseReleaseVersions(release)
|
||||||
return stableVersions
|
|
||||||
.Concat(betaVersions)
|
|
||||||
.OrderBy(p => p.Version)
|
.OrderBy(p => p.Version)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
});
|
});
|
||||||
|
@ -146,10 +124,9 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
Match match = Regex.Match(asset.FileName, @"SMAPI-(?<version>[\d\.]+(?:-.+)?)-installer(?<forDevs>-for-developers)?.zip");
|
Match match = Regex.Match(asset.FileName, @"SMAPI-(?<version>[\d\.]+(?:-.+)?)-installer(?<forDevs>-for-developers)?.zip");
|
||||||
if (!match.Success || !SemanticVersion.TryParse(match.Groups["version"].Value, out ISemanticVersion version))
|
if (!match.Success || !SemanticVersion.TryParse(match.Groups["version"].Value, out ISemanticVersion version))
|
||||||
continue;
|
continue;
|
||||||
bool isBeta = version.IsPrerelease();
|
|
||||||
bool isForDevs = match.Groups["forDevs"].Success;
|
bool isForDevs = match.Groups["forDevs"].Success;
|
||||||
|
|
||||||
yield return new ReleaseVersion(release, asset, version, isBeta, isForDevs);
|
yield return new ReleaseVersion(release, asset, version, isForDevs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,9 +145,6 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
/// <summary>The SMAPI version.</summary>
|
/// <summary>The SMAPI version.</summary>
|
||||||
public ISemanticVersion Version { get; }
|
public ISemanticVersion Version { get; }
|
||||||
|
|
||||||
/// <summary>Whether this is a beta download.</summary>
|
|
||||||
public bool IsBeta { get; }
|
|
||||||
|
|
||||||
/// <summary>Whether this is a 'for developers' download.</summary>
|
/// <summary>Whether this is a 'for developers' download.</summary>
|
||||||
public bool IsForDevs { get; }
|
public bool IsForDevs { get; }
|
||||||
|
|
||||||
|
@ -182,14 +156,12 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
/// <param name="release">The underlying GitHub release.</param>
|
/// <param name="release">The underlying GitHub release.</param>
|
||||||
/// <param name="asset">The underlying download asset.</param>
|
/// <param name="asset">The underlying download asset.</param>
|
||||||
/// <param name="version">The SMAPI version.</param>
|
/// <param name="version">The SMAPI version.</param>
|
||||||
/// <param name="isBeta">Whether this is a beta download.</param>
|
|
||||||
/// <param name="isForDevs">Whether this is a 'for developers' download.</param>
|
/// <param name="isForDevs">Whether this is a 'for developers' download.</param>
|
||||||
public ReleaseVersion(GitRelease release, GitAsset asset, ISemanticVersion version, bool isBeta, bool isForDevs)
|
public ReleaseVersion(GitRelease release, GitAsset asset, ISemanticVersion version, bool isForDevs)
|
||||||
{
|
{
|
||||||
this.Release = release;
|
this.Release = release;
|
||||||
this.Asset = asset;
|
this.Asset = asset;
|
||||||
this.Version = version;
|
this.Version = version;
|
||||||
this.IsBeta = isBeta;
|
|
||||||
this.IsForDevs = isForDevs;
|
this.IsForDevs = isForDevs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,8 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
if (model?.Mods == null)
|
if (model?.Mods == null)
|
||||||
return new ModEntryModel[0];
|
return new ModEntryModel[0];
|
||||||
|
|
||||||
|
ModUpdateCheckConfig config = this.Config.Value;
|
||||||
|
|
||||||
// fetch wiki data
|
// fetch wiki data
|
||||||
WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.Data).ToArray();
|
WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.Data).ToArray();
|
||||||
IDictionary<string, ModEntryModel> mods = new Dictionary<string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);
|
IDictionary<string, ModEntryModel> mods = new Dictionary<string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
@ -89,6 +91,11 @@ namespace StardewModdingAPI.Web.Controllers
|
||||||
if (string.IsNullOrWhiteSpace(mod.ID))
|
if (string.IsNullOrWhiteSpace(mod.ID))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// special case: if this is an update check for the official SMAPI repo, check the Nexus mod page for beta versions
|
||||||
|
if (mod.ID == config.SmapiInfo.ID && mod.UpdateKeys?.Any(key => key == config.SmapiInfo.DefaultUpdateKey) == true && mod.InstalledVersion?.IsPrerelease() == true)
|
||||||
|
mod.UpdateKeys = mod.UpdateKeys.Concat(config.SmapiInfo.AddBetaUpdateKeys).ToArray();
|
||||||
|
|
||||||
|
// fetch result
|
||||||
ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata, model.ApiVersion);
|
ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata, model.ApiVersion);
|
||||||
if (!model.IncludeExtendedMetadata && (model.ApiVersion == null || mod.InstalledVersion == null))
|
if (!model.IncludeExtendedMetadata && (model.ApiVersion == null || mod.InstalledVersion == null))
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,5 +14,8 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
|
||||||
|
|
||||||
/// <summary>Update-check metadata to override.</summary>
|
/// <summary>Update-check metadata to override.</summary>
|
||||||
public ModOverrideConfig[] ModOverrides { get; set; }
|
public ModOverrideConfig[] ModOverrides { get; set; }
|
||||||
|
|
||||||
|
/// <summary>The update-check config for SMAPI's own update checks.</summary>
|
||||||
|
public SmapiInfoConfig SmapiInfo { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,8 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
|
||||||
/*********
|
/*********
|
||||||
** Accessors
|
** Accessors
|
||||||
*********/
|
*********/
|
||||||
/// <summary>Whether to show SMAPI beta versions on the main page, if any.</summary>
|
/// <summary>A message to show below the download button (e.g. for details on downloading a beta version), in Markdown format.</summary>
|
||||||
public bool BetaEnabled { get; set; }
|
public string OtherBlurb { get; set; }
|
||||||
|
|
||||||
/// <summary>A short sentence shown under the beta download button, if any.</summary>
|
|
||||||
public string BetaBlurb { get; set; }
|
|
||||||
|
|
||||||
/// <summary>A list of supports to credit on the main page, in Markdown format.</summary>
|
/// <summary>A list of supports to credit on the main page, in Markdown format.</summary>
|
||||||
public string SupporterList { get; set; }
|
public string SupporterList { get; set; }
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace StardewModdingAPI.Web.Framework.ConfigModels
|
||||||
|
{
|
||||||
|
/// <summary>The update-check config for SMAPI's own update checks.</summary>
|
||||||
|
internal class SmapiInfoConfig
|
||||||
|
{
|
||||||
|
/// <summary>The mod ID used for SMAPI update checks.</summary>
|
||||||
|
public string ID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>The default update key used for SMAPI update checks.</summary>
|
||||||
|
public string DefaultUpdateKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>The update keys to add for SMAPI update checks when the player has a beta version installed.</summary>
|
||||||
|
public string[] AddBetaUpdateKeys { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,27 +2,29 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>SMAPI.Web</AssemblyName>
|
<AssemblyName>SMAPI.Web</AssemblyName>
|
||||||
<RootNamespace>StardewModdingAPI.Web</RootNamespace>
|
<RootNamespace>StardewModdingAPI.Web</RootNamespace>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="..\..\build\common.targets" />
|
<Import Project="..\..\build\common.targets" />
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Remove="Properties\PublishProfiles\**" />
|
||||||
|
<None Remove="Properties\ServiceDependencies\**" />
|
||||||
<Content Remove="aws-beanstalk-tools-defaults.json" />
|
<Content Remove="aws-beanstalk-tools-defaults.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.8.3" />
|
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.22" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.27" />
|
||||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
|
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.9.9" />
|
<PackageReference Include="Humanizer.Core" Version="2.13.14" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" />
|
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
|
||||||
<PackageReference Include="Markdig" Version="0.24.0" />
|
<PackageReference Include="Markdig" Version="0.26.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||||
<PackageReference Include="Pathoschild.FluentNexus" Version="1.0.2" />
|
<PackageReference Include="Pathoschild.FluentNexus" Version="1.0.5" />
|
||||||
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.0" />
|
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -9,11 +9,8 @@ namespace StardewModdingAPI.Web.ViewModels
|
||||||
/// <summary>The latest stable SMAPI version.</summary>
|
/// <summary>The latest stable SMAPI version.</summary>
|
||||||
public IndexVersionModel StableVersion { get; set; }
|
public IndexVersionModel StableVersion { get; set; }
|
||||||
|
|
||||||
/// <summary>The latest prerelease SMAPI version (if newer than <see cref="StableVersion"/>).</summary>
|
/// <summary>A message to show below the download button (e.g. for details on downloading a beta version), in Markdown format.</summary>
|
||||||
public IndexVersionModel BetaVersion { get; set; }
|
public string OtherBlurb { get; set; }
|
||||||
|
|
||||||
/// <summary>A short sentence shown under the beta download button, if any.</summary>
|
|
||||||
public string BetaBlurb { get; set; }
|
|
||||||
|
|
||||||
/// <summary>A list of supports to credit on the main page, in Markdown format.</summary>
|
/// <summary>A list of supports to credit on the main page, in Markdown format.</summary>
|
||||||
public string SupporterList { get; set; }
|
public string SupporterList { get; set; }
|
||||||
|
@ -27,14 +24,12 @@ namespace StardewModdingAPI.Web.ViewModels
|
||||||
|
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
/// <param name="stableVersion">The latest stable SMAPI version.</param>
|
/// <param name="stableVersion">The latest stable SMAPI version.</param>
|
||||||
/// <param name="betaVersion">The latest prerelease SMAPI version (if newer than <paramref name="stableVersion"/>).</param>
|
/// <param name="otherBlurb">A message to show below the download button (e.g. for details on downloading a beta version), in Markdown format.</param>
|
||||||
/// <param name="betaBlurb">A short sentence shown under the beta download button, if any.</param>
|
|
||||||
/// <param name="supporterList">A list of supports to credit on the main page, in Markdown format.</param>
|
/// <param name="supporterList">A list of supports to credit on the main page, in Markdown format.</param>
|
||||||
internal IndexModel(IndexVersionModel stableVersion, IndexVersionModel betaVersion, string betaBlurb, string supporterList)
|
internal IndexModel(IndexVersionModel stableVersion, string otherBlurb, string supporterList)
|
||||||
{
|
{
|
||||||
this.StableVersion = stableVersion;
|
this.StableVersion = stableVersion;
|
||||||
this.BetaVersion = betaVersion;
|
this.OtherBlurb = otherBlurb;
|
||||||
this.BetaBlurb = betaBlurb;
|
|
||||||
this.SupporterList = supporterList;
|
this.SupporterList = supporterList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
ViewData["ViewTitle"] = string.Empty;
|
ViewData["ViewTitle"] = string.Empty;
|
||||||
}
|
}
|
||||||
@section Head {
|
@section Head {
|
||||||
<link rel="stylesheet" href="~/Content/css/index.css?r=20200105" />
|
<link rel="stylesheet" href="~/Content/css/index.css" />
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script>
|
||||||
<script src="~/Content/js/index.js?r=20200105"></script>
|
<script src="~/Content/js/index.js"></script>
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
|
@ -29,25 +29,12 @@
|
||||||
<a href="https://www.nexusmods.com/stardewvalley/mods/2400"><img src="Content/images/nexus-icon.png" /> Download from Nexus</a>
|
<a href="https://www.nexusmods.com/stardewvalley/mods/2400"><img src="Content/images/nexus-icon.png" /> Download from Nexus</a>
|
||||||
<a href="@Model.StableVersion.DownloadUrl"><img src="Content/images/direct-download-icon.png" /> Direct download</a>
|
<a href="@Model.StableVersion.DownloadUrl"><img src="Content/images/direct-download-icon.png" /> Direct download</a>
|
||||||
</div>
|
</div>
|
||||||
</div><br />
|
|
||||||
|
|
||||||
@if (Model.BetaVersion != null)
|
|
||||||
{
|
|
||||||
<div class="cta-dropdown secondary-cta-dropdown">
|
|
||||||
<a href="@Model.BetaVersion.DownloadUrl" class="secondary-cta download">
|
|
||||||
Download SMAPI @Model.BetaVersion.Version
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.BetaBlurb))
|
|
||||||
{
|
|
||||||
<br /><small>@Model.BetaBlurb</small>
|
|
||||||
}
|
|
||||||
</a><br />
|
|
||||||
<div class="dropdown-content">
|
|
||||||
<a href="https://www.nexusmods.com/stardewvalley/mods/2400"><img src="Content/images/nexus-icon.png" /> Download from Nexus</a>
|
|
||||||
<a href="@Model.BetaVersion.DownloadUrl"><img src="Content/images/direct-download-icon.png" /> Direct download</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div><br />
|
|
||||||
}
|
|
||||||
<div><a href="https://stardewvalleywiki.com/Modding:Player_Guide" class="secondary-cta">Player guide</a></div>
|
<div><a href="https://stardewvalleywiki.com/Modding:Player_Guide" class="secondary-cta">Player guide</a></div>
|
||||||
|
@if (Model.OtherBlurb != null)
|
||||||
|
{
|
||||||
|
<div>@Html.Raw(Markdig.Markdown.ToHtml(Model.OtherBlurb))</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="area">
|
<div class="area">
|
||||||
|
@ -61,29 +48,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="area">
|
<div class="area">
|
||||||
@if (Model.BetaVersion == null)
|
|
||||||
{
|
|
||||||
<h2 id="whatsnew">What's new</h2>
|
<h2 id="whatsnew">What's new</h2>
|
||||||
<div class="github-description">
|
<div class="github-description">
|
||||||
@Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description))
|
@Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description))
|
||||||
</div>
|
</div>
|
||||||
<p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="@Url.PlainAction("Index", "Mods")">mod compatibility list</a> for more info.</p>
|
<p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="@Url.PlainAction("Index", "Mods")">mod compatibility list</a> for more info.</p>
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<h2 id="whatsnew">What's new in...</h2>
|
|
||||||
<h3>SMAPI @Model.StableVersion.Version?</h3>
|
|
||||||
<div class="github-description">
|
|
||||||
@Html.Raw(Markdig.Markdown.ToHtml(Model.StableVersion.Description))
|
|
||||||
</div>
|
|
||||||
<p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="@Url.PlainAction("Index", "Mods")">mod compatibility list</a> for more info.</p>
|
|
||||||
|
|
||||||
<h3>SMAPI @Model.BetaVersion.Version?</h3>
|
|
||||||
<div class="github-description">
|
|
||||||
@Html.Raw(Markdig.Markdown.ToHtml(Model.BetaVersion.Description))
|
|
||||||
</div>
|
|
||||||
<p>See the <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#release-notes">release notes</a> and <a href="@Url.PlainAction("Index", "Mods")">mod compatibility list</a> for more info.</p>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="area">
|
<div class="area">
|
||||||
|
@ -122,10 +91,6 @@
|
||||||
<h2 id="modcreators">For mod creators</h2>
|
<h2 id="modcreators">For mod creators</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="@Model.StableVersion.DevDownloadUrl">SMAPI @Model.StableVersion.Version for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li>
|
<li><a href="@Model.StableVersion.DevDownloadUrl">SMAPI @Model.StableVersion.Version for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li>
|
||||||
@if (Model.BetaVersion != null)
|
|
||||||
{
|
|
||||||
<li><a href="@Model.BetaVersion.DevDownloadUrl">SMAPI @Model.BetaVersion.Version for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li>
|
|
||||||
}
|
|
||||||
<li><a href="https://stardewvalleywiki.com/Modding:Index">Modding documentation</a></li>
|
<li><a href="https://stardewvalleywiki.com/Modding:Index">Modding documentation</a></li>
|
||||||
<li><a href="https://github.com/Pathoschild/SMAPI">Source code</a></li>
|
<li><a href="https://github.com/Pathoschild/SMAPI">Source code</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
|
|
||||||
"Site": {
|
"Site": {
|
||||||
"BetaEnabled": false,
|
"BetaEnabled": false,
|
||||||
"BetaBlurb": null,
|
"OtherBlurb": null
|
||||||
"SupporterList": null
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"ApiClients": {
|
"ApiClients": {
|
||||||
|
@ -76,6 +75,11 @@
|
||||||
"ID": "MartyrPher.SMAPI-Android-Installer",
|
"ID": "MartyrPher.SMAPI-Android-Installer",
|
||||||
"AllowNonStandardVersions": true
|
"AllowNonStandardVersions": true
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"SmapiInfo": {
|
||||||
|
"ID": "Pathoschild.SMAPI",
|
||||||
|
"DefaultUpdateKey": "GitHub:Pathoschild/SMAPI",
|
||||||
|
"AddBetaUpdateKeys": [ "Nexus:2400" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,10 @@ h1 {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cta-blurb {
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
.sublinks {
|
.sublinks {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
|
@ -167,6 +167,60 @@
|
||||||
"~ | StatusReasonPhrase": "split-screen mode was added in Stardew Valley 1.5"
|
"~ | StatusReasonPhrase": "split-screen mode was added in Stardew Valley 1.5"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Broke in SDV 1.5.5
|
||||||
|
*********/
|
||||||
|
"Animated Portrait Framework": {
|
||||||
|
"ID": "akai.AnimatedPortrait",
|
||||||
|
"~1.0.0 | Status": "AssumeBroken",
|
||||||
|
"~1.0.0 | StatusReasonDetails": "requires the 'System.Windows.Forms' API, which isn't available in .NET 5"
|
||||||
|
},
|
||||||
|
"Audio Devices": {
|
||||||
|
"ID": "maxvollmer.audiodevices",
|
||||||
|
"~3.0.1 | Status": "AssumeBroken",
|
||||||
|
"~3.0.1 | StatusReasonDetails": "fails to load due to an outdated implementation of the game's 'IAudioEngine' interface"
|
||||||
|
},
|
||||||
|
"Battery Warning": {
|
||||||
|
"ID": "Husky110.BatteryWarningMod",
|
||||||
|
"~1.0.4 | Status": "AssumeBroken",
|
||||||
|
"~1.0.4 | StatusReasonDetails": "requires the 'System.Management' API, which is a different DLL in .NET 5"
|
||||||
|
},
|
||||||
|
"Junimo Studio": {
|
||||||
|
"ID": "Becks723.JunimoStudio",
|
||||||
|
"~2.0.1 | Status": "AssumeBroken",
|
||||||
|
"~2.0.1 | StatusReasonDetails": "requires 'Microsoft.Xna.Framework.Audio.AudioCategory' which doesn't exist in MonoGame"
|
||||||
|
},
|
||||||
|
"Skip Intro": {
|
||||||
|
"ID": "Pathoschild.SkipIntro",
|
||||||
|
"~1.9.1 | Status": "AssumeBroken",
|
||||||
|
"~1.9.1 | StatusReasonDetails": "causes freeze during game launch"
|
||||||
|
},
|
||||||
|
"Stardew Hack": {
|
||||||
|
"ID": "bcmpinc.StardewHack",
|
||||||
|
"~5.1.0 | Status": "AssumeBroken",
|
||||||
|
"~5.1.0 | StatusReasonDetails": "runtime error when initializing due to an API change between .NET Framework and .NET 5"
|
||||||
|
},
|
||||||
|
"Stardew Valley Expanded": {
|
||||||
|
"ID": "FlashShifter.SVECode",
|
||||||
|
"~1.13.11 | Status": "AssumeBroken",
|
||||||
|
"~1.13.11 | StatusReasonDetails": "fails to load due to an outdated implementation of the game's 'ICue' interface"
|
||||||
|
},
|
||||||
|
"Stardew Web": {
|
||||||
|
"ID": "prism99.stardewweb",
|
||||||
|
"~0.7.2 | Status": "AssumeBroken",
|
||||||
|
"~0.7.2 | StatusReasonDetails": "requires the 'System.Drawing' API, which isn't available in .NET 5"
|
||||||
|
},
|
||||||
|
"Sundrop City": {
|
||||||
|
"ID": "SundropTeam.SundropCity",
|
||||||
|
"~0.4.1 | Status": "AssumeBroken",
|
||||||
|
"~0.4.1 | StatusReasonDetails": "causes freeze during game launch"
|
||||||
|
},
|
||||||
|
"Video Player": {
|
||||||
|
"ID": "aedenthorn.VideoPlayer",
|
||||||
|
"~0.2.5 | Status": "AssumeBroken",
|
||||||
|
"~0.2.5 | StatusReasonDetails": "requires an XNA Framework API that's not available in MonoGame and causes a crash to desktop"
|
||||||
|
},
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Broke in SMAPI 3.12.0
|
** Broke in SMAPI 3.12.0
|
||||||
*********/
|
*********/
|
||||||
|
@ -205,11 +259,6 @@
|
||||||
"~1.9.3 | Status": "AssumeBroken",
|
"~1.9.3 | Status": "AssumeBroken",
|
||||||
"~1.9.3 | StatusReasonDetails": "fails to load with 'ReflectionTypeLoadException' error"
|
"~1.9.3 | StatusReasonDetails": "fails to load with 'ReflectionTypeLoadException' error"
|
||||||
},
|
},
|
||||||
"Stardew Hack": {
|
|
||||||
"ID": "bcmpinc.StardewHack",
|
|
||||||
"~5.0.0 | Status": "AssumeBroken",
|
|
||||||
"~5.0.0 | StatusReasonDetails": "causes Harmony patching errors for other mods"
|
|
||||||
},
|
|
||||||
"Tilled Soil Decay": {
|
"Tilled Soil Decay": {
|
||||||
"ID": "bcmpinc.TilledSoilDecay",
|
"ID": "bcmpinc.TilledSoilDecay",
|
||||||
"~4.1.0 | Status": "AssumeBroken",
|
"~4.1.0 | Status": "AssumeBroken",
|
||||||
|
@ -238,12 +287,6 @@
|
||||||
/*********
|
/*********
|
||||||
** Broke in SDV 1.5 (SMAPI mods)
|
** Broke in SDV 1.5 (SMAPI mods)
|
||||||
*********/
|
*********/
|
||||||
"Audio Devices": {
|
|
||||||
"ID": "maxvollmer.audiodevices",
|
|
||||||
"~2.0.0 | Status": "AssumeBroken",
|
|
||||||
"~2.0.0 | StatusReasonDetails": "causes crash to desktop when starting the game"
|
|
||||||
},
|
|
||||||
|
|
||||||
"ChestEx": {
|
"ChestEx": {
|
||||||
"ID": "berkayylmao.ChestEx",
|
"ID": "berkayylmao.ChestEx",
|
||||||
"~1.3.4 | Status": "AssumeBroken",
|
"~1.3.4 | Status": "AssumeBroken",
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"Format": {
|
"Format": {
|
||||||
"title": "Format version",
|
"title": "Format version",
|
||||||
"description": "The format version. You should always use the latest version to enable the latest features and avoid obsolete behavior.",
|
"description": "The format version. You should always use the latest version to enable the latest features, avoid obsolete behavior, and reduce load times.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "1.23.0",
|
"const": "1.24.0",
|
||||||
"@errorMessages": {
|
"@errorMessages": {
|
||||||
"const": "Incorrect value '@value'. This should be set to the latest format version, currently '1.23.0'."
|
"const": "Incorrect value '@value'. You should always use the latest format version (currently 1.24.0) to enable the latest features, avoid obsolete behavior, and reduce load times."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConfigSchema": {
|
"ConfigSchema": {
|
||||||
|
|
|
@ -40,21 +40,16 @@ namespace StardewModdingAPI
|
||||||
internal static GamePlatform Platform { get; } = (GamePlatform)Enum.Parse(typeof(GamePlatform), LowLevelEnvironmentUtility.DetectPlatform());
|
internal static GamePlatform Platform { get; } = (GamePlatform)Enum.Parse(typeof(GamePlatform), LowLevelEnvironmentUtility.DetectPlatform());
|
||||||
|
|
||||||
/// <summary>The game framework running the game.</summary>
|
/// <summary>The game framework running the game.</summary>
|
||||||
internal static GameFramework GameFramework { get; } =
|
internal static GameFramework GameFramework { get; } = GameFramework.MonoGame;
|
||||||
#if SMAPI_FOR_XNA
|
|
||||||
GameFramework.Xna;
|
|
||||||
#else
|
|
||||||
GameFramework.MonoGame;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>The game's assembly name.</summary>
|
/// <summary>The game's assembly name.</summary>
|
||||||
internal static string GameAssemblyName => EarlyConstants.Platform == GamePlatform.Windows ? "Stardew Valley" : "StardewValley";
|
internal static string GameAssemblyName { get; } = "Stardew Valley";
|
||||||
|
|
||||||
/// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary>
|
/// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary>
|
||||||
internal static int? LogScreenId { get; set; }
|
internal static int? LogScreenId { get; set; }
|
||||||
|
|
||||||
/// <summary>SMAPI's current raw semantic version.</summary>
|
/// <summary>SMAPI's current raw semantic version.</summary>
|
||||||
internal static string RawApiVersion = "3.12.8";
|
internal static string RawApiVersion = "3.13.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Contains SMAPI's constants and assumptions.</summary>
|
/// <summary>Contains SMAPI's constants and assumptions.</summary>
|
||||||
|
@ -70,10 +65,10 @@ namespace StardewModdingAPI
|
||||||
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion(EarlyConstants.RawApiVersion);
|
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion(EarlyConstants.RawApiVersion);
|
||||||
|
|
||||||
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
||||||
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.4");
|
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.5");
|
||||||
|
|
||||||
/// <summary>The maximum supported version of Stardew Valley.</summary>
|
/// <summary>The maximum supported version of Stardew Valley.</summary>
|
||||||
public static ISemanticVersion MaximumGameVersion { get; } = new GameVersion("1.5.4");
|
public static ISemanticVersion MaximumGameVersion { get; } = null;
|
||||||
|
|
||||||
/// <summary>The target game platform.</summary>
|
/// <summary>The target game platform.</summary>
|
||||||
public static GamePlatform TargetPlatform { get; } = EarlyConstants.Platform;
|
public static GamePlatform TargetPlatform { get; } = EarlyConstants.Platform;
|
||||||
|
@ -240,18 +235,13 @@ namespace StardewModdingAPI
|
||||||
// The game assembly can have one of three names depending how the mod was compiled:
|
// The game assembly can have one of three names depending how the mod was compiled:
|
||||||
// - 'StardewValley': assembly name on Linux/macOS;
|
// - 'StardewValley': assembly name on Linux/macOS;
|
||||||
// - 'Stardew Valley': assembly name on Windows;
|
// - 'Stardew Valley': assembly name on Windows;
|
||||||
// - 'Netcode': an assembly that's separate on Windows only.
|
// - 'Netcode': an assembly that was separate on Windows only before Stardew Valley 1.5.5.
|
||||||
resolver.AddWithExplicitNames(AssemblyDefinition.ReadAssembly(typeof(Game1).Assembly.Location), "StardewValley", "Stardew Valley"
|
resolver.AddWithExplicitNames(AssemblyDefinition.ReadAssembly(typeof(Game1).Assembly.Location), "StardewValley", "Stardew Valley", "Netcode");
|
||||||
#if !SMAPI_FOR_WINDOWS
|
|
||||||
, "Netcode"
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get metadata for mapping assemblies to the current platform.</summary>
|
/// <summary>Get metadata for mapping assemblies to the current platform.</summary>
|
||||||
/// <param name="targetPlatform">The target game platform.</param>
|
/// <param name="targetPlatform">The target game platform.</param>
|
||||||
/// <param name="framework">The game framework running the game.</param>
|
internal static PlatformAssemblyMap GetAssemblyMap(Platform targetPlatform)
|
||||||
internal static PlatformAssemblyMap GetAssemblyMap(Platform targetPlatform, GameFramework framework)
|
|
||||||
{
|
{
|
||||||
var removeAssemblyReferences = new List<string>();
|
var removeAssemblyReferences = new List<string>();
|
||||||
var targetAssemblies = new List<Assembly>();
|
var targetAssemblies = new List<Assembly>();
|
||||||
|
@ -260,34 +250,7 @@ namespace StardewModdingAPI
|
||||||
removeAssemblyReferences.Add("StardewModdingAPI.Toolkit.CoreInterfaces");
|
removeAssemblyReferences.Add("StardewModdingAPI.Toolkit.CoreInterfaces");
|
||||||
targetAssemblies.Add(typeof(StardewModdingAPI.IManifest).Assembly);
|
targetAssemblies.Add(typeof(StardewModdingAPI.IManifest).Assembly);
|
||||||
|
|
||||||
// get changes for platform
|
// XNA Framework before Stardew Valley 1.5.5
|
||||||
if (Constants.Platform != Platform.Windows)
|
|
||||||
{
|
|
||||||
removeAssemblyReferences.AddRange(new[]
|
|
||||||
{
|
|
||||||
"Netcode",
|
|
||||||
"Stardew Valley"
|
|
||||||
});
|
|
||||||
targetAssemblies.Add(
|
|
||||||
typeof(StardewValley.Game1).Assembly // note: includes Netcode types on Linux/macOS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
removeAssemblyReferences.Add(
|
|
||||||
"StardewValley"
|
|
||||||
);
|
|
||||||
targetAssemblies.AddRange(new[]
|
|
||||||
{
|
|
||||||
typeof(Netcode.NetBool).Assembly,
|
|
||||||
typeof(StardewValley.Game1).Assembly
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// get changes for game framework
|
|
||||||
switch (framework)
|
|
||||||
{
|
|
||||||
case GameFramework.MonoGame:
|
|
||||||
removeAssemblyReferences.AddRange(new[]
|
removeAssemblyReferences.AddRange(new[]
|
||||||
{
|
{
|
||||||
"Microsoft.Xna.Framework",
|
"Microsoft.Xna.Framework",
|
||||||
|
@ -298,23 +261,15 @@ namespace StardewModdingAPI
|
||||||
targetAssemblies.Add(
|
targetAssemblies.Add(
|
||||||
typeof(Microsoft.Xna.Framework.Vector2).Assembly
|
typeof(Microsoft.Xna.Framework.Vector2).Assembly
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
|
|
||||||
case GameFramework.Xna:
|
// `Netcode.dll` merged into the game assembly in Stardew Valley 1.5.5
|
||||||
removeAssemblyReferences.Add(
|
removeAssemblyReferences.Add(
|
||||||
"MonoGame.Framework"
|
"Netcode"
|
||||||
);
|
);
|
||||||
targetAssemblies.AddRange(new[]
|
|
||||||
{
|
|
||||||
typeof(Microsoft.Xna.Framework.Vector2).Assembly,
|
|
||||||
typeof(Microsoft.Xna.Framework.Game).Assembly,
|
|
||||||
typeof(Microsoft.Xna.Framework.Graphics.SpriteBatch).Assembly
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
// Stardew Valley reference
|
||||||
throw new InvalidOperationException($"Unknown game framework '{framework}'.");
|
removeAssemblyReferences.Add("StardewValley");
|
||||||
}
|
targetAssemblies.Add(typeof(StardewValley.Game1).Assembly);
|
||||||
|
|
||||||
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray());
|
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using StardewModdingAPI.Toolkit.Utilities;
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
|
using StardewValley;
|
||||||
using xTile;
|
using xTile;
|
||||||
using xTile.Layers;
|
using xTile.Layers;
|
||||||
using xTile.Tiles;
|
using xTile.Tiles;
|
||||||
|
@ -25,18 +26,17 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
: base(locale, assetName, data, getNormalizedPath, onDataReplaced) { }
|
: base(locale, assetName, data, getNormalizedPath, onDataReplaced) { }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
/// <remarks>Derived from <see cref="StardewValley.GameLocation.ApplyMapOverride"/> with a few changes:
|
/// <remarks>Derived from <see cref="GameLocation.ApplyMapOverride(Map,string,Rectangle?,Rectangle?)"/> with a few changes:
|
||||||
/// - can be applied directly to the maps when loading, before the location is created;
|
/// - can be applied directly to the maps when loading, before the location is created;
|
||||||
/// - added support for source/target areas;
|
/// - added support for patch modes (overlay, replace by layer, or fully replace);
|
||||||
/// - added disambiguation if source has a modified version of the same tilesheet, instead of copying tiles into the target tilesheet;
|
/// - added disambiguation if source has a modified version of the same tilesheet, instead of copying tiles into the target tilesheet;
|
||||||
/// - changed to always overwrite tiles within the target area (to avoid edge cases where some tiles are only partly applied);
|
|
||||||
/// - fixed copying tilesheets (avoid "The specified TileSheet was not created for use with this map" error);
|
/// - fixed copying tilesheets (avoid "The specified TileSheet was not created for use with this map" error);
|
||||||
/// - fixed tilesheets not added at the end (via z_ prefix), which can cause crashes in game code which depends on hardcoded tilesheet indexes;
|
/// - fixed tilesheets not added at the end (via z_ prefix), which can cause crashes in game code which depends on hardcoded tilesheet indexes;
|
||||||
/// - fixed issue where different tilesheets are linked by ID.
|
/// - fixed issue where different tilesheets are linked by ID.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null)
|
public void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null, PatchMapMode patchMode = PatchMapMode.Overlay)
|
||||||
{
|
{
|
||||||
var target = this.Data;
|
Map target = this.Data;
|
||||||
|
|
||||||
// get areas
|
// get areas
|
||||||
{
|
{
|
||||||
|
@ -84,10 +84,13 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
tilesheetMap[sourceSheet] = targetSheet;
|
tilesheetMap[sourceSheet] = targetSheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get layer map
|
// get target layers
|
||||||
IDictionary<Layer, Layer> layerMap = source.Layers.ToDictionary(p => p, p => target.GetLayer(p.Id));
|
IDictionary<Layer, Layer> sourceToTargetLayers = source.Layers.ToDictionary(p => p, p => target.GetLayer(p.Id));
|
||||||
|
HashSet<Layer> orphanedTargetLayers = new HashSet<Layer>(target.Layers.Except(sourceToTargetLayers.Values));
|
||||||
|
|
||||||
// apply tiles
|
// apply tiles
|
||||||
|
bool replaceAll = patchMode == PatchMapMode.Replace;
|
||||||
|
bool replaceByLayer = patchMode == PatchMapMode.ReplaceByLayer;
|
||||||
for (int x = 0; x < sourceArea.Value.Width; x++)
|
for (int x = 0; x < sourceArea.Value.Width; x++)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < sourceArea.Value.Height; y++)
|
for (int y = 0; y < sourceArea.Value.Height; y++)
|
||||||
|
@ -96,47 +99,37 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
Point sourcePos = new Point(sourceArea.Value.X + x, sourceArea.Value.Y + y);
|
Point sourcePos = new Point(sourceArea.Value.X + x, sourceArea.Value.Y + y);
|
||||||
Point targetPos = new Point(targetArea.Value.X + x, targetArea.Value.Y + y);
|
Point targetPos = new Point(targetArea.Value.X + x, targetArea.Value.Y + y);
|
||||||
|
|
||||||
|
// replace tiles on target-only layers
|
||||||
|
if (replaceAll)
|
||||||
|
{
|
||||||
|
foreach (Layer targetLayer in orphanedTargetLayers)
|
||||||
|
targetLayer.Tiles[targetPos.X, targetPos.Y] = null;
|
||||||
|
}
|
||||||
|
|
||||||
// merge layers
|
// merge layers
|
||||||
foreach (Layer sourceLayer in source.Layers)
|
foreach (Layer sourceLayer in source.Layers)
|
||||||
{
|
{
|
||||||
// get layer
|
// get layer
|
||||||
Layer targetLayer = layerMap[sourceLayer];
|
Layer targetLayer = sourceToTargetLayers[sourceLayer];
|
||||||
if (targetLayer == null)
|
if (targetLayer == null)
|
||||||
{
|
{
|
||||||
target.AddLayer(targetLayer = new Layer(sourceLayer.Id, target, target.Layers[0].LayerSize, Layer.m_tileSize));
|
target.AddLayer(targetLayer = new Layer(sourceLayer.Id, target, target.Layers[0].LayerSize, Layer.m_tileSize));
|
||||||
layerMap[sourceLayer] = target.GetLayer(sourceLayer.Id);
|
sourceToTargetLayers[sourceLayer] = target.GetLayer(sourceLayer.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy layer properties
|
// copy layer properties
|
||||||
targetLayer.Properties.CopyFrom(sourceLayer.Properties);
|
targetLayer.Properties.CopyFrom(sourceLayer.Properties);
|
||||||
|
|
||||||
// copy tiles
|
// create new tile
|
||||||
Tile sourceTile = sourceLayer.Tiles[sourcePos.X, sourcePos.Y];
|
Tile sourceTile = sourceLayer.Tiles[sourcePos.X, sourcePos.Y];
|
||||||
Tile targetTile;
|
Tile newTile = sourceTile != null
|
||||||
switch (sourceTile)
|
? this.CreateTile(sourceTile, targetLayer, tilesheetMap[sourceTile.TileSheet])
|
||||||
{
|
: null;
|
||||||
case StaticTile _:
|
newTile?.Properties.CopyFrom(sourceTile.Properties);
|
||||||
targetTile = new StaticTile(targetLayer, tilesheetMap[sourceTile.TileSheet], sourceTile.BlendMode, sourceTile.TileIndex);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AnimatedTile animatedTile:
|
// replace tile
|
||||||
{
|
if (newTile != null || replaceByLayer || replaceAll)
|
||||||
StaticTile[] tileFrames = new StaticTile[animatedTile.TileFrames.Length];
|
targetLayer.Tiles[targetPos.X, targetPos.Y] = newTile;
|
||||||
for (int frame = 0; frame < animatedTile.TileFrames.Length; ++frame)
|
|
||||||
{
|
|
||||||
StaticTile frameTile = animatedTile.TileFrames[frame];
|
|
||||||
tileFrames[frame] = new StaticTile(targetLayer, tilesheetMap[frameTile.TileSheet], frameTile.BlendMode, frameTile.TileIndex);
|
|
||||||
}
|
|
||||||
targetTile = new AnimatedTile(targetLayer, tileFrames, animatedTile.FrameInterval);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // null or unhandled type
|
|
||||||
targetTile = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
targetTile?.Properties.CopyFrom(sourceTile.Properties);
|
|
||||||
targetLayer.Tiles[targetPos.X, targetPos.Y] = targetTile;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,6 +139,33 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
/*********
|
/*********
|
||||||
** Private methods
|
** Private methods
|
||||||
*********/
|
*********/
|
||||||
|
/// <summary>Create a new tile for the target map.</summary>
|
||||||
|
/// <param name="sourceTile">The source tile to copy.</param>
|
||||||
|
/// <param name="targetLayer">The target layer.</param>
|
||||||
|
/// <param name="targetSheet">The target tilesheet.</param>
|
||||||
|
private Tile CreateTile(Tile sourceTile, Layer targetLayer, TileSheet targetSheet)
|
||||||
|
{
|
||||||
|
switch (sourceTile)
|
||||||
|
{
|
||||||
|
case StaticTile _:
|
||||||
|
return new StaticTile(targetLayer, targetSheet, sourceTile.BlendMode, sourceTile.TileIndex);
|
||||||
|
|
||||||
|
case AnimatedTile animatedTile:
|
||||||
|
{
|
||||||
|
StaticTile[] tileFrames = new StaticTile[animatedTile.TileFrames.Length];
|
||||||
|
for (int frame = 0; frame < animatedTile.TileFrames.Length; ++frame)
|
||||||
|
{
|
||||||
|
StaticTile frameTile = animatedTile.TileFrames[frame];
|
||||||
|
tileFrames[frame] = new StaticTile(targetLayer, targetSheet, frameTile.BlendMode, frameTile.TileIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AnimatedTile(targetLayer, tileFrames, animatedTile.FrameInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
default: // null or unhandled type
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
/// <summary>Normalize a map tilesheet path for comparison. This value should *not* be used as the actual tilesheet path.</summary>
|
/// <summary>Normalize a map tilesheet path for comparison. This value should *not* be used as the actual tilesheet path.</summary>
|
||||||
/// <param name="path">The path to normalize.</param>
|
/// <param name="path">The path to normalize.</param>
|
||||||
private string NormalizeTilesheetPathForComparison(string path)
|
private string NormalizeTilesheetPathForComparison(string path)
|
||||||
|
|
|
@ -2,7 +2,6 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using StardewModdingAPI.Framework.Reflection;
|
using StardewModdingAPI.Framework.Reflection;
|
||||||
using StardewModdingAPI.Toolkit.Utilities;
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
@ -18,9 +17,6 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
/// <summary>The underlying asset cache.</summary>
|
/// <summary>The underlying asset cache.</summary>
|
||||||
private readonly IDictionary<string, object> Cache;
|
private readonly IDictionary<string, object> Cache;
|
||||||
|
|
||||||
/// <summary>Applies platform-specific asset key normalization so it's consistent with the underlying cache.</summary>
|
|
||||||
private readonly Func<string, string> NormalizeAssetNameForPlatform;
|
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Accessors
|
** Accessors
|
||||||
|
@ -48,17 +44,7 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
/// <param name="reflection">Simplifies access to private game code.</param>
|
/// <param name="reflection">Simplifies access to private game code.</param>
|
||||||
public ContentCache(LocalizedContentManager contentManager, Reflector reflection)
|
public ContentCache(LocalizedContentManager contentManager, Reflector reflection)
|
||||||
{
|
{
|
||||||
// init
|
|
||||||
this.Cache = reflection.GetField<Dictionary<string, object>>(contentManager, "loadedAssets").GetValue();
|
this.Cache = reflection.GetField<Dictionary<string, object>>(contentManager, "loadedAssets").GetValue();
|
||||||
|
|
||||||
// get key normalization logic
|
|
||||||
if (Constants.GameFramework == GameFramework.Xna)
|
|
||||||
{
|
|
||||||
IReflectedMethod method = reflection.GetMethod(typeof(TitleContainer), "GetCleanPath");
|
|
||||||
this.NormalizeAssetNameForPlatform = path => method.Invoke<string>(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****
|
/****
|
||||||
|
@ -75,23 +61,24 @@ namespace StardewModdingAPI.Framework.Content
|
||||||
/****
|
/****
|
||||||
** Normalize
|
** Normalize
|
||||||
****/
|
****/
|
||||||
/// <summary>Normalize path separators in a file path. For asset keys, see <see cref="NormalizeKey"/> instead.</summary>
|
/// <summary>Normalize path separators in an asset name.</summary>
|
||||||
/// <param name="path">The file path to normalize.</param>
|
/// <param name="path">The file path to normalize.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
public string NormalizePathSeparators(string path)
|
public string NormalizePathSeparators(string path)
|
||||||
{
|
{
|
||||||
return PathUtilities.NormalizePath(path);
|
return PathUtilities.NormalizeAssetName(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Normalize a cache key so it's consistent with the underlying cache.</summary>
|
/// <summary>Normalize a cache key so it's consistent with the underlying cache.</summary>
|
||||||
/// <param name="key">The asset key.</param>
|
/// <param name="key">The asset key.</param>
|
||||||
|
/// <remarks>This is equivalent to <see cref="NormalizePathSeparators"/> with added file extension logic.</remarks>
|
||||||
[Pure]
|
[Pure]
|
||||||
public string NormalizeKey(string key)
|
public string NormalizeKey(string key)
|
||||||
{
|
{
|
||||||
key = this.NormalizePathSeparators(key);
|
key = this.NormalizePathSeparators(key);
|
||||||
return key.EndsWith(".xnb", StringComparison.OrdinalIgnoreCase)
|
return key.EndsWith(".xnb", StringComparison.OrdinalIgnoreCase)
|
||||||
? key.Substring(0, key.Length - 4)
|
? key.Substring(0, key.Length - 4)
|
||||||
: this.NormalizeAssetNameForPlatform(key);
|
: key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****
|
/****
|
||||||
|
|
|
@ -64,6 +64,9 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>An unmodified content manager which doesn't intercept assets, used to compare asset data.</summary>
|
/// <summary>An unmodified content manager which doesn't intercept assets, used to compare asset data.</summary>
|
||||||
private readonly LocalizedContentManager VanillaContentManager;
|
private readonly LocalizedContentManager VanillaContentManager;
|
||||||
|
|
||||||
|
/// <summary>The language enum values indexed by locale code.</summary>
|
||||||
|
private Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>> LocaleCodes;
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Accessors
|
** Accessors
|
||||||
|
@ -133,6 +136,7 @@ namespace StardewModdingAPI.Framework
|
||||||
this.ContentManagers.Add(contentManagerForAssetPropagation);
|
this.ContentManagers.Add(contentManagerForAssetPropagation);
|
||||||
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
|
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
|
||||||
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations);
|
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations);
|
||||||
|
this.LocaleCodes = new Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>>(this.GetLocaleCodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary>
|
/// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary>
|
||||||
|
@ -195,6 +199,10 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>Perform any cleanup needed when the locale changes.</summary>
|
/// <summary>Perform any cleanup needed when the locale changes.</summary>
|
||||||
public void OnLocaleChanged()
|
public void OnLocaleChanged()
|
||||||
{
|
{
|
||||||
|
// rebuild locale cache (which may change due to custom mod languages)
|
||||||
|
this.LocaleCodes = new Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>>(this.GetLocaleCodes);
|
||||||
|
|
||||||
|
// reload affected content
|
||||||
this.ContentManagerLock.InReadLock(() =>
|
this.ContentManagerLock.InReadLock(() =>
|
||||||
{
|
{
|
||||||
foreach (IContentManager contentManager in this.ContentManagers)
|
foreach (IContentManager contentManager in this.ContentManagers)
|
||||||
|
@ -408,6 +416,25 @@ namespace StardewModdingAPI.Framework
|
||||||
return tilesheets ?? new TilesheetReference[0];
|
return tilesheets ?? new TilesheetReference[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the language enum which corresponds to a locale code (e.g. <see cref="LocalizedContentManager.LanguageCode.fr"/> given <c>fr-FR</c>).</summary>
|
||||||
|
/// <param name="locale">The locale code to search. This must exactly match the language; no fallback is performed.</param>
|
||||||
|
/// <param name="language">The matched language enum, if any.</param>
|
||||||
|
/// <returns>Returns whether a valid language was found.</returns>
|
||||||
|
public bool TryGetLanguageEnum(string locale, out LocalizedContentManager.LanguageCode language)
|
||||||
|
{
|
||||||
|
return this.LocaleCodes.Value.TryGetValue(locale, out language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the locale code which corresponds to a language enum (e.g. <c>fr-FR</c> given <see cref="LocalizedContentManager.LanguageCode.fr"/>).</summary>
|
||||||
|
/// <param name="language">The language enum to search.</param>
|
||||||
|
public string GetLocaleCode(LocalizedContentManager.LanguageCode language)
|
||||||
|
{
|
||||||
|
if (language == LocalizedContentManager.LanguageCode.mod && LocalizedContentManager.CurrentModLanguage == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Game1.content.LanguageCodeString(language);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Dispose held resources.</summary>
|
/// <summary>Dispose held resources.</summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
@ -457,5 +484,19 @@ namespace StardewModdingAPI.Framework
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Get the language enums (like <see cref="LocalizedContentManager.LanguageCode.ja"/>) indexed by locale code (like <c>ja-JP</c>).</summary>
|
||||||
|
private IDictionary<string, LocalizedContentManager.LanguageCode> GetLocaleCodes()
|
||||||
|
{
|
||||||
|
IDictionary<string, LocalizedContentManager.LanguageCode> map = new Dictionary<string, LocalizedContentManager.LanguageCode>();
|
||||||
|
foreach (LocalizedContentManager.LanguageCode code in Enum.GetValues(typeof(LocalizedContentManager.LanguageCode)))
|
||||||
|
{
|
||||||
|
string locale = this.GetLocaleCode(code);
|
||||||
|
if (locale != null)
|
||||||
|
map[locale] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <summary>A callback to invoke when the content manager is being disposed.</summary>
|
/// <summary>A callback to invoke when the content manager is being disposed.</summary>
|
||||||
private readonly Action<BaseContentManager> OnDisposing;
|
private readonly Action<BaseContentManager> OnDisposing;
|
||||||
|
|
||||||
/// <summary>The language enum values indexed by locale code.</summary>
|
|
||||||
protected IDictionary<string, LanguageCode> LanguageCodes { get; }
|
|
||||||
|
|
||||||
/// <summary>A list of disposable assets.</summary>
|
/// <summary>A list of disposable assets.</summary>
|
||||||
private readonly List<WeakReference<IDisposable>> Disposables = new List<WeakReference<IDisposable>>();
|
private readonly List<WeakReference<IDisposable>> Disposables = new List<WeakReference<IDisposable>>();
|
||||||
|
|
||||||
|
@ -92,7 +89,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
|
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
|
||||||
|
|
||||||
// get asset data
|
// get asset data
|
||||||
this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.OrdinalIgnoreCase);
|
|
||||||
this.BaseDisposableReferences = reflection.GetField<List<IDisposable>>(this, "disposableAssets").GetValue();
|
this.BaseDisposableReferences = reflection.GetField<List<IDisposable>>(this, "disposableAssets").GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +288,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
if (lastSepIndex >= 0)
|
if (lastSepIndex >= 0)
|
||||||
{
|
{
|
||||||
string suffix = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1);
|
string suffix = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1);
|
||||||
if (this.LanguageCodes.ContainsKey(suffix))
|
if (this.Coordinator.TryGetLanguageEnum(suffix, out _))
|
||||||
{
|
{
|
||||||
assetName = cacheKey.Substring(0, lastSepIndex);
|
assetName = cacheKey.Substring(0, lastSepIndex);
|
||||||
localeCode = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1);
|
localeCode = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1);
|
||||||
|
@ -311,17 +307,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <param name="language">The language to check.</param>
|
/// <param name="language">The language to check.</param>
|
||||||
protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language);
|
protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language);
|
||||||
|
|
||||||
/// <summary>Get the locale codes (like <c>ja-JP</c>) used in asset keys.</summary>
|
|
||||||
private IDictionary<LanguageCode, string> GetKeyLocales()
|
|
||||||
{
|
|
||||||
// create locale => code map
|
|
||||||
IDictionary<LanguageCode, string> map = new Dictionary<LanguageCode, string>();
|
|
||||||
foreach (LanguageCode code in Enum.GetValues(typeof(LanguageCode)))
|
|
||||||
map[code] = this.GetLocale(code);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get the asset name from a cache key.</summary>
|
/// <summary>Get the asset name from a cache key.</summary>
|
||||||
/// <param name="cacheKey">The input cache key.</param>
|
/// <param name="cacheKey">The input cache key.</param>
|
||||||
private string GetAssetName(string cacheKey)
|
private string GetAssetName(string cacheKey)
|
||||||
|
|
|
@ -249,7 +249,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
|
|
||||||
// extract language code
|
// extract language code
|
||||||
int splitIndex = rawAsset.LastIndexOf('.');
|
int splitIndex = rawAsset.LastIndexOf('.');
|
||||||
if (splitIndex != -1 && this.LanguageCodes.TryGetValue(rawAsset.Substring(splitIndex + 1), out language))
|
if (splitIndex != -1 && this.Coordinator.TryGetLanguageEnum(rawAsset.Substring(splitIndex + 1), out language))
|
||||||
{
|
{
|
||||||
assetName = rawAsset.Substring(0, splitIndex);
|
assetName = rawAsset.Substring(0, splitIndex);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -2,12 +2,12 @@ using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using BmFont;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Content;
|
using Microsoft.Xna.Framework.Content;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using StardewModdingAPI.Framework.Exceptions;
|
using StardewModdingAPI.Framework.Exceptions;
|
||||||
using StardewModdingAPI.Framework.Reflection;
|
using StardewModdingAPI.Framework.Reflection;
|
||||||
using StardewModdingAPI.Internal;
|
|
||||||
using StardewModdingAPI.Toolkit.Serialization;
|
using StardewModdingAPI.Toolkit.Serialization;
|
||||||
using StardewModdingAPI.Toolkit.Utilities;
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
@ -130,6 +130,14 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// unpacked Bitmap font
|
||||||
|
case ".fnt":
|
||||||
|
{
|
||||||
|
string source = File.ReadAllText(file.FullName);
|
||||||
|
asset = (T)(object)new XmlSource(source);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// unpacked data
|
// unpacked data
|
||||||
case ".json":
|
case ".json":
|
||||||
{
|
{
|
||||||
|
@ -172,13 +180,11 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.json', '.png', '.tbin', or '.xnb'.");
|
throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.fnt', '.json', '.png', '.tbin', or '.xnb'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (!(ex is SContentLoadException))
|
catch (Exception ex) when (!(ex is SContentLoadException))
|
||||||
{
|
{
|
||||||
if (ex.GetInnermostException() is DllNotFoundException dllEx && dllEx.Message == "libgdiplus.dylib")
|
|
||||||
throw GetContentError("couldn't find libgdiplus, which is needed to load mod images. Make sure Mono is installed and you're running the game through the normal launcher.");
|
|
||||||
throw new SContentLoadException($"The content manager failed loading content asset '{assetName}' from {this.Name}.", ex);
|
throw new SContentLoadException($"The content manager failed loading content asset '{assetName}' from {this.Name}.", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Input;
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
|
||||||
|
@ -157,11 +158,8 @@ namespace StardewModdingAPI.Framework.Input
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
foreach (var pair in this.ButtonStates)
|
foreach (Buttons button in this.GetPressedGamePadButtons())
|
||||||
{
|
|
||||||
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
|
|
||||||
yield return button.ToSButton();
|
yield return button.ToSButton();
|
||||||
}
|
|
||||||
|
|
||||||
// triggers
|
// triggers
|
||||||
if (this.LeftTrigger > 0.2f)
|
if (this.LeftTrigger > 0.2f)
|
||||||
|
@ -201,7 +199,7 @@ namespace StardewModdingAPI.Framework.Input
|
||||||
rightThumbStick: this.RightStickPos,
|
rightThumbStick: this.RightStickPos,
|
||||||
leftTrigger: this.LeftTrigger,
|
leftTrigger: this.LeftTrigger,
|
||||||
rightTrigger: this.RightTrigger,
|
rightTrigger: this.RightTrigger,
|
||||||
buttons: this.GetButtonBitmask() // MonoGame requires one bitmask here; don't specify multiple values
|
buttons: this.GetPressedGamePadButtons().ToArray()
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.State.Value;
|
return this.State.Value;
|
||||||
|
@ -211,17 +209,14 @@ namespace StardewModdingAPI.Framework.Input
|
||||||
/*********
|
/*********
|
||||||
** Private methods
|
** Private methods
|
||||||
*********/
|
*********/
|
||||||
/// <summary>Get a bitmask representing the pressed buttons.</summary>
|
/// <summary>Get the pressed gamepad buttons.</summary>
|
||||||
private Buttons GetButtonBitmask()
|
private IEnumerable<Buttons> GetPressedGamePadButtons()
|
||||||
{
|
{
|
||||||
Buttons flag = 0;
|
|
||||||
foreach (var pair in this.ButtonStates)
|
foreach (var pair in this.ButtonStates)
|
||||||
{
|
{
|
||||||
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
|
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
|
||||||
flag |= button;
|
yield return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
return flag;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ using System.Threading;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using StardewModdingAPI.Framework.Events;
|
using StardewModdingAPI.Framework.Events;
|
||||||
using StardewModdingAPI.Framework.Reflection;
|
using StardewModdingAPI.Framework.Reflection;
|
||||||
using StardewValley;
|
|
||||||
using StardewValley.Menus;
|
using StardewValley.Menus;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework
|
namespace StardewModdingAPI.Framework
|
||||||
|
@ -150,11 +149,7 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <param name="reflection">The reflection helper with which to access private fields.</param>
|
/// <param name="reflection">The reflection helper with which to access private fields.</param>
|
||||||
public static bool IsOpen(this SpriteBatch spriteBatch, Reflector reflection)
|
public static bool IsOpen(this SpriteBatch spriteBatch, Reflector reflection)
|
||||||
{
|
{
|
||||||
string fieldName = Constants.GameFramework == GameFramework.Xna
|
return reflection.GetField<bool>(spriteBatch, "_beginCalled").GetValue();
|
||||||
? "inBeginEndPair"
|
|
||||||
: "_beginCalled";
|
|
||||||
|
|
||||||
return reflection.GetField<bool>(Game1.spriteBatch, fieldName).GetValue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,36 +250,7 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
/// <param name="exception">The exception details.</param>
|
/// <param name="exception">The exception details.</param>
|
||||||
public void LogFatalLaunchError(Exception exception)
|
public void LogFatalLaunchError(Exception exception)
|
||||||
{
|
{
|
||||||
switch (exception)
|
|
||||||
{
|
|
||||||
// audio crash
|
|
||||||
case InvalidOperationException ex when ex.Source == "Microsoft.Xna.Framework.Xact" && ex.StackTrace.Contains("Microsoft.Xna.Framework.Audio.AudioEngine..ctor"):
|
|
||||||
this.Monitor.Log("The game couldn't load audio. Do you have speakers or headphones plugged in?", LogLevel.Error);
|
|
||||||
this.Monitor.Log($"Technical details: {ex.GetLogSummary()}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// missing content folder exception
|
|
||||||
case FileNotFoundException ex when ex.Message == "Couldn't find file 'C:\\Program Files (x86)\\Steam\\SteamApps\\common\\Stardew Valley\\Content\\XACT\\FarmerSounds.xgs'.": // path in error is hardcoded regardless of install path
|
|
||||||
this.Monitor.Log("The game can't find its Content\\XACT\\FarmerSounds.xgs file. You can usually fix this by resetting your content files (see https://smapi.io/troubleshoot#reset-content ), or by uninstalling and reinstalling the game.", LogLevel.Error);
|
|
||||||
this.Monitor.Log($"Technical details: {ex.GetLogSummary()}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// path too long exception
|
|
||||||
case PathTooLongException _:
|
|
||||||
{
|
|
||||||
string[] affectedPaths = PathUtilities.GetTooLongPaths(Constants.ModsPath).ToArray();
|
|
||||||
string message = affectedPaths.Any()
|
|
||||||
? $"SMAPI can't launch because some of your mod files exceed the maximum path length on {Constants.Platform}.\nIf you need help fixing this error, see https://smapi.io/help\n\nAffected paths:\n {string.Join("\n ", affectedPaths)}"
|
|
||||||
: $"The game failed to launch: {exception.GetLogSummary()}";
|
|
||||||
this.MonitorForGame.Log(message, LogLevel.Error);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// generic exception
|
|
||||||
default:
|
|
||||||
this.MonitorForGame.Log($"The game failed to launch: {exception.GetLogSummary()}", LogLevel.Error);
|
this.MonitorForGame.Log($"The game failed to launch: {exception.GetLogSummary()}", LogLevel.Error);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****
|
/****
|
||||||
|
@ -290,7 +261,7 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
/// <param name="customSettings">The custom SMAPI settings.</param>
|
/// <param name="customSettings">The custom SMAPI settings.</param>
|
||||||
public void LogIntro(string modsPath, IDictionary<string, object> customSettings)
|
public void LogIntro(string modsPath, IDictionary<string, object> customSettings)
|
||||||
{
|
{
|
||||||
// log platform & patches
|
// log platform
|
||||||
this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info);
|
this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info);
|
||||||
|
|
||||||
// log basic info
|
// log basic info
|
||||||
|
|
|
@ -53,16 +53,15 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
*********/
|
*********/
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
/// <param name="targetPlatform">The current game platform.</param>
|
/// <param name="targetPlatform">The current game platform.</param>
|
||||||
/// <param name="framework">The game framework running the game.</param>
|
|
||||||
/// <param name="monitor">Encapsulates monitoring and logging.</param>
|
/// <param name="monitor">Encapsulates monitoring and logging.</param>
|
||||||
/// <param name="paranoidMode">Whether to detect paranoid mode issues.</param>
|
/// <param name="paranoidMode">Whether to detect paranoid mode issues.</param>
|
||||||
/// <param name="rewriteMods">Whether to rewrite mods for compatibility.</param>
|
/// <param name="rewriteMods">Whether to rewrite mods for compatibility.</param>
|
||||||
public AssemblyLoader(Platform targetPlatform, GameFramework framework, IMonitor monitor, bool paranoidMode, bool rewriteMods)
|
public AssemblyLoader(Platform targetPlatform, IMonitor monitor, bool paranoidMode, bool rewriteMods)
|
||||||
{
|
{
|
||||||
this.Monitor = monitor;
|
this.Monitor = monitor;
|
||||||
this.ParanoidMode = paranoidMode;
|
this.ParanoidMode = paranoidMode;
|
||||||
this.RewriteMods = rewriteMods;
|
this.RewriteMods = rewriteMods;
|
||||||
this.AssemblyMap = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform, framework));
|
this.AssemblyMap = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform));
|
||||||
|
|
||||||
// init resolver
|
// init resolver
|
||||||
this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver());
|
this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver());
|
||||||
|
|
|
@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
||||||
{
|
{
|
||||||
/// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility between Linux/macOS or Windows.</summary>
|
/// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility with mods written for XNA Framework before Stardew Valley 1.5.5.</summary>
|
||||||
/// <remarks>This is public to support SMAPI rewriting and should not be referenced directly by mods.</remarks>
|
/// <remarks>This is public to support SMAPI rewriting and should not be referenced directly by mods.</remarks>
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")]
|
[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")]
|
||||||
[SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/macOS.")]
|
[SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/macOS.")]
|
||||||
|
@ -18,14 +18,6 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
||||||
public SpriteBatchFacade(GraphicsDevice graphicsDevice) : base(graphicsDevice) { }
|
public SpriteBatchFacade(GraphicsDevice graphicsDevice) : base(graphicsDevice) { }
|
||||||
|
|
||||||
|
|
||||||
/****
|
|
||||||
** MonoGame signatures
|
|
||||||
****/
|
|
||||||
public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix)
|
|
||||||
{
|
|
||||||
base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/****
|
/****
|
||||||
** XNA signatures
|
** XNA signatures
|
||||||
****/
|
****/
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace StardewModdingAPI.Framework.Reflection
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
public InterfaceProxyFactory()
|
public InterfaceProxyFactory()
|
||||||
{
|
{
|
||||||
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName($"StardewModdingAPI.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run);
|
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName($"StardewModdingAPI.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run);
|
||||||
this.ModuleBuilder = assemblyBuilder.DefineDynamicModule("StardewModdingAPI.Proxies");
|
this.ModuleBuilder = assemblyBuilder.DefineDynamicModule("StardewModdingAPI.Proxies");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,10 @@ using System.Runtime.ExceptionServices;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
#if SMAPI_FOR_WINDOWS
|
#if SMAPI_FOR_WINDOWS
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
#endif
|
#endif
|
||||||
#if SMAPI_FOR_XNA
|
|
||||||
using System.Windows.Forms;
|
|
||||||
#endif
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using StardewModdingAPI.Enums;
|
using StardewModdingAPI.Enums;
|
||||||
using StardewModdingAPI.Events;
|
using StardewModdingAPI.Events;
|
||||||
|
@ -224,10 +220,6 @@ namespace StardewModdingAPI.Framework
|
||||||
this.Toolkit.JsonHelper.JsonSettings.Converters.Add(converter);
|
this.Toolkit.JsonHelper.JsonSettings.Converters.Add(converter);
|
||||||
|
|
||||||
// add error handlers
|
// add error handlers
|
||||||
#if SMAPI_FOR_XNA
|
|
||||||
Application.ThreadException += (sender, e) => this.Monitor.Log($"Critical thread exception: {e.Exception.GetLogSummary()}", LogLevel.Error);
|
|
||||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
|
||||||
#endif
|
|
||||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error);
|
AppDomain.CurrentDomain.UnhandledException += (sender, e) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error);
|
||||||
|
|
||||||
// add more lenient assembly resolver
|
// add more lenient assembly resolver
|
||||||
|
@ -243,7 +235,7 @@ namespace StardewModdingAPI.Framework
|
||||||
monitor: this.Monitor,
|
monitor: this.Monitor,
|
||||||
reflection: this.Reflection,
|
reflection: this.Reflection,
|
||||||
eventManager: this.EventManager,
|
eventManager: this.EventManager,
|
||||||
modHooks: new SModHooks(this.OnNewDayAfterFade),
|
modHooks: new SModHooks(this.OnNewDayAfterFade, this.Monitor),
|
||||||
multiplayer: this.Multiplayer,
|
multiplayer: this.Multiplayer,
|
||||||
exitGameImmediately: this.ExitGameImmediately,
|
exitGameImmediately: this.ExitGameImmediately,
|
||||||
|
|
||||||
|
@ -657,13 +649,6 @@ namespace StardewModdingAPI.Framework
|
||||||
this.Monitor.Log("Game loader done.");
|
this.Monitor.Log("Game loader done.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance.NewDayTask?.Status == TaskStatus.Created)
|
|
||||||
{
|
|
||||||
this.Monitor.Log("New day task synchronizing...");
|
|
||||||
instance.NewDayTask.RunSynchronously();
|
|
||||||
this.Monitor.Log("New day task done.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// While a background task is in progress, the game may make changes to the game
|
// While a background task is in progress, the game may make changes to the game
|
||||||
// state while mods are running their code. This is risky, because data changes can
|
// state while mods are running their code. This is risky, because data changes can
|
||||||
// conflict (e.g. collection changed during enumeration errors) and data may change
|
// conflict (e.g. collection changed during enumeration errors) and data may change
|
||||||
|
@ -673,7 +658,7 @@ namespace StardewModdingAPI.Framework
|
||||||
// a small chance that the task will finish after we defer but before the game checks,
|
// a small chance that the task will finish after we defer but before the game checks,
|
||||||
// which means technically events should be raised, but the effects of missing one
|
// which means technically events should be raised, but the effects of missing one
|
||||||
// update tick are negligible and not worth the complications of bypassing Game1.Update.
|
// update tick are negligible and not worth the complications of bypassing Game1.Update.
|
||||||
if (instance.NewDayTask != null || Game1.gameMode == Game1.loadingMode)
|
if (Game1.gameMode == Game1.loadingMode)
|
||||||
{
|
{
|
||||||
events.UnvalidatedUpdateTicking.RaiseEmpty();
|
events.UnvalidatedUpdateTicking.RaiseEmpty();
|
||||||
runUpdate();
|
runUpdate();
|
||||||
|
@ -766,7 +751,7 @@ namespace StardewModdingAPI.Framework
|
||||||
** Locale changed events
|
** Locale changed events
|
||||||
*********/
|
*********/
|
||||||
if (state.Locale.IsChanged)
|
if (state.Locale.IsChanged)
|
||||||
this.Monitor.Log($"Context: locale set to {state.Locale.New}.");
|
this.Monitor.Log($"Context: locale set to {state.Locale.New} ({this.ContentCore.GetLocaleCode(state.Locale.New)}).");
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Load / return-to-title events
|
** Load / return-to-title events
|
||||||
|
@ -776,7 +761,7 @@ namespace StardewModdingAPI.Framework
|
||||||
else if (Context.IsWorldReady && Context.LoadStage != LoadStage.Ready)
|
else if (Context.IsWorldReady && Context.LoadStage != LoadStage.Ready)
|
||||||
{
|
{
|
||||||
// print context
|
// print context
|
||||||
string context = $"Context: loaded save '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}, locale set to {this.ContentCore.Language}.";
|
string context = $"Context: loaded save '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}, locale set to {this.ContentCore.GetLocale()}.";
|
||||||
if (Context.IsMultiplayer)
|
if (Context.IsMultiplayer)
|
||||||
{
|
{
|
||||||
int onlineCount = Game1.getOnlineFarmers().Count();
|
int onlineCount = Game1.getOnlineFarmers().Count();
|
||||||
|
@ -1304,9 +1289,6 @@ namespace StardewModdingAPI.Framework
|
||||||
{
|
{
|
||||||
// create client
|
// create client
|
||||||
string url = this.Settings.WebApiBaseUrl;
|
string url = this.Settings.WebApiBaseUrl;
|
||||||
#if !SMAPI_FOR_WINDOWS
|
|
||||||
url = url.Replace("https://", "http://"); // workaround for OpenSSL issues with the game's bundled Mono on Linux/macOS
|
|
||||||
#endif
|
|
||||||
WebApiClient client = new WebApiClient(url, Constants.ApiVersion);
|
WebApiClient client = new WebApiClient(url, Constants.ApiVersion);
|
||||||
this.Monitor.Log("Checking for updates...");
|
this.Monitor.Log("Checking for updates...");
|
||||||
|
|
||||||
|
@ -1491,7 +1473,7 @@ namespace StardewModdingAPI.Framework
|
||||||
|
|
||||||
// load mods
|
// load mods
|
||||||
IList<IModMetadata> skippedMods = new List<IModMetadata>();
|
IList<IModMetadata> skippedMods = new List<IModMetadata>();
|
||||||
using (AssemblyLoader modAssemblyLoader = new AssemblyLoader(Constants.Platform, Constants.GameFramework, this.Monitor, this.Settings.ParanoidWarnings, this.Settings.RewriteMods))
|
using (AssemblyLoader modAssemblyLoader = new AssemblyLoader(Constants.Platform, this.Monitor, this.Settings.ParanoidWarnings, this.Settings.RewriteMods))
|
||||||
{
|
{
|
||||||
// init
|
// init
|
||||||
HashSet<string> suppressUpdateChecks = new HashSet<string>(this.Settings.SuppressUpdateChecks, StringComparer.OrdinalIgnoreCase);
|
HashSet<string> suppressUpdateChecks = new HashSet<string>(this.Settings.SuppressUpdateChecks, StringComparer.OrdinalIgnoreCase);
|
||||||
|
@ -1701,9 +1683,8 @@ namespace StardewModdingAPI.Framework
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorReasonPhrase = "its DLL couldn't be loaded.";
|
errorReasonPhrase = "its DLL couldn't be loaded.";
|
||||||
// re-enable in Stardew Valley 1.5.5
|
if (ex is BadImageFormatException && !EnvironmentUtility.Is64BitAssembly(assemblyPath))
|
||||||
//if (ex is BadImageFormatException && !EnvironmentUtility.Is64BitAssembly(assemblyPath))
|
errorReasonPhrase = "it needs to be updated for 64-bit mode.";
|
||||||
// errorReasonPhrase = "it needs to be updated for 64-bit mode.";
|
|
||||||
|
|
||||||
errorDetails = $"Error: {ex.GetLogSummary()}";
|
errorDetails = $"Error: {ex.GetLogSummary()}";
|
||||||
failReason = ModFailReason.LoadFailed;
|
failReason = ModFailReason.LoadFailed;
|
||||||
|
|
|
@ -252,7 +252,7 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>Replicate the game's draw logic with some changes for SMAPI.</summary>
|
/// <summary>Replicate the game's draw logic with some changes for SMAPI.</summary>
|
||||||
/// <param name="gameTime">A snapshot of the game timing state.</param>
|
/// <param name="gameTime">A snapshot of the game timing state.</param>
|
||||||
/// <param name="target_screen">The render target, if any.</param>
|
/// <param name="target_screen">The render target, if any.</param>
|
||||||
/// <remarks>This implementation is identical to <see cref="Game1.Draw"/>, except for try..catch around menu draw code, private field references replaced by wrappers, and added events.</remarks>
|
/// <remarks>This implementation is identical to <see cref="Game1._draw"/>, except for try..catch around menu draw code, private field references replaced by wrappers, and added events.</remarks>
|
||||||
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator", Justification = "copied from game code as-is")]
|
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator", Justification = "copied from game code as-is")]
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "copied from game code as-is")]
|
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "copied from game code as-is")]
|
||||||
[SuppressMessage("ReSharper", "LocalVariableHidesMember", Justification = "copied from game code as-is")]
|
[SuppressMessage("ReSharper", "LocalVariableHidesMember", Justification = "copied from game code as-is")]
|
||||||
|
@ -286,7 +286,7 @@ namespace StardewModdingAPI.Framework
|
||||||
IClickableMenu menu = Game1.activeClickableMenu;
|
IClickableMenu menu = Game1.activeClickableMenu;
|
||||||
if (menu != null)
|
if (menu != null)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -304,7 +304,7 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (Game1.overlayMenu != null)
|
if (Game1.overlayMenu != null)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
Game1.overlayMenu.draw(Game1.spriteBatch);
|
Game1.overlayMenu.draw(Game1.spriteBatch);
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
}
|
}
|
||||||
|
@ -315,7 +315,7 @@ namespace StardewModdingAPI.Framework
|
||||||
if (Game1.activeClickableMenu != null && Game1.options.showMenuBackground && Game1.activeClickableMenu.showWithoutTransparencyIfOptionIsSet() && !this.takingMapScreenshot)
|
if (Game1.activeClickableMenu != null && Game1.options.showMenuBackground && Game1.activeClickableMenu.showWithoutTransparencyIfOptionIsSet() && !this.takingMapScreenshot)
|
||||||
{
|
{
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
|
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
IClickableMenu curMenu = null;
|
IClickableMenu curMenu = null;
|
||||||
|
@ -346,11 +346,11 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (Game1.gameMode == 11)
|
if (Game1.gameMode == 11)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3685"), new Vector2(16f, 16f), Microsoft.Xna.Framework.Color.HotPink);
|
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3685"), new Vector2(16f, 16f), Color.HotPink);
|
||||||
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3686"), new Vector2(16f, 32f), new Microsoft.Xna.Framework.Color(0, 255, 0));
|
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3686"), new Vector2(16f, 32f), new Color(0, 255, 0));
|
||||||
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.parseText(Game1.errorMessage, Game1.dialogueFont, Game1.graphics.GraphicsDevice.Viewport.Width), new Vector2(16f, 48f), Microsoft.Xna.Framework.Color.White);
|
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.parseText(Game1.errorMessage, Game1.dialogueFont, Game1.graphics.GraphicsDevice.Viewport.Width), new Vector2(16f, 48f), Color.White);
|
||||||
events.Rendered.RaiseEmpty();
|
events.Rendered.RaiseEmpty();
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
return;
|
return;
|
||||||
|
@ -368,8 +368,8 @@ namespace StardewModdingAPI.Framework
|
||||||
if (Game1.globalFade && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause))
|
if (Game1.globalFade && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause))
|
||||||
{
|
{
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Microsoft.Xna.Framework.Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha));
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha));
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PopUIMode();
|
Game1.PopUIMode();
|
||||||
}
|
}
|
||||||
|
@ -388,7 +388,7 @@ namespace StardewModdingAPI.Framework
|
||||||
if (Game1.showingEndOfNightStuff)
|
if (Game1.showingEndOfNightStuff)
|
||||||
{
|
{
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
if (Game1.activeClickableMenu != null)
|
if (Game1.activeClickableMenu != null)
|
||||||
{
|
{
|
||||||
|
@ -417,16 +417,16 @@ namespace StardewModdingAPI.Framework
|
||||||
{
|
{
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
base.GraphicsDevice.Clear(Game1.bgColor);
|
base.GraphicsDevice.Clear(Game1.bgColor);
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
string addOn = "";
|
string addOn = "";
|
||||||
for (int i = 0; (double)i < gameTime.TotalGameTime.TotalMilliseconds % 999.0 / 333.0; i++)
|
for (int i = 0; (double)i < gameTime.TotalGameTime.TotalMilliseconds % 999.0 / 333.0; i++)
|
||||||
{
|
{
|
||||||
addOn += ".";
|
addOn += ".";
|
||||||
}
|
}
|
||||||
string str = Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3688");
|
string text = Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3688");
|
||||||
string msg = str + addOn;
|
string msg = text + addOn;
|
||||||
string largestMessage = str + "... ";
|
string largestMessage = text + "... ";
|
||||||
int msgw = SpriteText.getWidthOfString(largestMessage);
|
int msgw = SpriteText.getWidthOfString(largestMessage);
|
||||||
int msgh = 64;
|
int msgh = 64;
|
||||||
int msgx = 64;
|
int msgx = 64;
|
||||||
|
@ -442,7 +442,7 @@ namespace StardewModdingAPI.Framework
|
||||||
byte batchOpens = 0; // used for rendering event
|
byte batchOpens = 0; // used for rendering event
|
||||||
if (Game1.gameMode == 0)
|
if (Game1.gameMode == 0)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if (++batchOpens == 1)
|
if (++batchOpens == 1)
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
}
|
}
|
||||||
|
@ -456,7 +456,7 @@ namespace StardewModdingAPI.Framework
|
||||||
if (Game1.drawLighting)
|
if (Game1.drawLighting)
|
||||||
{
|
{
|
||||||
Game1.SetRenderTarget(Game1.lightmap);
|
Game1.SetRenderTarget(Game1.lightmap);
|
||||||
base.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.White * 0f);
|
base.GraphicsDevice.Clear(Color.White * 0f);
|
||||||
Matrix lighting_matrix = Matrix.Identity;
|
Matrix lighting_matrix = Matrix.Identity;
|
||||||
if (this.useUnscaledLighting)
|
if (this.useUnscaledLighting)
|
||||||
{
|
{
|
||||||
|
@ -465,13 +465,13 @@ namespace StardewModdingAPI.Framework
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, lighting_matrix);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, lighting_matrix);
|
||||||
if (++batchOpens == 1)
|
if (++batchOpens == 1)
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
Microsoft.Xna.Framework.Color lighting = (Game1.currentLocation.Name.StartsWith("UndergroundMine") && Game1.currentLocation is MineShaft) ? (Game1.currentLocation as MineShaft).getLightingColor(gameTime) : ((Game1.ambientLight.Equals(Microsoft.Xna.Framework.Color.White) || (Game1.IsRainingHere() && (bool)Game1.currentLocation.isOutdoors)) ? Game1.outdoorLight : Game1.ambientLight);
|
Color lighting = ((Game1.currentLocation.Name.StartsWith("UndergroundMine") && Game1.currentLocation is MineShaft) ? (Game1.currentLocation as MineShaft).getLightingColor(gameTime) : ((Game1.ambientLight.Equals(Color.White) || (Game1.IsRainingHere() && (bool)Game1.currentLocation.isOutdoors)) ? Game1.outdoorLight : Game1.ambientLight));
|
||||||
float light_multiplier = 1f;
|
float light_multiplier = 1f;
|
||||||
if (Game1.player.hasBuff(26))
|
if (Game1.player.hasBuff(26))
|
||||||
{
|
{
|
||||||
if (lighting == Microsoft.Xna.Framework.Color.White)
|
if (lighting == Color.White)
|
||||||
{
|
{
|
||||||
lighting = new Microsoft.Xna.Framework.Color(0.75f, 0.75f, 0.75f);
|
lighting = new Color(0.75f, 0.75f, 0.75f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -504,12 +504,8 @@ namespace StardewModdingAPI.Framework
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.SetRenderTarget(target_screen);
|
Game1.SetRenderTarget(target_screen);
|
||||||
}
|
}
|
||||||
if (Game1.bloomDay && Game1.bloom != null)
|
|
||||||
{
|
|
||||||
Game1.bloom.BeginDraw();
|
|
||||||
}
|
|
||||||
base.GraphicsDevice.Clear(Game1.bgColor);
|
base.GraphicsDevice.Clear(Game1.bgColor);
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if (++batchOpens == 1)
|
if (++batchOpens == 1)
|
||||||
events.Rendering.RaiseEmpty();
|
events.Rendering.RaiseEmpty();
|
||||||
events.RenderingWorld.RaiseEmpty();
|
events.RenderingWorld.RaiseEmpty();
|
||||||
|
@ -522,10 +518,10 @@ namespace StardewModdingAPI.Framework
|
||||||
Game1.currentLocation.Map.GetLayer("Back").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, wrapAround: false, 4);
|
Game1.currentLocation.Map.GetLayer("Back").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, wrapAround: false, 4);
|
||||||
Game1.currentLocation.drawWater(Game1.spriteBatch);
|
Game1.currentLocation.drawWater(Game1.spriteBatch);
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
Game1.currentLocation.drawFloorDecorations(Game1.spriteBatch);
|
Game1.currentLocation.drawFloorDecorations(Game1.spriteBatch);
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
this._farmerShadows.Clear();
|
this._farmerShadows.Clear();
|
||||||
if (Game1.currentLocation.currentEvent != null && !Game1.currentLocation.currentEvent.isFestival && Game1.currentLocation.currentEvent.farmerActors.Count > 0)
|
if (Game1.currentLocation.currentEvent != null && !Game1.currentLocation.currentEvent.isFestival && Game1.currentLocation.currentEvent.farmerActors.Count > 0)
|
||||||
{
|
{
|
||||||
|
@ -555,17 +551,17 @@ namespace StardewModdingAPI.Framework
|
||||||
{
|
{
|
||||||
if (!k.swimming && !k.HideShadow && !k.IsInvisible && !this.checkCharacterTilesForShadowDrawFlag(k))
|
if (!k.swimming && !k.HideShadow && !k.IsInvisible && !this.checkCharacterTilesForShadowDrawFlag(k))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, k.GetShadowOffset() + k.Position + new Vector2((float)(k.GetSpriteWidthForPositioning() * 4) / 2f, k.GetBoundingBox().Height + ((!k.IsMonster) ? 12 : 0))), Game1.shadowTexture.Bounds, Microsoft.Xna.Framework.Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, (4f + (float)k.yJumpOffset / 40f) * (float)k.scale), SpriteEffects.None, Math.Max(0f, (float)k.getStandingY() / 10000f) - 1E-06f);
|
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, k.GetShadowOffset() + k.Position + new Vector2((float)(k.GetSpriteWidthForPositioning() * 4) / 2f, k.GetBoundingBox().Height + ((!k.IsMonster) ? 12 : 0))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, (4f + (float)k.yJumpOffset / 40f) * (float)k.scale), SpriteEffects.None, Math.Max(0f, (float)k.getStandingY() / 10000f) - 1E-06f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (NPC m in Game1.CurrentEvent.actors)
|
foreach (NPC l in Game1.CurrentEvent.actors)
|
||||||
{
|
{
|
||||||
if ((Game1.CurrentEvent == null || !Game1.CurrentEvent.ShouldHideCharacter(m)) && !m.swimming && !m.HideShadow && !this.checkCharacterTilesForShadowDrawFlag(m))
|
if ((Game1.CurrentEvent == null || !Game1.CurrentEvent.ShouldHideCharacter(l)) && !l.swimming && !l.HideShadow && !this.checkCharacterTilesForShadowDrawFlag(l))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, m.GetShadowOffset() + m.Position + new Vector2((float)(m.GetSpriteWidthForPositioning() * 4) / 2f, m.GetBoundingBox().Height + ((!m.IsMonster) ? ((m.Sprite.SpriteHeight <= 16) ? (-4) : 12) : 0))), Game1.shadowTexture.Bounds, Microsoft.Xna.Framework.Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, 4f + (float)m.yJumpOffset / 40f) * (float)m.scale, SpriteEffects.None, Math.Max(0f, (float)m.getStandingY() / 10000f) - 1E-06f);
|
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, l.GetShadowOffset() + l.Position + new Vector2((float)(l.GetSpriteWidthForPositioning() * 4) / 2f, l.GetBoundingBox().Height + ((!l.IsMonster) ? ((l.Sprite.SpriteHeight <= 16) ? (-4) : 12) : 0))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, 4f + (float)l.yJumpOffset / 40f) * (float)l.scale, SpriteEffects.None, Math.Max(0f, (float)l.getStandingY() / 10000f) - 1E-06f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,7 +569,7 @@ namespace StardewModdingAPI.Framework
|
||||||
{
|
{
|
||||||
if (!Game1.multiplayer.isDisconnecting(f3.UniqueMultiplayerID) && !f3.swimming && !f3.isRidingHorse() && !f3.IsSitting() && (Game1.currentLocation == null || !this.checkCharacterTilesForShadowDrawFlag(f3)))
|
if (!Game1.multiplayer.isDisconnecting(f3.UniqueMultiplayerID) && !f3.swimming && !f3.isRidingHorse() && !f3.IsSitting() && (Game1.currentLocation == null || !this.checkCharacterTilesForShadowDrawFlag(f3)))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(f3.GetShadowOffset() + f3.Position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Microsoft.Xna.Framework.Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((f3.running || f3.UsingTool) && f3.FarmerSprite.currentAnimationIndex > 1) ? ((float)Math.Abs(FarmerRenderer.featureYOffsetPerFrame[f3.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, 0f);
|
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(f3.GetShadowOffset() + f3.Position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((f3.running || f3.UsingTool) && f3.FarmerSprite.currentAnimationIndex > 1) ? ((float)Math.Abs(FarmerRenderer.featureYOffsetPerFrame[f3.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, 0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -581,26 +577,26 @@ namespace StardewModdingAPI.Framework
|
||||||
building_layer.Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, wrapAround: false, 4);
|
building_layer.Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, wrapAround: false, 4);
|
||||||
Game1.mapDisplayDevice.EndScene();
|
Game1.mapDisplayDevice.EndScene();
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if (!Game1.currentLocation.shouldHideCharacters())
|
if (!Game1.currentLocation.shouldHideCharacters())
|
||||||
{
|
{
|
||||||
if (Game1.CurrentEvent == null)
|
if (Game1.CurrentEvent == null)
|
||||||
{
|
{
|
||||||
foreach (NPC n in Game1.currentLocation.characters)
|
foreach (NPC m in Game1.currentLocation.characters)
|
||||||
{
|
{
|
||||||
if (!n.swimming && !n.HideShadow && !n.isInvisible && this.checkCharacterTilesForShadowDrawFlag(n))
|
if (!m.swimming && !m.HideShadow && !m.isInvisible && this.checkCharacterTilesForShadowDrawFlag(m))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, n.GetShadowOffset() + n.Position + new Vector2((float)(n.GetSpriteWidthForPositioning() * 4) / 2f, n.GetBoundingBox().Height + ((!n.IsMonster) ? 12 : 0))), Game1.shadowTexture.Bounds, Microsoft.Xna.Framework.Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, (4f + (float)n.yJumpOffset / 40f) * (float)n.scale), SpriteEffects.None, Math.Max(0f, (float)n.getStandingY() / 10000f) - 1E-06f);
|
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, m.GetShadowOffset() + m.Position + new Vector2((float)(m.GetSpriteWidthForPositioning() * 4) / 2f, m.GetBoundingBox().Height + ((!m.IsMonster) ? 12 : 0))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, (4f + (float)m.yJumpOffset / 40f) * (float)m.scale), SpriteEffects.None, Math.Max(0f, (float)m.getStandingY() / 10000f) - 1E-06f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (NPC n2 in Game1.CurrentEvent.actors)
|
foreach (NPC n in Game1.CurrentEvent.actors)
|
||||||
{
|
{
|
||||||
if ((Game1.CurrentEvent == null || !Game1.CurrentEvent.ShouldHideCharacter(n2)) && !n2.swimming && !n2.HideShadow && this.checkCharacterTilesForShadowDrawFlag(n2))
|
if ((Game1.CurrentEvent == null || !Game1.CurrentEvent.ShouldHideCharacter(n)) && !n.swimming && !n.HideShadow && this.checkCharacterTilesForShadowDrawFlag(n))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, n2.GetShadowOffset() + n2.Position + new Vector2((float)(n2.GetSpriteWidthForPositioning() * 4) / 2f, n2.GetBoundingBox().Height + ((!n2.IsMonster) ? 12 : 0))), Game1.shadowTexture.Bounds, Microsoft.Xna.Framework.Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, (4f + (float)n2.yJumpOffset / 40f) * (float)n2.scale), SpriteEffects.None, Math.Max(0f, (float)n2.getStandingY() / 10000f) - 1E-06f);
|
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, n.GetShadowOffset() + n.Position + new Vector2((float)(n.GetSpriteWidthForPositioning() * 4) / 2f, n.GetBoundingBox().Height + ((!n.IsMonster) ? 12 : 0))), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), Math.Max(0f, (4f + (float)n.yJumpOffset / 40f) * (float)n.scale), SpriteEffects.None, Math.Max(0f, (float)n.getStandingY() / 10000f) - 1E-06f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -609,7 +605,7 @@ namespace StardewModdingAPI.Framework
|
||||||
float draw_layer = Math.Max(0.0001f, f4.getDrawLayer() + 0.00011f) - 0.0001f;
|
float draw_layer = Math.Max(0.0001f, f4.getDrawLayer() + 0.00011f) - 0.0001f;
|
||||||
if (!f4.swimming && !f4.isRidingHorse() && !f4.IsSitting() && Game1.currentLocation != null && this.checkCharacterTilesForShadowDrawFlag(f4))
|
if (!f4.swimming && !f4.isRidingHorse() && !f4.IsSitting() && Game1.currentLocation != null && this.checkCharacterTilesForShadowDrawFlag(f4))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(f4.GetShadowOffset() + f4.Position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Microsoft.Xna.Framework.Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((f4.running || f4.UsingTool) && f4.FarmerSprite.currentAnimationIndex > 1) ? ((float)Math.Abs(FarmerRenderer.featureYOffsetPerFrame[f4.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, draw_layer);
|
Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(f4.GetShadowOffset() + f4.Position + new Vector2(32f, 24f)), Game1.shadowTexture.Bounds, Color.White, 0f, new Vector2(Game1.shadowTexture.Bounds.Center.X, Game1.shadowTexture.Bounds.Center.Y), 4f - (((f4.running || f4.UsingTool) && f4.FarmerSprite.currentAnimationIndex > 1) ? ((float)Math.Abs(FarmerRenderer.featureYOffsetPerFrame[f4.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, draw_layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -619,7 +615,7 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (Game1.player.currentUpgrade != null && Game1.player.currentUpgrade.daysLeftTillUpgradeDone <= 3 && Game1.currentLocation.Name.Equals("Farm"))
|
if (Game1.player.currentUpgrade != null && Game1.player.currentUpgrade.daysLeftTillUpgradeDone <= 3 && Game1.currentLocation.Name.Equals("Farm"))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.player.currentUpgrade.workerTexture, Game1.GlobalToLocal(Game1.viewport, Game1.player.currentUpgrade.positionOfCarpenter), Game1.player.currentUpgrade.getSourceRectangle(), Microsoft.Xna.Framework.Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, (Game1.player.currentUpgrade.positionOfCarpenter.Y + 48f) / 10000f);
|
Game1.spriteBatch.Draw(Game1.player.currentUpgrade.workerTexture, Game1.GlobalToLocal(Game1.viewport, Game1.player.currentUpgrade.positionOfCarpenter), Game1.player.currentUpgrade.getSourceRectangle(), Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, (Game1.player.currentUpgrade.positionOfCarpenter.Y + 48f) / 10000f);
|
||||||
}
|
}
|
||||||
Game1.currentLocation.draw(Game1.spriteBatch);
|
Game1.currentLocation.draw(Game1.spriteBatch);
|
||||||
foreach (Vector2 tile_position in Game1.crabPotOverlayTiles.Keys)
|
foreach (Vector2 tile_position in Game1.crabPotOverlayTiles.Keys)
|
||||||
|
@ -646,14 +642,14 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (Game1.tvStation >= 0)
|
if (Game1.tvStation >= 0)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.tvStationTexture, Game1.GlobalToLocal(Game1.viewport, new Vector2(400f, 160f)), new Microsoft.Xna.Framework.Rectangle(Game1.tvStation * 24, 0, 24, 15), Microsoft.Xna.Framework.Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1E-08f);
|
Game1.spriteBatch.Draw(Game1.tvStationTexture, Game1.GlobalToLocal(Game1.viewport, new Vector2(400f, 160f)), new Microsoft.Xna.Framework.Rectangle(Game1.tvStation * 24, 0, 24, 15), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1E-08f);
|
||||||
}
|
}
|
||||||
if (Game1.panMode)
|
if (Game1.panMode)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle((int)Math.Floor((double)(Game1.getOldMouseX() + Game1.viewport.X) / 64.0) * 64 - Game1.viewport.X, (int)Math.Floor((double)(Game1.getOldMouseY() + Game1.viewport.Y) / 64.0) * 64 - Game1.viewport.Y, 64, 64), Microsoft.Xna.Framework.Color.Lime * 0.75f);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle((int)Math.Floor((double)(Game1.getOldMouseX() + Game1.viewport.X) / 64.0) * 64 - Game1.viewport.X, (int)Math.Floor((double)(Game1.getOldMouseY() + Game1.viewport.Y) / 64.0) * 64 - Game1.viewport.Y, 64, 64), Color.Lime * 0.75f);
|
||||||
foreach (Warp w in Game1.currentLocation.warps)
|
foreach (Warp w in Game1.currentLocation.warps)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(w.X * 64 - Game1.viewport.X, w.Y * 64 - Game1.viewport.Y, 64, 64), Microsoft.Xna.Framework.Color.Red * 0.75f);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(w.X * 64 - Game1.viewport.X, w.Y * 64 - Game1.viewport.Y, 64, 64), Color.Red * 0.75f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch);
|
Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch);
|
||||||
|
@ -661,7 +657,7 @@ namespace StardewModdingAPI.Framework
|
||||||
Game1.mapDisplayDevice.EndScene();
|
Game1.mapDisplayDevice.EndScene();
|
||||||
Game1.currentLocation.drawAboveFrontLayer(Game1.spriteBatch);
|
Game1.currentLocation.drawAboveFrontLayer(Game1.spriteBatch);
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if (Game1.currentLocation.Map.GetLayer("AlwaysFront") != null)
|
if (Game1.currentLocation.Map.GetLayer("AlwaysFront") != null)
|
||||||
{
|
{
|
||||||
Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch);
|
Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch);
|
||||||
|
@ -670,7 +666,7 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (Game1.toolHold > 400f && Game1.player.CurrentTool.UpgradeLevel >= 1 && Game1.player.canReleaseTool)
|
if (Game1.toolHold > 400f && Game1.player.CurrentTool.UpgradeLevel >= 1 && Game1.player.canReleaseTool)
|
||||||
{
|
{
|
||||||
Microsoft.Xna.Framework.Color barColor = Microsoft.Xna.Framework.Color.White;
|
Color barColor = Color.White;
|
||||||
switch ((int)(Game1.toolHold / 600f) + 2)
|
switch ((int)(Game1.toolHold / 600f) + 2)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -686,7 +682,7 @@ namespace StardewModdingAPI.Framework
|
||||||
barColor = Tool.iridiumColor;
|
barColor = Tool.iridiumColor;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.Draw(Game1.littleEffect, new Microsoft.Xna.Framework.Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X - 2, (int)Game1.player.getLocalPosition(Game1.viewport).Y - ((!Game1.player.CurrentTool.Name.Equals("Watering Can")) ? 64 : 0) - 2, (int)(Game1.toolHold % 600f * 0.08f) + 4, 12), Microsoft.Xna.Framework.Color.Black);
|
Game1.spriteBatch.Draw(Game1.littleEffect, new Microsoft.Xna.Framework.Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X - 2, (int)Game1.player.getLocalPosition(Game1.viewport).Y - ((!Game1.player.CurrentTool.Name.Equals("Watering Can")) ? 64 : 0) - 2, (int)(Game1.toolHold % 600f * 0.08f) + 4, 12), Color.Black);
|
||||||
Game1.spriteBatch.Draw(Game1.littleEffect, new Microsoft.Xna.Framework.Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X, (int)Game1.player.getLocalPosition(Game1.viewport).Y - ((!Game1.player.CurrentTool.Name.Equals("Watering Can")) ? 64 : 0), (int)(Game1.toolHold % 600f * 0.08f), 8), barColor);
|
Game1.spriteBatch.Draw(Game1.littleEffect, new Microsoft.Xna.Framework.Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X, (int)Game1.player.getLocalPosition(Game1.viewport).Y - ((!Game1.player.CurrentTool.Name.Equals("Watering Can")) ? 64 : 0), (int)(Game1.toolHold % 600f * 0.08f), 8), barColor);
|
||||||
}
|
}
|
||||||
if (!Game1.IsFakedBlackScreen())
|
if (!Game1.IsFakedBlackScreen())
|
||||||
|
@ -699,7 +695,7 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (Game1.currentLocation.LightLevel > 0f && Game1.timeOfDay < 2000)
|
if (Game1.currentLocation.LightLevel > 0f && Game1.timeOfDay < 2000)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Microsoft.Xna.Framework.Color.Black * Game1.currentLocation.LightLevel);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * Game1.currentLocation.LightLevel);
|
||||||
}
|
}
|
||||||
if (Game1.screenGlow)
|
if (Game1.screenGlow)
|
||||||
{
|
{
|
||||||
|
@ -711,51 +707,51 @@ namespace StardewModdingAPI.Framework
|
||||||
Game1.player.CurrentTool.draw(Game1.spriteBatch);
|
Game1.player.CurrentTool.draw(Game1.spriteBatch);
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if (Game1.eventUp && Game1.currentLocation.currentEvent != null)
|
if (Game1.eventUp && Game1.currentLocation.currentEvent != null)
|
||||||
{
|
{
|
||||||
foreach (NPC l in Game1.currentLocation.currentEvent.actors)
|
foreach (NPC n2 in Game1.currentLocation.currentEvent.actors)
|
||||||
{
|
{
|
||||||
if (l.isEmoting)
|
if (n2.isEmoting)
|
||||||
{
|
{
|
||||||
Vector2 emotePosition = l.getLocalPosition(Game1.viewport);
|
Vector2 emotePosition = n2.getLocalPosition(Game1.viewport);
|
||||||
if (l.NeedsBirdieEmoteHack())
|
if (n2.NeedsBirdieEmoteHack())
|
||||||
{
|
{
|
||||||
emotePosition.X += 64f;
|
emotePosition.X += 64f;
|
||||||
}
|
}
|
||||||
emotePosition.Y -= 140f;
|
emotePosition.Y -= 140f;
|
||||||
if (l.Age == 2)
|
if (n2.Age == 2)
|
||||||
{
|
{
|
||||||
emotePosition.Y += 32f;
|
emotePosition.Y += 32f;
|
||||||
}
|
}
|
||||||
else if (l.Gender == 1)
|
else if (n2.Gender == 1)
|
||||||
{
|
{
|
||||||
emotePosition.Y += 10f;
|
emotePosition.Y += 10f;
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.Draw(Game1.emoteSpriteSheet, emotePosition, new Microsoft.Xna.Framework.Rectangle(l.CurrentEmoteIndex * 16 % Game1.emoteSpriteSheet.Width, l.CurrentEmoteIndex * 16 / Game1.emoteSpriteSheet.Width * 16, 16, 16), Microsoft.Xna.Framework.Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, (float)l.getStandingY() / 10000f);
|
Game1.spriteBatch.Draw(Game1.emoteSpriteSheet, emotePosition, new Microsoft.Xna.Framework.Rectangle(n2.CurrentEmoteIndex * 16 % Game1.emoteSpriteSheet.Width, n2.CurrentEmoteIndex * 16 / Game1.emoteSpriteSheet.Width * 16, 16, 16), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, (float)n2.getStandingY() / 10000f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
if (Game1.drawLighting && !Game1.IsFakedBlackScreen())
|
if (Game1.drawLighting && !Game1.IsFakedBlackScreen())
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, this.lightingBlend, SamplerState.LinearClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, this.lightingBlend, SamplerState.LinearClamp);
|
||||||
Viewport vp = base.GraphicsDevice.Viewport;
|
Viewport vp = base.GraphicsDevice.Viewport;
|
||||||
vp.Bounds = (target_screen?.Bounds ?? base.GraphicsDevice.PresentationParameters.Bounds);
|
vp.Bounds = target_screen?.Bounds ?? base.GraphicsDevice.PresentationParameters.Bounds;
|
||||||
base.GraphicsDevice.Viewport = vp;
|
base.GraphicsDevice.Viewport = vp;
|
||||||
float render_zoom = Game1.options.lightingQuality / 2;
|
float render_zoom = Game1.options.lightingQuality / 2;
|
||||||
if (this.useUnscaledLighting)
|
if (this.useUnscaledLighting)
|
||||||
{
|
{
|
||||||
render_zoom /= Game1.options.zoomLevel;
|
render_zoom /= Game1.options.zoomLevel;
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.Draw(Game1.lightmap, Vector2.Zero, Game1.lightmap.Bounds, Microsoft.Xna.Framework.Color.White, 0f, Vector2.Zero, render_zoom, SpriteEffects.None, 1f);
|
Game1.spriteBatch.Draw(Game1.lightmap, Vector2.Zero, Game1.lightmap.Bounds, Color.White, 0f, Vector2.Zero, render_zoom, SpriteEffects.None, 1f);
|
||||||
if (Game1.IsRainingHere() && (bool)Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert))
|
if (Game1.IsRainingHere() && (bool)Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.staminaRect, vp.Bounds, Microsoft.Xna.Framework.Color.OrangeRed * 0.45f);
|
Game1.spriteBatch.Draw(Game1.staminaRect, vp.Bounds, Color.OrangeRed * 0.45f);
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
events.RenderedWorld.RaiseEmpty();
|
events.RenderedWorld.RaiseEmpty();
|
||||||
if (Game1.drawGrid)
|
if (Game1.drawGrid)
|
||||||
{
|
{
|
||||||
|
@ -763,11 +759,11 @@ namespace StardewModdingAPI.Framework
|
||||||
float startingY = -Game1.viewport.Y % 64;
|
float startingY = -Game1.viewport.Y % 64;
|
||||||
for (int x = startingX; x < Game1.graphics.GraphicsDevice.Viewport.Width; x += 64)
|
for (int x = startingX; x < Game1.graphics.GraphicsDevice.Viewport.Width; x += 64)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle(x, (int)startingY, 1, Game1.graphics.GraphicsDevice.Viewport.Height), Microsoft.Xna.Framework.Color.Red * 0.5f);
|
Game1.spriteBatch.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle(x, (int)startingY, 1, Game1.graphics.GraphicsDevice.Viewport.Height), Color.Red * 0.5f);
|
||||||
}
|
}
|
||||||
for (float y = startingY; y < (float)Game1.graphics.GraphicsDevice.Viewport.Height; y += 64f)
|
for (float y = startingY; y < (float)Game1.graphics.GraphicsDevice.Viewport.Height; y += 64f)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle(startingX, (int)y, Game1.graphics.GraphicsDevice.Viewport.Width, 1), Microsoft.Xna.Framework.Color.Red * 0.5f);
|
Game1.spriteBatch.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle(startingX, (int)y, Game1.graphics.GraphicsDevice.Viewport.Width, 1), Color.Red * 0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Game1.ShouldShowOnscreenUsernames() && Game1.currentLocation != null)
|
if (Game1.ShouldShowOnscreenUsernames() && Game1.currentLocation != null)
|
||||||
|
@ -780,14 +776,14 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (!Game1.eventUp && Game1.farmEvent == null && Game1.currentBillboard == 0 && Game1.gameMode == 3 && !this.takingMapScreenshot && Game1.isOutdoorMapSmallerThanViewport())
|
if (!Game1.eventUp && Game1.farmEvent == null && Game1.currentBillboard == 0 && Game1.gameMode == 3 && !this.takingMapScreenshot && Game1.isOutdoorMapSmallerThanViewport())
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, -Math.Min(Game1.viewport.X, GameRunner.MaxTextureSize), Game1.graphics.GraphicsDevice.Viewport.Height), Microsoft.Xna.Framework.Color.Black);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, -Math.Min(Game1.viewport.X, GameRunner.MaxTextureSize), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black);
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64, 0, Math.Min(GameRunner.MaxTextureSize, Game1.graphics.GraphicsDevice.Viewport.Width - (-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64)), Game1.graphics.GraphicsDevice.Viewport.Height), Microsoft.Xna.Framework.Color.Black);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64, 0, Math.Min(GameRunner.MaxTextureSize, Game1.graphics.GraphicsDevice.Viewport.Width - (-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64)), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black);
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, Game1.graphics.GraphicsDevice.Viewport.Width, -Math.Min(Game1.viewport.Y, GameRunner.MaxTextureSize)), Microsoft.Xna.Framework.Color.Black);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, Game1.graphics.GraphicsDevice.Viewport.Width, -Math.Min(Game1.viewport.Y, GameRunner.MaxTextureSize)), Color.Black);
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, -Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64, Game1.graphics.GraphicsDevice.Viewport.Width, Math.Min(GameRunner.MaxTextureSize, Game1.graphics.GraphicsDevice.Viewport.Height - (-Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64))), Microsoft.Xna.Framework.Color.Black);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, -Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64, Game1.graphics.GraphicsDevice.Viewport.Width, Math.Min(GameRunner.MaxTextureSize, Game1.graphics.GraphicsDevice.Viewport.Height - (-Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64))), Color.Black);
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if ((Game1.displayHUD || Game1.eventUp) && Game1.currentBillboard == 0 && Game1.gameMode == 3 && !Game1.freezeControls && !Game1.panMode && !Game1.HostPaused && !this.takingMapScreenshot)
|
if ((Game1.displayHUD || Game1.eventUp) && Game1.currentBillboard == 0 && Game1.gameMode == 3 && !Game1.freezeControls && !Game1.panMode && !Game1.HostPaused && !this.takingMapScreenshot)
|
||||||
{
|
{
|
||||||
events.RenderingHud.RaiseEmpty();
|
events.RenderingHud.RaiseEmpty();
|
||||||
|
@ -807,13 +803,13 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PopUIMode();
|
Game1.PopUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
}
|
}
|
||||||
if (Game1.farmEvent != null)
|
if (Game1.farmEvent != null)
|
||||||
{
|
{
|
||||||
Game1.farmEvent.draw(Game1.spriteBatch);
|
Game1.farmEvent.draw(Game1.spriteBatch);
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
}
|
}
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
if (Game1.dialogueUp && !Game1.nameSelectUp && !Game1.messagePause && (Game1.activeClickableMenu == null || !(Game1.activeClickableMenu is DialogueBox)) && !this.takingMapScreenshot)
|
if (Game1.dialogueUp && !Game1.nameSelectUp && !Game1.messagePause && (Game1.activeClickableMenu == null || !(Game1.activeClickableMenu is DialogueBox)) && !this.takingMapScreenshot)
|
||||||
|
@ -822,35 +818,35 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
if (Game1.progressBar && !this.takingMapScreenshot)
|
if (Game1.progressBar && !this.takingMapScreenshot)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 128, Game1.dialogueWidth, 32), Microsoft.Xna.Framework.Color.LightGray);
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 128, Game1.dialogueWidth, 32), Color.LightGray);
|
||||||
Game1.spriteBatch.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 128, (int)(Game1.pauseAccumulator / Game1.pauseTime * (float)Game1.dialogueWidth), 32), Microsoft.Xna.Framework.Color.DimGray);
|
Game1.spriteBatch.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 128, (int)(Game1.pauseAccumulator / Game1.pauseTime * (float)Game1.dialogueWidth), 32), Color.DimGray);
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PopUIMode();
|
Game1.PopUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if (Game1.eventUp && Game1.currentLocation != null && Game1.currentLocation.currentEvent != null)
|
if (Game1.eventUp && Game1.currentLocation != null && Game1.currentLocation.currentEvent != null)
|
||||||
{
|
{
|
||||||
Game1.currentLocation.currentEvent.drawAfterMap(Game1.spriteBatch);
|
Game1.currentLocation.currentEvent.drawAfterMap(Game1.spriteBatch);
|
||||||
}
|
}
|
||||||
if (!Game1.IsFakedBlackScreen() && Game1.IsRainingHere() && Game1.currentLocation != null && (bool)Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert))
|
if (!Game1.IsFakedBlackScreen() && Game1.IsRainingHere() && Game1.currentLocation != null && (bool)Game1.currentLocation.isOutdoors && !(Game1.currentLocation is Desert))
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Microsoft.Xna.Framework.Color.Blue * 0.2f);
|
Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Blue * 0.2f);
|
||||||
}
|
}
|
||||||
if ((Game1.fadeToBlack || Game1.globalFade) && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause) && !this.takingMapScreenshot)
|
if ((Game1.fadeToBlack || Game1.globalFade) && !Game1.menuUp && (!Game1.nameSelectUp || Game1.messagePause) && !this.takingMapScreenshot)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Microsoft.Xna.Framework.Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha));
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha));
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PopUIMode();
|
Game1.PopUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
}
|
}
|
||||||
else if (Game1.flashAlpha > 0f && !this.takingMapScreenshot)
|
else if (Game1.flashAlpha > 0f && !this.takingMapScreenshot)
|
||||||
{
|
{
|
||||||
if (Game1.options.screenFlash)
|
if (Game1.options.screenFlash)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Microsoft.Xna.Framework.Color.White * Math.Min(1f, Game1.flashAlpha));
|
Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.White * Math.Min(1f, Game1.flashAlpha));
|
||||||
}
|
}
|
||||||
Game1.flashAlpha -= 0.1f;
|
Game1.flashAlpha -= 0.1f;
|
||||||
}
|
}
|
||||||
|
@ -866,14 +862,14 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
foreach (TemporaryAnimatedSprite uiOverlayTempSprite in Game1.uiOverlayTempSprites)
|
foreach (TemporaryAnimatedSprite uiOverlayTempSprite in Game1.uiOverlayTempSprites)
|
||||||
{
|
{
|
||||||
uiOverlayTempSprite.draw(Game1.spriteBatch, localPosition: true);
|
uiOverlayTempSprite.draw(Game1.spriteBatch, localPosition: true);
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PopUIMode();
|
Game1.PopUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
}
|
}
|
||||||
if (Game1.debugMode)
|
if (Game1.debugMode)
|
||||||
{
|
{
|
||||||
|
@ -905,14 +901,14 @@ namespace StardewModdingAPI.Framework
|
||||||
sb.Append(Game1.getMouseY() + Game1.viewport.Y);
|
sb.Append(Game1.getMouseY() + Game1.viewport.Y);
|
||||||
sb.Append(" debugOutput: ");
|
sb.Append(" debugOutput: ");
|
||||||
sb.Append(Game1.debugOutput);
|
sb.Append(Game1.debugOutput);
|
||||||
Game1.spriteBatch.DrawString(Game1.smallFont, sb, new Vector2(base.GraphicsDevice.Viewport.GetTitleSafeArea().X, base.GraphicsDevice.Viewport.GetTitleSafeArea().Y + Game1.smallFont.LineSpacing * 8), Microsoft.Xna.Framework.Color.Red, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f);
|
Game1.spriteBatch.DrawString(Game1.smallFont, sb, new Vector2(base.GraphicsDevice.Viewport.GetTitleSafeArea().X, base.GraphicsDevice.Viewport.GetTitleSafeArea().Y + Game1.smallFont.LineSpacing * 8), Color.Red, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f);
|
||||||
}
|
}
|
||||||
Game1.spriteBatch.End();
|
Game1.spriteBatch.End();
|
||||||
Game1.PushUIMode();
|
Game1.PushUIMode();
|
||||||
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
|
Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||||
if (Game1.showKeyHelp && !this.takingMapScreenshot)
|
if (Game1.showKeyHelp && !this.takingMapScreenshot)
|
||||||
{
|
{
|
||||||
Game1.spriteBatch.DrawString(Game1.smallFont, Game1.keyHelpString, new Vector2(64f, (float)(Game1.viewport.Height - 64 - (Game1.dialogueUp ? (192 + (Game1.isQuestion ? (Game1.questionChoices.Count * 64) : 0)) : 0)) - Game1.smallFont.MeasureString(Game1.keyHelpString).Y), Microsoft.Xna.Framework.Color.LightGray, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f);
|
Game1.spriteBatch.DrawString(Game1.smallFont, Game1.keyHelpString, new Vector2(64f, (float)(Game1.viewport.Height - 64 - (Game1.dialogueUp ? (192 + (Game1.isQuestion ? (Game1.questionChoices.Count * 64) : 0)) : 0)) - Game1.smallFont.MeasureString(Game1.keyHelpString).Y), Color.LightGray, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f);
|
||||||
}
|
}
|
||||||
if (Game1.activeClickableMenu != null && !this.takingMapScreenshot)
|
if (Game1.activeClickableMenu != null && !this.takingMapScreenshot)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework
|
namespace StardewModdingAPI.Framework
|
||||||
|
@ -12,15 +13,20 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</summary>
|
/// <summary>A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</summary>
|
||||||
private readonly Action BeforeNewDayAfterFade;
|
private readonly Action BeforeNewDayAfterFade;
|
||||||
|
|
||||||
|
/// <summary>Writes messages to the console.</summary>
|
||||||
|
private readonly IMonitor Monitor;
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
*********/
|
*********/
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
/// <param name="beforeNewDayAfterFade">A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</param>
|
/// <param name="beforeNewDayAfterFade">A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</param>
|
||||||
public SModHooks(Action beforeNewDayAfterFade)
|
/// <param name="monitor">Writes messages to the console.</param>
|
||||||
|
public SModHooks(Action beforeNewDayAfterFade, IMonitor monitor)
|
||||||
{
|
{
|
||||||
this.BeforeNewDayAfterFade = beforeNewDayAfterFade;
|
this.BeforeNewDayAfterFade = beforeNewDayAfterFade;
|
||||||
|
this.Monitor = monitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>A hook invoked when <see cref="Game1.newDayAfterFade"/> is called.</summary>
|
/// <summary>A hook invoked when <see cref="Game1.newDayAfterFade"/> is called.</summary>
|
||||||
|
@ -30,5 +36,27 @@ namespace StardewModdingAPI.Framework
|
||||||
this.BeforeNewDayAfterFade?.Invoke();
|
this.BeforeNewDayAfterFade?.Invoke();
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Start an asynchronous task for the game.</summary>
|
||||||
|
/// <param name="task">The task to start.</param>
|
||||||
|
/// <param name="id">A unique key which identifies the task.</param>
|
||||||
|
public override Task StartTask(Task task, string id)
|
||||||
|
{
|
||||||
|
this.Monitor.Log($"Synchronizing '{id}' task...");
|
||||||
|
task.RunSynchronously();
|
||||||
|
this.Monitor.Log(" task complete.");
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Start an asynchronous task for the game.</summary>
|
||||||
|
/// <param name="task">The task to start.</param>
|
||||||
|
/// <param name="id">A unique key which identifies the task.</param>
|
||||||
|
public override Task<T> StartTask<T>(Task<T> task, string id)
|
||||||
|
{
|
||||||
|
this.Monitor.Log($"Synchronizing '{id}' task...");
|
||||||
|
task.RunSynchronously();
|
||||||
|
this.Monitor.Log(" task complete.");
|
||||||
|
return task;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace StardewModdingAPI
|
namespace StardewModdingAPI
|
||||||
{
|
{
|
||||||
/// <summary>The game framework running the game.</summary>
|
/// <summary>The game framework running the game.</summary>
|
||||||
public enum GameFramework
|
public enum GameFramework
|
||||||
{
|
{
|
||||||
/// <summary>The XNA Framework on Windows.</summary>
|
/// <summary>The XNA Framework, previously used on Windows.</summary>
|
||||||
|
[Obsolete("Stardew Valley no longer uses XNA Framework on any supported platform.")]
|
||||||
Xna,
|
Xna,
|
||||||
|
|
||||||
/// <summary>The MonoGame framework, usually on non-Windows platforms.</summary>
|
/// <summary>The MonoGame framework.</summary>
|
||||||
MonoGame
|
MonoGame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace StardewModdingAPI
|
||||||
/// <param name="source">The map from which to copy.</param>
|
/// <param name="source">The map from which to copy.</param>
|
||||||
/// <param name="sourceArea">The tile area within the source map to copy, or <c>null</c> for the entire source map size. This must be within the bounds of the <paramref name="source"/> map.</param>
|
/// <param name="sourceArea">The tile area within the source map to copy, or <c>null</c> for the entire source map size. This must be within the bounds of the <paramref name="source"/> map.</param>
|
||||||
/// <param name="targetArea">The tile area within the target map to overwrite, or <c>null</c> to patch the whole map. The original content within this area will be erased. This must be within the bounds of the existing map.</param>
|
/// <param name="targetArea">The tile area within the target map to overwrite, or <c>null</c> to patch the whole map. The original content within this area will be erased. This must be within the bounds of the existing map.</param>
|
||||||
void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null);
|
/// <param name="patchMode">Indicates how the map should be patched.</param>
|
||||||
|
void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null, PatchMapMode patchMode = PatchMapMode.Overlay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,13 +226,8 @@ namespace StardewModdingAPI.Metadata
|
||||||
** Buildings
|
** Buildings
|
||||||
****/
|
****/
|
||||||
case "buildings\\houses": // Farm
|
case "buildings\\houses": // Farm
|
||||||
{
|
Farm.houseTextures = this.LoadAndDisposeIfNeeded(Farm.houseTextures, key);
|
||||||
var field = reflection.GetField<Texture2D>(typeof(Farm), nameof(Farm.houseTextures));
|
|
||||||
field.SetValue(
|
|
||||||
this.LoadAndDisposeIfNeeded(field.GetValue(), key)
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
case "buildings\\houses_paintmask": // Farm
|
case "buildings\\houses_paintmask": // Farm
|
||||||
{
|
{
|
||||||
|
@ -447,10 +442,6 @@ namespace StardewModdingAPI.Metadata
|
||||||
Game1.objectSpriteSheet = content.Load<Texture2D>(key);
|
Game1.objectSpriteSheet = content.Load<Texture2D>(key);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case "maps\\walls_and_floors": // Wallpaper
|
|
||||||
Wallpaper.wallpaperTexture = content.Load<Texture2D>(key);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/****
|
/****
|
||||||
** Content\Minigames
|
** Content\Minigames
|
||||||
****/
|
****/
|
||||||
|
|
|
@ -36,9 +36,6 @@ namespace StardewModdingAPI.Metadata
|
||||||
// rewrite for crossplatform compatibility
|
// rewrite for crossplatform compatibility
|
||||||
if (rewriteMods)
|
if (rewriteMods)
|
||||||
{
|
{
|
||||||
if (platformChanged)
|
|
||||||
yield return new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchFacade));
|
|
||||||
|
|
||||||
// rewrite for Stardew Valley 1.5
|
// rewrite for Stardew Valley 1.5
|
||||||
yield return new FieldReplaceRewriter(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture));
|
yield return new FieldReplaceRewriter(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture));
|
||||||
yield return new FieldReplaceRewriter(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
yield return new FieldReplaceRewriter(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
||||||
|
@ -48,9 +45,10 @@ namespace StardewModdingAPI.Metadata
|
||||||
yield return new HeuristicFieldRewriter(this.ValidateReferencesToAssemblies);
|
yield return new HeuristicFieldRewriter(this.ValidateReferencesToAssemblies);
|
||||||
yield return new HeuristicMethodRewriter(this.ValidateReferencesToAssemblies);
|
yield return new HeuristicMethodRewriter(this.ValidateReferencesToAssemblies);
|
||||||
|
|
||||||
// rewrite for 64-bit mode
|
// rewrite for Stardew Valley 1.5.5
|
||||||
// re-enable in Stardew Valley 1.5.5
|
if (platformChanged)
|
||||||
//yield return new ArchitectureAssemblyRewriter();
|
yield return new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchFacade));
|
||||||
|
yield return new ArchitectureAssemblyRewriter();
|
||||||
|
|
||||||
// detect Harmony & rewrite for SMAPI 3.12 (Harmony 1.x => 2.0 update)
|
// detect Harmony & rewrite for SMAPI 3.12 (Harmony 1.x => 2.0 update)
|
||||||
yield return new HarmonyRewriter();
|
yield return new HarmonyRewriter();
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace StardewModdingAPI
|
||||||
|
{
|
||||||
|
/// <summary>Indicates how a map should be patched.</summary>
|
||||||
|
public enum PatchMapMode
|
||||||
|
{
|
||||||
|
/// <summary>Replace matching tiles. Target tiles missing in the source area are kept as-is.</summary>
|
||||||
|
Overlay,
|
||||||
|
|
||||||
|
/// <summary>Replace all tiles on layers that exist in the source map.</summary>
|
||||||
|
ReplaceByLayer,
|
||||||
|
|
||||||
|
/// <summary>Replace all tiles with the source map.</summary>
|
||||||
|
Replace
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,8 +4,8 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using StardewModdingAPI.Framework;
|
using StardewModdingAPI.Framework;
|
||||||
using StardewModdingAPI.Toolkit.Framework;
|
|
||||||
using StardewModdingAPI.Toolkit.Serialization.Models;
|
using StardewModdingAPI.Toolkit.Serialization.Models;
|
||||||
|
using StardewModdingAPI.Toolkit.Utilities;
|
||||||
|
|
||||||
namespace StardewModdingAPI
|
namespace StardewModdingAPI
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ namespace StardewModdingAPI
|
||||||
/// <param name="args">The command-line arguments.</param>
|
/// <param name="args">The command-line arguments.</param>
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.Title = $"SMAPI {EarlyConstants.RawApiVersion} - {Console.Title}";
|
Console.Title = $"SMAPI {EarlyConstants.RawApiVersion}";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -34,9 +34,10 @@ namespace StardewModdingAPI
|
||||||
Program.AssertGamePresent();
|
Program.AssertGamePresent();
|
||||||
Program.AssertGameVersion();
|
Program.AssertGameVersion();
|
||||||
Program.AssertSmapiVersions();
|
Program.AssertSmapiVersions();
|
||||||
|
Program.AssertDepsJson();
|
||||||
Program.Start(args);
|
Program.Start(args);
|
||||||
}
|
}
|
||||||
catch (BadImageFormatException ex) when (ex.FileName == "StardewValley" || ex.FileName == "Stardew Valley") // don't use EarlyConstants.GameAssemblyName, since we want to check both possible names
|
catch (BadImageFormatException ex) when (ex.FileName == EarlyConstants.GameAssemblyName)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"SMAPI failed to initialize because your game's {ex.FileName}.exe seems to be invalid.\nThis may be a pirated version which modified the executable in an incompatible way; if so, you can try a different download or buy a legitimate version.\n\nTechnical details:\n{ex}");
|
Console.WriteLine($"SMAPI failed to initialize because your game's {ex.FileName}.exe seems to be invalid.\nThis may be a pirated version which modified the executable in an incompatible way; if so, you can try a different download or buy a legitimate version.\n\nTechnical details:\n{ex}");
|
||||||
}
|
}
|
||||||
|
@ -84,22 +85,10 @@ namespace StardewModdingAPI
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// unofficial 64-bit
|
|
||||||
if (EarlyConstants.Platform == GamePlatform.Windows)
|
|
||||||
{
|
|
||||||
FileInfo linuxExecutable = new FileInfo(Path.Combine(EarlyConstants.ExecutionPath, "StardewValley.exe"));
|
|
||||||
if (linuxExecutable.Exists && LowLevelEnvironmentUtility.Is64BitAssembly(linuxExecutable.FullName))
|
|
||||||
Program.PrintErrorAndExit("Oops! You're running Stardew Valley in unofficial 64-bit mode, which is no longer supported. You can update to Stardew Valley 1.5.5 or later instead. See https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows for more info.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// file doesn't exist
|
// file doesn't exist
|
||||||
if (!File.Exists(Path.Combine(EarlyConstants.ExecutionPath, $"{EarlyConstants.GameAssemblyName}.exe")))
|
if (!File.Exists(Path.Combine(EarlyConstants.ExecutionPath, $"{EarlyConstants.GameAssemblyName}.exe")))
|
||||||
Program.PrintErrorAndExit("Oops! SMAPI can't find the game. Make sure you're running StardewModdingAPI.exe in your game folder.");
|
Program.PrintErrorAndExit("Oops! SMAPI can't find the game. Make sure you're running StardewModdingAPI.exe in your game folder.");
|
||||||
|
|
||||||
// Stardew Valley 1.5.5+
|
|
||||||
if (File.Exists(Path.Combine(EarlyConstants.ExecutionPath, "Stardew Valley.dll")))
|
|
||||||
Program.PrintErrorAndExit("Oops! You're running Stardew Valley 1.5.5 or later, but this version of SMAPI is only compatible up to Stardew Valley 1.5.4. Please check for a newer version of SMAPI: https://smapi.io.");
|
|
||||||
|
|
||||||
// can't load file
|
// can't load file
|
||||||
Program.PrintErrorAndExit(
|
Program.PrintErrorAndExit(
|
||||||
message: "Oops! SMAPI couldn't load the game executable. The technical details below may have more info.",
|
message: "Oops! SMAPI couldn't load the game executable. The technical details below may have more info.",
|
||||||
|
@ -143,6 +132,20 @@ namespace StardewModdingAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Assert that SMAPI's <c>StardewModdingAPI.deps.json</c> matches <c>Stardew Valley.deps.json</c>, fixing it if necessary.</summary>
|
||||||
|
/// <remarks>This is needed to resolve native DLLs like libSkiaSharp.</remarks>
|
||||||
|
private static void AssertDepsJson()
|
||||||
|
{
|
||||||
|
string sourcePath = Path.Combine(Constants.ExecutionPath, "Stardew Valley.deps.json");
|
||||||
|
string targetPath = Path.Combine(Constants.ExecutionPath, "StardewModdingAPI.deps.json");
|
||||||
|
|
||||||
|
if (!File.Exists(targetPath) || FileUtilities.GetFileHash(sourcePath) != FileUtilities.GetFileHash(targetPath))
|
||||||
|
{
|
||||||
|
File.Copy(sourcePath, targetPath, overwrite: true);
|
||||||
|
Program.PrintErrorAndExit($"The '{Path.GetFileName(targetPath)}' file didn't match the game's version. SMAPI fixed it automatically, but you must restart SMAPI for the change to take effect.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Initialize SMAPI and launch the game.</summary>
|
/// <summary>Initialize SMAPI and launch the game.</summary>
|
||||||
/// <param name="args">The command-line arguments.</param>
|
/// <param name="args">The command-line arguments.</param>
|
||||||
/// <remarks>This method is separate from <see cref="Main"/> because that can't contain any references to assemblies loaded by <see cref="CurrentDomain_AssemblyResolve"/> (e.g. via <see cref="Constants"/>), or Mono will incorrectly show an assembly resolution error before assembly resolution is set up.</remarks>
|
/// <remarks>This method is separate from <see cref="Main"/> because that can't contain any references to assemblies loaded by <see cref="CurrentDomain_AssemblyResolve"/> (e.g. via <see cref="Constants"/>), or Mono will incorrectly show an assembly resolution error before assembly resolution is set up.</remarks>
|
||||||
|
|
|
@ -76,8 +76,6 @@ copy all the settings, or you may cause bugs due to overridden changes in future
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base URL for SMAPI's web API, used to perform update checks.
|
* The base URL for SMAPI's web API, used to perform update checks.
|
||||||
* Note: the protocol will be changed to http:// on Linux/macOS due to OpenSSL issues with the
|
|
||||||
* game's bundled Mono.
|
|
||||||
*/
|
*/
|
||||||
"WebApiBaseUrl": "https://smapi.io/api/",
|
"WebApiBaseUrl": "https://smapi.io/api/",
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,16 @@
|
||||||
<AssemblyName>StardewModdingAPI</AssemblyName>
|
<AssemblyName>StardewModdingAPI</AssemblyName>
|
||||||
<RootNamespace>StardewModdingAPI</RootNamespace>
|
<RootNamespace>StardewModdingAPI</RootNamespace>
|
||||||
<Description>The modding API for Stardew Valley.</Description>
|
<Description>The modding API for Stardew Valley.</Description>
|
||||||
<TargetFramework>net452</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<LargeAddressAware Condition="'$(OS)' == 'Windows_NT'">true</LargeAddressAware>
|
<LargeAddressAware Condition="'$(OS)' == 'Windows_NT'">true</LargeAddressAware>
|
||||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||||
|
|
||||||
|
<!--copy dependency DLLs to bin folder so we can include them in installer bundle -->
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="..\..\build\common.targets" />
|
<Import Project="..\..\build\common.targets" />
|
||||||
|
@ -19,43 +22,22 @@
|
||||||
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
|
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
|
||||||
<PackageReference Include="MonoMod.Common" Version="21.6.21.1" />
|
<PackageReference Include="MonoMod.Common" Version="21.6.21.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="Platonymous.TMXTile" Version="1.5.8" />
|
<PackageReference Include="Platonymous.TMXTile" Version="1.5.9" />
|
||||||
|
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
|
||||||
|
<PackageReference Include="System.Runtime.Caching" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="..\..\build\0Harmony.dll" Private="True" />
|
<Reference Include="..\..\build\0Harmony.dll" Private="True" />
|
||||||
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
|
<Reference Include="Stardew Valley" HintPath="$(GamePath)\Stardew Valley.dll" Private="False" />
|
||||||
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
|
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
|
||||||
<Reference Include="System.Numerics" Private="True" />
|
<Reference Include="BmFont" HintPath="$(GamePath)\BmFont.dll" Private="False" />
|
||||||
<Reference Include="System.Runtime.Caching" Private="True" />
|
|
||||||
<Reference Include="GalaxyCSharp" HintPath="$(GamePath)\GalaxyCSharp.dll" Private="False" />
|
<Reference Include="GalaxyCSharp" HintPath="$(GamePath)\GalaxyCSharp.dll" Private="False" />
|
||||||
<Reference Include="Lidgren.Network" HintPath="$(GamePath)\Lidgren.Network.dll" Private="False" />
|
<Reference Include="Lidgren.Network" HintPath="$(GamePath)\Lidgren.Network.dll" Private="False" />
|
||||||
|
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
|
||||||
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
|
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Windows only -->
|
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
|
||||||
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
|
|
||||||
<Reference Include="System.Windows.Forms" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Game framework -->
|
|
||||||
<Choose>
|
|
||||||
<When Condition="$(DefineConstants.Contains(SMAPI_FOR_XNA))">
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
</When>
|
|
||||||
<Otherwise>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Otherwise>
|
|
||||||
</Choose>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\SMAPI.Toolkit.CoreInterfaces\SMAPI.Toolkit.CoreInterfaces.csproj" />
|
<ProjectReference Include="..\SMAPI.Toolkit.CoreInterfaces\SMAPI.Toolkit.CoreInterfaces.csproj" />
|
||||||
<ProjectReference Include="..\SMAPI.Toolkit\SMAPI.Toolkit.csproj" />
|
<ProjectReference Include="..\SMAPI.Toolkit\SMAPI.Toolkit.csproj" />
|
||||||
|
|
Loading…
Reference in New Issue