diff --git a/apps/matchmakingserver/service/battleground_service.go b/apps/matchmakingserver/service/battleground_service.go index e255b077..c9d9366d 100644 --- a/apps/matchmakingserver/service/battleground_service.go +++ b/apps/matchmakingserver/service/battleground_service.go @@ -826,53 +826,64 @@ func (s *battleGroundService) PlayerBecomeOffline(ctx context.Context, playerGUI } func (s *battleGroundService) ProcessExpiredBattlegroundInvites(ctx context.Context) { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for { select { - case <-time.After(time.Second): - bgs, err := s.battlegroundsRepo.GetAllActiveBattlegrounds(ctx) - if err != nil { - log.Err(err).Msg("failed to get all active battlegrounds") - break - } - for _, bg := range bgs { - for _, invites := range bg.InvitedPlayersPerTeam { - for _, invite := range invites { - if time.Since(invite.InvitedTime) > time.Minute { - err = s.battlegroundsRepo.UpdateBattleground( - ctx, - bg.InstanceID, - repo.RealmWithBattlegroupKey{RealmID: bg.RealmID, BattlegroupID: bg.BattleGroupID}, - func(b *battleground.Battleground) error { - b.RemovePlayerFromInvite(uint64(invite.GUID.LowGUID), uint32(invite.GUID.RealmID)) - return nil - }, - ) - if err != nil { - log.Err(err).Msg("failed to remove invite from Battleground") - continue - } - - err = s.RemovePlayerFromQueue(ctx, uint64(invite.GUID.LowGUID), uint32(invite.GUID.RealmID), bg.QueueTypeID) - if err != nil { - log.Err(err).Msg("failed to remove invited player from queue") - continue - } - - slot := uint8(0) // TODO: provide real slot - err = s.eventsProducer.InviteExpired(&events.MatchmakingEventPlayersInviteExpiredPayload{ - RealmID: uint32(invite.GUID.RealmID), - PlayersGUID: []guid.LowType{invite.GUID.LowGUID}, - QueueSlotByPlayer: map[guid.LowType]uint8{invite.GUID.LowGUID: slot}, - }) - if err != nil { - log.Err(err).Msg("Failed to add invited player to queue") - } - } + case <-ticker.C: + s.processExpiredBattlegroundInvitesTick(ctx) + case <-ctx.Done(): + return + } + } +} + +func (s *battleGroundService) processExpiredBattlegroundInvitesTick(ctx context.Context) { + bgs, err := s.battlegroundsRepo.GetAllActiveBattlegrounds(ctx) + if err != nil { + log.Err(err).Msg("failed to get all active battlegrounds") + return + } + for _, bg := range bgs { + for _, invites := range bg.InvitedPlayersPerTeam { + for _, invite := range invites { + if time.Since(invite.InvitedTime) > time.Minute { + err = s.battlegroundsRepo.UpdateBattleground( + ctx, + bg.InstanceID, + repo.RealmWithBattlegroupKey{RealmID: bg.RealmID, BattlegroupID: bg.BattleGroupID}, + func(b *battleground.Battleground) error { + b.RemovePlayerFromInvite(uint64(invite.GUID.LowGUID), uint32(invite.GUID.RealmID)) + return nil + }, + ) + if err != nil { + log.Err(err).Msg("failed to remove invite from Battleground") + continue + } + + // Unlink only this battleground: RemovePlayerFromQueue + // removed the player from every battleground of the same + // queue type, including one where they are an active + // participant of a running match. + s.removeBattlegroundLinkForPlayer(BattlegroundKey{ + RealmID: bg.RealmID, + InstanceID: bg.InstanceID, + BattlegroupID: bg.BattleGroupID, + }, uint64(invite.GUID.LowGUID), uint32(invite.GUID.RealmID)) + + slot := uint8(0) // TODO: provide real slot + err = s.eventsProducer.InviteExpired(&events.MatchmakingEventPlayersInviteExpiredPayload{ + RealmID: uint32(invite.GUID.RealmID), + PlayersGUID: []guid.LowType{invite.GUID.LowGUID}, + QueueSlotByPlayer: map[guid.LowType]uint8{invite.GUID.LowGUID: slot}, + }) + if err != nil { + log.Err(err).Msg("Failed to add invited player to queue") } } } - case <-ctx.Done(): - return } } } diff --git a/apps/matchmakingserver/service/battleground_service_test.go b/apps/matchmakingserver/service/battleground_service_test.go index 6a1c68f5..4faf6b85 100644 --- a/apps/matchmakingserver/service/battleground_service_test.go +++ b/apps/matchmakingserver/service/battleground_service_test.go @@ -4,10 +4,12 @@ import ( "sync" "context" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/walkline/ToCloud9/apps/matchmakingserver/battleground" "github.com/walkline/ToCloud9/apps/matchmakingserver/repo" + "github.com/walkline/ToCloud9/shared/events" "github.com/walkline/ToCloud9/shared/wow/guid" ) @@ -205,3 +207,62 @@ func TestBattlegroundStatusChangedEndedPurgesInvitedLinks(t *testing.T) { assert.Empty(t, s.GetQueueOrBattlegroundLinkForPlayer(QueuesByRealmAndPlayerKey{activePlayer})) assert.Empty(t, s.GetQueueOrBattlegroundLinkForPlayer(QueuesByRealmAndPlayerKey{invitedPlayer})) } + +type noopMatchmakingProducer struct{} + +func (noopMatchmakingProducer) JoinedQueue(*events.MatchmakingEventPlayersQueuedPayload) error { + return nil +} +func (noopMatchmakingProducer) InvitedToBGOrArena(*events.MatchmakingEventPlayersInvitedPayload) error { + return nil +} +func (noopMatchmakingProducer) InviteExpired(*events.MatchmakingEventPlayersInviteExpiredPayload) error { + return nil +} + +func TestExpiredInviteOnlyUnlinksItsBattleground(t *testing.T) { + bgRepo := repo.NewBattlegroundInMemRepo() + player := guid.PlayerUnwrapped{RealmID: 1, LowGUID: 7} + + expiredBG := &battleground.Battleground{ + InstanceID: 42, + RealmID: 1, + QueueTypeID: battleground.QueueTypeIDWarsongGulch, + Status: battleground.StatusWaitJoin, + } + expiredBG.InvitedPlayersPerTeam[battleground.TeamAlliance] = []battleground.InvitedPlayer{ + {GUID: player, InvitedTime: time.Now().Add(-2 * time.Minute)}, + } + activeBG := &battleground.Battleground{ + InstanceID: 43, + RealmID: 1, + QueueTypeID: battleground.QueueTypeIDWarsongGulch, + Status: battleground.StatusInProgress, + } + activeBG.ActivePlayersPerTeam[battleground.TeamAlliance] = []guid.PlayerUnwrapped{player} + assert.NoError(t, bgRepo.SaveBattleground(context.Background(), expiredBG)) + assert.NoError(t, bgRepo.SaveBattleground(context.Background(), activeBG)) + + s := &battleGroundService{ + playersQueueOrBattleground: make(map[QueuesByRealmAndPlayerKey][]QueueOrBattlegroundLink), + battlegroundsRepo: bgRepo, + eventsProducer: noopMatchmakingProducer{}, + } + expiredKey := BattlegroundKey{RealmID: 1, InstanceID: 42} + activeKey := BattlegroundKey{RealmID: 1, InstanceID: 43} + s.playersQueueOrBattleground[QueuesByRealmAndPlayerKey{player}] = []QueueOrBattlegroundLink{ + {BattlegroundKey: &expiredKey}, + {BattlegroundKey: &activeKey}, + } + + s.processExpiredBattlegroundInvitesTick(context.Background()) + + links := s.GetQueueOrBattlegroundLinkForPlayer(QueuesByRealmAndPlayerKey{player}) + assert.Len(t, links, 1, "the active battleground link must survive") + assert.Equal(t, activeKey, *links[0].BattlegroundKey) + + // The invite itself is gone from the expired battleground. + got, err := bgRepo.GetBattlegroundByInstanceID(context.Background(), 42, repo.RealmWithBattlegroupKey{RealmID: 1}) + assert.NoError(t, err) + assert.Empty(t, got.InvitedPlayersPerTeam[battleground.TeamAlliance]) +}