Skip to content

Commit

Permalink
Fix for .net client failing to connect in AP 0.4 (#54)
Browse files Browse the repository at this point in the history
* Throw TimeoutException if accidentally caught in infinite loop when retrieving from DataStorage.

* Fix for .net client failing to connect in AP 0.4
Proposed changes to AP: ArchipelagoMW/Archipelago#757

* Added unit test + refactored

Co-authored-by: Hussein Farran <[email protected]>
  • Loading branch information
Jarno458 and Ijwu authored Jul 15, 2022
1 parent f0544a5 commit abe2766
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 46 deletions.
200 changes: 200 additions & 0 deletions Archipelago.MultiClient.Net.Tests/PlayerHelperFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.Models;
using Archipelago.MultiClient.Net.Packets;
using NSubstitute;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;

namespace Archipelago.MultiClient.Net.Tests
{
class PlayerHelperFixture
{
[Test]
public void Should_return_empty_collection_when_not_yet_initialized()
{
var socket = Substitute.For<IArchipelagoSocketHelper>();

var sut = new PlayerHelper(socket);

Assert.That(sut.AllPlayers, Is.Empty);
}

[Test]
public void Should_add_players_from_connected_packet()
{
var socket = Substitute.For<IArchipelagoSocketHelper>();

var sut = new PlayerHelper(socket);

var connectedPacket = new ConnectedPacket {
Players = new[] {
new NetworkPlayer { Name = "1", Alias = "One", Slot = 1, Team = 1 },
new NetworkPlayer { Name = "2", Alias = "Two", Slot = 2, Team = 1 },
new NetworkPlayer { Name = "3", Alias = "Three", Slot = 1, Team = 2 },
}
};

socket.PacketReceived += Raise.Event<ArchipelagoSocketHelper.PacketReceivedHandler>(connectedPacket);

var playerOne = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 1);
Assert.That(playerOne.Name, Is.EqualTo("1"));
Assert.That(playerOne.Alias, Is.EqualTo("One"));

var playerTwo = sut.AllPlayers.First(p => p.Slot == 2 && p.Team == 1);
Assert.That(playerTwo.Name, Is.EqualTo("2"));
Assert.That(playerTwo.Alias, Is.EqualTo("Two"));

var playerThree = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 2);
Assert.That(playerThree.Name, Is.EqualTo("3"));
Assert.That(playerThree.Alias, Is.EqualTo("Three"));
}

[Test]
public void Should_add_games_from_connected_packet()
{
var socket = Substitute.For<IArchipelagoSocketHelper>();

var sut = new PlayerHelper(socket);

var connectedPacket = new ConnectedPacket {
Players = new[] {
new NetworkPlayer { Name = "1", Alias = "One", Slot = 1, Team = 1 },
new NetworkPlayer { Name = "2", Alias = "Two", Slot = 2, Team = 1 },
new NetworkPlayer { Name = "3", Alias = "Three", Slot = 1, Team = 2 },
},
SlotInfo = new Dictionary<int, NetworkSlot> {
{ 1, new NetworkSlot { Type = SlotType.Player, Game = "Game1" } },
{ 2, new NetworkSlot { Type = SlotType.Player, Game = "Game2" } }
}
};

socket.PacketReceived += Raise.Event<ArchipelagoSocketHelper.PacketReceivedHandler>(connectedPacket);

var playerOne = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 1);
Assert.That(playerOne.Game, Is.EqualTo("Game1"));

var playerTwo = sut.AllPlayers.First(p => p.Slot == 2 && p.Team == 1);
Assert.That(playerTwo.Game, Is.EqualTo("Game2"));

var playerThree = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 2);
Assert.That(playerThree.Game, Is.EqualTo("Game1"));
}

[Test]
public void Should_add_groups_to_players_from_connected_packet()
{
var socket = Substitute.For<IArchipelagoSocketHelper>();

var sut = new PlayerHelper(socket);

var connectedPacket = new ConnectedPacket
{
Players = new[] {
new NetworkPlayer { Name = "1", Alias = "One", Slot = 1, Team = 1 },
new NetworkPlayer { Name = "2", Alias = "Two", Slot = 2, Team = 1 },
new NetworkPlayer { Name = "3", Alias = "Three", Slot = 3, Team = 1 },
},
SlotInfo = new Dictionary<int, NetworkSlot> {
{ 1, new NetworkSlot { Type = SlotType.Player, Game = "Game1" } },
{ 2, new NetworkSlot { Type = SlotType.Player, Game = "Game2" } },
{ 3, new NetworkSlot { Type = SlotType.Player, Game = "Game1" } },
{ 4, new NetworkSlot { Type = SlotType.Group, Game = "Game1",
Name = "Player3Personal", GroupMembers = new []{ 3 }} },
{ 5, new NetworkSlot { Type = SlotType.Group, Game = "Game1",
Name = "Game1All", GroupMembers = new []{ 1,3 }} },
}
};

socket.PacketReceived += Raise.Event<ArchipelagoSocketHelper.PacketReceivedHandler>(connectedPacket);

var playerOne = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 1);
Assert.That(playerOne.Groups.Length, Is.EqualTo(1));
var playerOneGroup = playerOne.Groups.First();
Assert.That(playerOneGroup.Name, Is.EqualTo("Game1All"));
Assert.That(playerOneGroup.Game, Is.EqualTo("Game1"));
Assert.That(playerOneGroup.GroupMembers, Is.EquivalentTo(new []{ 1, 3 }));

var playerTwo = sut.AllPlayers.First(p => p.Slot == 2 && p.Team == 1);
Assert.That(playerTwo.Groups, Is.Empty);

var playerThree = sut.AllPlayers.First(p => p.Slot == 3 && p.Team == 1);
Assert.That(playerThree.Groups.Length, Is.EqualTo(2));
var playerThreeGroupGame1All = playerThree.Groups.First(g => g.Name == "Game1All");
Assert.That(playerThreeGroupGame1All.Game, Is.EqualTo("Game1"));
Assert.That(playerThreeGroupGame1All.GroupMembers, Is.EquivalentTo(new[] { 1, 3 }));
var playerThreeGroupPersonal = playerThree.Groups.First(g => g.Name == "Player3Personal");
Assert.That(playerThreeGroupPersonal.Game, Is.EqualTo("Game1"));
Assert.That(playerThreeGroupPersonal.GroupMembers, Is.EquivalentTo(new[] { 3 }));
}

[Test]
public void Should_update_info_from_room_updated_packet()
{
var socket = Substitute.For<IArchipelagoSocketHelper>();

var sut = new PlayerHelper(socket);

var connectedPacket = new ConnectedPacket
{
Players = new[] {
new NetworkPlayer { Name = "1", Alias = "One", Slot = 1, Team = 1 },
new NetworkPlayer { Name = "2", Alias = "Two", Slot = 1, Team = 2 },
}
};

socket.PacketReceived += Raise.Event<ArchipelagoSocketHelper.PacketReceivedHandler>(connectedPacket);

var playerOne = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 1);
Assert.That(playerOne.Name, Is.EqualTo("1"));
Assert.That(playerOne.Alias, Is.EqualTo("One"));

var playerTwo = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 2);
Assert.That(playerTwo.Name, Is.EqualTo("2"));
Assert.That(playerTwo.Alias, Is.EqualTo("Two"));

var roomInfoUpdatedPacket = new RoomUpdatePacket {
Players = new[] {
new NetworkPlayer { Name = "Henk", Alias = "Terminator", Slot = 1, Team = 1 },
new NetworkPlayer { Name = "Frank", Alias = "Destroyer", Slot = 2, Team = 1 },
}
};

socket.PacketReceived += Raise.Event<ArchipelagoSocketHelper.PacketReceivedHandler>(roomInfoUpdatedPacket);

var playerOneUpdated = sut.AllPlayers.First(p => p.Slot == 1 && p.Team == 1);
Assert.That(playerOneUpdated.Name, Is.EqualTo("Henk"));
Assert.That(playerOneUpdated.Alias, Is.EqualTo("Terminator"));

var playerTwoUpdated = sut.AllPlayers.First(p => p.Slot == 2 && p.Team == 1);
Assert.That(playerTwoUpdated.Name, Is.EqualTo("Frank"));
Assert.That(playerTwoUpdated.Alias, Is.EqualTo("Destroyer"));
}

[Test]
public void Should_not_crash_when_room_update_does_not_contain_players()
{
var socket = Substitute.For<IArchipelagoSocketHelper>();

var sut = new PlayerHelper(socket);

var connectedPacket = new ConnectedPacket
{
Players = new[] {
new NetworkPlayer { Name = "1", Alias = "One", Slot = 1, Team = 1 },
}
};

socket.PacketReceived += Raise.Event<ArchipelagoSocketHelper.PacketReceivedHandler>(connectedPacket);

var roomInfoUpdatedPacket = new RoomUpdatePacket();

Assert.DoesNotThrow(() => {
socket.PacketReceived += Raise.Event<ArchipelagoSocketHelper.PacketReceivedHandler>(roomInfoUpdatedPacket);
});

Assert.That(sut.AllPlayers.Count, Is.EqualTo(1));
}
}
}
66 changes: 23 additions & 43 deletions Archipelago.MultiClient.Net/Helpers/PlayerHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Archipelago.MultiClient.Net.Models;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Models;
using Archipelago.MultiClient.Net.Packets;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

Expand All @@ -12,9 +14,9 @@ public class PlayerHelper
/// <summary>
/// A collection of PlayerInfo's where the index is the player their slot
/// </summary>
public ReadOnlyCollection<PlayerInfo> AllPlayers => new ReadOnlyCollection<PlayerInfo>(players);
public ReadOnlyCollection<PlayerInfo> AllPlayers => new ReadOnlyCollection<PlayerInfo>(players ?? new PlayerInfo[0]);

internal PlayerHelper(ArchipelagoSocketHelper socket)
internal PlayerHelper(IArchipelagoSocketHelper socket)
{
socket.PacketReceived += PacketReceived;
}
Expand Down Expand Up @@ -82,62 +84,39 @@ private void PacketReceived(ArchipelagoPacketBase packet)
switch (packet)
{
case ConnectedPacket connectedPacket:
OnConnectedPacketReceived(connectedPacket);
CreatePlayerInfo(connectedPacket.Players, connectedPacket.SlotInfo);
break;
case RoomUpdatePacket roomUpdatePacket:
OnRoomUpdatedPacketReceived(roomUpdatePacket);
UpdatePlayerInfo(roomUpdatePacket.Players);
break;
case RoomInfoPacket roomInfoPacket:
OnRoomInfoPacketReceived(roomInfoPacket);
break;
}
}

private void OnRoomInfoPacketReceived(RoomInfoPacket packet)
{
UpdateGames(packet.Games);
}

private void OnConnectedPacketReceived(ConnectedPacket packet)
{
UpdatePlayerInfo(packet.Players);
}

private void OnRoomUpdatedPacketReceived(RoomUpdatePacket packet)
{
if (packet.Players != null && packet.Players.Length > 0)
{
UpdatePlayerInfo(packet.Players);
}
}

private void UpdateGames(string[] games)
private void CreatePlayerInfo(NetworkPlayer[] networkPlayers, Dictionary<int, NetworkSlot> slotInfos)
{
if (players == null)
NetworkSlot[] groups;
if (slotInfos == null)
{
players = games.Select(g => new PlayerInfo { Game = g }).ToArray();
groups = new NetworkSlot[0];
}
else
{
for (int i = 0; i < games.Length; i++)
{
players[i].Game = games[i];
}
groups = slotInfos.Values.Where(s => s.Type == SlotType.Group).ToArray();
}

players = networkPlayers.Select(p => new PlayerInfo {
Team = p.Team,
Slot = p.Slot,
Name = p.Name,
Alias = p.Alias,
Game = slotInfos?[p.Slot].Game,
Groups = groups.Where(g => g.GroupMembers.Contains(p.Slot)).ToArray()
}).ToArray();
}

private void UpdatePlayerInfo(NetworkPlayer[] networkPlayers)
{
if (players == null)
{
players = networkPlayers.Select(p => new PlayerInfo {
Team = p.Team,
Slot = p.Slot,
Name = p.Name,
Alias = p.Alias
}).ToArray();
}
else
if (networkPlayers != null && networkPlayers.Length > 0)
{
for (int i = 0; i < networkPlayers.Length; i++)
{
Expand All @@ -157,5 +136,6 @@ public class PlayerInfo
public string Alias { get; internal set; }
public string Name { get; internal set; }
public string Game { get; internal set; }
public NetworkSlot[] Groups { get; internal set; }
}
}
5 changes: 2 additions & 3 deletions Archipelago.MultiClient.Net/Models/NetworkSlot.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Enums;
using Newtonsoft.Json;

namespace Archipelago.MultiClient.Net.Models
Expand All @@ -13,6 +12,6 @@ public struct NetworkSlot
[JsonProperty("type")]
public SlotType Type { get; set; }
[JsonProperty("group_members")]
public List<int> GroupMembers { get; set; }
public int[] GroupMembers { get; set; }
}
}

0 comments on commit abe2766

Please sign in to comment.