Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(match2): use new lpdb fields #4822

Closed
wants to merge 15 commits into from
49 changes: 43 additions & 6 deletions components/match2/commons/match.lua
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ end

---@param match table
function Match._prepareMatchRecordForStore(match)
Match._commonBackwardsCompatabilityForV3API(match)

match.dateexact = Logic.readBool(match.dateexact) and 1 or 0
match.finished = Logic.readBool(match.finished) and 1 or 0
match.match2bracketdata = match.match2bracketdata or match.bracketdata
Expand Down Expand Up @@ -364,12 +366,17 @@ end
---@param matchRecord table
---@param gameRecord table
function Match._prepareGameRecordForStore(matchRecord, gameRecord)
-- Backwards compatibility for API v3
gameRecord.opponents = gameRecord.opponents or {}
Match._commonBackwardsCompatabilityForV3API(gameRecord)

gameRecord.parent = matchRecord.parent
gameRecord.tournament = matchRecord.tournament

if not gameRecord.participants then
gameRecord.participants = {}
for opponentId, opponent in ipairs(gameRecord.opponents or {}) do
for playerId, player in pairs(opponent.players) do
for playerId, player in pairs(opponent.players or {}) do
-- Deep copy have to be used here, otherwise a json.stringify complains about circular references
-- between participants and opponents
gameRecord.participants[opponentId .. '_' .. playerId] = Table.deepCopy(player)
Expand All @@ -379,6 +386,35 @@ function Match._prepareGameRecordForStore(matchRecord, gameRecord)
Match.clampFields(gameRecord, Match.gameFields)
end

---Adds fields needed for backwards compatibility with API v3.
---walkover and resulttype are added to record.
---@param record table #game or match record
function Match._commonBackwardsCompatabilityForV3API(record)
if record.finished then
if not record.walkover then
local opponents = record.opponents or {}
local function calculateWalkover()
local walkoverOpponent = Array.find(opponents, function(opponent)
return opponent.status == 'FF' or opponent.status == 'DQ' or opponent.status == 'L'
end)
return walkoverOpponent and walkoverOpponent.status:lower() or ''
end
record.walkover = calculateWalkover()
end

if not record.resulttype then
if record.status == 'notplayed' then
record.resulttype = 'np'
elseif record.winner == 0 then
record.resulttype = 'draw'
elseif record.walkover ~= '' then
record.resulttype = record.walkover:upper()
else
record.resulttype = ''
end
end
end
end

Match.matchFields = Table.map({
'bestof',
Expand All @@ -400,7 +436,7 @@ Match.matchFields = Table.map({
'parentname',
'patch',
'publishertier',
'resulttype',
'resulttype', -- LPDB API v3: backwards compatibility
'series',
'shortname',
'status',
Expand All @@ -409,7 +445,7 @@ Match.matchFields = Table.map({
'tournament',
'type',
'vod',
'walkover',
'walkover', -- LPDB API v3: backwards compatibility
'winner',
'section',
}, function(_, field) return field, true end)
Expand Down Expand Up @@ -443,15 +479,16 @@ Match.gameFields = Table.map({
'parent',
'participants',
'patch',
'opponents', -- Not a real field yet, but will be in the future. Also for use in Match/Legacy
'resulttype',
'opponents',
'resulttype', -- LPDB API v3: backwards compatibility
'rounds',
'scores',
'status',
'subgroup',
'tournament',
'type',
'vod',
'walkover',
'walkover', -- LPDB API v3: backwards compatibility
'winner',
}, function(_, field) return field, true end)

Expand Down
84 changes: 15 additions & 69 deletions components/match2/commons/match_group_input_util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,10 @@ MatchGroupInputUtil.STATUS_INPUTS = {
MatchGroupInputUtil.STATUS = Table.copy(MatchGroupInputUtil.STATUS_INPUTS)
MatchGroupInputUtil.STATUS.SCORE = 'S'

MatchGroupInputUtil.RESULT_TYPE = {
DEFAULT = 'default',
NOT_PLAYED = 'np',
DRAW = 'draw',
MatchGroupInputUtil.MATCH_STATUS = {
NOT_PLAYED = 'notplayed',
}

MatchGroupInputUtil.WALKOVER = {
FORFIET = 'ff',
DISQUALIFIED = 'dq',
Expand Down Expand Up @@ -613,37 +612,27 @@ function MatchGroupInputUtil.isNotPlayed(winnerInput, finishedInput)
or (type(finishedInput) == 'string' and MatchGroupInputUtil.isNotPlayedInput(finishedInput))
end

---Should only be called on finished matches or maps
---@param winnerInput integer|string|nil
---@param finishedInput string?
---@param opponents {score: number?, status: string}[]
---@return string? #Result Type
function MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponents)
---@return string? #Match Status
function MatchGroupInputUtil.getMatchStatus(winnerInput, finishedInput)
if MatchGroupInputUtil.isNotPlayed(winnerInput, finishedInput) then
return MatchGroupInputUtil.RESULT_TYPE.NOT_PLAYED
end

if MatchGroupInputUtil.isDraw(opponents, winnerInput) then
return MatchGroupInputUtil.RESULT_TYPE.DRAW
end

if MatchGroupInputUtil.hasSpecialStatus(opponents) then
return MatchGroupInputUtil.RESULT_TYPE.DEFAULT
return MatchGroupInputUtil.MATCH_STATUS.NOT_PLAYED
end
end

---@param resultType string?
---@param status string?
---@param winnerInput integer|string|nil
---@param opponents {score: number, status: string, placement: integer?}[]
---@return integer? # Winner
function MatchGroupInputUtil.getWinner(resultType, winnerInput, opponents)
if resultType == MatchGroupInputUtil.RESULT_TYPE.NOT_PLAYED then
function MatchGroupInputUtil.getWinner(status, winnerInput, opponents)
if status == MatchGroupInputUtil.MATCH_STATUS.NOT_PLAYED then
return nil
elseif Logic.isNumeric(winnerInput) then
return tonumber(winnerInput)
elseif resultType == MatchGroupInputUtil.RESULT_TYPE.DRAW then
elseif MatchGroupInputUtil.isDraw(opponents, winnerInput) then
return MatchGroupInputUtil.WINNER_DRAW
elseif resultType == MatchGroupInputUtil.RESULT_TYPE.DEFAULT then
elseif MatchGroupInputUtil.hasSpecialStatus(opponents) then
return MatchGroupInputUtil.getDefaultWinner(opponents)
elseif MatchGroupInputUtil.findOpponentWithFirstPlace(opponents) then
return MatchGroupInputUtil.findOpponentWithFirstPlace(opponents)
Expand Down Expand Up @@ -675,27 +664,6 @@ function MatchGroupInputUtil.getHighestScoringOpponent(opponents)
return Array.indexOf(scores, FnUtil.curry(Operator.eq, maxScore))
end

---@param resultType string?
---@param opponents {status: string}[]
---@return string? # Walkover Type
function MatchGroupInputUtil.getWalkover(resultType, opponents)
if resultType == MatchGroupInputUtil.RESULT_TYPE.DEFAULT then
return MatchGroupInputUtil.getWalkoverType(opponents)
end
end

---@param opponents {status: string}[]
---@return string?
function MatchGroupInputUtil.getWalkoverType(opponents)
if MatchGroupInputUtil.hasForfeit(opponents) then
return MatchGroupInputUtil.WALKOVER.FORFIET
elseif MatchGroupInputUtil.hasDisqualified(opponents) then
return MatchGroupInputUtil.WALKOVER.DISQUALIFIED
elseif MatchGroupInputUtil.hasDefaultWinLoss(opponents) then
return MatchGroupInputUtil.WALKOVER.NO_SCORE
end
end

---@param match table
---@param maps {scores: integer[]?, winner: integer?}[]
---@return boolean
Expand Down Expand Up @@ -810,55 +778,33 @@ function MatchGroupInputUtil._opponentWithStatus(opponents, status)
return Array.indexOf(opponents, function (opponent) return opponent.status == status end)
end

-- function to check for forfeits
---@param opponents {status: string?}[]
---@return boolean
function MatchGroupInputUtil.hasForfeit(opponents)
return MatchGroupInputUtil._opponentWithStatus(opponents, MatchGroupInputUtil.STATUS.FORFIET) ~= 0
end

-- function to check for DQ's
---@param opponents {status: string?}[]
---@return boolean
function MatchGroupInputUtil.hasDisqualified(opponents)
return MatchGroupInputUtil._opponentWithStatus(opponents, MatchGroupInputUtil.STATUS.DISQUALIFIED) ~= 0
end

-- function to check for W/L
---@param opponents {status: string?}[]
---@return boolean
function MatchGroupInputUtil.hasDefaultWinLoss(opponents)
return MatchGroupInputUtil._opponentWithStatus(opponents, MatchGroupInputUtil.STATUS.DEFAULT_LOSS) ~= 0
end

-- function to check for Normal Scores
---@param opponents {status: string?}[]
---@return boolean
function MatchGroupInputUtil.hasScore(opponents)
return MatchGroupInputUtil._opponentWithStatus(opponents, MatchGroupInputUtil.STATUS.SCORE) ~= 0
end

-- Get the winner when resulttype=default
-- Get the winner with letter results
---@param opponents {status: string?}[]
---@return integer
function MatchGroupInputUtil.getDefaultWinner(opponents)
local idx = MatchGroupInputUtil._opponentWithStatus(opponents, MatchGroupInputUtil.STATUS.DEFAULT_WIN)
return idx > 0 and idx or -1
end


--- Calculate the correct value of the 'placement' for the two-opponent matches/games.
--- Cases:
--- If Winner = OpponentIndex, return 1
--- If Winner = 0, means it was a draw, return 1
--- If Winner = -1, means that mean no team won, returns 2
--- Otherwise return 2
---@param resultType string?
---@param status string?
---@param winner integer?
---@param opponentIndex integer
---@return integer?
function MatchGroupInputUtil.placementFromWinner(resultType, winner, opponentIndex)
if resultType == MatchGroupInputUtil.RESULT_TYPE.NOT_PLAYED then
function MatchGroupInputUtil.placementFromWinner(status, winner, opponentIndex)
if status == MatchGroupInputUtil.MATCH_STATUS.NOT_PLAYED then
return nil
end
if winner == 0 or winner == opponentIndex then
Expand Down
8 changes: 8 additions & 0 deletions components/match2/commons/match_group_util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ MatchGroupUtil.types.GameOpponent = TypeUtil.struct({
type = 'string',
})

---@alias MatchStatus 'notplayed'|''|nil
MatchGroupUtil.types.Status = TypeUtil.optional(TypeUtil.literalUnion('notplayed', ''))
---@alias ResultType 'default'|'draw'|'np'
MatchGroupUtil.types.ResultType = TypeUtil.literalUnion('default', 'draw', 'np')
---@alias WalkoverType 'l'|'ff'|'dq'
Expand All @@ -210,6 +212,7 @@ MatchGroupUtil.types.Walkover = TypeUtil.literalUnion('l', 'ff', 'dq')
---@field participants table
---@field resultType ResultType?
---@field scores number[]
---@field status MatchStatus
---@field subgroup number?
---@field type string?
---@field vod string?
Expand All @@ -228,6 +231,7 @@ MatchGroupUtil.types.Game = TypeUtil.struct({
participants = 'table',
resultType = TypeUtil.optional(MatchGroupUtil.types.ResultType),
scores = TypeUtil.array('number'),
status = MatchGroupUtil.types.Status,
subgroup = 'number?',
type = 'string?',
vod = 'string?',
Expand All @@ -249,6 +253,7 @@ MatchGroupUtil.types.Game = TypeUtil.struct({
---@field mode string?
---@field opponents standardOpponent[]
---@field resultType ResultType?
---@field status MatchStatus
---@field stream table
---@field tickername string?
---@field tournament string?
Expand All @@ -272,6 +277,7 @@ MatchGroupUtil.types.Match = TypeUtil.struct({
mode = 'string',
opponents = TypeUtil.array(MatchGroupUtil.types.Opponent),
resultType = 'string?',
status = MatchGroupUtil.types.Status,
stream = 'table',
tickername = 'string?',
tournament = 'string?',
Expand Down Expand Up @@ -530,6 +536,7 @@ function MatchGroupUtil.matchFromRecord(record)
parent = record.parent,
patch = record.patch,
resultType = nilIfEmpty(record.resulttype),
status = nilIfEmpty(record.status),
stream = Json.parseIfString(record.stream) or {},
tickername = record.tickername,
timestamp = tonumber(Table.extract(extradata, 'timestamp')),
Expand Down Expand Up @@ -725,6 +732,7 @@ function MatchGroupUtil.gameFromRecord(record, opponentCount)
participants = participants,
resultType = nilIfEmpty(record.resulttype),
scores = Json.parseIfString(record.scores) or {},
status = nilIfEmpty(record.status),
subgroup = tonumber(record.subgroup),
type = nilIfEmpty(record.type),
vod = nilIfEmpty(record.vod),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,10 @@ function StarcraftMatchGroupInput.processMatch(match, options)
match.finished = MatchGroupInputUtil.matchIsFinished(match, opponents)

if match.finished then
match.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponents)
match.walkover = MatchGroupInputUtil.getWalkover(match.resulttype, opponents)
match.winner = MatchGroupInputUtil.getWinner(match.resulttype, winnerInput, opponents)
match.status = MatchGroupInputUtil.getMatchStatus(winnerInput, finishedInput)
match.winner = MatchGroupInputUtil.getWinner(match.status, winnerInput, opponents)
Array.forEach(opponents, function(opponent, opponentIndex)
opponent.placement = MatchGroupInputUtil.placementFromWinner(match.resulttype, match.winner, opponentIndex)
opponent.placement = MatchGroupInputUtil.placementFromWinner(match.status, match.winner, opponentIndex)
end)
end

Expand Down Expand Up @@ -292,7 +291,7 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount)
}

map.finished = MapFunctions.isFinished(mapInput, opponentCount)
local opponentInfo = Array.map(Array.range(1, opponentCount), function(opponentIndex)
map.opponents = Array.map(Array.range(1, opponentCount), function(opponentIndex)
local score, status = MatchGroupInputUtil.computeOpponentScore({
walkover = mapInput.walkover,
winner = mapInput.winner,
Expand All @@ -302,12 +301,11 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount)
return {score = score, status = status}
end)

map.scores = Array.map(opponentInfo, Operator.property('score'))
map.scores = Array.map(map.opponents, Operator.property('score'))

if map.finished then
map.resulttype = MatchGroupInputUtil.getResultType(mapInput.winner, mapInput.finished, opponentInfo)
map.walkover = MatchGroupInputUtil.getWalkover(map.resulttype, opponentInfo)
map.winner = MatchGroupInputUtil.getWinner(map.resulttype, mapInput.winner, opponentInfo)
map.status = MatchGroupInputUtil.getMatchStatus(mapInput.winner, mapInput.finished)
map.winner = MatchGroupInputUtil.getWinner(map.status, mapInput.winner, map.opponents)
end

return map, subGroup
Expand Down
Loading
Loading