Itsenäinen käynnistys 2.1.1:llä
Ennen kuin siirryimme versioon 1.4.4 ja Undauntediin, aloitimme pelin viimeisestä versiosta, 2.1.1:stä (“Awakening”, joulukuu 2024, Unreal Engine 5.1.1, IoStore). Tavoitteena oli seistä Ramsgatessa (pelin kaupungissa, jossa pelaajat tapaavat toisiaan) yksin, ilman pelipalvelinta (ohjelmaa, joka pyörittää peliä pelaajille verkossa). Yritimme tehdä sen käynnistämällä pelin suoraan kaupunkiin omaa taustapalveluamme vasten. (Taustapalvelu tarkoittaa verkkopalveluja, joihin peli ottaa yhteyttä, esimerkiksi kirjautumista, hahmoja ja tavaroita varten.)
Pääsimme pitkälle. Koko kirjautumisketju toimii palvelintamme vasten. Ramsgate piirtyy näkyviin ja pysyy pystyssä. Asiakasohjelma (client, pelaajan oma peliohjelma) sitoo hahmonsa. Ohjattavaa pelaajaa emme kuitenkaan koskaan saaneet. Asiakasohjelman tilitunnus (account id) jää tyhjäksi, eikä ilman sitä pelaajahahmoa (pawn) ilmesty.
Tämä sivu kirjaa, mihin jäimme, jotta kuka tahansa pelin myöhäisten versioiden parissa työskentelevä voi jatkaa siitä. Kaikki sivulla koskee versiota 2.1.1, ellei rivillä toisin sanota. Vastausten muodot käydään tarkemmin läpi sivulla Taustapalvelun rajapinta. Käynnistysvalitsimet, lokit ja jumiutumisen tunnistin ovat sivulla Asiakasohjelman sisäosat.
Korjaus. Kokeilimme itsenäistä käynnistystä ylipäätään siksi, että olimme päätelleet moninpelin olevan mahdoton pelkällä asiakasohjelmakäännöksellä. Tuo päätelmä oli väärä. Ohjelmatiedosto ei pysty isännöimään peliä yksinään, mutta sen verkkokerros on ehjä, ja ohjelmaan ladattu DLL (lisäosa, joka ladataan ohjelman sisään) voi ohjata sitä. Näin Undaunted isännöi Ramsgatea ja metsästyksiä versiossa 1.4.4. Katso Näin moninpeli toimii. Jälkiviisaasti ajateltuna 2.1.1:n reitti on se, joka on kuvattu kohdassa Mistä jatkaa, eikä itsenäinen käynnistys.
Sisällys
Mihin jäimme
| Vaihe | Tila versiossa 2.1.1 |
|---|---|
| Asiakasohjelma käynnistyy ilman EasyAntiCheatia, asennus on muuttamaton ja allekirjoitukset kelvollisia | Toimii |
| Epic-kirjautuminen EOS:n kautta (tiliportaali selaimessa) | Toimii, syyskuun 2026 tilanteen mukaan |
| Phoenixin kirjautumisketju taustapalveluamme vasten, hahmon luonti mukaan lukien | Toimii |
| Käynnistys suoraan Ramsgateen | Toimii (GameDefaultMap) |
| Pysyy pystyssä | Toimii muistirajojen ja oikeiden vastausmuotojen kanssa. Ei kaatumista useissa viiden minuutin ajoissa. |
| Hahmo sidottu istuntoon | Toimii (?CharacterId= käynnistys-URL:ssa) |
| Tilitunnus sidottu | Ei. Tämä on este. |
| Pelaajahahmo Ramsgatessa | Ei koskaan ilmestynyt |
Käännös
| Versio | 2.1.1, käännös (build) 682486, muutoslista (changelist) 682875 |
| Moottorin merkkijono kaatumisraporteissa | 5.1.1-682875+//phx-archon/release/2.1.1 |
| Allekirjoitettu | Phoenix Labs, aikaleima joulukuu 2024 |
| Sisältö | IoStore. 157 .utoc/.ucas-paria. 141 tiedostoa 156 .pak-tiedostosta on 339 tavun tynkiä. Loput 15 sisältävät irrallisia tiedostoja, muun muassa pelin mukana tulevat valmiit asetukset (cooked config) tiedostossa Archon_50-WindowsClient.pak (Oodle-pakattu). |
| Kohde | Pelkkä asiakasohjelma (WITH_SERVER_CODE=0). Katso Asiakasohjelman sisäosat. |
| Taustapalvelu | OnlineSubsystemPhoenix, Phoenixin oma REST-rajapinta osoitteissa *.steelyard.ca. Se ei ole PlayFab. |
Tarkistimme kopiomme Phoenixin tiedostoluetteloa (manifest) vasten (352/352 tiedostoa) sekä jokaisen ohjelmatiedoston Authenticode-allekirjoituksen (75/75), ennen kuin ajoimme mitään. Emme koskaan muuttaneet asennusta. Kaikki alla kuvattu tehtiin käyttäjän asetusten, komentorivin ja taustapalvelun kautta.
Asiakasohjelman ohjaaminen taustapalveluumme
- Hosts-tiedosto. Jokainen valmiissa asetuksissa oleva
steelyard.ca-palvelinnimi (laskujemme mukaan 23) sekästeelyard.online,cdn.playdauntless.comjastore.playdauntless.comohjataan osoitteisiin127.0.0.1ja::1. - TLS. Palvelimemme esittää
*.steelyard.ca-jokerivarmenteen, jonka on allekirjoittanut oma yksityinen varmentajamme (CA). 2.1.1 lukee varmentajapakettinsa irrallisesta tiedostosta<game folder>\Engine\Content\Certificates\cacert.pem. Lisäsimme varmentajamme sinne. Älä koskaan lisää sitä Windowsin luotettujen varmenteiden säilöön. - Molemmat osoiteperheet. Asiakas käyttää sekä IPv4- että IPv6-merkintää. Testitaustapalvelumme (Python, FastAPI) ajaa neljää kuuntelijaa: portti 443 on HTTPS:lle ja portti 80 tavalliselle HTTP:lle ja presence-WebSocketille, kumpikin sekä IPv4:llä että IPv6:lla. Uvicorn asettaa
IPV6_V6ONLY-asetuksen::-sidonnalle, joten yksi kaksipinoinen (dual-stack) kuuntelija ei toimi. - Käynnistys. Käynnistä
Archon\Binaries\Win64\Dauntless-Win64-Shipping.exesuoraan valitsimilla-EpicPortal -AUTH_TYPE=accountportal -AUTH_LOGIN=unused -AUTH_PASSWORD=unused. Kaikki kolmeAUTH-valitsinta tarvitaan. EOS avaa Epicin tiliportaalin, ja pelaaja kirjautuu sinne itse. Älä koskaan käynnistä tiedostoastart_protected_game.exe, joka on EasyAntiCheatin käynnistäjä. - Jäljelle jääneet overlay-prosessit. Kaatunut ajo voi jättää jälkeensä
EOSOverlayRenderer-Win64-Shipping-prosesseja, ja ne estävät kirjautumisen päällyskerroksen (overlay) seuraavalla käynnistyksellä. Lopeta ne ennen uudelleenkäynnistystä.
Tämä riippuu siitä, että Epicin EOS-palvelu hyväksyy yhä kirjautumiset Dauntlessin käyttöönotolle (deployment). Syyskuussa 2026 se hyväksyi. Jos Epic lakkauttaa käyttöönoton, 2.1.1:n kirjautuminen pysähtyy ensimmäiseen vaiheeseen, teki taustapalvelu mitä tahansa. Versiolla 1.4.4 ei ole tällaista riippuvuutta.
Kirjautumisketju
EOS-kirjautumisen jälkeen asiakas kulkee tämän ketjun läpi. Näillä vastauksilla ketju valmistuu, ja FOnlineIdentityPhoenix ilmoittaa kirjautumisen valmistuneen oikealla tilitunnuksella. Kaikki tämä nähtiin oikeassa ajossa.
| Kutsu | Mitä vastaamme |
|---|---|
POST login-queue-prod/login | Litteä (ilman kirjekuorta), tasan viisi kenttää: {"state": "OPEN", "error_code": "", "title": "", "message": "", "timeout": 5000} |
GET gamesession-prod/features/platform/win | crossplay ja crossprogression, molemmat true. Lähetämme ne samassa rungossa sekä litteinä että käärittyinä, koska tulkintamme erosivat toisistaan. |
GET login-queue-prod/maintenance/status | Hyväksytään. Emme selvittäneet, mitä kenttiä se lukee. |
GET gamesession-prod/account/link/epic/{id} | Kääritty: {"code": "OK", "message": "", "payload": {"isLinked": true}} |
PUT gamesession-prod/gamesession/epiceos | Kääritty. Sisältö (payload) sisältää kentät sessiontoken ja sessionid. Tästä eteenpäin sessiontoken on bearer-tunniste kaikille muille palveluille. |
GET auth-prod/accountinfo | Litteä: {"username": "...", "accountId": "..."} |
GET auth-prod/tags, GET auth-prod/isbanned | Vastattu. Emme selvittäneet, mitä kenttiä ne lukevat. |
GET dauntless-prod/character | Pelkkä taulukko, jonka alkiot ovat muotoa {"id", "name", "updateVersion", "data"} |
PUT dauntless-prod/character rungolla {"name": "..."} | Luo hahmon. Pelkkä {"id", "name"}. |
POST dauntless-prod/character | Tallentaa hahmon datamöhkäleen (blob). Pelkkä {"data": "<string>"}. Vain data luetaan. |
Sen jälkeen tulevat inventory, mm2/candidate/player/register, game_tuning, accountinfo/public ja account/mapping.
Säännöt, joiden löytäminen kesti pisimpään:
- Kääritty vai litteä riippuu palvelimesta.
gamesession-*käärii kaiken kirjekuoreen{"code", "message", "payload"}. Litteä runko jäsentyy, mutta sisältöä ei koskaan lueta. SiksiisLinked: truejaisLinked: falsetoimivat aluksi samalla tavalla.auth-*jadauntless-*ovat litteitä. - Kirjautumisjonossa on tasan viisi kenttää. Vain
state-arvoOPENpäästää pelaajan läpi. Mikä tahansa muu saa asiakkaan kysymään uudelleenmax(timeout × 0.001, 5.0)sekunnin kuluttua. - Kaksi alijärjestelmää lukee
GET /character-vastauksen. Hahmoluettelo tarvitsee kentätidjaname. Datavarasto etsii alkion tunnuksensa perusteella ja tarvitsee kentätupdateVersion(luku) jadata(merkkijono, joka sisältää JSON-olion, tainull). Ilman kahta viimeistä asiakas lähetti pyynnön kuusi kertaa peräkkäin ja epäonnistui sitten kirjautumisessa. - Tallenna
updateVersiontäsmälleen sellaisena kuin se lähetettiin. Asiakas kasvattaa sitä ennen kuin se lähettää tallennuksen. Jos palvelin lisää siihen vielä 1:n, seuraava luku näyttää ristiriidalta. - Tunnista pelaaja Epicin JWT:n
sub-väitteen (claim) perusteella, älä bearer-tunnisteen perusteella. Istuntopyynnössä bearer-tunnisteena on EOS-tunniste (JWT), ja Epic myöntää tuon tunnisteen aina uudelleen. Kun käytimme avaimena raakaa bearer-tunnistetta, pelaaja sai uuden hahmon jokaisella kirjautumisella. Johdamme pysyvät Phoenix-tunnuksetsub-arvosta. Käytämme sitä vain hakuavaimena yksityisellä testipalvelimella emmekä tarkista allekirjoitusta, joten älä pidä sitä tunnistautumisena.
Mitä tavallinen kulku tekee seuraavaksi
Hahmon datamöhkäle ratkaisee, mihin “Play”-painike vie. ULoginScreen::AdvanceToPlay (0x142d128f0) lukee möhkäleestä avaimen PlayerAccountProgressStep funktion GetPlayerAccountProgressState (0x1429d0160) kautta:
| Arvo | Järjestysluku | “Play” vie |
|---|---|---|
New | 0 | Opetusosion (tutorial) välivideo ja hahmoeditori |
SavedCharacter | 1 | Harjoitusalue (training grounds) |
TrainingGroundsComplete | 2 | Metsästysalue (hunting grounds) |
DefeatedGnasher ja ylemmät | 3+ | “Progression is Entered Ramsgate : So…entering Ramsgate…” |
Jokainen näistä kohteista kulkee matchmakingin (pelaajien peliin kokoamisen) kautta, opetussaari mukaan lukien. Kun ei ollut pelipalvelinta, jonne siirtyä, tavallinen kulku päättyi ilmoitukseen “You have been signed out”. Juuri se sai meidät kokeilemaan itsenäistä käynnistystä.
Möhkäleessä on kaksi ansaa:
- Jokaisen arvon on oltava JSON-merkkijono. Jokainen arvo kulkee
FJsonValue::TryGetString-funktion (0x14142ba4a) läpi. Ensimmäinen epäonnistuminen nollaa onnistumislipun ja tyhjentää koko varaston, joten yksikin JSON-totuusarvo heittää hiljaa pois kaikki muut avaimet. Asiakas itse kirjoittaa arvon"true"lainausmerkeissä olevana merkkijonona. Alustamme uudet hahmot arvolla{"PlayerAccountProgressStep": "EnteredRamsgate"}. - Älä lisää
HasFinishedTutorial-avainta. Jos avain on ylipäätään olemassa, arvosta riippumatta, tila nostetaan vähintään arvoon 6. Arvosta 6 ylöspäinAArchonHUD::CanDisplayMOTDScreenläpäisee edistymistarkistuksensa, ja asiakas avaa koko näytön päivän viestin (message of the day) ja yrittää ladata sen kuvan taustapalvelusta, jossa sellaista ei ole.EnteredRamsgate(4) läpäisee jo kaikki löytämämme portit.
Käynnistys suoraan Ramsgateen
Käyttäjän asetukset
Unreal asettaa kirjoitettavan, käyttäjäkohtaisen asetuskerroksen valmiiden oletusasetusten päälle:
%LOCALAPPDATA%\Archon\Saved\Config\WindowsClient\Engine.ini
%LOCALAPPDATA%\Archon\Saved\Config\WindowsClient\Game.ini
%LOCALAPPDATA%\Archon\Saved\Config\WindowsClient\Input.ini
%LOCALAPPDATA%\Archon\Saved\Config\WindowsClient\RuntimeOptions.ini
Game.ini, Input.ini ja RuntimeOptions.ini ovat aluksi tyhjiä. Peli kirjoittaa Engine.ini-tiedoston itse. Kopioi tiedostot turvaan ennen kuin muokkaat niitä. Virheellinen GameDefaultMap estää peliä käynnistymästä lainkaan. Varhaisissa muistiinpanoissamme luki, että tämä versio jättää irralliset asetustiedostot huomiotta. Se oli väärin; korjaus on sivulla Asiakasohjelman sisäosat.
1.4.4 lukee samaa kansiota. Jos sinulla on molemmat versiot, siirrä 2.1.1:n tiedostot sivuun, ennen kuin ajat versiota 1.4.4. Jäljelle jäänyt GameDefaultMap tai LocalMapOptions rikkoo 1.4.4:n kirjautumiskulun.
Kartan ohitus
Valmiissa asetuksissa GameDefaultMap on /Game/Maps/Map_LoginMenu. Kun sen ohittaa käyttäjän Engine.ini-tiedostossa, asiakas käynnistyy suoraan kaupunkiin:
[/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Game/Maps/ramsgate/ramsgate_01_persistent
- Paketin polku.
ramsgate_01_persistenton pysyvä taso (persistent level). Muutramsgate_01_*-paketit ovat suoratoistettavia alitasoja (streaming sublevels). Löysimme polun listaamalla IoStoren hakemistoindeksitutocdir.py-työkalulla (katso Työkalut). Se on tiedostossaArchon_Maps_2-WindowsClient.utocmuodossa../../../Archon/Content/Maps/ramsgate/ramsgate_01_persistent.umap, jaArchon/Content/vastaa polkua/Game/. Valmiit asetukset nimeävät saman kartan joServerDefaultMap-arvona ja[RamsgateRework] CityMap-arvona. - Miten sitä käytetään.
UGameInstance::StartGameInstance(0x144dba330) rakentaa käynnistys-URL:n arvoistaGameDefaultMapjaLocalMapOptionsja siirtyy siihen. Staattisen tulkintamme mukaan tämä julkaisukäännös jättää huomiotta komentorivillä nimetyn kartan. Komentorivin osoitin korvataan ensin tyhjällä merkkijonolla (0x144dba3ad). ?listenepäonnistuu.LocalMapOptions=?listensaa käynnistyksen epäonnistumaan viestillä “The default map … could not be found. Exiting.”LoadMapkutsuu tyngäksi (stub) typistettyäUWorld::Listen-funktiota, joka palauttaa aina epätoden. Ikkuna tulee aina, kunBrowse()epäonnistuu, ei vain silloin, kun kartta puuttuu.
Pelitila
Tulkintamme mukaan ramsgate_01_persistent-kartan maailma-asetuksissa (world settings) ei ole pelitilan ohitusta: sen nimitaulukossa ei ole BPGM_*-luokkaa eikä /Game/Blueprints/GameMode/-polkua. (Toinen, riippumaton tarkistus ei antanut selvää tulosta, joten pidä tätä todennäköisenä.) CreateGameModeForURL (0x144da42c0) kokeilee järjestyksessä: maailma-asetukset, URL:n ?game=-valinta, GameModeMapPrefixes-taulukko, GlobalDefaultGameMode ja lopuksi moottorin perusluokka. Ensimmäiset käynnistyksemme ajoivat siis todennäköisimmin yleistä oletusta, BPGM_Archon_Prototype_C. Asetimme sen sijaan kaupungin pelitilan:
[/Script/EngineSettings.GameMapsSettings]
GlobalDefaultGameMode=/Game/blueprints/gamemode/BPGM_City.BPGM_City_C
Emme selvittäneet, miten kaupungin pelaajaohjain (player_controller_city_bp_C) valitaan. Mikään pelitilan blueprint tai ini-tiedosto ei aseta sitä, eikä ohjelmatiedostossa ole sille merkkijonoa. Älä oleta, että vaihtaminen BPGM_City-pelitilaan antaa sinulle kaupungin ohjaimen.
Pystyssä pitäminen
Ensimmäiset käynnistykset piirsivät Ramsgaten ja kuolivat sitten kolmella eri tavalla. Kaikki kolme käsitellään sivulla Kaatumisten tutkiminen:
- Muisti. Ilman rajoja asiakas kasvoi noin 9 gigatavuun. Suoratoistopoolin ja skaalautuvuusasetusten rajat käyttäjän
Engine.ini-tiedostossa pitävät sen noin 2,8 gigatavussa. Käynnistimemme myös tappaa prosessin, jos se ylittää 6,5 Gt. - Jumiutumiset. Unrealin jumiutumisen tunnistin sulki asiakkaan muutaman minuutin kuluttua. Tutkimista varten aseta
HangsAreFatal=Falseosioon[Core.System]ja nostaPlayerStartEventTimeoutGame.ini-tiedostossa, jotta 120 sekunnin turvaraja ei peitä todellista syytä. - Väärät muodot. Taustapalvelumme “discovery”-tilan paikkamerkit kaatoivat asiakkaan. Tuntemattomien reittien on palautettava 404 ilman runkoa, ja tunnettujen reittien on palautettava täsmälleen oikea muoto.
Kun kaikki tämä on paikallaan, Ramsgate latautuu noin 20 sekunnissa, ja se pysyi pystyssä ilman kaatumisia useissa viiden minuutin ajoissa. Kaupunki piirtyy kauppoineen, lyhtyineen ja NPC-hahmoineen (tietokoneen ohjaamine hahmoineen), mutta ilman omaa pelaajaamme. Koska konsolia tai lokitiedostoa ei ollut, telemetrian sykeviestin map-kenttä (tracking-prod/heartbeat, joka sekunti) oli keinomme varmistaa, missä asiakas oikeasti oli.
Lopullinen käynnistysskriptimme teki tämän. <character id> on tunnus, jonka GET /character palauttaa pelaajalle:
param([int]$CapMB = 6500, [int]$MaxSeconds = 240)
$cfg = "$env:LOCALAPPDATA\Archon\Saved\Config\WindowsClient"
Copy-Item "$cfg\Engine.ini.bak" "$cfg\Engine.ini" -Force # start from a known-good copy
Add-Content -Path "$cfg\Engine.ini" -Encoding ASCII -Value @"
[/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Game/Maps/ramsgate/ramsgate_01_persistent
LocalMapOptions=?CharacterId=<character id>
GlobalDefaultGameMode=/Game/blueprints/gamemode/BPGM_City.BPGM_City_C
[Core.System]
HangsAreFatal=False
HangDuration=600.0
[SystemSettings]
r.Streaming.PoolSize=400
r.Streaming.LimitPoolSizeToVRAM=1
; ... the rest of the caps listed on the Crash forensics page
"@
Set-Content -Path "$cfg\Game.ini" -Encoding ASCII -Value @"
[/Script/Archon.ArchonPlayerController]
PlayerStartEventTimeout=600.0
"@
$exe = "<game folder>\Archon\Binaries\Win64\Dauntless-Win64-Shipping.exe"
$p = Start-Process -FilePath $exe -WorkingDirectory (Split-Path $exe) -PassThru -ArgumentList @(
"-EpicPortal", "-AUTH_LOGIN=unused", "-AUTH_PASSWORD=unused", "-AUTH_TYPE=accountportal",
"-windowed", "-ResX=1280", "-ResY=720", "-nosplash",
"-nothreadtimeout", "-noheartbeatthread", "-nocheckpointhangdetector")
# ...then the memory watchdog loop from the Crash forensics page
Käytimme silloin arvoa HangDuration=600 ja valitsinta -nothreadtimeout. Jälkiviisaasti: pidä HangDuration 60:ssä ja jätä -nothreadtimeout pois. Pelkkä HangsAreFatal=False estää sulkemisen ja kertoo silti, mikä säie jumittui. Asiakasohjelman sisäosat selittää miksi. (Skriptin kommenteissa mainittu “Crash forensics page” on sivu Kaatumisten tutkiminen.)
Miksi pelaajaa ei ilmesty: pelaajatietojen lataajat
Kaupunki latautuu, mutta pelaajahahmo ilmestyy vasta, kun asiakas on ladannut pelaajan tiedot taustapalvelusta:
AArchonPlayerController::OnPostLogin(0x1429d6ea0) kutsuu funktiotaUArchonLoadManager::Begin(0x14299b670).Beginkäynnistää jokaisen rekisteröidynIArchonPlayerDataLoader-lataajan, kunkin vasta niiden lataajien jälkeen, joista se riippuu.UArchonLoadManager::Complete(0x14299d860) menee läpi vasta, kun jokainen lataaja on ilmoittanut olevansa valmis. Muuten se kiertää uuden kierroksen.- Sitten ajetaan
AArchonPlayerController::OnPlayerDataLoadComplete(0x1429d6900). Se on ainoa polku funktioihinCanRestartPlayer()jaServerRestartPlayer()ja siten ainoa polku pelaajahahmoon. - Jos lataajat aikakatkaistaan,
UArchonLoadManager::LoadFailed(0x1429ad0f0) lähettää telemetriatapahtumanplayerdata_load_failedja palauttaa pelaajan valikkoon viestillä “Loading timeout while receiving Player data”.
Yksitoista lataajaa
AArchonPlayerController::PostInitializeComponents (0x1429dbb60) rekisteröi ne, mutta vain kun ohjaimella on auktoriteetti (authority, eli se päättää pelin tilasta). Itsenäisessä käynnistyksessä sillä on se aina. Rekisteröintijärjestyksessä:
| # | Lataaja | Taustapalvelun kutsu | Huomiot |
|---|---|---|---|
| 1 | UProgressionComponent | progression-prod/progression/config, /progression/{accountid}, /progression/objectives/{accountid} | Odottaa Entitlements-lataajaa. Katso alempana oleva avoin riski. |
| 2 | UQuestSystemComponent | ei omaa kutsua | Odottaa HuntPass-lataajaa |
| 3 | AArchonLoadout | loadout-prod/loadout/{account_id}/{character_id}/all | Ainoa lataaja, joka pidättää “valmis”-ilmoituksen, kun sen pyyntö epäonnistuu. Odottaa tavaraluettelon lataajaa. |
| 4 | EntitlementsComponent | auth-prod/entitlementsv2 | Pisimmän ketjun juuri |
| 5 | CohortsComponent | cohort-prod/playertreatments/{id} | Epäonnistuu sallivasti (fails open): käyttää varalla käsittelyä (treatment) E1000 ja ilmoittaa olevansa valmis |
| 6 | HuntPassComponent | /huntpass/{id} | Rekisteröidään vain, kun eräs ominaisuuslippu (feature flag) on päällä. Ilmoittaa heti olevansa valmis, jos edistymisrajapinta puuttuu. |
| 7 | UArchonInventoryDataLoader | dauntless-prod/inventory/{a}/{c} | Epäonnistuminen palauttaa pelaajan valikkoon |
| 8 | UCooldownComponent | progression-prod/cooldown/{accountid} | |
| 9–11 | UBountyComponent, _Daily, _Weekly | progression-prod/bounty/... |
Riippuvuusketjut:
Entitlements -> Progression -> HuntPass -> QuestSystem -> Bounty -> Bounty_Weekly -> Bounty_Daily
Entitlements -> HuntPass
Cooldown, Progression, QuestSystem -> Bounty
InventoryDataLoader -> Loadout
Yksikin juuren lähellä jumiin jäänyt lataaja pysäyttää kaiken sen takana. Juuri niin kävi Entitlements-lataajan kanssa. Vastasimme ensin GET /entitlementsv2-pyyntöön pelkällä []-taulukolla. Sitä ei voi jäsentää (lukija vaatii ylimmälle tasolle olion), joten Entitlements-lataaja ei koskaan valmistunut, eikä kuusi sen takana olevaa lataajaa edes käynnistynyt. Asiakas pyysi entitlementsv2-tietoa uudelleen jokaisella latauskierroksella eikä pyytänyt /progression/*-tietoja lainkaan. Oikea vastaus on {"entitlements": []}, ja alkioiden kentät ovat name, duration ja activatedDate.
Työlista
Kun taustapalvelu oli tiukassa tilassa (tuntemattomat reitit saavat 404-vastauksen ilman runkoa), asiakkaan uudelleenyritykset tekivät työlistasta täsmällisen. Nämä kolmetoista pyyntöä epäonnistuivat kukin noin 70 kertaa, ennen kuin asiakas luovutti. Tyhjät polun osat ovat puuttuva tilitunnus (ja ennen ?CharacterId=-valintaa myös hahmotunnus):
| Pyyntö | Mitä vastaamme | Mistä tiedämme muodon |
|---|---|---|
progression-prod/pjm/ | {"nodes": {...}}, solmutunnuksilla avainnettu olio | Asiakkaan oma POST /pjm -runko |
progression-prod/cooldown/ | {"cooldowns": [...]} | Asiakkaan oma PUT /cooldown/batch -runko |
progression-prod/escalation/ESC_SEASON_1/ – ESC_SEASON_6/ (kuusi pyyntöä) | Kääritty: escalation_level, next_level_xp, talents_progress (taulukko), unlock_progress (taulukko), update_version | Luettu datan purkavasta koodista (deserialiser) |
mailbox-prod/survey/config | Kääritty, tyhjä lista | Vahvistamaton. Emme löytäneet sille purkavaa koodia. |
mailbox-prod/eventstats/ | Kääritty | Luettu purkavasta koodista |
gauntlet-prod/config | Kääritty; each_level_rewards ja milestone_rewards on oltava taulukoita | Luettu purkavasta koodista |
dauntless-prod/inventory// | Litteä {"stackedItems": [...], "instancedItems": [...]} | Luettu purkavasta koodista |
cohort-prod/playertreatments/ | Kääritty, {"treatments": []} | Luettu purkavasta koodista |
Reittien on hyväksyttävä tyhjät polun osat ja etsittävä pelaaja silloin bearer-tunnisteen perusteella. Täydet muodot ovat sivulla Taustapalvelun rajapinta.
Ase ja lyhty
Kun lataajat on saatu tyytyväisiksi, AArchonPlayerController::CheckForPlayerStart estää pelaajan aloituksen niin kauan kuin ArchonCharacter->Weapon tai ->Lantern on tyhjä (null). DefaultGame.ini-tiedoston [DefaultLoadout] nimeää esineet WP_EB_TRAINING ja LT_BASIC. Asiakkaan oma aloituslahja (POST /inventory, jonka se lähettää itse) kattaa haarniskan, viirin (banner), soihdun (flare) ja aseen osan, mutta ei näitä kahta. Alkuperäisillä palvelimilla ne antoi opetusosio, jonka me ohitamme. Taustapalvelumme antaa jokaiselle pelaajalle molemmat esineet ja palauttaa ne myös tavaraluettelotapahtuman vastauksen createdInstancedItems-kentässä.
Avoin riski edistymisessä
Auktoriteettipolulla UProgressionComponent ilmoittaa olevansa valmis vasta, kun neljä valmiuslippua on asetettu (tarkistetaan kohdassa 0x1427cd0d0). Löysimme kirjoittajat kolmelle niistä: asetusvastauksen, edistymisvastauksen ja tavoitevastauksen. Neljännelle (+0x3f3) emme löytäneet yhtään. Jos sillä todella ei ole kirjoittajaa, edistyminen ei voi koskaan valmistua itsenäisessä käynnistyksessä, lähetti taustapalvelu mitä tahansa. Emme voineet testata tätä, koska tilitunnus pysäytti meidät ensin. Jos ratkaiset tilitunnuksen ongelman eikä pelaajahahmo silti ilmesty, katso ensin tänne.
Hahmon sitominen: ?CharacterId=
Ennen tätä jokainen pyyntö lähti arvoilla "characterId": "" ja "accountId": "", ja polut kutistuivat muotoihin /inventory// ja /loadout///all.
Ohjelmatiedoston paikallisen pelaajan ja kirjautumisen koodissa on merkkijonot ?CharacterId=%s, ?PlatformPool=%s ja AuthToken=%s. Ne näyttävät URL-valinnoilta. Kokeilimme ensimmäistä käynnistys-URL:ssamme:
[/Script/EngineSettings.GameMapsSettings]
LocalMapOptions=?CharacterId=<character id>
Se otetaan käyttöön. Tämän jälkeen pyynnöissä oli hahmotunnus, ja poluista tuli muotoa /inventory//<character id>. Tunnuksen on oltava sellainen, jonka GET /character palauttaa tälle pelaajalle. Suora käynnistys vain listaa hahmot eikä koskaan luo uutta. Siksi taustapalvelumme luo hahmon, kun lista on tyhjä, sillä tyhjä lista jättää myös kaikki myöhemmät tunnukset tyhjiksi.
Tilitunnus jäi tyhjäksi.
Este: tyhjä tilitunnus
Oire
- Jokaisessa pyynnössä on
"accountId": "", ja jokainen{accountid}-osa on tyhjä (/progression//,/loadout//<character id>/all). - Yhdessäkään pyynnössä ei ole
Authorization-otsaketta lainkaan. - Asiakkaan oma telemetria ilmoittaa
client_login_failedvaiheessaLoginToEpicProxyviestillä “Failed to login to Epic with no given Auth info”.
Mistä tilitunnus tulee
{accountid}täytetään paikallisen pelaajan Phoenix-tunnuksenFUniqueNetId::ToString()-arvosta. Tunnus tulee funktiostaIOnlineIdentity::GetUniquePlayerId(), ja se tallennetaan paikalliseen pelaajaan (UArchonLocalPlayer + 0x930). Se ei ole merkkijono, jota asiakas säilyttäisi missään muualla.- Ainoa taustapalvelun vastaus, jossa on tilitunnus, on
GET auth-prod/accountinfo. Asiakas pyytää sitä vain kirjautumispolulta. - URL-osoitteiden rakentajat eivät jätä pyyntöä tekemättä, kun tunnus on tyhjä. Ne käyttävät sen tilalla tyhjää merkkijonoa, esimerkiksi
FOnlineLoadoutPhoenix::GetAllLoadoutskohdassa0x141420b24. Siksi pyyntöjä tulee jatkuvasti//-merkkeineen.
Miksi suora käynnistys ei koskaan saa sitä
Koko kirjautumis- ja hahmonvalintakulku on kartan sisällä. Map_LoginMenu ajaa Blueprint- ja UMG-näkymiä (LoginScreen_bps, PressStartScreen_bps ja muita), ja ne ohjaavat Phoenixin identiteettirajapintaa. Sikäli kuin voimme päätellä, asiakasohjelmassa ei ole C++-kielistä automaattista kirjautumista: funktiolla, joka lukee AuthEndpoint-arvon, ei ole suoria kutsujia. GameDefaultMap-ohitus ei viivästytä tuota kulkua. Se poistaa sen.
Mitä suljimme pois
| Idea | Tulos |
|---|---|
?AuthToken=... käynnistys-URL:ssa | Ei oteta käyttöön |
?AccountId=... käynnistys-URL:ssa | Ei oteta käyttöön |
| Asetusavain, joka pakottaa kirjautumisen ennen karttaa tai syöttää tunnuksen | Ei löytynyt. GameMapsSettings-asetuksissa ei ole sellaista valintaa. |
| Komentorivin valitsin | Ei yhtään. Listasimme ohjelmatiedoston jokaisen valitsimen näköisen merkkijonon. Ainoat Archon-kohtaiset ovat -DISABLE_MATCHMAKER_AUTH, -GAMESERVER_INSTRUCTION_FILE= ja -GAMESERVER_STATUS_FILE=. -AUTH_* on EOS:n oma automaattinen kirjautuminen Epic-identiteetille, ei Phoenix-identiteetille. |
| Taustapalvelun vastaus, joka asettaa tunnuksen | Ei yhtään. Suora käynnistys ei koskaan tee sitä ainoaa pyyntöä, jonka vastaus sisältää tunnuksen. |
| Konsoli | ALLOW_CONSOLE on jätetty pois käännöksestä |
Mistä jatkaa
Reitti, jonka valitsisimme nyt
Anna tavallisen kirjautumisen mennä läpi ja siirry sitten oikealle palvelimelle.
- Valmistuneen kirjautumisen jälkeen Phoenix-identiteetti ja paikallinen pelaaja elävät peli-instanssissa (game instance), eivät maailmassa (world). Identiteetti siis säilyy myöhemmässä kartan vaihdossa (todennäköistä olioiden rakenteen perusteella; ei testattu).
- Asiakkaan oma tie kaupunkiin on matchmaking.
UArchonOnlineSessionClient::TravelToCitypyytäämm2-prod-palvelulta kaupungin istuntoa ja ottaa sitten yhteyden yhteysmerkkijonolla%s:%d?ticket=%s?gameSessionId=%s?EncryptionToken=%s. Se on aina palvelin ja portti, ei koskaan paikallisen paketin nimi. - Tarvitset siis isännän. 2.1.1-ohjelmatiedosto ei pysty isännöimään yksinään, mutta ohjelmaan ladattu DLL voi saada saman ohjelmatiedoston toisen kopion isännöimään. Näin moninpeli toimii kuvaa, miten Undaunted tekee tämän versiossa 1.4.4. Asiakkaan odottama matchmaking-sopimus (
/candidate/status,serverInfo,isNewMatchmaker,playerStates) on sivulla Taustapalvelun rajapinta. - Älä lataa Undauntedin DLL:ää versioon 2.1.1. Se paikkaa kiinteitä osoitteita 1.4.4-ohjelmatiedostossa ja sotkisi muistin missä tahansa muussa versiossa. Jokainen osoite on etsittävä uudelleen versiolle 2.1.1. Toinen yhteisöprojekti, Mystic Paradox, siirtää samaa lähestymistapaa versioon 1.12.0. Emme ole kokeilleet sitä.
2.1.1-osoitteet, joista aloittaa
Staattiset osoitteet, perusosoite (image base) 0x140000000. Nimet ovat meidän, ja ne on otettu kunkin funktion lokimerkkijonoista. Julkaisuversion ohjelmatiedostossa ei ole symboleja.
| Mikä | Osoite |
|---|---|
UWorld::Listen, tynkä: xor al, al / ret | 0x140bfde50 |
Sen kutsu UEngine::LoadMap-funktiossa | 0x14537140e |
UWorld::GetNetMode, aina NM_Client, kun verkkoajuri on olemassa | 0x1453e4ef0 |
GIsClient / GIsServer | 0x1488ea0f2 / 0x1488ea0f3 |
GIsClient-kirjoitus FEngineLoop::PreInit-vaiheessa (rekisteristä; tavallisessa pelin ajossa 1) | 0x140c0f683 |
UIpNetDriver::InitListen, ehjä | 0x140c6d200 |
UGameInstance::StartGameInstance (käynnistys-URL) | 0x144dba330 |
UGameInstance::CreateGameModeForURL | 0x144da42c0 |
AArchonPlayerController::PostInitializeComponents (rekisteröi lataajat) | 0x1429dbb60 |
AArchonPlayerController::OnPostLogin | 0x1429d6ea0 |
UArchonLoadManager::Begin / Complete / LoadFailed | 0x14299b670 / 0x14299d860 / 0x1429ad0f0 |
AArchonPlayerController::OnPlayerDataLoadComplete | 0x1429d6900 |
GetPlayerAccountProgressState | 0x1429d0160 |
ULoginScreen::AdvanceToPlay | 0x142d128f0 |
FJsonSerializable::FromJson(const FString&) | 0x140c2fbe0 |
JSON int32 -lukija (tarkistaa tyypin, ohittaa kentän, jos tyyppi ei täsmää) | 0x140c3a870 |
FCandidateStatus::Serialize / FServerInfo::Serialize | 0x1414c2a10 / 0x1414c7bf0 |
FThreadHeartBeat::InitSettings (jumiutumisen tunnistimen asetukset) | 0x142ec08c0 |
Jos pysyt itsenäisessä käynnistyksessä
- Vipu olisi jokin, joka asettaa paikallisen pelaajan Phoenix-tunnuksen ennen kuin lataajat käynnistyvät. Mikään asetuksissa tai komentorivillä ei tee sitä. Se vaatisi ohjelmaan lisättyä koodia.
- Varaudu siihen, että edellä kuvattu edistymisen valmiuslippu on seuraava muuri.
- Seuraa
playerdata_load_failed-telemetriatapahtumaa. Senloaders-taulukko nimeää jokaisen lataajan ja kertoo, valmistuiko se. Se ei tosin ehkä koskaan laukea. Sen ajastimen tahti tulee kentästä, jolle emme löytäneet kirjoittajaa.
Miksi siirryimme versioon 1.4.4
- Undaunted pyörittää jo moninpeli-Ramsgatea, metsästyksiä ja opetusosiota versiossa 1.4.4 alkuperäisellä asiakasohjelmalla ja ohjelmaan ladatulla palvelin-DLL:llä. Versiossa 2.1.1 olisimme joutuneet rakentamaan kaiken tämän itse.
- 1.4.4 on EOS:ää vanhempi. Pelaajat kirjautuvat omalla palvelimellamme olevalla tilillä. Epic-tiliä ei tarvita, eikä mikään Epicin palvelu voi sammuttaa sitä.
- 2.1.1-työmme on edelleen hyödyksi. Molemmat versiot puhuvat samalle Phoenix-palvelulle. Niiden päätepisteasetuksissa on 170 yhteistä avainta, ja niistä 167:n arvot ovat tavu tavulta samat; kolme eroavaa liittyy äänichattiin. Vastausten muodot voivat silti erota versioiden välillä.
- Hinta: kaikki lokakuun 2020 jälkeen julkaistu sisältö puuttuu versiosta 1.4.4.