// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.Tests; using Microsoft.Extensions.Logging.Abstractions; using Moq; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.MuHelper; using MUnique.OpenMU.GameLogic.PlayerActions.Party; using MUnique.OpenMU.GameLogic.Views.Party; using MUnique.OpenMU.GameServer; using MUnique.OpenMU.Interfaces; using MUnique.OpenMU.Pathfinding; using MUnique.OpenMU.Persistence.InMemory; using MUnique.OpenMU.PlugIns; /// /// Tests the party functions. /// [TestFixture] public class PartyTest { private readonly PartyKickAction _kickAction = new(); /// /// Tests if an added party member gets added to the party list. /// [Test] public async ValueTask PartyMemberAddAsync() { var partyMember = await this.CreatePartyMemberAsync().ConfigureAwait(false); var party = new Party(new PartyManager(5, new NullLogger()), 5, new NullLogger()); await party.AddAsync(partyMember).ConfigureAwait(false); Assert.That(party.PartyList, Contains.Item(partyMember)); } /// /// Tests a kick request by a non-party-master for another player, which should fail. /// [Test] public async ValueTask PartyMemberKickFailByNonMasterAsync() { var party = await this.CreatePartyWithMembersAsync(3).ConfigureAwait(false); var partyMember2 = (Player)party.PartyList[1]; var partyMember3 = party.PartyList[2]; await this._kickAction.KickPlayerAsync(partyMember2, GetPartyMemberIndex(party, partyMember3)).ConfigureAwait(false); Assert.That(party.PartyList, Contains.Item(partyMember3)); } /// /// Tests if the player is kicking himself works, even if the player is no party master. /// [Test] public async ValueTask PartyMemberKickHimselfAsync() { var party = await this.CreatePartyWithMembersAsync(3).ConfigureAwait(false); var partyMember2 = party.PartyList[1]; await this._kickAction.KickPlayerAsync((Player)partyMember2, GetPartyMemberIndex(party, partyMember2)).ConfigureAwait(false); Assert.That(party.PartyList, Is.Not.Contains(partyMember2)); Assert.That(party.PartyList, Has.Count.EqualTo(2)); } /// /// Tests if another player can be kicked by the party master. /// [Test] public async ValueTask PartyMemberKickByMasterAsync() { var party = await this.CreatePartyWithMembersAsync(3).ConfigureAwait(false); var partyMaster = (Player)party.PartyList[0]; var partyMember = (Player)party.PartyList[1]; await this._kickAction.KickPlayerAsync(partyMaster, GetPartyMemberIndex(party, partyMember)).ConfigureAwait(false); Assert.That(party.PartyList, Is.Not.Contains(partyMember)); Assert.That(party.PartyList, Has.Count.EqualTo(2)); } /// /// Tests if the party disbands when the master kicks himself. /// [Test] public async ValueTask PartyMasterKicksHimselfAsync() { var party = await this.CreatePartyWithMembersAsync(3).ConfigureAwait(false); var partyMaster = party.PartyList[0]; var partyMember = party.PartyList[1]; await this._kickAction.KickPlayerAsync((Player)partyMaster, GetPartyMemberIndex(party, partyMaster)).ConfigureAwait(false); // Master leaves the party; the remaining 2 members stay. Assert.That(partyMaster.Party, Is.Null); Assert.That(party.PartyList, Does.Not.Contain(partyMaster)); Assert.That(partyMember.Party, Is.SameAs(party)); Assert.That(party.PartyList, Has.Count.EqualTo(2)); } /// /// Tests if the party automatically closes when one player is left. /// [Test] public async ValueTask PartyAutoCloseAsync() { var partyMember1 = await this.CreatePartyMemberAsync().ConfigureAwait(false); var party = new Party(new PartyManager(5, new NullLogger()), 5, new NullLogger()); await party.AddAsync(partyMember1).ConfigureAwait(false); var partyMember1Index = (byte)(party.PartyList.Count - 1); var partyMember2 = await this.CreatePartyMemberAsync().ConfigureAwait(false); await party.AddAsync(partyMember2).ConfigureAwait(false); var partyMember2Index = (byte)(party.PartyList.Count - 1); await this._kickAction.KickPlayerAsync(partyMember1, partyMember2Index).ConfigureAwait(false); Assert.That(partyMember1.Party, Is.Null); Assert.That(partyMember2.Party, Is.Null); Assert.That(party.PartyList, Is.Null.Or.Empty); Mock.Get(partyMember1.ViewPlugIns.GetPlugIn()!).Verify(v => v!.PartyMemberRemovedAsync(partyMember1Index), Times.Once); Mock.Get(partyMember2.ViewPlugIns.GetPlugIn()!).Verify(v => v!.PartyMemberRemovedAsync(partyMember2Index), Times.Once); } /// /// Tests the adding of party members. /// [Test] public async ValueTask PartyHandlerAddAsync() { var handler = new PartyRequestAction(); var player = await this.CreatePartyMemberAsync().ConfigureAwait(false); var toRequest = await this.CreatePartyMemberAsync().ConfigureAwait(false); player.Observers.Add(toRequest); await handler.HandlePartyRequestAsync(player, toRequest).ConfigureAwait(false); Mock.Get(toRequest.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(player), Times.Once); Assert.That(toRequest.LastPartyRequester, Is.SameAs(player)); } /// /// Tests if the party gets created after the requested player responses with accepting the party. /// [Test] public async ValueTask PartyResponseAcceptNewPartyAsync() { var handler = new PartyResponseAction(); var player = await this.CreatePartyMemberAsync().ConfigureAwait(false); var requester = await this.CreatePartyMemberAsync().ConfigureAwait(false); player.LastPartyRequester = requester; await player.PlayerState.TryAdvanceToAsync(PlayerState.PartyRequest).ConfigureAwait(false); await handler.HandleResponseAsync(player, true).ConfigureAwait(false); Assert.That(player.Party, Is.Not.Null); Assert.That(player.Party!.PartyMaster, Is.SameAs(requester)); Assert.That(player.LastPartyRequester, Is.Null); Assert.That(player.Party.PartyList, Contains.Item(player)); Mock.Get(player.ViewPlugIns.GetPlugIn()!).Verify(v => v!.UpdatePartyListAsync(), Times.AtLeastOnce); Mock.Get(requester.ViewPlugIns.GetPlugIn()!).Verify(v => v!.UpdatePartyListAsync(), Times.AtLeastOnce); } /// /// Tests if a request to a player which is already in a party (however this happened...) /// does not cause the player to be added to the other party. /// [Test] public async ValueTask PartyResponseAcceptExistingPartyAsync() { var handler = new PartyResponseAction(); // first put the player in a party with another player var player = await this.CreatePartyMemberAsync().ConfigureAwait(false); player.LastPartyRequester = await this.CreatePartyMemberAsync().ConfigureAwait(false); await player.PlayerState.TryAdvanceToAsync(PlayerState.PartyRequest).ConfigureAwait(false); await handler.HandleResponseAsync(player, true).ConfigureAwait(false); // now another player will try to request party from the player, which should fail var requester = await this.CreatePartyMemberAsync().ConfigureAwait(false); player.LastPartyRequester = requester; await handler.HandleResponseAsync(player, true).ConfigureAwait(false); Assert.That(player.Party!.PartyList, Is.Not.Contains(requester)); Assert.That(player.LastPartyRequester, Is.Null); Assert.That(requester.Party, Is.Null); Mock.Get(player.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(requester), Times.Never); } /// /// Tests if a party request is auto-accepted when is true /// and the requester is a friend. /// [Test] public async ValueTask PartyRequestAutoAcceptByFriendAsync() { var friendServer = new Mock(); friendServer.Setup(f => f.IsFriendAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); var gameContext = PartyTest.CreateGameServerContext(friendServer.Object); var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); var toRequest = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); player.SelectedCharacter!.Name = "Requester"; toRequest.SelectedCharacter!.Name = "Receiver"; player.Observers.Add(toRequest); var settingsMock = new Mock(); settingsMock.Setup(s => s.AutoAcceptFriend).Returns(true); toRequest.MuHelperSettings = settingsMock.Object; var handler = new PartyRequestAction(); await handler.HandlePartyRequestAsync(player, toRequest).ConfigureAwait(false); Assert.That(player.Party, Is.Not.Null); Assert.That(toRequest.Party, Is.SameAs(player.Party)); Mock.Get(toRequest.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(player), Times.Never); } /// /// Tests if a party request still shows the dialog when /// is true but the requester is not a friend. /// [Test] public async ValueTask PartyRequestAutoAcceptByFriendNotFriendAsync() { var friendServer = new Mock(); friendServer.Setup(f => f.IsFriendAsync(It.IsAny(), It.IsAny())).ReturnsAsync(false); var gameContext = PartyTest.CreateGameServerContext(friendServer.Object); var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); var toRequest = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); player.SelectedCharacter!.Name = "Requester"; toRequest.SelectedCharacter!.Name = "Receiver"; player.Observers.Add(toRequest); var settingsMock = new Mock(); settingsMock.Setup(s => s.AutoAcceptFriend).Returns(true); toRequest.MuHelperSettings = settingsMock.Object; var handler = new PartyRequestAction(); await handler.HandlePartyRequestAsync(player, toRequest).ConfigureAwait(false); Assert.That(player.Party, Is.Null); Mock.Get(toRequest.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(player), Times.Once); } /// /// Tests if a party request is auto-accepted when is true /// and both players are members of the same guild. /// [Test] public async ValueTask PartyRequestAutoAcceptByGuildAsync() { var gameContext = GameContextTestHelper.CreateGameContext(); var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); var toRequest = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); player.Observers.Add(toRequest); var guildId = 1u; player.GuildStatus = new GuildMemberStatus(guildId, GuildPosition.GuildMaster); toRequest.GuildStatus = new GuildMemberStatus(guildId, GuildPosition.NormalMember); var settingsMock = new Mock(); settingsMock.Setup(s => s.AutoAcceptGuild).Returns(true); toRequest.MuHelperSettings = settingsMock.Object; var handler = new PartyRequestAction(); await handler.HandlePartyRequestAsync(player, toRequest).ConfigureAwait(false); Assert.That(player.Party, Is.Not.Null); Assert.That(toRequest.Party, Is.SameAs(player.Party)); Mock.Get(toRequest.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(player), Times.Never); } /// /// Tests if a party request still shows the dialog when /// is true but the players are in different guilds. /// [Test] public async ValueTask PartyRequestAutoAcceptByGuildDifferentGuildAsync() { var gameContext = GameContextTestHelper.CreateGameContext(); var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); var toRequest = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); player.Observers.Add(toRequest); player.GuildStatus = new GuildMemberStatus(1u, GuildPosition.GuildMaster); toRequest.GuildStatus = new GuildMemberStatus(2u, GuildPosition.NormalMember); var settingsMock = new Mock(); settingsMock.Setup(s => s.AutoAcceptGuild).Returns(true); toRequest.MuHelperSettings = settingsMock.Object; var handler = new PartyRequestAction(); await handler.HandlePartyRequestAsync(player, toRequest).ConfigureAwait(false); Assert.That(player.Party, Is.Null); Mock.Get(toRequest.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(player), Times.Once); } /// /// Tests that a party request is not auto-accepted when no relevant flags are set. /// [Test] public async ValueTask PartyRequestAutoAcceptNoFlagsAsync() { var friendServer = new Mock(); var gameContext = PartyTest.CreateGameServerContext(friendServer.Object); var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); var toRequest = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); player.SelectedCharacter!.Name = "Requester"; toRequest.SelectedCharacter!.Name = "Receiver"; player.Observers.Add(toRequest); var settingsMock = new Mock(); toRequest.MuHelperSettings = settingsMock.Object; var handler = new PartyRequestAction(); await handler.HandlePartyRequestAsync(player, toRequest).ConfigureAwait(false); Assert.That(player.Party, Is.Null); Mock.Get(toRequest.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(player), Times.Once); } /// /// Tests that a party request is not auto-accepted when MuHelperSettings is null. /// [Test] public async ValueTask PartyRequestAutoAcceptNoSettingsAsync() { var friendServer = new Mock(); var gameContext = PartyTest.CreateGameServerContext(friendServer.Object); var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); var toRequest = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); player.Observers.Add(toRequest); var handler = new PartyRequestAction(); await handler.HandlePartyRequestAsync(player, toRequest).ConfigureAwait(false); Assert.That(player.Party, Is.Null); Mock.Get(toRequest.ViewPlugIns.GetPlugIn()!).Verify(v => v!.ShowPartyRequestAsync(player), Times.Once); } private static IGameServerContext CreateGameServerContext(IFriendServer friendServer) { var contextProvider = new InMemoryPersistenceContextProvider(); var context = contextProvider.CreateNewContext(); var gameConfiguration = context.CreateNew(); gameConfiguration.MaximumPartySize = 5; gameConfiguration.RecoveryInterval = int.MaxValue; gameConfiguration.MaximumInventoryMoney = int.MaxValue; var mapDef = context.CreateNew(); mapDef.Number = 0; mapDef.TerrainData = new byte[ushort.MaxValue + 3]; gameConfiguration.Maps.Add(mapDef); var mapInitializer = new MapInitializer(gameConfiguration, new NullLogger(), NullDropGenerator.Instance, null); var gameServer = new GameServerContext( new GameServerDefinition { GameConfiguration = gameConfiguration, ServerConfiguration = new GameServerConfiguration(), }, new Mock().Object, new Mock().Object, new Mock().Object, friendServer, new InMemoryPersistenceContextProvider(), mapInitializer, new NullLoggerFactory(), new PlugInManager(new List(), new NullLoggerFactory(), null, null), NullDropGenerator.Instance, new ConfigurationChangeMediator()); mapInitializer.PlugInManager = gameServer.PlugInManager; mapInitializer.PathFinderPool = gameServer.PathFinderPool; return gameServer; } private async ValueTask CreatePartyMemberAsync() { var result = await PlayerTestHelper.CreatePlayerAsync(GameContextTestHelper.CreateGameContext()).ConfigureAwait(false); await result.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false); return result; } private static byte GetPartyMemberIndex(Party party, IPartyMember member) { for (byte index = 0; index < party.PartyList.Count; index++) { if (party.PartyList[index] == member) { return index; } } throw new ArgumentException("The member is not part of the party.", nameof(member)); } private async ValueTask CreatePartyWithMembersAsync(int numberOfMembers) { var party = new Party(new PartyManager(5, new NullLogger()), 5, new NullLogger()); for (ushort i = 0; i < numberOfMembers; i++) { var partyMember = await this.CreatePartyMemberAsync().ConfigureAwait(false); await party.AddAsync(partyMember).ConfigureAwait(false); } return party; } }