> For the complete documentation index, see [llms.txt](https://axiomemu.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://axiomemu.gitbook.io/docs/data/hackpacketcheck.md).

# HackPacketCheck.txt

Per-opcode anti-cheat packet rules. For each incoming packet, the server looks up the row keyed by opcode (and optional subcode), validates the encryption flag, then applies a two-threshold rate limiter: log-and-reject at `MinCount`, disconnect at `MaxCount`.

## File Location

```
Data/Hack/HackPacketCheck.txt
```

Loaded once at GameServer startup. Changes require a restart.

{% hint style="info" %}
The `Data/Hack/` folder also ships `Dec1.dat` and `Enc2.dat` binary encryption blobs. Those are part of the packet encryption scheme used by the `Encrypt=1` check here; do not edit them.
{% endhint %}

## Format

Plain text, whitespace-delimited, one packet rule per row. Comment lines start with `//`. File ends with `end`.

```
//Index   Value   Encrypt   MaxDelay   MinCount   MaxCount   //Comment
0         *       0         2000       3          5          //Chat message - anti spam
19        *       0         150        25         40         //Skill attack
```

## Columns

| Column     | Type             | Effect                                                                                                                                                                                                     |
| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Index`    | int 0-255        | Packet opcode (also used for extended opcode headers like `0xF1`, `0xF3`, etc.)                                                                                                                            |
| `Value`    | int or `*`       | Subcode filter. `*` = applies to all subcodes, number = match that subcode only. You may define a wildcard row plus specific subcode rows for the same opcode; the specific row wins for matching subcodes |
| `Encrypt`  | `0`, `1`, or `*` | `0` = packet must arrive unencrypted, `1` = packet must arrive with valid encryption, `*` = no encryption check                                                                                            |
| `MaxDelay` | int ms           | Rate-limiting window length. `0` disables the rate limiter for this row                                                                                                                                    |
| `MinCount` | int              | Log-and-reject threshold. The `MinCount+1`-th packet in the window is dropped and logged. `0` disables this check                                                                                          |
| `MaxCount` | int              | Disconnect threshold. The `MaxCount+1`-th packet in the window drops the connection. `0` disables this check                                                                                               |

{% hint style="warning" %}
`Encrypt=0` is not the same as "no encryption check". A row with `Encrypt=0` rejects any packet that arrives encrypted. For no check at all, use `Encrypt=*`.
{% endhint %}

## Rate-Limit Math

For each packet arrival:

1. If the opcode has no row in this file, log an "unknown packet" hack entry and disconnect.
2. If `Encrypt` is `0` or `1` and the packet's actual encryption does not match, log and disconnect.
3. Serial check (always on, independent of this file): if the packet's serial is invalid, log and disconnect.
4. If the current time is past the window (`MaxDelay` ms since the first packet of the window), reset the counter and timestamp.
5. Increment the counter. If it now exceeds `MinCount`, log and reject the packet. If it also exceeds `MaxCount`, additionally disconnect.
6. Otherwise accept the packet.

Example: `Index=0, MaxDelay=2000, MinCount=3, MaxCount=5` accepts the first 3 chat packets per 2-second window, drops the 4th and 5th with a hack log entry, and disconnects on the 6th.

## Packet Categories

The shipped file groups opcodes by function. Categories are cosmetic comments only; the engine treats every row independently.

| Range                  | Category                                                       |
| ---------------------- | -------------------------------------------------------------- |
| `0x00-0x0F`            | Login / character                                              |
| `0x10-0x1E`            | Movement / action (combat `0x13-0x1A` has the tightest limits) |
| `0x1F-0x2F`            | Item / inventory                                               |
| `0x30-0x3F`            | NPC / shop                                                     |
| `0x40-0x4F`            | Party / guild                                                  |
| `0x50-0x5F`            | Trade                                                          |
| `0x80-0x8F`            | Storage                                                        |
| `0xC0-0xCF`            | Chat                                                           |
| `0xEC`, `0xF1`, `0xF3` | Extended / GDS / system handlers                               |

## Examples

{% tabs %}
{% tab title="Anti-spam chat" %}

```
0         *       0         2000       3          5          //Chat message
```

Chat allows 3 messages per 2-second window. The 4th and 5th are dropped with a hack log entry. The 6th disconnects.
{% endtab %}

{% tab title="Combat skill limiter" %}

```
19        *       0         150        25         40         //Skill attack
```

Allows up to 25 skill attacks per 150 ms window. Packets 26-40 are dropped with a log entry. Packet 41 drops the connection.
{% endtab %}

{% tab title="Encryption-only check" %}

```
3         *       1         0          0          0          //Character create
```

No rate limit. Rejects any character-create packet that did not arrive with valid encryption.
{% endtab %}

{% tab title="Wildcard plus specific subcode" %}

```
77        *       0         0          0          0          //Default sub
77        0       1         0          0          0          //Sub 0 requires encryption
77        1       1         0          0          0          //Sub 1 requires encryption
```

Subcodes 0 and 1 must be encrypted. All other subcodes of opcode 77 pass with no encryption check.
{% endtab %}

{% tab title="No-check row" %}

```
243       *       *         0          0          0          //0xF3 Special handler
```

`Encrypt=*` skips the encryption check. `MaxDelay=0` disables the rate limiter. Packet accepted unconditionally (serial check still applies).
{% endtab %}
{% endtabs %}

## Relationship to Common.dat Hack Settings

[Common.dat](/docs/gameserver/common-dat.md) Hack Settings govern **behavioral** anti-cheat (attack speed reporting, potion spam tracking, combo timing, serial seeds). This file governs **packet-level** rate limits and the encryption/plaintext contract.

Movement speed hacking is handled by always-on detectors (not configurable via either file).

## Cross-File Dependencies

| Value                 | Related in                                                                                                            |
| --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `Encrypt=1` rows      | Use the encryption blobs at `Data/Hack/Dec1.dat` and `Enc2.dat`                                                       |
| Hack log lines        | Gated by [Common.dat](/docs/gameserver/common-dat.md) `WriteHackLog`                                                  |
| Behavioral anti-cheat | [Common.dat](/docs/gameserver/common-dat.md) Hack Settings                                                            |
| Chat spam filters     | [Filter.txt](/docs/data/experiencetable/filter.md) complements the opcode-0 rate limiter by blocking disallowed words |

## Common Issues

* **Legit players randomly disconnected during combat** - `MaxCount` too low on opcode 19 (skill attack) or 20 (normal attack); raise in small increments.
* **All packets for an opcode rejected after adding a row** - row uses `Encrypt=0` while the client sends that opcode encrypted, or vice versa. Set `Encrypt=*` to disable the check.
* **Opcode disconnects immediately with "unknown" in hack log** - opcode has no row at all. Every handled opcode must have at least one entry (wildcard row is fine).
* **Chat spam not blocked** - opcode 0 row has `MaxDelay=0` or both count thresholds too high. Combine with [Filter.txt](/docs/data/experiencetable/filter.md) for word-level filtering.
* **`Encrypt=1` packets always rejected** - encryption blobs missing or corrupt in `Data/Hack/`, or the client build does not encrypt this opcode.
* **Warnings flood the log with no useful signal** - `MinCount` set too low; raise it to reduce noise while keeping `MaxCount` at the disconnect threshold.
* **File edits ignored** - a row has fewer than 6 columns; the parser aborts on the malformed row (error popup at startup). Check the server console for the script error line number.
* **Different players hit limits at different times** - rate window is per-player, measured from their own most recent window reset. There are no shared/global limits in this file.
