mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-28 23:00:21 -04:00
1625 lines
45 KiB
Lua
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
|