⚠️ The latest release is unstable. Use preview builds instead
Skip to content

Client Manipulation Techniques

This document catalogs server-to-client control mechanisms discovered through static analysis of the decompiled Stardew Valley 1.6.15 source code. These techniques enable a dedicated server mod to communicate with, control, and deliver content to connected game clients without requiring client-side mods.

Internal Reference

This is a developer reference for the dedicated server project. The mechanisms described here are part of the game's normal networking protocol. They are not exploits against players. All techniques operate within the host authority model that the game already uses.

Assumption Validation

The following table summarizes which assumptions about client limitations were confirmed or invalidated by the analysis.

#AssumptionStatusFinding
1Server can directly modify farmhand inventoryConfirmedFarmer.Items is a NetRef<Inventory> on the farmer root; server writes propagate via delta sync (msg type 0)
2Server can grant items via mailConfirmed%item and %action commands in mail data execute client-side when opened
3Server can force events on clientsConfirmedMessage type 4 warps the client and starts any event by ID
4modData syncs in multiplayerConfirmedModDataDictionary extends NetStringDictionary, fully synced and persisted
5Server validates client inventory changesInvalidatedNo server-side validation. Clients send farmer deltas and the server propagates them as-is
6NPC dialogue is synced over the networkInvalidatedDialogue is resolved client-side from data assets. NPC dialogue stacks are local
7Mail item grants are validated server-sideInvalidatedLetterViewerMenu.HandleItemCommand() runs entirely on the client
8Event rewards are validated server-sideInvalidatedEvents run locally. Event.DefaultCommands.AddItem() calls Game1.player.addItemByMenuIfNecessary()
9Debug commands are accessible in productionInvalidatedGated by Program.enableCheats or Game1.isRunningMacro, not available to normal clients
10FarmerTeam is client-writablePartiallyTeam state uses RequestPlayerAction pattern; farmhands must request changes through the host authority

Networking Architecture

Message Types

The multiplayer protocol uses numbered message types. The server dispatches incoming messages in Multiplayer.processIncomingMessage() (line 1565). Messages flagged in isClientBroadcastType() (line 216) are relayed to all clients.

TypeNameDirectionPurpose
0farmerDeltaBidirectionalDelta sync of NetFarmerRoot (inventory, mail, stats, modData)
1serverIntroductionServer → ClientFull serialized host farmer, team root, and world state
2playerIntroductionServer → ClientNew player joined, broadcasts farmer data
3activeLocationClient → ServerClient reports active location
4eventRequestServer → ClientForce client to warp and play an event
6locationDeltaServer → ClientDelta sync of GameLocation root (objects, terrain, NPCs)
7temporaryAnimatedSpritesServer → ClientBroadcast visual/audio sprites to a location
8warpCharacterServer → ClientWarp an NPC to a location
10chatMessageBidirectionalPlayer chat messages
12worldStateServer → ClientNetWorldState sync
13teamDeltaServer → ClientFarmerTeam delta sync
14newDaySyncBidirectionalDay transition synchronization barriers
15chatInfoMessageServer → ClientSystem chat notifications (translation key + args)
17farmerGainExperienceServer → ClientShared experience gains
18serverToClientsMessageServer → ClientString-switched commands (festivalEvent, endFest, trainApproach)
19playerDisconnectedServer → ClientPlayer left notification
20sharedAchievementServer → ClientAchievement unlock broadcast
21globalMessageServer → ClientHUD message broadcast (translation key + token args)
22partyWideMailServer → ClientDeliver mail to all players
23forceKickServer → ClientForcibly disconnect a client
24removeLocationFromLookupServer → ClientRemove a location from the client's lookup
25farmerKilledMonsterServer → ClientShared monster kill stat
26requestGrandpaReevaluationServer → ClientTrigger grandpa shrine re-evaluation
27nutDigServer → ClientGolden walnut dig notification
28passoutRequestBidirectionalPassout synchronization request
29passoutServer → ClientPassout execution
30startNewDaySyncServer → ClientSignals server ready for new day
31readySyncBidirectionalReady check barriers (sleep, festivals)
32chestHitSyncBidirectionalChest interaction sync
33dedicatedServerSyncBidirectionalDedicated server specific sync
127compressedN/ACompressed message wrapper (should be decompressed before dispatch)

Delta Sync Model

State synchronization uses Netcode delta serialization. Each tick (every farmerDeltaBroadcastPeriod = 3 ticks), dirty NetField trees are serialized as binary deltas and broadcast:

Multiplayer.broadcastFarmerDeltas()  →  writeObjectDeltaBytes(farmerRoot)
                                     →  sendMessage(peerId, type=0, deltaBytes)

Authority model: Each player owns their own NetFarmerRoot. The client sends deltas to the server, and the server propagates them to other clients. The server (host) owns all GameLocation roots, NetWorldState, and FarmerTeam.

Key implication: The server can write to any farmhand's NetFarmerRoot fields (inventory, mail, modData, stats) and the changes will propagate to that client via delta sync. There is no client-side rejection of server-originated deltas on the farmer root.

Server writes to farmer.Items[0] = new Item(...)
  → NetFarmerRoot marks dirty
  → Next broadcastFarmerDeltas() sends delta to client
  → Client applies delta, item appears in inventory

Server Introduction

When a farmhand connects, GameServer.sendServerIntroduction() sends a full serialized snapshot:

csharp
// Network/GameServer.cs:396
sendMessage(peer, new OutgoingMessage(1, Game1.serverHost.Value,
    Game1.multiplayer.writeObjectFullBytes(Game1.serverHost, peer),     // Host farmer
    Game1.multiplayer.writeObjectFullBytes(Game1.player.teamRoot, peer), // Team state
    Game1.multiplayer.writeObjectFullBytes(Game1.netWorldState, peer))); // World state

This means any state the server has set on the host farmer, team, or world is delivered to connecting clients immediately.

Leverage Mechanisms

Ranked by feasibility and reliability for dedicated server use.

Tier 1: High Feasibility

modData Data Channel

ModDataDictionary is present on almost every game object and provides an arbitrary string → string key-value store that is both net-synced and save-persisted.

Objects with modData:

ObjectFileLine
Farmer (via Character)Character.cs515
ItemItem.cs87
GameLocationGameLocation.cs631
BuildingBuilding.cs161
TerrainFeatureTerrainFeature.cs37
CropCrop.cs191
QuestQuest.cs102
ProjectileProjectile.cs196

How it works: ModDataDictionary extends NetStringDictionary<string, NetString>. It's added to the parent object's NetFields, so changes propagate via the parent's delta sync. The server can write:

csharp
farmer.modData["JunimoServer.Notification"] = "Welcome to the server!";
farmer.modData["JunimoServer.Config"] = jsonPayload;

These values sync to the client automatically and persist across saves. This makes modData the most reliable general-purpose data channel from server to client.

Limitations: Client-side code must poll or observe the modData to act on it. Without a client mod, modData values are inert data. The vanilla game only reads specific modData keys it knows about.

Direct Inventory Mutation

The server holds a reference to each farmhand's Farmer object in Game1.otherFarmers. Since Farmer.Items is a NetRef<Inventory> containing a NetObjectList<Item>, direct writes propagate via delta sync.

csharp
// Server-side: grant an item to a farmhand
Item item = ItemRegistry.Create("(O)388", amount: 99); // 99 Wood
farmer.Items[farmer.Items.IndexOf(null)] = item;        // Place in first empty slot

Caveats:

  • Farmer.OnItemReceived (line 4840) only fires for IsLocalPlayer, so HUD pickup notifications won't display on the client
  • The client will see the item appear in their inventory silently
  • Stack limits and slot validation must be handled by the server (there is no automatic enforcement)
  • Max inventory size is 36 slots (Farmer.maxInventorySpace)

Mail System

The mail system is a powerful mechanism because it supports embedded commands that execute client-side when the player reads the letter.

Delivery methods:

  1. Direct field write: Add mail IDs to farmer.mailbox (appears immediately) or farmer.mailForTomorrow
  2. Party-wide broadcast: Message type 22 via broadcastPartyWideMail() delivers to all players
csharp
// Multiplayer.cs:1475
protected virtual void receivePartyWideMail(IncomingMessage msg)
{
    string mail_key = msg.Reader.ReadString();
    PartyWideMessageQueue message_queue = (PartyWideMessageQueue)msg.Reader.ReadInt32();
    bool no_letter = msg.Reader.ReadBoolean();
    _performPartyWideMail(mail_key, message_queue, no_letter);
}

Mail content commands (parsed by LetterViewerMenu):

CommandSyntaxEffect
Item by ID%item id <qualifiedId> [count]%%Creates any item via ItemRegistry.Create()
Object%item object <id> <count>%%Random object from list
Money%item money <min> [max]%%Grants gold directly
Cooking recipe%item cookingrecipe <name>%%Teaches a cooking recipe
Crafting recipe%item craftingrecipe <name>%%Teaches a crafting recipe
Quest%item quest <questId> [immediate]%%Attaches a quest
Trigger action%action <action_string>%%Runs any TriggerActionManager action

The %action command is particularly powerful. It can invoke any registered trigger action including AddItem, AddMoney, AddBuff, AddQuest, AddFriendshipPoints, and more.

The %&NL&% suffix: Appending this to a mail key sets the mail flag without showing a letter. Useful for setting game state flags silently.

TIP

Mail requires the key to exist in Data/mail for the letter content to render. The server mod can add custom mail entries via SMAPI's content API.

Forced Events

Message type 4 forces a client to warp to a location and play an event.

csharp
// Multiplayer.cs:1607
case 4:
    string eventId = msg.Reader.ReadString();
    bool flag = msg.Reader.ReadBoolean();      // Use local player as actor?
    bool notify_when_done = msg.Reader.ReadBoolean();
    tileX = msg.Reader.ReadInt32();
    tileY = msg.Reader.ReadInt32();
    request = readLocationRequest(msg.Reader);  // Target location
    // ... creates cloned farmer actor, warps, starts event

Events run entirely client-side with full access to event commands including:

  • addItem: grants items to the player
  • awardFestivalPrize: grants festival rewards
  • action: runs arbitrary trigger actions
  • mail: adds mail flags
  • friendship: modifies friendship values
  • removeItem: removes items from inventory

Constraint: The event ID must exist in the location's event data (location.findEventById(eventId) must return non-null).

Tier 2: Medium Feasibility

NPC Dialogue Manipulation

NPC dialogue is resolved client-side from data assets, not synced over the network. However, dialogue supports powerful embedded commands.

Dialogue commands (parsed in Dialogue.cs):

CommandSyntaxEffect
$action$action <triggerAction>Runs a TriggerActionManager action when displayed
Item grant[qualifiedItemId] in textCreates and gives the item to the player
$v$v <eventId>Triggers playing a game event
$t$t <topicId> <days>Adds a conversation topic

The $action command (line 624 of Dialogue.cs) is the most flexible:

csharp
dialogues.Add(new DialogueLine("", delegate
{
    TriggerActionManager.TryRunAction(commandArgs, out var error2, out var exception);
}));

Server leverage: If the server mod patches Data/Characters or dialogue files via SMAPI's content API, NPCs will speak the modified dialogue to players, executing any embedded commands.

Location Object and Debris Spawning

The server controls location state via GameLocation root deltas (message type 6). Objects placed on the map sync to clients automatically.

csharp
// Place an object in a location
Object obj = new Object("388", 1); // Wood
location.objects.Add(new Vector2(10, 10), obj);
// → Syncs to all clients in the location via delta

The server can also broadcast TemporaryAnimatedSprite instances (message type 7) for visual/audio effects:

csharp
// Multiplayer.cs:1587
case 7:
    location = readLocation(msg.Reader);
    if (location != null)
    {
        readSprites(msg.Reader, location, delegate(TemporaryAnimatedSprite sprite)
        {
            location.temporarySprites.Add(sprite);
        });
    }

Sprites support text fields for display, startSound/endSound for audio cues, and full position/motion physics.

Global Messages

Message type 21 pushes HUD notification text to all clients.

csharp
// Multiplayer.cs:811
broadcastGlobalMessage(string translationKey, bool onlyShowIfEmpty,
    GameLocation location, params string[] substitutions)

The substitutions pass through TokenParser.ParseText() which resolves tokens like [LocalizedText ...], [ItemName qualifiedId], etc. The translationKey falls back to displaying the raw string if the key is not found in content.

Use case: Server notifications, welcome messages, announcements. Purely visual, no state changes.

FarmerTeam Shared State

FarmerTeam is synced via team delta (message type 13). It contains shared state fields including:

  • broadcastedMail: party-wide mail tracking
  • specialOrders: active special orders
  • completedSpecialOrders: completed order tracking
  • luauIngredients, grangeDisplay: festival state
  • sharedDailyLuck: luck value
  • toggleMineShrineOvernight, toggleSkullShrineOvernight: mine difficulty

The server (host) is the authority on team state. Farmhand modifications go through RequestPlayerAction() which routes the request to the host for approval.

Tier 3: Low Feasibility

TemporaryAnimatedSprite Text

Sprites broadcast via message type 7 can carry a text field (line 1102 of TemporaryAnimatedSprite.cs). This text renders at the sprite's position in the game world.

Limitations: Text is purely visual, no interactivity. The sprite must be in the player's current location to be visible. Ephemeral; does not persist across location changes or saves.

Sign Text Tokens

Object.signText (line 448 of Object.cs) passes through TokenParser.ParseText() on change (line 836) and then Utility.FilterDirtyWords(). Sign objects placed by the server can display tokenized text.

Limitations: Requires placing a sign object in the game world. Text is filtered for profanity. Read-only display only.

Exploit Surface

WARNING

This section documents potential abuse vectors in the game's networking code. Understanding these helps the dedicated server project avoid introducing vulnerabilities and properly validate inputs.

Debug Command Paths

Debug/cheat commands in ChatCommands.cs are gated:

csharp
// ChatCommands.cs:530
public static bool AllowCheats
{
    get
    {
        if (!Program.enableCheats)
            return Game1.isRunningMacro;
        return true;
    }
}

Program.enableCheats is false in production builds. The macro path (Game1.isRunningMacro) is only set during Game1.runMacro() calls. Not exploitable by clients in normal gameplay.

StaticDelegateBuilder Reflection

StaticDelegateBuilder (in StardewValley.Internal) resolves arbitrary static method delegates from strings in the format "FullTypeName, AssemblyName:MethodName":

csharp
// StaticDelegateBuilder.cs:71
Type type = Type.GetType(text);
MethodInfo method = type.GetMethod(text2,
    BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
createdDelegate = (TDelegate)Delegate.CreateDelegate(typeof(TDelegate), null, method);

This is used by data-driven systems (machine rules, item queries, trigger actions) to resolve delegates from data assets. If the server controls data assets (via SMAPI content patching), it can specify arbitrary static methods as callbacks in:

  • Machine output rules (Data/Machines)
  • Item query resolvers (Data/Objects)
  • Trigger action handlers (Data/TriggerActions)

Activator.CreateInstance Usage

Several systems instantiate types from string names in data assets:

SystemData SourcePattern
Building typesData/BuildingsBuildingTypeActivator.CreateInstance(Type.GetType(type))
Location typesData/LocationsTypeActivator.CreateInstance(Type.GetType(type))
Tool typesData/ToolsSame pattern
Special order objectivesData/SpecialOrdersnamespace + "." + type + "Objective"
Trinket effectsData/TrinketsSame pattern

These are all gated by the data asset content, not directly accessible via network messages. Only relevant if the server mod patches data assets.

Error Handling Behavior

Several message handlers silently swallow exceptions:

receiveChatInfoMessage (Multiplayer.cs:1215–1236):

csharp
catch (ContentLoadException) { }
catch (FormatException) { }
catch (OverflowException) { }
catch (KeyNotFoundException) { }

Dialogue parsing (Dialogue.cs:383): Falls back to "..." on any exception.

LoadString (LocalizedContentManager.cs:572): Returns the raw key path string if the key is not found.

LetterViewerMenu mail parsing (line 226): Falls back to "..." on parse errors.

This means malformed messages generally fail silently rather than crashing clients. This is good for stability but means errors may go unnoticed during development.

No Server-Side Validation of Client State

The most significant architectural finding: the server does not validate farmer deltas received from clients. A malicious client could theoretically:

  • Add arbitrary items to their inventory
  • Set arbitrary mail flags / quest completions
  • Modify their own stats, recipes, and achievements
  • Change their modData to any values

This is inherent to the peer-to-peer authority model. For the dedicated server project, this means server-side validation of client farmer deltas should be considered if preventing client cheating is a goal.

Priority Actions

Ordered by impact and implementation complexity.

1. Implement modData Communication Channel

Why first: modData is the most reliable, general-purpose channel. It syncs automatically, persists across saves, and requires no special message handling.

Implementation:

  • Define a key namespace (e.g., JunimoServer.*)
  • Write structured JSON to farmer.modData["JunimoServer.State"]
  • Poll or observe changes client-side (if a client mod is developed later)
  • Immediate use: store server config, player permissions, session metadata

Code path: FarmerCharacter.modDataNetStringDictionary → delta sync via msg type 0

2. Build Mail-Based Item Delivery

Why second: Mail provides a polished UX for item delivery with built-in notification (mailbox flag) and player agency (they choose when to open it).

Implementation:

  • Register custom mail entries via SMAPI content API
  • Add mail keys to farmer.mailbox for immediate delivery
  • Use %item id <qualifiedId> <count>%% for item attachments
  • Use %action AddMoney <amount>%% for currency grants

Code path: Server writes farmer.mailbox.Add("custom_mail_id") → delta sync → client sees mailbox flag → opens letter → LetterViewerMenu.HandleItemCommand() grants items

3. Direct Inventory Mutation for System Items

Why third: Useful for granting tools, quest items, or correcting inventory state without requiring player interaction.

Implementation:

  • Find first empty slot: farmer.Items.IndexOf(null)
  • Create item: ItemRegistry.Create(qualifiedId, amount, quality)
  • Assign: farmer.Items[slot] = item
  • Consider adding a HUD notification via global message (msg type 21) since OnItemReceived won't fire

Code path: Server writes farmer.Items[n]NetObjectList<Item> marks dirty → delta sync via msg type 0

4. Event-Based Tutorials and Cutscenes

Why fourth: Events provide the richest player experience but require event data authoring.

Implementation:

  • Define custom events in location data via SMAPI content API
  • Send message type 4 to trigger on specific clients
  • Use event commands for narrative, item grants, and state changes

Code path: Server sends msg type 4 with event ID, location, tile → client warps → location.startEvent(event) → event commands execute locally

Appendix A: Synced Farmer NetFields

All fields registered in Farmer.initNetFields() (lines 2087–2224) are delta-synced via message type 0. This is the complete list of server-writable state on a farmhand.

Inventory and Equipment

FieldTypeLineNotes
netItemsNetRef<Inventory>2106Full inventory (36 slots max)
cursorSlotItemNetRef<Item>2109Item held by mouse cursor
temporaryItemNetRef<Item>2108Temporary held item
_recoveredItemNetRef<Item>2193Marlon's item recovery
itemsLostLastDeathNetObjectList<Item>2194Lost items for recovery service
hatNetRef<Hat>N/AEquipment slot
bootsNetRef<Boots>N/AEquipment slot
leftRing, rightRingNetRef<Ring>N/AEquipment slots
shirtItem, pantsItemNetRef<Clothing>N/AEquipment slots
trinketItemsNetList<Trinket>2211Trinket equipment (1.6+)
toolBeingUpgradedNetRef<Tool>2171Tool at Clint's
personalShippingBinNetObjectList<Item>2181Player's shipping bin contents

Mail and Progress

FieldTypeLineNotes
mailReceivedNetStringHashSet2125All received mail flags (includes game state flags)
mailForTomorrowNetStringHashSet2126Mail queued for next day
mailboxNetStringList2127Currently in mailbox
eventsSeenNetStringHashSet2129Seen event IDs
triggerActionsRunNetStringHashSet2128Completed trigger actions
questLogNetObjectList<Quest>2149Active quests
dialogueQuestionsAnsweredNetStringHashSetN/ATracked dialogue choices
activeDialogueEventsNetStringIntDict2156Active conversation topics with remaining days
cookingRecipesNetStringIntDict2154Known cooking recipes + times cooked
craftingRecipesNetStringIntDict2155Known crafting recipes + times crafted

Stats and Collections

FieldTypeLineNotes
basicShippedNetIntDictionary2187Items shipped by ID
mineralsFoundNetIntDictionary2188Museum minerals found
recipesCookedNetIntDictionary2189Recipes cooked counts
fishCaughtNetIntIntDict2190Fish caught by ID
archaeologyFoundNetIntIntDictN/AArtifacts found
specialItemsNetIntList2159Special item flags
specialBigCraftablesNetIntList2160Special big craftable flags
friendshipDataNetStringDictionary<Friendship>2143NPC friendship data

State and Position

FieldTypeLineNotes
modDataModDataDictionary(Character:561)Arbitrary key-value data (inherited)
locationBeforeForcedEventNetString2208Return location after forced event
companionsNetList<NPC>2212Companion NPCs following player
acceptedDailyQuestNetBoolN/ADaily quest board flag
hasCompletedAllMonsterSlayerQuestsNetBoolN/AAdventurer's Guild completion

Appendix B: TriggerActionManager Actions

Complete list of built-in trigger actions available for %action (mail), $action (dialogue), and event action commands. All defined in TriggerActionManager.cs.

ActionArgsEffectLine
NullN/ANo-op21
If<gameStateQuery> <action> [elseAction]Conditional execution29
AddBuff<buffId>Apply a buff to player88
RemoveBuff<buffId>Remove a buff101
AddMail<mailId>Add to mailReceived112
RemoveMail<mailId>Remove from all mail lists123
AddQuest<questId>Add quest to log134
RemoveQuest<questId>Remove quest145
AddSpecialOrder<orderId>Start a special order156
RemoveSpecialOrder<orderId>Cancel a special order167
AddItem<itemId> [count] [quality]Grant item to player178
RemoveItem<itemId> [count]Remove item from inventory193
AddMoney<amount>Add/subtract gold (clamped to 0)204
AddFriendshipPoints<npcName> <points>Change NPC friendship219
AddConversationTopic<topicId> [days]Add dialogue event (default 4 days)236
RemoveConversationTopic<topicId>Remove dialogue event247
IncrementStat<statKey> [amount]Increment a game stat259
MarkActionApplied<actionId>Flag a trigger action as completed270
MarkCookingRecipeKnown<recipeName>Teach a cooking recipe281
MarkCraftingRecipeKnown<recipeName>Teach a crafting recipe292
MarkEventSeen<eventId>Flag event as seen303
MarkQuestionAnswered<questionId>Flag dialogue question as answered314
MarkSongHeard<songId>Flag song as heard (jukebox)325
RemoveTemporaryAnimatedSprites<locationName>Clear temp sprites337
SetNpcInvisible<npcName> [days]Hide NPC for N days345
SetNpcVisible<npcName>Restore NPC visibility363

TIP

The If action supports full Game State Queries, enabling conditional logic like If "PLAYER_COMBAT_LEVEL Current 5" AddItem (W)65 (grant Meowmere sword if combat level 5+).

Appendix C: Farmhand Connection Lifecycle

Understanding the full join/disconnect flow is important for knowing when server-side state manipulation takes effect.

Connection Flow

Client connects
  → GameServer.checkFarmhandRequest()        // Validates auth, cabin, availability
  → Game1.multiplayer.addPlayer(farmer)      // Adds to Game1.otherFarmers
  → broadcastPlayerIntroduction(farmer)      // Sends farmer to all other clients (msg type 2)
  → Send always-active locations             // Farm, farmhouse, etc.
  → If same-day reconnect: send last disconnect location
  → sendServerIntroduction(peerId)           // Full snapshot: host + team + world (msg type 1)
  → Send other connected farmers             // Each existing player's farmer data

Disconnection Flow

Client disconnects
  → Multiplayer.saveFarmhand(farmer)
    → NetWorldState.SaveFarmhand(farmhandRoot)
      → farmhandRoot.CloneInto(worldState)   // Full farmer state saved to world state
      → ResetFarmhandState(farmer)           // Clears transient state:
        - farmName synced from host
        - position set to bed
        - mount cleared
        - buffs cleared
        - swim state cleared
        - temporaryItem cleared

Save/Load Cycle

Save:
  farmhands stored in SaveGame.farmhands list
  → Full XML serialization of Farmer objects including inventory, mail, modData, quests

Load:
  SaveGame.Load() → farmhands loaded into netWorldState.farmhandData
  → loadDataToFarmer(farmer):
    - Items.OverwriteWith(Items)    // Copy to net-sync list
    - Pad inventory to maxItems
    - Position set to mostRecentBed
    - Null quests removed
  → ResetFarmhandState() for each farmhand

Key insight: There is no inventory validation during save or load. Whatever state was on the farmer at disconnect is exactly what gets saved and restored. Server-side modData writes persist across the full save/load cycle.

Appendix D: Chat System Details

The chat system provides two communication paths from server to clients.

Player Chat (Message Type 10)

csharp
// Multiplayer.cs:1649
case 10:
    long recipientID = msg.Reader.ReadInt64();
    LanguageCode language = msg.Reader.ReadEnum<LanguageCode>();
    string message = msg.Reader.ReadString();
    receiveChatMessage(msg.SourceFarmer, recipientID, language, message);

Chat messages support color via ChatBox.addMessage(string message, Color color). Internal types:

  • Type 0: chatMessage (white)
  • Type 1: errorMessage (red)
  • Type 2: userNotificationMessage (yellow)
  • Type 3: privateMessage (dark cyan)

System Chat Info (Message Type 15)

csharp
// Multiplayer.cs:1657
case 15:
    string messageKey = msg.Reader.ReadString();
    string[] args = new string[msg.Reader.ReadByte()];
    // ... read args
    receiveChatInfoMessage(msg.SourceFarmer, messageKey, args);

The messageKey is prefixed with "Strings\\UI:Chat_" and looked up in translations. Args pass through TokenParser.ParseText(). Invalid keys are silently dropped (all exceptions caught empty). The server can use known keys like "PlayerJoinedGame", "PlayerLeftGame", etc.

Appendix E: Mail Queue Internals

The party-wide mail system uses internal prefixes to track which queue a mail was delivered to, preventing duplicate delivery:

PrefixQueueConstant
%&MFT&%MailForTomorrowPartyWideMessageQueue.MailForTomorrow
%&SM&%SeenMailPartyWideMessageQueue.SeenMail
%&NL&%(suffix)No letter; sets flag without showing mail

These prefixes are appended to the mail key and stored in FarmerTeam.broadcastedMail to prevent re-sending. When using broadcastPartyWideMail():

csharp
// _performPartyWideMail flow:
1. Adds to player's queue (mailForTomorrow or mailReceived)
2. If no_letter: appends "%&NL&%" suffix to key
3. Prepends queue prefix ("%&MFT&%" or "%&SM&%")
4. Adds prefixed key to team.broadcastedMail (dedup set)

Code References

All file paths are relative to decompiled/sdv-1.6.15-24356/.

Core Networking

ReferenceFileLine(s)
Message dispatch (client)StardewValley/Multiplayer.cs1565–1714
Client broadcast typesStardewValley/Multiplayer.cs216–238
Farmer delta broadcastStardewValley/Multiplayer.cs287–300
Server introductionStardewValley/Network/GameServer.cs396–406
Farmhand request validationStardewValley/Network/GameServer.cs522
Global message broadcastStardewValley/Multiplayer.cs811–847
Global message receiveStardewValley/Multiplayer.cs1522–1537
Party-wide mail receiveStardewValley/Multiplayer.cs1475–1511
Chat info message receiveStardewValley/Multiplayer.cs1209–1236
Server-to-client string msgsStardewValley/Multiplayer.cs1238–1265
Force kickStardewValley/Multiplayer.cs1513–1520

Farmer and Inventory

ReferenceFileLine(s)
Inventory field (NetRef<Inventory>)StardewValley/Farmer.cs214–215
Items accessorStardewValley/Farmer.cs1647
Max inventory size (36)StardewValley/Farmer.cs117
Mail fields (mailbox, mailForTomorrow, mailReceived)StardewValley/Farmer.cs253–259
NetFields initializationStardewValley/Farmer.cs2087–2224
hasOrWillReceiveMail checkStardewValley/Farmer.cs3914
addQuestStardewValley/Farmer.cs7993
OnItemReceived (local player only)StardewValley/Farmer.cs4840
modData (from Character)StardewValley/Character.cs515
Farmhand save/resetStardewValley/Network/NetWorldState.cs748–776
Load farmhand dataStardewValley/SaveGame.cs860–869, 1325

Item System

ReferenceFileLine(s)
ItemRegistry.Create()StardewValley/ItemRegistry.cs349
Item modDataStardewValley/Item.cs87
Item NetFields initStardewValley/Item.cs356
Object synced fieldsStardewValley/Object.cs788–820
Object signText (tokenized)StardewValley/Object.cs448, 836

Mail System

ReferenceFileLine(s)
HandleItemCommandStardewValley/Menus/LetterViewerMenu.cs296–470
HandleActionCommandStardewValley/Menus/LetterViewerMenu.cs267–293
FarmerTeam.RequestSetMailStardewValley/FarmerTeam.cs921

Events and Triggers

ReferenceFileLine(s)
Forced event handlingStardewValley/Multiplayer.cs1607–1647
Event.DefaultCommands.AddItemStardewValley/Event.cs1523
Event.DefaultCommands.ActionStardewValley/Event.cs133
Trigger action listStardewValley/Triggers/TriggerActionManager.cs178+

Dialogue

ReferenceFileLine(s)
$action commandStardewValley/Dialogue.cs624–642
Item grant bracket syntaxStardewValley/Dialogue.cs1001–1047
Fallback on parse errorStardewValley/Dialogue.cs383–394

Reflection and Type Resolution

ReferenceFileLine(s)
StaticDelegateBuilderStardewValley/Internal/StaticDelegateBuilder.cs71
Location type instantiationStardewValley/Game1.cs7373

Token System

ReferenceFileLine(s)
TokenParser.ParseText()StardewValley/TokenizableStrings/TokenParser.csN/A
Debug command gateStardewValley/ChatCommands.cs530

Released under the MIT License.