# Debugging and performance

> Why a tick is slow, why a command didn't take effect, why the game isn't moving. Troubleshoot by symptom, then confirm with the bundled live verification scripts.

Source: https://war3ai.com/en/docs/debugging/

## Read the receipt

Every command's receipt is your first-hand clue:

```python
r = g.cast(hero, "blizzard", x=tx, y=ty)
if not r:
    print(r.reason, r.verdict)     # rejected（…） and the reason code
print(r.exec_us, r.engine_us)      # microseconds this command took on the game thread / how much of that the engine's order function itself took
```

Normally a command takes anywhere from a few to a few hundred microseconds on the game thread. After a batch block ends, `g.last_receipts` holds the receipt for each command in that batch.

## Watch it in the game

```python
g.say(unit, "Fall back")     # a chat bubble pops up over the unit (doesn't affect the game)
g.message("Going creeping")  # prints a line in the bottom-left message area (visible only on this machine)
```

Anything you `print` shows up in the terminal running the bot. Printing the key decisions of each tick, together with speech bubbles, is much faster than reading code.

## A tick is slow

First check whether it's one of these:

| Cause | Fix |
|---|---|
| Sending commands one at a time, each waiting a frame | Wrap them in `with g.batch():`, so dozens of commands wait only once |
| Calling `g.visible()` / `g.can_do()` one by one (each goes through the fast lane and waits a frame) | For visibility, use `u.visible_to()` from the snapshot; for feasibility, ask in one batch with `g.can_do_many([...])` |
| Calling `sleep` or waiting inside `on_tick` | Note the game time and check again on a later tick |
| Recomputing something expensive every tick (pathfinding, full-map scans) | Cache the result and recompute every few ticks. `g.grid()` has a built-in 2-second cache, and `g.stats()` caches tech levels for 5 seconds |

## The game isn't moving / the bot never gets into the game

| Symptom | Most likely cause |
|---|---|
| Stuck on "waiting for game" | Wrong instance number; or the game window is **minimized**, and the game simulation stops while it's minimized (the clock doesn't advance) |
| The game is running, but the bot's commands do nothing | Commanding someone else's units (receipt `not_owner`); or the bot connected as an observer (`forbidden`) |
| Commands come back `held` | The unit is held by a higher-priority layer (the reference brain's reflex layer, or a manual command from the console), so the command wasn't sent |
| Commands still go through while paused | That's normal: when paused, the engine clock stops, but event dispatch keeps running and commands execute as usual |

## Connect and check status

```bash
python -m openwar3 status --inst 5
```

This prints the connection status: game PID, world publish interval and per-capture time, fast lane counters, whether a game is in progress, unit count, and the game clock.

## Live verification scripts

Start a test instance and check, item by item, that the SDK's capabilities work on your machine:

```bash
python tools/sdk_live_check.py --inst 20                 # everything
python tools/sdk_live_check.py --inst 20 --only prod     # check just one section
```

Sections: batching, time, production, queued commands, combat stats, pathfinding, fair mode. Each section issues commands in a real game, reads back the effects, and prints the number of passes.

Offline tests don't need the game running:

```bash
python tools/run_tests.py
```

## Common "looks like a bug" cases

- **The build receipt says accepted, but no foundation ever appears**: the engine accepts a spot in a forest immediately too; it only fails when the worker gets there. Use `build_near`, which tracks the attempt and blacklists failed spots for a while.
- **The spell receipt says accepted, but the spell never went off**: it was interrupted, or the caster was out of mana. On the tick after casting, check whether `g.cooldown()` has started.
- **The attack order was accepted, but the soldiers attack something else**: to attack a specific target, use `g.attack(soldier, enemy)` (right-click semantics). The raw attack order on a target only changes the order without recording the target, so the units go after something else nearby.
- **The worker count doesn't add up**: workers inside a gold mine aren't in the snapshot.
- **A dead hero can't be trained**: heroes are unique, so use `g.revive(altar)`. Reviving costs food, and a hero can only be revived about 3 game seconds after it dies.
