Skip to content

Commit d138569

Browse files
committed
Send multiple PIDs in QR2 kick order
1 parent 2b91d5c commit d138569

File tree

2 files changed

+92
-11
lines changed

2 files changed

+92
-11
lines changed

gpcm/kick.go

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package gpcm
22

33
import (
4+
"strings"
45
"time"
56
"wwfc/common"
67
"wwfc/qr2"
78
)
89

910
func kickPlayer(profileID uint32, reason string) {
11+
pids := []uint32{profileID}
12+
1013
if session, exists := sessions[profileID]; exists {
1114
errorMessage := WWFCMsgKickedGeneric
1215

@@ -35,19 +38,26 @@ func kickPlayer(profileID uint32, reason string) {
3538
return
3639
}
3740

38-
session.replyError(GPError{
41+
gpError := GPError{
3942
ErrorCode: ErrConnectionClosed.ErrorCode,
4043
ErrorString: "The player was kicked from the server. Reason: " + reason,
4144
Fatal: true,
4245
WWFCMessage: errorMessage,
43-
})
46+
}
47+
48+
for _, match := range findMatchingSessions(session) {
49+
pids = append(pids, match.User.ProfileId)
50+
match.replyError(gpError)
51+
}
52+
53+
session.replyError(gpError)
4454
}
4555

4656
// After 3 seconds, send kick order to all players
4757
// This is to prevent the restricted player from staying in the group if he ignores the GPCM kick
4858
go func() {
4959
time.AfterFunc(3*time.Second, func() {
50-
qr2.OrderKickFromGroups(profileID)
60+
qr2.OrderKickFromGroups(pids)
5161
})
5262
}()
5363
}
@@ -59,25 +69,74 @@ func KickPlayer(profileID uint32, reason string) {
5969
kickPlayer(profileID, reason)
6070
}
6171

72+
func findMatchingSessions(badSession *GameSpySession) []*GameSpySession {
73+
ret := []*GameSpySession{}
74+
75+
badAddrSplit := strings.Split(badSession.RemoteAddr, ":")
76+
77+
var badAddr string
78+
// If the bad address cannot be split for some reason just send a blank string
79+
if len(badAddrSplit) > 0 {
80+
badAddr = badAddrSplit[0]
81+
} else {
82+
badAddr = ""
83+
}
84+
85+
for _, session := range sessions {
86+
if badSession.DeviceId != 67349608 && badSession.DeviceId == session.DeviceId {
87+
ret = append(ret, session)
88+
continue
89+
}
90+
91+
if badAddr == "" {
92+
continue
93+
}
94+
95+
// Addresses are in the form of IP:Port
96+
addrSplit := strings.Split(session.RemoteAddr, ":")
97+
98+
if len(addrSplit) == 0 {
99+
continue
100+
}
101+
102+
addr := addrSplit[0]
103+
104+
if addr == badAddr {
105+
ret = append(ret, session)
106+
}
107+
}
108+
109+
return ret
110+
}
111+
62112
func KickPlayerCustomMessage(profileID uint32, reason string, message WWFCErrorMessage) {
63113
mutex.Lock()
64114
defer mutex.Unlock()
65115

116+
pids := []uint32{profileID}
117+
66118
if session, exists := sessions[profileID]; exists {
67-
session.replyError(GPError{
119+
gpError := GPError{
68120
ErrorCode: ErrConnectionClosed.ErrorCode,
69121
ErrorString: "The player was kicked from the server. Reason: " + reason,
70122
Fatal: true,
71123
WWFCMessage: message,
72124
Reason: reason,
73-
})
125+
}
126+
127+
for _, match := range findMatchingSessions(session) {
128+
pids = append(pids, match.User.ProfileId)
129+
match.replyError(gpError)
130+
}
131+
132+
session.replyError(gpError)
74133
}
75134

76135
// After 3 seconds, send kick order to all players
77136
// This is to prevent the restricted player from staying in the group if he ignores the GPCM kick
78137
go func() {
79138
time.AfterFunc(3*time.Second, func() {
80-
qr2.OrderKickFromGroups(profileID)
139+
qr2.OrderKickFromGroups(pids)
81140
})
82141
}()
83142
}

qr2/group.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -585,17 +585,39 @@ func loadGroups() error {
585585
}
586586

587587
// Send kick order to all QR2-authenticated session to kick a player from their group
588-
func OrderKickFromGroups(profileToKick uint32) {
589-
moduleName := "QR2:OrderKickFromGroup/" + strconv.FormatUint(uint64(profileToKick), 10)
588+
func OrderKickFromGroups(profilesToKick []uint32) {
589+
// QR2 messages are limited at 0x80 in length. With a 7-byte header and a
590+
// byte for length, that leaves 120 bytes worth of pids, thus 30 4-byte
591+
// integers. Surely more than 30 online pids won't match at once :)
592+
593+
var profilesClamped []uint32
594+
if len(profilesToKick) > 30 {
595+
profilesClamped = profilesToKick[0:30]
596+
}
597+
598+
moduleName := "QR2:OrderKickFromGroup/"
599+
600+
clampedLen := len(profilesClamped)
601+
for i, pid := range profilesClamped {
602+
moduleName += strconv.FormatUint(uint64(pid), 10)
603+
604+
if i != clampedLen {
605+
moduleName += ", "
606+
}
607+
}
590608

591609
mutex.Lock()
592610
defer mutex.Unlock()
593611

594612
var numSent int = 0
595613
for _, session := range sessions {
596-
message := createResponseHeader(ClientKickPeerOrder, session.SessionID)
597-
message = append(message, 0) // padding for 4 byte alignment
598-
message = binary.BigEndian.AppendUint32(message, profileToKick)
614+
message := createResponseHeader(ClientKickPeerOrder, session.SessionID) // 7 byte header
615+
message = append(message, byte(len(profilesClamped))) // 1 byte len
616+
617+
for _, pid := range profilesClamped {
618+
message = binary.BigEndian.AppendUint32(message, pid) // All 4 bytes, preserves alignment
619+
}
620+
599621
_, err := masterConn.WriteTo(message, &session.Addr)
600622
if err != nil {
601623
logging.Error(moduleName, "Error sending message:", err.Error())

0 commit comments

Comments
 (0)