# Aeshnidae.Towns

**Allegiance towns** (Tom, 2026-09-29: "build me buyable towns" - something allegiances buy and
build on, never attacked). An allegiance pools pyreals in a treasury, its monarch buys a charter,
and a town on the menu becomes that allegiance's own private copy - **somewhere its members travel
to, not somewhere that replaces the real town** (Tom, 2026-10-09).

## The rule that makes it work

**`/town go` is the only way in.** A town's landblocks resolve to the real town for everyone,
members included, however they arrive - the town's own retail portal, a recall, walking in, logging
in outside. A member who wants their allegiance's copy asks for it with `/town go`, which checks
the allegiance. That is the access rule, whole: nothing routes anyone into a copy on its own, so
there is no route an outsider could take.

Inside, the copy's landblocks are **one region**, so walking between them keeps you in the copy and
the seams are invisible. Walk off the edge of the set and you are in the real world - no wall, you
simply leave - and walking back on gives you the **real** town until you `/town go` again. Members
leave deliberately by a portal, a recall or `/town leave`.

**Log out inside the copy and you log back in inside it.** The copy number is written on the
character (PropertyInt **9052**) while they are standing in it - on arrival, by a sweep every two
seconds, and once more as they log out - and the login path reads it back (`Towns.LoginCopyFor` /
`ArrivalCopyFor`, from `Patches.PreAddObject`, the same hook InstancesNoDat uses for the same job).
The record is never trusted on its own: at login the copy must still exist, still be registered,
still belong to **that character's** allegiance, and still cover the landblock they are standing
in, or they are placed in the real world and the record is cleared.

How it is built: the mechanism is InstancesNoDat's **enclaves** (Enclaves.cs), registered by
reflection (Instances.cs); a town is copy `CopyBase + id` (200001, 200002, ...) of each of its
landblocks. An enclave normally routes every member of its owner's allegiance into the copy by its
**owner key**, which is what made a bought town replace the real one. So a town is registered with
**owner key 0** - a key no player can ever match - and the registration does nothing but name the
landblocks, open them as one region and mark the copy shared and kept. This mod then puts a member
in by claiming the copy for the one teleport `/town go` makes (`Context.Assign` + `Context.Force`,
dropped the moment that teleport is over) and by aiming the login of a character who was inside.
**Nothing in `Aeshnidae.InstancesNoDat` was changed for any of this** - note that Enclaves.cs's own
comments still describe the automatic routing, which no longer has a user.

**The menu** (`/town menu`): Kryst and **every mainland town the Town Network links** - 47 towns
(Tom, 2026-09-29: "I want mainland towns to be purchasable"). A mainland town is the 3x3 of
landblocks around its Town Network landing; Kryst's entry is the whole archipelago
(`town-admin scan E922`: 33 landblocks, Kryst and the Vesayen Isles). **Holtburg joined the menu on
2026-10-09**: it was left off only because an owner would never have seen the real Holtburg - the
server's hub (the Hub of Awakening, the Trials and island portals, the buff bot, invasions) - again,
and that reason is gone now that a copy is somewhere you visit. The copy holds Holtburg's own
duplicates of those portals and they lead where they always do, out of the copy. Two allegiances
may own the same town - each has its own copy.

A town copy spawns **no monsters** (every monster generator held empty, `NoMonsters`), keeps the
town's own NPCs and vendors, keeps what is dropped in it (InstancesNoDat keeps enclave copies like
shared ones), is never scaled, and has no housing. It loads on demand and closes when empty like
any copy - everything a town is lives in the database. Statically placed harmless creatures are not
generator spawns and so are not held: the Holtburg copy has its cow.

## Money

- **Treasury**, per allegiance (by monarch): members give with `/town donate <n|all>` **from the
  bank** - Aeshnidae.Bank's `aeshnidae_bank` Pyreal row debited only where it covers the amount and
  the treasury credited, in one transaction.
- **Charter**: `/town buy <town>`, monarch only, `CharterPrice` (10,000,000) from the treasury;
  checked and debited and the town written in one transaction, never twice. The allegiance needs
  `MinAccounts` (2) distinct accounts among the monarch and those sworn under them.
- Every donation, charter and grant is in `aeshnidae_town_ledger` (`/town ledger`).
- No upkeep. A town is kept until abandoned (staff: `town-admin abandon`, no refund).

## Commands

| Command | Who | What |
| --- | --- | --- |
| `/town` | anyone | your allegiance's town, treasury, the charter price, the menu, and **which town you are standing in** |
| `/town where` | anyone | just that last line |
| `/town donate <n\|all>` | a member | bank to treasury |
| `/town buy <town>` | the monarch | a charter |
| `/town go` | a member | into the allegiance's own copy, at the town's landing - the only way in (cooldown `TownCooldownMinutes`; live runs 0.1667 = ten seconds) |
| `/town leave` | anyone | to Holtburg, out of the copy |
| `/town ledger` | a member | the treasury's history |
| `town-admin` | Admin, console | every town, its enclave and who is inside; `grant <monarch\|0xguid> <town>`, `abandon <monarch>`, `open <id>` (test), `route <monarch\|0xguid\|-> <recorded copy> <landblock>` (the access decision, as a login asks it), `where [player]`, `bindcheck [player]`, `scan <lb> [max]`, `reload` |

## Not yet

Building on a town: a steward and a catalogue, placing things where an officer stands, the
allegiance vault (safe containers), hooks and portal devices. What happens when a monarch swears
to another allegiance (the town is keyed on the monarch). A town registrar NPC (commands until the
portal's final home is known). A portal into a town copy, which would need its own allegiance check
(every portal is open to everyone, which is exactly why routing was used before).

## Tested

**2026-09-29, staging, console:** `mod-load` brought the mod in with nothing else touched; `scan
E922` found the 33-landblock archipelago; `grant` registered "Kryst of Drakkon" (copy 200001);
`open 1` opened it as one region (7 landblocks around the landing) with 4 NPCs and vendors and 0
monsters, 66 monster spawns held. `grant ... arwic` opened "Arwic of Drakkon" as a joined 3x3 with
62 NPCs and vendors, 0 monsters.

**2026-10-09 (the opt-in change), staging then live, console:**

- Both servers hot-reloaded with one `shut down` and one `Reloaded`, and every town registered as
  `enclave '<Town> of <Monarch>' copy 2000NN (owner 0x00000000, 9 landblocks)` - the owner key that
  routes nobody.
- `town-admin route` over the whole access matrix, on staging and again on live with the real
  guids: a member with the copy recorded gets it; a member with nothing recorded (what a portal, a
  recall or a walk in looks like) gets the real landblock; **another allegiance's monarch with that
  copy recorded gets the real landblock**; nobody sworn gets the real landblock; a copy no
  allegiance holds any more gets the real landblock; a member recorded in their copy but standing
  on a landblock their town does not cover gets the real landblock.
- Live, with three players online: two members of the allegiance that owns Lin were standing in
  copy 200002 and `town-admin where` reported `routing says 0` for both - the enclave no longer
  routes a real member - while a third player stood in the public Lin on the same landblock. The
  sweep recorded 200002 on the two inside, twice (once after each reload), which is the login
  record being established for characters who were already inside.
- `town-admin bindcheck` with two live players: claim, routing and aim all answer across the
  reflection binding (`Context.Assign`, `Context.CopyFor`, `Context.Force`).
- `town-admin open` on staging still opens a copy as one region with the owner key gone: Arwic, 9
  landblocks, 118 objects, 64 NPCs and vendors, **0 monsters**, 30 spawns held. Holtburg, granted
  to a test guid: 9 landblocks (A8B3..AAB5) as one region, 115 objects, 47 NPCs and vendors, 1
  monster (the cow), 57 spawns held - then abandoned again.
- The `/town go` cooldown now reads in seconds: live's 0.1667 minutes is told as "10 seconds".
- No warning, error or exception from this mod on either server.

**Not exercised (no client login available):** `/town go` itself, arriving by portal, recall or on
foot, walking off the edge, and the login inside a copy. Each is code whose decision has been
tested from the console, but the teleports and the placement have not been seen in a client.
