Skip to content

Commit

Permalink
feat(match2): properly attach participants to their playerid (#4645)
Browse files Browse the repository at this point in the history
* feat(match2): properly attach participants to their playerid

* fix variable naming

* do other refactored wikis

* tweak function

* do starcraft also

* long lines

* unused import

* rename to id

* rename variables

* from review

* idea on reworking an handling unattachedplayers

* rework again, apply to all

* var rename, spelling

* rework to be more generic

* changed the rest

* tweaks (not done)

* typo?

* maybe this?

* readd annos

* fix sc2

* fix sc2: use correct var

* ensure `match2players` is always set

---------

Co-authored-by: hjpalpha <[email protected]>
  • Loading branch information
Rathoz and hjpalpha authored Sep 12, 2024
1 parent 0b212fd commit a626f60
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 138 deletions.
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

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 = {}
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,
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])
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

0 comments on commit a626f60

Please sign in to comment.