From cb506eb9ee3d572268688e15b486fadb4565261f Mon Sep 17 00:00:00 2001 From: Blazico Date: Sat, 28 Mar 2026 15:16:05 +0100 Subject: [PATCH] mkw_rr api tweaks --- api/mkw_rr.go | 14 +++---- qr2/group.go | 81 +++++++++++++++++++++++++++++++++++ qr2/group_info.go | 105 ++++++++++++++++++++++++---------------------- qr2/session.go | 13 ++++++ 4 files changed, 154 insertions(+), 59 deletions(-) diff --git a/api/mkw_rr.go b/api/mkw_rr.go index 17a04b5..a1db84e 100644 --- a/api/mkw_rr.go +++ b/api/mkw_rr.go @@ -9,7 +9,7 @@ import ( ) type RaceResultInfo struct { - Players map[string]qr2.PlayerInfo `json:"players"` + Players map[uint32]qr2.PlayerInfo `json:"players"` Results map[int][]qr2.RaceResult `json:"results"` } @@ -33,16 +33,14 @@ func HandleMKWRR(w http.ResponseWriter, r *http.Request) { } groupName := query["id"][0] - groups := qr2.GetGroups([]string{}, []string{groupName}, false) - if len(groups) == 0 { + players := qr2.GetRacePlayersForGroup(groupName) + results := qr2.GetRaceResultsForGroup(groupName) + if results == nil { w.WriteHeader(http.StatusNotFound) return } - - players := groups[0].Players - results := qr2.GetRaceResultsForGroup(groupName) - if results == nil { - results = map[int][]qr2.RaceResult{} + if players == nil { + players = map[uint32]qr2.PlayerInfo{} } var jsonData []byte diff --git a/qr2/group.go b/qr2/group.go index bdc2e9c..beafe7c 100644 --- a/qr2/group.go +++ b/qr2/group.go @@ -52,6 +52,7 @@ type RaceResult struct { } var raceResults = map[string]map[int][]RaceResult{} // GroupName -> RaceNumber -> []RaceResult +var racePlayers = map[string]map[uint32]PlayerInfo{} // GroupName -> ProfileID -> historical PlayerInfo // Timing storage for delta calculation using start/finish times var raceStartTimings = map[uint32]struct { @@ -655,6 +656,51 @@ func ProcessMKWRaceResult(profileId uint32, playerPid int, finishTimeMs int, cha if raceResults[group.GroupName] == nil { raceResults[group.GroupName] = map[int][]RaceResult{} } + if racePlayers[group.GroupName] == nil { + racePlayers[group.GroupName] = map[uint32]PlayerInfo{} + } + + sortedJoinIndex := make([]string, 0, len(group.players)) + rawPlayers := make(map[string]map[string]string, len(group.players)) + for playerSession := range group.players { + mapData := map[string]string{} + for k, v := range playerSession.Data { + mapData[k] = v + } + + if login := playerSession.login; login != nil { + mapData["+ingamesn"] = login.InGameName + } else { + mapData["+ingamesn"] = "" + } + + joinIndex := mapData["+joinindex"] + rawPlayers[joinIndex] = mapData + + myJoinIndex, _ := strconv.Atoi(joinIndex) + added := false + for i, existingJoinIndex := range sortedJoinIndex { + intJoinIndex, _ := strconv.Atoi(existingJoinIndex) + if intJoinIndex > myJoinIndex { + sortedJoinIndex = append(sortedJoinIndex, "") + copy(sortedJoinIndex[i+1:], sortedJoinIndex[i:]) + sortedJoinIndex[i] = joinIndex + added = true + break + } + } + if !added { + sortedJoinIndex = append(sortedJoinIndex, joinIndex) + } + } + + for joinIndex, rawPlayer := range rawPlayers { + profileID, err := strconv.ParseUint(rawPlayer["dwc_pid"], 10, 32) + if err != nil { + continue + } + racePlayers[group.GroupName][uint32(profileID)] = buildPlayerInfo(rawPlayer, sortedJoinIndex, joinIndex) + } raceResults[group.GroupName][group.MKWRaceNumber] = append(raceResults[group.GroupName][group.MKWRaceNumber], raceResultData) @@ -683,6 +729,41 @@ func GetRaceResultsForGroup(groupName string) map[int][]RaceResult { return copiedRaceResults } +func StoreRacePlayersForGroup(groupName string, players map[uint32]PlayerInfo) { + mutex.Lock() + defer mutex.Unlock() + + if racePlayers[groupName] == nil { + racePlayers[groupName] = map[uint32]PlayerInfo{} + } + + for profileID, player := range players { + racePlayers[groupName][profileID] = player + } +} + +func GetRacePlayersForGroup(groupName string) map[uint32]PlayerInfo { + mutex.Lock() + defer mutex.Unlock() + + groupPlayers, ok := racePlayers[groupName] + if !ok { + return nil + } + + copiedPlayers := make(map[uint32]PlayerInfo, len(groupPlayers)) + for profileID, player := range groupPlayers { + playerCopy := player + if player.Mii != nil { + playerCopy.Mii = make([]MiiInfo, len(player.Mii)) + copy(playerCopy.Mii, player.Mii) + } + copiedPlayers[profileID] = playerCopy + } + + return copiedPlayers +} + // saveGroups saves the current groups state to disk. // Expects the mutex to be locked. func saveGroups() error { diff --git a/qr2/group_info.go b/qr2/group_info.go index 954c97f..5968616 100644 --- a/qr2/group_info.go +++ b/qr2/group_info.go @@ -49,6 +49,59 @@ type RaceInfo struct { EngineClassID int `json:"cc"` } +func buildPlayerInfo(rawPlayer map[string]string, sortedJoinIndex []string, joinIndex string) PlayerInfo { + playerInfo := PlayerInfo{ + Count: rawPlayer["+localplayers"], + ProfileID: rawPlayer["dwc_pid"], + InGameName: rawPlayer["+ingamesn"], + } + + pid, err := strconv.ParseUint(rawPlayer["dwc_pid"], 10, 32) + if err == nil { + if fcGame := rawPlayer["+fcgameid"]; len(fcGame) == 4 { + playerInfo.FriendCode = common.CalcFriendCodeString(uint32(pid), fcGame) + } + } + + if rawPlayer["gamename"] == "mariokartwii" { + playerInfo.VersusELO = rawPlayer["ev"] + playerInfo.BattleELO = rawPlayer["eb"] + } + + for i := 0; i < 32; i++ { + miiData := rawPlayer["+mii"+strconv.Itoa(i)] + if miiData == "" { + continue + } + + playerInfo.Mii = append(playerInfo.Mii, MiiInfo{ + MiiData: miiData, + MiiName: rawPlayer["+mii_name"+strconv.Itoa(i)], + }) + } + + for _, newIndex := range sortedJoinIndex { + if newIndex == joinIndex { + continue + } + + if rawPlayer["+conn_"+newIndex] == "" { + playerInfo.ConnMap += "0" + continue + } + + playerInfo.ConnMap += rawPlayer["+conn_"+newIndex] + } + + playerInfo.ConnFail = rawPlayer["+conn_fail"] + if playerInfo.ConnFail == "" { + playerInfo.ConnFail = "0" + } + + playerInfo.Suspend = rawPlayer["dwc_suspend"] + return playerInfo +} + func getGroupsRaw(gameNames []string, groupNames []string) []GroupInfo { var groupsCopy []GroupInfo @@ -156,57 +209,7 @@ func GetGroups(gameNames []string, groupNames []string, sorted bool) []GroupInfo for i, group := range groupsCopy { for joinIndex, rawPlayer := range group.PlayersRaw { - playerInfo := PlayerInfo{ - Count: rawPlayer["+localplayers"], - ProfileID: rawPlayer["dwc_pid"], - InGameName: rawPlayer["+ingamesn"], - } - - pid, err := strconv.ParseUint(rawPlayer["dwc_pid"], 10, 32) - if err == nil { - if fcGame := rawPlayer["+fcgameid"]; len(fcGame) == 4 { - playerInfo.FriendCode = common.CalcFriendCodeString(uint32(pid), fcGame) - } - } - - if rawPlayer["gamename"] == "mariokartwii" { - playerInfo.VersusELO = rawPlayer["ev"] - playerInfo.BattleELO = rawPlayer["eb"] - } - - for i := 0; i < 32; i++ { - miiData := rawPlayer["+mii"+strconv.Itoa(i)] - if miiData == "" { - continue - } - - playerInfo.Mii = append(playerInfo.Mii, MiiInfo{ - MiiData: miiData, - MiiName: rawPlayer["+mii_name"+strconv.Itoa(i)], - }) - } - - for _, newIndex := range group.SortedJoinIndex { - if newIndex == joinIndex { - continue - } - - if rawPlayer["+conn_"+newIndex] == "" { - playerInfo.ConnMap += "0" - continue - } - - playerInfo.ConnMap += rawPlayer["+conn_"+newIndex] - } - - playerInfo.ConnFail = rawPlayer["+conn_fail"] - if playerInfo.ConnFail == "" { - playerInfo.ConnFail = "0" - } - - playerInfo.Suspend = rawPlayer["dwc_suspend"] - - groupsCopy[i].Players[joinIndex] = playerInfo + groupsCopy[i].Players[joinIndex] = buildPlayerInfo(rawPlayer, group.SortedJoinIndex, joinIndex) } } diff --git a/qr2/session.go b/qr2/session.go index 037618e..a02a4f9 100644 --- a/qr2/session.go +++ b/qr2/session.go @@ -82,6 +82,19 @@ func (session *Session) removeFromGroup() { if len(session.groupPointer.players) == 0 { logging.Notice("QR2", "Deleting group", aurora.Cyan(session.groupPointer.GroupName)) + + groupName := session.groupPointer.GroupName + _, ok := raceResults[groupName] + if ok { + go func() { + time.AfterFunc(3*time.Hour, func() { + delete(raceResults, groupName) + delete(racePlayers, groupName) + logging.Notice("QR2", "Deleted race results and players for group", aurora.Cyan(groupName)) + }) + }() + } + delete(groups, session.groupPointer.GroupName) } else if session.groupPointer.server == session { logging.Notice("QR2", "Server down in group", aurora.Cyan(session.groupPointer.GroupName))