Files
gen1recomp/tests/drivers/g3battle_arena_loopback.lua
2026-09-24 05:17:32 -04:00

220 lines
9.0 KiB
Lua

local U = require("tests.drivers.util")
local H = require("tests.link3_harness")
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/g3battle_arena"
local failures = 0
local function result(ok, label)
print((ok and "PASS " or "FAIL ") .. label)
if not ok then failures = failures + 1 end
return ok
end
local function finish()
if failures == 0 then
print("PASS g3battle_arena_loopback")
love.event.quit(0)
else
print("FAIL g3battle_arena_loopback failures=" .. failures)
love.event.quit(1)
end
end
local function mon(species, level, moves)
return H.legal({
species = species, level = level, moves = moves, personality = 0x51,
ivs = { hp = 20, atk = 20, def = 20, spe = 20, spa = 20, spd = 20 },
evs = { hp = 0, atk = 0, def = 0, spe = 0, spa = 0, spd = 0 }, item = 0,
nickname = "", friendship = 70,
})
end
local function profile()
return { engine = 3, version = "firered", kind = "vanilla", rulesetId = "g3_single", rule = { partySize = 6 } }
end
local function seatMessages(relay, seat)
local out = {}
for _, row in ipairs(relay.log) do
if row.seat == seat then out[#out + 1] = row end
end
return out
end
return function(game)
print("PASS driver_started")
for _ = 1, 900 do
if game.phase == "boot" and game.boot then break end
U.wait(1)
end
local Battle = require("src.core.game3.battle")
local BattleUi = require("src.core.game3.battle.ui")
local LB = require("src.core.game3.link.battle")
local partyA = { mon(6, 50, { 10, 45 }) }
local partyB = { mon(9, 50, { 33, 39 }) }
local packedA, packedB = H.pack(partyA), H.pack(partyB)
local SEED = 0x1D2C3B4A
local returned
game.returnToLauncher = function(o) returned = o or {} end
local function runArena(spec, peerStep, shots)
returned = nil
local digests
local userDone = spec.onDone
spec.onDone = function(r)
digests = {}
for turn, value in pairs(LB._myHashes or {}) do digests[turn] = value end
if userDone then userDone(r) end
end
game:enterArena(spec)
local taken = {}
local lastAction = -1
for _ = 1, 30000 do
if returned then break end
if peerStep then peerStep() end
local st = Battle.getState()
if Battle.isActive() and st then
local Message = package.loaded["src.ui.game3.message"]
local page = (Message and Message.isOpen and Message.isOpen() and Message.isWaiting and Message.isWaiting()
and tostring(Message.currentPage() or "")) or ""
page = page:gsub("\n", " ")
if shots.start and not taken.start and Battle._phase == "command" and BattleUi._mode == "menu" then
taken.start = true
U.still(game, DIR .. "/" .. shots.start)
elseif shots.start and not taken.start and st.spectate and page:find("Go! ", 1, true) then
taken.start = true
U.still(game, DIR .. "/" .. shots.start)
end
if shots.mid and not taken.mid and (st.turn or 0) >= 2 and page:find(" used ", 1, true) then
taken.mid = true
U.still(game, DIR .. "/" .. shots.mid)
end
if shots.result and not taken.result and (page:find("lost against", 1, true)
or page:find("defeated", 1, true) or page:find("draw against", 1, true)) then
taken.result = true
U.still(game, DIR .. "/" .. shots.result)
end
if not st.spectate and Battle._phase == "command" and (BattleUi._mode == "menu" or BattleUi._mode == "moves")
and U.frame() - lastAction > 6 then
lastAction = U.frame()
U.tap(game, "a")
else
U.wait(1)
end
else
U.wait(1)
end
end
return digests, taken
end
local relayA = H.relay({ seed = SEED, seats = 2, roomSeed = 1 })
local peerA = relayA:session(1)
local sessionA = relayA:session(0)
local peerSeatSent = false
local function scriptedPeer()
for _, msg in ipairs(peerA:poll()) do
if msg.type == "game3_hello" then
local hello = LB.copy(msg)
hello.seat = nil
hello.name = "LEAF"
hello.game3.trainerId, hello.game3.gender, hello.game3.seat = 0x2468, 1, 1
peerA:send(hello)
elseif msg.type == "game3_battle_linkup" then
peerA:send({ type = "game3_battle_linkup", linkType = msg.linkType, players = 2 })
elseif msg.type == "game3_battle_seat" and not peerSeatSent then
peerSeatSent = true
peerA:send({ type = "game3_battle_seat" })
elseif msg.type == "game3_battle_setup" then
peerA:send({ type = "game3_battle_setup", mode = "single", unionRoom = false, name = "LEAF",
trainerId = 0x2468, gender = 1, party = packedB })
elseif msg.type == "game3_battle_action" then
peerA:send({ type = "game3_battle_action", turn = msg.turn, kind = "move", slot = 1 })
elseif msg.type == "game3_battle_hash" then
peerA:send({ type = "game3_battle_hash", turn = msg.turn, value = msg.value, parts = msg.parts })
elseif msg.type == "game3_battle_outcome" then
local mirror = ({ [1] = 2, [2] = 1, [3] = 3, [128] = 1 })[msg.outcome] or 3
peerA:send({ type = "game3_battle_outcome", outcome = mirror, turn = msg.turn })
end
end
relayA:tick()
end
local resultA
local digestsA, shotsA = runArena({
profile = profile(), role = "host", seat = 0, seats = 2, slotId = nil, seed = SEED, match = "m1",
room = relayA.room, players = { { id = "00000001", name = "RED", seat = 0 }, { id = "00000002", name = "LEAF", seat = 1 } },
myParty = packedA, session = sessionA, client = relayA:client(), mode = "single",
onDone = function(r) resultA = r end,
}, scriptedPeer, {
start = "g3battle_arena_seat0_battle_start.png",
mid = "g3battle_arena_seat0_mid_turn.png",
result = "g3battle_arena_seat0_result.png",
})
result(resultA == "win" or resultA == "lose" or resultA == "draw", "seat 0 arena battle finished (" .. tostring(resultA) .. ")")
result(returned and returned.tab == "online", "seat 0 returned to the launcher's online tab")
result(shotsA.start and shotsA.mid and shotsA.result, "seat 0 battle start, mid-turn and result were captured")
result(game.phase == "arena" and game.arena == nil, "the arena state was torn down")
local relayB = H.relay({ seed = SEED, seats = 2, roomSeed = 2 })
local sessionB = relayB:session(1)
local replay = relayB:session(0)
for _, row in ipairs(seatMessages(relayA, 0)) do
if row.msg.type ~= "game3_bye" then replay:send(row.msg) end
end
local resultB
local digestsB, shotsB = runArena({
profile = profile(), role = "guest", seat = 1, seats = 2, seed = SEED, match = "m2",
room = relayB.room, players = { { id = "00000001", name = "RED", seat = 0 }, { id = "00000002", name = "LEAF", seat = 1 } },
myParty = packedB, session = sessionB, client = relayB:client(), mode = "single",
onDone = function(r) resultB = r end,
}, function() relayB:tick() end, { result = "g3battle_arena_seat1_result.png" })
local mirror = { win = "lose", lose = "win", draw = "draw" }
result(mirror[resultA] == resultB, "seat 1 replaying seat 0's stream finished mirrored (" .. tostring(resultB) .. ")")
result(shotsB.result, "seat 1 result was captured")
local same, n = true, 0
for turn, value in pairs(digestsA or {}) do
if digestsB and digestsB[turn] then
n = n + 1
if digestsB[turn] ~= value then same = false end
end
end
result(same and n >= 1, "every per-turn digest matched between the two seats (" .. n .. " turns)")
local relayC = H.relay({ seed = SEED, seats = 2, roomSeed = 3 })
local feed0, feed1 = relayC:session(0), relayC:session(1)
for _, row in ipairs(seatMessages(relayA, 0)) do
if row.msg.type ~= "game3_bye" then feed0:send(row.msg) end
end
for _, row in ipairs(seatMessages(relayB, 1)) do
if row.msg.type ~= "game3_bye" then feed1:send(row.msg) end
end
local spectator = relayC:session(nil)
local resultC
local digestsC, shotsC = runArena({
profile = profile(), role = "spectator", seats = 2, seed = SEED, match = "m3", room = relayC.room,
players = { { id = "00000001", name = "RED", seat = 0 }, { id = "00000002", name = "LEAF", seat = 1 } },
session = spectator, client = relayC:client(), mode = "single",
onDone = function(r) resultC = r end,
}, function() relayC:tick() end, {
start = "g3battle_arena_spectator_battle_start.png",
result = "g3battle_arena_spectator_result.png",
})
result(resultC == "ended", "the spectator watched the whole battle (" .. tostring(resultC) .. ")")
result(shotsC.start and shotsC.result, "spectator battle start and result were captured")
local sameC, nC = true, 0
for turn, value in pairs(digestsA or {}) do
if digestsC and digestsC[turn] then
nC = nC + 1
if digestsC[turn] ~= value then sameC = false end
end
end
result(sameC and nC >= 1, "the spectator's simulation digested the same turns (" .. nC .. ")")
local sent = false
for _, row in ipairs(relayC.log) do if row.seat == nil then sent = true end end
result(not sent, "the spectator never sent a message")
finish()
end