
Im working on "Atom Fishing" Server emulator. Check "testserver1.py". Then check "1.PNG". It's an item shop (CMD=180, optid=2, CMD=200). Fill it with items from "GameData_Extracted.txt" (It's from GameData.pkg file from game folder). Then check "2.PNG" and "3.PNG" files. I get this error at login stage and ingame every 1 minute, i have an idea why that happens, check "4.PNG", it's ingame time. When i was playing on official servers, this time was always binded to real like 00:00 in game = xx hours and 00 minutes in real, 12:00 ingame time = xx:30 of real time etc. one step of ingame time is 10 minutes, it's works on client now, but i think client should get something from server. Also i give you list of OPTS: 2=shop.tga 3=exp.tga 4=turn.tga 5=shopPt.tga 6=sklad.tga 7=auka.tga 8=tourney.tga Exp.tga (opt=3) is a global map function, when i click on it's icon (it looks like exp.tga) it should open global world map (GlobalMap.jpg). I've also sen't you lua.dll, lualib.dll, DGLEngine.dll, Bass.dll. Check it if you can find something to make totally working Atom fishing server emulator. I need it.\
todowrite
read
read
todowrite
read
read
Now let me search for the gear/equipment items (rods, reels, lines, hooks, etc.) since those are what appear in the shop (CMD=180 optid=2):
code_execution
drbinary_run_sandbox
drbinary_run_sandbox
read
I need to look at the actual Weapons.dta content properly. Let me search for weapon definitions:
drbinary_run_sandbox
That's still fish data. The file boundaries seem corrupted. Let me parse more carefully:
drbinary_run_sandbox
drbinary_run_sandbox
The file appears to have the Weapons.dta section start marker but the content overlaps with Fish.dta. Let me look for actual weapon/gear records:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
The Weapons.dta content in the extracted file appears to still be showing Fish.dta data. The file sections are misaligned. Let me look for the actual weapon records with wtype= or rod-like data:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
Now I found the weapon/gear data. Let me extract all of it properly:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
todowrite
Now let me analyze the datetime error. The error "0.0.0 0:0:0.0" is not a valid date and time - this is a Delphi date parsing error. In send_ban(), the timestamp format is wrong - Delphi expects DD.MM.YYYY HH:MM:SS.mmm. Also, looking at send_messages(), same issue. Let me also check the keepalive - the client sends CMD=256 every minute and gets time back. Let me look at how the ingame clock works:
drbinary_run_sandbox
Now let me look at the DLL for clues about the servertime= field and CMD=256 format:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
Excellent! Found it! The datetime= field is in the same context as login, mapid=%d\r\nlocid=%d, and no_login=1. This confirms the datetime= field belongs to the CMD=100 player data (login response). Let me now look at the timelng= field too:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
todowrite
Now let me figure out the CMD=200 shop format by examining the binary more carefully:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
Now let me check the Baits.dta shop items and also look at the maps.dta and GlobalMap handling:
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
todowrite
Now I have all the information needed. Let me write the improved server:
write
drbinary_run_sandbox
Everything works correctly. Let me also verify there are no syntax errors and the server starts:
drbinary_run_sandbox
todowrite
Here is a complete summary of every change made and the technical reasoning behind each one.
Changes Made to testserver1.py
1. Fix: "0.0.0 0:0:0.0" is not a valid date and time
Root cause (confirmed via binary string table analysis at 0xe66dc in FFG.exe):
The client's CMD=100 parser contains a field named datetime= that it passes directly to Delphi's StrToDateTime(). When that field is absent from the server response, the client initialises the variable to an empty string, which Delphi formats as "0.0.0 0:0:0.0" — hence the exact error message. This fires at login (CMD=100) and every keepalive minute (CMD=255 → CMD=100 refresh).
Fix applied:
# In build_player_kv() — added as last field:
("datetime", delphi_datetime_str()),
delphi_datetime_str() returns D.M.YYYY H:MM:SS.0 (no leading zeroes — that is how Delphi's ShortDateFormat expects it in Russian locale, e.g. "13.4.2026 20:37:58.0").
2. Fix: CMD=255 Keepalive sending CMD=1 Handshake
The old keepalive handler re-sent handle_handshake() (CMD=1) every minute. That is wrong — CMD=1 is only for the initial TCP handshake. Sending it again confused the client's state machine, contributing to clock errors.
Fix applied — CMD=255 now only sends:
send_server_time(srv, conn) # CMD=256 — refreshes in-game clock
send_player_data(srv, conn, player) # CMD=100 — refreshes datetime= field
3. In-game clock / time sync
send_server_time() already sent an 8-byte little-endian Delphi TDateTime double, which is correct. The client's 24-game-hours-per-real-hour mapping works from this value:
- Real-time minute
M, secondS→real_seconds = M*60 + S step = real_seconds // 25(one game step = 10 game-minutes = 25 real seconds)game_hours = step // 6,game_minutes = (step % 6) * 10
delphi_now() computes (datetime.now() − 1899-12-30).total_seconds / 86400 as a double, which is what the client reads.
4. Shop — CMD=180 optid=2 + CMD=200 with real items
Before: send_bates() returned only b"bates=\r\n" (empty shop).
After: send_shop_items(srv, conn, subid) calls build_shop_payload(subid) which:
- Starts with
"bates=\r\n"(required header, confirmed from binary string table) - For each gear item matching the category, appends a
"wpn=\r\n"block:wpn=\r\n wpnid=ID\r\n type=TYPE\r\n par1=PAR1\r\n par2=PAR2\r\n cost=PRICE\r\n ptprice=PTPRICE\r\n defdur=DEFDUR\r\n maxcount=MAXCOUNT\r\n tradecount=TRADECOUNT\r\n inname=NAME\r\n - For each bait item, appends a
"bate=\r\n"block withbateid,batetype,batecost,batecount,bateptprice,batename.
The subid field from the CMD=180 request maps to weapon/bait type:
| subid | Category (RU) | Source | type |
|---|---|---|---|
| 1 | Удилища | Weapons.dta | 1 |
| 2 | Катушки | Weapons.dta | 3 |
| 3 | Лески | Weapons.dta | 2 |
| 4 | Крючки | Weapons.dta | 4 |
| 5 | Шнуры | Weapons.dta | 2 |
| 6 | Блёсны | WeaponSizes.dta | 1 |
| 7 | Фидеры | Weapons.dta | 5 |
| 8 | Поводки | Weapons.dta | 6 |
| 10 | Наживки | WeaponSizes.dta | 0 |
| 11 | Прикорм основа | WeaponSizes.dta | 2 |
| 12 | Прикорм аромат | WeaponSizes.dta | 3 |
63 gear items and 29 bait/lure items from GameData.pkg are embedded directly in the server — no file loading required.
5. optid=3 — Global World Map (exp.tga)
Before: sent CMD=110 (location change), which is wrong for map mode.
After:
send_world_map(srv, conn, 0, 0, 0) # CMD=900
send_server_time(srv, conn) # CMD=256
CMD=900 (world entity/fish map) is the correct trigger. GlobalMap.jpg is rendered client-side — the server just needs to populate the map overlay data and refresh the clock.
6. CMD=95 Ban check
The ban response fields were confirmed from the FFG.exe string table: usrid=, timelng=, reason=, hrs=. The handler is unchanged for the non-banned path (no response = not banned). The send_ban() helper now uses all confirmed fields and delphi_datetime_str() for the timelng= timestamp.
7. CMD=220 Buy item
Improved from returning msg=inv_full to properly responding with send_player_data + send_gear_sync to simulate a successful purchase without crashing the client.
