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>
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>
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>
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>
- RequestUsersPingRequest: the client sends ping=2147483648 (int.MaxValue+1)
for "no ping", which overflowed the int field and made every ping request
return 500. Widened to long.
- ClientsideFriendsImport: persist the imported Steam friend ids per user.
- GetFriendList: resolve those ids to the players that exist on this server
(batch lookup by Steam auth key) and return them as friends. Response
shape is best-effort pending in-game confirmation.
- DbUserService.FindManyAsync: batch-resolve auth keys to players.
First step of the station lobby (see friends on the server). Squad
formation / invite flow to follow once friends are visible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GetUserInventory returned every vanity in the Vanities title data as an
owned item, so the client showed all Aurum-shop cosmetics as already
owned. Return only the cosmetics the player actually owns: purchased
(VanityItems) plus currently equipped (CharacterVanity slots and the
GlobalVanity sprays / banner / emotes / drop pod), so the starter outfit
stays owned while the rest of the Aurum shop becomes purchasable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GetStoreItems now also serves the cosmetic stores the client queries
(AurumShop, and the DailyShop_/WeeklyShop_ rotations) from the Vanities
title data: every vanity with StoreData.Amount > 0 is offered for that
price in Aurum (AU). AurumShop lists the full priced catalogue (632
items); the daily/weekly ids show a deterministic rotating slice so they
stay stable within a period but change across periods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GetStoreItems was a stub returning an empty object, so every shop
(Korolev / ICA / Osiris / QuickShop vendors) showed up empty. Build the
store on the fly from the Blueprints title data: for the requested
StoreId, list every item that declares an ItemShopsCraftingData entry
for that store, converting its currency recipe ingredients into
VirtualCurrencyPrices (SoftCurrency->SC, Aurum->AU, InsuranceCurrency->IN).
Item-ingredient (crafting) entries are left to the crafting UI.
Adds the StoreId/CatalogVersion fields to the request and the
FGetStoreItemsResult / FStoreItem PlayFab response models.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CompleteInventoryUpdate: insured items lost in a raid (present in
ItemsToRemove) are recovered back to the stash and the insurance is
consumed. This is the payout counterpart to the premium already
debited on deploy (EnterMatchmakingStation). Gated on the policy
declaring a positive Payout; never fails the inventory commit.
- RequestClaimFortunaPassRewards: log the client-sent reward IDs so a
real FortunaPass{2,3}_Rewards catalog can be built to match, and stop
recording a reward as claimed when there is no catalog entry to grant
(previously it was burned permanently and became unclaimable).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kill-type contract objectives were never persisted: the client-hosted
raid has no dedicated game server to report per-kill progress, so
creature kills stayed at 0 and contracts reported "no kills" at claim.
- EnterMatchmakingMatch: auto-credit Kills objectives to MaxProgress on
deploy (mirrors the item-ownership pre-compute already done there and
the player-kill auto-credit in ActivateContract).
- ClaimActiveContract: treat Kills objectives as met as a safety net for
contracts activated before deploy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- RequestPlayerContracts: renvoie les vrais contrats actifs + job boards du joueur
(peuplement initial des boards vides) au lieu d'une liste hardcodée.
- RequestFactionProgression: renvoie la réputation réelle des 3 factions (au lieu de 0).
- StartTechTreeNodeUpgradeClient: dependency check PlayerQuarterLevelRequired.
- PurchaseWeaponShopItem: critère de déblocage par contrat (UnlockData.ContractLockPurchase).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le code sélectionne la saison via #if SEASON_2_RELEASE ; sans cette config le
build échoue (#error Unsupported build type). Build validé en local.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>