Files
gen1recomp/tests/support/union_prep_loopback.lua
T
2026-10-07 14:31:38 -04:00

138 lines
4.1 KiB
Lua

local Loop = {}
Loop.__index = Loop
Loop.PEER_DIGEST = "00000000000000b2"
local function copy(v)
if type(v) ~= "table" then return v end
local out = {}
for k, x in pairs(v) do out[k] = copy(x) end
return out
end
function Loop.new(opts)
opts = opts or {}
local self = setmetatable({ seat = opts.seat or 0, rev = 0, inbox = {}, rosters = {}, sizeReq = {},
ready = {}, size = nil, rules = nil, closed = false, stage = "prep", sent = {} }, Loop)
local me = self
self.session = {
send = function(m)
me.sent[#me.sent + 1] = copy(m)
me:fromSeat(me.seat, m)
return not me.closed
end,
take = function(pred)
for i, m in ipairs(me.inbox) do
if pred(m) then return table.remove(me.inbox, i) end
end
return nil
end,
seat = function() return me.seat end,
open = function() return not me.closed or #me.inbox > 0 end,
leave = function() me.closed = true end,
}
return self
end
function Loop:peer() return 1 - self.seat end
function Loop:push(m)
m.relay = true
self.inbox[#self.inbox + 1] = m
end
function Loop:bump()
self.rev = self.rev + 1
self.ready = {}
end
function Loop:setRules(r)
self:bump()
self.rules = copy(r)
local m = copy(r)
m.type, m.rev = "xg_rules", self.rev
m.mode = m.mode or "battle"
self:push(m)
end
function Loop:sizeOf()
local a, b = self.rosters[0], self.rosters[1]
if not a or not b then return nil end
local q0, q1 = self.sizeReq[0], self.sizeReq[1]
if q0 and q0 == q1 and q0 <= a.size and q0 <= b.size then return q0 end
return math.min(a.size, b.size)
end
function Loop:nack(seat, kind, why, rev)
if seat == self.seat then
self:push({ type = "xg_nack", of = kind, why = why, rev = rev, current = self.rev })
end
end
function Loop:fromSeat(seat, m)
if self.closed or type(m) ~= "table" then return end
local kind = m.type
if kind == "xg_cancel" then return self:close("cancel", seat) end
if self.stage ~= "prep" then return end
if m.rev ~= self.rev then return self:nack(seat, kind, "stale_rev", m.rev) end
local fwd
if kind == "xg_roster" then
self.rosters[seat] = { size = m.size, digest16 = m.digest16 }
fwd = { type = kind, rev = m.rev, size = m.size, digest16 = m.digest16 }
elseif kind == "xg_size_req" then
self.sizeReq[seat] = m.size
fwd = { type = kind, rev = m.rev, size = m.size }
elseif kind == "xg_ready" then
if not self.rules then return self:nack(seat, kind, "no_rules", m.rev) end
if not self.size then return self:nack(seat, kind, "no_roster", m.rev) end
if self.ready[seat] then return end
self.ready[seat] = m.digest16
if seat ~= self.seat then self:push({ type = kind, rev = self.rev, digest16 = m.digest16 }) end
if self.ready[0] and self.ready[1] then self:go() end
return
else
return
end
self:bump()
if seat ~= self.seat then self:push(fwd) end
self:push({ type = "xg_rev", rev = self.rev, seat = seat, cause = kind })
self.size = self:sizeOf()
if self.size then self:push({ type = "xg_size", rev = self.rev, size = self.size }) end
end
function Loop:go()
self.stage = "battling"
local r = self.rules or {}
self:push({ type = "xg_go", rev = self.rev, seed = 777, match = "loop-m1", mode = "battle",
ruleset = r.ruleset, size = self.size, gen = r.gen, dexMax = r.dexMax, moveMax = r.moveMax, moveGen = r.moveGen })
end
function Loop:close(why, seat)
if self.closed then return end
self:push({ type = "xg_closed", why = why, seat = seat })
self.closed = true
end
function Loop:peerRoster(size, digest)
self:fromSeat(self:peer(), { type = "xg_roster", rev = self.rev, size = size, digest16 = digest or Loop.PEER_DIGEST })
end
function Loop:peerSizeReq(size)
self:fromSeat(self:peer(), { type = "xg_size_req", rev = self.rev, size = size })
end
function Loop:peerReady(digest)
self:fromSeat(self:peer(), { type = "xg_ready", rev = self.rev, digest16 = digest or Loop.PEER_DIGEST })
end
function Loop:peerCancel()
self:close("cancel", self:peer())
end
function Loop:prep()
local Prep = require("src.online.union.Prep")
return Prep.new(self.session, { mode = "battle" })
end
return Loop