Skip to content

Commit

Permalink
Merge branch 'develop' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
Pathoschild committed Jan 16, 2017
2 parents b9dd6eb + 65a52f4 commit b28c28f
Show file tree
Hide file tree
Showing 25 changed files with 398 additions and 133 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Expand Up @@ -7,11 +7,11 @@ StardewInjector/bin/
StardewInjector/obj/
packages/
steamapps/


*.symlink
*.lnk
!*.exe
!*.dll
!*.dll

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
Expand Down
46 changes: 34 additions & 12 deletions README.md
Expand Up @@ -6,12 +6,15 @@ with the game. It's safely installed alongside the game's executable, and doesn'
your game files.

## Contents
* [For players](#for-players)
* [For mod developers](#for-mod-developers)
* **[For players](#for-players)**
* **[For mod developers](#for-mod-developers)**
* [For SMAPI developers](#for-smapi-developers)
* [Compiling from source](#compiling-from-source)
* [Debugging a local build](#debugging-a-local-build)
* [Preparing a release](#preparing-a-release)
* [Advanced usage](#advanced-usage)
* [Configuration file](#configuration-file)
* [Command-line arguments](#command-line-arguments)

## For players
* [How to install SMAPI & use mods](http://canimod.com/guides/using-mods#installing-smapi)
Expand All @@ -36,18 +39,18 @@ If you'd like to compile SMAPI from source, you can do that on any platform usin
[Visual Studio](https://www.visualstudio.com/vs/community/) or [MonoDevelop](http://www.monodevelop.com/).
SMAPI uses build configuration derived from the [crosswiki mod config](https://github.com/Pathoschild/Stardew.ModBuildConfig#readme)
to detect your current OS automatically and load the correct references. Compile output will be
placed in a `bin` directory at the root of the git repository.
placed in a `bin` folder at the root of the git repository.

### Debugging a local build
Rebuilding the solution in debug mode will copy the SMAPI files into your game directory. Starting
Rebuilding the solution in debug mode will copy the SMAPI files into your game folder. Starting
the `StardewModdingAPI` project with debugging will launch SMAPI with the debugger attached, so you
can intercept errors and step through the code being executed.

### Preparing a release
To prepare a crossplatform SMAPI release, you'll need to compile it on two platforms. See
_[crossplatforming a SMAPI mod](http://canimod.com/guides/crossplatforming-a-smapi-mod#preparing-a-mod-release)_
for the first-time setup. For simplicity, all paths are relative to the root of the repository (the
directory containing `src`).
folder containing `src`).

1. Update the version number in `GlobalAssemblyInfo.cs` and `Constants::Version`. Make sure you use a
[semantic version](http://semver.org). Recommended format:
Expand All @@ -61,14 +64,14 @@ directory containing `src`).
2. In Windows:
1. Rebuild the solution in _Release_ mode.
2. Rename `bin/Packaged` to `SMAPI-<version>` (e.g. `SMAPI-1.0`).
2. Transfer the `SMAPI-<version>` directory to Linux or Mac.
2. Transfer the `SMAPI-<version>` folder to Linux or Mac.
_This adds the installer executable and Windows files. We'll do the rest in Linux or Mac,
since we need to set Unix file permissions that Windows won't save._

2. In Linux or Mac:
1. Rebuild the solution in _Release_ mode.
2. Copy `bin/Packaged/Mono` into the `SMAPI-<version>` directory.
3. If you did everything right so far, you should have a directory like this:
2. Copy `bin/Packaged/Mono` into the `SMAPI-<version>` folder.
3. If you did everything right so far, you should have a folder like this:

```
SMAPI-1.x/
Expand Down Expand Up @@ -101,10 +104,29 @@ directory containing `src`).
install.exe
readme.txt
```
4. Open a terminal in the `SMAPI-<version>` directory and run `chmod 755 Mono/StardewModdingAPI`.
5. Copy & paste the `SMAPI-<version>` directory as `SMAPI-<version>-for-developers`.
6. In the `SMAPI-<version>` directory, delete the following files:
4. Open a terminal in the `SMAPI-<version>` folder and run `chmod 755 Mono/StardewModdingAPI`.
5. Copy & paste the `SMAPI-<version>` folder as `SMAPI-<version>-for-developers`.
6. In the `SMAPI-<version>` folder, delete the following files:
* `Mono/StardewModdingAPI.config.json`
* `Windows/StardewModdingAPI.config.json`
* `Windows/StardewModdingAPI.xml`
7. Compress the two folders into `SMAPI-<version>.zip` and `SMAPI-<version>-for-developers.zip`.
7. Compress the two folders into `SMAPI-<version>.zip` and `SMAPI-<version>-for-developers.zip`.

## Advanced usage
### Configuration file
You can customise the SMAPI behaviour by editing the `StardewModdingAPI.config.json` file in your
game folder. If it's missing, it'll be generated automatically next time SMAPI runs. It contains
these fields:

field | purpose
----- | -------
`DeveloperMode` | Default `false` (except in _SMAPI for developers_ releases). Whether to enable features intended for mod developers. Currently this only makes `TRACE`-level messages appear in the console.
`CheckForUpdates` | Default `true`. Whether SMAPI should check for a newer version when you load the game. If a new version is available, a small message will appear in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.

### Command-line arguments
SMAPI recognises the following command-line arguments. These are intended for internal use and may
change without warning.

argument | purpose
-------- | -------
`--no-terminal` | SMAPI won't write anything to the console window. (Messages will still be written to the log file.)
25 changes: 25 additions & 0 deletions release-notes.md
@@ -1,5 +1,30 @@
# Release notes

## 1.6
See [log](https://github.com/Pathoschild/SMAPI/compare/1.5...1.6).

For players:
* Added console commands to open the game/data folders.
* Updated list of incompatible mods.
* Fixed `config.json` values being duplicated in some cases.
* Fixed some Linux users not being able to launch SMAPI from Steam.
* Fixed the installer not finding custom install paths on 32-bit Windows.
* Fixed error when loading a mod which was released with a `.cache` folder for a different platform.
* Fixed error when the console doesn't support colour.
* Fixed error when a mod reads a custom JSON file from a directory that doesn't exist.

For mod developers:
* Added three events: `SaveEvents.BeforeSave`, `SaveEvents.AfterSave`, and `SaveEvents.AfterLoad`.
* Deprecated three events:
* `TimeEvents.OnNewDay` is unreliable; use `TimeEvents.DayOfMonthChanged` or `SaveEvents` instead.
* `PlayerEvents.LoadedGame` is replaced by `SaveEvents.AfterLoad`.
* `PlayerEvents.FarmerChanged` serves no purpose.

For SMAPI developers:
* Added support for specifying a lower bound in mod incompatibility data.
* Added support for custom incompatible-mod error text.
* Fixed issue where `TrainerMod` used older logic to detect the game path.

## 1.5
See [log](https://github.com/Pathoschild/SMAPI/compare/1.4...1.5).

Expand Down
58 changes: 42 additions & 16 deletions src/StardewModdingAPI.Installer/InteractiveInstaller.cs
Expand Up @@ -77,6 +77,9 @@ private IEnumerable<string> DefaultInstallPaths
"StardewModdingAPI-settings.json" // 1.0-1.4
};

/// <summary>Whether the current console supports color formatting.</summary>
private static readonly bool ConsoleSupportsColor = InteractiveInstaller.GetConsoleSupportsColor();


/*********
** Public methods
Expand Down Expand Up @@ -253,18 +256,18 @@ public void Run(string[] args)
/****
** exit
****/
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine("Done!");
this.PrintColor("Done!", ConsoleColor.DarkGreen);
if (platform == Platform.Windows)
{
Console.WriteLine(action == ScriptAction.Install
? "Don't forget to launch StardewModdingAPI.exe instead of the normal game executable. See the readme.txt for details."
: "If you manually changed shortcuts or Steam to launch SMAPI, don't forget to change those back."
this.PrintColor(
action == ScriptAction.Install
? "Don't forget to launch StardewModdingAPI.exe instead of the normal game executable. See the readme.txt for details."
: "If you manually changed shortcuts or Steam to launch SMAPI, don't forget to change those back.",
ConsoleColor.DarkGreen
);
}
else if (action == ScriptAction.Install)
Console.WriteLine("You can launch the game the same way as before to play with mods.");
Console.ResetColor();
this.PrintColor("You can launch the game the same way as before to play with mods.", ConsoleColor.DarkGreen);
Console.ReadKey();
}

Expand All @@ -287,6 +290,20 @@ private Platform DetectPlatform()
}
}

/// <summary>Test whether the current console supports color formatting.</summary>
private static bool GetConsoleSupportsColor()
{
try
{
Console.ForegroundColor = Console.ForegroundColor;
return true;
}
catch (Exception)
{
return false; // Mono bug
}
}

#if SMAPI_FOR_WINDOWS
/// <summary>Get the value of a key in the Windows registry.</summary>
/// <param name="key">The full path of the registry key relative to HKLM.</param>
Expand All @@ -306,30 +323,39 @@ private string GetLocalMachineRegistryValue(string key, string name)
/// <param name="text">The text to print.</param>
private void PrintDebug(string text)
{
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine(text);
Console.ResetColor();
this.PrintColor(text, ConsoleColor.DarkGray);
}

/// <summary>Print a warning message.</summary>
/// <param name="text">The text to print.</param>
private void PrintWarning(string text)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(text);
Console.ResetColor();
this.PrintColor(text, ConsoleColor.DarkYellow);
}

/// <summary>Print an error and pause the console if needed.</summary>
/// <param name="error">The error text.</param>
private void ExitError(string error)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(error);
Console.ResetColor();
this.PrintColor(error, ConsoleColor.Red);
Console.ReadLine();
}

/// <summary>Print a message to the console.</summary>
/// <param name="text">The message text.</param>
/// <param name="color">The text foreground color.</param>
private void PrintColor(string text, ConsoleColor color)
{
if (InteractiveInstaller.ConsoleSupportsColor)
{
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ResetColor();
}
else
Console.WriteLine(text);
}

/// <summary>Interactively ask the user to choose a value.</summary>
/// <param name="message">The message to print.</param>
/// <param name="options">The allowed options (not case sensitive).</param>
Expand Down
2 changes: 2 additions & 0 deletions src/StardewModdingAPI.sln
Expand Up @@ -41,11 +41,13 @@ Global
{28480467-1A48-46A7-99F8-236D95225359}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Debug|x86.ActiveCfg = Debug|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Debug|x86.Build.0 = Debug|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Any CPU.Build.0 = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|x86.ActiveCfg = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|x86.Build.0 = Release|Any CPU
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
Expand Down
20 changes: 18 additions & 2 deletions src/StardewModdingAPI/Events/PlayerEvents.cs
Expand Up @@ -14,9 +14,11 @@ public static class PlayerEvents
** Events
*********/
/// <summary>Raised after the player loads a saved game.</summary>
[Obsolete("Use " + nameof(SaveEvents) + "." + nameof(SaveEvents.AfterLoad) + " instead")]
public static event EventHandler<EventArgsLoadedGameChanged> LoadedGame;

/// <summary>Raised after the game assigns a new player character. This happens just before <see cref="LoadedGame"/>; it's unclear how this would happen any other time.</summary>
[Obsolete("should no longer be used")]
public static event EventHandler<EventArgsFarmerChanged> FarmerChanged;

/// <summary>Raised after the player's inventory changes in any way (added or removed item, sorted, etc).</summary>
Expand All @@ -34,7 +36,14 @@ public static class PlayerEvents
/// <param name="loaded">Whether the save has been loaded. This is always true.</param>
internal static void InvokeLoadedGame(IMonitor monitor, EventArgsLoadedGameChanged loaded)
{
monitor.SafelyRaiseGenericEvent($"{nameof(PlayerEvents)}.{nameof(PlayerEvents.LoadedGame)}", PlayerEvents.LoadedGame?.GetInvocationList(), null, loaded);
if (PlayerEvents.LoadedGame == null)
return;

string name = $"{nameof(PlayerEvents)}.{nameof(PlayerEvents.LoadedGame)}";
Delegate[] handlers = PlayerEvents.LoadedGame.GetInvocationList();

Program.DeprecationManager.WarnForEvent(handlers, name, "1.6", DeprecationLevel.Notice);
monitor.SafelyRaiseGenericEvent(name, handlers, null, loaded);
}

/// <summary>Raise a <see cref="FarmerChanged"/> event.</summary>
Expand All @@ -43,7 +52,14 @@ internal static void InvokeLoadedGame(IMonitor monitor, EventArgsLoadedGameChang
/// <param name="newFarmer">The new player character.</param>
internal static void InvokeFarmerChanged(IMonitor monitor, Farmer priorFarmer, Farmer newFarmer)
{
monitor.SafelyRaiseGenericEvent($"{nameof(PlayerEvents)}.{nameof(PlayerEvents.FarmerChanged)}", PlayerEvents.FarmerChanged?.GetInvocationList(), null, new EventArgsFarmerChanged(priorFarmer, newFarmer));
if (PlayerEvents.FarmerChanged == null)
return;

string name = $"{nameof(PlayerEvents)}.{nameof(PlayerEvents.FarmerChanged)}";
Delegate[] handlers = PlayerEvents.FarmerChanged.GetInvocationList();

Program.DeprecationManager.WarnForEvent(handlers, name, "1.6", DeprecationLevel.Notice);
monitor.SafelyRaiseGenericEvent(name, handlers, null, new EventArgsFarmerChanged(priorFarmer, newFarmer));
}

/// <summary>Raise an <see cref="InventoryChanged"/> event.</summary>
Expand Down
46 changes: 46 additions & 0 deletions src/StardewModdingAPI/Events/SaveEvents.cs
@@ -0,0 +1,46 @@
using System;
using StardewModdingAPI.Framework;

namespace StardewModdingAPI.Events
{
/// <summary>Events raised before and after the player saves/loads the game.</summary>
public static class SaveEvents
{
/*********
** Events
*********/
/// <summary>Raised before the game begins writes data to the save file.</summary>
public static event EventHandler BeforeSave;

/// <summary>Raised after the game finishes writing data to the save file.</summary>
public static event EventHandler AfterSave;

/// <summary>Raised after the player loads a save slot.</summary>
public static event EventHandler AfterLoad;


/*********
** Internal methods
*********/
/// <summary>Raise a <see cref="BeforeSave"/> event.</summary>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
internal static void InvokeBeforeSave(IMonitor monitor)
{
monitor.SafelyRaisePlainEvent($"{nameof(SaveEvents)}.{nameof(SaveEvents.BeforeSave)}", SaveEvents.BeforeSave?.GetInvocationList(), null, EventArgs.Empty);
}

/// <summary>Raise a <see cref="AfterSave"/> event.</summary>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
internal static void InvokeAfterSave(IMonitor monitor)
{
monitor.SafelyRaisePlainEvent($"{nameof(SaveEvents)}.{nameof(SaveEvents.AfterSave)}", SaveEvents.AfterSave?.GetInvocationList(), null, EventArgs.Empty);
}

/// <summary>Raise a <see cref="AfterLoad"/> event.</summary>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
internal static void InvokeAfterLoad(IMonitor monitor)
{
monitor.SafelyRaisePlainEvent($"{nameof(SaveEvents)}.{nameof(SaveEvents.AfterLoad)}", SaveEvents.AfterLoad?.GetInvocationList(), null, EventArgs.Empty);
}
}
}
10 changes: 9 additions & 1 deletion src/StardewModdingAPI/Events/TimeEvents.cs
Expand Up @@ -22,6 +22,7 @@ public static class TimeEvents
public static event EventHandler<EventArgsStringChanged> SeasonOfYearChanged;

/// <summary>Raised when the player is transitioning to a new day and the game is performing its day update logic. This event is triggered twice: once after the game starts transitioning, and again after it finishes.</summary>
[Obsolete("Use " + nameof(TimeEvents) + "." + nameof(DayOfMonthChanged) + " or " + nameof(SaveEvents) + " instead")]
public static event EventHandler<EventArgsNewDay> OnNewDay;


Expand Down Expand Up @@ -71,7 +72,14 @@ internal static void InvokeSeasonOfYearChanged(IMonitor monitor, string priorSea
/// <param name="isTransitioning">Whether the game just started the transition (<c>true</c>) or finished it (<c>false</c>).</param>
internal static void InvokeOnNewDay(IMonitor monitor, int priorDay, int newDay, bool isTransitioning)
{
monitor.SafelyRaiseGenericEvent($"{nameof(TimeEvents)}.{nameof(TimeEvents.OnNewDay)}", TimeEvents.OnNewDay?.GetInvocationList(), null, new EventArgsNewDay(priorDay, newDay, isTransitioning));
if (TimeEvents.OnNewDay == null)
return;

string name = $"{nameof(TimeEvents)}.{nameof(TimeEvents.OnNewDay)}";
Delegate[] handlers = TimeEvents.OnNewDay.GetInvocationList();

Program.DeprecationManager.WarnForEvent(handlers, name, "1.6", DeprecationLevel.Notice);
monitor.SafelyRaiseGenericEvent(name, handlers, null, new EventArgsNewDay(priorDay, newDay, isTransitioning));
}
}
}

0 comments on commit b28c28f

Please sign in to comment.