Skip to content

Commit

Permalink
90 list players by ip (#92)
Browse files Browse the repository at this point in the history
* Fix #90 by implementing list players command
* Updated docs to represent the change
  • Loading branch information
Annonator authored Jul 8, 2022
1 parent c89930e commit 54ef701
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 8 deletions.
68 changes: 68 additions & 0 deletions src/PlayFabBuddy.Cli/Commands/Player/ListPlayersCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Net;
using PlayFabBuddy.Lib.Aggregate;
using PlayFabBuddy.Lib.Interfaces.Adapter;
using PlayFabBuddy.Lib.UseCases.Player;
using Spectre.Console;
using Spectre.Console.Cli;

namespace PlayFabBuddy.Cli.Commands.Player;

public class ListPlayersCommand : AsyncCommand<ListPlayersCommandSettings>
{
private readonly IDataExplorerAdapter _dataAdapter;
private readonly IPlayerAccountAdapter _playerAdapter;

public ListPlayersCommand(IDataExplorerAdapter dataAdapter, IPlayerAccountAdapter playerAccountAdapter)
{
_dataAdapter = dataAdapter;
_playerAdapter = playerAccountAdapter;
}

public async override Task<int> ExecuteAsync(CommandContext context, ListPlayersCommandSettings settings)
{
var getPlayerUsecase =
new GetPlayersByIpUseCAse(_dataAdapter, _playerAdapter, IPAddress.Parse(settings.IpAddress));

var playerList = new List<MasterPlayerAccountAggregate>();

await AnsiConsole.Status()
.Spinner(Spinner.Known.Star)
.Start("Searching for Players...", async ctx =>
{
playerList = await getPlayerUsecase.ExecuteAsync();
});

if (playerList.Count == 0)
{
AnsiConsole.MarkupLine("No Player for given IP have been found.");
return 0;
}

var playerTable = new Table();
// Add some columns
playerTable.AddColumn("Master Id");
playerTable.AddColumn("Last Known IP");
playerTable.AddColumn("TitlePlayerData");

foreach (var aggregate in playerList)
{
var titlePlayer = "";

if (aggregate.MasterPlayerAccount.PlayerAccounts != null)
{
foreach (var playerAccount in aggregate.MasterPlayerAccount.PlayerAccounts)
{
titlePlayer += "ID: " + playerAccount.Id + "\n ";
}
}

playerTable.AddRow(new Text(aggregate.MasterPlayerAccount.Id!),
new Text(aggregate.MasterPlayerAccount.LastKnownIp!),
new Text(titlePlayer));
}

AnsiConsole.Write(playerTable);

return 0;
}
}
23 changes: 23 additions & 0 deletions src/PlayFabBuddy.Cli/Commands/Player/ListPlayersCommandSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.ComponentModel;
using System.Net;
using Spectre.Console;
using Spectre.Console.Cli;

namespace PlayFabBuddy.Cli.Commands.Player;

public class ListPlayersCommandSettings : PlayerSettings
{
[Description("The IP Address to find Players by")]
[CommandArgument(0, "[IPAddress]")]
public string IpAddress { get; set; } = "";

public override ValidationResult Validate()
{
if (IpAddress.Length == 0 && !IPAddress.TryParse(IpAddress, out _))
{
return ValidationResult.Error("Please provide a valid IPAddress");
}

return ValidationResult.Success();
}
}
1 change: 1 addition & 0 deletions src/PlayFabBuddy.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public async static Task<int> Main(string[] args)
players.AddCommand<CreateNewPlayersCommand>("create");
players.AddCommand<DeletePlayersCommand>("delete");
players.AddCommand<LoginPlayerCommand>("login");
players.AddCommand<ListPlayersCommand>("list");
});
configurator.AddBranch<MatchmakingSettings>("matchmaking", matchmaking =>
{
Expand Down
12 changes: 12 additions & 0 deletions src/PlayFabBuddy.Cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ As `segment name`, you may use one of the pre-defined segment names:

> Please note: Due to the nature of how segments work, you may need to execute this action multiple times to make sure to really delete all players in the given segment.
### List

Running this will connect to your PlayFab Advanced Analytics cluster and run some query's on it to give your a list with all players using a specific IP.
Before you can run this query you need to setup your PlayFab to enable PlayFabBuddy to access the data.

1. Create a Azure Active Director Application Like: https://docs.microsoft.com/en-us/gaming/playfab/features/insights/connectivity/creating-aad-app-for-insights
2. Configure Client ID and Client Secret for PlayFab Buddy to use
3. Run the command


dotnet run -- players list <IP_ADDRESS>

## Matchmaking
Manages Matchmaking Queues

Expand Down
6 changes: 5 additions & 1 deletion src/PlayFabBuddy.Cli/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@
"titleId": "<YourTitleId>",
"devSecret": "<YourDevSecret>",
"concurrent": "10",
"defaultSavePath": "MasterAccountOutput.json"
"defaultSavePath": "MasterAccountOutput.json",
"PFDataCluster": "https://insights.playfab.com",
"PFDataClientId": "",
"PFDataClientSecret": "",
"AzureAuthority": "microsoft.onmicrosoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Net;
using System.Text.Json;
using Kusto.Data.Common;
using PlayFabBuddy.Lib.Entities.Accounts;
using PlayFabBuddy.Lib.Interfaces.Adapter;

namespace PlayFabBuddy.Infrastructure.Adapter.PlayFab.Analytics;

public class DataExplorerAdapter : IDataExplorerAdapter
{
private readonly ICslQueryProvider _kustoQueryProvider;

public DataExplorerAdapter(ICslQueryProvider kustoQueryProvider)
{
_kustoQueryProvider = kustoQueryProvider;
}

public Task<List<MasterPlayerAccountEntity>> GetPlayersByIp(IPAddress ip)
{
var query = "['events.all'] " +
"| where FullName_Name == 'player_logged_in' " +
"| where EventData.IPV4Address == '" + ip + "'";
var clientRequestProperties = new ClientRequestProperties {
Application = "PlayFabBuddy", ClientRequestId = Guid.NewGuid().ToString()
};

var entityList = new List<MasterPlayerAccountEntity>();

using (var reader = _kustoQueryProvider.ExecuteQuery(query, clientRequestProperties))
{
while (reader.Read())
{
var rawObjectData = reader.GetValue(6);

var eventData = JsonSerializer.Deserialize<EventData>(rawObjectData.ToString() ?? string.Empty);

if (eventData != null)
{
entityList.Add(new MasterPlayerAccountEntity {
Id = eventData.EntityId, LastKnownIp = eventData.IPV4Address
});
}
}
}

return Task.FromResult(entityList);
}

private class EventData
{
public PlayFabEnvironment? PlayFabEnvironment { get; set; }
public string? EventNamespace { get; set; }
public DateTime? Timestamp { get; set; }
public string? EntityType { get; set; }
public string? SourceType { get; set; }
public string? EventName { get; set; }
public string? EntityId { get; set; }
public string? EventId { get; set; }
public string? TitleId { get; set; }
public string? Source { get; set; }
public string? PlatformUserId { get; set; }
public List<object>? ExperimentVariants { get; set; }
public string? Platform { get; set; }
public string? IPV4Address { get; set; }
public Location? Location { get; set; }
}

private class Location
{
public string? ContinentCode { get; set; }
public string? CountryCode { get; set; }
public double? Longitude { get; set; }
public double? Latitude { get; set; }
public string? City { get; set; }
}
private class PlayFabEnvironment
{
public string? Application { get; set; }
public string? Vertical { get; set; }
public string? Commit { get; set; }
public string? Cloud { get; set; }
}
}
25 changes: 18 additions & 7 deletions src/PlayFabBuddy.Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using Microsoft.Extensions.Configuration;
using Kusto.Data;
using Kusto.Data.Common;
using Kusto.Data.Net.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PlayFab;
using PlayFabBuddy.Infrastructure.Adapter.PlayFab;
using PlayFabBuddy.Infrastructure.Adapter.PlayFab.Admin;
using PlayFabBuddy.Infrastructure.Adapter.PlayFab.Analytics;
using PlayFabBuddy.Infrastructure.Config;
using PlayFabBuddy.Infrastructure.Repositories;
using PlayFabBuddy.Lib.Aggregate;
Expand All @@ -28,11 +32,8 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi
var pfConfig = new PlayFabConfig(config["titleId"], config["devSecret"]);
var adminEntityToken = pfConfig.InitAsync().Result;

var playFabApiSettings = new PlayFabApiSettings
{
TitleId = config["titleId"],
DeveloperSecretKey = config["devSecret"]
};
var playFabApiSettings =
new PlayFabApiSettings { TitleId = config["titleId"], DeveloperSecretKey = config["devSecret"] };

services.AddSingleton<IConfig>(pfConfig);
services.AddSingleton(playFabApiSettings);
Expand All @@ -48,6 +49,16 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi
// Policy
services.AddTransient<IPolicyAdapter, PolicyAdapter>();

// Register KUSTO
var kustoConnectionString = new KustoConnectionStringBuilder(config["PFDataCluster"], config["titleId"])
.WithAadApplicationKeyAuthentication(config["PFDataClientId"], config["PFDataClientSecret"],
config["AzureAuthority"]);


services.AddTransient<ICslQueryProvider>(sp =>
KustoClientFactory.CreateCslQueryProvider(kustoConnectionString));
services.AddTransient<IDataExplorerAdapter, DataExplorerAdapter>();

return services;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Kusto.Data" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="PlayFabAllSDK" Version="1.108.220118" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ public class MasterPlayerAccountEntity
public string? Id { get; set; }
//Let's work with only a customId for now, re-evaluate when other authentications get important
public string? CustomId { get; set; }

public string? LastKnownIp { get; set; }
}
10 changes: 10 additions & 0 deletions src/PlayFabBuddy.Lib/Interfaces/Adapter/IDataExplorerAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net;
using PlayFabBuddy.Lib.Aggregate;
using PlayFabBuddy.Lib.Entities.Accounts;

namespace PlayFabBuddy.Lib.Interfaces.Adapter;

public interface IDataExplorerAdapter
{
public Task<List<MasterPlayerAccountEntity>> GetPlayersByIp(IPAddress ip);
}
35 changes: 35 additions & 0 deletions src/PlayFabBuddy.Lib/UseCases/Player/GetPlayersByIpUseCAse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Net;
using PlayFabBuddy.Lib.Aggregate;
using PlayFabBuddy.Lib.Interfaces.Adapter;

namespace PlayFabBuddy.Lib.UseCases.Player;

public class GetPlayersByIpUseCAse : UseCase<List<MasterPlayerAccountAggregate>>
{
private readonly IDataExplorerAdapter _dataExplorerAdapter;
private readonly IPAddress _ip;
private readonly IPlayerAccountAdapter _playerAccountAdapter;

public GetPlayersByIpUseCAse(IDataExplorerAdapter dataExplorerAdapter, IPlayerAccountAdapter playerAccountAdapter,
IPAddress ip)
{
_dataExplorerAdapter = dataExplorerAdapter;
_playerAccountAdapter = playerAccountAdapter;
_ip = ip;
}

public async override Task<List<MasterPlayerAccountAggregate>> ExecuteAsync(IProgress<double>? progress = null)
{
var entityList = await _dataExplorerAdapter.GetPlayersByIp(_ip);

var aggregateList = new List<MasterPlayerAccountAggregate>();

foreach (var masterPlayerEntity in entityList)
{
var masterAggregate = new MasterPlayerAccountAggregate(masterPlayerEntity);
aggregateList.Add(await _playerAccountAdapter.GetTitleAccountsAndCustomId(masterAggregate));
}

return aggregateList;
}
}

0 comments on commit 54ef701

Please sign in to comment.