Skip to content

Commit

Permalink
Merge pull request #18 from DaniElectra/update-session-url
Browse files Browse the repository at this point in the history
Implement various functions and player disconnects
  • Loading branch information
jonbarrow authored Oct 4, 2023
2 parents d2d3440 + 0e7243f commit ca8ad16
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 93 deletions.
158 changes: 152 additions & 6 deletions globals/matchmaking_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import (
"strconv"
"strings"

"github.com/PretendoNetwork/nex-go"
nex "github.com/PretendoNetwork/nex-go"
match_making "github.com/PretendoNetwork/nex-protocols-go/match-making"
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
notifications "github.com/PretendoNetwork/nex-protocols-go/notifications"
notifications_types "github.com/PretendoNetwork/nex-protocols-go/notifications/types"
"golang.org/x/exp/slices"
)

Expand Down Expand Up @@ -57,13 +60,83 @@ func FindClientSession(connectionID uint32) uint32 {
return 0
}

// RemoveConnectionIDFromAllSessions removes a client from every session
func RemoveConnectionIDFromAllSessions(clientConnectionID uint32) {
// RemoveClientFromAllSessions removes a client from every session
func RemoveClientFromAllSessions(client *nex.Client) {
// * Keep checking until no session is found
for gid := FindClientSession(clientConnectionID); gid != 0; {
RemoveConnectionIDFromSession(clientConnectionID, gid)
for gid := FindClientSession(client.ConnectionID()); gid != 0; {
session := Sessions[gid]
lenParticipants := len(session.ConnectionIDs)

gid = FindClientSession(clientConnectionID)
RemoveConnectionIDFromSession(client.ConnectionID(), gid)

if lenParticipants <= 1 {
gid = FindClientSession(client.ConnectionID())
continue
}

ownerPID := session.GameMatchmakeSession.Gathering.OwnerPID

if client.PID() == ownerPID {
// This flag tells the server to change the matchmake session owner if they disconnect
// If the flag is not set, delete the session
// More info: https://nintendo-wiki.pretendo.network/docs/nex/protocols/match-making/types#flags
if session.GameMatchmakeSession.Gathering.Flags&match_making.GatheringFlags.DisconnectChangeOwner == 0 {
delete(Sessions, gid)
} else {
ChangeSessionOwner(client, gid)
}
} else {
server := client.Server()

rmcMessage := nex.NewRMCRequest()
rmcMessage.SetProtocolID(notifications.ProtocolID)
rmcMessage.SetCallID(0xffff0000)
rmcMessage.SetMethodID(notifications.MethodProcessNotificationEvent)

category := notifications.NotificationCategories.Participation
subtype := notifications.NotificationSubTypes.Participation.Disconnected

oEvent := notifications_types.NewNotificationEvent()
oEvent.PIDSource = client.PID()
oEvent.Type = notifications.BuildNotificationType(category, subtype)
oEvent.Param1 = gid
oEvent.Param2 = client.PID()

stream := nex.NewStreamOut(server)
oEventBytes := oEvent.Bytes(stream)
rmcMessage.SetParameters(oEventBytes)

rmcMessageBytes := rmcMessage.Bytes()

targetClient := server.FindClientFromPID(uint32(ownerPID))
if targetClient == nil {
// TODO - We don't have a logger here
// logger.Warning("Owner client not found")
gid = FindClientSession(client.ConnectionID())
continue
}

var messagePacket nex.PacketInterface

if server.PRUDPVersion() == 0 {
messagePacket, _ = nex.NewPacketV0(targetClient, nil)
messagePacket.SetVersion(0)
} else {
messagePacket, _ = nex.NewPacketV1(targetClient, nil)
messagePacket.SetVersion(1)
}
messagePacket.SetSource(0xA1)
messagePacket.SetDestination(0xAF)
messagePacket.SetType(nex.DataPacket)
messagePacket.SetPayload(rmcMessageBytes)

messagePacket.AddFlag(nex.FlagNeedsAck)
messagePacket.AddFlag(nex.FlagReliable)

server.Send(messagePacket)
}

gid = FindClientSession(client.ConnectionID())
}
}

Expand Down Expand Up @@ -221,3 +294,76 @@ func AddPlayersToSession(session *CommonMatchmakeSession, connectionIDs []uint32

return nil, 0
}

// ChangeSessionOwner changes the session owner to a different client
func ChangeSessionOwner(ownerClient *nex.Client, gathering uint32) {
server := ownerClient.Server()
var otherClient *nex.Client

otherConnectionID := FindOtherConnectionID(ownerClient.ConnectionID(), gathering)
if otherConnectionID != 0 {
otherClient = server.FindClientFromConnectionID(uint32(otherConnectionID))
if otherClient != nil {
Sessions[gathering].GameMatchmakeSession.Gathering.OwnerPID = otherClient.PID()
} else {
// TODO - We don't have a logger here
// logger.Warning("Other client not found")
return
}
} else {
return
}

rmcMessage := nex.NewRMCRequest()
rmcMessage.SetProtocolID(notifications.ProtocolID)
rmcMessage.SetCallID(0xffff0000)
rmcMessage.SetMethodID(notifications.MethodProcessNotificationEvent)

category := notifications.NotificationCategories.OwnershipChanged
subtype := notifications.NotificationSubTypes.OwnershipChanged.None

oEvent := notifications_types.NewNotificationEvent()
oEvent.PIDSource = otherClient.PID()
oEvent.Type = notifications.BuildNotificationType(category, subtype)
oEvent.Param1 = gathering
oEvent.Param2 = otherClient.PID()

// TODO - StrParam doesn't have this value on some servers
// https://github.com/kinnay/NintendoClients/issues/101
// unixTime := time.Now()
// oEvent.StrParam = strconv.FormatInt(unixTime.UnixMicro(), 10)

stream := nex.NewStreamOut(server)
oEventBytes := oEvent.Bytes(stream)
rmcMessage.SetParameters(oEventBytes)

rmcRequestBytes := rmcMessage.Bytes()

for _, connectionID := range Sessions[gathering].ConnectionIDs {
targetClient := server.FindClientFromConnectionID(connectionID)
if targetClient != nil {
var messagePacket nex.PacketInterface

if server.PRUDPVersion() == 0 {
messagePacket, _ = nex.NewPacketV0(targetClient, nil)
messagePacket.SetVersion(0)
} else {
messagePacket, _ = nex.NewPacketV1(targetClient, nil)
messagePacket.SetVersion(1)
}

messagePacket.SetSource(0xA1)
messagePacket.SetDestination(0xAF)
messagePacket.SetType(nex.DataPacket)
messagePacket.SetPayload(rmcRequestBytes)

messagePacket.AddFlag(nex.FlagNeedsAck)
messagePacket.AddFlag(nex.FlagReliable)

server.Send(messagePacket)
} else {
// TODO - We don't have a logger here
// logger.Warning("Client not found")
}
}
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ module github.com/PretendoNetwork/nex-protocols-common-go
go 1.19

require (
github.com/PretendoNetwork/nex-go v1.0.39
github.com/PretendoNetwork/nex-go v1.0.41
github.com/PretendoNetwork/nex-protocols-go v1.0.52
github.com/PretendoNetwork/plogger-go v1.0.4
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
)

require (
Expand All @@ -16,6 +16,6 @@ require (
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/superwhiskers/crunch/v3 v3.5.7 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/term v0.11.0 // indirect
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/PretendoNetwork/nex-go v1.0.39 h1:F0EDhQWqh1TWD7HnU3QjRdCUepgN9YyAxophT25FkcM=
github.com/PretendoNetwork/nex-go v1.0.39/go.mod h1:QepeB2ImpmIuAAYMyAsARGdfLntTBROEnBXfxpJ6FQg=
github.com/PretendoNetwork/nex-go v1.0.41 h1:TcBb1Bpe2KAB+AXaGX1K9NVQBRtZJIoy4yCvRde2xbI=
github.com/PretendoNetwork/nex-go v1.0.41/go.mod h1:QwHEa165DeVd0xIuthrgc3j6NWXT8lyPSG6t5kC/Shk=
github.com/PretendoNetwork/nex-protocols-go v1.0.52 h1:romSI1brKF+IGcK6+v2p8AjgbztVnTOVENT2U+dcWq0=
github.com/PretendoNetwork/nex-protocols-go v1.0.52/go.mod h1:9r4KbNELVZj01TY8p+FGYDb9+e4mHLiWKo5NL1fPqD8=
github.com/PretendoNetwork/plogger-go v1.0.4 h1:PF7xHw9eDRHH+RsAP9tmAE7fG0N0p6H4iPwHKnsoXwc=
Expand All @@ -16,16 +16,16 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/superwhiskers/crunch/v3 v3.5.7 h1:N9RLxaR65C36i26BUIpzPXGy2f6pQ7wisu2bawbKNqg=
github.com/superwhiskers/crunch/v3 v3.5.7/go.mod h1:4ub2EKgF1MAhTjoOCTU4b9uLMsAweHEa89aRrfAypXA=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
2 changes: 1 addition & 1 deletion matchmake-extension/auto_matchmake_postpone.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func autoMatchmake_Postpone(err error, client *nex.Client, callID uint32, anyGat

// A client may disconnect from a session without leaving reliably,
// so let's make sure the client is removed from the session
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
common_globals.RemoveClientFromAllSessions(client)

var matchmakeSession *match_making_types.MatchmakeSession
anyGatheringDataType := anyGathering.TypeName()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func autoMatchmakeWithSearchCriteria_Postpone(err error, client *nex.Client, cal

// * A client may disconnect from a session without leaving reliably,
// * so let's make sure the client is removed from the session
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
common_globals.RemoveClientFromAllSessions(client)

var matchmakeSession *match_making_types.MatchmakeSession
anyGatheringDataType := anyGathering.TypeName()
Expand Down
2 changes: 1 addition & 1 deletion matchmake-extension/create_matchmake_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func createMatchmakeSession(err error, client *nex.Client, callID uint32, anyGat

// A client may disconnect from a session without leaving reliably,
// so let's make sure the client is removed from the session
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
common_globals.RemoveClientFromAllSessions(client)

var matchmakeSession *match_making_types.MatchmakeSession
anyGatheringDataType := anyGathering.TypeName()
Expand Down
2 changes: 1 addition & 1 deletion matchmake-extension/create_matchmake_session_with_param.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func createMatchmakeSessionWithParam(err error, client *nex.Client, callID uint3

// * A client may disconnect from a session without leaving reliably,
// * so let's make sure the client is removed from all sessions
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
common_globals.RemoveClientFromAllSessions(client)

joinedMatchmakeSession := createMatchmakeSessionParam.SourceMatchmakeSession.Copy().(*match_making_types.MatchmakeSession)

Expand Down
121 changes: 121 additions & 0 deletions matchmake-extension/join_matchmake_session.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package matchmake_extension

import (
nex "github.com/PretendoNetwork/nex-go"
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
notifications "github.com/PretendoNetwork/nex-protocols-go/notifications"
notifications_types "github.com/PretendoNetwork/nex-protocols-go/notifications/types"
)

func joinMatchmakeSession(err error, client *nex.Client, callID uint32, gid uint32, strMessage string) uint32 {
if err != nil {
logger.Error(err.Error())
return nex.Errors.Core.InvalidArgument
}

server := client.Server()

session, ok := common_globals.Sessions[gid]
if !ok {
return nex.Errors.RendezVous.SessionVoid
}

// TODO - More checks here

err, errCode := common_globals.AddPlayersToSession(session, []uint32{client.ConnectionID()})
if err != nil {
logger.Error(err.Error())
return errCode
}

joinedMatchmakeSession := session.GameMatchmakeSession

rmcResponseStream := nex.NewStreamOut(server)

if server.MatchMakingProtocolVersion().GreaterOrEqual("3.0.0") {
rmcResponseStream.WriteBuffer(joinedMatchmakeSession.SessionKey)
}

rmcResponseBody := rmcResponseStream.Bytes()

rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
rmcResponse.SetSuccess(matchmake_extension.MethodJoinMatchmakeSession, rmcResponseBody)

rmcResponseBytes := rmcResponse.Bytes()

var responsePacket nex.PacketInterface

if server.PRUDPVersion() == 0 {
responsePacket, _ = nex.NewPacketV0(client, nil)
responsePacket.SetVersion(0)
} else {
responsePacket, _ = nex.NewPacketV1(client, nil)
responsePacket.SetVersion(1)
}

responsePacket.SetSource(0xA1)
responsePacket.SetDestination(0xAF)
responsePacket.SetType(nex.DataPacket)
responsePacket.SetPayload(rmcResponseBytes)

responsePacket.AddFlag(nex.FlagNeedsAck)
responsePacket.AddFlag(nex.FlagReliable)

server.Send(responsePacket)

for i := 0; i < len(session.ConnectionIDs); i++ {
target := server.FindClientFromConnectionID(session.ConnectionIDs[i])
if target == nil {
// TODO - Error here?
logger.Warning("Player not found")
continue
}

// * Works for Minecraft, not tried on anything else
notificationRequestMessage := nex.NewRMCRequest()
notificationRequestMessage.SetProtocolID(notifications.ProtocolID)
notificationRequestMessage.SetCallID(0xffff0000 + callID + uint32(i))
notificationRequestMessage.SetMethodID(notifications.MethodProcessNotificationEvent)

notificationCategory := notifications.NotificationCategories.Participation
notificationSubtype := notifications.NotificationSubTypes.Participation.NewParticipant

oEvent := notifications_types.NewNotificationEvent()
oEvent.PIDSource = client.PID()
oEvent.Type = notifications.BuildNotificationType(notificationCategory, notificationSubtype)
oEvent.Param1 = joinedMatchmakeSession.ID
oEvent.Param2 = target.PID()
oEvent.StrParam = strMessage
oEvent.Param3 = 1

notificationStream := nex.NewStreamOut(server)

notificationStream.WriteStructure(oEvent)

notificationRequestMessage.SetParameters(notificationStream.Bytes())
notificationRequestBytes := notificationRequestMessage.Bytes()

var messagePacket nex.PacketInterface

if server.PRUDPVersion() == 0 {
messagePacket, _ = nex.NewPacketV0(client, nil)
messagePacket.SetVersion(0)
} else {
messagePacket, _ = nex.NewPacketV1(client, nil)
messagePacket.SetVersion(1)
}

messagePacket.SetSource(0xA1)
messagePacket.SetDestination(0xAF)
messagePacket.SetType(nex.DataPacket)
messagePacket.SetPayload(notificationRequestBytes)

messagePacket.AddFlag(nex.FlagNeedsAck)
messagePacket.AddFlag(nex.FlagReliable)

server.Send(messagePacket)
}

return 0
}
1 change: 1 addition & 0 deletions matchmake-extension/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func initDefault(c *CommonMatchmakeExtensionProtocol) {
c.DefaultProtocol.UpdateProgressScore(updateProgressScore)
c.DefaultProtocol.CreateMatchmakeSessionWithParam(createMatchmakeSessionWithParam)
c.DefaultProtocol.UpdateApplicationBuffer(updateApplicationBuffer)
c.DefaultProtocol.JoinMatchmakeSession(joinMatchmakeSession)
c.DefaultProtocol.JoinMatchmakeSessionWithParam(joinMatchmakeSessionWithParam)
c.DefaultProtocol.ModifyCurrentGameAttribute(modifyCurrentGameAttribute)
c.DefaultProtocol.BrowseMatchmakeSession(browseMatchmakeSession)
Expand Down
Loading

0 comments on commit ca8ad16

Please sign in to comment.