Changes

Jump to navigation Jump to search
m
Text replacement - "tt>" to "samp>"
Line 31: Line 31:  
==Major changes==
 
==Major changes==
 
===⚠ Net fields===
 
===⚠ Net fields===
A 'net type' is any of several new classes which Stardew Valley 1.3 uses to sync data between players, named for the <code>Net</code> prefix in their name. A net type can represent a simple value like <tt>NetBool</tt>, or complex values like <tt>NetFieldDictionary</tt>. Many existing fields have been converted to net types (called 'net fields'), each wrapping the underlying value:
+
A 'net type' is any of several new classes which Stardew Valley 1.3 uses to sync data between players, named for the <code>Net</code> prefix in their name. A net type can represent a simple value like <samp>NetBool</samp>, or complex values like <samp>NetFieldDictionary</samp>. Many existing fields have been converted to net types (called 'net fields'), each wrapping the underlying value:
 
: <syntaxhighlight lang="C#">
 
: <syntaxhighlight lang="C#">
 
NetString str = new NetString("bar");
 
NetString str = new NetString("bar");
Line 38: Line 38:     
Impact on mods:
 
Impact on mods:
* The game will regularly collect all the net fields reachable from <tt>Game1.netWorldState</tt> and sync them with other players. That means that many mod changes will be synchronised automatically in multiplayer.
+
* The game will regularly collect all the net fields reachable from <samp>Game1.netWorldState</samp> and sync them with other players. That means that many mod changes will be synchronised automatically in multiplayer.
 
* Net fields can implicitly convert to their equivalent normal values (like <code>bool x = new NetBool(true)</code>), but their conversion rules can be counterintuitive and error-prone. For example, <code>item?.category == null && item?.category != null</code> can both be true at once. '''Always avoid implicit casts to minimise bugs.'''
 
* Net fields can implicitly convert to their equivalent normal values (like <code>bool x = new NetBool(true)</code>), but their conversion rules can be counterintuitive and error-prone. For example, <code>item?.category == null && item?.category != null</code> can both be true at once. '''Always avoid implicit casts to minimise bugs.'''
   Line 46: Line 46:  
===⚠ Location changes for farmhands===
 
===⚠ Location changes for farmhands===
 
In multiplayer, if the current player isn't the main player:
 
In multiplayer, if the current player isn't the main player:
* '''<tt>Game1.locations</tt> does not contain the actual list of locations'''. It contains a set of locations generated locally, which don't match the actual in-game locations. This also affects related functionality like <tt>Utility.getAllCharacters()</tt>, which searches the in-game locations. There's no fix for this yet. You can use SMAPI's <tt>helper.Multiplayer.GetActiveLocations()</tt> to get the list of locations currently being sync'd from the host, but there's currently no way to fetch all locations. That means SMAPI mods installed by a non-main player have no way to fetch all NPCs, locations, objects, etc.
+
* '''<samp>Game1.locations</samp> does not contain the actual list of locations'''. It contains a set of locations generated locally, which don't match the actual in-game locations. This also affects related functionality like <samp>Utility.getAllCharacters()</samp>, which searches the in-game locations. There's no fix for this yet. You can use SMAPI's <samp>helper.Multiplayer.GetActiveLocations()</samp> to get the list of locations currently being sync'd from the host, but there's currently no way to fetch all locations. That means SMAPI mods installed by a non-main player have no way to fetch all NPCs, locations, objects, etc.
* <tt>Game1.currentLocation</tt> is always an active location, but may be null when the player transition between locations. Make sure any references to that field can handle it being null.
+
* <samp>Game1.currentLocation</samp> is always an active location, but may be null when the player transition between locations. Make sure any references to that field can handle it being null.
    
===Game1.player.friendships is obsolete===
 
===Game1.player.friendships is obsolete===
The <tt>Game1.player.friendships</tt> field is always null in Stardew Valley 1.3. Use the new <tt>Game1.player.friendshipData</tt> field instead, which wraps the raw data with a more useful model and more data.
+
The <samp>Game1.player.friendships</samp> field is always null in Stardew Valley 1.3. Use the new <samp>Game1.player.friendshipData</samp> field instead, which wraps the raw data with a more useful model and more data.
    
To convert old code:
 
To convert old code:
Line 72: Line 72:     
===Texture constructor arguments===
 
===Texture constructor arguments===
Many constructors which previously accepted <tt>Texture2D texture</tt> arguments now take a <tt>string textureName</tt> argument instead. It's usually better to use SMAPI's content API to override textures instead. You can change the cached texture after the object is constructed (may need [[Modding:Modder Guide/APIs|reflection]]), but don't change the texture name to avoid multiplayer sync issues.
+
Many constructors which previously accepted <samp>Texture2D texture</samp> arguments now take a <samp>string textureName</samp> argument instead. It's usually better to use SMAPI's content API to override textures instead. You can change the cached texture after the object is constructed (may need [[Modding:Modder Guide/APIs|reflection]]), but don't change the texture name to avoid multiplayer sync issues.
    
===Reserved key bindings===
 
===Reserved key bindings===
Line 83: Line 83:     
===Overlay objects===
 
===Overlay objects===
Stardew Valley 1.3 adds an <tt>overlayObjects</tt> field to <tt>GameLocation</tt> instances. These have two special properties:
+
Stardew Valley 1.3 adds an <samp>overlayObjects</samp> field to <samp>GameLocation</samp> instances. These have two special properties:
 
* They're not synced to other players, so each player has their own overlay objects. (That's used for special quest items, so other players can't take your item.)
 
* They're not synced to other players, so each player has their own overlay objects. (That's used for special quest items, so other players can't take your item.)
 
* They're positioned on top of the normal object layer. (If there was already an object where the item is placed, the previous object will be hidden until you pick up the overlay object instead of being deleted.)
 
* They're positioned on top of the normal object layer. (If there was already an object where the item is placed, the previous object will be hidden until you pick up the overlay object instead of being deleted.)
Line 97: Line 97:  
|-
 
|-
 
| 2.3
 
| 2.3
| <tt>IReflectionHelper.GetPrivateField</tt><br /><tt>IReflectionHelper.GetPrivateMethod</tt><br /><tt>IReflectionHelper.GetPrivateProperty</tt>
+
| <samp>IReflectionHelper.GetPrivateField</samp><br /><samp>IReflectionHelper.GetPrivateMethod</samp><br /><samp>IReflectionHelper.GetPrivateProperty</samp>
| renamed to <tt>GetField</tt>, <tt>GetMethod</tt>, and <tt>GetProperty</tt> respectively; their return values have also been renamed (<tt>IPrivateField</tt> → <tt>IReflectedField</tt>, <tt>IPrivateProperty</tt> → <tt>IReflectedProperty</tt>, and <tt>IPrivateMethod</tt> → <tt>IReflectedMethod</tt>).
+
| renamed to <samp>GetField</samp>, <samp>GetMethod</samp>, and <samp>GetProperty</samp> respectively; their return values have also been renamed (<samp>IPrivateField</samp> → <samp>IReflectedField</samp>, <samp>IPrivateProperty</samp> → <samp>IReflectedProperty</samp>, and <samp>IPrivateMethod</samp> → <samp>IReflectedMethod</samp>).
 
|-
 
|-
 
| 2.3
 
| 2.3
| <tt>IReflectionHelper.GetPrivateValue</tt>
+
| <samp>IReflectionHelper.GetPrivateValue</samp>
| use <tt>GetPrivateField(...).GetValue()</tt> instead.
+
| use <samp>GetPrivateField(...).GetValue()</samp> instead.
 
|}
 
|}
   Line 115: Line 115:  
! migration notes
 
! migration notes
 
|-
 
|-
| <tt>LocationEvents.CurrentLocationChanged</tt>
+
| <samp>LocationEvents.CurrentLocationChanged</samp>
 
| &rarr;
 
| &rarr;
| <tt>PlayerEvents.Warped</tt>
+
| <samp>PlayerEvents.Warped</samp>
 
| &#32;
 
| &#32;
* Change <tt>EventArgsCurrentLocationChanged</tt> to <tt>EventArgsPlayerWarped</tt>.
+
* Change <samp>EventArgsCurrentLocationChanged</samp> to <samp>EventArgsPlayerWarped</samp>.
 
|-
 
|-
| <tt>LocationEvents.LocationsChanged</tt>
+
| <samp>LocationEvents.LocationsChanged</samp>
 
|  
 
|  
 
| ''(same name)''
 
| ''(same name)''
 
| &#32;
 
| &#32;
* Change <tt>EventArgsGameLocationsChanged</tt> to <tt>EventArgsLocationsChanged</tt>.
+
* Change <samp>EventArgsGameLocationsChanged</samp> to <samp>EventArgsLocationsChanged</samp>.
* The event is now raised when ''any'' location is added/removed (including building interiors), not just the main world locations in <tt>Game1.locations</tt>. If you need to handle only main world locations, you can check <code>if (Game1.locations.Contains(e.NewLocation))</code>.
+
* The event is now raised when ''any'' location is added/removed (including building interiors), not just the main world locations in <samp>Game1.locations</samp>. If you need to handle only main world locations, you can check <code>if (Game1.locations.Contains(e.NewLocation))</code>.
* The event data previously contained the current list of locations; it now contains the locations added or removed since the last tick. If you previously used <tt>e.NewLocations</tt>, you can replace it with <tt>Game1.locations</tt>.
+
* The event data previously contained the current list of locations; it now contains the locations added or removed since the last tick. If you previously used <samp>e.NewLocations</samp>, you can replace it with <samp>Game1.locations</samp>.
 
|-
 
|-
| <tt>LocationEvents.LocationObjectsChanged</tt>
+
| <samp>LocationEvents.LocationObjectsChanged</samp>
 
| →
 
| →
| <tt>LocationEvents.ObjectsChanged</tt>
+
| <samp>LocationEvents.ObjectsChanged</samp>
 
| &#32;
 
| &#32;
 
* The event is now raised when objects are added/removed to ''any'' location (including building interiors), not just the current player's location. If you need to handle only the current player's location, you can check <code>if (e.Location == Game1.player.currentLocation)</code>.
 
* The event is now raised when objects are added/removed to ''any'' location (including building interiors), not just the current player's location. If you need to handle only the current player's location, you can check <code>if (e.Location == Game1.player.currentLocation)</code>.
* The event data previously contained the current location's list of objects; it now contains the location, and the objects added/removed in it since the last tick. If you previously used <tt>e.NewObjects</tt>, you can use <tt>e.Location.netObjects.FieldDict</tt> instead.
+
* The event data previously contained the current location's list of objects; it now contains the location, and the objects added/removed in it since the last tick. If you previously used <samp>e.NewObjects</samp>, you can use <samp>e.Location.netObjects.FieldDict</samp> instead.
 
|}
 
|}
   Line 140: Line 140:  
Stardew Valley 1.3 includes several changes which benefit modders. These aren't disruptive, but worth noting for use. Some of the most relevant are...
 
Stardew Valley 1.3 includes several changes which benefit modders. These aren't disruptive, but worth noting for use. Some of the most relevant are...
 
* Many more methods and properties are now virtual.
 
* Many more methods and properties are now virtual.
* <tt>Game1.WorldDate</tt> is a new field which provides a more useful way to check the date. This combines the day, season, and year with useful logic like day-of-week and date comparisons. This incorporates many of the features from SMAPI's <tt>SDate</tt> class.
+
* <samp>Game1.WorldDate</samp> is a new field which provides a more useful way to check the date. This combines the day, season, and year with useful logic like day-of-week and date comparisons. This incorporates many of the features from SMAPI's <samp>SDate</samp> class.
 
* Many type checks now allow subclasses (like <code>item.GetType() == typeof(Axe)</code> &rarr; <code>item is Axe</code>).
 
* Many type checks now allow subclasses (like <code>item.GetType() == typeof(Axe)</code> &rarr; <code>item is Axe</code>).
* Any <tt>GameLocation</tt> can now set <tt>IsGreenhouse = true</tt>, and crops will grow there all year.
+
* Any <samp>GameLocation</samp> can now set <samp>IsGreenhouse = true</samp>, and crops will grow there all year.
* Any <tt>NPC</tt> can now set <tt>IsSocial</tt> to determine whether they appear in the social menu.
+
* Any <samp>NPC</samp> can now set <samp>IsSocial</samp> to determine whether they appear in the social menu.
 
* Bee houses now find nearby flowers in any location, not only when placed on the farm.
 
* Bee houses now find nearby flowers in any location, not only when placed on the farm.
 
* Custom map tilesheets no longer need hacks to avoid the game's seasonal logic. Tilesheets which don't start with a season name and underscore won't be seasonalised.
 
* Custom map tilesheets no longer need hacks to avoid the game's seasonal logic. Tilesheets which don't start with a season name and underscore won't be seasonalised.
Line 159: Line 159:  
Sample warning: "''This implicitly converts '{0}' from Net{1} to {2}, but Net{1} has unintuitive implicit conversion rules. Consider comparing against the actual value instead to avoid bugs. See https://smapi.io/package/avoid-implicit-net-field-cast for details.''"
 
Sample warning: "''This implicitly converts '{0}' from Net{1} to {2}, but Net{1} has unintuitive implicit conversion rules. Consider comparing against the actual value instead to avoid bugs. See https://smapi.io/package/avoid-implicit-net-field-cast for details.''"
   −
Your code is referencing a [[#Net fields|net field]], which can cause subtle bugs. The field you're referencing has an equivalent non-net property, like <tt>monster.Health</tt> (<tt>int</tt>) instead of <tt>monster.health</tt> (<tt>NetBool</tt>). Change your code to use the suggested property instead.
+
Your code is referencing a [[#Net fields|net field]], which can cause subtle bugs. The field you're referencing has an equivalent non-net property, like <samp>monster.Health</samp> (<samp>int</samp>) instead of <samp>monster.health</samp> (<samp>NetBool</samp>). Change your code to use the suggested property instead.
    
===FieldName is a Net* field...===
 
===FieldName is a Net* field...===
Line 166: Line 166:  
Your code is referencing a [[#Net fields|net field]], which can cause subtle bugs. You should access the underlying value instead:
 
Your code is referencing a [[#Net fields|net field]], which can cause subtle bugs. You should access the underlying value instead:
 
<ul>
 
<ul>
<li>For a reference type (i.e. one that can contain <tt>null</tt>), you can use the <tt>.Value</tt> property (or <tt>.FieldDict</tt> for a <tt>NetDictionary</tt>):
+
<li>For a reference type (i.e. one that can contain <samp>null</samp>), you can use the <samp>.Value</samp> property (or <samp>.FieldDict</samp> for a <samp>NetDictionary</samp>):
 
<syntaxhighlight lang="c#">
 
<syntaxhighlight lang="c#">
 
if (building.indoors.Value == null)
 
if (building.indoors.Value == null)
Line 177: Line 177:  
   // ...
 
   // ...
 
</syntaxhighlight></li>
 
</syntaxhighlight></li>
<li>For a value type (i.e. one that can't contain <tt>null</tt>), check if the parent is null (if needed) and compare with <tt>.Value</tt>:
+
<li>For a value type (i.e. one that can't contain <samp>null</samp>), check if the parent is null (if needed) and compare with <samp>.Value</samp>:
 
<syntaxhighlight lang="c#">
 
<syntaxhighlight lang="c#">
 
if (item != null && item.category.Value == 0)
 
if (item != null && item.category.Value == 0)
105,667

edits

Navigation menu