Im making Atom fishing server emulator. Check my last_server.py. The CMD= packets there are wrong. i need you to decompile FFG.exe and find all "CMD=" packets and make full list. also Check CMD=180, it has optids 2-8. CMD=220 opens world map (optid=3), CMD=900 opens Storage (склад), CMD=172 is not for perks, it's for прикорм. CMD=200 opens shop. The shop contents storaged in GameData.pkg. Find all other CMD's and optids and make full list with full structure and functionality. FFG.exe (it's Client.exe).
continue
drbinary_run_sandbox
read
drbinary_run_sandbox
read
read
drbinary_run_sandbox
read
read
find_suspicious_functions
task
read
read
read
read
todowrite
im sure you. ive made parsing test of all "CMD=" 220 is for opening world map. i tried it in client as an answer for 180 optid 3. It's opens world map. 900 opens storage. 200 for shop. 172 opens создание прикорма. I test it in game.
task
drbinary_run_sandbox
read
Now let me read the CMD=180 handler to find the optid values:
read
read
read
todowrite
Based on my analysis of the FFG.exe decompiled code, here's the complete CMD= packet documentation:
Complete CMD= Packet List from FFG.exe Analysis
Main Command Dispatcher (at 0x5170e4)
Critical Corrections for your server:
CMD=180 optid Responses (from 0x4e6464):
| optid | Correct Response CMD | Purpose |
|---|---|---|
| 2 | CMD=200 | Bait/Weapon selection |
| 3 | CMD=220 | World Map (глобальная карта) |
| 4 | CMD=402 + CMD=950 | Tournaments/Rating |
| 5 | CMD=601 | Premium Shop (премиум магазин) |
| 6 | CMD=120 | Storage opens inventory |
| 7 | ? | Unknown |
| 8 | ? | Unknown |
Complete CMD= List:
| CMD | Hex | Handler | Purpose |
|---|---|---|---|
| 1 | 0x1 | Built-in | Handshake |
| 2 | 0x2 | FUN_0055eb34 | Server announcement |
| 10 | 0xa | FUN_004d99d8 | Game event |
| 11 | 0xb | FUN_004d81a4 | Chat |
| 14-22 | 0xe-0x16 | Various | Inventory/Entity operations |
| 50 | 0x32 | FUN_004c7ab0 | Message display |
| 90 | 0x5a | FUN_0055fc34 | Registration |
| 95 | 0x5f | FUN_0055fc34 | Ban/Kick |
| 100 | 0x64 | FUN_0054bbe8 | Login response |
| 101 | 0x65 | FUN_0054bff0 | Location spots |
| 102 | 0x66 | FUN_0055accc | Fanfare |
| 103 | 0x67 | FUN_004f7188 | Level-up dialog |
| 104 | 0x68 | FUN_004f7998 | Sound effect |
| 105 | 0x69 | FUN_0054c26c | INTURN counter |
| 106 | 0x6a | FUN_0054cc50 | Camera update |
| 110 | 0x6e | FUN_0053f8f8 | Location change |
| 114 | 0x72 | Built-in | Weather data |
| 115 | 0x73 | Built-in | Weather update |
| 119 | 0x77 | Built-in | Slot index |
| 120 | 0x78 | FUN_0054ca20 | Inventory sync |
| 130 | 0x82 | FUN_00539f2c | Disconnect/reset |
| 132-134 | 0x84-0x86 | Various | UI operations |
| 140 | 0x8c | FUN_005391e4 | Sell result |
| 148 | 0x94 | FUN_004eb1d0 | - |
| 160 | 0xa0 | FUN_0054d254 | Entity sync |
| 165 | 0xa5 | FUN_0054cee0 | Bait sync |
| 172 | 0xac | Built-in | Bait creation (прикорм) |
| 173 | 0xad | FUN_0054b4f0 | Bait update |
| 200 | 0xc8 | FUN_004f694c | Shop (магазин) |
| 210 | 0xd2 | FUN_004f31e8 | Craft/Upgrade |
| 220 | 0xdc | FUN_004eaf08 | World Map |
| 230 | 0xe6 | FUN_004ea3e4 | - |
| 240 | 0xf0 | FUN_004f1bac | Player list |
| 246 | 0xf6 | FUN_004f2ef8 | Transmute check |
| 255 | 0xff | Handshake | Keepalive |
| 256 | 0x100 | FUN_004f999c | Server datetime |
| 260 | 0x104 | FUN_00535a38 | Tournament win |
| 300 | 0x12c | FUN_004fa38c | Race list |
| 301 | 0x12d | FUN_004f999c | Bulk ID list |
| 305-306 | 0x131-132 | Various | Race results |
| 400 | 0x190 | FUN_0052835c | Special dispatch |
| 402 | 0x192 | FUN_00526fd0 | Tournament standings |
| 420 | 0x1a4 | FUN_00525a28 | Race message |
| 430 | 0x1ae | FUN_005259dc | Race update |
| 460 | 0x1cc | FUN_00525990 | Race finish |
| 470 | 0x1d6 | FUN_00525944 | Race round |
| 510 | 0x1fe | Built-in | Audio on/off |
| 520 | 0x208 | FUN_004d38d8 | Shutup.ogg |
| 550 | 0x226 | Built-in | Error response |
| 600-606 | 0x258-0x25e | Various | Shop categories |
| 610 | 0x262 | FUN_00521294 | - |
| 615 | 0x267 | FUN_00520eac | - |
| 620 | 0x26c | FUN_00520f78 | - |
| 640 | 0x280 | FUN_0051fc30 | - |
| 650 | 0x28a | FUN_0052162c | - |
| 660 | 0x294 | FUN_00521958 | - |
| 674 | 0x29e | FUN_00522b94 | - |
| 680 | 0x2a8 | FUN_00522278 | - |
| 690 | 0x2b2 | FUN_00521958 | Quests |
| 705 | 0x2c1 | FUN_0054ccd8 | Trophies |
| 713 | 0x2c9 | FUN_0054c3c0 | - |
| 720 | 0x2d0 | FUN_0054c3c0 | - |
| 721 | 0x2d1 | FUN_0054c3c0 | Perks |
| 800 | 0x320 | FUN_00543664 | Private messages |
| 801 | 0x321 | FUN_00543538 | Achievement |
| 805 | 0x325 | FUN_005445cc | Fish record |
| 900 | 0x384 | FUN_004f2888 | Storage (склад) |
| 930 | 0x3a2 | FUN_0054c830 | World records |
| 940 | 0x3a7 | FUN_00535e84 | Statistics |
| 950 | 0x3b6 | FUN_004d96e8 | Top 20 |
| 953 | 0x3b9 | FUN_004d96e8 | List dialog |
| 955 | 0x3ba | FUN_005410d0 | Rating display |
| 1000 | 0x3e8 | FUN_004d38d8 | Reset/Clear |
| 1002 | 0x3ea | Built-in | Error |
| 1015 | 0x3f7 | Built-in | Error |
| 1200 | 0x4b0 | FUN_0052feb4 | - |
| 1202 | 0x4b2 | FUN_0052fc5c | - |
| 1266 | 0x4f2 | FUN_0052f8d4 | - |
| 1270 | 0x4f6 | FUN_0052fb84 | - |
| 1271 | 0x4f7 | FUN_0052faf8 | - |
| 1280 | 0x500 | FUN_00530eec | - |
| 1282 | 0x502 | FUN_0052fc20 | - |
| 1290 | 0x50a | FUN_005303a8 | - |
| 1300 | 0x514 | Built-in | Error |
| 2000 | 0x7d0 | FUN_00548d70 | Artel/Guild |
| 2009 | 0x7d9 | FUN_00547b0c | - |
| 2014 | 0x7de | FUN_0054850c | - |
| 2030 | 0x7ee | Built-in | Artel remove |
| 2040 | 0x7f8 | Built-in | Artel operations |
| 2055 | 0x807 | Built-in | - |
| 2060 | 0x80c | FUN_0054a2cc | - |
| 2065 | 0x811 | FUN_005491e4 | - |
Corrected CMD=180 Handler:
def handle_cmd180(srv, conn, payload):
kv = parse_kv(payload)
optid = int(kv.get("optid", "0"))
print(f" [OPT] CMD=180 optid={optid}")
if optid == 2:
# Bait/Weapons - CMD=200 response
print(" [OPT] → Bait/Weapons → CMD=200")
srv.send_pkt(conn, cmd=200, payload=b"bates=\r\n", flags=0)
elif optid == 3:
# World Map - CMD=220 response
print(" [OPT] → World Map → CMD=220")
srv.send_pkt(conn, cmd=220, payload=b"", flags=0)
elif optid == 4:
# Tournaments - CMD=402 + CMD=950
print(" [OPT] → Tournaments → CMD=402 + CMD=950")
srv.send_pkt(conn, cmd=402, payload=b"", flags=0)
srv.send_pkt(conn, cmd=950, payload=b"", flags=1)
elif optid == 5:
# Premium Shop - CMD=601
print(" [OPT] → Premium Shop → CMD=601")
srv.send_pkt(conn, cmd=601, payload=b"", flags=1)
elif optid == 6:
# Storage - CMD=120 (inventory sync)
print(" [OPT] → Storage → CMD=120")
send_cmd120(srv, conn)
elif optid == 7:
print(" [OPT] → optid=7 (unknown)")
elif optid == 8:
print(" [OPT] → optid=8 (unknown)")
Data Structures:
CMD=900 (Storage) reads binary:
// Header:
uint32 storage_id
uint32 field1
uint32 field2
// Items loop:
while (has_data) {
uint32 item_id
uint32 field_30, field_34, field_38
uint32 field_40, field_3c, field_1c, field_20
bool field_68, field_69
}
CMD=200 (Shop) reads:
// Items loop:
while (has_data) {
uint32 item_type
uint64 price (field_30/34)
uint32 field_40, field_3c, field_1c, field_20
bool field_68, field_69
}
The main corrections needed in your last_server.py:
- CMD=180 optid=3 should send CMD=220 (not CMD=110)
- CMD=172 is for bait durability, not perks
- CMD=721 is the actual perks command
