# DigiSouls server

Same architecture as Altera Earth's Object Forge — flat JSON, one file per
record plus a master index, PHP admin that writes both. No database, so it
drops onto the same host with nothing to provision.

## Layout

```
digisouls/
  admin/
    index.php          the forge — pets, assets, synergy
    save_pet.php
    save_asset.php
  data/
    pets.json          master pet index
    pets/<name>.json   one file per pet
    assets.json        the id -> mesh table AssetLookup reads
    synergy.json       pet-combination bonuses
  assets/
    models/  textures/  icons/
  loader.php           what the headset calls
```

## Endpoints

```
GET loader.php?what=assets        the lookup table
GET loader.php?what=pets          the catalogue, ladders stripped
GET loader.php?what=pets&full=1   the catalogue with ladders
GET loader.php?what=pet&id=N      one pet in full
GET loader.php?what=line&id=N     a whole evolution line
GET loader.php?what=synergy       combination bonuses
```

Every response carries `version`, so the sequencer can compare against what
the headset already has and skip the download when nothing changed.

**No player endpoints.** What a player owns, what is equipped, what is bound to
the soul, and every pet's level and care state all live on the Quest 3. The
server is a catalogue — the same for everyone. Accounts arrive with the
companion app, and then for floor data and websockets, not for pets.

**The catalogue is light by default.** 140 pets x 120 skills is a large
response for something the headset asks just to compare versions, so the ladder
only comes with `full=1` or a single-pet fetch.

## Evolution

Each stage is its **own pet** with its own id and its own mesh, linked both ways
by `evolvesFrom` / `evolvesTo`. Saving one end writes the other, so a chain can
be walked in either direction without editing both records.

The headset holds only the **current stage**. At 140 pets and ~35&nbsp;MB each,
nothing speculative is cached — devolving follows `evolvesFrom` and
re-downloads that form.

Triggers, any of which can be blank to disable:

| forward | backward |
|---|---|
| `level` | `devolveBelowCare` |
| `itemId` | `devolveItemId` |
| `battles` | `devolveAfterDays` |

Care and neglect are evaluated on the headset against elapsed real time, so
devolution runs with no server involved.

## Skills

The full ladder from level 1 to 120 ships with the pet, defined at creation. The
headset reads down to the pet's current level and never calls the server when a
pet levels up.

```
level | id | name | kind | target
```

`kind` is `skill` or `magic`. `target` is `pet`, `player` or `both` — a pet
bound to the soul grants the player anything marked `player` or `both`, while
the pet itself uses `pet` or `both`.

## What is different from Altera Earth

**No geo filter on pets.** Altera Earth objects are placed in the world and its
loader filters by radius from a lat/lon. DigiSouls pets are summoned wherever
the player happens to be, so there is no radius and no geolocation on a pet.

**Two record types.** Pets are what a player can summon. Assets are the id-to-
mesh table for staging — trees, rocks, grass, bowls.

**Model name is separate from pet name.** The headset downloads and caches by
model, so two pets sharing a `meshName` share one download and one parse. That
parse is roughly 600&nbsp;ms of blocked main thread, so it is worth sharing.

## Two rules that are easy to break

**Asset ids are permanent.** A headset stores the number in its saved
placements, not the name. Reuse a deleted id and every saved bowl becomes
whatever took the number. `save_asset.php` allocates from a stored `nextId`
rather than `max(id)+1`, so deleting the highest asset does not hand its number
to the next one created.

**Pets are deactivated, not deleted.** `isactive: false` hides a pet from the
loader while keeping its id reserved, because players may still have it in
their roster.

## Not built yet

- Auth on `/admin`. It is wide open. Put HTTP basic auth on it before it is
  reachable from anywhere but your own machine.
- The scan upload endpoints from `SCAN_SERVER_API.md` — floor tiles, shared
  parks, visibility rules. Separate concern, separate storage.
- Player writes. `data/players/` is read-only here; nothing yet grants a pet.
