├── .gitignore ├── docs ├── using.png ├── omnisharp.png ├── overview.png └── rightclick.png ├── resources ├── assets │ └── howtoexamplemod │ │ ├── lang │ │ └── en.json │ │ ├── textures │ │ └── block │ │ │ ├── instatnt-side.png │ │ │ └── instatnt-topbottom.png │ │ └── blocktypes │ │ └── instatnt.json ├── modicon.png └── modinfo.json ├── .vscode ├── tasks.json └── launch.json ├── src ├── InstaTNTBehavior.cs └── HowtoExampleMod.cs ├── UNLICENSE ├── HowtoExample.csproj └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /obj/ 3 | -------------------------------------------------------------------------------- /docs/using.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copygirl/howto-example-mod/HEAD/docs/using.png -------------------------------------------------------------------------------- /resources/assets/howtoexamplemod/lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "block-instatnt": "Insta-TNT" 3 | } 4 | -------------------------------------------------------------------------------- /docs/omnisharp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copygirl/howto-example-mod/HEAD/docs/omnisharp.png -------------------------------------------------------------------------------- /docs/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copygirl/howto-example-mod/HEAD/docs/overview.png -------------------------------------------------------------------------------- /docs/rightclick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copygirl/howto-example-mod/HEAD/docs/rightclick.png -------------------------------------------------------------------------------- /resources/modicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copygirl/howto-example-mod/HEAD/resources/modicon.png -------------------------------------------------------------------------------- /resources/assets/howtoexamplemod/textures/block/instatnt-side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copygirl/howto-example-mod/HEAD/resources/assets/howtoexamplemod/textures/block/instatnt-side.png -------------------------------------------------------------------------------- /resources/assets/howtoexamplemod/textures/block/instatnt-topbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copygirl/howto-example-mod/HEAD/resources/assets/howtoexamplemod/textures/block/instatnt-topbottom.png -------------------------------------------------------------------------------- /resources/modinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "code", 3 | "name": "HowtoExample", 4 | "modid": "howtoexample", 5 | "version": "1.3.0", 6 | 7 | "description" : "An example mod using VS Code and .NET", 8 | "website": "https://github.com/copygirl/howto-example-mod", 9 | "authors": [ "copygirl" ], 10 | 11 | "dependencies": { 12 | "game": "1.15.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /resources/assets/howtoexamplemod/blocktypes/instatnt.json: -------------------------------------------------------------------------------- 1 | { 2 | code: "instatnt", 3 | creativeinventory: { "general": ["*"] }, 4 | shape: { base: "game:block/basic/cube" }, 5 | blockmaterial: "Stone", 6 | drawtype: "Cube", 7 | textures: { 8 | verticals: { base: "block/instatnt-topbottom" }, 9 | horizontals: { base: "block/instatnt-side" } 10 | }, 11 | resistance: 1.0, 12 | sounds: { 13 | "place": "game:block/anvil", 14 | "walk": "game:walk/stone" 15 | }, 16 | behaviors: [ 17 | { name: "InstaTNT" } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [{ 4 | "label": "Build (Debug)", 5 | "group": { "kind": "build", "isDefault": true }, 6 | "presentation": { "reveal": "silent" }, 7 | "problemMatcher": "$msCompile", 8 | 9 | "type": "process", 10 | "command": "dotnet", 11 | "args": [ "build", "-c", "Debug" ] 12 | },{ 13 | "label": "Build (Release)", 14 | "group": "build", 15 | "presentation": { "reveal": "silent" }, 16 | "problemMatcher": "$msCompile", 17 | 18 | "type": "process", 19 | "command": "dotnet", 20 | "args": [ "build", "-c", "Release" ] 21 | }] 22 | } 23 | -------------------------------------------------------------------------------- /src/InstaTNTBehavior.cs: -------------------------------------------------------------------------------- 1 | using Vintagestory.API.Common; 2 | using Vintagestory.API.Server; 3 | 4 | namespace HowtoExample 5 | { 6 | public class InstaTNTBehavior : BlockBehavior 7 | { 8 | public static string NAME { get; } = "InstaTNT"; 9 | 10 | public InstaTNTBehavior(Block block) 11 | : base(block) { } 12 | 13 | public override bool OnBlockInteractStart( 14 | IWorldAccessor world, IPlayer byPlayer, 15 | BlockSelection blockSel, ref EnumHandling handling) 16 | { 17 | (world as IServerWorldAccessor)?.CreateExplosion( 18 | blockSel.Position, EnumBlastType.RockBlast, 5.0, 8.0); 19 | handling = EnumHandling.PreventDefault; 20 | return true; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/HowtoExampleMod.cs: -------------------------------------------------------------------------------- 1 | using Vintagestory.API.Client; 2 | using Vintagestory.API.Common; 3 | using Vintagestory.API.Server; 4 | 5 | [assembly: ModInfo( "HowtoExample", 6 | Description = "An example mod using VS Code and .NET", 7 | Website = "https://github.com/copygirl/howto-example-mod", 8 | Authors = new []{ "copygirl" } )] 9 | 10 | namespace HowtoExample 11 | { 12 | public class HowtoExampleMod : ModSystem 13 | { 14 | public override void Start(ICoreAPI api) 15 | { 16 | api.RegisterBlockBehaviorClass(InstaTNTBehavior.NAME, typeof(InstaTNTBehavior)); 17 | } 18 | 19 | public override void StartClientSide(ICoreClientAPI api) 20 | { 21 | 22 | } 23 | 24 | public override void StartServerSide(ICoreServerAPI api) 25 | { 26 | 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [{ 4 | "name": "Launch Client (Mono)", 5 | "type": "mono", 6 | "request": "launch", 7 | "preLaunchTask": "Build (Debug)", 8 | "program": "${env:VINTAGE_STORY}/Vintagestory.exe", 9 | "args": [ 10 | "--playStyle" , "preset-surviveandbuild", 11 | "--openWorld" , "modding test world", 12 | "--addModPath", "${workspaceFolder}/bin/Debug/net461", 13 | "--addOrigin" , "${workspaceFolder}/resources/assets", 14 | ], 15 | "console": "internalConsole", 16 | "internalConsoleOptions": "openOnSessionStart", 17 | },{ 18 | "name": "Launch Client (.NET)", 19 | "type": "clr", 20 | "request": "launch", 21 | "preLaunchTask": "Build (Debug)", 22 | "program": "${env:VINTAGE_STORY}/Vintagestory.exe", 23 | "args": [ 24 | "--playStyle" , "preset-surviveandbuild", 25 | "--openWorld" , "modding test world", 26 | "--addModPath", "${workspaceFolder}/bin/Debug/net461", 27 | "--addOrigin" , "${workspaceFolder}/resources/assets", 28 | ], 29 | "console": "internalConsole", 30 | "internalConsoleOptions": "openOnSessionStart", 31 | }] 32 | } 33 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /HowtoExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net461 4 | 5 | 6 | 7 | 8 | $(VINTAGE_STORY)/VintagestoryAPI.dll 9 | false 10 | 11 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | %(RecursiveDir)%(Filename)%(Extension) 25 | PreserveNewest 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to set up Vintage Story modding with VS Code and .NET tools 2 | 3 | This repository will attempt to explain and demonstrate how to set up a modding enviroment for the creation of mods for the block-based sandbox game [Vintage Story][VS] using the [.NET SDK][dotnet] and [VS Code][vscode]. 4 | 5 | ![Overview](docs/overview.png) 6 | 7 | This how-to was last updated for Vintage Story version 1.15 but will hopefully continue working in the future. 8 | 9 | ## Why .NET 5? 10 | 11 | Vintage Story is built against .NET Framework 4.6.1 and runs using Mono on platforms other than Windows. Why are we using .NET 5? 12 | 13 | - It's **cross-platform**. With .NET 5, the successor of .NET Core, we don't need to change our tooling or commands to work on different platforms. I've moved from Windows to Linux while working on Vintage Story mods and only needed to make minor changes to also make it work on another platform. 14 | - It's **convenient**. As the .NET SDK comes with the `dotnet` command to manipulate, create and build project and solution files, we don't need to rely on IDEs. The newer `.csproj` file format doesn't require referencing every single `.cs` file, either. 15 | 16 | ## Why Visual Studio Code? 17 | 18 | Despite what its name might imply, this has nothing to do with the arguably overblown IDE that is Visual Studio. It's a relatively light-weight, cross-platform code editor that comes with syntax highlighting, code completion, source control integration, debugging tools, ... 19 | 20 | Essentially you get most of the IDE features you're used without any of the bloat, in a modern look. 21 | 22 | ## Prerequisites 23 | 24 | - Get the [game][VS]. 25 | - Install the [.NET SDK][dotnet-dl]. 26 | - Install [Visual Studio Code][vscode-dl]. 27 | - Install the [C# extension][cs-ext] from the "Extensions" tab in VS Code. 28 | - If you run on Mono, install the [Mono Debug][mono-ext] extension. 29 | 30 | If you're on Arch Linux like me, you can get the game using the [AUR][AUR] package [`vintagestory`][VS-AUR] (created by me), and the .NET SDK and VS Code by installing the official packages `dotnet-sdk` and `code`. 31 | 32 | ### Environment Variables 33 | 34 | I also recommend setting up two environmental variables, however only the former is required for this setup to work. 35 | 36 | - `VINTAGE_STORY`, which points to the where the game is installed. 37 | - `VINTAGE_STORY_DATA`, pointing to the user data directory. 38 | 39 | Since I'm on Linux, I just add the following lines to my `~/.xprofile` (which takes effect after a restart). Of course, you have to adjust this depending on where you have installed the game. 40 | 41 | ```sh 42 | export VINTAGE_STORY=/usr/share/vintagestory 43 | export VINTAGE_STORY_DATA=$HOME/.config/VintagestoryData 44 | ``` 45 | 46 | If you're on another operating system, I suggest you search online how to set environment variables, as it's not straight forward. On Windows, the paths can be set to be `%AppData%\Vintagestory` and `%AppData%\VintagestoryData`. 47 | 48 | This will just make things more consistent across different platforms and setups, and it's what I will use in this repository and how-to, but it's not required. However, if you decide to leave this out, do make sure to update any occurances in the `.csproj` and `launch.json` with absolute paths. 49 | 50 | ## Project Setup 51 | 52 | Now that we're done with that, it's time to set up the project structure. Create and open a new folder in VS Code! 53 | 54 | OmniSharp gets a bit confused with Mono so if you see the following error, open your user or workspace settings (press `F1` and search for "user / workspace settings") and set "Omnisharp: Use Global Mono" to "always". 55 | 56 | > The reference assemblies for .NETFramework,Version=v4.6.1 were not found. To resolve this, install the Developer Pack (SDK/Targeting Pack) for this framework version or retarget your application. 57 | 58 | ### Directory Structure 59 | 60 | - `./`: The root folder is mainly where our `.csproj` file will reside, but other files are going to end up here, such as `.gitignore` if you're using Git, the readme, license, ... 61 | - `./src/`: Contains all source (`.cs`) files for our mod. This is what makes this a code mod over a simple content mod, which just contains assets. More on this destinction on the [official wiki][VS-wiki]. 62 | - `./resources/`: This directory contains all files which will eventually be included in the release `.zip` file such as the `modinfo.json`. 63 | - `./resources/assets/`: Contains all assets for our mod. Unlike some other games (*cough* Minecraft *cough*), a lot of the heavy lifting will already be done by the asset loading portion of the engine. 64 | 65 | ### Project File 66 | 67 | Copy the [`.csproj`](HowtoExample.csproj) from this repository into your project folder, and rename it. It contains the minimum requirements for the project to build and a release to be created. 68 | 69 | As for example seen in [CarryCapacity's project file][carrycapacity-csproj], there are more fields you can include for the sake of completion, but these are not strictly required. 70 | 71 | #### References 72 | 73 | ```xml 74 | 75 | $(VINTAGE_STORY)/VintagestoryAPI.dll 76 | false 77 | 78 | ``` 79 | 80 | Here, we're adding a reference to the modding API `.dll`. In [the example's .csproj](HowtoExample.csproj) you can see some additional commented-out references, which you can use to include the official base mods (essentials, survival and creative) if your code needs them. 81 | 82 | Setting `false` means that this `.dll` will not be copied to the output folder, as we don't want them to be included in our releases. 83 | 84 | #### Releases 85 | 86 | ```xml 87 | 88 | 89 | 90 | %(RecursiveDir)%(Filename)%(Extension) 91 | PreserveNewest 92 | 93 | 94 | ``` 95 | 96 | This causes additional files to be included in the output folder (`bin/debug/net461/`) when building the project in release configuration. As you can see, you can use this to include a license file as well - remove or change this as needed for your own project. Then, we also want to include the contents of the `resources` folder. 97 | 98 | ```xml 99 | 100 | 101 | 102 | ``` 103 | 104 | This packs all the files in the output folder into a single `.zip` file for you to release. 105 | 106 | To build the mod in release configuration, run `dotnet build -c Release` or the "Build (Release)" task that I show you how to set up further below. 107 | 108 | ### General VS Code suggestions and Cheat Sheet 109 | 110 | At this point you have everything set up to start writing some code, so here are some basic things to get you set up. 111 | 112 | First of all, one of the issues you might encounter is code completion not working properly. This is likely because you have created the project after 113 | the C# extension has started up. To fix that, press `F1` to open the **Command Palette** and enter "Reload Window". 114 | 115 | This time OmniSharp should recognize the project, and you can open the "Output" window to verify this. There's a lot of ways to get to it, including the command palette (search for "Toggle Output" this time), just make sure you have "OmniSharp Log" selected in the dropdown: 116 | 117 | ![OmniSharp Output Log](docs/omnisharp.png) 118 | 119 | Next up, if you already know the type names you want to use, but not which namespace they're residing in, or you don't want to bother manually importing them, simply write the type name and pay attention to the **Quick Fix** bulb to the left, which you can either click or press `Ctrl+.`to get to: 120 | 121 | ![Quick Fix Using](docs/using.png) 122 | 123 | Also, on a right click you have the option of **Go to Definiton** (`F12`) and **Rename Symbol** (`F2`) which are two other things I end up using a lot. The former can also be used to see which methods are available on an API type to which you don't have source access to. 124 | 125 | ![Right Click](docs/rightclick.png) 126 | 127 | ## Tasks and Launchers 128 | 129 | After you've gotten familiar with some resources on how to create your mod, either from looking at the [Vintage Story Wiki][VS-wiki] or taking inspiration from other open source mods, it is time to actually build and launch it in the game. 130 | 131 | For this we need two things: First, a `.vscode/tasks.json` file, in which we'll define a build task to compile our mod into a handy `.dll` file. The compilation step **can** be done by the game itself, which is explained on the wiki, but you lose out on the possibility of debbugging your code. 132 | 133 | ```json 134 | { 135 | "version": "2.0.0", 136 | "tasks": [{ 137 | "label": "Build (Debug)", 138 | "group": { "kind": "build", "isDefault": true }, 139 | "presentation": { "reveal": "silent" }, 140 | "problemMatcher": "$msCompile", 141 | 142 | "type": "process", 143 | "command": "dotnet", 144 | "args": [ "build", "-c", "Debug" ] 145 | },{ 146 | "label": "Build (Release)", 147 | "group": "build", 148 | "presentation": { "reveal": "silent" }, 149 | "problemMatcher": "$msCompile", 150 | 151 | "type": "process", 152 | "command": "dotnet", 153 | "args": [ "build", "-c", "Release" ] 154 | }] 155 | } 156 | 157 | ``` 158 | 159 | The default `build` task can be run by pressing `Ctrl+Shift+B`. Other tasks may be run by pressing `F1` and typing "Run Task". 160 | 161 | To run the game and make it load our new mod, we'll create a **launcher**. This way, we can also hook the debugger into the game, allowing us to use breakpoints and debug exceptions originating from our code. 162 | 163 | ```json 164 | { 165 | "version": "0.2.0", 166 | "configurations": [{ 167 | "name": "Launch Client", 168 | "type": "mono", 169 | "request": "launch", 170 | "preLaunchTask": "Build (Debug)", 171 | "program": "${env:VINTAGE_STORY}/Vintagestory.exe", 172 | "args": [ 173 | "--playStyle" , "preset-surviveandbuild", 174 | "--openWorld" , "modding test world", 175 | "--addModPath", "${workspaceFolder}/bin/Debug/net461", 176 | "--addOrigin" , "${workspaceFolder}/resources/assets", 177 | ], 178 | "console": "internalConsole", 179 | "internalConsoleOptions": "openOnSessionStart", 180 | }] 181 | } 182 | 183 | ``` 184 | 185 | Note that if you're using **.NET on Windows**, you need to replace `type` with `"clr"`. In this project's [launch.json](.vscode/launch.json) you'll find that I've created two launchers: One for .NET, and one for Mono. You may choose to do this as well to ensure your project can be run from multiple plaforms. 186 | 187 | These are the arguments we're passing to the game: 188 | 189 | - `--playStyle`: The world configuration preset. Only used if it doesn't exist yet. 190 | `preset-surviveandbuild` = normal / survival. `creativebuilding` = superflat / creative. 191 | - `--openWorld`: Open or create a world with the specified name. 192 | - `--addModPath`: Additional paths that the game will search for mods from. 193 | - `--addOrigin`: Load assets from one or multiple directories. 194 | 195 | You should now be able to run the game in debug mode with your mod by simply selecting the appropriate launcher by pressing on "Launch Client" to the left of the bottom status bar in VS Code. When you've done this once, you can also run the last used launcher by pressing `F5`. 196 | 197 | 198 | [VS]: https://www.vintagestory.at/ 199 | [VS-wiki]: http://wiki.vintagestory.at/ 200 | [VS-AUR]: https://aur.archlinux.org/packages/vintagestory 201 | [dotnet]: https://dotnet.microsoft.com/ 202 | [dotnet-dl]: https://dotnet.microsoft.com/download 203 | [vscode]: https://code.visualstudio.com/ 204 | [vscode-dl]: https://code.visualstudio.com/Download 205 | [cs-ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp 206 | [mono-ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.mono-debug 207 | [AUR]: https://wiki.archlinux.org/index.php/Arch_User_Repository 208 | [carrycapacity-csproj]: https://github.com/copygirl/CarryCapacity/blob/master/CarryCapacity.csproj 209 | [VSMods-github]: https://github.com/copygirl/VintageStoryMods 210 | --------------------------------------------------------------------------------