From 310e2bf7e934aab1d3ba774788248a9452dc1f4f Mon Sep 17 00:00:00 2001 From: neckfire Date: Tue, 14 Jul 2026 14:18:44 +0200 Subject: [PATCH 1/4] feat(social): friends list resolution + fix ping request 500 - 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 --- .../Functions/ClientsideFriendsImport.cs | 25 ++++++- .../CloudScript/Functions/GetFriendList.cs | 70 +++++++++++++++++-- .../Functions/RequestUsersPingRequest.cs | 2 +- .../Services/Database/DbUserService.cs | 16 ++++- 4 files changed, 102 insertions(+), 11 deletions(-) diff --git a/src/Prospect.Server.Api/Services/CloudScript/Functions/ClientsideFriendsImport.cs b/src/Prospect.Server.Api/Services/CloudScript/Functions/ClientsideFriendsImport.cs index ff45d57..68e83b6 100644 --- a/src/Prospect.Server.Api/Services/CloudScript/Functions/ClientsideFriendsImport.cs +++ b/src/Prospect.Server.Api/Services/CloudScript/Functions/ClientsideFriendsImport.cs @@ -1,5 +1,8 @@ +using System.Text.Json; using System.Text.Json.Serialization; +using Prospect.Server.Api.Services.Auth.Extensions; using Prospect.Server.Api.Services.CloudScript; +using Prospect.Server.Api.Services.UserData; public class ClientsideFriendsImportRequest { @@ -18,15 +21,33 @@ public class ClientsideFriendsImportResponse public class ClientsideFriendsImportFunction : ICloudScriptFunction { private readonly ILogger _logger; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly UserDataService _userDataService; - public ClientsideFriendsImportFunction(ILogger logger) + public ClientsideFriendsImportFunction(ILogger logger, IHttpContextAccessor httpContextAccessor, UserDataService userDataService) { _logger = logger; + _httpContextAccessor = httpContextAccessor; + _userDataService = userDataService; } public async Task ExecuteAsync(ClientsideFriendsImportRequest request) { - _logger.LogInformation("Processing friends import"); + var context = _httpContextAccessor.HttpContext; + if (context == null) + { + throw new CloudScriptException("CloudScript was not called within a http request"); + } + var userId = context.User.FindAuthUserId(); + + // Persist the platform friend ids the client imports (Steam ids) so GetFriendList + // can later resolve which of them actually play on this server. + var ids = request.UserIDs ?? Array.Empty(); + await _userDataService.UpdateAsync(userId, userId, new Dictionary + { + ["ImportedSteamFriends"] = JsonSerializer.Serialize(ids), + }); + _logger.LogInformation("Imported {Count} {Platform} friend id(s) for user {User}", ids.Length, request.Platform, userId); return new ClientsideFriendsImportResponse{}; } diff --git a/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs b/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs index 34d6d78..0bc51b0 100644 --- a/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs +++ b/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs @@ -1,15 +1,71 @@ -using Prospect.Server.Api.Services.CloudScript.Models; +using System.Text.Json; +using Prospect.Server.Api.Services.Auth.Extensions; +using Prospect.Server.Api.Services.CloudScript.Models; +using Prospect.Server.Api.Services.Database; +using Prospect.Server.Api.Services.Database.Models; +using Prospect.Server.Api.Services.UserData; namespace Prospect.Server.Api.Services.CloudScript.Functions; [CloudScriptFunction("GetFriendList")] public class GetFriendList : ICloudScriptFunction { - public Task ExecuteAsync(FYBaseSocialRequest request) + private readonly ILogger _logger; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly UserDataService _userDataService; + private readonly DbUserService _userService; + + public GetFriendList(ILogger logger, IHttpContextAccessor httpContextAccessor, UserDataService userDataService, DbUserService userService) { - return Task.FromResult(new - { - - }); + _logger = logger; + _httpContextAccessor = httpContextAccessor; + _userDataService = userDataService; + _userService = userService; } -} \ No newline at end of file + + public async Task ExecuteAsync(FYBaseSocialRequest request) + { + var context = _httpContextAccessor.HttpContext; + if (context == null) + { + throw new CloudScriptException("CloudScript was not called within a http request"); + } + var userId = context.User.FindAuthUserId(); + + // Resolve the imported Steam friends (persisted by ClientsideFriendsImport) to the + // players that actually exist on this server. It's a private server, so only friends + // who have logged in here will show up. + var steamIds = new List(); + var userData = await _userDataService.FindAsync(userId, userId, new List { "ImportedSteamFriends" }); + if (userData.TryGetValue("ImportedSteamFriends", out var rec) && !string.IsNullOrWhiteSpace(rec.Value)) + { + try { steamIds = JsonSerializer.Deserialize>(rec.Value) ?? new List(); } + catch { /* ignore malformed */ } + } + + var friends = new List(); + if (steamIds.Count > 0) + { + var players = await _userService.FindManyAsync(PlayFabUserAuthType.Steam, steamIds); + foreach (var player in players) + { + if (player.Id == userId) continue; // never list yourself + friends.Add(new + { + profile = new + { + playerId = player.Id, + displayName = player.DisplayName, + avatarUrl = "", + }, + onlineState = 0, // EYUserState — real presence tracking is a later phase + }); + } + } + + // NOTE: the exact response shape the client expects is not yet confirmed; this is a + // best-effort structure. The log lets us verify resolution while we validate in game. + _logger.LogInformation("GetFriendList for {User}: {Count} friend(s) resolved on server", userId, friends.Count); + return new { friends }; + } +} diff --git a/src/Prospect.Server.Api/Services/CloudScript/Functions/RequestUsersPingRequest.cs b/src/Prospect.Server.Api/Services/CloudScript/Functions/RequestUsersPingRequest.cs index 9a6f9c1..c522a0a 100644 --- a/src/Prospect.Server.Api/Services/CloudScript/Functions/RequestUsersPingRequest.cs +++ b/src/Prospect.Server.Api/Services/CloudScript/Functions/RequestUsersPingRequest.cs @@ -6,7 +6,7 @@ public class RegionObject [JsonPropertyName("region")] public string Region { get; set; } [JsonPropertyName("ping")] - public int Ping { get; set; } + public long Ping { get; set; } // client sends 2147483648 (int.MaxValue+1) for "no ping" -> must be long or deserialization 500s [JsonPropertyName("instanceType")] public int InstanceType { get; set; } } diff --git a/src/Prospect.Server.Api/Services/Database/DbUserService.cs b/src/Prospect.Server.Api/Services/Database/DbUserService.cs index db759c5..9feee53 100644 --- a/src/Prospect.Server.Api/Services/Database/DbUserService.cs +++ b/src/Prospect.Server.Api/Services/Database/DbUserService.cs @@ -40,7 +40,21 @@ public class DbUserService : BaseDbService public async Task FindOrCreateAsync(PlayFabUserAuthType type, string key) { - return await FindAsync(type, key) ?? + return await FindAsync(type, key) ?? await CreateAsync(type, key); } + + // Resolve several auth keys (e.g. a batch of Steam IDs) to the players that exist on + // this server in a single query. Used by the friends list to keep only the imported + // Steam friends who have actually played here. + public async Task> FindManyAsync(PlayFabUserAuthType type, IReadOnlyCollection keys) + { + if (keys == null || keys.Count == 0) + { + return new List(); + } + var wanted = new HashSet(keys); + return await Collection.Find(user => user.Auth.Any(auth => + auth.Type == type && wanted.Contains(auth.Key))).ToListAsync(); + } } \ No newline at end of file -- 2.54.0 From b0822c34cd558f9b57994adff0076240f4988470 Mon Sep 17 00:00:00 2001 From: nfteam head <1+neckfire@noreply.git.nfteam.ovh> Date: Tue, 14 Jul 2026 12:33:52 +0000 Subject: [PATCH 2/4] docs: guide install client amis --- FRIENDS-INSTALL.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 FRIENDS-INSTALL.md diff --git a/FRIENDS-INSTALL.md b/FRIENDS-INSTALL.md new file mode 100644 index 0000000..37a8933 --- /dev/null +++ b/FRIENDS-INSTALL.md @@ -0,0 +1,63 @@ +# 🎮 The Cycle: Frontier — Rejoindre le serveur privé (installation client) + +Guide pour **installer le client et jouer sur le serveur privé**. Tu n'installes **rien côté serveur** — juste le jeu + un loader + un certificat. + +> ⚠️ **À savoir avant de commencer** +> - Le jeu a été retiré de Steam en septembre 2022 : il faut **l'avoir déjà eu dans ta bibliothèque Steam** pour le retélécharger. +> - **Ce n'est pas du co-op en raid.** Chacun joue sa propre instance (stations solo) ; l'émulateur ne supporte pas plusieurs joueurs dans le même raid. Vous partagez le même serveur (progression, boutiques, comptes), pas la même partie. +> - Le serveur doit être **allumé** au moment où tu joues (demande à l'hébergeur). + +--- + +## Ce que l'hébergeur doit te fournir +1. **`LoaderPack.zip`** (le loader qui redirige le jeu vers le serveur privé). +2. **`certificate.crt`** (le certificat **public** du serveur — sans clé privée, safe à partager). + +--- + +## Étapes (Windows) + +### 1. Installer le client Saison 2 +Deux options : +- **Simple** : installe The Cycle: Frontier depuis ta bibliothèque Steam (`steam://install/868270`). +- **Recommandé (client Saison 2 exact)** via la console Steam : + 1. Steam ouvert, `Win+R` → `steam://nav/console`. + 2. Va sur les manifests SteamDB du dépôt 868271, cherche `4623363103423775682` (client S2 v2.7.2). + 3. Copie la commande **Steam console** et colle-la dans la console Steam → le téléchargement démarre. + +### 2. Installer le LoaderPack +1. Va dans le dossier du jeu → **`Prospect\Binaries\Win64`**. +2. Ouvre `LoaderPack.zip` et **glisse tout son contenu** dans ce dossier `Win64`. +3. Crée un **raccourci** vers `Prospect.Client.Loader.exe` (c'est avec ça que tu lanceras le jeu). + +### 3. Faire confiance au certificat du serveur +1. Double-clique **`certificate.crt`** → **Installer le certificat**. +2. **Emplacement : Utilisateur actuel** → Suivant. +3. **Placer tous les certificats dans le magasin suivant** → **Parcourir** → **Autorités de certification racines de confiance** → OK → Suivant → Terminer. +4. Accepte l'avertissement de sécurité (il mentionne `2EA46.playfabapi.com`). + +### 4. Pointer le client vers le serveur +Dans **`Prospect\Binaries\Win64`**, crée (ou édite) un fichier **`backend.txt`** contenant **exactement** cette ligne : + +``` +https://tc.nfteam.ovh:8443 +``` + +### 5. Lancer le jeu +1. **Steam doit être ouvert** (connecté à ton compte). +2. Lance le jeu via le **raccourci `Prospect.Client.Loader`** (⚠️ pas via Steam directement). +3. Connexion automatique avec ton compte Steam. Bienvenue sur Fortuna III ! 🚀 + +--- + +## Dépannage +| Symptôme | Cause / solution | +|---|---| +| `libcurl error 7 (Couldn't connect to server)` | Le serveur est **éteint** → demande à l'hébergeur de l'allumer. | +| Erreur de certificat / `hostname mismatch` | `certificate.crt` **pas importé** (ou pas dans « Autorités racines de confiance »). Refais l'étape 3. | +| `missing license` au téléchargement | Tu n'as jamais eu le jeu sur ce compte Steam → impossible de le retélécharger. | +| Parties du corps manquantes (client S3) | Va en station → change ton apparence une fois (corrige les IDs). | + +--- + +*Serveur privé The Cycle: Frontier — émulateur Prospect. Usage privé entre amis.* -- 2.54.0 From f4d93c9e718e716483394614644e69fb8e09aa97 Mon Sep 17 00:00:00 2001 From: nfteam head <1+neckfire@noreply.git.nfteam.ovh> Date: Tue, 14 Jul 2026 13:07:36 +0000 Subject: [PATCH 3/4] docs: ajoute la commande download_depot + astuce disque --- FRIENDS-INSTALL.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/FRIENDS-INSTALL.md b/FRIENDS-INSTALL.md index 37a8933..7f3d907 100644 --- a/FRIENDS-INSTALL.md +++ b/FRIENDS-INSTALL.md @@ -17,13 +17,25 @@ Guide pour **installer le client et jouer sur le serveur privé**. Tu n'installe ## Étapes (Windows) -### 1. Installer le client Saison 2 -Deux options : -- **Simple** : installe The Cycle: Frontier depuis ta bibliothèque Steam (`steam://install/868270`). -- **Recommandé (client Saison 2 exact)** via la console Steam : - 1. Steam ouvert, `Win+R` → `steam://nav/console`. - 2. Va sur les manifests SteamDB du dépôt 868271, cherche `4623363103423775682` (client S2 v2.7.2). - 3. Copie la commande **Steam console** et colle-la dans la console Steam → le téléchargement démarre. +### 1. Installer le client Saison 2 (via la console Steam) +1. Steam ouvert, `Win+R` → tape `steam://nav/console` → Entrée (la **console Steam** s'ouvre). +2. Colle **exactement** cette commande → Entrée : + ``` + download_depot 868270 868271 4623363103423775682 + ``` + (`868270` = le jeu, `868271` = le dépôt client, `4623363103423775682` = manifest **Saison 2 v2.7.2**.) +3. Le téléchargement démarre (⚠️ **pas de barre de progression**, sois patient — plusieurs Go). +4. À la fin, Steam affiche **« Depot download complete »** + le **chemin du dossier** + (ex. `...\Steam\steamapps\content\app_868270\depot_868271`). **Ce dossier = ton install du jeu.** + +> Il faut **avoir eu The Cycle: Frontier dans ta bibliothèque Steam** avant, sinon → erreur `missing license`. + +> **Pas assez d'espace sur le disque principal ?** `download_depot` écrit toujours dans `...\Steam\steamapps\content`. Pour le mettre sur un autre disque : ferme Steam, supprime `...\Steam\steamapps\content`, puis en **cmd admin** : +> ``` +> mkdir "D:\SteamContent" +> mklink /J "C:\Program Files (x86)\Steam\steamapps\content" "D:\SteamContent" +> ``` +> Rouvre Steam et relance la commande → le jeu ira sur `D:\`. ### 2. Installer le LoaderPack 1. Va dans le dossier du jeu → **`Prospect\Binaries\Win64`**. -- 2.54.0 From 922deb8397baa89061212c7815929948c5e44055 Mon Sep 17 00:00:00 2001 From: nfteam head <1+neckfire@noreply.git.nfteam.ovh> Date: Tue, 14 Jul 2026 13:09:57 +0000 Subject: [PATCH 4/4] docs: pointer vers la release pour LoaderPack + cert --- FRIENDS-INSTALL.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/FRIENDS-INSTALL.md b/FRIENDS-INSTALL.md index 7f3d907..0e57fa4 100644 --- a/FRIENDS-INSTALL.md +++ b/FRIENDS-INSTALL.md @@ -9,9 +9,14 @@ Guide pour **installer le client et jouer sur le serveur privé**. Tu n'installe --- -## Ce que l'hébergeur doit te fournir -1. **`LoaderPack.zip`** (le loader qui redirige le jeu vers le serveur privé). -2. **`certificate.crt`** (le certificat **public** du serveur — sans clé privée, safe à partager). +## Fichiers à télécharger (dans la release) +Les 2 fichiers sont dispo dans la **release Gitea** : +👉 **https://git.nfteam.ovh/neckfire/the-cycle/releases/tag/friends-client** + +1. **`LoaderPack.zip`** — le loader qui redirige le jeu vers le serveur privé (build b8 / Saison 2). +2. **`certificate.crt`** — le certificat **public** du serveur (sans clé privée, safe). + +Télécharge les deux depuis la page de release avant de commencer. --- -- 2.54.0