QR2: Add MKW course and engine class to groups API

This commit is contained in:
mkwcat 2024-09-11 21:22:30 -04:00
parent 3d1cdc7ab5
commit 4e94cdde4a
No known key found for this signature in database
GPG Key ID: 7A505679CE9E7AA9
3 changed files with 285 additions and 190 deletions

View File

@ -77,6 +77,14 @@ func (g *GameSpySession) handleWWFCReport(command common.GameSpyCommand) {
profileId := g.User.ProfileId
logging.Warn(g.ModuleName, "Kicking", aurora.BrightCyan(strconv.FormatUint(uint64(profileId), 10)), fmt.Sprintf("for dropping too many frames (%d)", framesDropped))
kickPlayer(profileId, "too_many_frames_dropped")
case "mkw_select_course", "mkw_select_cc":
if g.GameName != "mariokartwii" {
logging.Warn(g.ModuleName, "Ignoring mkw_select_* from wrong game")
continue
}
qr2.ProcessMKWSelectRecord(g.User.ProfileId, key, value)
}
}
}

View File

@ -6,7 +6,6 @@ import (
"encoding/gob"
"fmt"
"os"
"sort"
"strconv"
"strings"
"time"
@ -26,6 +25,10 @@ type Group struct {
LastJoinIndex int
server *Session
players map[*Session]bool
MKWRaceNumber int
MKWCourseID int
MKWEngineClassID int
}
var groups = map[string]*Group{}
@ -460,204 +463,64 @@ func (g *Group) updateMatchType() {
g.MatchType = g.server.Data["dwc_mtype"]
}
type MiiInfo struct {
MiiData string `json:"data"`
MiiName string `json:"name"`
}
type PlayerInfo struct {
Count string `json:"count"`
ProfileID string `json:"pid"`
InGameName string `json:"name"`
ConnMap string `json:"conn_map"`
ConnFail string `json:"conn_fail"`
Suspend string `json:"suspend"`
// Mario Kart Wii-specific fields
FriendCode string `json:"fc,omitempty"`
VersusELO string `json:"ev,omitempty"`
BattleELO string `json:"eb,omitempty"`
Mii []MiiInfo `json:"mii,omitempty"`
}
type GroupInfo struct {
GroupName string `json:"id"`
GameName string `json:"game"`
CreateTime time.Time `json:"created"`
MatchType string `json:"type"`
Suspend bool `json:"suspend"`
ServerIndex string `json:"host,omitempty"`
MKWRegion string `json:"rk,omitempty"`
Players map[string]PlayerInfo `json:"players"`
PlayersRaw map[string]map[string]string `json:"-"`
SortedJoinIndex []string `json:"-"`
}
func getGroupsRaw(gameNames []string, groupNames []string) []GroupInfo {
var groupsCopy []GroupInfo
func ProcessMKWSelectRecord(profileId uint32, key string, value string) {
moduleName := "QR2:MKWSelectRecord:" + strconv.FormatUint(uint64(profileId), 10)
mutex.Lock()
defer mutex.Unlock()
for _, group := range groups {
if len(gameNames) > 0 && !common.StringInSlice(group.GameName, gameNames) {
continue
}
if len(groupNames) > 0 && !common.StringInSlice(group.GroupName, groupNames) {
continue
}
groupInfo := GroupInfo{
GroupName: group.GroupName,
GameName: group.GameName,
CreateTime: group.CreateTime,
MatchType: "",
Suspend: true,
ServerIndex: "",
MKWRegion: "",
Players: map[string]PlayerInfo{},
PlayersRaw: map[string]map[string]string{},
SortedJoinIndex: []string{},
}
if group.MatchType == "0" || group.MatchType == "1" {
groupInfo.MatchType = "anybody"
} else if group.MatchType == "2" || group.MatchType == "3" {
groupInfo.MatchType = "private"
} else {
groupInfo.MatchType = "unknown"
}
if group.server != nil {
groupInfo.ServerIndex = group.server.Data["+joinindex"]
}
if groupInfo.GameName == "mariokartwii" {
groupInfo.MKWRegion = group.MKWRegion
}
for session := range group.players {
mapData := map[string]string{}
for k, v := range session.Data {
mapData[k] = v
}
if login := session.login; login != nil {
mapData["+ingamesn"] = login.InGameName
} else {
mapData["+ingamesn"] = ""
}
groupInfo.PlayersRaw[mapData["+joinindex"]] = mapData
if mapData["dwc_hoststate"] == "2" && mapData["dwc_suspend"] == "0" {
groupInfo.Suspend = false
}
// Add the join index to the sorted list
myJoinIndex, _ := strconv.Atoi(mapData["+joinindex"])
added := false
for i, joinIndex := range groupInfo.SortedJoinIndex {
if joinIndex == mapData["+joinindex"] {
added = true
break
}
intJoinIndex, _ := strconv.Atoi(joinIndex)
if intJoinIndex > myJoinIndex {
groupInfo.SortedJoinIndex = append(groupInfo.SortedJoinIndex, "")
copy(groupInfo.SortedJoinIndex[i+1:], groupInfo.SortedJoinIndex[i:])
groupInfo.SortedJoinIndex[i] = mapData["+joinindex"]
added = true
break
}
}
if !added {
groupInfo.SortedJoinIndex = append(groupInfo.SortedJoinIndex, mapData["+joinindex"])
}
}
groupsCopy = append(groupsCopy, groupInfo)
login := logins[profileId]
if login == nil {
mutex.Unlock()
logging.Warn(moduleName, "Received SELECT record from non-existent profile ID", aurora.Cyan(profileId))
return
}
return groupsCopy
}
session := login.session
if session == nil {
mutex.Unlock()
logging.Warn(moduleName, "Received SELECT record from profile ID", aurora.Cyan(profileId), "but no session exists")
return
}
mutex.Unlock()
// GetGroups returns a copy of all online rooms
func GetGroups(gameNames []string, groupNames []string, sorted bool) []GroupInfo {
groupsCopy := getGroupsRaw(gameNames, groupNames)
group := session.groupPointer
if group == nil {
return
}
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
switch key {
case "mkw_select_course":
courseId, err := strconv.ParseUint(value, 10, 32)
if err != nil {
logging.Error(moduleName, "Error decoding mkw_select_course:", err.Error())
return
}
logging.Info(moduleName, "Selected course", aurora.BrightCyan(strconv.FormatUint(courseId, 10)))
mutex.Lock()
defer mutex.Unlock()
group.MKWRaceNumber++
group.MKWCourseID = int(courseId)
group.MKWEngineClassID = -1
return
case "mkw_select_cc":
ccId, err := strconv.ParseUint(value, 10, 32)
if err != nil {
logging.Error(moduleName, "Error decoding mkw_select_cc:", err.Error())
return
}
logging.Info(moduleName, "Selected CC", aurora.BrightCyan(strconv.FormatUint(ccId, 10)))
mutex.Lock()
defer mutex.Unlock()
group.MKWEngineClassID = int(ccId)
return
}
if sorted {
sort.Slice(groupsCopy, func(i, j int) bool {
if groupsCopy[i].CreateTime.Equal(groupsCopy[j].CreateTime) {
return groupsCopy[i].GroupName < groupsCopy[j].GroupName
}
return groupsCopy[i].CreateTime.Before(groupsCopy[j].CreateTime)
})
}
return groupsCopy
}
// saveGroups saves the current groups state to disk.

224
qr2/group_info.go Normal file
View File

@ -0,0 +1,224 @@
package qr2
import (
"sort"
"strconv"
"time"
"wwfc/common"
)
type MiiInfo struct {
MiiData string `json:"data"`
MiiName string `json:"name"`
}
type PlayerInfo struct {
Count string `json:"count"`
ProfileID string `json:"pid"`
InGameName string `json:"name"`
ConnMap string `json:"conn_map"`
ConnFail string `json:"conn_fail"`
Suspend string `json:"suspend"`
// Mario Kart Wii-specific fields
FriendCode string `json:"fc,omitempty"`
VersusELO string `json:"ev,omitempty"`
BattleELO string `json:"eb,omitempty"`
Mii []MiiInfo `json:"mii,omitempty"`
}
type GroupInfo struct {
GroupName string `json:"id"`
GameName string `json:"game"`
CreateTime time.Time `json:"created"`
MatchType string `json:"type"`
Suspend bool `json:"suspend"`
ServerIndex string `json:"host,omitempty"`
MKWRegion string `json:"rk,omitempty"`
Players map[string]PlayerInfo `json:"players"`
RaceInfo *RaceInfo `json:"race,omitempty"`
PlayersRaw map[string]map[string]string `json:"-"`
SortedJoinIndex []string `json:"-"`
}
type RaceInfo struct {
RaceNumber int `json:"num"`
CourseID int `json:"course"`
EngineClassID int `json:"cc"`
}
func getGroupsRaw(gameNames []string, groupNames []string) []GroupInfo {
var groupsCopy []GroupInfo
mutex.Lock()
defer mutex.Unlock()
for _, group := range groups {
if len(gameNames) > 0 && !common.StringInSlice(group.GameName, gameNames) {
continue
}
if len(groupNames) > 0 && !common.StringInSlice(group.GroupName, groupNames) {
continue
}
groupInfo := GroupInfo{
GroupName: group.GroupName,
GameName: group.GameName,
CreateTime: group.CreateTime,
MatchType: "",
Suspend: true,
ServerIndex: "",
MKWRegion: "",
Players: map[string]PlayerInfo{},
PlayersRaw: map[string]map[string]string{},
SortedJoinIndex: []string{},
}
if group.MatchType == "0" || group.MatchType == "1" {
groupInfo.MatchType = "anybody"
} else if group.MatchType == "2" || group.MatchType == "3" {
groupInfo.MatchType = "private"
} else {
groupInfo.MatchType = "unknown"
}
if group.server != nil {
groupInfo.ServerIndex = group.server.Data["+joinindex"]
}
if groupInfo.GameName == "mariokartwii" {
groupInfo.MKWRegion = group.MKWRegion
if group.MKWRaceNumber != 0 {
groupInfo.RaceInfo = &RaceInfo{
RaceNumber: group.MKWRaceNumber,
CourseID: group.MKWCourseID,
EngineClassID: group.MKWEngineClassID,
}
}
}
for session := range group.players {
mapData := map[string]string{}
for k, v := range session.Data {
mapData[k] = v
}
if login := session.login; login != nil {
mapData["+ingamesn"] = login.InGameName
} else {
mapData["+ingamesn"] = ""
}
groupInfo.PlayersRaw[mapData["+joinindex"]] = mapData
if mapData["dwc_hoststate"] == "2" && mapData["dwc_suspend"] == "0" {
groupInfo.Suspend = false
}
// Add the join index to the sorted list
myJoinIndex, _ := strconv.Atoi(mapData["+joinindex"])
added := false
for i, joinIndex := range groupInfo.SortedJoinIndex {
if joinIndex == mapData["+joinindex"] {
added = true
break
}
intJoinIndex, _ := strconv.Atoi(joinIndex)
if intJoinIndex > myJoinIndex {
groupInfo.SortedJoinIndex = append(groupInfo.SortedJoinIndex, "")
copy(groupInfo.SortedJoinIndex[i+1:], groupInfo.SortedJoinIndex[i:])
groupInfo.SortedJoinIndex[i] = mapData["+joinindex"]
added = true
break
}
}
if !added {
groupInfo.SortedJoinIndex = append(groupInfo.SortedJoinIndex, mapData["+joinindex"])
}
}
groupsCopy = append(groupsCopy, groupInfo)
}
return groupsCopy
}
// GetGroups returns a copy of all online rooms
func GetGroups(gameNames []string, groupNames []string, sorted bool) []GroupInfo {
groupsCopy := getGroupsRaw(gameNames, groupNames)
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
}
}
if sorted {
sort.Slice(groupsCopy, func(i, j int) bool {
if groupsCopy[i].CreateTime.Equal(groupsCopy[j].CreateTime) {
return groupsCopy[i].GroupName < groupsCopy[j].GroupName
}
return groupsCopy[i].CreateTime.Before(groupsCopy[j].CreateTime)
})
}
return groupsCopy
}