Files
2026-09-24 05:17:32 -04:00

151 lines
5.8 KiB
Lua

return function(game)
local U = dofile("tests/drivers/util.lua")
local Net = require("src.link.Net")
local addr = os.getenv("POKEPORT_RELAY_ADDR") or "127.0.0.1:7778"
local failures = 0
local function check(cond, label)
U.log((cond and "ok " or "FAIL ") .. label)
if not cond then failures = failures + 1 end
end
local function newClient()
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 = addr })
return C
end
local clients = {}
local function pumpAll()
for _, C in ipairs(clients) do C.update(1 / 60) end
end
local function waitFor(pred, seconds)
local deadline = love.timer.getTime() + (seconds or 4)
while love.timer.getTime() < deadline do
pumpAll()
if pred() then return true end
coroutine.yield()
end
return false
end
local function profile(rulesetId)
return { engine = 3, version = "firered", engineVersion = "0.0.0-dev", apiVersion = 2,
fingerprint = "clasmoke", rulesetId = rulesetId or "g3_single",
kind = "vanilla", rule = { partySize = 3 } }
end
local old = Net.new()
check(old:connectTCP(addr), "a v1 socket still dials the relay")
old:send({ type = "host" })
local up
local deadline = love.timer.getTime() + 4
while not up and love.timer.getTime() < deadline do
old:update()
for _, m in ipairs(old:poll()) do
if m.type == "upgrade_required" then up = m end
end
coroutine.yield()
end
check(up ~= nil and up.protocol == 3, "a v1 host is answered with upgrade_required")
old:close()
local Stale = newClient()
clients[#clients + 1] = Stale
local Protocol2 = require("src.online.Protocol2")
local savedProtocol = Protocol2.PROTOCOL
Protocol2.PROTOCOL = 2
Stale.connect({ name = "STALE", profiles = { profile() } })
Protocol2.PROTOCOL = savedProtocol
waitFor(function() return Stale.state() == "error" end)
check(Stale.upgradeRequired() ~= nil, "a protocol 2 client surfaces upgrade_required")
check(tostring(Stale.error()):find("update", 1, true) ~= nil,
"with the relay's update text")
local A = newClient()
local B = newClient()
clients[#clients + 1] = A
clients[#clients + 1] = B
A.connect({ name = "CLAHOST", profiles = { profile() },
presence = { where = "launcher", status = "idle", version = "firered" } })
B.connect({ name = "CLAJOIN", profiles = { profile() },
presence = { where = "launcher", status = "idle", version = "leafgreen" } })
check(waitFor(function() return A.state() == "online" and B.state() == "online" end),
"two protocol 3 clients come online")
local made = A.createRoom({ intent = "battle", profile = profile(), private = true,
pin = "2468", maxSpectators = 2 })
waitFor(function() return made.done end)
local roomId = made.id
check(made.done and made.error == nil and roomId ~= nil,
"a private engine 3 room is created: " .. tostring(made.error))
check(A.room() and A.room().locked == true, "the room is locked")
check(A.seat() == 0, "the host sits in seat 0")
local listed = waitFor(function()
for _, e in ipairs(B.lobby()) do
if e.room == roomId then return e.locked == true and e.code == nil end
end
return false
end)
check(listed, "the joiner sees the room listed with a lock and no code")
local noPin = B.joinRoom(roomId, "player", profile())
waitFor(function() return noPin.done end)
check(noPin.reason == "pin_required", "no PIN answers pin_required: " .. tostring(noPin.reason))
local wrong = B.joinRoom(roomId, "player", profile(), "1357")
waitFor(function() return wrong.done end)
check(wrong.reason == "bad_pin" and wrong.triesLeft == 4,
"a wrong PIN answers bad_pin with 4 tries left: " .. tostring(wrong.reason)
.. " " .. tostring(wrong.triesLeft))
local startA, startB
A.on("match_start", function(p) startA = p end)
B.on("match_start", function(p) startB = p end)
local right = B.joinRoom(roomId, "player", profile(), "2468")
waitFor(function() return right.done and startA and startB end)
check(right.done and right.error == nil, "the right PIN seats the joiner")
check(startA and startA.seat == 0 and startA.role == "host", "match_start seats the host at 0")
check(startB and startB.seat == 1 and startB.role == "guest", "and the joiner at 1")
check(startA and startB and startA.seed == startB.seed and startA.seed ~= nil,
"both seats share the relay's seed")
check(B.room() and B.room().stage == "battling", "the full engine 3 room is battling")
local rsA, rsB = A.roomSession(), B.roomSession()
rsA:send({ type = "game3_battle_action", turn = 1, kind = "move", slot = 1, move = 33 })
local got
waitFor(function()
got = got or rsB:take("game3_battle_action")
return got ~= nil
end)
check(got ~= nil and got.seat == 0 and got.move == 33,
"an inner game3 message crosses the real relay tagged with seat 0")
rsB:send({ type = "game3_made_up", x = 1 })
rsB:send({ type = "game3_battle_switch", slot = 2 })
local sw
waitFor(function()
sw = sw or rsA:take("game3_battle_switch")
return sw ~= nil
end)
check(sw ~= nil and sw.seat == 1, "the guest's switch arrives tagged with seat 1")
local token
A.on("invite_token", function(m) token = m.token end)
A.inviteToken(roomId)
waitFor(function() return token ~= nil end)
check(token ~= nil, "a seated player mints an invite token")
A.leaveRoom()
B.leaveRoom()
waitFor(function() return A.room() == nil and B.room() == nil end, 1)
A.disconnect()
B.disconnect()
Stale.disconnect()
U.log("CLA_RELAY_SMOKE:", failures == 0 and "PASS" or ("FAIL " .. failures))
love.event.quit(failures == 0 and 0 or 1)
end