Spatial

Dungeons

Instanced-zone lifecycle: create/seed via IDungeonContent, complete/reset, abandon sweep. Server-only.

Server
services.AddCrossplayDungeons();
Depends on
world
Shape
server-only · zero wire

Seams you implement 1

Crossplay calls these; your game supplies them. Each ships an inert or permissive default, so register yours before services.AddCrossplayDungeons(); and it wins.

IDungeonContent game SPI

The game's dungeon content — THE SPI to implement for the Dungeons piece. Spawn this template's NPCs/props/loot into the instance zone (via IServerEntities / ISpawnerService / anything). Called on create AND on every reset — the piece despawns the previous run's content first, so Seed always starts from an empty zone. Default: empty rooms (EmptyDungeonContent). How to implement. Register your implementation in DI BEFORE AddCrossplayDungeons (it is a TryAdd seam). Branch on TemplateKey to decide what to spawn, and read Data for per-run parameters (difficulty, seed…). Spawn into Zone so the piece can wipe and release it later.

public sealed class MyDungeonContent : IDungeonContent
{
    private readonly IServerEntities _entities;
    public MyDungeonContent(IServerEntities entities) => _entities = entities;

    public void Seed(DungeonContext ctx)
    {
        if (ctx.TemplateKey == "cave")
            for (int i = 0; i < 5; i++)
                _entities.Spawn(i * 2f, 0f, 0f, 0f, batAppearance, ctx.Zone);
    }
}
  • void Seed(DungeonContext context)

    Populate the instance's fresh (or freshly-wiped) zone. Called on create and reset.

Services you call 1

Crossplay implements these. Resolve them from DI and call them from your own systems.

IDungeonService

Private instanced-zone lifecycle: create for a group, complete (teleport out + teardown), reset (wipe + reseed the same instance), abandon-teardown when everyone leaves. Content and meaning stay the game's; the piece owns the zone's cradle-to-grave.

  • bool Complete(long dungeonId)

    Ends the run: teleports remaining players to the return zone, despawns the content, releases the zone. Raises Ended.

  • long Create(string templateKey, IReadOnlyList<ISession> party, byte[] data = null)

    Creates an instance for the group and teleports its members in, then seeds it via the game's IDungeonContent.

  • event Action<DungeonContext, bool> Ended

    Raised after a run ends: (context, completed) — completed is true for an explicit Complete, false for an abandon-teardown (everyone left). Hook this to grant rewards / record results.

  • bool Reset(long dungeonId)

    Wipes the instance's server entities and reseeds the SAME zone (players stay) — e.g. a "next wave" or a boss re-pull.

  • bool TryGet(long dungeonId, out DungeonContext context)

    The live instance's context, if any.

Configuration 1

Every tunable lives in an options object — there are no magic numbers to hunt for.

DungeonOptions

Dungeons tuning (defaults here, never inline).

  • double AbandonSeconds { get; set; }

    Teardown an instance this long after its last player leaves.

  • string ReturnZone { get; set; }

    Where completed/abandoned parties land ("" = the world's default zone).

  • float SpawnX { get; set; }

    Spawn point inside a fresh instance.

  • float SpawnY { get; set; }

    Y coordinate of the spawn point inside a fresh instance.

  • float SpawnZ { get; set; }

    Z coordinate of the spawn point inside a fresh instance.

  • double SweepSeconds { get; set; }

    How often abandonment is checked.

  • string ZonePrefix { get; set; }

    Zone-name prefix for instances (instance = "prefix:id").