Changes

Jump to navigation Jump to search
remove {{upcoming|1.6}}
Line 3: Line 3:  
This page explains some of the Stardew Valley fundamentals that are useful for modders. See also [[Modding:Common tasks]].
 
This page explains some of the Stardew Valley fundamentals that are useful for modders. See also [[Modding:Common tasks]].
   −
==Concepts==
+
==General concepts==
 +
===Time format===
 +
The in-game time of day is tracked using a version of [[wikipedia:24-hour clock|24-hour format]] informally called "26-hour time", measured in 10-minute intervals. This is the format used by <samp>Game1.timeOfDay</samp> in a [[Modding:Modder Guide/Get Started|C# mod]] or <samp><nowiki>{{Time}}</nowiki></samp> in a [[Modding:Content Patcher|Content Patcher pack]].
 +
 
 +
Sample times:
 +
{| class="wikitable"
 +
|-
 +
! time value
 +
! display text
 +
|-
 +
| 600
 +
| 6:00 am
 +
|-
 +
| 1250
 +
| 12:50 am
 +
|-
 +
| 1300
 +
| 1:00 pm
 +
|-
 +
| 2600
 +
| 2:00 am (before sleeping)
 +
|}
 +
 
 +
The internal time will continue incrementing forever until you sleep (e.g. 6am the next day would be 3000 in that case).
 +
 
 
===Tiles===
 
===Tiles===
 
The world is laid out as a grid of ''tiles''. Each tile has an (x, y) coordinate which represents its position on the map, where (0, 0) is the top-left tile. The ''x'' value increases towards the right, and ''y'' increases downwards. For example:
 
The world is laid out as a grid of ''tiles''. Each tile has an (x, y) coordinate which represents its position on the map, where (0, 0) is the top-left tile. The ''x'' value increases towards the right, and ''y'' increases downwards. For example:
    
[[File:Modding - creating an XNB mod - tile coordinates.png]]
 
[[File:Modding - creating an XNB mod - tile coordinates.png]]
 +
 +
You can use the {{Nexus mod|679|Debug Mode}} mod to see tile coordinates in-game.
    
===Positions===
 
===Positions===
Line 31: Line 57:  
|}
 
|}
   −
Here's how to convert between them:
+
Here's how to convert between them (there are also helpful methods in Utility for some of these):
    
{| class="wikitable"
 
{| class="wikitable"
Line 55: Line 81:  
| tile || → || screen
 
| tile || → || screen
 
| <code>(x * Game1.tileSize) - Game1.viewport.X, (y * Game1.tileSize) - Game1.viewport.Y</code>
 
| <code>(x * Game1.tileSize) - Game1.viewport.X, (y * Game1.tileSize) - Game1.viewport.Y</code>
|}
  −
  −
===Net fields===
  −
A 'net type' is any of several classes which Stardew Valley uses to sync data between players, and a 'net field' is any field or property of those types. They're named for the <code>Net</code> prefix in their type names. Net types can represent simple values like <samp>NetBool</samp>, or complex values like <samp>NetFieldDictionary</samp>. 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.
  −
  −
Although net fields can be implicitly converted to an equivalent value type (like <code>bool x = new NetBool(true)</code>), their conversion rules are counterintuitive and error-prone (''e.g.,'' <code>item?.category == null && item?.category != null</code> can both be true at once). To avoid bugs, never implicitly cast net fields; access the underlying value directly instead. The build config NuGet package should detect most implicit conversions and show an appropriate build warning.
  −
  −
Here's how to access the data in some common net types:
  −
{| class="wikitable"
  −
|-
  −
! net type
  −
! description
  −
|-
  −
| <samp>NetBool</samp><br /><samp>NetColor</samp><br /><samp>NetFloat</samp><br /><samp>NetInt</samp><br /><samp>NetPoint</samp><br /><samp>NetString</samp>
  −
| A simple synchronised value. Access the value using <samp>field.Value</samp>.
  −
|-
  −
| <samp>NetCollection&lt;T&gt;</samp><br /><samp>NetList&lt;T&gt;</samp><br /><samp>NetObjectList&lt;T&gt;</samp>
  −
| A list of <samp>T</samp> values. This implements the standard interfaces like [https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerable-1 <samp>IEnumerable&lt;T&gt;</samp>] and [https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1 <samp>IList&lt;T&gt;</samp>], so you can iterate it directly like <code>foreach (T value in field)</code>.
  −
|-
  −
| <samp>NetLongDictionary&lt;TValue, TNetValue&gt;</samp><br /><samp>NetPointDictionary&lt;TValue, TNetValue&gt;</samp><br /><samp>NetVector2Dictionary&lt;TValue, TNetValue&gt;</samp>
  −
| Maps <samp>Long</samp>, <samp>Point</samp>, or <samp>Vector2</samp> keys to instances of <samp>TValue</samp> (the underlying value type) and <samp>TNetValue</samp> (the synchronised net type). You can iterate key/value pairs like <code>foreach (KeyValuePair<Long, TValue> pair in field.Pairs)</code> (replacing <samp>Long</samp> with <samp>Point</samp> or <samp>Vector2</samp> if needed).
   
|}
 
|}
   Line 144: Line 149:  
: You can test whether your mod accounts for this correctly by setting the zoom to maximum and the UI scale to minimum (''i.e.,'' have them at opposite values) or vice versa; in particular check any logic which handles pixel positions, like menus clicking.
 
: You can test whether your mod accounts for this correctly by setting the zoom to maximum and the UI scale to minimum (''i.e.,'' have them at opposite values) or vice versa; in particular check any logic which handles pixel positions, like menus clicking.
   −
==Assets==
+
==Multiplayer concepts for C# mods==
===String keys===
+
===Net fields===
A ''string key'' uniquely identifies where to find translatable text, in the form <samp>{{t|asset name}}:{{t|key}}</samp>. For example, <code>Game1.content.GetString("Strings\\StringsFromCSFiles:spring")</code> will look for a <samp>spring</samp> key in the <samp>Strings\StringsFromCSFiles</samp> file in the content folder (which contains "spring"). This is commonly used in the game code (via <code>Game1.content.GetString</code>) and in data assets which need translatable text.
+
A 'net type' is any of several classes which Stardew Valley uses to sync data between players, and a 'net field' is any field or property of those types. They're named for the <code>Net</code> prefix in their type names. Net types can represent simple values like <samp>NetBool</samp>, or complex values like <samp>NetFieldDictionary</samp>. 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.
 +
 
 +
Although net fields can be implicitly converted to an equivalent value type (like <code>bool x = new NetBool(true)</code>), their conversion rules are counterintuitive and error-prone (''e.g.,'' <code>item?.category == null && item?.category != null</code> can both be true at once). To avoid bugs, never implicitly cast net fields; access the underlying value directly instead. The build config NuGet package should detect most implicit conversions and show an appropriate build warning.
 +
 
 +
Here's how to access the data in some common net types:
 +
{| class="wikitable"
 +
|-
 +
! net type
 +
! description
 +
|-
 +
| <samp>NetBool</samp><br /><samp>NetColor</samp><br /><samp>NetFloat</samp><br /><samp>NetInt</samp><br /><samp>NetPoint</samp><br /><samp>NetString</samp>
 +
| A simple synchronised value. Access the value using <samp>field.Value</samp>.
 +
|-
 +
| <samp>NetCollection&lt;T&gt;</samp><br /><samp>NetList&lt;T&gt;</samp><br /><samp>NetObjectList&lt;T&gt;</samp>
 +
| A list of <samp>T</samp> values. This implements the standard interfaces like [https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerable-1 <samp>IEnumerable&lt;T&gt;</samp>] and [https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1 <samp>IList&lt;T&gt;</samp>], so you can iterate it directly like <code>foreach (T value in field)</code>.
 +
|-
 +
| <samp>NetLongDictionary&lt;TValue, TNetValue&gt;</samp><br /><samp>NetPointDictionary&lt;TValue, TNetValue&gt;</samp><br /><samp>NetVector2Dictionary&lt;TValue, TNetValue&gt;</samp>
 +
| Maps <samp>Long</samp>, <samp>Point</samp>, or <samp>Vector2</samp> keys to instances of <samp>TValue</samp> (the underlying value type) and <samp>TNetValue</samp> (the synchronised net type). You can iterate key/value pairs like <code>foreach (KeyValuePair<Long, TValue> pair in field.Pairs)</code> (replacing <samp>Long</samp> with <samp>Point</samp> or <samp>Vector2</samp> if needed).
 +
|}
 +
 
 +
===Farmhand shadow world===
 +
In [[multiplayer]], secondary players (''farmhands'') don't see most of the in-game locations. Instead their game creates a single-player copy of the world before they join, and then only fetches the [[farm|farm area]] and their current location (called ''active locations'') from the host player. The unsynchronized locations often don't match what players within those locations see.
 +
 
 +
This has some significant implications for C# mods:
 +
* The <samp>Game1.locations</samp> list shows both active and shadow locations. While mods can access the shadow locations, these don't reflect the real data on the server and any changes to them won't be synced to the host.
 +
* There may be duplicate copies of NPCs, horses, etc in the shadow world. Only those in active locations are 'real'.
 +
* Game methods (like <samp>Game1.getCharacterByName</samp>) may not correctly distinguish between the 'real' and 'shadow' copies.
 +
* When a farmhand warps to a location, the game fetches the real location from the host player before the warp completes. For a short while, the farmhand may have a null <samp>currentLocation</samp> field while they're between locations.
 +
 
 +
You can check whether a location is active using its <samp>IsActiveLocation</samp> method:
 +
<syntaxhighlight lang="c#">
 +
foreach (GameLocation location in Game1.locations)
 +
{
 +
    if (!location.IsActiveLocation())
 +
        continue; // shadow location
 +
 
 +
    ...
 +
}
 +
</syntaxhighlight>
    
==Main classes==
 
==Main classes==
Line 278: Line 321:  
<li>There are a number of subclasses for specific location (like <samp>AdventureGuild</samp>) which have fields useful for specific cases.</li>
 
<li>There are a number of subclasses for specific location (like <samp>AdventureGuild</samp>) which have fields useful for specific cases.</li>
 
</ul>
 
</ul>
 +
 +
[[es:Modding:Guía del Modder/Fundamentos del juego]]
 
[[ru:Модификации:Моддер гайд/Основы игры]]
 
[[ru:Модификации:Моддер гайд/Основы игры]]
 
[[zh:模组:制作指南/游戏基本架构]]
 
[[zh:模组:制作指南/游戏基本架构]]
translators
8,444

edits

Navigation menu