# Aeshnidae.OverworldCopies

Private copies of one **outdoor** landblock - a *bubble* - on
`Aeshnidae.InstancesNoDat`'s copy machinery. **Test server only** (2026-09-24): it is
built straight into `~/ace-staging/Mods` and never deployed to live.

## The switch

| Command | Who | What |
| --- | --- | --- |
| `/overworld on` / `off` | Admin, console | The switch, kept in `Settings.json`. Off closes every bubble (everyone inside is put back in the real world where they stand) and every hook passes straight through. |
| `/overworld` | anyone | Status: on or off, open bubbles, where you are. |
| `/overworld enter` | `EnterAccessLevel` (Player) | Your own copy of the outdoor landblock you stand on, at the same spot. Reopens your own if it is still open. |
| `/overworld join` | `EnterAccessLevel` | A fellowship member's copy of the landblock you stand on. |
| `/overworld leave` | `EnterAccessLevel` | Back to the real world, where you stand. |
| `/overworld list`, `close <lb> <copy>` | Admin | Every open bubble; close one. |

## What a bubble is

- One 192 m landblock, copied: its own creatures, generators and random outdoor
  encounters, spawned fresh from the world database. The client needs nothing - the
  copy keeps the landblock's id, exactly like a dungeon copy.
- **Nothing beyond the edge.** While a bubble ticks, physics asking about any other
  landblock gets no cells, so nothing in the bubble sees, touches or walks into the real
  world next to it. The terrain beyond is still drawn by the client; it is empty.
- **Walk off the edge** and you are put back in the real world where you stand, and
  carry on walking. A teleport out (recall, portal, death) drops your claim on the
  bubble, so later visits to that landblock are the real world.
- **No housing** in a bubble: houses, slumlords, storage, hooks, house portals, boot
  spots, deeds and allegiance bindstones exist once, with one owner (`LeftOutTypes`).
- No saved player objects either (InstancesNoDat never loads them into a copy).
- Everything else is InstancesNoDat's: a bubble is a personal copy, so its creatures are
  scaled to the owner like any personal copy; a death in it loses no items; it closes
  after `EmptyGraceSeconds` empty; its copy number counts against
  `MaxCopiesPerLandblock`.

## Solo and group (2026-09-25)

Each outdoor landblock's copies are **solo** or **group**, set in InstancesNoDat's settings:
`Outdoor` entries carry a `Scope` (`Personal` = solo, `Fellowship` = group), and
`OutdoorScope` (default `Personal`) covers every landblock not listed.

- **Solo:** dungeon rules - one person per copy, entering leaves your fellowship, no
  fellowship can be formed inside, `/overworld join` is refused.
- **Group:** fellowship rules as anywhere - `/overworld enter` goes into a fellow's open copy
  of the landblock (the fellowship's copy) and opens a new one only when there is none;
  `/overworld join` does the same explicitly. Fellowships work inside. A member leaving by
  teleport keeps the group's claim while a fellow is still inside. Monsters are **not
  scaled** and loot is not awakened in a group copy (InstancesNoDat scales Personal copies
  only: a group has no one owner to scale to) - an open design question.
- No experience is shared between copies (InstancesNoDat `ShareExperienceAcrossCopies`
  off, Tom 2026-09-25) - in dungeons and in solo copies alike.
- Logging back in lands you in your copy if it is still open.

## Variants (2026-09-25)

A bubble opens with a variant when InstancesNoDat's `OutdoorVariants` names its landblock -
the same entry shape as `Instanced` (Landblock, Variants, VariantRule, FixedVariant; Swaps,
Removes, Adds), but nothing routes from it, so only a bubble ever copies the landblock.
Tested in the client on staging the same day with Holtburg A9B4 "bubble-test" (buff bot
removed, Royal Guards swapped to Lugian Overseers 3000022, two Lugian Thralls 3000021 added
by the Hub of Awakening portal): all three showed in the bubble, and `/overworld leave` put
Buffy back in an unchanged Holtburg.

Two-character tests on staging, 2026-09-25 (Buffy, admin, and Drakkon, a player), all passed:
1. Buffy alone in her own Holtburg with the test variant's Lugians.
2. Both standing in Holtburg, Buffy in her bubble and Drakkon in the real world - each saw
   their own version.
3. Both inside the same bubble.

## Regions (2026-09-25)

Several outdoor landblocks opened as ONE copy, joined at their seams - an island. Defined in
InstancesNoDat's `Outdoor` (an entry with `Landblocks`; see its Regions.cs); entered with
`/overworld enter` on any of its landblocks, or `/overworld send <region> <players...>` (Admin,
console: a fellowship, straight to the region's landing in `Landings`). Inner seams let
everything through; the outer edge is sealed like a bubble's.

First test, staging, same day: `aerlinthe-south` (BA-BC x E8-EA, 9 landblocks, group, every
monster a Lugian Thrall). Opened in 82 ms, ~6 MB a landblock, 79 Thralls. Buffy and Drakkon
sent in as a fellowship: fighting, killing, and dragging a Thrall across a seam all worked.
Drakkon's `/overworld leave` left his client waiting forever on its arrival reveal
("wait-cue ... ready=False") after a same-spot teleport that the client followed with a
memory trim - the server had placed him in the real BAE9 correctly; a relog fixed it. Looks
like an OpenAC client issue with a zero-distance teleport; the same leave in Holtburg (no
memory trim) was fine.

## Layers: the PK Aerlinthe (2026-09-28)

A Shared region can have more than one shared copy, each with its own portal: InstancesNoDat's
`Layers` on the region's `Outdoor` entry (`Name`, `Copy` 2+, `OnlyVia`, `PlayerKiller`). The
region's own `OnlyVia` always opens copy 1; a layer's portal opens the layer's copy. So the
Aerlinthe instance (3000120, copy 1) and the player-killer Aerlinthe (3000121, the red portal,
copy 2, layer `pk`) are the same island twice, side by side, and always the same two copy
numbers. `/overworld send aerlinthe:pk <players>` sends into the layer from the console.

Everything else a layer is lives in InstancesNoDat: its copy number keeps its own corpses and
dropped items (Keep tags them with the copy number), a death in it loses items as copy 1 does,
it is not scaled (Shared), and with `PlayerKiller` everyone standing in it is ACE's PK - status
and PkLevel, so ACE's pk_timer, PK death rules and respite all apply - and put back to what they
were the moment they are anywhere else (Layers.cs, PropertyInt 9045 holds what they were).
`inst-layers` (console) lists each layer, who is in it and their PK state.

## Who is on the island, and when it closes (2026-10-01)

A region copy closes only when NOBODY is in any of its open landblocks (InstancesNoDat
`CloseEmptyCopies` asks `Regions.Occupied` - every landblock's player list - and waits
`EmptyGraceSeconds`, 120 s), or when one of the two mods is reloaded. The player who opened
it is not special: leaving drops only their own claims (`PreTeleport`, `ReleaseRegion`); the
copy stays open for everyone else. Read 2026-10-01 after an admin was stranded: the island
that closed at 16:40:05 was closed by a hot reload of InstancesNoDat (its `Dispose` closes
every copy, `InstanceWorld.Clear`), not by its opener leaving.

**Both reloads close the island.** InstancesNoDat's reload recalls everyone in a copy to
their lifestone; this mod's reload puts everyone on the island back on the real island where
they stand (`CloseAll`). Reload either only when `/overworld list` shows no copy open, or
warn the players on it first.

**Admin teleports land in the copy.** `/teleto <player>` to someone on the island, and
`/teletome <player>` by an admin on the island, used to land on the REAL island at the same
spot - the island's landblocks are not InstancesNoDat dungeons, so nothing routed the
arrival - alone, unseen, and not counted as on the island. Since 2026-10-01 the traveller is
given the copy first (`Bubbles.FollowInto`: every landblock of it claimed, plus
`Context.Force`) and arrives in it. A plain `/tele` to coordinates, a recall or a lifestone
still means the real island: only the portal (and these two follow commands) leads in.

## How it plugs in

`Bridge.cs` binds to InstancesNoDat by reflection (the sibling-mod pattern in
AwakenedRewards' `Instances.cs`) and calls `InstanceWorld.PlaceIn / GetOrCreate /
AllocateCopy / Close` and `Context.Ambient`. InstancesNoDat is unchanged; with this mod
absent or off it behaves exactly as it does on live.

`Patches.cs` adds only the outdoor parts, each checking first that it is looking at one
of this mod's bubbles.

## Deploy (staging only)

```bash
# from C:\ACEPublic\Mods\src
tar --exclude=obj --exclude=bin -czf - Aeshnidae.OverworldCopies | ssh aelrynth-vps 'cd ~/ace/Mods/src && tar -xzf - && cd Aeshnidae.OverworldCopies && dotnet build -c Release'
bash /c/Aeshnidae/ops/ace-cmd.sh --staging 'mod find' 'mod enable Aeshnidae.OverworldCopies'   # first time only
```

The csproj sends every Linux build to `~/ace-staging/Mods/Aeshnidae.OverworldCopies`,
never live's `~/ace/Mods`, so a plain build on the VPS cannot reach live. The source
sits in `~/ace/Mods/src` with the others; publish-source only publishes mods live runs.
