Files
gen1recomp/src/link/Wire.lua
T
2026-09-25 10:28:21 -04:00

1625 lines
45 KiB
Lua

local Wire = {}
local MAX_INT = 2147483647
local MAX_TIME = 9007199254740991
local MAX_STRING = 64
local MAX_DETAIL = 160
local MAX_NAME = 40
local MAX_LIST = 64
local MAX_PARTY = 32
local MAX_MOVES = 8
local MAX_MODS = 256
local MAX_RECORDS = 4096
local MAX_EXTRA_DEPTH = 8
local MAX_ROUNDS = 16
function Wire.num(v, default, min, max)
local n = tonumber(v)
if n == nil or n ~= n then return default end
min = min or -MAX_INT
max = max or MAX_INT
if n < min then return min end
if n > max then return max end
return math.floor(n)
end
function Wire.str(v, default, maxLen)
if type(v) ~= "string" then return default end
maxLen = maxLen or MAX_STRING
if #v > maxLen then return v:sub(1, maxLen) end
return v
end
function Wire.chars(v, default, maxChars)
if type(v) ~= "string" then return default end
local maxBytes = maxChars * 4
local count, cut = 0, #v
for i = 1, #v do
local b = v:byte(i)
if b < 0x80 or b >= 0xC0 then
count = count + 1
if count > maxChars then
cut = i - 1
break
end
end
end
if cut > maxBytes then
cut = maxBytes
while cut > 0 do
local b = v:byte(cut + 1)
if b < 0x80 or b >= 0xC0 then break end
cut = cut - 1
end
end
if cut < #v then return v:sub(1, cut) end
return v
end
function Wire.bool(v, default)
if type(v) == "boolean" then return v end
return default
end
function Wire.list(v, maxN, fn)
local out = {}
if type(v) ~= "table" then return out end
local n = math.min(#v, maxN or MAX_LIST)
for i = 1, n do
local entry = fn(v[i])
if entry ~= nil then out[#out + 1] = entry end
end
return out
end
function Wire.records(v)
local out = {}
if type(v) ~= "table" then return out end
local n = 0
for k, val in pairs(v) do
if type(k) == "string" then
out[k] = Wire.str(val, nil, MAX_STRING) or tostring(Wire.num(val, 0))
n = n + 1
if n >= MAX_RECORDS then break end
end
end
return out
end
function Wire.plain(v, depth)
if type(v) ~= "table" then return nil end
depth = depth or 0
if depth > MAX_EXTRA_DEPTH then return nil end
local out = {}
for k, val in pairs(v) do
local kt, vt = type(k), type(val)
if kt == "string" or kt == "number" then
if vt == "string" then out[k] = Wire.str(val, nil, MAX_STRING)
elseif vt == "number" or vt == "boolean" then out[k] = val
elseif vt == "table" then out[k] = Wire.plain(val, depth + 1) end
end
end
return out
end
local STAT_KEYS = { "hp", "attack", "defense", "speed", "special" }
local function statMap(v)
local out = {}
if type(v) ~= "table" then return out end
for _, k in ipairs(STAT_KEYS) do
out[k] = Wire.num(v[k], nil, 0, 65535)
end
return out
end
local function move(v)
if type(v) ~= "table" then return { id = nil } end
return {
id = Wire.str(v.id, nil, MAX_STRING),
pp = Wire.num(v.pp, nil, 0, 255),
ppUps = Wire.num(v.ppUps, nil, 0, 255),
maxPp = Wire.num(v.maxPp, nil, 0, 255),
}
end
local function mon(v)
if type(v) ~= "table" then return {} end
return {
species = Wire.str(v.species, nil, MAX_STRING),
level = Wire.num(v.level, nil, 0, 65535),
exp = Wire.num(v.exp, nil, 0, MAX_INT),
experience = Wire.num(v.experience, nil, 0, MAX_INT),
hp = Wire.num(v.hp, nil, 0, 65535),
status = Wire.str(v.status, nil, MAX_STRING),
nickname = Wire.str(v.nickname, nil, MAX_NAME),
dvs = statMap(v.dvs),
statExp = statMap(v.statExp),
moves = Wire.list(v.moves, MAX_MOVES, move),
ot = Wire.str(v.ot, nil, MAX_NAME),
otId = Wire.num(v.otId, nil, 0, MAX_INT),
item = Wire.str(v.item, nil, MAX_STRING),
happiness = Wire.num(v.happiness, nil, 0, 65535),
pokerus = Wire.num(v.pokerus, nil, 0, 65535),
caughtLevel = Wire.num(v.caughtLevel, nil, 0, 65535),
-- ../pokecrystal/constants/pokemon_data_constants.asm:93-99
caughtTime = Wire.num(v.caughtTime, nil, 0, 255),
caughtLocation = Wire.num(v.caughtLocation, nil, 0, 255),
caughtByGender = Wire.str(v.caughtByGender, nil, MAX_NAME),
isEgg = Wire.bool(v.isEgg, nil),
eggSteps = Wire.num(v.eggSteps, nil, 0, MAX_INT),
extra = Wire.plain(v.extra),
}
end
local function modEntry(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.str(v.id, nil, MAX_NAME),
version = Wire.str(v.version, nil, MAX_NAME)
or Wire.num(v.version, nil, 0, MAX_INT),
affectsLink = Wire.bool(v.affectsLink, nil),
language = Wire.bool(v.language, nil),
}
end
local function name(v)
return Wire.str(v, nil, MAX_NAME)
end
local sanitize
local arenaStart
local SCHEMAS = {}
SCHEMAS.hello = function(m)
return {
protocol = Wire.num(m.protocol, nil, 0, MAX_INT),
name = name(m.name),
mode = Wire.str(m.mode, nil, MAX_STRING),
engineVersion = Wire.str(m.engineVersion, nil, MAX_STRING),
apiVersion = Wire.str(m.apiVersion, nil, MAX_STRING),
generation = Wire.num(m.generation, nil, 0, 255),
fingerprint = Wire.str(m.fingerprint, nil, MAX_STRING),
linkModified = Wire.bool(m.linkModified, nil),
ruleset = Wire.str(m.ruleset, nil, MAX_STRING),
mods = Wire.list(m.mods, MAX_MODS, modEntry),
}
end
SCHEMAS.records = function(m)
return {
pokemon = Wire.records(m.pokemon),
moves = Wire.records(m.moves),
heldItems = m.heldItems ~= nil and Wire.records(m.heldItems) or nil,
}
end
SCHEMAS.party = function(m)
return {
mons = Wire.list(m.mons, MAX_PARTY, mon),
seed = Wire.num(m.seed, nil, 0, MAX_INT),
forceLevel = Wire.num(m.forceLevel, nil, 0, 65535),
ruleset = Wire.str(m.ruleset, nil, MAX_STRING),
}
end
SCHEMAS.pick = function(m)
return { index = Wire.num(m.index, nil, -MAX_INT, MAX_INT) }
end
SCHEMAS.confirm = function(m)
return { ok = Wire.bool(m.ok, false) }
end
SCHEMAS.action = function(m)
return {
kind = Wire.str(m.kind, "", MAX_STRING),
slot = Wire.num(m.slot, nil, 1, MAX_MOVES),
index = Wire.num(m.index, nil, 1, MAX_PARTY),
-- an ITEM action (RFC 0021) is the turn, and the peer cannot apply
-- one it cannot name: the item id, and for the ETHERs the move slot.
-- Dropped here, the action still arrives and still spends the turn,
-- so the two sides diverge by a heal with nothing on screen to say
-- so -- the quietest desync there is.
item = Wire.str(m.item, nil, MAX_STRING),
move = Wire.num(m.move, nil, 1, MAX_MOVES),
}
end
SCHEMAS.hash = function(m)
local parts
if type(m.parts) == "table" then
parts = {
actives = Wire.str(m.parts.actives, nil, MAX_STRING),
volatile = Wire.str(m.parts.volatile, nil, MAX_STRING),
bench = Wire.str(m.parts.bench, nil, MAX_STRING),
}
end
return {
turn = Wire.num(m.turn, 0, 0, MAX_INT),
value = Wire.str(m.value, nil, MAX_STRING),
parts = parts,
}
end
SCHEMAS.replace = function(m)
return { index = Wire.num(m.index, 1, 1, MAX_PARTY) }
end
local MAX_SEAT = 4
local function seatNum(v)
return Wire.num(v, nil, -1, MAX_SEAT)
end
SCHEMAS.bye = function(m)
return { seat = seatNum(m.seat), relay = Wire.bool(m.relay, nil) }
end
SCHEMAS.forfeit = function(m)
return { match = Wire.str(m.match, nil, MAX_NAME),
reason = Wire.str(m.reason, nil, MAX_NAME),
seat = seatNum(m.seat),
relay = Wire.bool(m.relay, nil) }
end
SCHEMAS.ping = function(m)
return { t = Wire.num(m.t, 0, 0, MAX_TIME) }
end
SCHEMAS.pong = SCHEMAS.ping
SCHEMAS.join_error = function(m)
return { reason = Wire.str(m.reason, "", MAX_STRING),
field = Wire.str(m.field, nil, MAX_STRING),
detail = Wire.str(m.detail, nil, MAX_DETAIL),
triesLeft = Wire.num(m.triesLeft, nil, 0, 255),
retryAt = Wire.num(m.retryAt, nil, 0, MAX_TIME) }
end
SCHEMAS.match_start = function(m)
local out = {
opponent = name(m.opponent),
round = Wire.num(m.round, 0, 0, MAX_ROUNDS),
turnLimit = Wire.num(m.turnLimit, nil, 0, 65535),
role = Wire.str(m.role, "", MAX_STRING),
match = Wire.str(m.match, nil, MAX_NAME),
}
arenaStart(m, out)
return out
end
SCHEMAS.match_start_spectate = function(m)
local out = {
round = Wire.num(m.round, 0, 0, MAX_ROUNDS),
playerHost = name(m.playerHost),
playerGuest = name(m.playerGuest),
match = Wire.str(m.match, nil, MAX_NAME),
role = Wire.str(m.role, nil, MAX_STRING),
}
arenaStart(m, out)
return out
end
local SPECTATABLE = {
action = true, replace = true, bye = true, forfeit = true,
hello = true, party = true, hash = true,
}
SCHEMAS.spectate = function(m)
if type(m.msg) ~= "table" or not SPECTATABLE[m.msg.type] then return nil end
local inner = sanitize(m.msg)
if not inner then return nil end
return { side = Wire.str(m.side, "", MAX_STRING), msg = inner }
end
local CodeEntry = require("src.link.CodeEntry")
local MAX_DISPLAY_NAME = 16
local MAX_TICKET = 128
local MAX_SESSION = 64
local MAX_PROFILES = 16
local MAX_LOBBY_ENTRIES = 200
local MAX_NOTE = 40
local MAX_SPECTATORS = 64
local MAX_REPLAY = 512
local MAX_ROOM_PLAYERS = 5
local MAX_ENTRY_PLAYERS = 64
local MAX_TEAM = 6
local MAX_DEADLINES = 8
local MAX_U32 = 4294967295
local MAX_SEATS = 5
local MAX_PLAZA = 40
local MAX_DIRECT = 64
local MAX_GROUPS = 64
local MAX_AVATAR_NAME = 16
local MAX_MON_TEXT = 32
local MAX_CHAT_NAME = 7
local MAX_CHAT_TEXT = 15
local MAX_WIRE_DIGEST = 16
local function displayName(v)
return Wire.str(v, nil, MAX_DISPLAY_NAME)
end
local function codeStr(v)
if type(v) ~= "string" then return nil end
local s = v:upper()
if #s ~= CodeEntry.LENGTH then return nil end
for i = 1, #s do
if not CodeEntry.CHARSET:find(s:sub(i, i), 1, true) then return nil end
end
return s
end
Wire.code = codeStr
local function patterned(pattern)
return function(v)
if type(v) ~= "string" then return nil end
local s = v:lower()
if not s:match(pattern) then return nil end
return s
end
end
Wire.roomId = patterned("^r" .. ("[0-9a-f]"):rep(16) .. "$")
Wire.tourId = patterned("^t" .. ("[0-9a-f]"):rep(16) .. "$")
Wire.inviteId = patterned("^i" .. ("[0-9a-f]"):rep(16) .. "$")
Wire.inviteToken = patterned("^" .. ("[0-9a-f]"):rep(32) .. "$")
Wire.playerId = patterned("^" .. ("[0-9a-f]"):rep(8) .. "$")
Wire.digest = patterned("^" .. ("[0-9a-f]"):rep(MAX_WIRE_DIGEST) .. "$")
function Wire.pin(v)
if type(v) ~= "string" or not v:match("^[0-9][0-9][0-9][0-9]$") then
return nil
end
return v
end
local STAT6 = { "hp", "atk", "def", "spe", "spa", "spd" }
local function stat6(v, max)
local out = {}
if type(v) ~= "table" then v = {} end
for _, k in ipairs(STAT6) do out[k] = Wire.num(v[k], 0, 0, max) end
return out
end
local function move3(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.num(v.id, 0, 0, 65535),
pp = Wire.num(v.pp, 0, 0, 255),
ppUps = Wire.num(v.ppUps, 0, 0, 3),
}
end
local MON3_GENDERS = { M = true, F = true, U = true }
function Wire.mon3(v)
if type(v) ~= "table" then return nil end
local gender = v.gender
if type(gender) ~= "string" or not MON3_GENDERS[gender] then gender = "U" end
return {
species = Wire.num(v.species, 0, 0, 65535),
nickname = Wire.str(v.nickname, "", MAX_MON_TEXT),
level = Wire.num(v.level, 0, 0, 255),
exp = Wire.num(v.exp, 0, 0, MAX_INT),
hp = Wire.num(v.hp, 0, 0, 65535),
status = Wire.str(v.status, "", 8),
personality = Wire.num(v.personality, 0, 0, MAX_U32),
otId = Wire.num(v.otId, 0, 0, 65535),
otSecretId = Wire.num(v.otSecretId, 0, 0, 65535),
otName = Wire.str(v.otName, "", MAX_MON_TEXT),
otGender = Wire.num(v.otGender, 0, 0, 1),
nature = Wire.num(v.nature, 0, 0, 255),
ability = Wire.num(v.ability, 0, 0, 255),
gender = gender,
ivs = stat6(v.ivs, 255),
evs = stat6(v.evs, 65535),
moves = Wire.list(v.moves, 4, move3),
item = Wire.num(v.item, 0, 0, 65535),
friendship = Wire.num(v.friendship, 0, 0, 255),
pokerus = Wire.num(v.pokerus, 0, 0, 255),
metLocation = Wire.num(v.metLocation, 0, 0, 255),
metLevel = Wire.num(v.metLevel, 0, 0, 255),
metGame = Wire.num(v.metGame, 0, 0, 255),
pokeball = Wire.num(v.pokeball, 0, 0, 255),
ribbons = Wire.num(v.ribbons, 0, 0, MAX_U32),
markings = Wire.num(v.markings, 0, 0, 255),
isEgg = Wire.bool(v.isEgg, false),
fatefulEncounter = Wire.bool(v.fatefulEncounter, false),
eggCycles = Wire.num(v.eggCycles, 0, 0, 255),
}
end
local function party3(v)
return Wire.list(v, MAX_TEAM, Wire.mon3)
end
Wire.party3 = party3
function Wire.avatar(v)
if type(v) ~= "table" then return nil end
return {
name = Wire.str(v.name, "", MAX_AVATAR_NAME),
trainerId = Wire.num(v.trainerId, 0, 0, 65535),
gender = Wire.num(v.gender, 0, 0, 1),
version = Wire.str(v.version, nil, MAX_AVATAR_NAME),
}
end
local function recruiting(v)
if type(v) ~= "table" then return nil end
return {
activity = Wire.str(v.activity, nil, MAX_NAME),
joined = Wire.num(v.joined, 0, 0, MAX_SEATS),
min = Wire.num(v.min, 0, 0, MAX_SEATS),
max = Wire.num(v.max, 0, 0, MAX_SEATS),
}
end
local function board(v)
if type(v) ~= "table" then return nil end
return {
species = Wire.num(v.species, 0, 0, 65535),
level = Wire.num(v.level, 0, 0, 255),
wantType = Wire.num(v.wantType, 0, 0, 255),
personality = Wire.num(v.personality, nil, 0, MAX_U32),
}
end
local function memberGroup(v)
if type(v) ~= "table" then return nil end
return {
leader = Wire.playerId(v.leader),
members = Wire.list(v.members, MAX_SEATS, Wire.playerId),
activity = Wire.str(v.activity, nil, MAX_NAME),
}
end
function Wire.member(v)
if type(v) ~= "table" then return nil end
local id = Wire.playerId(v.id)
if not id then return nil end
return {
id = id,
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
slot = Wire.num(v.slot, nil, 1, MAX_PLAZA),
online = Wire.bool(v.online, true),
status = Wire.str(v.status, nil, MAX_NAME),
avatar = Wire.avatar(v.avatar),
recruiting = recruiting(v.recruiting),
board = board(v.board),
group = memberGroup(v.group),
}
end
local function cartRef(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.str(v.id, nil, MAX_NAME),
version = Wire.str(v.version, nil, MAX_NAME),
hash = Wire.str(v.hash, nil, MAX_STRING),
}
end
local function arenaRule(v)
if type(v) ~= "table" then v = {} end
return {
partySize = Wire.num(v.partySize, nil, 1, MAX_TEAM),
minLevel = Wire.num(v.minLevel, nil, 1, 100),
maxLevel = Wire.num(v.maxLevel, nil, 1, 100),
forceLevel = Wire.num(v.forceLevel, nil, 1, 100),
}
end
local function profile(v)
if type(v) ~= "table" then return nil end
return {
engine = Wire.num(v.engine, nil, 1, 3),
version = Wire.str(v.version, nil, MAX_NAME),
engineVersion = Wire.str(v.engineVersion, nil, MAX_STRING),
apiVersion = Wire.num(v.apiVersion, nil, 0, MAX_INT),
fingerprint = Wire.str(v.fingerprint, nil, MAX_STRING),
rulesetId = Wire.str(v.rulesetId, nil, MAX_NAME),
kind = Wire.str(v.kind, nil, MAX_NAME),
cart = cartRef(v.cart),
rule = arenaRule(v.rule),
}
end
Wire.profile = profile
local function partyOf(v, engine)
if engine == 3 then return party3(v) end
return Wire.list(v, MAX_TEAM, mon)
end
local function seatPlayer(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.str(v.id, nil, MAX_NAME),
name = displayName(v.name),
seat = Wire.num(v.seat, nil, 0, MAX_SEAT),
}
end
local function seatParties(v, engine)
if type(v) ~= "table" then return nil end
local out = {}
for i = 1, MAX_SEATS do
if type(v[i]) == "table" then out[i] = partyOf(v[i], engine) end
end
return out
end
arenaStart = function(m, out)
local engine = Wire.num(m.engine, nil, 1, 3)
out.room = Wire.roomId(m.room)
out.engine = engine
out.seat = Wire.num(m.seat, nil, 0, MAX_SEAT)
out.seats = Wire.num(m.seats, nil, 2, MAX_SEATS)
out.players = m.players ~= nil and Wire.list(m.players, MAX_SEATS, seatPlayer) or nil
out.parties = seatParties(m.parties, engine)
out.seed = Wire.num(m.seed, nil, 0, MAX_INT)
out.ruleset = Wire.str(m.ruleset, nil, MAX_STRING)
out.rule = m.rule ~= nil and arenaRule(m.rule) or nil
out.peerName = displayName(m.peerName)
out.hostName = displayName(m.hostName)
out.guestName = displayName(m.guestName)
out.theirParty = m.theirParty ~= nil and partyOf(m.theirParty, engine) or nil
out.hostParty = m.hostParty ~= nil and partyOf(m.hostParty, engine) or nil
out.guestParty = m.guestParty ~= nil and partyOf(m.guestParty, engine) or nil
return out
end
local function youEntry(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.str(v.id, nil, MAX_NAME),
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
account = Wire.str(v.account, nil, MAX_NAME),
}
end
local function lobbyEntry(v)
if type(v) ~= "table" then return nil end
local id = Wire.str(v.id, nil, MAX_NAME)
if not id then return nil end
return {
id = id,
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
online = Wire.bool(v.online, true),
where = Wire.str(v.where, nil, MAX_NAME),
status = Wire.str(v.status, nil, MAX_NAME),
engine = Wire.num(v.engine, nil, 0, 3),
version = Wire.str(v.version, nil, MAX_NAME),
intent = Wire.str(v.intent, nil, MAX_NAME),
profile = profile(v.profile),
since = Wire.num(v.since, nil, 0, MAX_TIME),
note = Wire.str(v.note, nil, MAX_STRING),
room = Wire.roomId(v.room),
tour = Wire.tourId(v.tour),
locked = Wire.bool(v.locked, false),
auto = Wire.bool(v.auto, false),
open = Wire.bool(v.open, nil),
stage = Wire.str(v.stage, nil, MAX_NAME),
players = Wire.num(v.players, nil, 0, MAX_ENTRY_PLAYERS),
seats = Wire.num(v.seats, nil, 0, MAX_ENTRY_PLAYERS),
spectators = Wire.num(v.spectators, nil, 0, MAX_SPECTATORS),
maxSpectators = Wire.num(v.maxSpectators, nil, 0, MAX_SPECTATORS),
}
end
local function playerEntry(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.str(v.id, nil, MAX_NAME),
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
role = Wire.str(v.role, nil, MAX_NAME),
ready = Wire.bool(v.ready, false),
online = Wire.bool(v.online, true),
seat = Wire.num(v.seat, nil, 0, MAX_SEAT),
party = Wire.list(v.party, MAX_TEAM, mon),
partyDigest = Wire.str(v.partyDigest, nil, MAX_STRING),
}
end
local function spectatorEntry(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.str(v.id, nil, MAX_NAME),
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
online = Wire.bool(v.online, true),
}
end
local function deadlineEntry(v)
if type(v) ~= "table" then return nil end
return {
kind = Wire.str(v.kind, nil, MAX_NAME),
at = Wire.num(v.at, nil, 0, MAX_TIME),
}
end
local function innerMsg(v)
if type(v) ~= "table" then return nil end
return sanitize(v)
end
local function replayEntry(v)
if type(v) ~= "table" then return nil end
local inner = innerMsg(v.msg)
if not inner then
inner = innerMsg(v)
if not inner then return nil end
return { seq = Wire.num(v.seq, nil, 0, MAX_INT), msg = inner }
end
return {
seq = Wire.num(v.seq, nil, 0, MAX_INT),
clientSeq = Wire.num(v.clientSeq, nil, 0, MAX_INT),
seat = seatNum(v.seat),
side = Wire.str(v.side, nil, MAX_NAME),
relay = Wire.bool(v.relay, nil),
msg = inner,
}
end
local function presenceFields(v)
if type(v) ~= "table" then return nil end
local out = {
where = Wire.str(v.where, nil, MAX_NAME),
status = Wire.str(v.status, nil, MAX_NAME),
version = Wire.str(v.version, nil, MAX_NAME),
engine = Wire.num(v.engine, nil, 1, 3),
}
if v.board == false then
out.board = false
else
out.board = board(v.board)
end
return out
end
SCHEMAS.lobby_hello = function(m)
return {
protocol = Wire.num(m.protocol, nil, 0, MAX_INT),
ticket = Wire.str(m.ticket, nil, MAX_TICKET),
name = displayName(m.name),
engineVersion = Wire.str(m.engineVersion, nil, MAX_STRING),
platform = Wire.str(m.platform, nil, MAX_NAME),
profiles = Wire.list(m.profiles, MAX_PROFILES, profile),
presence = presenceFields(m.presence),
}
end
SCHEMAS.upgrade_required = function(m)
return {
protocol = Wire.num(m.protocol, nil, 0, MAX_INT),
minBuild = Wire.str(m.minBuild, nil, MAX_STRING),
text = Wire.str(m.text, nil, MAX_DETAIL),
}
end
SCHEMAS.set_profiles = function(m)
return { profiles = Wire.list(m.profiles, MAX_PROFILES, profile) }
end
SCHEMAS.presence = function(m)
return presenceFields(m)
end
SCHEMAS.lobby_welcome = function(m)
return {
session = Wire.str(m.session, nil, MAX_SESSION),
you = youEntry(m.you),
serverTime = Wire.num(m.serverTime, nil, 0, MAX_TIME),
heartbeatMs = Wire.num(m.heartbeatMs, nil, 0, 600000),
resumed = Wire.bool(m.resumed, false),
}
end
SCHEMAS.resume = function(m)
return {
session = Wire.str(m.session, nil, MAX_SESSION),
ack = Wire.num(m.ack, 0, 0, MAX_INT),
}
end
SCHEMAS.advertise = function(m)
return {
intent = Wire.str(m.intent, nil, MAX_NAME),
profile = profile(m.profile),
note = Wire.str(m.note, nil, MAX_STRING),
}
end
SCHEMAS.unadvertise = function() return {} end
SCHEMAS.room_create = function(m)
return {
intent = Wire.str(m.intent, nil, MAX_NAME),
profile = profile(m.profile),
playing = Wire.bool(m.playing, true),
maxSpectators = Wire.num(m.maxSpectators, nil, 0, MAX_SPECTATORS),
private = Wire.bool(m.private, false),
pin = Wire.pin(m.pin),
seats = Wire.num(m.seats, nil, 2, MAX_SEATS),
auto = Wire.bool(m.auto, false),
note = Wire.str(m.note, nil, MAX_NOTE),
}
end
SCHEMAS.room_join = function(m)
return {
room = Wire.roomId(m.room),
invite = Wire.inviteToken(m.invite),
as = Wire.str(m.as, nil, MAX_NAME),
profile = profile(m.profile),
pin = Wire.pin(m.pin),
}
end
SCHEMAS.invite_token = function(m)
return {
room = Wire.roomId(m.room),
token = Wire.inviteToken(m.token),
expiresAt = Wire.num(m.expiresAt, nil, 0, MAX_TIME),
}
end
SCHEMAS.room_leave = function() return {} end
SCHEMAS.room_ready = function(m)
return {
party = Wire.list(m.party, MAX_TEAM, mon),
partyDigest = Wire.str(m.partyDigest, nil, MAX_STRING),
}
end
SCHEMAS.room_msg = function(m)
local inner = innerMsg(m.msg)
if not inner then return nil end
return {
seq = Wire.num(m.seq, 0, 0, MAX_INT),
clientSeq = Wire.num(m.clientSeq, nil, 0, MAX_INT),
seat = seatNum(m.seat),
side = Wire.str(m.side, nil, MAX_NAME),
relay = Wire.bool(m.relay, nil),
msg = inner,
}
end
SCHEMAS.room_report = function(m)
return {
match = Wire.str(m.match, nil, MAX_NAME),
result = Wire.str(m.result, nil, MAX_NAME),
}
end
SCHEMAS.room_kick = function(m)
return { id = Wire.str(m.id, nil, MAX_NAME) }
end
SCHEMAS.room_close = function() return {} end
SCHEMAS.lobby_query = function() return {} end
SCHEMAS.room_ack = function(m)
return { seq = Wire.num(m.seq, 0, 0, MAX_INT) }
end
SCHEMAS.lobby_list = function(m)
return {
entries = Wire.list(m.entries, MAX_LOBBY_ENTRIES, lobbyEntry),
online = Wire.num(m.online, nil, 0, MAX_INT),
}
end
SCHEMAS.lobby_delta = function(m)
local ids = function(v)
return Wire.list(v, MAX_LOBBY_ENTRIES, function(x)
return Wire.str(x, nil, MAX_NAME)
end)
end
return {
added = Wire.list(m.added, MAX_LOBBY_ENTRIES, lobbyEntry),
changed = Wire.list(m.changed, MAX_LOBBY_ENTRIES, lobbyEntry),
removed = ids(m.removed),
add = Wire.list(m.add, MAX_LOBBY_ENTRIES, lobbyEntry),
update = Wire.list(m.update, MAX_LOBBY_ENTRIES, lobbyEntry),
remove = Wire.list(m.remove, MAX_LOBBY_ENTRIES, function(v)
return Wire.str(v, nil, MAX_NAME)
end),
op = Wire.str(m.op, nil, MAX_NAME),
entry = lobbyEntry(m.entry),
id = Wire.str(m.id, nil, MAX_NAME),
}
end
local ORIGINS = { create = true, invite = true, direct = true, group = true,
tour = true }
SCHEMAS.room_state = function(m)
local origin = Wire.str(m.origin, nil, MAX_NAME)
return {
room = Wire.roomId(m.room),
intent = Wire.str(m.intent, nil, MAX_NAME),
engine = Wire.num(m.engine, nil, 1, 3),
profile = profile(m.profile),
rule = m.rule ~= nil and arenaRule(m.rule) or nil,
seats = Wire.num(m.seats, nil, 2, MAX_SEATS),
locked = Wire.bool(m.locked, false),
listed = Wire.bool(m.listed, nil),
auto = Wire.bool(m.auto, false),
origin = ORIGINS[origin or ""] and origin or nil,
players = Wire.list(m.players, MAX_ROOM_PLAYERS, playerEntry),
spectators = Wire.list(m.spectators, MAX_SPECTATORS, spectatorEntry),
stage = Wire.str(m.stage, nil, MAX_NAME),
host = Wire.str(m.host, nil, MAX_NAME),
seed = Wire.num(m.seed, nil, 0, MAX_INT),
match = Wire.str(m.match, nil, MAX_NAME),
maxSpectators = Wire.num(m.maxSpectators, nil, 0, MAX_SPECTATORS),
leader = Wire.num(m.leader, nil, 0, MAX_SEAT),
deadlines = Wire.list(m.deadlines, MAX_DEADLINES, deadlineEntry),
}
end
SCHEMAS.room_replay = function(m)
return {
from = Wire.num(m.from, 0, 0, MAX_INT),
yourSeq = Wire.num(m.yourSeq, nil, 0, MAX_INT),
msgs = Wire.list(m.msgs, MAX_REPLAY, replayEntry),
}
end
SCHEMAS.room_deadline = function(m)
return {
kind = Wire.str(m.kind, nil, MAX_NAME),
at = Wire.num(m.at, nil, 0, MAX_TIME),
}
end
SCHEMAS.room_result = function(m)
return {
room = Wire.roomId(m.room),
match = Wire.str(m.match, nil, MAX_NAME),
winner = displayName(m.winner),
winnerId = Wire.str(m.winnerId, nil, MAX_NAME),
how = Wire.str(m.how, nil, MAX_NAME),
winnerSide = Wire.num(m.winnerSide, nil, 0, 1),
winners = Wire.list(m.winners, MAX_SEATS, function(v)
return Wire.str(v, nil, MAX_NAME)
end),
}
end
SCHEMAS.room_closed = function(m)
return {
reason = Wire.str(m.reason, nil, MAX_NAME),
room = Wire.roomId(m.room),
}
end
local function idList(v, maxN)
return Wire.list(v, maxN, Wire.playerId)
end
local function inviteDetail(v)
if type(v) ~= "table" then return nil end
return {
ruleset = Wire.str(v.ruleset, nil, MAX_NAME),
room = Wire.roomId(v.room),
tour = Wire.tourId(v.tour),
board = board(v.board),
join = Wire.bool(v.join, nil),
}
end
SCHEMAS.invite = function(m)
return {
to = Wire.playerId(m.to),
activity = Wire.str(m.activity, nil, MAX_NAME),
detail = inviteDetail(m.detail),
profile = profile(m.profile),
}
end
SCHEMAS.invite_reply = function(m)
return { id = Wire.inviteId(m.id), accept = Wire.bool(m.accept, false) }
end
SCHEMAS.invite_sent = function(m)
return {
id = Wire.inviteId(m.id),
to = Wire.playerId(m.to),
activity = Wire.str(m.activity, nil, MAX_NAME),
expiresAt = Wire.num(m.expiresAt, nil, 0, MAX_TIME),
}
end
local function inviteFrom(v)
if type(v) ~= "table" then return nil end
local id = Wire.playerId(v.id)
if not id then return nil end
return {
id = id,
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
where = Wire.str(v.where, nil, MAX_NAME),
avatar = Wire.avatar(v.avatar),
}
end
SCHEMAS.invite_in = function(m)
return {
id = Wire.inviteId(m.id),
from = inviteFrom(m.from),
activity = Wire.str(m.activity, nil, MAX_NAME),
detail = inviteDetail(m.detail),
expiresAt = Wire.num(m.expiresAt, nil, 0, MAX_TIME),
}
end
SCHEMAS.invite_closed = function(m)
return {
id = Wire.inviteId(m.id),
why = Wire.str(m.why, nil, MAX_NAME),
room = Wire.roomId(m.room),
detail = Wire.str(m.detail, nil, MAX_DETAIL),
to = Wire.playerId(m.to),
activity = Wire.str(m.activity, nil, MAX_NAME),
}
end
SCHEMAS.plaza_join = function(m)
return {
kind = Wire.str(m.kind, nil, MAX_NAME),
cap = Wire.num(m.cap, nil, 1, MAX_PLAZA),
profile = profile(m.profile),
avatar = Wire.avatar(m.avatar),
}
end
SCHEMAS.plaza_leave = function(m)
return { kind = Wire.str(m.kind, nil, MAX_NAME) }
end
SCHEMAS.plaza_state = function(m)
return {
kind = Wire.str(m.kind, nil, MAX_NAME),
instance = Wire.num(m.instance, nil, 0, MAX_INT),
cap = Wire.num(m.cap, nil, 1, MAX_PLAZA),
rev = Wire.num(m.rev, nil, 0, MAX_INT),
you = Wire.num(m.you, nil, 1, MAX_PLAZA),
members = type(m.members) == "table" and Wire.list(m.members, MAX_PLAZA, Wire.member)
or nil,
}
end
SCHEMAS.plaza_delta = function(m)
return {
kind = Wire.str(m.kind, nil, MAX_NAME),
instance = Wire.num(m.instance, nil, 0, MAX_INT),
rev = Wire.num(m.rev, nil, 0, MAX_INT),
joined = Wire.list(m.joined, MAX_PLAZA, Wire.member),
left = idList(m.left, MAX_PLAZA),
changed = Wire.list(m.changed, MAX_PLAZA, Wire.member),
}
end
SCHEMAS.plaza_counts = function(m)
return {
union = Wire.num(m.union, 0, 0, MAX_INT),
trade = Wire.num(m.trade, 0, 0, MAX_INT),
battle = Wire.num(m.battle, 0, 0, MAX_INT),
chat = Wire.num(m.chat, 0, 0, MAX_INT),
minigame = Wire.num(m.minigame, 0, 0, MAX_INT),
total = Wire.num(m.total, 0, 0, MAX_INT),
}
end
local function groupPerson(v)
if type(v) ~= "table" then return nil end
local id = Wire.playerId(v.id)
if not id then return nil end
return {
id = id,
name = displayName(v.name),
avatar = Wire.avatar(v.avatar),
seat = Wire.num(v.seat, nil, 0, MAX_SEAT),
}
end
local function groupEntry(v)
if type(v) ~= "table" then return nil end
local leader = Wire.playerId(v.leader)
if not leader then return nil end
return {
leader = leader,
name = displayName(v.name),
avatar = Wire.avatar(v.avatar),
version = Wire.str(v.version, nil, MAX_NAME),
joined = Wire.num(v.joined, 0, 0, MAX_SEATS),
min = Wire.num(v.min, 0, 0, MAX_SEATS),
max = Wire.num(v.max, 0, 0, MAX_SEATS),
}
end
SCHEMAS.group_open = function(m)
return {
activity = Wire.str(m.activity, nil, MAX_NAME),
profile = profile(m.profile),
avatar = Wire.avatar(m.avatar),
}
end
SCHEMAS.group_list = function(m)
return {
activity = Wire.str(m.activity, nil, MAX_NAME),
profile = profile(m.profile),
groups = m.groups ~= nil and Wire.list(m.groups, MAX_GROUPS, groupEntry) or nil,
}
end
SCHEMAS.group_join = function(m)
return {
leader = Wire.playerId(m.leader),
profile = profile(m.profile),
avatar = Wire.avatar(m.avatar),
}
end
SCHEMAS.group_accept = function(m)
return { from = Wire.playerId(m.from), ok = Wire.bool(m.ok, false) }
end
SCHEMAS.group_leave = function() return {} end
SCHEMAS.group_start = function() return {} end
SCHEMAS.group_state = function(m)
return {
leader = Wire.playerId(m.leader),
activity = Wire.str(m.activity, nil, MAX_NAME),
min = Wire.num(m.min, 0, 0, MAX_SEATS),
max = Wire.num(m.max, 0, 0, MAX_SEATS),
members = type(m.members) == "table" and Wire.list(m.members, MAX_SEATS, groupPerson)
or nil,
pending = Wire.list(m.pending, MAX_GROUPS, groupPerson),
}
end
SCHEMAS.group_request = function(m)
return {
from = Wire.playerId(m.from),
name = displayName(m.name),
avatar = Wire.avatar(m.avatar),
}
end
SCHEMAS.group_closed = function(m)
return {
leader = Wire.playerId(m.leader),
why = Wire.str(m.why, nil, MAX_NAME),
}
end
local function speciesList(v)
return Wire.list(v, MAX_TEAM, function(x) return Wire.num(x, nil, 0, 65535) end)
end
local function directEntry(v)
if type(v) ~= "table" then return nil end
local kind = v.kind
if kind == "player" then
local id = Wire.playerId(v.id)
if not id then return nil end
return {
kind = "player",
id = id,
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
avatar = Wire.avatar(v.avatar),
version = Wire.str(v.version, nil, MAX_NAME),
preview = speciesList(v.preview),
since = Wire.num(v.since, nil, 0, MAX_TIME),
}
elseif kind == "room" then
local room = Wire.roomId(v.room)
if not room then return nil end
return {
kind = "room",
room = room,
host = Wire.playerId(v.host),
name = displayName(v.name),
avatar = Wire.avatar(v.avatar),
version = Wire.str(v.version, nil, MAX_NAME),
locked = Wire.bool(v.locked, false),
auto = Wire.bool(v.auto, false),
seats = Wire.num(v.seats, nil, 2, MAX_SEATS),
players = Wire.num(v.players, nil, 0, MAX_SEATS),
since = Wire.num(v.since, nil, 0, MAX_TIME),
}
end
return nil
end
SCHEMAS.direct_queue = function(m)
return {
activity = Wire.str(m.activity, nil, MAX_NAME),
ruleset = Wire.str(m.ruleset, nil, MAX_NAME),
auto = Wire.bool(m.auto, false),
pin = Wire.pin(m.pin),
profile = profile(m.profile),
avatar = Wire.avatar(m.avatar),
preview = speciesList(m.preview),
}
end
SCHEMAS.direct_list = function(m)
return {
activity = Wire.str(m.activity, nil, MAX_NAME),
profile = profile(m.profile),
entries = m.entries ~= nil and Wire.list(m.entries, MAX_DIRECT, directEntry) or nil,
}
end
SCHEMAS.direct_leave = function() return {} end
SCHEMAS.direct_state = function(m)
return {
activity = Wire.str(m.activity, nil, MAX_NAME),
queued = Wire.bool(m.queued, false),
hosting = Wire.roomId(m.hosting),
why = Wire.str(m.why, nil, MAX_NAME),
}
end
local MAX_TOUR_PLAYERS = 64
local MAX_TOUR_SPECTATORS = 64
local MAX_TOUR_ROUNDS = 7
local MAX_TOUR_MATCHES = 32
local TOUR_STAGES = { registering = true, running = true, finished = true }
local TOUR_MATCH_STATES = { pending = true, live = true, done = true,
bye = true }
local function tourPlayerEntry(v)
if type(v) ~= "table" then return nil end
return {
id = Wire.str(v.id, nil, MAX_NAME),
name = displayName(v.name),
verified = Wire.bool(v.verified, false),
online = Wire.bool(v.online, true),
eliminated = Wire.bool(v.eliminated, false),
}
end
local function tourMatchEntry(v)
if type(v) ~= "table" then return nil end
local state = Wire.str(v.state, nil, MAX_NAME)
return {
match = Wire.str(v.match, nil, MAX_NAME),
a = Wire.str(v.a, nil, MAX_NAME),
b = Wire.str(v.b, nil, MAX_NAME),
winner = Wire.str(v.winner, nil, MAX_NAME),
how = Wire.str(v.how, nil, MAX_NAME),
state = TOUR_MATCH_STATES[state or ""] and state or nil,
}
end
local function tourRoundEntry(v)
if type(v) ~= "table" then return nil end
return {
round = Wire.num(v.round, 0, 0, MAX_TOUR_ROUNDS),
matches = Wire.list(v.matches, MAX_TOUR_MATCHES, tourMatchEntry),
}
end
local function profileEngine(p)
return type(p) == "table" and p.engine or nil
end
SCHEMAS.tour_create = function(m)
local prof = profile(m.profile)
return {
profile = prof,
rule = m.rule ~= nil and arenaRule(m.rule) or nil,
playing = Wire.bool(m.playing, true),
shotClock = Wire.num(m.shotClock, nil, 0, 3600),
maxSpectators = Wire.num(m.maxSpectators, nil, 0, MAX_TOUR_SPECTATORS),
party = m.party ~= nil and partyOf(m.party, profileEngine(prof)) or nil,
partyDigest = Wire.str(m.partyDigest, nil, MAX_STRING),
public = Wire.bool(m.public, true),
note = Wire.str(m.note, nil, MAX_NOTE),
}
end
SCHEMAS.tour_join = function(m)
local prof = profile(m.profile)
return {
tour = Wire.tourId(m.tour),
code = codeStr(m.code),
as = Wire.str(m.as, nil, MAX_NAME),
profile = prof,
party = partyOf(m.party, profileEngine(prof)),
partyDigest = Wire.str(m.partyDigest, nil, MAX_STRING),
}
end
SCHEMAS.tour_leave = function() return {} end
SCHEMAS.tour_start = function() return {} end
SCHEMAS.tour_close = function() return {} end
SCHEMAS.tour_kick = function(m)
return { id = Wire.str(m.id, nil, MAX_NAME) }
end
SCHEMAS.tour_state = function(m)
local stage = Wire.str(m.stage, nil, MAX_NAME)
return {
tour = Wire.tourId(m.tour),
code = codeStr(m.code),
creator = Wire.str(m.creator, nil, MAX_NAME),
stage = TOUR_STAGES[stage or ""] and stage or "registering",
players = Wire.list(m.players, MAX_TOUR_PLAYERS, tourPlayerEntry),
spectators = Wire.list(m.spectators, MAX_TOUR_SPECTATORS, spectatorEntry),
profile = profile(m.profile),
rule = m.rule ~= nil and arenaRule(m.rule) or nil,
shotClock = Wire.num(m.shotClock, nil, 0, 3600),
round = Wire.num(m.round, 0, 0, MAX_TOUR_ROUNDS),
bracket = Wire.list(m.bracket, MAX_TOUR_ROUNDS, tourRoundEntry),
live = Wire.str(m.live, nil, MAX_NAME),
champion = Wire.str(m.champion, nil, MAX_NAME),
championId = Wire.str(m.championId, nil, MAX_NAME),
maxSpectators = Wire.num(m.maxSpectators, nil, 0, MAX_TOUR_SPECTATORS),
}
end
SCHEMAS.tour_match = function(m)
return {
match = Wire.str(m.match, nil, MAX_NAME),
round = Wire.num(m.round, 0, 0, MAX_TOUR_ROUNDS),
room = Wire.roomId(m.room),
}
end
SCHEMAS.tour_match_spectate = SCHEMAS.tour_match
SCHEMAS.tour_bye = function(m)
return {
match = Wire.str(m.match, nil, MAX_NAME),
round = Wire.num(m.round, 0, 0, MAX_TOUR_ROUNDS),
}
end
SCHEMAS.tour_deadline = function(m)
return {
kind = Wire.str(m.kind, nil, MAX_NAME),
at = Wire.num(m.at, nil, 0, MAX_TIME),
match = Wire.str(m.match, nil, MAX_NAME),
}
end
SCHEMAS.tour_closed = function(m)
return {
tour = Wire.tourId(m.tour),
reason = Wire.str(m.reason, nil, MAX_NAME),
}
end
SCHEMAS.tour_over = function(m)
return {
tour = Wire.tourId(m.tour),
champion = displayName(m.champion),
championId = Wire.str(m.championId, nil, MAX_NAME),
}
end
local function inner3(fn)
return function(m)
local out = fn(m)
if not out then return nil end
out.seat = seatNum(m.seat)
out.relay = Wire.bool(m.relay, nil)
return out
end
end
local function g3extra(v)
if type(v) ~= "table" then return nil end
return {
cacheVersion = Wire.num(v.cacheVersion, nil, 0, MAX_INT),
nativeVersion = Wire.num(v.nativeVersion, nil, 0, MAX_INT)
or Wire.str(v.nativeVersion, nil, MAX_NAME),
linkType = Wire.num(v.linkType, nil, 0, 65535),
trainerId = Wire.num(v.trainerId, 0, 0, MAX_U32),
gender = Wire.num(v.gender, 0, 0, 1),
seat = Wire.num(v.seat, nil, 0, MAX_SEAT),
}
end
SCHEMAS.game3_hello = inner3(function(m)
local out = SCHEMAS.hello(m)
out.game3 = g3extra(m.game3)
return out
end)
SCHEMAS.game3_bye = inner3(function(m)
return { reason = Wire.str(m.reason, nil, MAX_NAME) }
end)
SCHEMAS.game3_link_card = inner3(function(m)
return { card = Wire.plain(m.card, MAX_EXTRA_DEPTH - 3) }
end)
SCHEMAS.game3_exit_link_room = inner3(function() return {} end)
SCHEMAS.game3_battle_linkup = inner3(function(m)
return {
linkType = Wire.num(m.linkType, nil, 0, 65535),
players = Wire.num(m.players, nil, 0, MAX_SEATS),
}
end)
SCHEMAS.game3_battle_seat = inner3(function() return {} end)
SCHEMAS.game3_battle_setup = inner3(function(m)
return {
mode = Wire.str(m.mode, nil, MAX_NAME),
unionRoom = Wire.bool(m.unionRoom, false),
name = Wire.str(m.name, nil, MAX_NAME),
trainerId = Wire.num(m.trainerId, 0, 0, MAX_U32),
gender = Wire.num(m.gender, 0, 0, 1),
seed = Wire.num(m.seed, nil, 0, MAX_U32),
party = party3(m.party),
}
end)
local function action3(v)
if type(v) ~= "table" then return nil end
return {
kind = Wire.str(v.kind, nil, MAX_NAME),
slot = Wire.num(v.slot, nil, 0, 255),
move = Wire.num(v.move, nil, 0, 65535) or Wire.str(v.move, nil, MAX_NAME),
target = Wire.num(v.target, nil, 0, 255),
itemId = Wire.num(v.itemId, nil, 0, 65535),
partySlot = Wire.num(v.partySlot, nil, 0, 255),
}
end
SCHEMAS.game3_battle_action = inner3(function(m)
local out = action3(m)
out.turn = Wire.num(m.turn, 0, 0, MAX_INT)
out.actions = m.actions ~= nil and Wire.list(m.actions, 2, action3) or nil
return out
end)
SCHEMAS.game3_battle_switch = inner3(function(m)
return { slot = Wire.num(m.slot, nil, 0, 255) }
end)
SCHEMAS.game3_battle_outcome = inner3(function(m)
return {
outcome = Wire.num(m.outcome, nil, 0, 255),
turn = Wire.num(m.turn, nil, 0, MAX_INT),
}
end)
SCHEMAS.game3_battle_hash = inner3(function(m)
local parts
if type(m.parts) == "table" then
parts = {
actives = Wire.str(m.parts.actives, nil, MAX_STRING),
volatile = Wire.str(m.parts.volatile, nil, MAX_STRING),
bench = Wire.str(m.parts.bench, nil, MAX_STRING),
field = Wire.str(m.parts.field, nil, MAX_STRING),
rng = Wire.str(m.parts.rng, nil, MAX_STRING),
}
end
return {
turn = Wire.num(m.turn, 0, 0, MAX_INT),
value = Wire.str(m.value, nil, MAX_STRING),
parts = parts,
}
end)
SCHEMAS.game3_trade_cmd = inner3(function(m)
return {
cmd = Wire.num(m.cmd, nil, 0, 65535),
cursor = Wire.num(m.cursor, nil, 0, 255),
}
end)
SCHEMAS.game3_trade_party = inner3(function(m)
return {
party = party3(m.party),
name = Wire.str(m.name, nil, MAX_NAME),
trainerId = Wire.num(m.trainerId, 0, 0, MAX_U32),
gender = Wire.num(m.gender, 0, 0, 1),
version = Wire.num(m.version, nil, 0, 255) or Wire.str(m.version, nil, MAX_NAME),
progressFlags = Wire.num(m.progressFlags, nil, 0, MAX_INT),
}
end)
SCHEMAS.game3_trade_mon = inner3(function(m)
return {
mon = Wire.mon3(m.mon),
mail = Wire.plain(m.mail, MAX_EXTRA_DEPTH - 3),
name = Wire.str(m.name, nil, MAX_NAME),
trainerId = Wire.num(m.trainerId, 0, 0, MAX_U32),
}
end)
local function confirmDigest(m)
return { digest = Wire.digest(m.digest) }
end
SCHEMAS.game3_trade_confirm = inner3(confirmDigest)
SCHEMAS.trade_confirm = inner3(confirmDigest)
SCHEMAS.trade_commit = inner3(function(m)
return {
n = Wire.num(m.n, 0, 0, MAX_INT),
digests = Wire.list(m.digests, 2, Wire.digest),
}
end)
SCHEMAS.trade_abort = inner3(function(m)
return {
n = Wire.num(m.n, 0, 0, MAX_INT),
why = Wire.str(m.why, nil, MAX_NAME),
}
end)
SCHEMAS.game3_union_hello = inner3(function(m)
return {
name = Wire.str(m.name, nil, MAX_NAME),
gender = Wire.num(m.gender, 0, 0, 1),
trainerId = Wire.num(m.trainerId, 0, 0, MAX_U32),
activity = Wire.num(m.activity, nil, 0, 255),
}
end)
SCHEMAS.game3_union_bye = inner3(function(m)
return { reason = Wire.str(m.reason, nil, MAX_NAME) }
end)
SCHEMAS.game3_union_request = inner3(function(m)
return {
activity = Wire.num(m.activity, nil, 0, 255),
name = Wire.str(m.name, nil, MAX_NAME),
}
end)
SCHEMAS.game3_union_response = inner3(function(m)
return { accept = Wire.bool(m.accept, false) }
end)
SCHEMAS.game3_chat_line = inner3(function(m)
return {
name = Wire.chars(m.name, nil, MAX_CHAT_NAME),
text = Wire.chars(m.text, "", MAX_CHAT_TEXT),
}
end)
SCHEMAS.game3_chat_bye = inner3(function() return {} end)
SCHEMAS.game3_mg_ready = inner3(function(m)
return {
species = Wire.num(m.species, nil, 0, 65535),
partySlot = Wire.num(m.partySlot, nil, 0, 255),
name = Wire.chars(m.name, nil, MAX_CHAT_NAME),
trainerId = Wire.num(m.trainerId, nil, 0, 65535),
gender = Wire.num(m.gender, nil, 0, 1),
}
end)
local function mgPlayer(v)
if type(v) ~= "table" then return nil end
return {
seat = Wire.num(v.seat, nil, 0, MAX_SEAT),
name = Wire.str(v.name, nil, MAX_NAME),
trainerId = Wire.num(v.trainerId, 0, 0, MAX_U32),
gender = Wire.num(v.gender, 0, 0, 1),
species = Wire.num(v.species, nil, 0, 65535),
}
end
SCHEMAS.game3_mg_start = inner3(function(m)
return {
game = Wire.str(m.game, nil, MAX_NAME),
seed = Wire.num(m.seed, nil, 0, MAX_U32),
epoch = Wire.num(m.epoch, nil, 0, MAX_INT),
players = Wire.list(m.players, MAX_SEATS, mgPlayer),
}
end)
SCHEMAS.game3_mg_state = inner3(function(m)
return {
f = Wire.num(m.f, 0, 0, MAX_INT),
e = Wire.num(m.e, 0, 0, MAX_INT),
s = Wire.plain(m.s, MAX_EXTRA_DEPTH - 3),
}
end)
SCHEMAS.game3_mg_input = inner3(function(m)
local i = m.i
if type(i) == "table" then
i = Wire.plain(i, MAX_EXTRA_DEPTH - 2)
else
i = Wire.num(i, nil)
end
return { f = Wire.num(m.f, 0, 0, MAX_INT), i = i }
end)
local function mgScore(v)
if type(v) ~= "table" then return nil end
return {
seat = Wire.num(v.seat, nil, 0, MAX_SEAT),
score = Wire.num(v.score, 0, 0, MAX_INT),
stats = Wire.plain(v.stats, MAX_EXTRA_DEPTH - 2),
}
end
local function mgPowder(v)
if type(v) ~= "table" then return nil end
return {
seat = Wire.num(v.seat, nil, 0, MAX_SEAT),
amount = Wire.num(v.amount, 0, 0, MAX_INT),
}
end
SCHEMAS.game3_mg_result = inner3(function(m)
return {
game = Wire.str(m.game, nil, MAX_NAME),
results = Wire.list(m.results, MAX_SEATS, mgScore),
powder = Wire.list(m.powder, MAX_SEATS, mgPowder),
}
end)
SCHEMAS.game3_mg_bye = inner3(function(m)
return { reason = Wire.str(m.reason, nil, MAX_NAME) }
end)
SCHEMAS.game3_mg_leader = inner3(function(m)
return {
prev = Wire.num(m.prev, nil, -1, MAX_SEAT),
epoch = Wire.num(m.epoch, 0, 0, MAX_INT),
}
end)
Wire.SCHEMAS = SCHEMAS
local function passthrough(m)
local out = Wire.plain(m) or {}
out.type = nil
return out
end
sanitize = function(msg)
if type(msg) ~= "table" then return nil end
local kind = msg.type
if type(kind) ~= "string" or #kind > MAX_STRING then return nil end
local schema = SCHEMAS[kind]
local out
if schema then
out = schema(msg)
if not out then return nil end
elseif kind:sub(1, 6) == "game3_" then
return nil
else
out = passthrough(msg)
end
out.type = kind
return out
end
Wire.sanitize = sanitize
return Wire