Commit Graph
196 Commits
Author SHA1 Message Date
neckfire cc392238f7 docs(netcode): findings AES-GCM — le client ferme systématiquement (plaintext bClose) après notre réponse au Hello ; pistes restantes
Build Game Server / build (push) Successful in 15s
2026-07-16 23:58:13 +02:00
neckfire 9cb15ad9e2 fix(netcode): Incoming en sens inverse d'Outgoing (couche chiffrement externe retirée en premier)
Build Game Server / build (push) Successful in 22s
Avec >1 composant actif (stateless + AES), Incoming et Outgoing doivent être en
sens opposés. Les deux étaient en avant -> le stateless tentait de parser des octets
chiffrés. Incoming itère désormais en inverse.
2026-07-16 23:53:52 +02:00
neckfire 5c81fba79c feat(netcode): chiffrement AES-256-GCM keyé par la PSK (piste retenue)
Build Game Server / build (push) Successful in 21s
Le client n'entame pas de handshake DTLS et ferme si nos paquets post-ack sont en
clair. AesGcmHandlerComponent (System.Security.Cryptography.AesGcm, [IV12][ct][tag16]),
activé après l'EncryptionAck : ack en clair -> activation -> Challenge chiffré.
2026-07-16 23:47:00 +02:00
neckfire a3dc1e8543 docs(netcode): findings test live — client ferme après EncryptionAck (attend voie serveur chiffrée) + crash ConditionalCleanUp
Build Game Server / build (push) Successful in 15s
2026-07-16 23:38:10 +02:00
neckfire 7e34c61b2a test(dtls): EncryptionAck sans activer DTLS — le client continue le login EN CLAIR après l'ack (observé live)
Build Game Server / build (push) Successful in 21s
2026-07-16 23:33:33 +02:00
neckfire e397541418 debug(dtls): log en tête d'Incoming (état de la garde active/transport/failed)
Build Game Server / build (push) Successful in 22s
2026-07-16 23:28:37 +02:00
neckfire 3e47b0680c debug(dtls): logs Incoming/Outgoing (voir ce que reçoit le serveur après EncryptionAck)
Build Game Server / build (push) Successful in 21s
2026-07-16 23:24:34 +02:00
neckfire a21ac4d0e4 docs(netcode): mur DTLS franchi côté serveur + boucle d'itération live
Build Game Server / build (push) Successful in 14s
2026-07-16 23:07:43 +02:00
neckfire 36122578a3 feat(netcode): serveur DTLS-PSK (côté serveur) — franchit le mur du chiffrement
Build Game Server / build (push) Successful in 20s
Implémente le pendant serveur du DTLSHandlerComponent d'UE (la pile client est
[DTLS, Stateless]) : le client exige un DTLS en mode PSK dont l'identité = user_id.

- DtlsPskStore : table user_id -> PSK 32o, chargée depuis l'env (PROSPECT_DTLS_PSKS,
  rendu Vault) — aucune clé en dur ni commitée.
- DtlsPacketTransport : pont entre l'API bloquante DTLS de BouncyCastle et le modèle
  paquet-par-paquet du PacketHandler (handshake sur thread dédié).
- ProspectPskTlsServer : serveur DTLS-PSK BouncyCastle (DTLS 1.2, identité=user_id).
- DTLSHandlerComponent : composant du pipeline (Incoming déchiffre / Outgoing chiffre),
  activé au NMT_Hello portant un EncryptionToken.
- UWorld.NotifyControlMessage : sur NMT_Hello chiffré, si la PSK de l'identité est
  connue -> NMT_EncryptionAck + BeginHandshake ; sinon warning (comme avant).

Compile (0 erreur, BouncyCastle 2.4). ⚠️ Le framing exact DTLS-sur-PacketHandler et
le routage des records de handshake demandent une ITÉRATION EN LIVE contre le vrai
client (invalidable hors client).
2026-07-16 23:06:14 +02:00
neckfireandClaude Opus 4.8 f9c44a89b3 game-server: catch-all request capture for squad-invite RE
Build Game Server / build (push) Successful in 25s
Log every unrouted POST at Warning [CAPTURE UNROUTED] (reveals native PlayFab
endpoints the emulator doesn't implement, e.g. /Group/CreateGroup, /Lobby/*) and
every social/squad/invite/friend/matchmaking call (path or body) at Information
[CAPTURE SOCIAL]. Lets us pin the exact invite mechanism when reproduced in game.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 10:35:37 +02:00
neckfire 4ec54f894b docs(game-server): study PlayFab Groups/Party emulation for squad invites
Build Game Server / build (push) Successful in 18s
2026-07-16 08:28:58 +00:00
neckfire f7048ff173 Merge pull request 'chore(game-server): merge main (emulator fixes: contracts, shop, fortuna, friends, admin, presence)' (#16) from main into game-server
Build Game Server / build (push) Successful in 26s
2026-07-16 08:25:21 +00:00
neckfire 91bdde3797 Merge pull request 'friends: richer GetFriendList payload + logging' (#15) from preprod into main
Build & Deploy / build (push) Successful in 38s
2026-07-16 07:06:20 +00:00
neckfireandClaude Opus 4.8 d7821ebf1f friends: richer GetFriendList payload (field aliases) + log returned JSON
Build & Deploy / build (push) Successful in 53s
The friend tile rendered empty/offline because the UE client couldn't read our
best-guess response shape. Expose each friend's identity/name/steam/presence under
every plausible field name (camelCase + PlayFab PascalCase, flat + nested profile)
under both friends/Friends keys, and log the JSON so we can pin the exact shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:06:17 +02:00
neckfire e5ab911dfd Merge pull request 'fortuna: FortunaPass2_Rewards catalog (claimable tiers)' (#14) from preprod into main
Build & Deploy / build (push) Successful in 34s
2026-07-15 20:17:37 +00:00
neckfireandClaude Opus 4.8 81092b077e fortuna: add FortunaPass2_Rewards catalog so tiers can be claimed
Build & Deploy / build (push) Successful in 50s
The claim function only grants a tier when a FortunaPass2_Rewards TitleData catalog
maps its rewardId (Level_N, sent by the client) to an item/amount. That catalog was
absent (client-side DataTable) so claims granted nothing and stayed unclaimable.
Add a Level_1..Level_100 catalog (K-Marks scaling, Insurance every 5, Aurum every 10)
so players can actually collect Fortuna pass rewards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 22:17:34 +02:00
neckfire 042f806697 Merge pull request 'docs: rewrite main README (dev + CI + client switch tool)' (#13) from preprod into main
Build & Deploy / build (push) Successful in 32s
2026-07-15 11:54:39 +00:00
neckfireandClaude Opus 4.8 95541b6221 docs: rewrite README around our build/CI + client switch tool
Build & Deploy / build (push) Successful in 32s
Replace the upstream Windows/local-run README with a dev-focused one: how the
client redirection works (loader+agent+backend.txt), repo structure, building
and running the API in a container, the TLS cert SAN gotcha, the Gitea CI/CD
branch model (preprod:preprod / main:latest), the Prospect.Client.Config
switch+cert tool, and an up-to-date feature status.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:54:36 +02:00
neckfire a4c8cf5955 Merge pull request 'admin: fix empty tables (DT engine ordering)' (#12) from preprod into main
Build & Deploy / build (push) Successful in 33s
2026-07-15 09:52:00 +00:00
neckfireandClaude Opus 4.8 a8fca38038 admin: fix empty tables — define DT engine before page init scripts
Build & Deploy / build (push) Successful in 43s
The DT table engine lived in the page footer, after the per-page
<script>DT.init(...)</script> in the body, so DT was undefined when init ran
(ReferenceError, nothing rendered). Move the engine into a DtScript const
injected right after <main>, before any body content, so it is always defined
first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:51:58 +02:00
neckfire d1f3fd1125 Merge pull request 'admin: interactive tables (sort/filter/search/paging)' (#11) from preprod into main
Build & Deploy / build (push) Successful in 31s
2026-07-15 09:31:21 +00:00
neckfireandClaude Opus 4.8 b5c295f45c admin: interactive tables (sort/filter/search/paging) via client-side JS engine
Build & Deploy / build (push) Successful in 42s
Players and Catalog pages now fetch /admin/api/{players,catalog} JSON and render
a self-contained vanilla-JS data table: clickable column sort, live search,
dropdown filters (auth / category / rarity), page-size selector + prev/next
paging, result count. No CDN, works offline. Server-rendered dashboard,
player detail and titledata pages unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:30:22 +02:00
neckfire b05782d4e9 Merge pull request 'Admin: noms lisibles + catalogue (gameref)' (#10) from preprod into main
Build & Deploy / build (push) Successful in 29s
2026-07-15 09:19:14 +00:00
neckfireandClaude Opus 4.8 0b771c5447 feat(admin): resolve item/vanity ids to readable names + catalog browser
Build & Deploy / build (push) Successful in 42s
Bundle a compact id -> {name,rarity,category} lookup (Data/gameref.json, 1177 entries,
built from community-datamined TCF-Wiki data) and a GameRefService. The admin back-office
now shows readable names + coloured rarity for inventory items instead of raw ids, and a
new /admin/catalog page browses the known game catalog (searchable, by category).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:19:14 +02:00
neckfire d7c89327a7 Merge pull request 'Steam persona display names + admin backfill' (#9) from preprod into main
Build & Deploy / build (push) Successful in 29s
2026-07-15 08:25:53 +00:00
neckfireandClaude Opus 4.8 4614b135a1 feat: resolve player display names from Steam persona (Steam Web API)
Build & Deploy / build (push) Successful in 41s
Accounts were created as 'Unknown' and the client never sends a name, so the admin
panel / friends / squad showed no names. Add SteamWebApiService (GetPlayerSummaries),
wired into LoginWithSteam to backfill the DisplayName from the Steam persona when it's
still 'Unknown'. Admin gets a LAN-only /admin/backfill-names action (+ button) to
backfill existing accounts in one shot. Key read from config SteamWebApiKey (Vault ->
env), inert when unset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 10:25:52 +02:00
neckfire 03048b793d Merge pull request 'Back-office admin /admin (LAN-only)' (#8) from preprod into main
Build & Deploy / build (push) Successful in 32s
2026-07-15 07:56:34 +00:00
neckfireandClaude Opus 4.8 b8d023b02a feat(admin): read-only back-office at /admin (LAN-only)
Build & Deploy / build (push) Successful in 38s
Filament-style dark UI served directly by the API:
- Dashboard: player/entity/userdata/titledata counts + recent players
- /admin/players: searchable player list (name/id/steam)
- /admin/players/{id}: full player view — balance/faction/Fortuna cards, inventory
  table, and every UserData key pretty-printed (collapsible)
- /admin/titledata + /admin/titledata/{key}: browse in-memory title data
Restricted to LAN/loopback callers (RemoteIpAddress check) so it is never reachable
from the public internet even though :8443 is port-forwarded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:56:34 +02:00
neckfire 1d9cf795dd Merge pull request 'Audit léger: stats carrière, présence amis, AddGenericId' (#7) from preprod into main
Build & Deploy / build (push) Successful in 27s
2026-07-15 07:02:46 +00:00
neckfireandClaude Opus 4.8 0a68a97291 fix(audit léger): stats carrière, présence amis, AddGenericId
Build & Deploy / build (push) Successful in 43s
- GetPlayerStatisticsRequestsClient: renvoyait vide -> renvoie les stats demandées
  (0 pour l'instant, raid client-hosted non traçable) pour que l'écran carrière s'affiche.
- UpdatePlayerPresenceState: persiste {inMatch,lastSeenUtc}; GetFriendList lit la présence
  et calcule onlineState (online si vu <3min, 2=en raid) au lieu de 0 en dur.
- AddGenericId: persiste l'id de service lié (était un no-op).
(Free-loadout non touché: S3 only, pas de free-loadout en S2.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:02:45 +02:00
neckfire 7dbab1aab8 Merge pull request 'Audit fixes: Fortuna pass level, rename persist, loadout & craft-timer' (#6) from preprod into main
Build & Deploy / build (push) Successful in 26s
2026-07-15 00:57:11 +00:00
neckfireandClaude Opus 4.8 1b4c1b2606 fix(audit): Fortuna pass level, display-name persist, loadout & craft-timer queries
Build & Deploy / build (push) Successful in 39s
Audit of the emulator surface + 4 fixes:
- Fortuna pass level was frozen: nothing ever granted season XP. ClaimActiveContract
  now grants + persists FortunaPass{2,3}_SeasonXp (scaled to the contract's reputation)
  and returns the new total, so the pass level moves as you complete contracts.
- UpdateUserTitleDisplayName never persisted the rename (echoed back, lost on relog).
  Added DbUserService.UpdateDisplayNameAsync and call it.
- GetPlayerSets returned a blank loadout; now reads the persisted LOADOUT key.
- GetCraftingInProgressData was a stub; now returns the persisted CraftingTimer so an
  in-progress craft + remaining time survive a menu reopen/relog.

Remaining known gaps documented in Plane (need client data / multiplayer / out of scope).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 02:56:47 +02:00
neckfire 078d2f80be Merge pull request 'Fix critique: achats bloqués (sentinelle ContractLockPurchase None)' (#5) from preprod into main
Build & Deploy / build (push) Successful in 24s
2026-07-15 00:15:14 +00:00
neckfireandClaude Opus 4.8 14ab3267f4 fix(shop): don't treat ContractLockPurchase sentinel 'None' as a real lock
Build & Deploy / build (push) Successful in 34s
Regression from the contract-lock feature: the check gated on any non-empty
ContractLockPurchase, but the blueprint data uses the string "None" (not "")
for items with no lock (749/773 items). So virtually every item returned
'Item is locked' before the currency deduction -> players couldn't buy anything
(error shown, K-Marks unchanged). Skip the gate when the value is null/empty/"None".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 02:15:13 +02:00
neckfire c986a98471 Merge pull request 'Promotion: fix sauvegarde des missions en zone (raid client-hosted)' (#4) from preprod into main
Build & Deploy / build (push) Successful in 23s
2026-07-14 23:58:55 +00:00
neckfireandClaude Opus 4.8 dc34233e40 fix(contracts): persist raid-zone objective progress (client-hosted raids)
Build & Deploy / build (push) Successful in 36s
Symptom: missions done in raid zones weren't saved -> contracts stuck on
'Objective not met', no claim possible.

Root cause: client-hosted raids have no dedicated game server, so
UpdatePlayerActiveContracts is never called (confirmed: 0 calls in prod logs).
Only Kills and OwnNumOfItem objectives were handled; DeadDrop/VisitArea/
LootContainer had Progress[i]=0 forever -> unclaimable and not shown at station.

Fix: EYContractObjectiveType.IsRaidRuntime() groups the objectives that can only
be observed in a live raid (Kills/DeadDrop/VisitArea/LootContainer). Auto-credit
them on deploy (EnterMatchmakingMatch, persisted) so the station shows them
complete, and accept them in ClaimActiveContract. OwnNumOfItem still validated
against the real stash; FactionLevel/CompletedMission unchanged. Added a
defensive guard against a Progress array shorter than the objective list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 01:54:25 +02:00
neckfireandClaude Opus 4.8 f0343bc1ea gameserver: consolidate netcode R&D — remove diag spam, document DTLS-PSK wall
Build Game Server / build (push) Successful in 21s
- Keep the R3.5.0 header-bit fix (bunch alignment breakthrough)
- Remove per-packet [HS-RAW]/[HS-HDR]/[HS-BUNCH] diagnostics
- Document the DTLS-PSK encryption wall + packed-binary finding in NETCODE-RND.md
- UWorld NMT_Hello: honest comment on why the connection stops here

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 01:13:03 +02:00
neckfireandClaude Opus 4.8 ba9976c5b6 gameserver: skip encryption on Hello (try plaintext challenge)
Build Game Server / build (push) Successful in 19s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:54:01 +02:00
neckfireandClaude Opus 4.8 fb73038901 gameserver: consume Cycle's extra header bit -> bunches align (NMT_Hello parses)
Build Game Server / build (push) Successful in 22s
Bit-decoded real client packets: the header is 65 bits (not stock UE 4.27's 64).
One extra bit sits between the ack history and the packet-info payload; consuming
it realigns bHasPacketInfoPayload + jitter + bHasServerFrameTime, and the first
control-channel bunch parses (ChIndex 0, open, reliable = NMT_Hello).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:50:19 +02:00
neckfireandClaude Opus 4.8 629e8a4c31 gameserver: dump full packet bits (512) to decode NMT_Hello netver
Build Game Server / build (push) Successful in 18s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:29:11 +02:00
neckfireandClaude Opus 4.8 1691af4be6 gameserver: dump raw packet bit-stream for hand-decode
Build Game Server / build (push) Successful in 18s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:53:53 +02:00
neckfireandClaude Opus 4.8 b1386bef56 gameserver: bit-position diagnostics in ReceivedPacket (header + bunch flags)
Build Game Server / build (push) Successful in 17s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:41:39 +02:00
neckfireandClaude Opus 4.8 71aca5e3d0 fix(game-server): survive bad-packet exceptions in tick loop
Build Game Server / build (push) Successful in 17s
Wrap world.Tick in try/catch so an incompatible/misaligned client packet
logs an error and the server keeps running (stable R&D bench).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:30:12 +02:00
neckfireandClaude Opus 4.8 4d2280f8ab fix(game-server): don't crash on malformed bunch channel index
Build Game Server / build (push) Successful in 16s
Log + drop the packet (with net version) instead of throwing, so a
bit-alignment/version mismatch with the client doesn't kill the server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:26:53 +02:00
neckfireandClaude Opus 4.8 1caa46f846 fix(game-server): adopt client packet sequences on first packet
Build Game Server / build (push) Successful in 16s
The Cycle client does not derive its initial packet sequences from the
handshake cookie the way stock UE does (its acked server-seq isn't present
anywhere in the cookie), so the cookie-based InitSequence disagreed and
every post-handshake packet was rejected as out-of-order. On the first
packet, adopt the client's announced Seq/AckedSeq instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:21:49 +02:00
neckfireandClaude Opus 4.8 9092dda950 diag(game-server): log packet sequence mismatch (post-handshake)
Build Game Server / build (push) Successful in 21s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:08:52 +02:00
neckfireandClaude Opus 4.8 6ccad507a8 diag(game-server): log connectionless handshake (challenge/response)
Build Game Server / build (push) Successful in 16s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:00:19 +02:00
neckfireandClaude Opus 4.8 f4d1757840 diag(game-server): log stateless handshake packet size vs expected
Build Game Server / build (push) Successful in 15s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:52:52 +02:00
neckfireandClaude Opus 4.8 71a2c8a1e9 feat(deploy): optional dedicated-server travel via GAMESERVER_ADDRESS
Build & Deploy / build (push) Successful in 31s
When GAMESERVER_ADDRESS ("host:port") is set, EnterMatchmaking returns
that server (SingleplayerStation=false) and EnterMatchmakingMatch's
deploy signal carries its address, so the client travels to the real
game server instead of the client-hosted station. Unset = unchanged
(normal preprod). Lets a 2nd preprod run the same :preprod image and
differ only by this env var (dedicated-server test bench).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:41:20 +02:00
neckfireandClaude Opus 4.8 9749828af8 feat(game-server): scaffold dedicated server toward player join
Build Game Server / build (push) Successful in 17s
R&D branch for an authoritative Unreal game server (Prospect.Unreal).
The connection handshake already reaches PlayerController spawn; this
commit pushes it toward real players joining:

- Program.cs: target the Cycle station map + game mode (configurable via
  PROSPECT_MAP/GAMEMODE/PORT), proper host loop, and initialise a
  GameSession so NMT_Join -> Login no longer fails ("GameSession is null").
- UWorld.WelcomePlayer: send the world's real map/game mode to the client
  instead of the UE ThirdPerson template.
- Dockerfile.gameserver + .gitea/workflows/game-server.yml: build/push a
  separate image (the-cycle-game) on the game-server branch, ntfy notify.
- GAMESERVER.md: honest state + roadmap (player spawn + movement
  replication need live client testing; AI/loot intentionally deferred).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:30:09 +02:00