Skip to content

Getting Started

Fievetl edited this page Jul 4, 2026 · 5 revisions

Getting Started

Requirements

  • A Vintage Story 1.22.x install (server or client, either works: Atlas only needs VintagestoryAPI.dll and the game's own libraries). 1.22.0 is the hard floor: earlier versions lack the server exit lifecycle API Atlas hooks. See Compatibility for the full per-version sweep results.
  • The VINTAGE_STORY environment variable set to the folder containing VintagestoryAPI.dll.
  • .NET 10 SDK.

1. Create the test project

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
    <PackageReference Include="xunit" Version="2.9.*" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
    <PackageReference Include="Pixnop.Atlas.XUnit" Version="0.1.0" />
  </ItemGroup>

  <ItemGroup>
    <!-- VintagestoryAPI is needed to compile game types (BlockPos and friends) that appear
         directly in IWorldSession method signatures used in your scenario bodies. -->
    <Reference Include="VintagestoryAPI">
      <HintPath>$(VINTAGE_STORY)\VintagestoryAPI.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>

Each piece matters:

  • The Reference to VintagestoryAPI.dll is required because game types appear directly in IWorldSession method signatures, so the test project must compile against them.
  • Pixnop.Atlas.XUnit ships build/Atlas.E2E.targets as a buildTransitive MSBuild target (packed into the NuGet package itself, under buildTransitive/Pixnop.Atlas.XUnit.targets). For package consumers, this means no manual <Import> is needed: the target runs automatically on every build and copies the game's own Newtonsoft.Json.dll over whatever version the test SDK pulled in transitively. Skipping this fix does not fail the build; it fails at test run time instead, with a confusing MissingMethodException. See Troubleshooting.
Building from source instead

The manual <Import> path applies only to consumers building Atlas from source (a ProjectReference to Atlas.XUnit.csproj rather than a PackageReference), where the buildTransitive packaging step never runs:

<ItemGroup>
  <Reference Include="VintagestoryAPI">
    <HintPath>$(VINTAGE_STORY)\VintagestoryAPI.dll</HintPath>
  </Reference>
  <ProjectReference Include="path/to/Atlas.XUnit/Atlas.XUnit.csproj" />
</ItemGroup>

<!-- Only needed when building from source. Package consumers get this automatically. -->
<Import Project="path/to/build/Atlas.E2E.targets" />

2. Declare assembly-level settings

using Atlas.XUnit;
using Xunit;

[assembly: CollectionBehavior(DisableTestParallelization = true)]

// Resolved relative to the test assembly's own output directory.
[assembly: AtlasMods("relative/path/to/your/mod")]
  • DisableTestParallelization is required: Atlas hosts at most one live server per process, so scenario classes cannot run concurrently.
  • AtlasModsAttribute paths (folder, .zip, or .dll) are resolved relative to the test assembly's own output directory, not the source tree. See Mod Staging for the full path resolution rules and the MSBuild ProjectReference sugar that avoids hand-writing this path.

3. Write a scenario

This one uses a vanilla block, so no mod is required to try Atlas out:

using Atlas.Api;
using Atlas.XUnit;
using Vintagestory.API.MathTools;
using Xunit;

public class MarkerScenarios : AtlasScenarioBase
{
    [AtlasScenario]
    public async Task Chest_Should_BePlaceable_When_WorldIsReady()
    {
        BlockPos pos = World.Spawn.Offset(1, 1, 0);
        World.SetBlock("game:chest-east", pos);
        await World.Ticks(5);
        Assert.Equal("game:chest-east", World.BlockAt(pos).Code.ToString());
    }
}

4. Run it

dotnet test

Atlas boots a fresh headless server per test class (superflat world, creative playstyle, fixed seed by default), pumps it on a dedicated game thread, runs your scenario on that thread, then tears it down.

See Writing Scenarios for the full attribute reference and authoring rules, and Troubleshooting if something does not work as expected.

Clone this wiki locally