QR2: Track groups / rooms

This commit is contained in:
mkwcat
2023-12-11 12:35:30 -05:00
parent acab015f31
commit 22045c4f49
5 changed files with 165 additions and 3 deletions

View File

@@ -8,6 +8,7 @@ import (
"strings"
"wwfc/common"
"wwfc/logging"
"wwfc/qr2"
)
func (g *GameSpySession) isFriendAdded(profileId uint32) bool {
@@ -182,6 +183,12 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
return
}
if len(msg) < msgDataIndex+1 {
logging.Error(g.ModuleName, "Invalid message length; message:", msg)
g.replyError(ErrMessage)
return
}
cmd := msg[msgDataIndex]
msgDataIndex++
@@ -232,6 +239,18 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
return
}
if cmd == common.MatchReservation {
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) == int32(msgMatchData.Reservation.PublicIP) {
g.QR2IP = uint64(msgMatchData.Reservation.PublicIP) | (uint64(msgMatchData.Reservation.PublicPort) << 32)
}
} else if cmd == common.MatchResvOK {
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) == int32(msgMatchData.ResvOK.PublicIP) {
g.QR2IP = uint64(msgMatchData.ResvOK.PublicIP) | (uint64(msgMatchData.ResvOK.PublicPort) << 32)
}
}
// TODO: Replace public IP with QR2 search ID
mutex.Lock()
defer mutex.Unlock()
@@ -248,6 +267,29 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
return
}
if cmd == common.MatchReservation {
g.ReservationPID = uint32(toProfileId)
} else if cmd == common.MatchResvOK || cmd == common.MatchResvDeny || cmd == common.MatchResvWait {
if toSession.ReservationPID != g.User.ProfileId {
logging.Error(g.ModuleName, "Destination", aurora.Cyan(toProfileId), "has no reservation with the sender")
g.replyError(ErrMessage)
return
}
if cmd == common.MatchResvOK {
if g.QR2IP == 0 || toSession.QR2IP == 0 {
logging.Error(g.ModuleName, "Missing QR2 IP")
g.replyError(ErrMessage)
return
}
if !qr2.ProcessGPResvOK(*msgMatchData.ResvOK, g.QR2IP, g.User.ProfileId, toSession.QR2IP, uint32(toProfileId)) {
g.replyError(ErrMessage)
return
}
}
}
sendMessageToSession("1", g.User.ProfileId, toSession, msg)
}

View File

@@ -28,6 +28,9 @@ type GameSpySession struct {
LocString string
FriendList []uint32
AuthFriendList []uint32
QR2IP uint64
ReservationPID uint32
}
var (

81
qr2/group.go Normal file
View File

@@ -0,0 +1,81 @@
package qr2
import (
"fmt"
"github.com/logrusorgru/aurora/v3"
"strconv"
"wwfc/common"
"wwfc/logging"
)
type Group struct {
GroupID uint32
ServerAID uint8
Players map[uint8]*Session
}
var groups = map[*Group]bool{}
func processResvOK(moduleName string, cmd common.MatchCommandDataResvOK, sender, destination *Session) bool {
group := sender.GroupPointer
if group == nil {
logging.Notice(moduleName, "Creating new group", aurora.Cyan(cmd.GroupID), "/", aurora.Cyan(cmd.ProfileID))
group = &Group{
GroupID: cmd.GroupID,
ServerAID: uint8(cmd.SenderAID),
Players: map[uint8]*Session{uint8(cmd.SenderAID): sender},
}
sender.GroupPointer = group
groups[group] = true
}
if uint8(cmd.SenderAID) != sender.GroupAID {
logging.Error(moduleName, "ResvOK: Invalid sender AID")
return false
}
// TODO: Check if the sender is the actual server (host) once host migration works
// Keep group ID updated
sender.GroupPointer.GroupID = cmd.GroupID
logging.Info(moduleName, "New AID", aurora.Cyan(uint8(cmd.ReceiverNewAID)), "in group", aurora.Cyan(group.GroupID))
group.Players[uint8(cmd.ReceiverNewAID)] = destination
destination.GroupPointer = group
destination.GroupAID = uint8(cmd.ReceiverNewAID)
return true
}
func ProcessGPResvOK(cmd common.MatchCommandDataResvOK, senderIP uint64, senderPid uint32, destIP uint64, destPid uint32) bool {
senderPidStr := strconv.FormatUint(uint64(senderPid), 10)
destPidStr := strconv.FormatUint(uint64(destPid), 10)
moduleName := "QR2:GPMsg:" + senderPidStr + "->" + destPidStr
mutex.Lock()
defer mutex.Unlock()
from := sessionByPublicIP[senderIP]
if from == nil {
logging.Error(moduleName, "Sender IP does not exist:", aurora.Cyan(fmt.Sprintf("%012x", senderIP)))
return false
}
to := sessionByPublicIP[destIP]
if to == nil {
logging.Error(moduleName, "Destination IP does not exist:", aurora.Cyan(fmt.Sprintf("%012x", destIP)))
return false
}
// Validate dwc_pid values
if !from.setProfileID(moduleName, senderPidStr) {
return false
}
if !to.setProfileID(moduleName, destPidStr) {
return false
}
return processResvOK(moduleName, cmd, from, to)
}

View File

@@ -226,7 +226,26 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
cookie := binary.BigEndian.Uint32(message[0x2:0x6])
logging.Notice(moduleName, "Send NN cookie", aurora.Cyan(cookie), "to", aurora.BrightCyan(destPid))
} else {
common.LogMatchCommand(moduleName, destPid, message[8], matchData)
cmd := message[8]
common.LogMatchCommand(moduleName, destPid, cmd, matchData)
if cmd == common.MatchReservation {
sender.ReservationID = receiver.SearchID
} else if cmd == common.MatchResvOK || cmd == common.MatchResvDeny || cmd == common.MatchResvWait {
if receiver.ReservationID != sender.SearchID {
logging.Error(moduleName, "Destination has no reservation with the sender")
return
}
if cmd == common.MatchResvOK {
mutex.Lock()
if !processResvOK(moduleName, *matchData.ResvOK, sender, receiver) {
mutex.Unlock()
return
}
mutex.Unlock()
}
}
}
payload := createResponseHeader(ClientMessageRequest, destSessionID)

View File

@@ -28,6 +28,9 @@ type Session struct {
Endianness byte // Some fields depend on the client's endianness
Data map[string]string
PacketCount uint32
ReservationID uint64
GroupPointer *Group
GroupAID uint8
}
var (
@@ -43,10 +46,23 @@ func removeSession(sessionId uint32) {
mutex.Lock()
defer mutex.Unlock()
if sessions[sessionId] == nil {
session := sessions[sessionId]
if session == nil {
return
}
if session.GroupPointer != nil {
delete(session.GroupPointer.Players, session.GroupAID)
if len(session.GroupPointer.Players) == 0 {
logging.Notice("QR2", "Deleting group", aurora.Cyan(session.GroupPointer.GroupID))
delete(groups, session.GroupPointer)
} else if session.GroupPointer.ServerAID == session.GroupAID {
logging.Notice("QR2", "Server down in group", aurora.Cyan(session.GroupPointer.GroupID))
// TODO: Search for new host via dwc_hoststate
}
}
// Delete search ID lookup
delete(sessionBySearchID, sessions[sessionId].SearchID)
@@ -85,6 +101,7 @@ func setSessionData(sessionId uint32, payload map[string]string, addr net.Addr)
Endianness: ClientNoEndian,
Data: payload,
PacketCount: 0,
ReservationID: 0,
}
}
@@ -97,7 +114,7 @@ func setSessionData(sessionId uint32, payload map[string]string, addr net.Addr)
// Set search ID
for {
searchID := uint64(rand.Int63n(0x400 << 32))
searchID := uint64(rand.Int63n((0x400<<32)-1) + 1)
if _, exists := sessionBySearchID[searchID]; !exists {
session.SearchID = searchID
session.Data["+searchid"] = strconv.FormatUint(searchID, 10)