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

247 lines
7.5 KiB
Lua

local U = require("tests.drivers.util")
local M = {}
M.CENTER_2F = "FR_VIRIDIAN_CITY_POKEMON_CENTER_2F"
M.TRADE_CENTER = "FR_TRADE_CENTER"
M.COLOSSEUM = "FR_BATTLE_COLOSSEUM_2P"
-- pokefirered/include/constants/flags.h:1375
M.FLAG_SYS_POKEDEX_GET = 0x829
local function now() return love.timer.getTime() end
M.now = now
function M.new(game, tag, dir)
local W = { game = game, tag = tag, dir = dir, failures = 0, partners = {} }
function W.result(ok, label)
print((ok and "PASS " or "FAIL ") .. label)
if not ok then W.failures = W.failures + 1 end
return ok
end
function W.finish()
local Link = package.loaded["src.core.game3.link"]
if Link then pcall(Link.reset) end
for _, P in ipairs(W.partners) do
if P.C then pcall(P.C.disconnect) end
end
local Connect = package.loaded["src.online.Connect"]
if Connect and Connect.disconnect then pcall(Connect.disconnect) end
local Client = package.loaded["src.online.Client"]
if Client then pcall(Client.disconnect) end
if W.failures == 0 then
print("PASS " .. tag)
love.event.quit(0)
else
print("FAIL " .. tag .. " failures=" .. W.failures)
love.event.quit(1)
end
end
function W.boot()
for _ = 1, 900 do
if game.phase == "boot" and game.boot then break end
U.wait(1)
end
game:_handleBootAction({ action = "new_game", name = "RED" })
U.wait(240)
W.Runtime = require("src.core.game3.runtime")
W.Map = require("src.core.game3.map")
W.Space = require("src.core.game3.scripting.space")
W.Flags = require("src.core.game3.scripting.flags")
W.Player = require("src.core.game3.player")
W.Message = require("src.ui.game3.message")
W.Choice = require("src.ui.game3.choice")
W.SaveMenu = require("src.ui.game3.save_menu")
W.Party = require("src.core.game3.party")
W.RomText = require("src.core.game3.rom_text")
W.Link = require("src.core.game3.link")
W.Strings = require("src.core.Strings")
W.Client = require("src.online.Client")
W.Direct = require("src.ui.game3.link_menu").Direct
W.PinEntry = require("src.ui.game3.pin_entry")
W.session = W.Runtime.getSession()
return W.session ~= nil
end
function W.ctx() return W.Space.vm and W.Space.vm.ctx end
function W.place(x, y, facing)
local P = W.Player
P.cellX, P.cellY = x, y
P.px, P.py = x * 16, y * 16
P.targetX, P.targetY = x, y
P.facing = facing
game.session.x, game.session.y, game.session.facing = x, y, facing
end
function W.pump()
if W.relay then W.relay:pump() end
for _, P in ipairs(W.partners) do
if P.C then P.C.update(1 / 60) end
end
end
function W.wait(n)
for _ = 1, n do
W.pump()
U.wait(1)
end
end
function W.waitFor(cond, seconds, frames)
local t0, n = now(), 0
while not cond() do
W.pump()
U.wait(1)
n = n + 1
if now() - t0 > (seconds or 5) and n > (frames or 60) then return false end
end
return true
end
function W.tap(btn, settle)
U.tap(game, btn)
W.wait(settle or 4)
end
function W.pageHas(text)
local page = W.Message.isOpen() and W.Message.currentPage() or ""
local flat = function(s) return (tostring(s):gsub("%s+", " ")) end
return flat(page):find(flat(text), 1, true) ~= nil
end
local function messageNeedsA()
local M = W.Message
if not (M.isOpen() and M.isWaiting()) then return false end
local lastPage = (M._page or 1) >= #(M._pages or {})
return not (M._stay and lastPage)
end
function W.drive(cond, seconds)
local t0 = now()
while not cond() and now() - t0 < (seconds or 10) do
if W.Direct.isOpen() or W.PinEntry.isOpen() then
W.wait(2)
elseif W.Choice.active or W.SaveMenu.isOpen() or messageNeedsA() then
U.tap(game, "a")
W.wait(6)
else
W.wait(2)
end
end
W.wait(2)
return cond()
end
function W.live()
return W.Link.liveProfile()
end
function W.withRuleset(rulesetId)
local p = {}
for k, v in pairs(W.live()) do p[k] = v end
p.rulesetId = rulesetId
return p
end
function W.relayAddr()
return os.getenv("POKEPORT_RELAY_ADDR")
end
function W.useRelay()
local addr = W.relayAddr()
if addr and addr ~= "" then
W.mode = "real"
W.Client.configure({ relayAddress = addr })
return
end
W.mode = "fake"
local Relay = require("tests.support.fake_relay")
W.relay = Relay.new({ clock = function() return now() end })
W.me = W.relay:seat("a0000001", "RED")
W.Client.configure({ relayAddress = "fake:1", connect = function() return W.me.transport end })
end
function W.partner(id, name, avatar)
local P = { id = id, name = name, avatar = avatar, seq = 0 }
if W.mode == "fake" then
local s = W.relay:seat(id, name)
P.s = s
W.relay:handle(s, { type = "lobby_hello", protocol = 3, name = name, profiles = { W.withRuleset("g3_link") },
presence = { where = "launcher", status = "idle", version = avatar.version } })
function P.queue(opts)
local msg = { type = "direct_queue" }
for k, v in pairs(opts) do msg[k] = v end
W.relay:handle(s, msg)
end
function P.send(msg)
P.seq = P.seq + 1
W.relay:handle(s, { type = "room_msg", seq = P.seq, clientSeq = P.seq, msg = msg })
end
function P.online() return true end
function P.room()
local rid = s.room
return rid and W.relay.rooms[rid] or nil
end
function P.seat()
local room = P.room()
return room and W.relay:seatOf(room, id) or nil
end
else
local shared = package.loaded["src.online.Client"]
package.loaded["src.online.Client"] = nil
local C = require("src.online.Client")
package.loaded["src.online.Client"] = shared
C.reset()
C.configure({ relayAddress = W.relayAddr() })
C.connect({ name = name, profiles = { W.withRuleset("g3_link"), W.withRuleset("g3_single") },
presence = { where = "launcher", status = "idle", version = avatar.version } })
P.C = C
W.partners[#W.partners + 1] = P
local Json = require("src.link.Json")
C.on("room", function(r)
if type(r) ~= "table" then return end
local names = {}
for _, p in ipairs(r.players or {}) do names[#names + 1] = tostring(p.name) .. "@" .. tostring(p.seat) end
print("[driver] " .. name .. " room " .. tostring(r.room) .. " stage=" .. tostring(r.stage)
.. " players=" .. table.concat(names, ","))
end)
C.on("error", function(e) print("[driver] " .. name .. " error " .. Json.encode(e or {})) end)
function P.queue(opts) C.queueDirect(opts) end
function P.send(msg)
local rs = C.roomSession()
if rs then rs:send(msg) end
end
function P.online() return C.state() == "online" end
function P.room() return C.room() end
function P.seat() return C.seat() end
end
return P
end
function W.sendHello(P, seat)
local lk = W.Link.link
local hello = {}
for k, v in pairs(lk.myHello) do hello[k] = v end
hello.name = P.name
hello.game3 = { cacheVersion = lk.myHello.game3.cacheVersion,
nativeVersion = lk.myHello.game3.nativeVersion, linkType = lk.linkType,
trainerId = P.avatar.trainerId, gender = P.avatar.gender, seat = seat }
P.send(hello)
end
function W.shot(name)
return U.still(game, W.dir .. "/" .. name .. ".png")
end
function W.frame(name)
return U.shot(game, W.dir .. "/" .. name .. ".png")
end
return W
end
return M