Dungeons
Instanced-zone lifecycle: create/seed via IDungeonContent, complete/reset, abandon sweep. Server-only.
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> EndedRaised 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").