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): properly attach participants to their playerid #4645

Merged
merged 24 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions components/match2/commons/match_group_input_util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ function MatchGroupInputUtil.mergeRecordWithOpponent(record, opponent, substitut
record.icondark = opponent.icondark or record.icondark
end

if not record.match2players and Logic.isNotEmpty(opponent.players) then
record.match2players = Array.map(opponent.players, function(player)
if not record.match2players then
record.match2players = Array.map(opponent.players or {}, function(player)
return {
displayname = player.displayName,
flag = player.flag,
Expand Down Expand Up @@ -1089,6 +1089,78 @@ function MatchGroupInputUtil.getCharacterName(alias, character)
return (assert(alias[character:lower()], 'Invalid character:' .. character))
end

---@param players {name: string?, displayname: string?}[]
---@param playerInput string?
---@param playerLink string?
---@return integer?
function MatchGroupInputUtil.findPlayerId(players, playerInput, playerLink)
if Logic.isEmpty(playerInput) and Logic.isEmpty(playerLink) then
return
end

local playerLinks = Array.map(players, Operator.property('name'))
local playerIndex = Array.indexOf(playerLinks, FnUtil.curry(Operator.eq, playerLink))
if playerIndex > 0 then
return playerIndex
end

local playerDisplayNames = Array.map(players, Operator.property('displayname'))
playerIndex = Array.indexOf(playerDisplayNames, FnUtil.curry(Operator.eq, playerInput))
if playerIndex > 0 then
return playerIndex
end
mw.log('Player with id ' .. playerInput .. ' not found in opponent data')
end

---@param name string
---@param options {pagifyPlayerNames: boolean?}?
---@return string
function MatchGroupInputUtil.makeLinkFromName(name, options)
local link = mw.ext.TeamLiquidIntegration.resolve_redirect(name)

if (options or {}).pagifyPlayerNames then
link = Page.pageifyLink(link) --[[@as string]]
end
hjpalpha marked this conversation as resolved.
Show resolved Hide resolved

return link
end

---@param playerIds table[]
---@param inputPlayers table[]
---@param indexToPlayer fun(playerIndex: integer): {name: string?, link: string?}?
---@param transform fun(playerIndex: integer, playerIdData?: table, playerInputData?: table): table?
---@param options {pagifyPlayerNames: boolean?}?
---@return table, table
function MatchGroupInputUtil.parseParticipants(playerIds, inputPlayers, indexToPlayer, transform, options)
local participants = {}
local unattachedParticipants = {}
hjpalpha marked this conversation as resolved.
Show resolved Hide resolved
local function parsePlayer(_, playerIndex)
local playerInputData = indexToPlayer(playerIndex) or {}
if playerInputData.name and not playerInputData.link then
playerInputData.link = MatchGroupInputUtil.makeLinkFromName(playerInputData.name, options)
end
local playerId = MatchGroupInputUtil.findPlayerId(playerIds, playerInputData.name, playerInputData.link)
local toStoreData = transform(playerIndex, playerIds[playerId] or {}, playerInputData)
if playerId then
participants[playerId] = toStoreData
else
table.insert(unattachedParticipants, toStoreData)
end
end
Array.forEach(inputPlayers, parsePlayer)

return participants, unattachedParticipants
end

---@generic T:table
---@param opponentIndex integer
---@return fun(playerIndex: integer, data: T): string, T
function MatchGroupInputUtil.prefixPartcipants(opponentIndex)
return function(playerIndex, data)
return opponentIndex .. '_' .. playerIndex, data
end
end

--- Warning, both match and standalone match may be mutated
---@param match table
---@param standaloneMatch table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ local Flags = require('Module:Flags')
local Logic = require('Module:Logic')
local Lua = require('Module:Lua')
local Operator = require('Module:Operator')
local Page = require('Module:Page')
local String = require('Module:StringUtils')
local Table = require('Module:Table')
local Variables = require('Module:Variables')
Expand Down Expand Up @@ -371,73 +370,52 @@ end
---@param opponentIndex integer
---@return table<string, {faction: string?, player: string, position: string, flag: string?}>
function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex)
local players = opponent.match2players

local archonFaction = Faction.read(mapInput['t' .. opponentIndex .. 'p1race'])
or Faction.read(mapInput['opponent' .. opponentIndex .. 'race'])
or ((players[1] or {}).extradata or {}).faction
or ((opponent.match2players[1] or {}).extradata or {}).faction
local isArchon = MapFunctions.isArchon(mapInput, opponent, opponentIndex)

---@type {input: string, faction: string?, link: string?}[]
local participantsList = Array.mapIndexes(function(playerIndex)
local prefix = 't' .. opponentIndex .. 'p' .. playerIndex

if Logic.isEmpty(mapInput[prefix]) then return end

return {
input = mapInput[prefix],
link = Logic.nilIfEmpty(mapInput[prefix .. 'link']),
faction = isArchon and archonFaction or Faction.read(mapInput[prefix .. 'race']),
}
local players = Array.mapIndexes(function(playerIndex)
return Logic.nilIfEmpty(mapInput['t' .. opponentIndex .. 'p' .. playerIndex])
end)

local participants = {}

Array.forEach(participantsList, function(participantInput, position)
local nameInput = participantInput.input

local isTBD = nameInput:upper() == TBD or nameInput:upper() == TBA

local link = participantInput.link or Variables.varDefault(nameInput .. '_page') or nameInput
link = Page.pageifyLink(link) --[[@as string -- can't be nil as input isn't nil]]

local playerIndex = MapFunctions.getPlayerIndex(players, link, nameInput)

-- in case we have a TBD or a player not known in match2players inster a new player in match2players
if isTBD or playerIndex == 0 then
table.insert(players, {
name = isTBD and TBD or link,
displayname = isTBD and TBD or nameInput,
extradata = {faction = participantInput.faction or Faction.defaultFaction},
})
playerIndex = #players
end

local player = players[playerIndex]
local participants, unattachedParticipants = MatchGroupInputUtil.parseParticipants(
opponent.match2players,
players,
Rathoz marked this conversation as resolved.
Show resolved Hide resolved
function(playerIndex)
local prefix = 't' .. opponentIndex .. 'p' .. playerIndex
return {
name = mapInput[prefix],
link = Logic.nilIfEmpty(mapInput[prefix .. 'link']) or Variables.varDefault(mapInput[prefix] .. '_page'),
faction = isArchon and archonFaction or Faction.read(mapInput[prefix .. 'race']),
}
end,
function(playerIndex, playerIdData, playerInputData)
return {
faction = playerInputData.faction or (playerIdData.extradata or {}).faction or Faction.defaultFaction,
player = playerIdData.name or playerInputData.link,
flag = Flags.CountryName(playerIdData.flag),
position = playerIndex,
}
end,
OPPONENT_CONFIG
)

participants[opponentIndex .. '_' .. playerIndex] = {
faction = participantInput.faction or player.extradata.faction,
player = link,
position = position,
flag = Flags.CountryName(player.flag),
}
Array.forEach(unattachedParticipants, function(participant)
local name = mapInput['t' .. opponentIndex .. 'p' .. participant.position]
local nameUpper = name:upper()
local isTBD = nameUpper == TBD or nameUpper == TBA

table.insert(opponent.match2players, {
name = isTBD and TBD or participant.player,
displayname = isTBD and TBD or name,
flag = participant.flag,
extradata = {faction = participant.faction},
})
participants[#opponent.match2players] = participant
end)

return participants
end

---@param players {name: string, displayname: string}
---@param name string
---@param displayName string
---@return integer
function MapFunctions.getPlayerIndex(players, name, displayName)
local playerIndex = Array.indexOf(players, function(player) return player.name == name end)

if playerIndex ~= 0 then
return playerIndex
end

return Array.indexOf(players, function(player) return player.displayname == displayName end)
return Table.map(participants, MatchGroupInputUtil.prefixPartcipants(opponentIndex))
end

---@param mapInput table
Expand Down
54 changes: 34 additions & 20 deletions components/match2/wikis/brawlstars/match_group_input_custom.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function CustomMatchGroupInput.processMatch(match, options)
local opponents = Array.mapIndexes(function(opponentIndex)
return MatchGroupInputUtil.readOpponent(match, opponentIndex, {})
end)
local games = CustomMatchGroupInput.extractMaps(match, #opponents)
local games = CustomMatchGroupInput.extractMaps(match, opponents)
match.bestof = MatchFunctions.getBestOf(match)

local autoScoreFunction = MatchGroupInputUtil.canUseAutoScore(match, games)
Expand Down Expand Up @@ -84,20 +84,20 @@ function CustomMatchGroupInput.processMatch(match, options)
end

---@param match table
---@param opponentCount integer
---@param opponents table[]
---@return table[]
function CustomMatchGroupInput.extractMaps(match, opponentCount)
function CustomMatchGroupInput.extractMaps(match, opponents)
local maps = {}
for key, map, mapIndex in Table.iter.pairsByPrefix(match, 'map', {requireIndex = true}) do
local finishedInput = map.finished --[[@as string?]]
local winnerInput = map.winner --[[@as string?]]

map.vod = map.vod or String.nilIfEmpty(match['vodgame' .. mapIndex])
map.bestof = MapFunctions.getBestOf(map)
map.participants = MapFunctions.getParticipants(map, opponentCount)
map.extradata = MapFunctions.getExtraData(map, opponentCount)
map.participants = MapFunctions.getParticipants(map, opponents)
map.extradata = MapFunctions.getExtraData(map, #opponents)

local opponentInfo = Array.map(Array.range(1, opponentCount), function(opponentIndex)
local opponentInfo = Array.map(opponents, function(_, opponentIndex)
local score, status = MatchGroupInputUtil.computeOpponentScore({
walkover = map.walkover,
winner = map.winner,
Expand Down Expand Up @@ -201,23 +201,37 @@ function MapFunctions.getExtraData(map, opponentCount)
end

---@param map table
---@param opponentCount integer
---@param opponents table[]
---@return table
function MapFunctions.getParticipants(map, opponentCount)
local participants = {}
function MapFunctions.getParticipants(map, opponents)
local allParticipants = {}
local getCharacterName = FnUtil.curry(MatchGroupInputUtil.getCharacterName, BrawlerNames)
for opponentIndex = 1, opponentCount do
for _, player, playerIndex in Table.iter.pairsByPrefix(map, 't' .. opponentIndex .. 'p') do
participants[opponentIndex .. '_' .. playerIndex] = {player = player}
end

for _, brawler, pickIndex in Table.iter.pairsByPrefix(map, 't' .. opponentIndex .. 'c') do
participants[opponentIndex .. '_' .. pickIndex] = participants[opponentIndex .. '_' .. pickIndex] or {}
participants[opponentIndex .. '_' .. pickIndex].brawler = getCharacterName(brawler)
end
end
Array.forEach(opponents, function(opponent, opponentIndex)
local players = Array.mapIndexes(function(playerIndex)
return opponent.match2players[playerIndex] or Logic.nilIfEmpty(map['t' .. opponentIndex .. 'c' .. playerIndex])
hjpalpha marked this conversation as resolved.
Show resolved Hide resolved
end)
local participants, unattachedParticipants = MatchGroupInputUtil.parseParticipants(
opponent.match2players,
players,
function(playerIndex)
local player = map['t' .. opponentIndex .. 'p' .. playerIndex]
return player and {name = player} or nil
end,
function(playerIndex, playerIdData)
local brawler = map['t' .. opponentIndex .. 'c' .. playerIndex]
return {
player = playerIdData.name,
brawler = getCharacterName(brawler),
}
end
)
Array.forEach(unattachedParticipants, function()
table.insert(participants, table.remove(unattachedParticipants, 1))
end)
Table.mergeInto(allParticipants, Table.map(participants, MatchGroupInputUtil.prefixPartcipants(opponentIndex)))
end)

return participants
return allParticipants
end

return CustomMatchGroupInput
46 changes: 30 additions & 16 deletions components/match2/wikis/dota2/match_group_input_custom.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function CustomMatchGroupInput.processMatchWithoutStandalone(MatchParser, match)
local opponents = Array.mapIndexes(function(opponentIndex)
return MatchGroupInputUtil.readOpponent(match, opponentIndex, OPPONENT_CONFIG)
end)
local games = MatchFunctions.extractMaps(MatchParser, match, #opponents)
local games = MatchFunctions.extractMaps(MatchParser, match, opponents)
match.bestof = MatchGroupInputUtil.getBestOf(match.bestof, games)

local autoScoreFunction = MatchGroupInputUtil.canUseAutoScore(match, games)
Expand Down Expand Up @@ -120,9 +120,9 @@ end

---@param MatchParser Dota2MatchParserInterface
---@param match table
---@param opponentCount integer
---@param opponents table[]
---@return table[]
function MatchFunctions.extractMaps(MatchParser, match, opponentCount)
function MatchFunctions.extractMaps(MatchParser, match, opponents)
local maps = {}
for key, mapInput, mapIndex in Table.iter.pairsByPrefix(match, 'map', {requireIndex = true}) do
local map = MatchParser.getMap(mapInput)
Expand All @@ -136,11 +136,11 @@ function MatchFunctions.extractMaps(MatchParser, match, opponentCount)
map.length = MatchParser.getLength(map)
map.vod = map.vod or String.nilIfEmpty(match['vodgame' .. mapIndex])
map.publisherid = map.matchid or String.nilIfEmpty(match['matchid' .. mapIndex])
map.participants = MapFunctions.getParticipants(MatchParser, map, opponentCount)
map.extradata = MapFunctions.getExtraData(MatchParser, map, opponentCount)
map.participants = MapFunctions.getParticipants(MatchParser, map, opponents)
map.extradata = MapFunctions.getExtraData(MatchParser, map, #opponents)

map.finished = MatchGroupInputUtil.mapIsFinished(map)
local opponentInfo = Array.map(Array.range(1, opponentCount), function(opponentIndex)
local opponentInfo = Array.map(opponents, function(_, opponentIndex)
local score, status = MatchGroupInputUtil.computeOpponentScore({
walkover = map.walkover,
winner = map.winner,
Expand Down Expand Up @@ -261,20 +261,34 @@ end
-- Parse participant information
---@param MatchParser Dota2MatchParserInterface
---@param map table
---@param opponentCount integer
---@param opponents table[]
---@return table
function MapFunctions.getParticipants(MatchParser, map, opponentCount)
local participants = {}
function MapFunctions.getParticipants(MatchParser, map, opponents)
local allParticipants = {}
local getCharacterName = FnUtil.curry(MatchGroupInputUtil.getCharacterName, HeroNames)

for opponentIndex = 1, opponentCount do
for playerIndex, participant in ipairs(MatchParser.getParticipants(map, opponentIndex) or {}) do
participant.character = getCharacterName(participant.character)
participants[opponentIndex .. '_' .. playerIndex] = participant
end
end
Array.forEach(opponents, function(opponent, opponentIndex)
local participantList = MatchParser.getParticipants(map, opponentIndex) or {}
local participants, unattachedParticipants = MatchGroupInputUtil.parseParticipants(
opponent.match2players,
participantList,
function (playerIndex)
local participant = participantList[playerIndex]
return participant and {name = participant.player} or nil
end,
function(playerIndex)
local participant = participantList[playerIndex]
participant.character = getCharacterName(participant.character)
return participant
end,
OPPONENT_CONFIG)
Array.forEach(unattachedParticipants, function(participant)
table.insert(participants, participant)
end)
Table.mergeInto(allParticipants, Table.map(participants, MatchGroupInputUtil.prefixPartcipants(opponentIndex)))
end)

return participants
return allParticipants
end

---@param winnerInput string|integer|nil
Expand Down
Loading
Loading