From bcc5fc6e39e279196deeffc9d47b8858821df510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sat, 2 Sep 2023 20:30:23 +0100 Subject: [PATCH 1/4] Implement UpdateSessionURL --- go.mod | 2 +- go.sum | 4 +- matchmaking/protocol.go | 1 + matchmaking/update_session_url.go | 76 +++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 matchmaking/update_session_url.go diff --git a/go.mod b/go.mod index fbcb78c..aafd1cd 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ 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.40 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 diff --git a/go.sum b/go.sum index dcc47db..744bac3 100644 --- a/go.sum +++ b/go.sum @@ -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.40 h1:/fjtx7iucsXnODtAILEm6ottw4LlurTWpi4UxuQnaxQ= +github.com/PretendoNetwork/nex-go v1.0.40/go.mod h1:QepeB2ImpmIuAAYMyAsARGdfLntTBROEnBXfxpJ6FQg= 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= diff --git a/matchmaking/protocol.go b/matchmaking/protocol.go index 31177b8..afc12fc 100644 --- a/matchmaking/protocol.go +++ b/matchmaking/protocol.go @@ -26,6 +26,7 @@ func NewCommonMatchMakingProtocol(server *nex.Server) *CommonMatchMakingProtocol common_globals.Sessions = make(map[uint32]*common_globals.CommonMatchmakeSession) // TODO - Organize these by method ID + commonMatchMakingProtocol.UpdateSessionURL(updateSessionURL) commonMatchMakingProtocol.GetSessionURLs(getSessionURLs) commonMatchMakingProtocol.UnregisterGathering(unregisterGathering) commonMatchMakingProtocol.UpdateSessionHostV1(updateSessionHostV1) diff --git a/matchmaking/update_session_url.go b/matchmaking/update_session_url.go new file mode 100644 index 0000000..f6dab9d --- /dev/null +++ b/matchmaking/update_session_url.go @@ -0,0 +1,76 @@ +package matchmaking + +import ( + nex "github.com/PretendoNetwork/nex-go" + common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals" + match_making "github.com/PretendoNetwork/nex-protocols-go/match-making" +) + +func updateSessionURL(err error, client *nex.Client, callID uint32, idGathering uint32, strURL string) uint32 { + if err != nil { + logger.Error(err.Error()) + return nex.Errors.Core.InvalidArgument + } + + session, ok := common_globals.Sessions[idGathering] + if !ok { + return nex.Errors.RendezVous.SessionVoid + } + + logger.Info("=== UpdateSessionURL ===") + logger.Info(strURL) + logger.Infof("PID: %d", client.PID()) + logger.Infof("CID: %d", client.ConnectionID()) + logger.Info("========================") + + server := commonMatchMakingProtocol.server + hostPID := session.GameMatchmakeSession.Gathering.HostPID + host := server.FindClientFromPID(hostPID) + if host == nil { + logger.Warning("Host client not found") // This popped up once during testing. Leaving it noted here in case it becomes a problem. + return nex.Errors.Core.Exception + } + + stations := host.StationURLs() + + stationURL := nex.NewStationURL(strURL) + + if stationURL.Type() == 3 { + stations[1] = stationURL + } else { + stations[0] = stationURL + } + + rmcResponseStream := nex.NewStreamOut(server) + rmcResponseStream.WriteBool(true) // %retval% + + rmcResponseBody := rmcResponseStream.Bytes() + + // Build response packet + rmcResponse := nex.NewRMCResponse(match_making.ProtocolID, callID) + rmcResponse.SetSuccess(match_making.MethodGetSessionURLs, 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) + + return 0 +} From c158b4f3b7e5ebe05885c466d6efa28701a47fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sat, 2 Sep 2023 21:13:14 +0100 Subject: [PATCH 2/4] Implement UpdateSessionURL properly Hopefully... --- matchmaking/update_session_url.go | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/matchmaking/update_session_url.go b/matchmaking/update_session_url.go index f6dab9d..fc0a074 100644 --- a/matchmaking/update_session_url.go +++ b/matchmaking/update_session_url.go @@ -17,29 +17,10 @@ func updateSessionURL(err error, client *nex.Client, callID uint32, idGathering return nex.Errors.RendezVous.SessionVoid } - logger.Info("=== UpdateSessionURL ===") - logger.Info(strURL) - logger.Infof("PID: %d", client.PID()) - logger.Infof("CID: %d", client.ConnectionID()) - logger.Info("========================") - server := commonMatchMakingProtocol.server - hostPID := session.GameMatchmakeSession.Gathering.HostPID - host := server.FindClientFromPID(hostPID) - if host == nil { - logger.Warning("Host client not found") // This popped up once during testing. Leaving it noted here in case it becomes a problem. - return nex.Errors.Core.Exception - } - - stations := host.StationURLs() - stationURL := nex.NewStationURL(strURL) - - if stationURL.Type() == 3 { - stations[1] = stationURL - } else { - stations[0] = stationURL - } + // * Mario Kart 7 seems to set an empty strURL, so I assume this is what the method does? + session.GameMatchmakeSession.Gathering.HostPID = client.PID() rmcResponseStream := nex.NewStreamOut(server) rmcResponseStream.WriteBool(true) // %retval% From d33369f5bc65587b87eb91fa45db6f2caca0ffa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 3 Sep 2023 00:46:50 +0100 Subject: [PATCH 3/4] Rework handling of player disconnects Reassign owner on session when it leaves unreliably. --- globals/matchmaking_utils.go | 158 +++++++++++++++++- .../auto_matchmake_postpone.go | 2 +- ...matchmake_with_search_criteria_postpone.go | 2 +- .../create_matchmake_session.go | 2 +- .../create_matchmake_session_with_param.go | 2 +- matchmaking-ext/end_participation.go | 72 +------- matchmaking/get_session_urls.go | 8 +- matchmaking/protocol.go | 2 +- matchmaking/update_session_host_v1.go | 3 + matchmaking/update_session_url.go | 3 + 10 files changed, 170 insertions(+), 84 deletions(-) diff --git a/globals/matchmaking_utils.go b/globals/matchmaking_utils.go index 67f1f5b..5a58c1b 100644 --- a/globals/matchmaking_utils.go +++ b/globals/matchmaking_utils.go @@ -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" ) @@ -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()) } } @@ -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") + } + } +} diff --git a/matchmake-extension/auto_matchmake_postpone.go b/matchmake-extension/auto_matchmake_postpone.go index 381ab02..1aea0c6 100644 --- a/matchmake-extension/auto_matchmake_postpone.go +++ b/matchmake-extension/auto_matchmake_postpone.go @@ -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() diff --git a/matchmake-extension/auto_matchmake_with_search_criteria_postpone.go b/matchmake-extension/auto_matchmake_with_search_criteria_postpone.go index d5b2c7c..a4f8c49 100644 --- a/matchmake-extension/auto_matchmake_with_search_criteria_postpone.go +++ b/matchmake-extension/auto_matchmake_with_search_criteria_postpone.go @@ -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() diff --git a/matchmake-extension/create_matchmake_session.go b/matchmake-extension/create_matchmake_session.go index 3c46eff..7606c45 100644 --- a/matchmake-extension/create_matchmake_session.go +++ b/matchmake-extension/create_matchmake_session.go @@ -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() diff --git a/matchmake-extension/create_matchmake_session_with_param.go b/matchmake-extension/create_matchmake_session_with_param.go index 8ed4870..3820a8a 100644 --- a/matchmake-extension/create_matchmake_session_with_param.go +++ b/matchmake-extension/create_matchmake_session_with_param.go @@ -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) diff --git a/matchmaking-ext/end_participation.go b/matchmaking-ext/end_participation.go index c99b211..124d3e3 100644 --- a/matchmaking-ext/end_participation.go +++ b/matchmaking-ext/end_participation.go @@ -33,7 +33,7 @@ func endParticipation(err error, client *nex.Client, callID uint32, idGathering if matchmakeSession.Gathering.Flags&match_making.GatheringFlags.DisconnectChangeOwner == 0 { deleteSession = true } else { - changeSessionOwner(client.ConnectionID(), idGathering, callID) + common_globals.ChangeSessionOwner(client, idGathering) } } @@ -122,73 +122,3 @@ func endParticipation(err error, client *nex.Client, callID uint32, idGathering return 0 } - -func changeSessionOwner(ownerConnectionID uint32, gathering uint32, callID uint32) { - server := commonMatchMakingExtProtocol.server - var otherClient *nex.Client - - otherConnectionID := common_globals.FindOtherConnectionID(ownerConnectionID, gathering) - if otherConnectionID != 0 { - otherClient = server.FindClientFromConnectionID(uint32(otherConnectionID)) - if otherClient != nil { - common_globals.Sessions[gathering].GameMatchmakeSession.Gathering.OwnerPID = otherClient.PID() - } else { - logger.Warning("Other client not found") - return - } - } else { - return - } - - rmcMessage := nex.NewRMCRequest() - rmcMessage.SetProtocolID(notifications.ProtocolID) - rmcMessage.SetCallID(0xffff0000 + callID) - 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 common_globals.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 { - logger.Warning("Client not found") - } - } -} diff --git a/matchmaking/get_session_urls.go b/matchmaking/get_session_urls.go index ccbec66..5317ec7 100644 --- a/matchmaking/get_session_urls.go +++ b/matchmaking/get_session_urls.go @@ -21,8 +21,12 @@ func getSessionURLs(err error, client *nex.Client, callID uint32, gid uint32) ui hostPID := session.GameMatchmakeSession.Gathering.HostPID host := server.FindClientFromPID(hostPID) if host == nil { - logger.Warning("Host client not found") // This popped up once during testing. Leaving it noted here in case it becomes a problem. - return nex.Errors.Core.Exception + logger.Warning("Host client not found, trying with owner client") // This popped up once during testing. Leaving it noted here in case it becomes a problem. + host = server.FindClientFromPID(session.GameMatchmakeSession.Gathering.OwnerPID) + if host == nil { + logger.Error("Owner client not found") // This popped up once during testing. Leaving it noted here in case it becomes a problem. + return nex.Errors.Core.Exception + } } rmcResponseStream := nex.NewStreamOut(server) diff --git a/matchmaking/protocol.go b/matchmaking/protocol.go index afc12fc..b1d76ff 100644 --- a/matchmaking/protocol.go +++ b/matchmaking/protocol.go @@ -35,7 +35,7 @@ func NewCommonMatchMakingProtocol(server *nex.Server) *CommonMatchMakingProtocol server.On("Kick", func(packet nex.PacketInterface) { fmt.Println("Leaving") - common_globals.RemoveConnectionIDFromAllSessions(packet.Sender().ConnectionID()) + common_globals.RemoveClientFromAllSessions(packet.Sender()) }) return commonMatchMakingProtocol diff --git a/matchmaking/update_session_host_v1.go b/matchmaking/update_session_host_v1.go index 55771e7..05a613e 100644 --- a/matchmaking/update_session_host_v1.go +++ b/matchmaking/update_session_host_v1.go @@ -24,6 +24,9 @@ func updateSessionHostV1(err error, client *nex.Client, callID uint32, gid uint3 server := commonMatchMakingProtocol.server session.GameMatchmakeSession.Gathering.HostPID = client.PID() + if session.GameMatchmakeSession.Gathering.Flags&match_making.GatheringFlags.DisconnectChangeOwner != 0 { + session.GameMatchmakeSession.Gathering.OwnerPID = client.PID() + } rmcResponse := nex.NewRMCResponse(match_making.ProtocolID, callID) rmcResponse.SetSuccess(match_making.MethodUpdateSessionHostV1, nil) diff --git a/matchmaking/update_session_url.go b/matchmaking/update_session_url.go index fc0a074..67349ad 100644 --- a/matchmaking/update_session_url.go +++ b/matchmaking/update_session_url.go @@ -21,6 +21,9 @@ func updateSessionURL(err error, client *nex.Client, callID uint32, idGathering // * Mario Kart 7 seems to set an empty strURL, so I assume this is what the method does? session.GameMatchmakeSession.Gathering.HostPID = client.PID() + if session.GameMatchmakeSession.Gathering.Flags&match_making.GatheringFlags.DisconnectChangeOwner != 0 { + session.GameMatchmakeSession.Gathering.OwnerPID = client.PID() + } rmcResponseStream := nex.NewStreamOut(server) rmcResponseStream.WriteBool(true) // %retval% From 0e7243fb78a7614a040d2212e9f932ea171d5338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Tue, 3 Oct 2023 23:45:34 +0100 Subject: [PATCH 4/4] Update go modules and add `JoinMatchmakeSession` This allows players on Mario Kart 7 to join to their friends or other people they have played with (rivals). --- go.mod | 6 +- go.sum | 12 +- matchmake-extension/join_matchmake_session.go | 121 ++++++++++++++++++ matchmake-extension/protocol.go | 1 + 4 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 matchmake-extension/join_matchmake_session.go diff --git a/go.mod b/go.mod index aafd1cd..76ade1d 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/PretendoNetwork/nex-protocols-common-go go 1.19 require ( - github.com/PretendoNetwork/nex-go v1.0.40 + 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 ( @@ -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 ) diff --git a/go.sum b/go.sum index 744bac3..40cafe2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/PretendoNetwork/nex-go v1.0.40 h1:/fjtx7iucsXnODtAILEm6ottw4LlurTWpi4UxuQnaxQ= -github.com/PretendoNetwork/nex-go v1.0.40/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= @@ -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= diff --git a/matchmake-extension/join_matchmake_session.go b/matchmake-extension/join_matchmake_session.go new file mode 100644 index 0000000..aca0a25 --- /dev/null +++ b/matchmake-extension/join_matchmake_session.go @@ -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 +} diff --git a/matchmake-extension/protocol.go b/matchmake-extension/protocol.go index 5c65f3b..72fc92e 100644 --- a/matchmake-extension/protocol.go +++ b/matchmake-extension/protocol.go @@ -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)