Skip to content

Instantly share code, notes, and snippets.

@boneskull
Last active October 5, 2023 21:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save boneskull/52a5c3d6eb10d3f38239a6157c6413d3 to your computer and use it in GitHub Desktop.
Save boneskull/52a5c3d6eb10d3f38239a6157c6413d3 to your computer and use it in GitHub Desktop.
Building Stardew Valley Mods using SMAPI in VS Code

Building Stardew Valley Mods using SMAPI in VS Code

This guide aims to get you setup with Visual Studio Code (as opposed to Visual Studio or MonoDevelop) for developing Stardew Valley mods using SMAPI.

Motivation

Why would you want to use VS Code to mod Stardew? A couple reasons:

  1. You are already familiar with VS Code. You have your preferred extensions, keybindings, themes, etc., already setup.
  2. You prefer a less heavyweight IDE environment.
  3. You are on Mac or Linux where Visual Studio is not available or is otherwise a subpar experience. VS Code is cross-platform.

Demotivation

This is not without drawbacks:

  • To get this working, you'll need to take some additional, manual steps.
  • OmniSharp (the underlying C# toolkit in VS Code) works well, but the integration with the IDE is not as robust as what VS offers.
  • VS Code will not do everything we need "out of the box"--installing extensions is necessary. Not so much with Visual Studio.

Those already comfortable with Visual Studio or MonoDevelop should probably just stick to those IDEs.

Non-Goals of this Guide

  • Version control
  • Mod distribution
  • Anything else already covered in the modding wiki

Disclaimer

I have not tested any of this on Mac or Linux. Corrections accepted!

Requirements

The Reader

You (the reader) are expected to have basic familiarity with VS Code or can otherwise figure this stuff out:

  • How to install extensions
  • How to change settings
  • How to edit JSON configuration files

Software

The following requirements differ somewhat from the "Get Started Modding" page on the Stardew Valley Wiki'. It also assumes you're familiar with how to use Stardew Valley mods.

  1. Install Stardew Valley from wherever you bought it.
  2. Install SMAPI
  3. Install the NET 5.0 SDK (x64 version)
  4. Install Visual Studio Code

Additional Requirements (Windows)

Additional Requirements (Linux/Mac):

Required VS Code Extensions

Once you have VS Code installed and running, you'll need to install the following extensions. Extensions can be installed via VS Code directly or via the links below.

You may be asked to reload the window after installation of certain extensions (you can do this quickly by opening the Command Palette--F1 on Windows--and choosing Developer: Reload Window); please do so if necessary. You can also just quit VS Code and reopen it.

Note: When installing via web browser, clicking on the green "Install" button should attempt to open the URL in VS Code and complete the installation there. You will likely need to approve this behavior in your browser.

  1. C# - Without this, you won't have much luck.

Mostly Required VS Code Extensions

This guide assumes you have these installed. They are not strictly required to build or debug SMAPI mods in VS Code.

  1. Dotnet Core Essentials - Provides project templates and Nuget package management, among other things.
  2. C# Utilities - Easy creation of Classes, Interfaces, etc.

Recommended VS Code Extensions

These are nice-to-have.

  1. EditorConfig for VS Code - Support for .editorconfig files. In a C# project context, this is akin to a combination linter and formatter. _Strongly recommended.
  2. SLN Support - Syntax highlighting for .sln files
  3. vscode-solution-explorer - Provides something simliar to VS' "Solution Explorer" panel. This is helpful if you are accustomed to Visual Studio, maybe.

VS Code Settings

After extensions are installed, you will need to change the following via VS Code's Settings:

  1. Omnisharp: Use Modern Net: This value should be unchecked (false) in the UI. Apply this to the "User" preference (as opposed to "Workspace") for now; you can change it later if needed.

    This is necessary because Stardew Valley uses an older ("non-modern?" .NET framework).

  2. I think that's it, actually.

Creating a New Project

To create a new project, you first need somewhere to put it.

  1. Create an empty directory (e.g., Projects) somewhere in the filesystem.

  2. Open this Projects folder with VS Code.

    1. In Windows, this can be accomplished by right-clicking the directory and choosing "Open with Code".
    2. On Mac, you can drag the folder into Visual Studio Code either in the Dock or Applications directory.
    3. On Linux, you're on your own (you should be used to this).
  3. If you haven't installed the required extensions, please do so now.

  4. Open the Command Palette and choose DCE: New Project. You will be prompted for information:

    1. Select .Net Framework: Choose DotNet Standard.
    2. Select Language: Choose C#.
    3. Select the solution file (.sln): Enter TestMod or choose your own name.
    4. Enter project name: For now, use the same name as in step 3.
  5. You should now have a directory named TestMod (or whatever you chose). In that directory there will be TestMod.sln and a subdirectory named TestMod, containing TestMod.csproj, Class1.cs, and an obj directory.

  6. Important: Given that you're unlikely to have multiple solutions:

    1. Exit VS Code.
    2. Open the TestMod folder--not the Projects directory--in VS code.
  7. Open TestMod.csproj. Change:

    <PropertyGroup>
      <TargetFramework>net7.0</TargetFramework>
      <ImplicitUsings>enable</ImplicitUsings>
      <Nullable>enable</Nullable>
    </PropertyGroup>

    To:

    <PropertyGroup>
      <TargetFramework>net5.0</TargetFramework>    
      <Nullable>enable</Nullable>
    </PropertyGroup>
  8. Class1.cs is now invalid. Delete it.

  9. We need to add the Pathoschild.Stardew.ModBuildConfig package to our project.

    1. Open the Command Palette and choose DCE: Add NugetPackage (sic).
    2. DCE: Select csproj or sln for installing nuget package: Just accept the default.
    3. DCE: Enter the Nuget Package name: Type Pathoschild.Stardew.ModBuildConfig.
    4. This will open a terminal window showing that VS Code has executed dotnet add /path/to/TestMod.csproj package Pathoschild.Stardew.ModBuildConfig.
    5. Re-open TestMod.csproj. You should see a package reference here.
  10. Create a .vscode directory. This directory should be a sibling of TestMod.sln.

  11. In this directory, create two files: tasks.json and launch.json. If you are using a different name than TestMod, you will need to replace some paths as necessary.

  12. Create TestMod.cs. This differs in name only from the SMAPI Quick Start Guide.

  13. Finally, create manifest.json.

Building

If you've got everything in place, the mod should now build. Open the Command Palette and choose Tasks: Run Task, then choose build.

A terminal window will open, and your mod will be built and deployed into the mods folder.

Building Often

If you have used the provided tasks.json, the "default" build task is a task (called dev) which watches for changes and rebuilds (and redeploys) your project as you work. If this sounds appealing, you can open the Command Palette and run Tasks: Run Build Task.

You can set a different task as the "default" build task by (re)moving the "isDefault": true key/value from the groups prop of the dev task in tasks.json

Debugging

Assuming your mod builds, you can start a debugging session by opening the Run and Debug panel (an icon of a cute bug and a "play" triangle) and choosing the .Net Core Launch config, then clicking the green "play" button.

This will build and deploy your project, then start a SMAPI console in your Debug Console. You can set breakpoint in your code and do all that debugg-y stuff here.

For more information further configuration and abilities of the debugger, see the OmniSharp docs.

Example Files

TestMod.cs

using System;
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewModdingAPI.Utilities;
using StardewValley;

namespace TestMod
{
    /// <summary>The mod entry point.</summary>
    public class TestMod : Mod
    {
        /*********
        ** Public methods
        *********/
        /// <summary>The mod entry point, called after the mod is first loaded.</summary>
        /// <param name="helper">Provides simplified APIs for writing mods.</param>
        public override void Entry(IModHelper helper)
        {
            helper.Events.Input.ButtonPressed += this.OnButtonPressed;
        }

        /*********
        ** Private methods
        *********/
        /// <summary>Raised after the player presses a button on the keyboard, controller, or mouse.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event data.</param>
        private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
        {
            // ignore if player hasn't loaded a save yet
            if (!Context.IsWorldReady)
                return;

            // print button presses to the console window
            this.Monitor.Log($"{Game1.player.Name} pressed {e.Button}.", LogLevel.Debug);
        }
    }
}

tasks.json

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "command": "dotnet",
      "type": "process",
      "args": [
        "build",
        "${workspaceFolder}/TestMod/TestMod.csproj",
        "/property:GenerateFullPaths=true",
        "/consoleloggerparameters:NoSummary"
      ],
      "problemMatcher": "$msCompile",
      "group": {
        "kind": "build"        
      }
    },
    {
      "label": "dev",
      "command": "dotnet",
      "type": "process",
      "args": [
        "watch",        
        "build",
        "--project",
        "${workspaceFolder}/TestMod/TestMod.csproj",
        "--",
        "/property:GenerateFullPaths=true",
        "/consoleloggerparameters:NoSummary"
      ],
      "problemMatcher": "$msCompile",
      "group": {
        "kind": "build",
        "isDefault": true   
      }
    },
    {
      "label": "publish",
      "command": "dotnet",
      "type": "process",
      "args": [
        "publish",
        "${workspaceFolder}/TestMod/TestMod.csproj",
        "/property:GenerateFullPaths=true",
        "/consoleloggerparameters:NoSummary"
      ],
      "problemMatcher": "$msCompile"
    },
    {
      "label": "watch",
      "command": "dotnet",
      "type": "process",
      "args": [
        "watch",
        "run",
        "--project",
        "${workspaceFolder}/TestMod/TestMod.csproj"
      ],
      "problemMatcher": "$msCompile",      
      "dependsOn": "build",
      "isBackground": true
    }
  ]
}

launch.json

When using Visual Studio, the Pathoschild.Stardew.ModBuildConfig package will automatically set configure debug profile(s) for you. However, in our case, we must configure this manually.

You will need the path to SMAPI's StardewModdingAPI.exe. This should be in the same directory as Stardew Valley itself. If you aren't sure where it's installed, refer to this file which Pathoschild.Stardew.ModBuildConfig uses to attempt to find Stardew's install dir.

Once you've determined it, replace <PATH-TO> in the below file with the Stardew Valley installation path.

Note: If you open the Debug panel before creating .vscode/launch.json, you may see a button labeled Generate C# Assets for Build and Debug. Don't bother clicking this; it won't work.

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": ".NET Core Launch",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "<PATH-TO>/StardewModdingAPI.exe",
      "args": [],
      "cwd": "${workspaceFolder}",
      "console": "internalConsole",
      "stopAtEntry": false
    },
    {
      "name": ".NET Core Attach",
      "type": "coreclr",
      "request": "attach"
    }
  ]
}

manifest.json

{
  "Name": "Test Mod",
  "Author": "Me",
  "Version": "1.0.0",
  "Description": "Test",
  "UniqueID": "test.testmod",
  "EntryDll": "TestMod.dll",
  "MinimumApiVersion": "3.0.0",
  "UpdateKeys": []
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment