The Cycle: Frontier — serveur privé (émulateur Prospect)

Émulateur des services en ligne de The Cycle: Frontier (jeu retiré de Steam en septembre 2022), permettant de rejouer en solo sur un serveur auto-hébergé. Fork de deiteris/Prospect, figé sur le Build 8 / client Saison 2, buildé depuis les sources et déployé en conteneur via une CI Gitea.

Ce n'est pas du multijoueur. Le raid tourne côté client (station solo) : chacun joue sa propre instance. Le serveur partage la progression, les comptes et les boutiques, pas la partie. Le vrai multi (squad en raid, voix de proximité) suppose un serveur de jeu Unreal dédié — voir Hors-périmètre & R&D.


Sommaire


Comment ça marche

Le client officiel parle à PlayFab. On l'intercepte côté client et on le redirige vers notre serveur, qui réimplémente juste ce qu'il faut de PlayFab (auth Steam, CloudScript, UserData/TitleData, matchmaking solo).

flowchart LR
  subgraph client [Poste de jeu]
    L[Prospect.Client.Loader<br/>injecte l'agent] --> A[Prospect.Agent<br/>hooke l'URL PlayFab]
    A -- lit --> B[backend.txt]
    A --> G[Jeu &#40;TCF&#41;]
  end
  G -- HTTPS / SignalR --> API[Prospect.Server.Api<br/>émulateur PlayFab]
  API --> M[(MongoDB)]
  • Prospect.Client.Loader lance le jeu en injectant l'agent.
  • Prospect.Agent hooke l'URL de l'API PlayFab et la remplace par le contenu de backend.txt (placé dans Prospect/Binaries/Win64). Absent → fallback https://127.0.0.1:8443.
  • Prospect.Server.Api émule PlayFab (auth Steam → JWT, CloudScript, données joueur) et pousse le temps-réel via SignalR. Les données vivent dans MongoDB.

Toute la redirection du client tient donc dans une seule valeur (backend.txt) — gérée par l'outil Prospect.Client.Config.


Structure du dépôt

Projet Rôle
Prospect.Server.Api Cœur : émulateur PlayFab (ASP.NET 8). Controllers Client/CloudScript/Multiplayer, services Auth/UserData/TitleData/Database(Mongo)/Qos, hub SignalR.
Prospect.Steam Validation du ticket Steam (auth).
Prospect.Client.Loader Loader C++ : lance le jeu et injecte l'agent.
Prospect.Agent Agent C++ injecté : hooke l'URL PlayFab → backend.txt.
Prospect.Client.Config Utilitaire multi-OS : écrit backend.txt, importe le certificat, lance le jeu. Voir son README.
Prospect.Server.Game Serveur de jeu dédié (squelette, R&D).
Prospect.Unreal[.Generator/.Tests] Réimplémentation C# du netcode Unreal (R&D serveur dédié).
utils/ generate_ssl.py — génération du certificat auto-signé.

Build config Season 2 Release obligatoire pour l'API (le code sélectionne la saison via #if SEASON_2_RELEASE / SEASON_3_RELEASE → sinon #error Unsupported build type).


Build & lancement du serveur

Le serveur tourne en conteneur. L'image est buildée depuis les sources par le Dockerfile (multi-stage SDK .NET 8 → runtime aspnet 8, publish en Season 2 Release).

Build de l'image

docker build -t the-cycle .

Lancement

Il faut une instance MongoDB joignable et un certificat TLS monté (voir section suivante). Variables d'environnement :

Variable Rôle
DatabaseSettings__ConnectionString URI de connexion MongoDB.
DatabaseSettings__DatabaseName Base à utiliser (ex. ProspectDb).
AuthTokenSettings__Secret Secret de signature des JWT émis par le serveur.
PlayFabSettings__SignalRURL URL SignalR telle que le client doit l'atteindre (voir gotcha ci-dessous).
Kestrel__Certificates__Default__Path Chemin du .pfx dans le conteneur.
Kestrel__Endpoints__Https__Url ex. https://0.0.0.0:8443.
SteamWebApiKey (optionnel) clé Steam Web API pour récupérer les pseudos ; inerte si absente.
docker run -d --name the-cycle-api \
  -e DatabaseSettings__ConnectionString="mongodb://user:pass@HOST:27017/?authSource=ProspectDb" \
  -e DatabaseSettings__DatabaseName="ProspectDb" \
  -e AuthTokenSettings__Secret="<secret>" \
  -e PlayFabSettings__SignalRURL="https://<host-public>:8443/signalr/?hub=pubsub" \
  -e Kestrel__Endpoints__Https__Url="https://0.0.0.0:8443" \
  -e Kestrel__Certificates__Default__Path="/certs/certificate.pfx" \
  -v "$PWD/certs:/certs:ro" \
  -p 8443:8443 \
  the-cycle

⚠️ PlayFabSettings__SignalRURL = l'URL que le CLIENT doit joindre, pas 127.0.0.1. Le serveur y renvoie le client pour l'event de matchmaking ; s'il pointe sur 127.0.0.1, le déploiement en raid timeout. Mets le hostname/IP public du serveur.

Dev local (.NET)

dotnet build src/Prospect.Server.Api/Prospect.Server.Api.csproj -c "Season 2 Release"
dotnet run  --project src/Prospect.Server.Api -c "Season 2 Release"

Certificat TLS

La connexion est en HTTPS et le client valide le certificat → il doit être auto-signé et fait confiance côté client. Générer le .pfx avec utils/generate_ssl.py.

⚠️ Le SAN doit contenir DNS:<ip> ET IP:<ip>, en plus des hostnames. Le HTTP du jeu (libcurl) accepte l'IP en SAN IP, mais le WebSocket (libwebsockets) valide l'IP contre les SAN DNS → sans DNS:<ip>, la connexion SignalR échoue (Hostname mismatch err=62). Inclure aussi 2EA46.playfabapi.com, localhost, 127.0.0.1 et tous les hostnames publics utilisés dans backend.txt.

Côté client, l'import du certificat est automatisé par Prospect.Client.Config.


CI/CD

.gitea/workflows/build.yml — sur push main / preprod (ou workflow_dispatch) :

  1. build de l'image depuis le Dockerfile ;
  2. push sur le registry git.nfteam.ovh/neckfire/the-cycle ;
  3. notification du résultat (ntfy).

Modèle de branches :

Branche Tag image Usage
preprod :preprod (+ :preprod-<sha>) banc de test — valider un build avant de merger
main :latest (+ :<sha>) production

Workflow type : coder → push preprod → tester sur le serveur preprod → PR preprod → main → la CI republie :latest. Le déploiement applique la nouvelle image (docker compose pull && docker compose up -d).

Prospect.Client.Config (outil client, cross-OS) n'est pas buildé par cette CI — voir sa section publication.


Config du client (switch de serveur + certificat)

L'outil Prospect.Client.Config (binaire ProspectServerSwitcher, multi-OS Linux/Proton + Windows) fait tout le boulot côté client :

  • écrit backend.txt (presets prod / preprod ou URL libre) ;
  • récupère le certificat en direct depuis le serveur ciblé (TLS) et le rend fiable :
    • Windows : import dans Autorités de certification racines de confiance (utilisateur) ;
    • Linux/Proton : import direct dans le préfixe Wine du jeu via wine reg import (car wine certutil est cassé sous Proton) ;
  • lance le jeu.
# menu interactif
ProspectServerSwitcher

# scriptable
ProspectServerSwitcher --folder "<...>/Prospect/Binaries/Win64" --set preprod
ProspectServerSwitcher --set https://mon-serveur:8443

Détails complets, gotchas Proton (préfixe non-Steam) et commandes de publication des binaires autonomes : src/Prospect.Client.Config/README.md.

Installation complète pas-à-pas pour un nouveau joueur (télécharger le client S2, le LoaderPack, importer le certificat) : FRIENDS-INSTALL.md.


État des fonctionnalités

Fonctionne :

  • Login Steam, EULA, tutoriel
  • Station solo (S2/S3) : onboarding, matchmaking & déploiement solo
  • Inventaire & loadout, stash, vente, réparation
  • Contrats / quêtes — y compris les objectifs kills et de zone (auto-crédités : pas de serveur dédié pour remonter les events runtime du raid client-hosted)
  • Progression des factions
  • Season pass : claim + gain d'XP de saison (niveau Fortuna)
  • Boutiques d'items (Korolev / ICA / Osiris / QuickShop / CraftingStation)
  • Aurum Shop (cosmétiques) + rotation daily/weekly
  • Craft, Quarters, solde joueur, connexion quotidienne
  • Apparence & emotes
  • Assurance : débit de la prime au déploiement + payout à la mort
  • Stats de carrière (valeurs à 0 — non traçables sans serveur de jeu)
  • Présence des amis (en ligne / en raid)
  • Pseudos réels via Steam Web API (le client n'envoie que le SteamID)
  • Cartes : Bright Sands, Crescent Falls, Tharis Island

Non implémenté / hors-périmètre :

  • Squad / multi dans le même raid — nécessite un serveur de jeu dédié
  • Voix de proximité (login/join Vivox = placeholders)
  • Free loadouts & presets (Saison 3 uniquement)
  • Achat de cosmétiques (endpoint d'achat vanity distinct, non câblé)
  • Catalogue des récompenses Fortuna (DataTable côté client, dans des paks chiffrés)
  • Défis quotidiens Fortuna

Hors-périmètre & R&D

Un serveur de jeu Unreal dédié (Prospect.Server.Game + Prospect.Unreal, branche game-server) est en cours de reverse-engineering pour, à terme, permettre le vrai multi. État : handshake stateless UE, séquençage et décodage des bunches franchis, canal de contrôle ouvert, NMT_Hello parsé. Bloqué sur le chiffrement DTLS-PSK du client (clé dérivée du user_id), derrière un exe packé (BattlEye) → la dérivation n'est pas extractible statiquement. Détails dans NETCODE-RND.md (branche game-server).

Le « lobby squad » via l'API seule n'est pas faisable : l'invitation d'amis est gérée 100 % côté client Steam.


Crédits & licence

Fork de deiteris/Prospect (lui-même issu du projet Prospect original). Voir LICENSE. Usage privé.

S
Description
The Cycle: Frontier — émulateur Prospect (fork, déploiement homelab CI)
Readme AGPL-3.0
59 MiB
2026-07-14 12:38:11 +00:00
Languages
C# 96.8%
C++ 2.8%
Python 0.3%