Installing

This page is for a modder who already has a project, or is about to make one, and wants exlib in it. It covers what exlib ships, where each piece comes from, and the handful of lines that wire it into a mod. If you would rather start from a working repository, clone exmod-starter instead; everything below is already done there.

What ships, and where

exlib is two things at once: a mod the game loads, and a set of libraries your project compiles against. Players only ever see the first. Modders need both.

PieceWhere to get itWhat it is for
exlib_<version>.zipthe releases pagethe mod itself: one folder holding exlib.dll and exlib.industry.dll, installed into the game's Mods folder like any other mod. Your mod declares it as a dependency and the game loads it first.
ExpandedLibnuget.orgthe framework you compile against: exlib.dll, the config and lang source generators, and the MSBuild plumbing that finds the game, copies your assets and stamps your version.
ExpandedLib.Industrynuget.orgthe industry layer: pipe networks, molten metal, mechanical-power ports, heat balance. Reference it when your blocks carry gas, liquid, metal or shaft power.
ExpandedLib.Testingnuget.orgthe headless test harness. A test project references it and drives your blocks under dotnet test, with no game running.
ExpandedLib.Templatesnuget.orgdotnet new templates for a block, item, recipe, node, structure, config, command, migration or test project. exmod scaffold installs and runs them for you.

The packages are built for the current game version only (1.22 on .NET 10). The zips on the releases page also cover 1.21 and 1.20, named exlib_<version>_1.21.0.zip and exlib_<version>_1.20.0.zip. None of the packages carries the game's own assemblies; your project references VintagestoryAPI.dll and friends from a game install, the way every mod does.

Adding exlib to an existing mod

Four edits, then a build.

1. Reference the framework package. In your mod's .csproj:

<ItemGroup>
  <PackageReference Include="ExpandedLib" Version="0.8.2" ExcludeAssets="runtime" />
  <PackageReference Include="ExpandedLib.Industry" Version="0.8.2" ExcludeAssets="runtime" />
</ItemGroup>

Leave ExpandedLib.Industry out if you use none of the industry systems. ExcludeAssets="runtime" is required on both: the player installs exlib as its own mod, and a second copy of exlib.dll inside your mod's folder makes the game refuse the whole folder with "Found multiple .dll files with ModSystems". The refusal is silent from the player's side; your mod is simply not in the list.

2. Name your asset domain. In the same .csproj:

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
  <AssetDomain>yourmod</AssetDomain>
</PropertyGroup>

AssetDomain is your mod id. It switches on the two things the package does for you at build time: copying your assets/ tree into the output, and generating a typed YourmodLang class from assets/yourmod/lang/en.json (see Source Generators). The package also resolves the game install for you, from the VINTAGE_STORY environment variable or -p:GamePath=... on the command line, so there is no GamePath property to write.

3. Declare the runtime dependency. In modinfo.json:

{
  "type": "Code",
  "modid": "yourmod",
  "name": "Your Mod",
  "version": "1.0.0",
  "dependencies": {
    "game": "1.22.0",
    "exlib": "0.8.2"
  }
}

The number is a floor, not a pin: the game accepts any installed exlib at or above it. Write the version you compiled against and raise it whenever you start calling something newer, or a player with an older exlib gets a missing-member crash at world load instead of a clear dependency error.

4. Install the mod into the game. Put exlib_<version>.zip in the game's Mods folder, next to your own mod's output, for every install you run or test in. exmod provision mods does this for you when you use the exmod tool.

Then build. dotnet build restores the packages, finds the game, compiles and copies your assets; the output folder under bin/ is a complete mod.

Registering your first class

With the package in place, exlib registers your classes from attributes. Tag them and derive one empty mod system:

using ExpandedLib.Registries;
using Vintagestory.API.Common;

[BlockRegister]
public class BlockMachine : Block { }

[BlockEntityRegister]
public class BlockEntityMachine : BlockEntity { }

public class YourModSystem : ExModSystem { }

There is no RegisterBlockClass list to keep in step with your classes. Getting Started carries on from here with a complete block, its saved state, a config value and a test.

A test project

Reference the harness from a separate test project, never from the mod itself:

<ItemGroup>
  <PackageReference Include="ExpandedLib.Testing" Version="0.8.2" />
  <ProjectReference Include="../src/YourMod.csproj" />
</ItemGroup>

The harness loads the real game assemblies from your install and gives you a TestWorld to place blocks in, tick, and assert on. Its API still moves between releases, so pin the version you build against. Testing Harness shows what a first test looks like.

The exmod tool

Nothing above needs it, but the whole loop (build, test, boot a server with your mod, scaffold a new block) is one script when you copy scripts/exmod.sh and scripts/exmod.ps1 from the exlib repository, or from the starter, into yours and add an exmod.json naming your mod. exmod provision game downloads the dedicated server so you can build and test without a game purchase on that machine; exmod provision mods fetches the exlib zip your modinfo.json names; exmod smoke boots the server against your built mod and fails on any error in the log. The commands are documented in extools.

Building from source instead

If you clone the exlib repository beside your own, a Directory.Build.props above both that sets ExlibRoot to the checkout switches your project from the packages to a plain project reference. That is the loop for changing exlib and your mod together without a release in between; nothing about it is part of the package's public contract.

Versions

exlib follows semantic versioning from 0.8.0 on: a minor bump may change a supported signature, a patch bump does not. Supported API lists which types are the supported contract and which are internal plumbing that may move without notice.