Taustapalvelun rajapinta
Dauntless keskustelee Phoenix Labsin verkkopalvelujen kanssa HTTPS-yhteyksillä (salatuilla verkkoyhteyksillä). Nuo palvelut suljettiin pelin mukana, joten ainoa jäljellä oleva täydellinen kuvaus niistä on asiakasohjelman (pelaajan koneella toimivan peliohjelman) koodi, joka kutsuu niitä ja lukee niiden vastaukset. Tämä sivu on tuo kuvaus: palvelinnimet, kuorisäännöt ja ne päätepisteet (verkko-osoitteet, joihin peli lähettää pyyntönsä), joiden tarkat vastausmuodot olemme saaneet selville. Vastaukset ovat JSON-muotoista tekstiä (yleinen tapa, jolla ohjelmat lähettävät tietoa toisilleen), ja kuori tarkoittaa yhteistä kehystä, jonka sisään varsinainen tieto pakataan.
Suurin osa on luettu 2.1.1-asiakasohjelmasta (viimeinen julkaisu, UE5). Kun tiedämme, miten 1.4.4 (lokakuu 2020, UE4, versio jota pelaamme) käyttäytyy, kerromme sen. Sitä, miten muodot luetaan ohjelmatiedostosta, käsitellään sivulla JSON-rakenteen lukeminen ohjelmatiedostosta.
Sisällys
- Kuinka varmoja olemme
- Ei PlayFab:
OnlineSubsystemPhoenixosoitteessa steelyard.ca - Palvelinnimet
- Säännöt, jotka koskevat jokaista päätepistettä
- Kuori
- Kirjautuminen ja istunto (2.1.1)
- Hahmot (
dauntless-prod) - Käyttöoikeudet (
auth-prod) - Varustukset (
loadout-prod) - Tavaraluettelo (
dauntless-prod) - Eteneminen (
progression-prod) - Kohortit ja postilaatikko
- Tila, tietojen siirto ja Gauntlet
- Matchmaking ja siirtyminen (
mm2-prod) - Telemetria mittarina
- Miksi näillä päätepisteillä on väliä: pelaajatietojen lataajat (2.1.1)
- 2.1.1 ja 1.4.4 rinnakkain
- Avoimet kysymykset
Kuinka varmoja olemme
Jokaisella tämän sivun muodolla on yksi seuraavista merkinnöistä.
| Merkintä | Merkitys |
|---|---|
| testattu | Palvelimemme lähetti tämän oikealle asiakasohjelmalle, ja asiakasohjelma toimi sen mukaan näkyvästi: se teki ketjun seuraavan kutsun, loi hahmon tai jatkoi toimintaansa kohdassa, jossa se ennen kaatui. |
| koodista luettu | Luettu asiakasohjelman jäsentimestä ohjelmatiedostossa. Palvelimemme saattaa lähettää sen, mutta emme ole nähneet todisteita siitä, että asiakasohjelma käytti sitä. |
| Undaunted | Se, mitä Undauntedin palvelin lähettää 1.4.4-asiakasohjelmalle, jolla sen kautta oikeasti pelataan. Tämä osoittaa, että asiakasohjelma hyväksyy muodon. Se ei osoita, että asiakasohjelma jäsentää sen. Useat näistä lataajista jatkavat, vaikka jäsennys epäonnistuisi. |
| varmistamaton | Päätelty, tai lähteemme ovat eri mieltä. Tekstissä kerrotaan kumpi. |
Osoitteet ovat staattisia virtuaaliosoitteita version 2.1.1 tiedostossa Dauntless-Win64-Shipping.exe, kantaosoite 0x140000000. Ne eivät päde versioon 1.4.4.
Ei PlayFab: OnlineSubsystemPhoenix osoitteessa steelyard.ca
Jotkin aiemmat yhteisön kirjoitukset väittivät Dauntlessin taustapalvelun olevan PlayFab. Se ei ole. Version 2.1.1 151 megatavun ohjelmatiedostossa on täsmälleen kaksi PlayFab-polkua (/Client/UpdateUserTitleDisplayName ja /Server/WriteTitleEvent) eikä PlayFab-kirjautumista lainkaan. Taustapalvelu on OnlineSubsystemPhoenix: Phoenix Labsin oma REST-rajapinta steelyard.ca-verkkotunnuksessa.
Päätepistetaulukko on paketoiduissa asetuksissa (pelin mukana toimitetuissa, valmiiksi käsitellyissä asetustiedostoissa): DefaultGame.ini, osio [OnlineSubsystemPhoenix]. Jokainen päätepiste on avain, jonka arvo on osoitepohja, esimerkiksi:
EntitlementsEndpoint = "https://auth-{environment}.steelyard.ca/entitlementsv2"
{environment} saa arvon prod. Laskujemme mukaan version 2.1.1 osiossa on 198 avainta, joiden arvo on http(s)-osoite, ja version 1.4.4 osiossa 166, ja lisäksi kummassakin yksi ws://-avain (presence-yhteys). Osa on kehitys- tai testiympäristön merkintöjä tai _v2-kaksoiskappaleita.
Tiedoston saaminen ulos:
- 2.1.1 pitää asetuksensa tiedostossa
Archon_50-WindowsClient.pak. Se on pakattu Oodlella, eikä pelin mukana tuleoo2core-DLL:ää (Oodle on linkitetty staattisesti), joten tavalliset pak-työkalut eivät saaneet sitä auki. Käänsimme avoimen lähdekoodinooz-purkajan jaetuksi kirjastoksi ja ohjasimme sitä Pythonista. - 1.4.4 käyttää pak v9 -muotoa, jossa on salaamaton vanhanmallinen indeksi ja zlib-pakkaus. Asetukset ovat tiedostossa
Archon_35-WindowsClient.pak. Pieni pak v9 -lukija riittää.
Salaisuudet julkaistuissa asetuksissa. Version 2.1.1 paketoiduissa asetuksissa on toimiva Slack-webhook-osoite (avain
PhoenixEventsMessageEndpoint) sekä Epic Online Services -palvelun asiakassalaisuus ja salausavain selväkielisinä. Version 1.4.4 asetuksissa on myös Slack-webhook. Mitään näistä ei toisteta tässä, eikä yksityispalvelin tarvitse niistä yhtäkään.
Palvelinnimet
Jokainen palvelinnimi noudattaa kaavaa <service>-prod.steelyard.ca, muutamaa poikkeusta lukuun ottamatta. Version 2.1.1 tutkimuspalvelimemme vastasi niihin kaikkiin niin, että jokainen nimi ohjattiin hosts-tiedostossa (Windowsin tiedosto, joka kertoo, mihin koneeseen mikäkin verkkonimi osoittaa) palvelimelle itselleen (sekä 127.0.0.1 että ::1). Undaunted tekee sen versiossa 1.4.4 toisin: sen ohjelmaan ujuttama DLL (ohjelmakirjasto, jonka koodi liitetään käynnissä olevaan peliin) koukuttaa asiakasohjelman asetushaun ja kirjoittaa jokaisen [OnlineSubsystemPhoenix]-osoitteen uudelleen tavalliseksi http://-osoitteeksi metagame-palvelimen (Undauntedin taustapalvelu, joka korvaa Phoenixin verkkopalvelut) omaan osoitteeseen, joten hosts-tiedostoa tai TLS-salausta ei tarvita. Pelipalvelinprosessit saavat saman vaikutuksen käyttäjän Game.ini-tiedostoon lainausmerkeissä kirjoitetuista ohituksista (katso alla).
| Palvelinnimi | Mihin asiakasohjelma sitä käyttää | Kuori |
|---|---|---|
auth-prod | Salasanakirjautuminen, tilitiedot, tunnisteet (tags), porttikieltotarkistus, käyttöoikeudet (entitlements) | litteä |
gamesession-prod | Epicin tunnisteen vaihto Phoenixin istuntotunnisteeksi, tilien linkitys, alustan ominaisuudet | kuoressa |
login-queue-prod | Kirjautumisjono, huoltokatkon tila | litteä |
dauntless-prod | Hahmot, tavaraluettelo, kilta | litteä; GET /character on paljas taulukko |
loadout-prod | Varustukset (loadouts) | kuoressa |
progression-prod | Eteneminen, pelaajan polku (/pjm), odotusajat (cooldowns), escalation, palkkiotehtävät (bounties), pelin viritysarvot (game tuning) | sekalainen; katso Eteneminen |
mm2-prod | Matchmaking (pelaajien yhdistäminen samaan peliin), porukat (parties), äänikanavalle liittyminen | katso Matchmaking |
presence-prod | Istunnon WebSocket-yhteys, tavallinen ws:// portissa 80 | — |
gauntlet-prod | Gauntlet (vain 2.1.1) | kuoressa |
mailbox-prod | Posti, tapahtumatilastot, päivitysmuistiinpanot, kyselyt | kuoressa siltä osin kuin luimme |
cohort-prod | A/B-testien käsittelyryhmät (treatments) | kuoressa |
migration-prod | Pelaajatietojen siirto alustalta toiselle (vain 2.1.1) | litteä |
store-prod | Kauppa, täsmäytys (reconcile), sisällöntuottajakoodit | litteä siltä osin kuin luimme |
tracking-prod, telemetry-ingest-prod, telemetry | Sykeviesti (heartbeat), analytiikkatapahtumat, lokien lähetys | — |
breadcrumbs-prod, guild-prod, leaderboards-prod, motd-prod, profanity-filter-prod, social-prod, subscription-prod | Kuten nimi kertoo | — |
steelyard.online/dauntless-status | Tilabanneri, tavallinen http:// | litteä |
cdn.playdauntless.com, store.playdauntless.com | Uutiskuvat, verkkokauppa | — |
“Kuoressa” tarkoittaa, että vastaus on kääritty yhteiseen kehykseen (katso Kuori). “Litteä” tarkoittaa, että vastaus on tavallinen JSON-olio ilman kuorta.
Säännöt, jotka koskevat jokaista päätepistettä
Nämä pätevät versioon 2.1.1, jonka käsittelijät luimme. Emme ole nähneet version 1.4.4 rikkovan yhtäkään niistä.
- Vain tilakoodit 200–206. Jokainen lukemamme valmistumiskäsittelijä tarkistaa tilakoodin käskyillä
add eax, 0xffffff38 / cmp eax, 6 / ja <fail>, mikä tarkoittaa200 <= status <= 206. Kaikki muu ohjautuu epäonnistumishaaraan ennen kuin runkoa luetaan. Kirjautumisjono haluaa täsmälleen koodin 200. - Ylimmällä tasolla JSON-olio. Lähes jokainen vastaus kulkee funktion
FJsonSerializable::FromJson(const FString&)(0x140c2fbe0) kautta, ja se vaatii JSON-olion. Paljas taulukko ei jäsenny. Tuo epäonnistuminen on turvallinen, mutta useimmiten vastausta odottava lataaja ei koskaan valmistu. Ainoa poikkeus onGET /character, joka jäsennetään taulukkona. - Puuttuvat tai väärän tyyppiset kentät eivät ole virheitä. Tyypitetyt lukijat ohittavat kentän, joka puuttuu tai on väärää JSON-tyyppiä, ja jättävät C++-jäsenen ennalleen. Osa vastausrakenteista on pinossa (ohjelman väliaikaisessa työmuistissa) eikä niitä koskaan nollata, joten “ennalleen” voi tarkoittaa alustamatonta muistia.
"7777"siellä, missä asiakasohjelma odottaa arvoa7777, ei ole validointivirhe: kenttä ohitetaan hiljaa, ja rakenteessa, jota ei ole koskaan nollattu, se tarkoittaa määrittelemätöntä käytöstä. Yksityiskohdat ovat takaisinmallinnussivulla. - Tuntematon reitti:
404tyhjällä rungolla. Älä koskaan vastaa tuntemattomaan reittiin{}. Juuri se kaatoi asiakasohjelmamme (katso Väärä muoto kaataa). - Kuuntele sekä IPv4:ää että IPv6:ta. Hosts-tiedosto ohjaa jokaisen nimen sekä osoitteeseen
127.0.0.1että::1, joten asiakasohjelma voi ottaa yhteyden kumman tahansa kautta. Version 2.1.1 palvelimellamme (uvicorn) sidomme kummankin osoiteperheen erikseen, koska::-sidonta asettaaIPV6_V6ONLY-valinnan. - TLS pelin omaa CA-kokoelmaa vasten. Asiakasohjelma tarkistaa palvelinvarmenteet mukana tulevaa
cacert.pem-tiedostoa vasten (luettelo varmentajista, joihin peli luottaa), ei Windowsin varmennevarastoa vasten. 2.1.1 lukee pelikansiosta irrallisen tiedostonEngine/Content/Certificates/cacert.pem, ja sinne laitoimme oman yksityisen varmentajamme version 2.1.1 testausta varten. 1.4.4 pitää kokoelmansa tiedostonArchon_0-WindowsClient.paksisällä. Emme koskaan koske Windowsin luottamusvarastoon. (Undaunted välttää koko kysymyksen versiossa 1.4.4 kirjoittamalla jokaisen osoitteen uudelleenhttp://-muotoon, kuten kohdassa Palvelinnimet kerrotaan.) - Laita osoitteet lainausmerkkeihin käyttäjän ini-ohituksissa. Päätepisteet voi ohittaa käyttäjän asetuksissa,
%LOCALAPPDATA%\Archon\Saved\Config\WindowsClient\Game.ini. Meidän 1.4.4-kokoonpanomme ohjaa pelipalvelinprosessinsa metagame-palvelimeen tällä tavalla. Jos osoite jää ilman lainausmerkkejä, ini-jäsennin tulkitsee merkit//kommentin aluksi ja katkaisee arvon muotoonhttps:.
Kuori
Monet palvelut käärivät vastauksensa kuoreen:
{"code": "OK", "message": "", "payload": { }}
payload on yleensä olio. Se voi olla myös taulukko (gauntletin tulostaulukko) tai pelkkä luku (gauntletin kiltapalkinnot). Muistissa kääre on järjestetty näin (vtable on olion funktiotaulun osoite): vtable +0x00, code +0x08, message +0x18, payload +0x28.
Jos payload puuttuu tai on väärää JSON-tyyppiä, jäsennys onnistuu silti, eikä sisältörakenteeseen kosketa. Litteä runko, joka lähetetään kuorta odottavaan päätepisteeseen, siis “toimii”: ei virhettä, ei uudelleenyritystä, vain oletusarvot. Niin kävi kutsun GET /account/link/... kanssa: se antoi saman tuloksen litteällä rungolla {"isLinked": true} ja {"isLinked": false}, koska asiakasohjelma ei lukenut kumpaakaan.
Oletimme ensin, että kuorta käyttää vain gamesession-*. Käsittelijöiden lukeminen osoitti, että kääre on paljon yleisempi:
| Muoto | Missä (2.1.1) |
|---|---|
Kuoressa, code on merkkijono | gamesession-*, loadout-*, gauntlet-*, cohort-*, mailbox-* (/eventstats/, /all/), ja progression-prod-palvelussa: /progression/config, /escalation/..., /progression/{a}/{p} sekä staattisen luentamme mukaan /pjm |
Kuoressa, code on int32 ja payload taulukko | progression-prod /progression/{accountid} ja /progression/objectives/{accountid} |
| Litteä olio | auth-*, dauntless-* (paitsi GET /character), login-queue-*, migration-*, store-*, progression-prod /cooldown ja /bounty, dauntless-status |
| Paljas taulukko | GET dauntless-prod/character |
Millään jäljittämällämme polulla mikään ei toimi code-kentän perusteella. (Loadout-käsittelijä muuntaa sen virhearvoksi, mutta sen kuuntelija ei koskaan katso tuota arvoa.) Undaunted lähettää versiolle 1.4.4 lähes jokaisessa kuorellisessa vastauksessa "code": null, ja 1.4.4 toimii sen kanssa; poikkeus on /candidate/regions, joka saa arvon "code": 200. Lähetä silti merkkijono (tai luku int32-muunnelmalle), jotta kenttä ainakin jäsentyy.
Kirjautuminen ja istunto (2.1.1)
Tämä on version 2.1.1 käynnistysketju siinä järjestyksessä, jossa asiakasohjelma sitä kutsuu. Näillä vastauksilla asiakasohjelma kirjautuu sisään, ja FOnlineIdentityPhoenix ilmoittaa kirjautumisen valmistuneen oikealla tilitunnisteella. Se todistaa jokaisen alla testatuksi merkityn rivin toimivan alusta loppuun.
| # | Kutsu | Vastaus | Merkintä |
|---|---|---|---|
| 1 | POST login-queue-prod/login | litteä, täsmälleen viisi kenttää (alla) | testattu |
| 2 | GET gamesession-prod/features/platform/{platform} | crossplay, crossprogression (totuusarvoja) | testattu, katso huomautus |
| 3 | GET gamesession-prod/account/link/{service}/{accountid} | kuoressa, payload {"isLinked": true} | testattu |
| 4 | PUT gamesession-prod/gamesession/{linkedaccountservice} | kuoressa, payload {"sessiontoken", "sessionid"} | testattu |
| 5 | GET auth-prod/accountinfo | litteä {"username", "accountId"} | testattu |
| 6 | GET auth-prod/tags, GET auth-prod/isbanned | vastattu; muotoa ei luettu | — |
| 7 | GET dauntless-prod/character ja siitä eteenpäin | katso Hahmot | testattu |
Kirjautumisjono. Vastauksen lukee käsin kirjoitettu läpikävijä (visitor), joka tuntee täsmälleen viisi avainta:
{"state": "OPEN", "error_code": "", "title": "", "message": "", "timeout": 5000}
UArchonLoginQueueClient päästää pelaajan läpi vain, kun state on OPEN (kirjainkoolla ei ole väliä). Mikä tahansa muu tila, tai state-kentän puuttuminen kokonaan, saa sen kysymään uudelleen max(timeout * 0.001, 5.0) sekunnin kuluttua. Muut avaimet jätetään yksinkertaisesti huomiotta: varhaisissa vastauksissamme oli kenttiä kuten position ja ready mutta ei state-kenttää, ja asiakasohjelma jatkoi kyselemistä 5 sekunnin välein. timeout-kentän on oltava JSON-luku. Undaunted lähettää samat viisi avainta versiolle 1.4.4 (error_code "TicketRateOk", timeout 8000).
Alustan ominaisuudet. Kaksi staattista luentaamme olivat eri mieltä siitä, onko tämä kuoressa, joten 2.1.1-palvelimemme lähettää molemmat muodot samassa rungossa; tuntemattomat avaimet jätetään huomiotta. Undaunted lähettää versiolle 1.4.4 vain kuorellisen muodon.
Istuntotunniste. Asiakasohjelma lähettää Epic-tunnisteensa muodossa Authorization: BEARER <jwt> ja odottaa takaisin kenttiä payload.sessiontoken ja payload.sessionid. Siitä eteenpäin sessiontoken on bearer-tunniste kaikkiin muihin palveluihin. Polun osa on versiossa 2.1.1 epiceos ja versiossa 1.4.4 epic (Undauntedin mukaan), ja se täytetään samaan {linkedaccountservice}-pohjaan. Epic myöntää JWT-tunnisteen uudelleen, joten bearer vaihtuu kirjautumisten välillä. Ota pelaajan identiteetti sen sijaan JWT:n sub-kentästä (claim), tai pelaaja saa uuden hahmon jokaisella kirjautumisella.
Tilitiedot. Litteä, ei käärettä. Tässä palautettu accountId menee jokaiseen myöhempään {accountid}-polun osaan, joten sen on pysyttävä samana samalle pelaajalle. Undauntedin 1.4.4-vastaus lisää kentät creationDate, email, preferredLanguage ja verified.
Muut identiteettipäätepisteet (koodista luettu). POST auth-prod/game/login (AuthEndpoint, sähköposti- ja salasanapolku) luetaan litteänä muotona {"displayName", "accountId", "token"}, ja kaikki kolme ovat pakollisia. Version 2.1.1 Epic-kirjautuminen ei koskaan kutsu sitä, joten emme ole kokeilleet sitä. Undauntedin 1.4.4-kokoonpano käynnistää pelin valitsimella -AUTH_TYPE=exchangecode ja vastaa itse Epic-tyyliseen tunnisteenvaihtoon. Sen DLL ohjaa myös AuthEndpoint-osoitteen uudelleen, mutta sen palvelimella ei ole käsittelijää polulle /game/login, joten päättelemme, ettei 1.4.4 kutsu sitä siinäkään kokoonpanossa (varmistamaton). PUT gamesession-prod/account (Phoenix-tilin luonti) tavoitetaan vain, kun isLinked on false. Sen payload on {"id", "sessiontoken"}.
Presence-WebSocket (koodista luettu). ws://presence-prod.steelyard.ca/ws/{accountid}, tavallinen ws:// portissa 80. Asiakasohjelma pyytää täsmälleen yhtä aliprotokollaa, json, ja 101-vastauksen pitäisi toistaa se (WebSocket-asiakas voi hylätä kättelyn, joka ei toista sitä). Luentamme mukaan asiakasohjelma ei koskaan lähetä kehystä ja jättää vastaanottamansa huomiotta, joten koko sopimus on “hyväksy yhteys ja pidä se auki”. Mikään 2.1.1-ajomme ei ole vielä avannut tätä yhteyttä, joten tätä ei ole testattu.
Hahmot (dauntless-prod)
Litteitä, ja kaikki testattuja versiossa 2.1.1.
| Kutsu | Pyyntö | Vastaus |
|---|---|---|
GET /character | — | paljas taulukko muotoa {"id", "name", "updateVersion", "data"} |
PUT /character (luonti) | {"name": "..."} | paljas {"id", "name"}; vain nämä kaksi luetaan |
POST /character (tietovaraston tallennus) | {"characterId", "data", "updateVersion"} | paljas olio; vain data luetaan |
[
{
"id": "<character id>",
"name": "Slayer",
"updateVersion": 3,
"data": "{\"PlayerAccountProgressStep\":\"EnteredRamsgate\"}"
}
]
Kaksi asiakasohjelman osaa lukee kutsun GET /character. Hahmoluettelo tarvitsee kentät id ja name. FPhoenixCharacterDataStore etsii alkion, jonka id vastaa sen hahmoa, ja tarvitsee sitten updateVersion-kentän JSON-lukuna ja data-kentän JSON-merkkijonona (tai null). Jos jompikumpi puuttuu, se kirjoittaa lokiin “Failed To Parse All Required Fields”, hakee uudelleen enintään kuusi kertaa ja epäonnistuttaa sitten kirjautumisen. Luettelon kääriminen muotoon {"characters": [...]} antaa virheen Failed to get all characters: ParseError.
updateVersion. Asiakasohjelma kasvattaa välimuistissa olevaa versionumeroaan ennen kuin se lähettää tallennuksen. Tallenna luku täsmälleen sellaisena kuin se lähetettiin. Jos palvelin lisää siihen 1, se menee asiakasohjelman edelle, ja seuraava luku näyttää ristiriidalta. Undaunted hylkää tallennuksen koodilla 409, kun tallennettu versio on jo suurempi tai yhtä suuri kuin saapuva. Se sääntö on sopusoinnussa tämän kanssa.
data-möhkäle on JSON-merkkijono, joka sisältää JSON-olion, ja sen jokaisen arvon on oltava merkkijono. Asiakasohjelma syöttää jokaisen arvon funktion FJsonValue::TryGetString läpi (kutsu osoitteessa 0x14142ba4a). Ensimmäinen arvo, joka ei ole merkkijono, nollaa onnistumislipun ja tyhjentää koko varaston, joten yksi ainoa JSON-arvo true heittää hiljaa pois kaikki muut avaimet. Asiakasohjelma itse kirjoittaa arvon "true" lainausmerkeissä olevana merkkijonona.
Avain, joka ratkaisee, minne uusi hahmo menee, on PlayerAccountProgressStep. Sen arvo on luettelotyypin nimi ilman etuliitettä:
| Arvo | Arvo | ||
|---|---|---|---|
New | 0 | EnteredRamsgate | 4 |
SavedCharacter | 1 | FinishedFirstHunt | 5 |
TrainingGroundsComplete | 2 | FinishedSecondHunt | 6 |
DefeatedGnasher | 3 | mikä tahansa muu | “Unknown” |
ULoginScreen::AdvanceToPlay (0x142d128f0) lähettää tilan 0 alkuelokuvaan ja hahmonluontiin, tilan 1 harjoitusalueelle (training grounds), tilan 2 metsästysmaille (hunting grounds) ja tilan 3 tai suuremman Ramsgateen. “On saapunut Ramsgateen” tarkoittaa kaikkialla, mihin katsoimme, ehtoa state >= 4. Komentorivivalitsin -ForceFTUEFlow pakottaa opetusosion tilasta riippumatta. Uusi hahmo, joka luodaan datalla
{"PlayerAccountProgressStep": "EnteredRamsgate"}
ohittaa opetusosion. Älä lisää avainta HasFinishedTutorial. Pelkkä sen olemassaolo (arvoa ei koskaan tarkisteta) nostaa tilan arvoon 6, mikä kytkee päälle MOTD-näkymän (päivän viesti; AArchonHUD::CanDisplayMOTDScreen vaatii >= 6). Näkymä yrittää sitten hakea sisältöä, jota yksityispalvelimella ei ole. (2.1.1, koodista luettu.)
Käyttöoikeudet (auth-prod)
GET /entitlementsv2 (2.1.1, koodista luettu):
{"entitlements": []}
Tämä on litteä olio, jossa on yksi avain; se ei ole paljas taulukko eikä kuoressa. Serialize 0x14145f960 lukee vain ASCII-avaimen entitlements ja tarkistaa, että se on taulukko. Jokaisella alkiolla on name (merkkijono), duration (int32) ja activatedDate (merkkijono). Nimikenttä on name, ei entitlement.
Tällä on enemmän merkitystä kuin näyttää. Käyttöoikeuksien lataaja on version 2.1.1 pelaajatietojen riippuvuusketjun juuressa (katso Lataajat). Niin kauan kuin vastasimme [], asiakasohjelma pyysi käyttöoikeuksia uudelleen jokaisella latauskierroksella eikä koskaan pyytänyt mitään polusta /progression/*.
1.4.4: Undaunted vastaa {"code": null, "message": "OK", "payload": []}, mikä on eri muoto. Emme tiedä, odottaako version 1.4.4 jäsennin sitä vai jatkaako se vain, kun jäsennys epäonnistuu. (varmistamaton)
Varustukset (loadout-prod)
Kaikki loadout-vastaukset ovat kuoressa, ja jokaisella niissä olevalla luvulla on merkitystä.
GET /loadout/{account_id}/{character_id}/all
2.1.1. Testattu: tämä vastaus lopetti kaatumisen ja vei asiakasohjelman vakaasti loadout-järjestelmän ohi.
{
"code": "OK",
"message": "",
"payload": {
"loadouts": [],
"persistent": {
"manual_emotes": [], "intro_emote": "", "banner": "", "bannerCustomization": "",
"flare": "", "title": "", "head_accessory": "", "back_accessory": "", "pet": "",
"glider": "", "update_version": 0, "quick_chats": [], "emojis": [],
"quick_curiosities_items": [], "quickwheel": []
},
"num_account_slots": 1,
"max_account_slots": 5,
"num_character_slots": 0,
"max_character_slots": 0,
"active_index": -1,
"needs_migration": false
}
}
- Neljän paikkamäärän ja
active_index-kentän on oltava lainausmerkittömiä JSON-lukuja, janeeds_migration-kentän oikea JSON-arvofalse. Sisältörakenne on paikallinen pinomuuttuja. Sen konstruktori nollaaloadouts-taulukon japersistent-kentän, mutta ei kuutta skalaarikenttää kohdissa+0x108..+0x11c. Puuttuva tai lainausmerkeissä oleva arvo jättää pinoon roskaa.UArchonLoadout::InitializeFromOnlineLoadoutData(0x1428cd790) mitoittaa sitten taulukon summallanum_account_slots + num_character_slotsja kuolee virheeseenTrying to resize TArray to an invalid size of ~3 billion. - Lähetä
active_index: -1, kunloadoutson tyhjä. Arvolla0version 2.1.1 käynnistyksemme kaatui virheeseenEXCEPTION_ACCESS_VIOLATION reading 0x0noin 20 sekunnin kohdalla. Arvoon-1vaihtaminen vei asiakasohjelman loadout-järjestelmän ohi. Emme pysty selittämään eroa lukemamme koodin perusteella: loadout-alustaja (0x1428cda4d) palaa paikkaan 0 kummallakin arvolla, kun paikka on olemassa (selittämätön, katso Kaatumisten tutkinta). - Paikkojen yhteismäärän on oltava yli nolla. Nollalla lataus pysähtyy viestiin “Loadout active index is invalid (no loadouts exist!)”, eikä lataaja koskaan ilmoita valmistuneensa. Ehdoton yläraja on 20 paikkaa (
LoadoutSlot00..LoadoutSlot19). needs_migration: falsevalitsee tavallisen polun, joka rakentaa varustukset.truevie sen sijaan erilliseen siirtohaaraan.persistent.update_version-kentän pitäisi olla mukana.persistent-konstruktori nollaa sen ympärillä olevat kentät mutta ohittaa tämän.loadouts: []on vähäriskinen valinta. Jokaiselle tyhjälle paikalle asiakasohjelma rakentaa oletusvarustuksen oman asetuksensa[DefaultLoadout]-osion pohjalta. Käsin kirjoitettu alkio (0x280 tavua kenttiä) voi aiheuttaa virheen “Loadout slot data could not be initialized”.
Loadout-alkiot käyttävät snake_case-kirjoitustapaa: slot_index (int32, paikka, johon alkio sijoittuu; alueen ulkopuoliset alkiot pudotetaan), update_version; weapon, helmet, chest, arms, legs, lantern, player_role, subweapon ja persistent ovat kukin muotoa {"instance_id", "instance_data"}; lisäksi appearance, flask, custom_name (merkkijonoja) ja quick_items ([{"item_index", "item_id", "instance_id"}]). Tavaraluettelo käyttää samoista asioista camelCase-kirjoitustapaa. Tyylien sekoittaminen antaa hiljaa tyhjiä kenttiä.
1.4.4 (Undaunted): sama kääre ja samat kenttien nimet, oikeat varustukset, paikat 1/1/1/1 ja active_index: 0.
Muut loadout-kutsut (2.1.1, koodista luettu)
| Kutsu | Payload |
|---|---|
GET /loadout/{account_id}/slotcount, .../{character_id}/slotcount | {"num_account_slots", "max_account_slots", "num_character_slots", "max_character_slots"}, kaikki int32. Neljä määrää ovat paikallisessa pinomuuttujassa, jota ei koskaan nollata, joten älä koskaan lähetä tätä ilman kuorta. Pidä luvut yhtenäisinä /all-vastauksen kanssa. |
GET /loadout/{account_id}/{character_id} (aktiivinen varustesarja) | Yhden loadout-alkion kentät ja persistent-kentät litistettyinä payloadiin, sekä active_slot (int32). active_slot-kenttää ei nollata, joten se on lähetettävä. Yksi katselmoinneistamme havaitsi, ettei alkion slot_index- ja update_version-kenttiäkään nollata, joten lähetä nekin (varmistamaton). Ei vielä nähty liikenteessä. |
Tavaraluettelo (dauntless-prod)
Litteitä olioita, ei käärettä. Käsittelijät jäsentävät rungon tavalliseksi FJsonObject-olioksi ja poimivat taulukot nimen perusteella. (2.1.1, koodista luettu; pyyntörungot on tallennettu oikeasta liikenteestä.)
GET /inventory/{accountid}/{characterid}:
{
"stackedItems": [{"catalogId": "QI_BASIC_FLARE_DURABLE", "quantity": 5}],
"instancedItems": [{"catalogId": "WP_EB_TRAINING", "instanceId": "WP_EB_TRAINING", "updateVersion": 1},
{"catalogId": "LT_BASIC", "instanceId": "LT_BASIC", "updateVersion": 1}]
}
POST /inventory (transaktion suorittaminen). Asiakasohjelma lähettää kentät characterId, accountId, source ja transactionId sekä luettelot addInstancedItems, addStackedItems, removeInstancedItems, removeStackedItems ja saveInstancedItems. Vastaus käyttää eri avaimia, jotka kaikki ovat valinnaisia taulukoita:
{"createdInstancedItems": [], "updatedInstancedItems": [], "updatedStackedItems": [], "removedInstancedItems": []}
Jos palautat pyynnön sellaisenaan takaisin, asiakasohjelma laskee transaktion onnistuneeksi eikä saa mitään. Undaunted käyttää samoja neljää tulosavainta versiolle 1.4.4. Version 2.1.1 palvelimemme lähettää tällä hetkellä kentän deletedInstancedItems, jota asiakasohjelma ei koskaan lue. Siitä ei ole haittaa niin kauan kuin se on aina tyhjä, mutta oikea nimi on removedInstancedItems.
Esineiden kentät: catalogId (pakollinen, ja sen on oltava asiakasohjelman omassa luettelossa, muuten tulee virhe “Can’t find catalog id”), quantity (luku) pinottaville esineille, instanceId ja updateVersion (luku) yksilöllisille esineille sekä valinnainen itemData-merkkijono. Asiakasohjelman luettelo ratkaisee, onko esine pinottava vai yksilöllinen, eikä se taulukko, jossa esine saapuu. Laita kaikki neljä kenttää jokaiseen esineeseen, niin oikea haara löytää tarvitsemansa.
Epäonnistunut tavaraluettelokysely huomataan heti. Asiakasohjelma näyttää viestin “Failed to retrieve your character’s inventory.” ja palaa päävalikkoon.
Aloitusvarusteet (version 2.1.1 erillinen kaupunkikäynnistys). AArchonPlayerController::CheckForPlayerStart ei aloita pelaajaa niin kauan kuin hahmolla ei ole asetta tai lyhtyä. Asiakasohjelman oma aloituslahja (panssari, banneri, soihtu, aseen osa) ei sisällä kumpaakaan. Julkaistussa pelissä ne annettiin opetusosiossa. Palvelimemme lisää kaksi [DefaultLoadout]-osiossa nimettyä esinettä, WP_EB_TRAINING ja LT_BASIC.
Eteneminen (progression-prod)
Tällä palvelimella muodot vaihtelevat, joten tarkista jokainen päätepiste erikseen. Kaikki 2.1.1, koodista luettu, ellei toisin mainita.
| Kutsu | Muoto |
|---|---|
GET /progression/config | kuoressa (code on merkkijono); payload {"paths": [...]}, jonka on oltava taulukko |
GET /progression/{accountid} | kuoressa, code on int32, payload on taulukko etenemisratojen tietueita |
GET /progression/{accountid}/{progressionid} | kuoressa (code on merkkijono); payload on yksi radan tietue (todennäköisesti) |
GET /progression/objectives/{accountid} | kuoressa, code on int32, payload on taulukko (todennäköisesti) |
GET /escalation/{season_id}/{account_id} | kuoressa; katso alla |
GET /pjm, GET /pjm/{accountid} | nodes on solmun tunnisteella avainnettu olio; kuori varmistamaton, katso alla |
GET /cooldown/{accountid} | litteä {"cooldowns": [...]} |
PUT /cooldown/batch/{accountid} | litteä; sama sarjallistaja kuin pyynnössä |
GET /bounty/{accountid} | litteä {"bounties": [], "draft_data": {...}} |
/progression/config. Jokaisella polulla on progression_id, premium_gating_entitlement, progression_multiplier (float), requirements ([{"rank_id", "xp_required"}]), free_rewards, premium_rewards, start_date, end_date, progression_multiplier_start_date, progression_multiplier_end_date ja prestige ({"xp_per_level"}). Jokaiselle palkintoluettelossa esiintyvälle rank_id-arvolle tarvitaan vastaava vaatimus, muuten asiakasohjelma kirjoittaa lokiin “Found rewards for rank_id %d but could not find a corresponding requirement”. Version 2.1.1 ohjelmatiedostosta vakioina löytämämme ratojen tunnisteet ovat SLAYER_RANK ja WEAPONSMITH. Undaunted tarjoaa versiolle 1.4.4 oikean 10 polun asetuksen, jossa on tunnisteet season09b ja MasteryTrack_*.
Ratojen tietueet (/progression/{accountid} ja vastaavat): phx_account_id, progression_id, progress, confirmed_fremium_rank, confirmed_premium_rank (int32-lukuja) ja confirmed_date (luetaan päivämääränä). fremium on asiakasohjelmassa todella kirjoitettu noin; oikein kirjoitettuna arvo jää nollaksi.
Escalation. Asiakasohjelma pyytää tunnisteet ESC_SEASON_1–ESC_SEASON_6 jokaisella kaupungin latauksella:
{"code": "OK", "message": "", "payload": {
"escalation_level": 0, "next_level_xp": 0,
"talents_progress": [], "unlock_progress": [], "update_version": 0}}
Talent-alkiot ovat muotoa {"rank", "talent_id"} ja unlock-alkiot muotoa {"collected", "reward_id"}. update_version on int32 naapuriensa perusteella; sen lukemista emme eristäneet. Undaunted lähettää versiolle 1.4.4 saman kuorellisen muodon. Version 1.4.4 kausiluettelo, tallennusreitti ja palvelimemme säännöt (oletuksena pois päältä) ovat sivulla Escalation.
Pelaajan polku (/pjm). Asiakasohjelman oma kirjoitus, POST /pjm/{accountid}, näyttää tältä:
{"nodes": {
"Slayer_00": {"node_id": "Slayer_00", "node_status": 2, "objectives": []},
"Slayer_01": {"node_id": "Slayer_01", "node_status": 0,
"objectives": [{"online_objective_id": "...", "objective_amount": 0, "objective_status": 0}]}}}
nodes on solmun tunnisteella avainnettu olio eikä taulukko. Pyyntörunko näyttää sen, ja lukija vahvistaa sen tarkistamalla, että kyseessä on JSON-olio. node_status kulkee kokonaislukulukijan kautta. Varmistamaton: staattinen luentamme GET-käsittelijästä (UPlayerJourneyComponent::OnQueryPlayerJourneyDataComplete, 0x1427d47a0) löytää tavallisen kääreen, jonka payload sisältää {"nodes": {...}, "update_version": 0}. Version 2.1.1 palvelimemme vastaa ilman kuorta. Asiakasohjelma lakkasi yrittämästä uudelleen, mutta se ei todista mitään: kuoreton runko jäsentyy virheettä joka tapauksessa. Journey-komponentti tarkistetaan vasta, kun pelaajan hahmo (pawn) on olemassa, eikä 2.1.1-käynnistyksemme koskaan päässyt niin pitkälle. Kuorellisella muodolla on paremmat todisteet.
Odotusajat (cooldowns). Alkiot ovat muotoa {"cooldown_id", "cooldown_started_date"}. cooldown_started_date luetaan tavallisena merkkijonona eikä päivämäärälukijan kautta, esimerkiksi "2026-09-20T00:00:00.000Z". Erä-PUT-pyyntö ja vastaus jakavat saman sarjallistajan, joten saapuvan erän yhdistäminen hallussasi olevaan ja sen palauttaminen on oikein. Saman sarjallistajan käytön GET-kutsussa näimme vain välillisesti (todennäköisesti). Undaunted lähettää versiolle 1.4.4 kuorellisen cooldown-vastauksen.
Palkkiotehtävät (bounties). bounties-alkioissa on kentät bounty_id, premium_bounty, slot_index, objectives ([{"objective_id", "progress"}]), drafted_timestamp, update_version ja claimed. draft_data on {"current_draft_choices": [], "previous_draft_selections": [], "bronze_count": 0, "silver_count": 0, "gold_count": 0}. 2.1.1 ei koskaan pyydä polkua GET /bounty/game-data. Sen asetusavain, GetBountiesConfigEndpoint, on yhä ini-tiedostossa, mutta avaimen nimeä ei esiinny missään ohjelmatiedostossa, joten mikään ei koskaan hae sitä. Palkkiotehtävien asetukset tulevat sen sijaan pelin viritysmöhkäleestä bounty_game_data (GET /game_tuning/{blobid}). Undaunted tarjoaa polun /bounty/game-data versiolle 1.4.4.
Oma 1.4.4-palvelimemme: tasopalkinnot, uusinnat ja asetukset
Tämä osa koskee versiota 1.4.4 meidän palvelimellamme (oikea eteneminen, oletus), ja se perustuu ohjelmatiedostoon ja pelitesteihimme. Merkit: B luettu 1.4.4-ohjelmatiedostosta, V varmistettu pelissä, S vahva päätelmä.
- Vahvistus ei maksa mitään. 1.4.4-pelipalvelin maksaa jokaisen tason palkinnot itse reitin
/inventorykautta kolmea polkua pitkin (B0x141472fa0,0x1414876a0,0x141481920), ja pelitestimme näki jokaisen palkinnon annettavan täsmälleen kerran (V, tiekartan kohta 2.10). SiksiPOST /progression/<tili>/<rata>/<taso>/confirm/<public tai premium>vain nostaa vahvistettua tasoa eikä anna mitään. Testi vartioi, että vahvistus jättää tavaraluettelot, tavaralokin ja oikeudet ennalleen. Harmonicin haara maksoi vahvistuksessa; meidän tietokannassamme, jossa sen lunastuskirjanpito alkaisi tyhjänä, se maksaisi uudelleen jokaisen tason, jonka pelaajat ovat jo vahvistaneet. - Elite-tasojen oikeudet (valinnainen). Vain pelin oma
GrantProgressionActionnimeää oikeuden myönnön (B0x1446c84c0, käytössä kohdassa0x141487aa5). Antaako pelipalvelin season09b-tasojen pysyvät oikeudet (premium 6, 9, 29 ja 50, ilmainen 50) itse reitinPOST /entitlementv2kautta, on auki.PROGRESSION_CONFIRM_ENTITLEMENTS=1saa tasoa nostavan vahvistuksen antamaan kyseisen tason pysyvät oikeudet asetuksista (lähdeconfirm:<rata>:<taso>), ei koskaan tavaroita, valuuttoja tai määräaikaisia tehosteita (premium 16, 34, 42 ja 47). Oletuksena pois, kunnes pelitesti ratkaisee: saavuta Elite-taso 6 metsästysten XP:llä ja katso, tuleekoPOST /entitlementv2. - Uusinnat. Pelipalvelin yrittää epäonnistunutta pyyntöä uudelleen jopa 5 kertaa (
HTTPRetryCount=5, B). Myöntö (POST /progression/<tili>), jonka runko on tavu tavulta sama kuin tilin edellinen myöntö, allePROGRESSION_REPLAY_WINDOW_Ssekuntia sen jälkeen (oletus 5) eikä välissä ole muuta etenemisen kirjoitusta, saa ensimmäisen myönnön tallennetun vastauksen eikä lisää mitään; tauluunprogression_eventskirjataan rivi. Mikä tahansa välissä tullut vahvistus, nollaus tai muu myöntö tekee samasta rungosta uuden myönnön. Ikkuna pysyy selvästi pelipalvelimen myöntöjen lähetysvälin alla:UProgressionComponentlähettää jonoon kertyneet myöntönsä enintään kerranQueuedGrantTimeout-ajassa, joka on 10 sekuntia (DefaultGame.ini; kertakäyttöinen ajastin asetetaan kohdassa0x14145a4a9, B), joten kaksi tarkoituksellista myöntöä tulee yleensä noin 10 sekunnin välein ja voi olla samanlaisia. Yhteysvirheen jälkeinen uusinta tulee sekunneissa; kadonnut vastaus uusitaan vastaHTTPTimeoutSeconds=600jälkeen, minkään ikkunan ulkopuolella. Vähintään 10 sekunnin ikkuna pitäisi seuraavaa lähetystä uusintana ja hukkaisi sen XP:n. Tavoite, joka saapuu tallennettua pienempänä, kirjataan lokiin (“objective went backwards”) ja tallennetaan lähetettynä. - Asetukset.
GET /progression/configja tasolaskenta lukevat yhtä lataajaa: mukana tulevaa tiedostoavendor/progression_config.jsontai, asetuksellaPROGRESSION_CONFIG_DIR, kansiota kausitiedostoja, jotka korvaavat ratoja tai lisäävät niitä ja jotka tarkistetaan käynnistyksessä (huono tiedosto pysäyttää käynnistyksen).ACTIVE_HUNT_PASS(oletusseason09b) on Hunt Pass, jonka tili saa, kun mitään ei ole tallennettu. Älä koskaan muokkaa kautta paikallaan, kun pelaajilla on siinä etenemistä (S): tallennettu eteneminen lasketaan sen vaatimuksia vasten. - Saldot.
GET /balancejaPOST /reconcilevastaavat peliohjelman valuuttataulukkoon (se lukee kentänCURRENCY_PLATINUM, B0x14161a790,0x1415d16dc). JokainenCURRENCY_*-avain kummallakin kirjoitusasulla kertoo nyt, mitä tilin aktiivisella hahmolla on tavaraluettelossa pinona (BALANCE_FROM_INVENTORY, päällä).CURRENCY_PLATINUM_UNIVei ole mukana: ohjelmatiedosto nimeää sen vain Elite-tarjousnäkymän yhteydessä (B0x14477fc50, käytössä kohdissa0x1417ddd68ja0x1417dddeb), ei siellä, missä saldot luetaan (S).
Nämä asetukset ovat sivuilla Asetukset ja Pelin asetukset. Version 1.4.4 kauppareitit (/product/..., /token/..., /notification/...) ovat sivulla Pelin kauppa.
Kohortit ja postilaatikko
2.1.1, koodista luettu.
| Kutsu | Muoto |
|---|---|
GET cohort-prod/playertreatments/{account_id} | kuoressa; payload {"treatments": ["..."]}. Alkiot ovat merkkijonoja. |
GET mailbox-prod/eventstats/ | kuoressa; payload {"stats": [{"statname": "...", "amount": 0}]} |
GET mailbox-prod/all/ | kuoressa; payloadin omaa kenttäluetteloa ei ole selvitetty |
GET mailbox-prod/survey/config | ei selvitetty |
Cohorts-lataaja epäonnistuu avoimesti (fails open) eli päästää pelaajan silti eteenpäin. Kun kysely epäonnistuu, se palaa oletuskäsittelyyn E1000 ja ilmoittaa silti valmistuneensa. Siksi Undauntedin kuoreton 1.4.4-vastaus ({"treatments": [...]}) ei kerro meille mitään suuntaan eikä toiseen.
Tila, tietojen siirto ja Gauntlet
GET http://steelyard.online/dauntless-status (2.1.1, koodista luettu; luimme funktion alusta ret-käskyyn asti). Litteä, täsmälleen yhdeksän kenttää, kysellään noin 30 sekunnin välein koko istunnon ajan:
{"show-status": false, "en": "", "fr": "", "it": "", "es": "", "de": "", "pt": "", "ru": "", "ja": ""}
show-status-kentän on oltava oikea JSON-totuusarvo. Kumpikaan kutsukohta ei nollaa tuota tavua ennen jäsennystä, joten arvolla {} (tai "false") asiakasohjelma haarautuu alustamattoman muistin perusteella ja saattaa piirtää tyhjän tilabannerin. Kahdeksan merkkijonoa nollataan etukäteen, joten ne voi turvallisesti jättää pois, mutta niiden lähettäminen ei maksa mitään.
GET /migration/status, POST /migration/trigger (vain 2.1.1, koodista luettu). Litteä:
{"migration_finished": true, "migration_failed": false}
Tämä jumittaa eikä kaada. Arvolla {} migration_finished jää epätodeksi, ja asiakasohjelma kyselee loputtomiin.
Gauntlet (vain 2.1.1, koodista luettu). Kaikki on kuoressa.
| Kutsu | Payload |
|---|---|
GET /config | {"gauntlet_id", "start_at", "end_at", "each_level_rewards": [], "milestone_rewards": []}; päivämäärät kulkevat päivämäärälukijan kautta |
GET /leaderboard/get_leaderboard/{gauntlet_id} | taulukko muotoa {"guild_name", "guild_nameplate", "level", "remaining_sec"} |
GET /rewards/personal/{gauntlet_id}/{account_id} | kuvauksena (map) käytetty olio; jokaisella arvolla on numeerinen cleared (todennäköisesti). {} on turvallinen. |
GET /rewards/guild/{gauntlet_id}/{account_id} | pelkkä luku, esim. "payload": 0 |
GET /progression/{gauntlet_id}/{account_id} | {"personal_progression": 0, "guild_progression": 0} |
Jokainen steelyard-palvelinnimi osoittaa samaan palvelimeen, joten kaksi palvelua voi vaatia samaa polkua: /progression/{a}/{b} on sekä Gauntletin eteneminen että progression-prod-palvelun rata. Reititä Host-otsakkeen perusteella.
Kauppa (koodista luettu). POST store-prod/reconcile lähettää {"authorization"}, emmekä löytäneet vastauksen jäsennystä; se näyttää “lähetä ja unohda” -kutsulta. store-prod/creator on POST, jossa on {"slug", "authorization"}. Lähellä oleva vastausrakenne {"success", "reason"} on luultavasti sen vastaus, mutta emme saaneet niitä yhdistettyä (varmistamaton).
Matchmaking ja siirtyminen (mm2-prod)
Ramsgate ei ole kartta, jonka asiakasohjelma avaa paikallisesti. UArchonOnlineSessionClient::TravelToCity tekee kaupungille matchmaking-pyynnön (pyynnön päästä pelaamaan samaan peliin muiden kanssa). Kun matchmaking-palvelu sanoo ottelun olevan valmis, asiakasohjelma ottaa yhteyden oikeaan pelipalvelimeen UDP:n kautta. Metsästykset ja opetussaari toimivat samalla tavalla.
Korjaus. Kirjoitimme version 2.1.1 matchmaking-käsittelijämme aikomuksena pyörittää kaupunkia asiakasohjelman toisella kopiolla. Sitten päättelimme, ettei julkaistu asiakasohjelma voi toimia pelipalvelimena lainkaan, ja lopetimme siihen. Siksi siirtymisosuutta ei koskaan testattu versiolla 2.1.1. Tuo päätelmä oli väärä. Asiakasohjelman verkkokerros on ehjä, ja ohjelmaan ujutettu DLL voi ohjata sitä; näin Undaunted toimii isäntänä versiossa 1.4.4 (katso Näin moninpeli toimii). Kaikki alla oleva on version 2.1.1 osalta koodista luettua. Version 1.4.4 kulku toimii käytännössä Undauntedin kautta.
Kulku
Kaikki polut ovat suhteessa osoitteeseen MatchmakingEndpoint = https://mm2-{environment}.steelyard.ca.
| Vaihe | Kutsu |
|---|---|
| Alueet | GET /candidate/regions |
| Aloita tai liity | POST /candidate/join, tai POST /candidate/join/{candidateId}, kun liitytään tiettyyn ehdokkaaseen |
| Kysele tilaa | GET /candidate/status |
| Peru | DELETE /candidate, DELETE /candidate/leave |
| Muut | POST /candidate/player/ready, PUT /candidate/player/{playerId}/loadout, GET /candidate/players/loadout?playerIds=... |
Kutsua POST /candidate ei ole. DELETE /candidate peruu. (Version 2.1.1 tutkimuspalvelimemme tekee tämän väärin. Se kirjoitettiin ennen kuin luimme nämä käsittelijät.)
/candidate/join-pyynnön rungossa on kentät buildId, gameMode, gameType, gameArgs, regionUrlsPings (olio), isPrivate ja privateMatch (molemmat valinnaisia ja jätetään pois, kun niitä ei ole asetettu, joten älä tee niistä pakollisia), playerId, partyId, hunts (taulukko), playerHuntId, allow_crossplay, session_id, gauntlet_level, hashcode ja loadout.
GET /candidate/status
{
"candidateId": "<id>",
"status": "IN_PROGRESS",
"statusReason": "",
"gameMode": "<as requested>",
"candidateStatusPeriodMillis": 1000,
"serverInfo": {
"gameSessionId": "<id>",
"instanceId": "<id>",
"host": "127.0.0.1",
"port": 7777,
"gameArgs": ""
}
}
Kentät, jotka asiakasohjelma lukee (2.1.1, FCandidateStatus::Serialize 0x1414c2a10): candidateId, status, statusReason, message, gameMode, huntId, gameArgs (merkkijonoja); serverInfo (olio); playerStates (olio); playerHuntIDs (kuvaus eli map); createdTimeMillis ja lobbyDuration (luetaan float-lukijalla); idealRemainingMillis, forcedRemainingMillis, candidateStatusPeriodMillis (int32); isNewMatchmaker (bool).
statuson jokin seuraavista:NEW,MATCHING,MATCHED,QUEUED_FOR_START,IN_PROGRESS,CANCELED,FAILED.CANCELEDjaFAILEDlopettavat matchmakingin. Mikä tahansa muu kysyy uudelleen ajancandidateStatusPeriodMilliskuluttua, rajattuna välille 1–10 sekuntia (oletus 1000 ms).isNewMatchmakerratkaisee, mitkä tilat käynnistävät siirtymisen. False tai puuttuu (oletus):MATCHED,QUEUED_FOR_STARTtaiIN_PROGRESS, eikä pelaajatarkistusta. True: vainIN_PROGRESS, japlayerStates-kentän on sisällettävä paikallinen pelaaja.playerStateson pelaajan tunnisteella avainnettu olio eikä taulukko. Jokainen arvo on{"isReadyToLeaveLobby": bool, "publicLoadoutHash": "..."}. Sitä haetaan vain, kunisNewMatchmakeron true. (Undaunted lähettää versiolle 1.4.4 kirjaimellisen avaimen"UserId". Se toimii vain siksi, ettäisNewMatchmakerpuuttuu, joten hakua ei koskaan tehdä.)serverInfo-kentässä on täsmälleen viisi kenttää:gameSessionId,instanceId,host(merkkijonoja),port(int32) jagameArgs. Siinä ei oleticket-kenttää.hostei saa olla tyhjä. Tyhjä host jättää asiakasohjelman kyselemään loputtomiin, hiljaa.port-kentän on oltava JSON-luku."7777"jättää sen nollaksi. Tilatarkistus menee läpi (se katsoo vainhost-kenttää), ja sittenGetResolvedConnectStringInternalhylkää istunnon viestillä “Invalid session info”. Julkaisuversioista on poistettu tiedostoon lokitus, joten tuo viesti ei koskaan päädy lokitiedostoon.
Kuoressa vai ei? Versiolle 1.4.4 Undaunted vastaa ilman kuorta, ja se toimii käytännössä. Versiossa 2.1.1 emme saaneet tilakäsittelijää yhdistettyä mihinkään jäsentimeen. Lähellä on kaksi kääreen jäsennintä, joista toinen lukee muotoa {code, message, payload} ja toinen muotoa {statusCode, message, payload}, kummassakin int32-koodit. (varmistamaton)
/candidate/regions. Undaunted lähettää versiolle 1.4.4 kuorellisen vastauksen, code 200, payloadina {"maxPingingStepTime", "pingCount", "pingFrequency", "regionUrls"} ja ajat sekunteina. Versiossa 2.1.1 pingFrequency ja maxPingingStepTime kulkevat float-lukijan kautta. Version 2.1.1 kuori on varmistamaton.
Siirtymisosoite (2.1.1)
Yhteysmerkkijono rakennetaan tällä muotoilulla:
%s:%d?ticket=%s?gameSessionId=%s?EncryptionToken=%s
Kentät ovat palvelin (host), portti, lippu (ticket), peli-istunnon tunniste ja salaustunniste. Jokainen valinta erotetaan ?-merkillä, ei &-merkillä. Esimerkiksi:
127.0.0.1:7777?ticket=<ticket>?gameSessionId=<id>?EncryptionToken=<a>:<b>
- Lippu ja salaustunniste tulevat asiakasohjelman istuntotiedoista, eivät
serverInfo-kentästä.EncryptionTokenon itse muotoa"%s:%s"kahdesta istuntotietojen kentästä, ja se on tyhjä, kun ensimmäinen niistä on tyhjä. - Paketoitu
DefaultEngine.inikytkee päälle asetuksennet.AllowEncryption=Trueja AES-pakettikäsittelijän. POST /key/generatelähettääX-Session-Id-otsakkeen. Isännän puolella valitsin-DISABLE_MATCHMAKER_AUTHsaa funktionRedeemMatchmakerTicketohittamaan lipun tarkistuksen.- Asiakasohjelmaan käännetty palvelinpuolen koodi ilmoittaa liittyvästä pelaajasta kutsulla
POST /gamesession/playerjoinedja lähettääX-SecretKey-otsakkeen. Se lukee takaisin{"playerId", "gameSessionId", "statusCode" (int32), "message", "token", "gameId"}. -UseStandaloneDedicatedServersaa asiakasohjelman pyytämään matchmaking-palvelultaBUILDVERSION=LOCAL_<ComputerName>. Se näyttää Phoenixin omalta paikallisen palvelimen kehitystilalta. Emme ole tutkineet sitä.
Telemetria mittarina
Telemetria tarkoittaa pelin lähettämiä käyttötietoja. Nämä kutsut eivät estä mitään, mutta ne kertovat, mitä asiakasohjelma tekee. Tiedostoon lokitus on poistettu julkaisuversiosta.
tracking-prod/heartbeat(2.1.1): asiakasohjelma lähettää{state, map, server, session, ping, playtime}joka sekunti.mapon luotettavin tapa tietää, missä asiakasohjelma oikeasti on. Undauntedin 1.4.4-palvelin ottaa vastaan kutsunPOST /heartbeatrungolla{map}ja vastaa pelkällä tekstillä20000, jonka merkitystä emme tiedä.telemetry-ingest-prod/event?id=prod: analytiikkatapahtumat, muun muassaclient_login_failed. Versiossa 2.1.1UArchonLoadManager::LoadFailed(0x1429ad0f0) lähettää tapahtumanplayerdata_load_failedsisällöllä{"map", "time", "loaders": [{"name", "loaded"}]}, joka nimeää jokaisen jumiin jääneen lataajan. Sen ajastimen tahti tulee kentästä, jolle emme löytäneet kirjoittajaa. Jos tahti on 0, tapahtuma ei koskaan laukea.telemetry.steelyard.ca/log: paketoidut asetukset kytkevät päälle osion[HTTPEventLog]ilman otantaa. Periaatteessa se lähettää asiakasohjelman lokin jatkuvana virtana Splunk HEC -riveinä, ja odotettu vastaus on{"text": "Success", "code": 0}. Emme ole vielä saaneet sinne mitään liikennettä.
Miksi näillä päätepisteillä on väliä: pelaajatietojen lataajat (2.1.1)
Versiossa 2.1.1 pelaajan hahmo (pawn) ilmestyy pelimaailmaan vasta, kun jokainen rekisteröity IArchonPlayerDataLoader ilmoittaa valmistuneensa. AArchonPlayerController::OnPostLogin käynnistää funktion UArchonLoadManager::Begin (0x14299b670). Kun kaikki lataajat ovat valmiita, ajetaan OnPlayerDataLoadComplete (0x1429d6900), ja se on ainoa polku funktioon ServerRestartPlayer(). Jos jokin lataaja ei koskaan valmistu, pelaaja näkee lopulta ilmoituksen “You have been signed out”.
Lataajia on yksitoista:
| Lataaja | Päätepiste | Jos vastaus epäonnistuu |
|---|---|---|
EntitlementsComponent | auth-prod/entitlementsv2 | ei koskaan valmis; ketjun juuri |
UProgressionComponent | /progression/config, /progression/{id}, /progression/objectives/{id} | odottaa kaikkia kolmea |
HuntPassComponent | /huntpass/{id} | valmis heti, jos etenemisrajapinta puuttuu; rekisteröidään vain, kun eräs ominaisuuslippu on päällä |
UQuestSystemComponent | ei omaa päätepistettä | paikallinen tila |
UBountyComponent, _Weekly, _Daily | /bounty/{id} | — |
UCooldownComponent | /cooldown/{id} | — |
UArchonInventoryDataLoader | /inventory/{a}/{c} | virheikkuna, takaisin päävalikkoon |
AArchonLoadout | /loadout/{a}/{c}/all | ei koskaan valmis: ainoa lataaja, joka jättää “valmis”-ilmoituksen antamatta epäonnistuessaan |
CohortsComponent | /playertreatments/{id} | päästää läpi käsittelyllä E1000 |
Riippuvuudet: Entitlements → Progression → HuntPass → QuestSystem → Bounty → Bounty_Weekly → Bounty_Daily. HuntPass odottaa myös Entitlementsia, ja Bounty odottaa myös Progressionia ja Cooldownia. InventoryDataLoader → Loadout. Yksi jumiin jäänyt lataaja lähellä juurta pysäyttää kaiken sen takana. Ainoa yllätys: UArchonLoadManager::Add kirjoittaa lokiin tyhjän (null) lataajan mutta lisää sen silti, eikä tyhjä merkintä voi koskaan ilmoittaa valmistuneensa.
Toinen, myöhempi portti, AArchonPlayerController::CheckForPlayerStart, ajetaan, kun hahmo on olemassa, ja sillä on oma aikakatkaisunsa (PlayerStartEventTimeout, oletuksena 120 s). Se odottaa asetta, lyhtyä ja panssaria, pelaajan polun komponenttia, escalation-komponenttia (siitä ne kuusi ESC_SEASON_*-pyyntöä) ja varustesarjojen replikointia.
Version 2.1.1 erillisessä käynnistyksessämme palvelimemme vastaa lataajien päätepisteisiin, mutta tilitunniste on tyhjä. Asiakasohjelma ei siinä tilassa koskaan ajanut kirjautumistaan, joten hahmo ei koskaan ilmestynyt. Siksi moni tämän sivun muoto on koodista luettu eikä testattu. Yksityiskohdat ovat sivulla Version 2.1.1 yksinpelikokeilu.
2.1.1 ja 1.4.4 rinnakkain
Sama palvelu, lähes samat osoitteet. Niistä 165 http(s)-osoiteavaimesta osiossa [OnlineSubsystemPhoenix], jotka kumpikin versio määrittelee, 162:lla on tavulleen samat osoitteet. Kokonaismäärät riippuvat siitä, mitä lasketaan (mitkä avaimet, kehitysmerkinnät, _v2-kaksoiskappaleet, ws://-yhteys). Ensimmäinen vertailumme antoi tulokseksi 151/154, emmekä pystyneet toistamaan juuri tuota jakoa. Jokainen kokeilemamme laskutapa antaa kuitenkin saman vastauksen: ainoat eroavat osoitteet ovat kolme äänichatin avainta:
| Avain | 2.1.1 (EOS-ääni) | 1.4.4 (Vivox) |
|---|---|---|
VoiceChatJoinPartyEndpoint | mm2-.../evoice/join/party | mm2-.../vivox/join/party/{channel_type} |
VoiceChatJoinGameEndpoint | mm2-.../evoice/join/game | mm2-.../vivox/join/game/{game_id}/{channel_type} |
VoiceChatJoinDebugEndpoint | mm2-.../evoice/join/channel | mm2-.../vivox/join/channel/{channel_id}/{channel_type} |
Versiossa 1.4.4 on lisäksi VoiceChatLoginEndpoint, jonka 2.1.1 jätti pois. Versiossa 1.4.4 ääni kulki Vivoxin kautta, joka on maksullinen kolmannen osapuolen palvelu. Emme aio palauttaa sitä.
28 päätepistettä on vain versiossa 2.1.1. Nämä ovat steelyard.ca-palvelimien ...Endpoint-avaimia, joita versiossa 1.4.4 ei ole, joten se ei koskaan kutsu niitä:
| Alue | Avaimet |
|---|---|
| Gauntlet ja trials (11) | GauntletConfigEndpoint, GauntletEndedLevelEndpoint, GauntletGetEntryEndpoint, GauntletGrantGuildRewardsEndpoint, GauntletGuildRewardsEndpoint, GauntletLevelAccessEndpoint, GauntletLevelFinishedEndpoint, GauntletLevelRewardsEndpoint, GauntletProgressionEndpoint, GauntletSeasonLeaderboardEndpoint, GetTrialsLeaderboardsEndpoint |
| Kauppa ja aarrearkut (5) | StoreGetSteamOffersEndpoint, StoreLootboxDetailsEndpoint, StoreLootboxDrawEndpoint, StoreLootboxListEndpoint, StoreLootboxServerDrawEndpoint |
| Eteneminen (4) | SetPlayerFactionEndpoint, TrackedObjectivesEndpoint, Tracker_Endpoint, Tracker_Delete_Endpoint |
| Postilaatikko (3) | MailboxQueryTriggerConfigEndpoint, MailboxQueryTriggerSurveyEndpoint, PatchNotesGetDataEndpoint |
| Tietojen siirto (2) | PlayerDataMigrationCheckStatusEndpoint, PlayerDataMigrationTriggerEndpoint |
| Tili ja tila (3) | CloneAccountEndpoint, IsBannedEndpoint, PhoenixAlternativeStatusMessageEndpoint |
Viisi muuta avainta on vain versiossa 2.1.1, mutta ne on nimetty eri tavalla: GetPlayerFactionsEndpointClient, GetPlayerFactionsEndpointServer, GrantProgressionPurchaseEndPoint sekä Steam-transaktioiden avaimet StoreSteamInitializeTxn ja StoreSteamFinalizeTxn.
Erilainen kirjautuminen. 1.4.4 on Epic Online Servicesiä vanhempi. Se käyttää asetusta DefaultPlatformService=Phoenix Epicin vanhemman MCP-alijärjestelmän kanssa ja hyväksyy valitsimet -AUTH_TYPE=password, exchangecode tai developer. 2.1.1 kirjautuu EOS:n kautta (-AUTH_TYPE=accountportal). Istuntotunnisteen polun osa seuraa samaa linjaa: /gamesession/epic versiossa 1.4.4, /gamesession/epiceos versiossa 2.1.1.
Sama osoite ei tarkoita samaa muotoa. Undauntedin 1.4.4-palvelin ja meidän 2.1.1-luentamme ovat eri mieltä useista päätepisteistä:
| Päätepiste | 2.1.1 (staattinen luentamme) | 1.4.4 (mitä Undaunted lähettää) |
|---|---|---|
/entitlementsv2 | litteä {"entitlements": []} | kuoressa, payload: [] |
/cooldown/{id} | litteä {"cooldowns": [...]} | kuoressa |
/bounty/{id} | litteä | kuoressa, mukana päivittäisten ja viikoittaisten luonnosvalintojen tiedot (draft data) |
/eventstats/, /playertreatments/{id} | kuoressa | litteä |
/progression/objectives/{id} | payload on taulukko | payload on {"objectives", "progress_tracks"} |
/candidate/regions | kentät luetaan float-lukuina; kuori tuntematon | kuoressa, code 200, ajat sekunteina |
| Etenemisratojen tunnisteet | SLAYER_RANK, WEAPONSMITH | season09b, MasteryTrack_* |
Osa näistä on todellisia muutoksia neljän vuoden ajalta. Toiset voivat olla muotoja, jotka 1.4.4-asiakasohjelma sietää jäsentämättä niitä. Tiedämme sen vasta, kun luemme version 1.4.4 jäsentimet, mitä emme ole vielä tehneet.
Kirjainkoko. Undaunted lähettää versiolle 1.4.4 kentän sessionToken, kun taas version 2.1.1 lukija pyytää kenttää sessiontoken. Unrealin FJsonObject pitää kenttänsä TMap<FString, ...>-rakenteessa, ja FString-avaimia verrataan yleensä kirjainkoosta välittämättä, joten tällaisilla kirjainkokoeroilla ei pitäisi olla merkitystä. Tämä on pelimoottorin vakiokäytöstä; emme ole testanneet sitä kummallakaan versiolla. Kirjoitusasulla on sen sijaan merkitystä (confirmed_fremium_rank).
Avoimet kysymykset
- Onko
GET /candidate/statuskuoressa versiossa 2.1.1. - Onko
GET /pjmkuoressa (staattinen luentamme sanoo kyllä, palvelimemme sanoo ei). mailbox-prod/all/-vastauksen payload-kentät ja kaikki kutsusta/survey/config.- Version 2.1.1
UProgressionComponenttarvitsee neljä valmiuslippua ennen kuin se ilmoittaa valmistuneensa, emmekä löytäneet koodia, joka koskaan asettaisi yhtä niistä (+0x3f3). Jos sillä todella ei ole asettajaa, eteneminen ei voi koskaan valmistua erillisessä käynnistyksessämme, lähetti taustapalvelu mitä tahansa. - Onko latausvirheiden telemetria-ajastimen tahti nollasta poikkeava.
- Version 1.4.4 jäsentimet itse. Jokainen tämän sivun 1.4.4-muoto tulee Undauntedin palvelimelta, ei version 1.4.4 ohjelmatiedostosta.