From 2fb7d43c311517c53abcef51b249c88863036d01 Mon Sep 17 00:00:00 2001 From: mkwcat Date: Tue, 31 Oct 2023 10:30:28 -0400 Subject: [PATCH] Matchmaking: Basic filter handling --- matchmaking/filter.go | 77 +++++++++++++++++++++++++++++++++++++++++++ matchmaking/server.go | 6 ++-- 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 matchmaking/filter.go diff --git a/matchmaking/filter.go b/matchmaking/filter.go new file mode 100644 index 0000000..5444f39 --- /dev/null +++ b/matchmaking/filter.go @@ -0,0 +1,77 @@ +package matchmaking + +import ( + "github.com/logrusorgru/aurora/v3" + "regexp" + "strconv" + "wwfc/logging" +) + +// TODO: Even if we don't use it in the end, we could still implement parsing the filter string. +// DWC makes requests in the following two formats: +// Matching: dwc_mver = %d and dwc_pid != %u and maxplayers = %d and numplayers < %d and dwc_mtype = %d and dwc_hoststate = %u and dwc_suspend = %u and (%s) +// ...OR +// Self Lookup: dwc_pid = %u + +// Example: dwc_mver = 90 and dwc_pid != 43 and maxplayers = 11 and numplayers < 11 and dwc_mtype = 0 and dwc_hoststate = 0 and dwc_suspend = 0 and (rk = 'vs' and ev >= 4250 and ev <= 5750 and p = 0) + +var ( + regexSelfLookup = regexp.MustCompile(`^dwc_pid = (\d{1,10})$`) + regexMatchmaking = regexp.MustCompile(`^dwc_mver = -?(\d{1,10}) and dwc_pid != (\d{1,10}) and maxplayers = -?(\d{1,10}) and numplayers < -?(\d{1,10}) and dwc_mtype = -?(\d{1,10}) and dwc_hoststate = (\d{1,10}) and dwc_suspend = (\d{1,10}) and \((.*)\)$`) +) + +func FilterServers(servers []map[string]string, queryGame string, filter string) []map[string]string { + if match := regexSelfLookup.FindStringSubmatch(filter); match != nil { + dwc_pid := match[1] + + // Search for where the profile ID matches + for _, server := range servers { + if server["dwc_pid"] == dwc_pid { + logging.Info(ModuleName, "Self lookup from", aurora.Cyan(dwc_pid), "ok") + return []map[string]string{server} + } + } + + logging.Error(ModuleName, "Could not find server with dwc_pid", aurora.Cyan(dwc_pid)) + return []map[string]string{} + } + + if match := regexMatchmaking.FindStringSubmatch(filter); match != nil { + dwc_mver := match[1] + dwc_pid := match[2] + maxplayers := match[3] + numplayers, err := strconv.ParseInt(match[4], 10, 32) + if err != nil { + logging.Error(ModuleName, "Invalid numplayers:", aurora.Cyan(match[4]), "from", aurora.Cyan(match)) + return []map[string]string{} + } + dwc_mtype := match[5] + dwc_hoststate := match[6] + dwc_suspend := match[7] + // gameFilter := match[8] + + filtered := []map[string]string{} + + // Find servers that match the requested parameters + // TODO: Handle game specific filters (i.e. Regionals or MKW VR search) + for _, server := range servers { + if server["dwc_mver"] == dwc_mver && server["dwc_pid"] != dwc_pid && server["maxplayers"] == maxplayers && server["dwc_mtype"] == dwc_mtype && server["dwc_hoststate"] == dwc_hoststate && server["dwc_suspend"] == dwc_suspend { + server_numplayers, err := strconv.ParseInt(server["numplayers"], 10, 32) + if err != nil { + logging.Error(ModuleName, "Invalid numplayers:", aurora.Cyan(match[4])) + continue + } + + if server_numplayers < numplayers { + filtered = append(filtered, server) + } + } + } + + logging.Info(ModuleName, "Matched", aurora.BrightCyan(len(filtered)), "servers") + return filtered + } + + logging.Error(ModuleName, "Unable to match filter for", aurora.Cyan(filter)) + return []map[string]string{} +} diff --git a/matchmaking/server.go b/matchmaking/server.go index 7f02087..fe20c34 100644 --- a/matchmaking/server.go +++ b/matchmaking/server.go @@ -38,9 +38,9 @@ const ( LimitResultCountOption = 1 << 7 // 0x80 / 128 ) -func FindServers(gueryGame string, filter string) ([]map[string]string, error) { +func FindServers(queryGame string, filter string) ([]map[string]string, error) { // TODO: Handle gueryGame, filter - return qr2.GetSessionServers(), nil + return FilterServers(qr2.GetSessionServers(), queryGame, filter), nil } func popString(buffer []byte, index int) (string, int) { @@ -65,7 +65,7 @@ func handleServerListRequest(conn net.Conn, buffer []byte) { fields, index := popString(buffer, index) options, index := popUint32(buffer, index) - logging.Notice(ModuleName, "queryGame:", aurora.Cyan(queryGame).String(), "- gameName:", aurora.Cyan(gameName).String(), "- filter:", aurora.Cyan(filter).String(), "- fields:", aurora.Cyan(fields).String()) + logging.Info(ModuleName, "queryGame:", aurora.Cyan(queryGame).String(), "- gameName:", aurora.Cyan(gameName).String(), "- filter:", aurora.Cyan(filter).String(), "- fields:", aurora.Cyan(fields).String()) var output []byte for _, s := range strings.Split(strings.Split(conn.RemoteAddr().String(), ":")[0], ".") {