Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ git subtree pull --prefix=client redemption-upstream main --squash

- Never commit `Tibia.dat`, `Tibia.spr`, OTClient executables, logs, screenshots,
minimap caches, or database volumes.
- Restore the pinned client runtime and assets with:
- Use PowerShell 7+ (`pwsh`) on Windows or Linux. On Windows, bootstrap restores
the pinned client runtime and assets. On Linux, first build `client/otclient`;
bootstrap installs verified assets and leaves that executable untouched:

```powershell
pwsh -File scripts/bootstrap-client.ps1
Expand All @@ -114,7 +116,8 @@ pwsh -File scripts/bootstrap-client.ps1

## Server And Database

- Docker Compose is the supported local server environment.
- Docker Compose is the supported local server environment: Docker Desktop on
Windows, or Docker Engine with Compose v2 on Linux.
- Keep the Compose project name `angelion` so resources do not depend on the
checkout directory name.
- Keep ports `7171` and `7172` bound to `127.0.0.1` unless external exposure is
Expand Down Expand Up @@ -179,6 +182,10 @@ pwsh -File scripts/bootstrap-client.ps1

## Verification

See `docs/testing.md` for focused scenarios and non-Docker regression checks.
Persistence tests must restart or recreate only the server with `--no-deps`;
rerunning provisioning can refill equipment slots and invalidate saved-state checks.

For server, infrastructure, or cross-stack changes, run at minimum:

```powershell
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,21 @@ changing either subtree.

## Running it

The supported local environment uses Docker Desktop and PowerShell 7+. GitHub
CLI authentication is needed only when the bootstrap script must download the
pinned private client executable.
Use PowerShell 7+ (`pwsh`) with Docker Desktop on Windows or Docker Engine with
Compose v2 on Linux. Linux bootstrap uses a locally built `client/otclient` and
installs the same verified assets; it does not download a Windows executable.
GitHub CLI authentication is needed only to download the pinned Windows runtime.
See [client runtime](docs/client-runtime.md) and [testing](docs/testing.md) for
Linux usage and Docker privilege limits.

```powershell
pwsh -File scripts/bootstrap-client.ps1
docker compose -f server/compose.yaml up --build --detach
docker compose -f server/compose.yaml logs --follow server
```

Launch `client/launch-angelion-redemption.cmd`. Ports `7171` and `7172` bind to
On Windows, launch `client/launch-angelion-redemption.cmd`; on Linux, run
`(cd client && ./otclient)` from a POSIX shell. Ports `7171` and `7172` bind to
localhost only.

The tracked accounts are local development defaults:
Expand Down
28 changes: 18 additions & 10 deletions docs/client-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,32 @@
## Bootstrap

The OTClient executable and Tibia DAT/SPR assets are excluded from Git. Install
and verify the pinned runtime with:
and verify the runtime assets with PowerShell 7+ on Windows or Linux:

```powershell
pwsh -File scripts/bootstrap-client.ps1
```

GitHub CLI authentication is required only when the private executable must be
downloaded. The script verifies SHA-256 for the executable, asset archive,
`Tibia.dat`, and `Tibia.spr`; do not weaken these checks.
On Windows, GitHub CLI authentication is required only when the pinned private
executable must be downloaded. On Linux, build `client/otclient` locally first;
bootstrap requires that file and leaves it untouched, including with `-Force`.
No Linux binary is published or downloaded, and the local build has no pinned
hash. The script preserves SHA-256 verification for the Windows executable,
asset archive, `Tibia.dat`, and `Tibia.spr`. `-Force` reinstalls assets on both
platforms and the executable on Windows only.

| File | Runtime path |
| ---- | ------------ |
| OTClient | `client/otclient_gl_x64.exe` |
| OTClient (Windows) | `client/otclient_gl_x64.exe` |
| OTClient (Linux local build) | `client/otclient` |
| DAT | `client/data/things/860/Tibia.dat` |
| SPR | `client/data/things/860/Tibia.spr` |

Launch `client/launch-angelion-redemption.cmd`. The isolated profile is
`angelion-redemption`.
On Windows, launch `client/launch-angelion-redemption.cmd`. On Linux, run
`(cd client && ./otclient)` from a POSIX shell, or `Push-Location client;
./otclient; Pop-Location` in PowerShell. The isolated profile remains
`angelion-redemption`. Bootstrap does not validate native build dependencies,
executable permissions, display/input behavior, or in-game compatibility.

## Compatibility contract

Expand All @@ -43,9 +51,9 @@ feature boundaries in `client/modules/game_features/features.lua`.
## Asset boundary

Client asset auto-installation is disabled. `scripts/bootstrap-client.ps1` is
the only supported runtime source; the client's auto-install documentation
the supported asset installer (and Windows runtime source); the client's auto-install documentation
describes an upstream capability, not project behavior.

Never commit `Tibia.dat`, `Tibia.spr`, OTClient executables, logs, screenshots,
or minimap caches. When changing the runtime, update the durable private release
asset, expected hashes, and this documentation together.
or minimap caches. When changing the pinned Windows runtime or assets, update
the durable private release asset, expected hashes, and this documentation together.
13 changes: 10 additions & 3 deletions docs/playerbots.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,13 @@ carried-gold reserve. If total gold cannot raise stock above the return
threshold, service stops with `insufficient_potion_funds` without buying an
unusable partial reserve. The cycle deposits carried money and withdraws up to
100 gp without exceeding the bot's total available gold. It does not buy food
merely because none is carried.
merely because none is carried. The low-wealth regression isolates this banking
contract with 56 bank gp, zero carried gp, ten selected health potions, and no
sale cargo. Initial free capacity is 10 oz (1000 native units), below the 30 oz
readiness threshold. Depositing the displaced 25 oz club restores 35 oz, above
the 30 oz return threshold, without sale cargo; currency weight is reclaimable.
It verifies a normal 56 gp withdrawal and retention of equipped
upgrade 2384 before hunting; dedicated liquidation scenarios cover selling.
Hunting ends after the configured duration or below 30 oz
effective free capacity. Effective capacity is physical free capacity plus the
weight of carried standard food and currency, because food can be consumed or
Expand All @@ -413,8 +419,9 @@ effective capacity above the reserve. Remaining top-level backpack loot is moved
locker into that player's real depot chest. Depot locality and locker identity
are independent: discovery enumerates
all map-indexed lockers, then validates that each candidate still has its indexed
depot ID. It ranks all standable adjacent squares by weighted current-position
distance, locker ID, locker position, and approach position. It validates at
depot ID. It ranks standable adjacent squares by topology distance, then breaks
equal costs by floor-weighted distance from the current position, depot ID,
locker position, and approach position. It validates at
most two routes per scheduler decision and resumes the sorted queue on the next
decision. A moved player rebuilds the queue from a new anchor. Failed approaches
are suppressed for two seconds, then become
Expand Down
105 changes: 104 additions & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
# Testing

Use PowerShell 7+ (`pwsh`) on Windows or Linux. The same scripts, scenario
catalog, and assertions apply on both platforms. Linux needs Docker Engine and
Compose v2; bootstrap also requires a locally built `client/otclient`.
Commands containing `$env:` or `Remove-Item` below run inside PowerShell, not Bash.
The `pwsh -File ...` commands work from either shell at the repository root.

If Docker requires elevation on your Linux setup, run Docker commands and the
gameplay driver from an explicitly authorized elevated shell; the scripts do not
elevate themselves or require changes to socket permissions or group membership.
Elevated test runs can leave root-owned failure artifacts. The suite resets the
disposable `angelion` database and removes its stack unless `-KeepStack` is set;
do not run it alongside a normal development session. A failed daemon-access preflight does not attempt stack cleanup.

The spell contract check needs neither Docker nor elevation:

```powershell
pwsh -File scripts/test-knight-spell-contract.ps1
```

Run the non-Docker telemetry assertion regressions:

```powershell
pwsh -File scripts/test-playerbot-navigation-assertions.ps1
pwsh -File scripts/test-playerbot-readiness-assertions.ps1
pwsh -File scripts/test-playerbot-log-parsing.ps1
pwsh -File scripts/test-playerbot-scenario-isolation.ps1
pwsh -File scripts/test-playerbot-depot-scenario.ps1
pwsh -File scripts/test-playerbot-death-scenario.ps1
pwsh -File scripts/test-playerbot-magic-training-assertions.ps1
```

On Linux, pure contract checks require a C++17 compiler; fixture-isolation checks
require Lua or LuaJIT. Run these from the repository root:

```sh
sh server/tests/playerbot_contracts.sh
lua scripts/test-playerbot-fixture-isolation.lua
lua scripts/test-playerbot-depot-fixture.lua
lua scripts/test-playerbot-death-fixture.lua
```

The depot Lua regression checks login-time inventory expectations, bounded waiting
for the Depart pause, and lost inventory/reserve failures. The PowerShell depot
regression checks pause, verification, and stopped-database rearm ordering.
Recovery uses `--no-deps` to keep provisioning from changing saved inventory;
the Lua success marker is flushed explicitly because the paused bot emits no
later telemetry.
`PLAYERBOT_DEPOT_VERIFIER_PHASE` is Lua-fixture-only: it preserves the original
restart phase's prior-gold expectation while recovery pauses at Depart.

Run the affected live depot scenarios without the longer risk-fallback fixture:

```powershell
pwsh -File scripts/test-playerbot-gameplay.ps1 -Scenario real_depot,real_depot_restart_approach,real_depot_restart_locker,real_depot_restart_chest,real_depot_restart_deposit,real_depot_restart_depart,real_depot_partial_move,real_depot_rejected_move
```

`server/tests/playerbotdepotworkflow_test.cpp` includes its compile command and
requires the server development headers. These checks supplement, not replace,
live gameplay validation.

## Server smoke test

For server, infrastructure, or cross-stack changes, run:
Expand Down Expand Up @@ -52,7 +112,7 @@ the changed behavior:
| `-CombatReadiness` | Equipment, the one-potion return threshold and 10-potion restock target, optional-food hunting, generic food consumption and reclaimable capacity, low-wealth banking, carried-upgrade retention through service, and restart reconstruction. It does not cover the terminal case where total funds cannot buy enough potions to exceed the return threshold. |
| `-EquipmentPurchases` | Justified purchase and equip verification, clean restart persistence, carried-upgrade recovery, displaced-item-space rejection, and rejected transactions. |
| `-MainlandRewards` | Real Thais reward object from a teleported, high-capacity fixture; scale-armor claim and equip, displaced-item and bundle preservation, restart reconstruction, and non-null battle-axe rejection evidence. It does not prove normal traversal, realistic capacity limits, or a specific rejection reason. |
| `-Depot` | Real Thais locker/chest discovery from Naji, including exact nearest-locker selection, carried-upgrade equipment, displaced and inferior equipment deposits, one carried rope and shovel, surplus tool deposits, nested loot, move verification, retries, and restart checkpoints. |
| `-Depot` | Real Thais locker/chest discovery from Naji, including exact nearest-locker selection, carried-upgrade equipment, displaced and inferior equipment deposits, one carried rope and shovel, surplus tool deposits, nested loot, move verification, retries, and restart checkpoints. Both normal cycles, all five checkpoint recoveries, and partial moves verify completed inventory while paused at Depart, before optional selling. Rejected moves retain separate exact delta/retry/discard assertions. |
| `-SlottedLoot` | Invalid-slot loot sale through a live seller, direct depot fallback without an eligible seller, protected-equipment retention, move verification, and interrupted-deposit restart recovery. |
| `-SellLoot` | Local and remote-depot liquidation, capacity-bounded manifests, verified withdrawal, seller travel, sale ordering, and proceeds-funded resupply. The workflow excludes fluid containers and splashes; current fixtures do not seed those item types. |
| `-MainlandLoop` | Two real Thais hunt/depot cycles, local services, depot fallback for remote-buyer loot, restart recovery, and teleport exclusion. |
Expand Down Expand Up @@ -86,6 +146,33 @@ not prove that the image matches the worktree. `-KeepStack` preserves the final
stack for debugging. `-TimeoutSeconds` accepts `30` through `3600` and replaces
each scenario's fail-fast deadline.

`combat_readiness_low_wealth` isolates banking from selling: zero carried gold,
56 gp in the bank, ten selected health potions, a carried upgrade (2384), and
no rabbit sale cargo. Setup leaves 10 oz free capacity (`usedCapacity + 1000`
in native units), below the 30 oz readiness threshold. Depositing the displaced
25 oz club (2382) restores 35 oz, above the 30 oz return threshold. This forces
readiness → equip → depot → bank without sale proceeds or a zero-capacity dead
end; withdrawn currency weight remains reclaimable.
It requires exactly one normal 56 gp withdrawal (bank
56 → 0), the Lua-verified 56 gp carried balance and equipped upgrade, a hunt
start, and no sale or terminal event. Liquidation remains in `sell_loot` and
`sell_loot_remote_depot`: their assertions check manifest withdrawal and sales;
the local case also checks proceeds-funded potion resupply.

`magic_training_progression` seeds ten selected health potions, two meat,
100 carried gp and 500 bank gp. This leaves the 100 gp reserve plus the
500 gp Great Light price. Only nearby currency reward 50082 is marked claimed.
The scenario ends at `learn_spell` selection over feasible magic training
(utilities 550 and 350); it does not wait for NPC dialogue or spell payment.
`magic_training_reserve` and `magic_training_service` retain their intentional
low-mana and capacity/service setups.

Local regressions: `lua scripts/test-playerbot-fixture-isolation.lua` and
`pwsh -File scripts/test-playerbot-readiness-assertions.ps1`. Live coverage uses
`-Focused -CombatReadiness` and `-Focused -MagicTraining -MagicTrainingCase`
with each of `magic_training_progression`, `magic_training_reserve`, and
`magic_training_service` on `scripts/test-playerbot-gameplay.ps1`.

Use `-MagicTrainingCase <name>` with `-Focused` to run one case from the
16-scenario magic-training matrix without paying for the other server
recreations. PowerShell validates the case name from the supported mode list.
Expand All @@ -98,6 +185,22 @@ pwsh -File scripts/test-playerbot-gameplay.ps1 -Healing -Focused -SkipBuild
The driver includes Compose status and the last 80 server-log lines in timeout
failures. Docker builds reuse a persistent BuildKit `ccache` mount.

Each selected scenario owns six environment settings: `PLAYERBOT_GAMEPLAY_MODE`
(default `cycle`), `PLAYERBOT_HUNT_DURATION_SECONDS` (`1500`),
`PLAYERBOT_RELOG_DELAY_SECONDS` (`5`), `PLAYERBOT_MAX_CONSECUTIVE_DEATHS` (`3`),
`PLAYERBOT_DEPOT_RESTART_PHASE` (empty), `PLAYERBOT_DEPOT_VERIFIER_PHASE` (empty),
and `PLAYERBOT_DEPOT_MOVE_CASE` (`normal`).
`Invoke-Scenario` applies these defaults before the body; individual cases may
then override them. It restores incoming values after success or failure.
Skipped scenarios do not touch the environment. Suite CLI options (including
`-TimeoutSeconds`) and unrelated environment settings remain unchanged; this
is test-harness ownership, not a change to production configuration.

The death fixture's third kill waits for `service_discovered` after the second
recovered controller reports online. The driver releases a DB-only fixture marker;
Lua polls it for at most 30 seconds. The scenario's overall 45-second deadline and
existing recovery/terminal assertions remain unchanged.

Independent scenarios still receive a fresh database and server process. The
database container remains healthy between them to avoid repeated MariaDB and
volume initialization. Scenarios that verify clean-shutdown persistence stop and
Expand Down
13 changes: 11 additions & 2 deletions scripts/bootstrap-client.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#Requires -Version 7.0

param(
[switch]$Force
)
Expand All @@ -7,7 +9,7 @@ $ErrorActionPreference = "Stop"
$projectRoot = Split-Path -Parent $PSScriptRoot
$clientRoot = Join-Path $projectRoot "client"
$thingsRoot = Join-Path $clientRoot "data\things\860"
$executable = Join-Path $clientRoot "otclient_gl_x64.exe"
$executable = Join-Path $clientRoot $(if ($IsLinux) { "otclient" } else { "otclient_gl_x64.exe" })
$tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) "angelion-client-$PID"

$expectedExecutableHash = "3054ec603454cc71536851da979d11a02546f0348473b1f2a931ae4c10bf6b55"
Expand All @@ -29,10 +31,17 @@ if (-not (Test-Path -LiteralPath $clientRoot)) {
throw "Client source directory not found: $clientRoot"
}

if (-not $IsWindows -and -not $IsLinux) {
throw "Client bootstrap supports Windows and Linux only."
}
if ($IsLinux -and -not (Test-Path -LiteralPath $executable -PathType Leaf)) {
throw "Build the Linux client at $executable first. Bootstrap installs assets, not a Linux executable."
}

New-Item -ItemType Directory -Path $tempRoot | Out-Null

try {
if ($Force -or -not (Test-ExpectedHash -Path $executable -Expected $expectedExecutableHash)) {
if ($IsWindows -and ($Force -or -not (Test-ExpectedHash -Path $executable -Expected $expectedExecutableHash))) {
if (-not (Get-Command gh -ErrorAction SilentlyContinue)) {
throw "GitHub CLI is required to access the private client runtime release."
}
Expand Down
Loading