docs: add FLE bridge direction pivot and squad-injection test tooling
Adds the app-centric FLE bridge direction (direction.md) replacing the Blaze-backend route as primary plan, plus a corrected foundational test procedure and snapshot/diff/apply tooling for reverse-engineering FLE's Freeze Lineup write mechanism. Also picks up prior untracked research docs (status-review, fut-integration-options, fifa23-startup-flow, track-c) and existing capture/export tooling that hadn't been committed yet.
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
-- squad-exporter/export_squad.lua
|
||||
-- FIFA Live Editor Lua script (v2 API).
|
||||
-- Run from FLE's Lua Engine while in-game to export squad/FUT-relevant
|
||||
-- database table data to JSON in the FIFA 23 Live Editor directory.
|
||||
--
|
||||
-- Exports:
|
||||
-- players table (id, name, overall, potential, position, team)
|
||||
-- teams table (id, name, league, budget)
|
||||
-- fut_items table (if present in memory — only when FUT is loaded)
|
||||
--
|
||||
-- Output: C:\FIFA 23 Live Editor\openfut_squad_export.json
|
||||
|
||||
local OUT_PATH = "C:\\FIFA 23 Live Editor\\openfut_squad_export.json"
|
||||
|
||||
-- Minimal JSON serialiser (no external deps)
|
||||
local function json_val(v)
|
||||
local t = type(v)
|
||||
if t == "nil" then return "null"
|
||||
elseif t == "boolean" then return tostring(v)
|
||||
elseif t == "number" then
|
||||
if v ~= v then return "null" end -- NaN
|
||||
return string.format("%.6g", v)
|
||||
elseif t == "string" then
|
||||
return '"' .. v:gsub('\\', '\\\\'):gsub('"', '\\"'):gsub('\n','\\n') .. '"'
|
||||
elseif t == "table" then
|
||||
-- array check: sequential integer keys starting at 1
|
||||
local n = #v
|
||||
local is_arr = n > 0
|
||||
if is_arr then
|
||||
local parts = {}
|
||||
for i=1,n do parts[i] = json_val(v[i]) end
|
||||
return "[" .. table.concat(parts, ",") .. "]"
|
||||
else
|
||||
local parts = {}
|
||||
for k,vv in pairs(v) do
|
||||
parts[#parts+1] = json_val(tostring(k)) .. ":" .. json_val(vv)
|
||||
end
|
||||
return "{" .. table.concat(parts, ",") .. "}"
|
||||
end
|
||||
end
|
||||
return "null"
|
||||
end
|
||||
|
||||
local function export_table(tbl_name, fields)
|
||||
local rows = GetDBTableRows(tbl_name)
|
||||
if not rows or #rows == 0 then
|
||||
Log("[squad-exporter] table '" .. tbl_name .. "': empty or not found")
|
||||
return {}
|
||||
end
|
||||
Log("[squad-exporter] table '" .. tbl_name .. "': " .. #rows .. " rows")
|
||||
local out = {}
|
||||
for _, row in ipairs(rows) do
|
||||
local rec = {}
|
||||
for _, f in ipairs(fields) do
|
||||
local cell = row[f]
|
||||
if cell then
|
||||
rec[f] = cell.value
|
||||
end
|
||||
end
|
||||
out[#out+1] = rec
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- ── Export players ────────────────────────────────────────────────────────────
|
||||
local players = export_table("players", {
|
||||
"playerid", "overallrating", "potential", "preferredposition1",
|
||||
"height", "weight", "birthdate", "nationality", "skillmoves",
|
||||
"weakfootabilitytypecode", "attackingworkrate", "defensiveworkrate",
|
||||
})
|
||||
|
||||
-- Enrich with names (GetPlayerName is an FLE function)
|
||||
for _, p in ipairs(players) do
|
||||
local pid = p.playerid
|
||||
if pid then
|
||||
local ok, name = pcall(GetPlayerName, math.floor(pid))
|
||||
if ok and name then p.name = name end
|
||||
local ok2, tid = pcall(GetTeamIdFromPlayerId, math.floor(pid))
|
||||
if ok2 and tid then
|
||||
p.teamid = tid
|
||||
local ok3, tname = pcall(GetTeamName, tid)
|
||||
if ok3 then p.teamname = tname end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ── Export teams ─────────────────────────────────────────────────────────────
|
||||
local teams = export_table("teams", {
|
||||
"teamid", "leagueid", "teamcolor1r", "teamcolor1g", "teamcolor1b",
|
||||
"stadiumid", "domesticprestige", "internationalprestige",
|
||||
"transferbudget", "wagebudget",
|
||||
})
|
||||
for _, t in ipairs(teams) do
|
||||
local ok, name = pcall(GetTeamName, math.floor(t.teamid or 0))
|
||||
if ok then t.name = name end
|
||||
end
|
||||
|
||||
-- ── Export FUT tables if available ───────────────────────────────────────────
|
||||
-- These tables are only in memory when FUT mode is active.
|
||||
local fut_tables = {}
|
||||
local all_table_names = GetDBTablesNames()
|
||||
for _, tname in ipairs(all_table_names) do
|
||||
local lower = tname:lower()
|
||||
if lower:find("fut") or lower:find("club") or lower:find("pack")
|
||||
or lower:find("item") or lower:find("market") then
|
||||
Log("[squad-exporter] found FUT-related table: " .. tname)
|
||||
fut_tables[tname] = export_table(tname, GetDBTableFields(tname) or {})
|
||||
end
|
||||
end
|
||||
|
||||
-- ── Write output ─────────────────────────────────────────────────────────────
|
||||
local payload = {
|
||||
exported_at = os.date("!%Y-%m-%dT%H:%M:%SZ"),
|
||||
is_career_mode = IsInCM(),
|
||||
player_count = #players,
|
||||
team_count = #teams,
|
||||
players = players,
|
||||
teams = teams,
|
||||
fut_tables = fut_tables,
|
||||
all_db_tables = all_table_names,
|
||||
}
|
||||
|
||||
local f = io.open(OUT_PATH, "w")
|
||||
if f then
|
||||
f:write(json_val(payload))
|
||||
f:close()
|
||||
Log("[squad-exporter] Written to " .. OUT_PATH)
|
||||
MessageBox("OpenFUT Squad Export", "Done! " .. #players .. " players, "
|
||||
.. #teams .. " teams.\n" .. OUT_PATH)
|
||||
else
|
||||
Log("[squad-exporter] ERROR: could not open " .. OUT_PATH)
|
||||
MessageBox("OpenFUT Squad Export", "ERROR writing to " .. OUT_PATH)
|
||||
end
|
||||
Reference in New Issue
Block a user