mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-21 17:44:58 -05:00
712 lines
20 KiB
Go
712 lines
20 KiB
Go
package common
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"wwfc/logging"
|
|
|
|
"github.com/logrusorgru/aurora/v3"
|
|
)
|
|
|
|
const (
|
|
MatchReservation = 0x01
|
|
MatchResvOK = 0x02
|
|
MatchResvDeny = 0x03
|
|
MatchResvWait = 0x04
|
|
MatchResvCancel = 0x05
|
|
MatchTellAddr = 0x06
|
|
MatchNewPidAid = 0x07
|
|
MatchLinkClientsRequest = 0x08
|
|
MatchLinkClientsSuccess = 0x09
|
|
MatchCloseLink = 0x0A
|
|
MatchResvPrior = 0x0B
|
|
MatchCancel = 0x0C
|
|
MatchCancelSyn = 0x0D
|
|
MatchCancelSynAck = 0x0E
|
|
MatchCancelAck = 0x0F
|
|
MatchServerCloseClient = 0x10
|
|
MatchPollTimeout = 0x11
|
|
MatchPollToAck = 0x12
|
|
MatchServerConnBlock = 0x13
|
|
MatchFriendAccept = 0x20
|
|
MatchClientWaitPoll = 0x40
|
|
MatchKeepAliveToClient = 0x41
|
|
MatchServerDownQuery = 0x52
|
|
MatchServerDownAck = 0x53
|
|
MatchServerDownNak = 0x54
|
|
MatchServerDownKeep = 0x55
|
|
MatchSuspendMatch = 0x82
|
|
MatchClientAIDUsage = 0x83
|
|
)
|
|
|
|
type MatchCommandData struct {
|
|
Version int
|
|
Command byte
|
|
|
|
Reservation *MatchCommandDataReservation
|
|
ResvOK *MatchCommandDataResvOK
|
|
ResvDeny *MatchCommandDataResvDeny
|
|
TellAddr *MatchCommandDataTellAddr
|
|
ServerCloseClient *MatchCommandDataServerCloseClient
|
|
SuspendMatch *MatchCommandDataSuspendMatch
|
|
Other []byte
|
|
}
|
|
|
|
type MatchCommandDataReservation struct {
|
|
MatchType byte
|
|
HasPublicIP bool
|
|
PublicIP uint32
|
|
PublicPort uint16
|
|
LocalIP uint32
|
|
LocalPort uint16
|
|
Unknown uint32
|
|
IsFriend bool
|
|
LocalPlayerCount uint32
|
|
ResvCheckValue uint32
|
|
|
|
UserData []byte
|
|
}
|
|
|
|
type MatchCommandDataResvOK struct {
|
|
MaxPlayers uint32
|
|
SenderAID uint32
|
|
ProfileID uint32
|
|
PublicIP uint32
|
|
PublicPort uint16
|
|
LocalIP uint32
|
|
LocalPort uint16
|
|
Unknown uint32
|
|
LocalPlayerCount uint32
|
|
GroupID uint32
|
|
ReceiverNewAID uint32
|
|
ClientCount uint32
|
|
ResvCheckValue uint32
|
|
|
|
// Version 3 and 11
|
|
ProfileIDs []uint32
|
|
|
|
// Version 11
|
|
IsFriend bool
|
|
|
|
UserData []byte
|
|
}
|
|
|
|
type MatchCommandDataResvDeny struct {
|
|
Reason uint32
|
|
ReasonString string
|
|
|
|
UserData []byte
|
|
}
|
|
|
|
type MatchCommandDataTellAddr struct {
|
|
LocalIP uint32
|
|
LocalPort uint16
|
|
}
|
|
|
|
type MatchCommandDataServerCloseClient struct {
|
|
// TODO: Check are these really profile IDs
|
|
ProfileIDs []uint32
|
|
}
|
|
|
|
type MatchCommandDataSuspendMatch struct {
|
|
HostProfileID uint32
|
|
IsHostFlag uint32
|
|
Short bool
|
|
SuspendValue uint32
|
|
ClientAIDValue uint32
|
|
}
|
|
|
|
func GetMatchCommandString(command byte) string {
|
|
switch command {
|
|
case MatchReservation:
|
|
return "RESERVATION"
|
|
case MatchResvOK:
|
|
return "RESV_OK"
|
|
case MatchResvDeny:
|
|
return "RESV_DENY"
|
|
case MatchResvWait:
|
|
return "RESV_WAIT"
|
|
case MatchResvCancel:
|
|
return "RESV_CANCEL"
|
|
case MatchTellAddr:
|
|
return "TELL_ADDR"
|
|
case MatchNewPidAid:
|
|
return "NEW_PID_AID"
|
|
case MatchLinkClientsRequest:
|
|
return "LINK_CLS_REQ"
|
|
case MatchLinkClientsSuccess:
|
|
return "LINK_CLS_SUC"
|
|
case MatchCloseLink:
|
|
return "CLOSE_LINK"
|
|
case MatchResvPrior:
|
|
return "RESV_PRIOR"
|
|
case MatchCancel:
|
|
return "CANCEL"
|
|
case MatchCancelSyn:
|
|
return "CANCEL_SYN"
|
|
case MatchCancelSynAck:
|
|
return "CANCEL_SYN_ACK"
|
|
case MatchCancelAck:
|
|
return "CANCEL_ACK"
|
|
case MatchServerCloseClient:
|
|
return "SC_CLOSE_CL"
|
|
case MatchPollTimeout:
|
|
return "POLL_TIMEOUT"
|
|
case MatchPollToAck:
|
|
return "POLL_TO_ACK"
|
|
case MatchServerConnBlock:
|
|
return "SC_CONN_BLOCK"
|
|
case MatchFriendAccept:
|
|
return "FRIEND_ACCEPT"
|
|
case MatchClientWaitPoll:
|
|
return "CL_WAIT_POLL"
|
|
case MatchKeepAliveToClient:
|
|
return "SV_KA_TO_CL"
|
|
case MatchServerDownQuery:
|
|
return "SVDOWNQUERY"
|
|
case MatchServerDownAck:
|
|
return "SVDOWN_ACK"
|
|
case MatchServerDownNak:
|
|
return "SVDOWN_NAK"
|
|
case MatchServerDownKeep:
|
|
return "SVDOWN_KEEP"
|
|
case MatchSuspendMatch:
|
|
return "SUSPEND_MATCH"
|
|
case MatchClientAIDUsage:
|
|
return "CLIENT_AID_USAGE"
|
|
}
|
|
return "UNKNOWN"
|
|
}
|
|
|
|
func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandData, bool) {
|
|
if version != 3 && version != 11 && version != 90 {
|
|
return MatchCommandData{}, false
|
|
}
|
|
|
|
// Match commands must be 4 byte aligned
|
|
if (len(buffer) & 3) != 0 {
|
|
return MatchCommandData{}, false
|
|
}
|
|
|
|
switch command {
|
|
case MatchReservation:
|
|
if version == 3 && len(buffer) < 0x04 {
|
|
break
|
|
}
|
|
|
|
if (version == 11 && len(buffer) < 0x14) || (version == 90 && len(buffer) < 0x24) {
|
|
break
|
|
}
|
|
|
|
matchType := binary.LittleEndian.Uint32(buffer[0x00:0x04])
|
|
if matchType > 3 {
|
|
break
|
|
}
|
|
|
|
if version == 3 && len(buffer) < 0x0C {
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
Reservation: &MatchCommandDataReservation{
|
|
MatchType: byte(matchType),
|
|
HasPublicIP: false,
|
|
},
|
|
}, true
|
|
}
|
|
|
|
publicPort := binary.LittleEndian.Uint32(buffer[0x08:0x0C])
|
|
if publicPort > 0xffff {
|
|
break
|
|
}
|
|
|
|
switch version {
|
|
case 3:
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
Reservation: &MatchCommandDataReservation{
|
|
MatchType: byte(matchType),
|
|
HasPublicIP: true,
|
|
PublicIP: binary.BigEndian.Uint32(buffer[0x04:0x08]),
|
|
PublicPort: uint16(publicPort),
|
|
UserData: buffer[0x0C:],
|
|
},
|
|
}, true
|
|
|
|
case 11:
|
|
isFriendValue := binary.LittleEndian.Uint32(buffer[0x0C:0x10])
|
|
if isFriendValue > 1 {
|
|
break
|
|
}
|
|
isFriend := isFriendValue != 0
|
|
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
Reservation: &MatchCommandDataReservation{
|
|
MatchType: byte(matchType),
|
|
HasPublicIP: true,
|
|
PublicIP: binary.BigEndian.Uint32(buffer[0x04:0x08]),
|
|
PublicPort: uint16(publicPort),
|
|
IsFriend: isFriend,
|
|
LocalPlayerCount: binary.LittleEndian.Uint32(buffer[0x10:0x14]),
|
|
UserData: buffer[0x14:],
|
|
},
|
|
}, true
|
|
|
|
case 90:
|
|
localPort := binary.LittleEndian.Uint32(buffer[0x10:0x14])
|
|
if localPort > 0xffff {
|
|
break
|
|
}
|
|
|
|
isFriendValue := binary.LittleEndian.Uint32(buffer[0x18:0x1C])
|
|
if isFriendValue > 1 {
|
|
break
|
|
}
|
|
isFriend := isFriendValue != 0
|
|
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
Reservation: &MatchCommandDataReservation{
|
|
MatchType: byte(matchType),
|
|
HasPublicIP: true,
|
|
PublicIP: binary.BigEndian.Uint32(buffer[0x04:0x08]),
|
|
PublicPort: uint16(publicPort),
|
|
LocalIP: binary.BigEndian.Uint32(buffer[0x0C:0x10]),
|
|
LocalPort: uint16(localPort),
|
|
Unknown: binary.LittleEndian.Uint32(buffer[0x14:0x18]),
|
|
IsFriend: isFriend,
|
|
LocalPlayerCount: binary.LittleEndian.Uint32(buffer[0x1C:0x20]),
|
|
ResvCheckValue: binary.LittleEndian.Uint32(buffer[0x20:0x24]),
|
|
UserData: buffer[0x24:],
|
|
}}, true
|
|
}
|
|
|
|
case MatchResvOK:
|
|
if version == 3 || version == 11 {
|
|
if len(buffer) < 0xC {
|
|
break
|
|
}
|
|
|
|
clientCount := binary.LittleEndian.Uint32(buffer[0x00:0x04])
|
|
if version == 3 && (clientCount > 29 || len(buffer) < int(0x0C+clientCount*0x4)) {
|
|
break
|
|
}
|
|
if version == 11 && (clientCount > 24 || len(buffer) < int(0x20+clientCount*0x4)) {
|
|
break
|
|
}
|
|
|
|
var profileIDs []uint32
|
|
for i := uint32(0); i < clientCount; i++ {
|
|
profileIDs = append(profileIDs, binary.LittleEndian.Uint32(buffer[0x04+i*4:0x04+i*4+4]))
|
|
}
|
|
|
|
index := 0x04 + clientCount*4
|
|
|
|
publicPort := binary.LittleEndian.Uint32(buffer[index+0x04 : index+0x08])
|
|
if publicPort > 0xffff {
|
|
break
|
|
}
|
|
|
|
if version == 3 {
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
ResvOK: &MatchCommandDataResvOK{
|
|
PublicIP: binary.BigEndian.Uint32(buffer[index : index+0x04]),
|
|
PublicPort: uint16(publicPort),
|
|
ClientCount: clientCount,
|
|
ProfileIDs: profileIDs,
|
|
UserData: buffer[index+0x8:],
|
|
},
|
|
}, true
|
|
} else if version == 11 {
|
|
isFriendValue := binary.LittleEndian.Uint32(buffer[index+0x08 : index+0x0C])
|
|
if isFriendValue > 1 {
|
|
break
|
|
}
|
|
isFriend := isFriendValue != 0
|
|
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
ResvOK: &MatchCommandDataResvOK{
|
|
MaxPlayers: binary.LittleEndian.Uint32(buffer[index+0x14 : index+0x18]),
|
|
SenderAID: binary.LittleEndian.Uint32(buffer[index+0x0C : index+0x10]),
|
|
PublicIP: binary.BigEndian.Uint32(buffer[index : index+0x04]),
|
|
PublicPort: uint16(publicPort),
|
|
GroupID: binary.LittleEndian.Uint32(buffer[index+0x10 : index+0x14]),
|
|
ClientCount: clientCount,
|
|
ProfileIDs: profileIDs,
|
|
IsFriend: isFriend,
|
|
UserData: buffer[index+0x18:],
|
|
}}, true
|
|
}
|
|
break
|
|
}
|
|
|
|
// Version 90
|
|
if len(buffer) < 0x34 {
|
|
break
|
|
}
|
|
|
|
publicPort := binary.LittleEndian.Uint32(buffer[0x10:0x14])
|
|
if publicPort > 0xffff {
|
|
break
|
|
}
|
|
|
|
localPort := binary.LittleEndian.Uint32(buffer[0x18:0x1C])
|
|
if localPort > 0xffff {
|
|
break
|
|
}
|
|
|
|
// TODO: Fortune Street Wii seems to have ClientCount removed and two more values added after ResvCheckValue
|
|
// It also seems to have a broken LocalPlayerCount field, despite the code handling it the same as other games?
|
|
// As is, it works fine, but it might break something in the server that checks those values
|
|
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
ResvOK: &MatchCommandDataResvOK{
|
|
MaxPlayers: binary.LittleEndian.Uint32(buffer[0x00:0x04]),
|
|
SenderAID: binary.LittleEndian.Uint32(buffer[0x04:0x08]),
|
|
ProfileID: binary.LittleEndian.Uint32(buffer[0x08:0x0C]),
|
|
PublicIP: binary.BigEndian.Uint32(buffer[0x0C:0x10]),
|
|
PublicPort: uint16(publicPort),
|
|
LocalIP: binary.BigEndian.Uint32(buffer[0x14:0x18]),
|
|
LocalPort: uint16(localPort),
|
|
Unknown: binary.LittleEndian.Uint32(buffer[0x1C:0x20]),
|
|
LocalPlayerCount: binary.LittleEndian.Uint32(buffer[0x20:0x24]),
|
|
GroupID: binary.LittleEndian.Uint32(buffer[0x24:0x28]),
|
|
ReceiverNewAID: binary.LittleEndian.Uint32(buffer[0x28:0x2C]),
|
|
ClientCount: binary.LittleEndian.Uint32(buffer[0x2C:0x30]),
|
|
ResvCheckValue: binary.LittleEndian.Uint32(buffer[0x30:0x34]),
|
|
UserData: buffer[0x34:],
|
|
},
|
|
}, true
|
|
|
|
case MatchResvDeny:
|
|
if len(buffer) != 0x04 {
|
|
break
|
|
}
|
|
|
|
reason := binary.LittleEndian.Uint32(buffer[0x00:0x04])
|
|
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
ResvDeny: &MatchCommandDataResvDeny{
|
|
Reason: reason,
|
|
ReasonString: map[uint32]string{
|
|
0x10: "Game server is fully occupied.",
|
|
0x11: "This Domain is already closed.",
|
|
0x12: "The condition was not satisfied.",
|
|
0x13: "This Domain is already locked.",
|
|
0x14: "It tried to go to the client for the reservation.",
|
|
0x15: "It is a reservation to the friend who doesn't exist in the list.",
|
|
0x16: "It was rejected by the attempt callback.",
|
|
0x17: "The reservation came from a different other host.",
|
|
0x18: "Illegal mesh reservation.",
|
|
}[reason],
|
|
UserData: buffer[0x4:],
|
|
},
|
|
}, true
|
|
|
|
case MatchResvWait:
|
|
if len(buffer) != 0x00 {
|
|
break
|
|
}
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
}, true
|
|
|
|
case MatchResvCancel:
|
|
if len(buffer) != 0x00 {
|
|
break
|
|
}
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
}, true
|
|
|
|
case MatchTellAddr:
|
|
if len(buffer) != 0x08 {
|
|
break
|
|
}
|
|
|
|
localPort := binary.LittleEndian.Uint32(buffer[0x04:0x08])
|
|
if localPort > 0xffff {
|
|
break
|
|
}
|
|
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
TellAddr: &MatchCommandDataTellAddr{
|
|
LocalIP: binary.BigEndian.Uint32(buffer[0x00:0x04]),
|
|
LocalPort: uint16(localPort),
|
|
},
|
|
}, true
|
|
|
|
case MatchServerCloseClient:
|
|
// Max match command buffer size for QR2/GT2
|
|
// This gives a maximum of 32 clients in the room
|
|
if len(buffer) > 0x80 {
|
|
break
|
|
}
|
|
|
|
if (len(buffer) & 3) != 0 {
|
|
break
|
|
}
|
|
|
|
pidCount := len(buffer) >> 2
|
|
var pids []uint32
|
|
for i := 0; i < pidCount; i++ {
|
|
pids = append(pids, binary.LittleEndian.Uint32(buffer[i*4:i*4+4]))
|
|
}
|
|
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
ServerCloseClient: &MatchCommandDataServerCloseClient{
|
|
ProfileIDs: pids,
|
|
},
|
|
}, true
|
|
|
|
case MatchPollTimeout:
|
|
if len(buffer) != 0x00 {
|
|
break
|
|
}
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
}, true
|
|
|
|
case MatchPollToAck:
|
|
if len(buffer) != 0x04 {
|
|
break
|
|
}
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
Other: buffer,
|
|
}, true
|
|
|
|
case MatchSuspendMatch:
|
|
if len(buffer) == 0x08 {
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
SuspendMatch: &MatchCommandDataSuspendMatch{
|
|
HostProfileID: binary.LittleEndian.Uint32(buffer[0x00:0x04]),
|
|
IsHostFlag: binary.LittleEndian.Uint32(buffer[0x04:0x08]),
|
|
Short: true,
|
|
},
|
|
}, true
|
|
} else if len(buffer) == 0x10 {
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
SuspendMatch: &MatchCommandDataSuspendMatch{
|
|
HostProfileID: binary.LittleEndian.Uint32(buffer[0x00:0x04]),
|
|
IsHostFlag: binary.LittleEndian.Uint32(buffer[0x04:0x08]),
|
|
Short: false,
|
|
SuspendValue: binary.LittleEndian.Uint32(buffer[0x08:0x0C]),
|
|
ClientAIDValue: binary.LittleEndian.Uint32(buffer[0x0C:0x10]),
|
|
},
|
|
}, true
|
|
}
|
|
|
|
default:
|
|
return MatchCommandData{
|
|
Version: version,
|
|
Command: command,
|
|
Other: buffer,
|
|
}, true
|
|
}
|
|
|
|
return MatchCommandData{}, false
|
|
}
|
|
|
|
func EncodeMatchCommand(command byte, data MatchCommandData) ([]byte, bool) {
|
|
version := data.Version
|
|
|
|
if version != 3 && version != 11 && version != 90 {
|
|
return []byte{}, false
|
|
}
|
|
|
|
switch command {
|
|
case MatchReservation:
|
|
message := binary.LittleEndian.AppendUint32([]byte{}, uint32(data.Reservation.MatchType))
|
|
|
|
if version == 3 && !data.Reservation.HasPublicIP {
|
|
return message, true
|
|
}
|
|
|
|
message = binary.BigEndian.AppendUint32(message, data.Reservation.PublicIP)
|
|
message = binary.LittleEndian.AppendUint32(message, uint32(data.Reservation.PublicPort))
|
|
|
|
if version == 11 {
|
|
isFriendInt := uint32(0)
|
|
if data.Reservation.IsFriend {
|
|
isFriendInt = 1
|
|
}
|
|
message = binary.LittleEndian.AppendUint32(message, isFriendInt)
|
|
|
|
message = binary.LittleEndian.AppendUint32(message, data.Reservation.LocalPlayerCount)
|
|
} else if version == 90 {
|
|
message = binary.BigEndian.AppendUint32(message, data.Reservation.LocalIP)
|
|
message = binary.LittleEndian.AppendUint32(message, uint32(data.Reservation.LocalPort))
|
|
message = binary.LittleEndian.AppendUint32(message, data.Reservation.Unknown)
|
|
|
|
isFriendInt := uint32(0)
|
|
if data.Reservation.IsFriend {
|
|
isFriendInt = 1
|
|
}
|
|
message = binary.LittleEndian.AppendUint32(message, isFriendInt)
|
|
|
|
message = binary.LittleEndian.AppendUint32(message, data.Reservation.LocalPlayerCount)
|
|
message = binary.LittleEndian.AppendUint32(message, data.Reservation.ResvCheckValue)
|
|
}
|
|
|
|
message = append(message, data.Reservation.UserData...)
|
|
if (len(message) & 3) != 0 {
|
|
return []byte{}, false
|
|
}
|
|
|
|
return message, true
|
|
|
|
case MatchResvOK:
|
|
if version == 3 || version == 11 {
|
|
if int(data.ResvOK.ClientCount) != len(data.ResvOK.ProfileIDs) {
|
|
return []byte{}, false
|
|
}
|
|
message := binary.LittleEndian.AppendUint32([]byte{}, data.ResvOK.ClientCount)
|
|
for _, pid := range data.ResvOK.ProfileIDs {
|
|
message = binary.LittleEndian.AppendUint32(message, pid)
|
|
}
|
|
message = binary.BigEndian.AppendUint32(message, data.ResvOK.PublicIP)
|
|
message = binary.LittleEndian.AppendUint32(message, uint32(data.ResvOK.PublicPort))
|
|
|
|
if version == 3 {
|
|
message = append(message, data.ResvOK.UserData...)
|
|
if (len(message) & 3) != 0 {
|
|
return []byte{}, false
|
|
}
|
|
|
|
return message, true
|
|
}
|
|
|
|
// Version 11
|
|
isFriendInt := uint32(0)
|
|
if data.ResvOK.IsFriend {
|
|
isFriendInt = 1
|
|
}
|
|
message = binary.LittleEndian.AppendUint32(message, isFriendInt)
|
|
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.SenderAID)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.GroupID)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.MaxPlayers)
|
|
|
|
message = append(message, data.ResvOK.UserData...)
|
|
if (len(message) & 3) != 0 {
|
|
return []byte{}, false
|
|
}
|
|
|
|
return message, true
|
|
}
|
|
|
|
// Version 90
|
|
message := binary.LittleEndian.AppendUint32([]byte{}, data.ResvOK.MaxPlayers)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.SenderAID)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.ProfileID)
|
|
message = binary.BigEndian.AppendUint32(message, data.ResvOK.PublicIP)
|
|
message = binary.LittleEndian.AppendUint32(message, uint32(data.ResvOK.PublicPort))
|
|
message = binary.BigEndian.AppendUint32(message, data.ResvOK.LocalIP)
|
|
message = binary.LittleEndian.AppendUint32(message, uint32(data.ResvOK.LocalPort))
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.Unknown)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.LocalPlayerCount)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.GroupID)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.ReceiverNewAID)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.ClientCount)
|
|
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.ResvCheckValue)
|
|
|
|
message = append(message, data.ResvOK.UserData...)
|
|
if (len(message) & 3) != 0 {
|
|
return []byte{}, false
|
|
}
|
|
|
|
return message, true
|
|
|
|
case MatchResvDeny:
|
|
message := binary.LittleEndian.AppendUint32([]byte{}, data.ResvDeny.Reason)
|
|
|
|
message = append(message, data.ResvDeny.UserData...)
|
|
if (len(message) & 3) != 0 {
|
|
return []byte{}, false
|
|
}
|
|
|
|
return message, true
|
|
|
|
case MatchResvWait:
|
|
return []byte{}, true
|
|
|
|
case MatchResvCancel:
|
|
return []byte{}, true
|
|
|
|
case MatchTellAddr:
|
|
message := binary.BigEndian.AppendUint32([]byte{}, data.TellAddr.LocalIP)
|
|
message = binary.LittleEndian.AppendUint32(message, uint32(data.TellAddr.LocalPort))
|
|
return message, true
|
|
|
|
case MatchServerCloseClient:
|
|
var message []byte
|
|
for i := 0; i < len(data.ServerCloseClient.ProfileIDs); i++ {
|
|
message = binary.LittleEndian.AppendUint32(message, data.ServerCloseClient.ProfileIDs[i])
|
|
}
|
|
return message, true
|
|
|
|
case MatchPollTimeout:
|
|
return []byte{}, true
|
|
|
|
case MatchPollToAck:
|
|
return data.Other, true
|
|
|
|
case MatchSuspendMatch:
|
|
message := binary.LittleEndian.AppendUint32([]byte{}, data.SuspendMatch.HostProfileID)
|
|
message = binary.LittleEndian.AppendUint32(message, data.SuspendMatch.IsHostFlag)
|
|
|
|
if !data.SuspendMatch.Short {
|
|
message = binary.LittleEndian.AppendUint32(message, data.SuspendMatch.SuspendValue)
|
|
message = binary.LittleEndian.AppendUint32(message, data.SuspendMatch.ClientAIDValue)
|
|
}
|
|
return message, true
|
|
|
|
default:
|
|
logging.Info("Common", "Unknown match command:", aurora.Cyan(command), "data:", data.Other)
|
|
return data.Other, true
|
|
}
|
|
}
|
|
|
|
func LogMatchCommand(moduleName string, dest string, command byte, data MatchCommandData) {
|
|
logging.Notice(moduleName, "Match", aurora.Yellow(GetMatchCommandString(command)), "to", aurora.BrightCyan(dest))
|
|
|
|
if command == MatchReservation && data.Reservation != nil {
|
|
logging.Info(moduleName, "Match type:", aurora.Cyan(fmt.Sprintf("0x%02X", data.Reservation.MatchType)))
|
|
logging.Info(moduleName, "Local player count:", aurora.Cyan(data.Reservation.LocalPlayerCount))
|
|
} else if command == MatchResvOK && data.ResvOK != nil {
|
|
logging.Info(moduleName, "Group ID:", aurora.Cyan(data.ResvOK.GroupID))
|
|
logging.Info(moduleName, "Local player count:", aurora.Cyan(data.ResvOK.LocalPlayerCount))
|
|
logging.Info(moduleName, "Current client count:", aurora.Cyan(data.ResvOK.ClientCount))
|
|
logging.Info(moduleName, "Max client count:", aurora.Cyan(data.ResvOK.MaxPlayers))
|
|
logging.Info(moduleName, "Sender slot:", aurora.Cyan(data.ResvOK.SenderAID))
|
|
logging.Info(moduleName, "Receiver's new slot:", aurora.Cyan(data.ResvOK.ReceiverNewAID))
|
|
} else if command == MatchResvDeny && data.ResvDeny != nil {
|
|
reasonByte := fmt.Sprintf("0x%02X", data.ResvDeny.Reason)
|
|
logging.Notice(moduleName, "Reason:", aurora.BrightRed(data.ResvDeny.ReasonString), "("+aurora.Cyan(reasonByte).String()+")")
|
|
}
|
|
}
|