feat(host): migrate non-economy UTAS routes to Rust + launcher redesign

Host/adapter (deployed to prod-host):
- POST /ut/auth (+/ut/delete/auth): Rust mints sid, opens Rust session, adopts
  persona from body; POST /openfut/account/sync full Rust envelope.
- GET /userMassInfo: full Rust (was proxy+overlay), shared build_user_mass_info.
- GET/PUT /clientdata/<key>: new clientdata_store.rs (JSON-persisted).
- GET /club/stats/{country,league,team}: context-aware club_stats_body
  (nation/league/team buckets).
- GET /store,/match/keepalive,/captcha,/tfa,/livemessage,/activeMessage: StaticAck.
- GET /watchList, /squad/0, /user: Rust handlers.
- host_test.rs updated for the new routing.

Launcher: bump gitlink to c277213 (shareholder-grade redesign + live account panel).

Docs: PRODUCTION_AUTHORITY_MATRIX, PYTHON_RETIREMENT_PLAN, MATCH_LIFECYCLE, and
route-shapes-2026-08-17 reference fixtures for the still-Python tail.
This commit is contained in:
funman300
2026-08-17 16:04:20 +00:00
parent 42fd3c7e90
commit e06fd57211
25 changed files with 2061 additions and 147 deletions
+208
View File
@@ -0,0 +1,208 @@
# FIFA 17 FUT Match Lifecycle
Design + contract reference for the **FUT match loop** as OpenFUT implements it on
the backend/responder side. Consolidates knowledge previously scattered across
`docs/PROJECT_STATE.md`, `fifa17-recon/tools/utas_server.py` (`match_route`),
`openfut-utas-host` (Rust economy END leg), `fifa17-recon/tools/test_match_lifecycle.py`,
and the vault (`Protocol Findings.md`, `Project State.md`, `Known Issues.md`).
Evidence labels: **OBSERVED** (live), **PROVEN** (test/static-analysis),
**HYPOTHESIS** (reasoned, not yet live-confirmed).
> ## Status caveat (read first)
> **No football match has ever started or completed in FIFA against this stack.**
> The lifecycle below is validated by CardsDLL static analysis (RPC descriptor
> blocks) + isolated persistence replay (`test_match_lifecycle.py`), **not** in-game
> acceptance. The reward amounts are FUT-plausible env-tunable defaults, **not**
> reversed values. Two blockers keep `FUT_MODES` **off by default** (see
> [Open questions](#open-questions--blockers)).
## Scope
This documents the **UTAS responder handshake + economy reward** for a match — the
HTTP calls CardsDLL makes around a match and the state they mutate. It does **not**
cover the actual football simulation (Blaze game-server side, "past the FUT hub"),
which remains unverified.
## The loop: a four-call state machine
CardsDLL issues six match RPCs; four form the playable loop. All share the base
path `ut/<sku>/match`; the operation is discriminated by **suffix + body**, not by
HTTP verb (verb selection lives outside CardsDLL, so the classifier is verb-agnostic).
```mermaid
stateDiagram-v2
[*] --> Created: POST /match (no matchId)
Created --> Ready: POST|PUT /match/ready {matchId}
Ready --> Playing: POST /match {matchId} (bare path + int matchId)
Playing --> Ended: POST|PUT|DELETE /match/end {matchId, endReason, ...}
Ended --> [*]: rewards credited, W/D/L + matchesPlayed persisted
```
### Call classifier (`_match_call`, utas_server.py:3325)
The discriminator (**PROVEN** from CardsDLL RPC descriptors):
1. path ends `/match/end`**END** (routed as `FutDestroyMatch` regardless of verb).
2. body has integer `matchId` on the **bare** `/match` path → **PLAY** (`FutPlayGame`).
CREATE and PLAY share `ut/<sku>/match`; the presence of an int `matchId` is the
only discriminator — this is why a bare `/match` carrying `matchId` must NOT
allocate a new match.
3. path ends `/match/ready`**READY** (`FutMatchReady`).
4. otherwise → **CREATE** (`FutCreateMatch`).
## Per-call contracts
### CREATE — `POST /ut/<sku>/match` (empty/no `matchId`)
CardsDLL: `FutCreateMatch` @ `0x180120380`; deserializes `startDateTime`(740,int),
`reportIdEnabled`(641,bool). `squad`(717,nested) is a **FREEZE-RISK** and is omitted
(SKIP-safe).
Response (`match_route`, utas_server.py:3399):
```json
{"startDateTime": <unix_ts:int>, "reportIdEnabled": false, "id": <matchId:int>}
```
Effect: allocates a match id; **advances `nextItemId` by 1** (PROVEN,
`test_match_lifecycle.py:51`).
### READY — `POST|PUT /ut/<sku>/match/ready` (`{matchId}`)
CardsDLL: `FutMatchReady` — no deserializer at all on the request; the server
response parser has two scalar members + one nested member.
Response (`match_ready_body`, utas_server.py:3353):
```json
{"matchId": <int>, "opponentPersonaId": <int, default 0>}
```
- `opponentPersonaId` defaults to `0` — a neutral placeholder, **never** the
logged-in user's persona.
- The parser also has a nested `items` member (the opponent squad). It is **omitted
deliberately** until the opponent-squad item contract is recovered from a live
capture; unrecognized/absent members are skip-safe. **This omission is one of the
two blockers.**
### PLAY — `POST /ut/<sku>/match` (bare path, `{matchId:int}`)
CardsDLL: `FutPlayGame` — no request deserializer.
Response: `{}` (empty). Effect: **none** — must NOT allocate a match or advance
`nextItemId` (PROVEN, `test_match_lifecycle.py:62-63`). This is purely a client
keepalive/transition ack.
### END — `POST|PUT|DELETE /ut/<sku>/match/end` (`{matchId, endReason, myMatchStats, opponentMatchStats}`)
CardsDLL: `FutDestroyMatch` @ `0x180121b60`**the rewards call**. Routed as
DestroyMatch regardless of verb (`FUT_MATCH_END`, default ON).
> **Wire path (Rust vs Python).** The Rust-owned reward is classified by `classify_economy`
> on **`POST /ut/delete/game/<sku>/match`** — EA/CardsDLL tunnels DELETE-semantics ops through the
> `/ut/delete/game/` prefix (`FutDestroyMatch` = `DELETE ut/%s/match/{id}`). Python's `match_route`
> additionally accepts `/ut/game/<sku>/match/end`. Which exact form the retail client emits is
> unverified (no match ever played); the Rust economy owns the `/ut/delete/game` form, and a
> `/ut/game/.../match/end` would fall to Python. Both credit the same reward shape.
Request fields that matter:
- `endReason` (atom 260) — **STRING enum, the AUTHORITATIVE result signal**. A score
comparison is NOT how the client reports the outcome. Nine values, mapped to a
win/draw/loss bucket:
| endReason | bucket |
|---|---|
| `WIN`, `DNF_WIN` | won |
| `DRAW`, `DNF_DRAW`, `NO_CONTEST` | draw |
| `LOSS`, `DNF_LOSS`, `DNF`, `QUIT` | loss |
| (missing/unknown) | draw (neutral fallback — credits without inventing a win) |
- `myMatchStats` / `opponentMatchStats` — literal-keyed objects, 15 int fields each,
first is `goals`. **Omitted by the client when `endReason` is `DNF`/`QUIT`**, so
nothing may require them. Used only as a fallback outcome probe if `endReason` is
absent.
Response (`destroy_match_body` / Rust `build_match_reward_body`) — every field a
**top-level scalar** (zero freeze risk) except the deliberately nested reward:
```json
{
"allCoins": <post-credit balance:int>,
"matchCoins": <per-result coins:int>,
"seasonCoins": 0,
"tournamentCoins": 0,
"boostConis": 0, // EA's typo — exact key required
"participationAward": <int>,
"teamOfTournamentWinner": false,
"gameModeAward": { "coins": <total award:int> }
}
```
> **Critical correction (2026-08-04):** the reward `coins` (atom 149) is read by the
> deserializer **only inside `gameModeAward`**, never as a top-level key. An earlier
> top-level `"coins"` was silently skipped and never reached the client — the one
> field most obviously named "the reward" was the one going nowhere.
Effect (persisted to the active FUT save): credit coins; increment the matching
`record.{won,draw,loss}`; increment `matchesPlayed` (PROVEN,
`test_match_lifecycle.py:74-81`).
## Reward policy
Env-tunable defaults (FUT-plausible, **not reversed**`economy_policy.rs:20-36`,
`utas_server.py:3201-3206`):
| Result | Match coins | Participation | Total |
|---|---|---|---|
| Win | 400 (`FUT_MATCH_COINS_WIN`) | 0 (`FUT_MATCH_PARTICIPATION`) | 400 |
| Draw | 200 (`FUT_MATCH_COINS_DRAW`) | 0 | 200 |
| Loss | 100 (`FUT_MATCH_COINS_LOSS`) | 0 | 100 |
`allCoins` = post-credit balance; `gameModeAward.coins` = per-result + participation.
## Ownership split (Rust vs Python)
The match loop is **partially migrated**. Only the coin-crediting END leg is
economy state, so only it is Rust/Core-owned; the CREATE/READY/PLAY legs are still
served by the Python oracle.
| Call | Owner | Where |
|---|---|---|
| CREATE | Python | `utas_server.py::match_route` (via host `Route::Passthrough`) |
| READY | Python | `utas_server.py::match_route` |
| PLAY | Python | `utas_server.py::match_route` |
| END (reward) | **Rust/Core** (on `POST /ut/delete/game/<sku>/match`) | `EconomyRoute::MatchEnd``handle_match_end``build_match_reward_body`; outcome via `economy_policy::match_result_from_reason`, coins via `match_result_coins`/`match_reward_total`. 503 on Core error, never Python. Python also accepts `/ut/game/<sku>/match/end`. |
END is classified in `classify_economy` (`openfut-utas-host/src/lib.rs`) and dispatched
by the economy authority barrier ahead of the general classifier — so it can never
also reach the Python passthrough (NEVER-BOTH). The Rust END writes the coin reward
through the single Core economy transaction (`grant_reward`); W/D/L record + match
count still live in the Python save until CREATE/READY/PLAY migrate.
## Test coverage
`fifa17-recon/tools/test_match_lifecycle.py` (PROVEN, isolated — temp profile, no
live server): drives CREATE→READY→PLAY→END and asserts:
- CREATE returns `reportIdEnabled:false` + advances `nextItemId` by 1.
- READY returns exactly `{matchId, opponentPersonaId:0}`.
- PLAY returns `{}` and does **not** advance `nextItemId`.
- END credits `MATCH_COINS["won"] + MATCH_PARTICIPATION`, persists
`record == {won:1,draw:0,loss:0}` and `matchesPlayed == 1`.
## Open questions / blockers
1. **READY `items` contract (opponent squad)** — the nested `items` member of
`FutMatchReadyServerResponse` is unserved pending a live capture. Whether the
client requires it present/non-empty to enter a match is unknown. **Blocker.**
2. **Client mode-entry gate** — reaching a match from the FUT hub UI is unverified;
`FUT_MODES` stays **off by default** (the season/tournament routes return `{}`).
3. **No in-game acceptance** — every claim here is static-analysis + isolated replay.
The first live match is expected to reveal whether the static read was complete
(every match body is logged for exactly this reason).
4. **Football simulation** — the actual gameplay (Blaze game-server) is out of scope
here and unverified.
## Sources
- `docs/PROJECT_STATE.md` (match lifecycle status), `docs/direction.md` (Tier-2 loop).
- `fifa17-recon/tools/utas_server.py`: `_match_call` (3325), `match_ready_body` (3346),
`match_route` (3357), `destroy_match_body` (3281), `_match_result` (3236),
`MATCH_COINS`/`MATCH_PARTICIPATION` (3201), `_END_REASON` (3229); CardsDLL RPC
descriptor block (3183-3197).
- `openfut-adapter-fifa17/src/fut/economy_policy.rs` (reward policy + outcome map).
- `openfut-utas-host/src/lib.rs`: `EconomyRoute::MatchEnd`, `handle_match_end`,
`build_match_reward_body`.
- `fifa17-recon/tools/test_match_lifecycle.py` (lifecycle regression).
- Vault: `02 Reverse Engineering/FIFA 17/Protocol Findings.md`,
`06 Agent Memory/{Project State,Known Issues}.md`.
+34 -20
View File
@@ -1,4 +1,9 @@
# Production Authority Matrix (post-P1)
# Production Authority Matrix (post-P1 -> P2-routes promoted 2026-08-17)
> **P2-routes promoted to production 2026-08-17.** prod-host swapped P1 `e5be8730` -> post-P1
> `fda40d12`; catalog `35a0913b` -> `9f6addaa` (resolves all owned assets, dropped_no_asset=0).
> Every previously-Python non-economy route now RUST in prod (OBSERVED prod log). Rollback hot:
> restore P1 binary + backup catalog (`economy-2026-08-17-p2/backup/`).
Definitive inventory of every production-reachable FIFA17 route/service and its
current owner. Derived from the live `prod-host` dispatch log (owner= labels,
@@ -28,29 +33,38 @@ Legend: owner R = Rust/Core, P = Python oracle (:8199 proxied via PYTHON_FALLBAC
| /trade/<id> (POST/PUT/GET) | R | coins,inv,listings | handle_market_buy | NO |
| DELETE /ut/delete/../trade/<id> | R | listings | handle_market_cancel | NO |
### NON-ECONOMY — Rust-owned
### NON-ECONOMY — Rust-owned (route migration 2026-08-17, OBSERVED prod log)
| Route | Owner | Notes |
|---|---|---|
| POST /ut/auth | R (OBSERVE) | session_opened; envelope currently Python-generated, Rust observes — auth boundary TBD |
| POST /ut/auth (+ /ut/delete/auth) | R | Rust mints the SID (OPENFUT-SID-{:016X}); opens Rust session; adopts persona from body. No Python. |
| POST /openfut/account/sync | R | full Rust envelope; coins/unopenedPacks from Core; clubName OpenFUT / clubAbbr OFC constants |
| GET /userMassInfo | R | FULL Rust envelope (userInfo+squad+settings+pileSizeClientData); no Python. coins from Core, squad == /squad/active |
| GET/PUT /clientdata/<key> | R | host ClientDataStore (JSON-persisted); PUT acks {}, GET returns blob or {} |
| capability (/openfut/fifa17/capability) | R | -> Bound (CleanV1) |
| GET /userMassInfo | R (OVERLAY) | Rust overlays economy fields + squad onto Python envelope (hybrid) |
| GET /club, /club/* readers | R | Core-backed collection |
| squad-active, /squad/* | R | Core squad tx (SquadReplace) |
| GET /club, /club/* readers | R | Core-backed collection (dropped_no_asset=0) |
| GET /squad/0, /squad/active, /squad/list; PUT /squad/<n> | R | Core squad projection + tx; GET /squad/0 == active squad (verified structurally identical) |
| GET /user/accountinfo | R | {} |
| GET /user | R | `{"userInfo": …}` — same userInfo builder as userMassInfo (shared); squad rating is Core-authoritative (DIFFERENT-BY-DESIGN vs Python's stale value) |
| GET /settings | R | {"configs":[]} |
| GET /leaderboards/options | R | {} |
| PUT /match/reset | R | {} |
| GET /phishing/trusteddevice | R | security-question stateless ack |
| GET /hub | R | Core-derived counts |
| GET /club/stats/{year,consumables,staff,country,league,team} | R | Core aggregation; context buckets keyed nation/league/team (owned=1982); staff={} |
| GET /store, /match/keepalive, /captcha, /tfa, /livemessage, /activeMessage | R | unconditional constant acks (byte-identical to the oracle; StaticAck route) |
| GET /watchList (+ PUT/POST/DELETE) | R | empty watch list + authoritative Core credits; add/remove is a no-op ack (oracle persists none) |
| POST /ut/.../match/end (DestroyMatch) | R | economy reward (coins credited via Core grant_reward) |
### NON-ECONOMY — still Python (PYTHON_FALLBACK, OBSERVED live)
| Method/path | Owner | Response (observed) | Stateful | Migration target |
|---|---|---|---|---|
| POST /openfut/account/sync | P | account envelope | selects profile | R (candidate) |
| GET /user/accountinfo | P | {} | no (static) | R (trivial) |
| GET /settings | P | {"configs":[]} | no (static) | R (trivial) |
| GET /phishing/trusteddevice | P | trusted-device/security-question | first-entry state | R (security-question priority) |
| PUT /match/reset | P | {} | maybe | R (trivial/small) |
| GET /hub | P | tile counts (clubPlayers/auction/tradePile) | reads inventory+listings | R (reads Core) |
| GET /club/stats/year | P | stat[] (players/gold/...) | reads inventory | R (reads Core) |
| GET /club/stats/consumables | P | stat[] | reads inventory | R (reads Core) |
| GET /club/stats/staff | P | {} | no | R (trivial/reads Core) |
| GET /leaderboards/options | P | {} | no (static) | R (trivial) |
| PUT /clientdata/userHubData | P | {} | stores blob | R (small stateful) |
### NON-ECONOMY — still Python (PYTHON_FALLBACK)
| Method/path | Owner | Reason |
|---|---|---|
| GET /item/resource, /defid | P | `item_def(rid)` shapes `{itemData:[…]}` from `PLAYER_DEFS` (asset=rid&0xffffff → name/rating/pos/attrs) + consumable defs, placeholder fallback ("Player",75,attrs 70). Faithful Rust needs those def tables in the host. Shape captured: `docs/evidence/route-shapes-2026-08-17/defs_resource.json` |
| GET /clubUser, /user/list, /user/club | P | club identity (off→{}, flag-gated) + rename (mutating); Core has `clubs` but rename needs a Core write |
| GET /squad/<n> (n≠0, non-active) | P | no multi-squad Core model (Rust owns squad/0, squad/active, /squad/list, PUT) |
| /squad/mode/draft/* | P | FUT Draft mode |
| GET /season, /tournament, /champion, /leaderboards, /sbs/* | P | mode-gated (FUT_MODES/_SBC off by default → `{}`); real behavior needs the mode logic ported |
| GET /marketdata (+ /marketdata/pricelimits) | P | suggested pricing; freeze-risk (array vs object container); Python-correct, constant band 150..15000 |
| POST /ut/.../match (CREATE), /match/ready (READY), /match (PLAY) | P | match handshake legs; no match ever played in-game (see docs/MATCH_LIFECYCLE.md) |
## AUXILIARY SERVICES (prod container OPENFUT_SERVERS="blaze roster pow")
All aux services are **Python in production today** (container `entrypoint.sh` runs
+6 -3
View File
@@ -8,7 +8,7 @@ rollback-to-python-p2.sh, :p2-rollback image b1b929953f, profile 39bb3e83).
## A. LIVE PRODUCTION REQUIRED (still owns behavior in the live flow)
| Component | Role | Rust status | Retire when |
|---|---|---|---|
| oracle utas_server.py (:8199) NON-ECONOMY | account/sync, hub, club/stats, clientdata, userMassInfo envelope | 5 routes migrated (accountinfo/settings/leaderboards-options/match-reset/phishing); hub/club-stats/clientdata/account-sync still proxied | remaining non-economy routes migrated + operator-validated |
| oracle utas_server.py (:8199) NON-ECONOMY | **remaining**: item-defs (item/resource,defid), user-identity (user,clubUser,user/list,user/club), non-active squad/<n>, draft, watchList, marketdata, mode-gated (season/tournament/champion/leaderboards/sbs, off→{}) | **Most non-economy migrated 2026-08-17** (account/sync, auth, userMassInfo, clientdata, hub, club/stats/*, settings, accountinfo, phishing, match/reset, leaderboards/options, static acks — all Rust). See PRODUCTION_AUTHORITY_MATRIX | remaining tail migrated (needs live captures for the data routes; mode-logic port for the gated ones) OR mode-gated ones kept Python |
| blaze_responder_v3b.py (:42130 Blaze) | FUT Blaze transport | Rust openfut-blaze-host COMPLETE, gate-proven (Gate 10) | container cutover (operator-gated deploy) |
| blaze_responder_v3b.py (:42127 redirector TLS) | first-hop TLS redirect | Rust openfut-redirector-host COMPLETE (OpenSSL) | container cutover + cert consistency |
| roster_server.py (:8081) | roster-update XML | Rust openfut-roster-host COMPLETE (unit-only) | container cutover |
@@ -33,8 +33,11 @@ rollback-to-python-p2.sh, :p2-rollback image b1b929953f, profile 39bb3e83).
- fifa-blaze (FIFA23 capture stub), openfut-bridge (FIFA23): retired lineage, not in FIFA17 flow.
## Retirement gating
1. Migrate remaining non-economy oracle routes to Rust (hub/club-stats/clientdata/account-sync) — then oracle
is oracle-only (class B).
1. **Mostly DONE (2026-08-17)**: account/sync, ut/auth (Rust SID mint), userMassInfo (full), clientdata,
club/stats/{country,league,team}, and the trivial static acks migrated + deployed. **Remaining on Python**:
item-defs, user-identity (user/clubUser/user-list/user-club), non-active squad/<n>, draft, watchList,
marketdata (no captured wire shape), and mode-gated season/tournament/champion/leaderboards/sbs
(return {} while FUT_MODES/_SBC off). Migrate the data ones once live shapes are captured.
2. Deploy Rust blaze/roster/redirector via container cutover (operator-gated) — then those Python responders
are class D/E only.
3. POW: build Rust host + reverse bodies (largest blocker) OR keep Python POW as class A indefinitely.
@@ -0,0 +1,67 @@
{
"champion": {
"bytes": 2,
"path": "/ut/game/fifa17/champion",
"status": 200
},
"clubUser": {
"bytes": 2,
"path": "/ut/game/fifa17/clubUser",
"status": 200
},
"defid": {
"bytes": 600,
"path": "/ut/game/fifa17/defid?definitionId=200389",
"status": 200
},
"defs_resource": {
"bytes": 600,
"path": "/ut/game/fifa17/item/resource?resourceId=200389",
"status": 200
},
"draft_state": {
"bytes": 118,
"path": "/ut/game/fifa17/squad/mode/draft/state",
"status": 200
},
"marketdata": {
"bytes": 110,
"path": "/ut/game/fifa17/marketdata/pricelimits?defId=200389,200104",
"status": 200
},
"sbs_sets": {
"bytes": 537,
"path": "/ut/game/fifa17/sbs/sets",
"status": 200
},
"season": {
"bytes": 2,
"path": "/ut/game/fifa17/season",
"status": 200
},
"squad_0": {
"bytes": 7792,
"path": "/ut/game/fifa17/squad/0",
"status": 200
},
"tournament": {
"bytes": 2,
"path": "/ut/game/fifa17/tournament",
"status": 200
},
"user": {
"bytes": 751,
"path": "/ut/game/fifa17/user",
"status": 200
},
"user_list": {
"bytes": 2,
"path": "/ut/game/fifa17/user/list",
"status": 200
},
"watchList": {
"bytes": 52,
"path": "/ut/game/fifa17/watchList",
"status": 200
}
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1 @@
{}
@@ -0,0 +1,53 @@
{
"itemData": [
{
"assetId": 200389,
"attributeList": [
{
"index": 0,
"value": 70
},
{
"index": 1,
"value": 70
},
{
"index": 2,
"value": 70
},
{
"index": 3,
"value": 70
},
{
"index": 4,
"value": 70
},
{
"index": 5,
"value": 70
}
],
"cardType": 0,
"cardassetid": 200389,
"cardsubtypeid": 0,
"commodityId": 200389,
"commonName": "Player",
"definitionId": 200389,
"id": 200389,
"itemState": "free",
"itemType": "player",
"lastName": "Player",
"leagueId": 0,
"name": "Player",
"nation": 0,
"playStyle": 250,
"preferredPosition": "CM",
"rareflag": 1,
"rating": 75,
"resourceId": 200389,
"teamid": 0,
"untradeable": true
}
]
}
@@ -0,0 +1,53 @@
{
"itemData": [
{
"assetId": 200389,
"attributeList": [
{
"index": 0,
"value": 70
},
{
"index": 1,
"value": 70
},
{
"index": 2,
"value": 70
},
{
"index": 3,
"value": 70
},
{
"index": 4,
"value": 70
},
{
"index": 5,
"value": 70
}
],
"cardType": 0,
"cardassetid": 200389,
"cardsubtypeid": 0,
"commodityId": 200389,
"commonName": "Player",
"definitionId": 200389,
"id": 200389,
"itemState": "free",
"itemType": "player",
"lastName": "Player",
"leagueId": 0,
"name": "Player",
"nation": 0,
"playStyle": 250,
"preferredPosition": "CM",
"rareflag": 1,
"rating": 75,
"resourceId": 200389,
"teamid": 0,
"untradeable": true
}
]
}
@@ -0,0 +1,9 @@
[
{
"gamesWonCurrentMatch": 0,
"roundsInfo": [],
"squadState": "INVALID",
"stateParam1": "INVALID",
"stateParam2": "0"
}
]
@@ -0,0 +1,12 @@
[
{
"defId": 200389,
"maxPrice": 15000,
"minPrice": 150
},
{
"defId": 200104,
"maxPrice": 15000,
"minPrice": 150
}
]
@@ -0,0 +1,35 @@
{
"categories": [
{
"categoryId": 1,
"name": "Foundations",
"priority": 1,
"sets": [
{
"awards": [],
"categoryId": 1,
"challengesCompletedCount": 0,
"challengesCount": 1,
"description": "Submit an 11-player squad.",
"endTime": 4102444800,
"hidden": false,
"name": "Bronze Challenge",
"priority": 1,
"setId": 1
},
{
"awards": [],
"categoryId": 1,
"challengesCompletedCount": 0,
"challengesCount": 1,
"description": "Get started with your first SBC.",
"endTime": 4102444800,
"hidden": false,
"name": "Simple Start",
"priority": 2,
"setId": 2
}
]
}
]
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1,707 @@
{
"actives": [],
"captain": 100000001,
"changed": 0,
"chemistry": 49,
"custom": "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,0,50,40,65,0,65,50,50,1]",
"formation": "f433",
"id": 0,
"kicktakers": [
{
"dream": false,
"id": 100000001,
"index": 0
},
{
"dream": false,
"id": 100000001,
"index": 1
},
{
"dream": false,
"id": 100000001,
"index": 2
},
{
"dream": false,
"id": 100000001,
"index": 3
},
{
"dream": false,
"id": 100000001,
"index": 4
}
],
"manager": [
{
"dream": false,
"id": 100000427
}
],
"personaId": 33068179,
"players": [
{
"index": 0,
"itemData": {
"assetId": 200389,
"attributeList": [
{
"index": 0,
"value": 83
},
{
"index": 1,
"value": 90
},
{
"index": 2,
"value": 77
},
{
"index": 3,
"value": 82
},
{
"index": 4,
"value": 50
},
{
"index": 5,
"value": 87
}
],
"cardassetid": 200389,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 200389,
"fitness": 99,
"id": 100000003,
"itemState": "free",
"itemType": "player",
"leagueId": 53,
"nation": 44,
"owners": 1,
"playStyle": 250,
"preferredPosition": "GK",
"rareflag": 1,
"rating": 87,
"resourceId": 200389,
"teamid": 240,
"untradeable": true
},
"kitNumber": 1
},
{
"index": 1,
"itemData": {
"assetId": 197445,
"attributeList": [
{
"index": 0,
"value": 86
},
{
"index": 1,
"value": 73
},
{
"index": 2,
"value": 81
},
{
"index": 3,
"value": 83
},
{
"index": 4,
"value": 83
},
{
"index": 5,
"value": 73
}
],
"cardassetid": 197445,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 197445,
"fitness": 99,
"id": 100000006,
"itemState": "free",
"itemType": "player",
"leagueId": 19,
"nation": 4,
"owners": 1,
"playStyle": 250,
"preferredPosition": "LB",
"rareflag": 1,
"rating": 87,
"resourceId": 197445,
"teamid": 21,
"untradeable": true
},
"kitNumber": 4
},
{
"index": 2,
"itemData": {
"assetId": 155862,
"attributeList": [
{
"index": 0,
"value": 78
},
{
"index": 1,
"value": 63
},
{
"index": 2,
"value": 70
},
{
"index": 3,
"value": 70
},
{
"index": 4,
"value": 87
},
{
"index": 5,
"value": 83
}
],
"cardassetid": 155862,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 155862,
"fitness": 99,
"id": 100000005,
"itemState": "free",
"itemType": "player",
"leagueId": 53,
"nation": 45,
"owners": 1,
"playStyle": 250,
"preferredPosition": "CB",
"rareflag": 1,
"rating": 89,
"resourceId": 155862,
"teamid": 243,
"untradeable": true
},
"kitNumber": 3
},
{
"index": 3,
"itemData": {
"assetId": 182521,
"attributeList": [
{
"index": 0,
"value": 45
},
{
"index": 1,
"value": 80
},
{
"index": 2,
"value": 88
},
{
"index": 3,
"value": 79
},
{
"index": 4,
"value": 69
},
{
"index": 5,
"value": 70
}
],
"cardassetid": 182521,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 182521,
"fitness": 99,
"id": 100000008,
"itemState": "free",
"itemType": "player",
"leagueId": 53,
"nation": 21,
"owners": 1,
"playStyle": 250,
"preferredPosition": "CM",
"rareflag": 1,
"rating": 88,
"resourceId": 182521,
"teamid": 243,
"untradeable": true
},
"kitNumber": 6
},
{
"index": 4,
"itemData": {
"assetId": 189332,
"attributeList": [
{
"index": 0,
"value": 93
},
{
"index": 1,
"value": 69
},
{
"index": 2,
"value": 75
},
{
"index": 3,
"value": 83
},
{
"index": 4,
"value": 81
},
{
"index": 5,
"value": 75
}
],
"cardassetid": 189332,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 189332,
"fitness": 99,
"id": 100000007,
"itemState": "free",
"itemType": "player",
"leagueId": 53,
"nation": 45,
"owners": 1,
"playStyle": 250,
"preferredPosition": "LB",
"rareflag": 1,
"rating": 86,
"resourceId": 189332,
"teamid": 241,
"untradeable": true
},
"kitNumber": 5
},
{
"index": 5,
"itemData": {
"assetId": 158023,
"attributeList": [
{
"index": 0,
"value": 89
},
{
"index": 1,
"value": 90
},
{
"index": 2,
"value": 86
},
{
"index": 3,
"value": 96
},
{
"index": 4,
"value": 26
},
{
"index": 5,
"value": 61
}
],
"cardassetid": 158023,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 158023,
"fitness": 99,
"id": 100000002,
"itemState": "free",
"itemType": "player",
"leagueId": 53,
"nation": 52,
"owners": 1,
"playStyle": 250,
"preferredPosition": "RW",
"rareflag": 1,
"rating": 93,
"resourceId": 158023,
"teamid": 241,
"untradeable": true
},
"kitNumber": 9
},
{
"index": 6,
"itemData": {
"assetId": 183907,
"attributeList": [
{
"index": 0,
"value": 79
},
{
"index": 1,
"value": 50
},
{
"index": 2,
"value": 72
},
{
"index": 3,
"value": 68
},
{
"index": 4,
"value": 90
},
{
"index": 5,
"value": 85
}
],
"cardassetid": 183907,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 183907,
"fitness": 99,
"id": 100000004,
"itemState": "free",
"itemType": "player",
"leagueId": 19,
"nation": 21,
"owners": 1,
"playStyle": 250,
"preferredPosition": "CB",
"rareflag": 1,
"rating": 90,
"resourceId": 183907,
"teamid": 21,
"untradeable": true
},
"kitNumber": 2
},
{
"index": 7,
"itemData": {
"assetId": 183277,
"attributeList": [
{
"index": 0,
"value": 90
},
{
"index": 1,
"value": 81
},
{
"index": 2,
"value": 82
},
{
"index": 3,
"value": 91
},
{
"index": 4,
"value": 32
},
{
"index": 5,
"value": 64
}
],
"cardassetid": 183277,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 183277,
"fitness": 99,
"id": 100000009,
"itemState": "free",
"itemType": "player",
"leagueId": 13,
"nation": 7,
"owners": 1,
"playStyle": 250,
"preferredPosition": "LM",
"rareflag": 1,
"rating": 88,
"resourceId": 183277,
"teamid": 5,
"untradeable": true
},
"kitNumber": 7
},
{
"index": 8,
"itemData": {
"assetId": 176580,
"attributeList": [
{
"index": 0,
"value": 82
},
{
"index": 1,
"value": 90
},
{
"index": 2,
"value": 79
},
{
"index": 3,
"value": 87
},
{
"index": 4,
"value": 42
},
{
"index": 5,
"value": 79
}
],
"cardassetid": 176580,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 176580,
"fitness": 99,
"id": 100000010,
"itemState": "free",
"itemType": "player",
"leagueId": 53,
"nation": 60,
"owners": 1,
"playStyle": 250,
"preferredPosition": "ST",
"rareflag": 1,
"rating": 92,
"resourceId": 176580,
"teamid": 241,
"untradeable": true
},
"kitNumber": 10
},
{
"index": 9,
"itemData": {
"assetId": 188545,
"attributeList": [
{
"index": 0,
"value": 81
},
{
"index": 1,
"value": 87
},
{
"index": 2,
"value": 74
},
{
"index": 3,
"value": 85
},
{
"index": 4,
"value": 38
},
{
"index": 5,
"value": 82
}
],
"cardassetid": 188545,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 188545,
"fitness": 99,
"id": 100000025,
"itemState": "free",
"itemType": "player",
"leagueId": 19,
"nation": 37,
"owners": 1,
"pile": "club",
"playStyle": 250,
"preferredPosition": "ST",
"rareflag": 1,
"rating": 90,
"resourceId": 188545,
"teamid": 21,
"untradeable": true
},
"kitNumber": 11
},
{
"index": 10,
"itemData": {
"assetId": 20801,
"attributeList": [
{
"index": 0,
"value": 92
},
{
"index": 1,
"value": 92
},
{
"index": 2,
"value": 81
},
{
"index": 3,
"value": 91
},
{
"index": 4,
"value": 33
},
{
"index": 5,
"value": 80
}
],
"cardassetid": 20801,
"cardsubtypeid": 0,
"contract": 7,
"definitionId": 20801,
"fitness": 99,
"id": 100000001,
"itemState": "free",
"itemType": "player",
"leagueId": 53,
"nation": 38,
"owners": 1,
"playStyle": 250,
"preferredPosition": "LW",
"rareflag": 1,
"rating": 94,
"resourceId": 20801,
"teamid": 243,
"untradeable": true
},
"kitNumber": 8
},
{
"index": 11,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 12,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 13,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 14,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 15,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 16,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 17,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 18,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 19,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 20,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 21,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
},
{
"index": 22,
"itemData": {
"dream": false,
"id": 0
},
"kitNumber": 0
}
],
"rating": 90,
"squadName": "OpenFUT",
"squadType": "REGULAR_SQUAD",
"starRating": 90
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1,54 @@
{
"userInfo": {
"accountCreatedPlatformName": "pc",
"actives": [],
"bidTokens": {
"count": 0,
"updateTime": 0
},
"clubAbbr": "OFC",
"clubName": "OpenFUT",
"clubNameChangeAllowed": false,
"currencies": [
{
"active": true,
"finalFunds": 29876776,
"funds": 29876776,
"name": "coins"
},
{
"active": true,
"finalFunds": 0,
"funds": 0,
"name": "points"
}
],
"divisionOffline": 10,
"divisionOnline": 10,
"draw": 0,
"established": "2026",
"feature": {},
"loss": 0,
"personaId": 33068179,
"purchased": false,
"reliability": {
"matchUnfinishedTime": 0,
"reliability": 100
},
"sessionCoinsBankBalance": 0,
"squadList": {
"squad": [
{
"chemistry": 49,
"formation": "f433",
"id": 0,
"rating": 89,
"squadName": "OpenFUT",
"squadType": "REGULAR_SQUAD"
}
]
},
"trophies": 0,
"won": 0
}
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1,5 @@
{
"auctionInfo": [],
"credits": 29876776,
"total": 0
}