diff --git a/src/Prospect.Server.Api/Config/DatabaseSettings.cs b/src/Prospect.Server.Api/Config/DatabaseSettings.cs new file mode 100644 index 0000000..d26435a --- /dev/null +++ b/src/Prospect.Server.Api/Config/DatabaseSettings.cs @@ -0,0 +1,8 @@ +namespace Prospect.Server.Api.Config +{ + public class DatabaseSettings + { + public string ConnectionString { get; set; } + public string DatabaseName { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Config/PlayFabSettings.cs b/src/Prospect.Server.Api/Config/PlayFabSettings.cs new file mode 100644 index 0000000..7c39d6b --- /dev/null +++ b/src/Prospect.Server.Api/Config/PlayFabSettings.cs @@ -0,0 +1,8 @@ +namespace Prospect.Server.Api.Config +{ + public class PlayFabSettings + { + public string PublisherId { get; set; } + public string TitleId { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Controllers/ClientController.cs b/src/Prospect.Server.Api/Controllers/ClientController.cs index a010d3b..9de7d96 100644 --- a/src/Prospect.Server.Api/Controllers/ClientController.cs +++ b/src/Prospect.Server.Api/Controllers/ClientController.cs @@ -1,8 +1,13 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Prospect.Server.Api.Config; using Prospect.Server.Api.Models.Client; using Prospect.Server.Api.Models.Client.Data; +using Prospect.Server.Api.Services.Database; +using Prospect.Server.Api.Services.Database.Models; using Prospect.Server.Steam; namespace Prospect.Server.Api.Controllers @@ -11,22 +16,30 @@ namespace Prospect.Server.Api.Controllers [ApiController] public class ClientController : Controller { + private readonly PlayFabSettings _settings; + private readonly DbUserService _userService; + private readonly DbEntityService _entityService; + + public ClientController(IOptions settings, DbUserService userService, DbEntityService entityService) + { + _settings = settings.Value; + _userService = userService; + _entityService = entityService; + } + [HttpPost("LoginWithSteam")] [Produces("application/json")] - public IActionResult LoginWithSteam(ClientLoginWithSteamRequest request) + public async Task LoginWithSteam(ClientLoginWithSteamRequest request) { - var playerIdOne = "AAAABBBBCCCCDDDD"; // PlayFabId - var playerIdTwo = "0000111122223333"; // EntityId - var playerName = "AeonLucid"; - - var titleId = "A22AB"; - var publisherId = "850902E5B40508ED"; - if (!string.IsNullOrEmpty(request.SteamTicket)) { var ticket = AppTicket.Parse(request.SteamTicket); if (ticket.IsValid && ticket.HasValidSignature) { + var userSteamId = ticket.SteamId.ToString(); + var user = await _userService.FindOrCreateAsync(PlayFabUserAuthType.Steam, userSteamId); + var entity = await _entityService.FindOrCreateAsync(user.Id); + return Ok(new ClientResponse { Code = 200, @@ -37,7 +50,7 @@ namespace Prospect.Server.Api.Controllers { Entity = new FEntityKey { - Id = playerIdTwo, + Id = entity.Id, Type = "title_player_account", TypeString = "title_player_account" }, @@ -49,10 +62,10 @@ namespace Prospect.Server.Api.Controllers CharacterInventories = new List(), PlayerProfile = new FPlayerProfileModel { - DisplayName = playerName, - PlayerId = playerIdOne, - PublisherId = publisherId, - TitleId = titleId + DisplayName = user.DisplayName, + PlayerId = user.Id, + PublisherId = _settings.PublisherId, + TitleId = _settings.TitleId }, UserDataVersion = 0, UserInventory = new List(), @@ -60,7 +73,7 @@ namespace Prospect.Server.Api.Controllers }, LastLoginTime = DateTime.UtcNow, NewlyCreated = false, - PlayFabId = playerIdOne, + PlayFabId = user.Id, SessionTicket = "SOME", SettingsForUser = new FUserSettings { diff --git a/src/Prospect.Server.Api/Prospect.Server.Api.csproj b/src/Prospect.Server.Api/Prospect.Server.Api.csproj index ead6723..ba7bc00 100644 --- a/src/Prospect.Server.Api/Prospect.Server.Api.csproj +++ b/src/Prospect.Server.Api/Prospect.Server.Api.csproj @@ -5,6 +5,7 @@ + diff --git a/src/Prospect.Server.Api/Services/Database/BaseDbService.cs b/src/Prospect.Server.Api/Services/Database/BaseDbService.cs new file mode 100644 index 0000000..f4235d4 --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/BaseDbService.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using Prospect.Server.Api.Config; + +namespace Prospect.Server.Api.Services.Database +{ + public abstract class BaseDbService + { + protected BaseDbService(IOptions options, string collection) + { + var settings = options.Value; + var client = new MongoClient(settings.ConnectionString); + var database = client.GetDatabase(settings.DatabaseName); + + Collection = database.GetCollection(collection); + } + + protected IMongoCollection Collection { get; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Database/DbEntityService.cs b/src/Prospect.Server.Api/Services/Database/DbEntityService.cs new file mode 100644 index 0000000..78eab9b --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/DbEntityService.cs @@ -0,0 +1,38 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using Prospect.Server.Api.Config; +using Prospect.Server.Api.Services.Database.Models; + +namespace Prospect.Server.Api.Services.Database +{ + public class DbEntityService : BaseDbService + { + public DbEntityService(IOptions settings) : base(settings, nameof(DbEntityService)) + { + } + + public async Task FindAsync(string userId) + { + return await Collection.Find(user => user.UserId == userId).FirstOrDefaultAsync(); + } + + private async Task CreateAsync(string userId) + { + var user = new PlayFabEntity + { + UserId = userId + }; + + await Collection.InsertOneAsync(user); + + return user; + } + + public async Task FindOrCreateAsync(string userId) + { + return await FindAsync(userId) ?? + await CreateAsync(userId); + } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Database/DbUserService.cs b/src/Prospect.Server.Api/Services/Database/DbUserService.cs new file mode 100644 index 0000000..fb969be --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/DbUserService.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using Prospect.Server.Api.Config; +using Prospect.Server.Api.Services.Database.Models; + +namespace Prospect.Server.Api.Services.Database +{ + public class DbUserService : BaseDbService + { + public DbUserService(IOptions settings) : base(settings, nameof(PlayFabUser)) + { + } + + public async Task FindAsync(PlayFabUserAuthType type, string key) + { + return await Collection.Find(user => user.Auth.Any(auth => + auth.Type == type && + auth.Key == key)).FirstOrDefaultAsync(); + } + + private async Task CreateAsync(PlayFabUserAuthType type, string key) + { + var user = new PlayFabUser + { + DisplayName = "Unknown", + Auth = new List + { + new PlayFabUserAuth + { + Type = type, + Key = key + } + } + }; + + await Collection.InsertOneAsync(user); + + return user; + } + + public async Task FindOrCreateAsync(PlayFabUserAuthType type, string key) + { + return await FindAsync(type, key) ?? + await CreateAsync(type, key); + } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Database/Generator/PlayFabIdGenerator.cs b/src/Prospect.Server.Api/Services/Database/Generator/PlayFabIdGenerator.cs new file mode 100644 index 0000000..04f18be --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/Generator/PlayFabIdGenerator.cs @@ -0,0 +1,23 @@ +using System; +using System.Security.Cryptography; +using MongoDB.Bson.Serialization; + +namespace Prospect.Server.Api.Services.Database.Generator +{ + public class PlayFabIdGenerator : IIdGenerator + { + private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider(); + + public object GenerateId(object container, object document) + { + Span data = stackalloc byte[8]; + Random.GetBytes(data); + return Convert.ToHexString(data); + } + + public bool IsEmpty(object id) + { + return id is not string idStr || string.IsNullOrEmpty(idStr); + } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Database/Models/PlayFabEntity.cs b/src/Prospect.Server.Api/Services/Database/Models/PlayFabEntity.cs new file mode 100644 index 0000000..740f8d0 --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/Models/PlayFabEntity.cs @@ -0,0 +1,18 @@ +using MongoDB.Bson.Serialization.Attributes; +using Prospect.Server.Api.Services.Database.Generator; + +namespace Prospect.Server.Api.Services.Database.Models +{ + /// + /// title_player_account + /// + public class PlayFabEntity + { + [BsonId(IdGenerator = typeof(PlayFabIdGenerator))] + public string Id { get; set; } + + [BsonRequired] + [BsonElement("UserId")] + public string UserId { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Database/Models/PlayFabUser.cs b/src/Prospect.Server.Api/Services/Database/Models/PlayFabUser.cs new file mode 100644 index 0000000..3cff796 --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/Models/PlayFabUser.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MongoDB.Bson.Serialization.Attributes; +using Prospect.Server.Api.Services.Database.Generator; + +namespace Prospect.Server.Api.Services.Database.Models +{ + /// + /// master_player_account + /// + public class PlayFabUser + { + [BsonId(IdGenerator = typeof(PlayFabIdGenerator))] + public string Id { get; set; } + + [BsonRequired] + [BsonElement("DisplayName")] + public string DisplayName { get; set; } + + [BsonRequired] + [BsonElement("Auth")] + public List Auth { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Database/Models/PlayFabUserAuth.cs b/src/Prospect.Server.Api/Services/Database/Models/PlayFabUserAuth.cs new file mode 100644 index 0000000..6ee6008 --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/Models/PlayFabUserAuth.cs @@ -0,0 +1,13 @@ +using MongoDB.Bson.Serialization.Attributes; + +namespace Prospect.Server.Api.Services.Database.Models +{ + public class PlayFabUserAuth + { + [BsonElement("Type")] + public PlayFabUserAuthType Type { get; set; } + + [BsonElement("Key")] + public string Key { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Database/Models/PlayFabUserAuthType.cs b/src/Prospect.Server.Api/Services/Database/Models/PlayFabUserAuthType.cs new file mode 100644 index 0000000..479dfd4 --- /dev/null +++ b/src/Prospect.Server.Api/Services/Database/Models/PlayFabUserAuthType.cs @@ -0,0 +1,8 @@ +namespace Prospect.Server.Api.Services.Database.Models +{ + public enum PlayFabUserAuthType + { + Epic, + Steam + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Startup.cs b/src/Prospect.Server.Api/Startup.cs index 7cee2ae..234eba9 100644 --- a/src/Prospect.Server.Api/Startup.cs +++ b/src/Prospect.Server.Api/Startup.cs @@ -1,16 +1,32 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Prospect.Server.Api.Config; using Prospect.Server.Api.Middleware; +using Prospect.Server.Api.Services.Database; using Serilog; namespace Prospect.Server.Api { public class Startup { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + private IConfiguration Configuration { get; } + public void ConfigureServices(IServiceCollection services) { + services.Configure(Configuration.GetSection(nameof(DatabaseSettings))); + services.Configure(Configuration.GetSection(nameof(PlayFabSettings))); + + services.AddSingleton(); + services.AddSingleton(); + services.AddControllers(); } diff --git a/src/Prospect.Server.Api/appsettings.json b/src/Prospect.Server.Api/appsettings.json index 2c63c08..a487048 100644 --- a/src/Prospect.Server.Api/appsettings.json +++ b/src/Prospect.Server.Api/appsettings.json @@ -1,2 +1,10 @@ { + "DatabaseSettings": { + "ConnectionString": "mongodb://localhost:27017", + "DatabaseName": "ProspectDb" + }, + "PlayFabSettings": { + "PublisherId": "850902E5B40508ED", + "TitleId": "A22AB" + } } diff --git a/src/Prospect.sln.DotSettings b/src/Prospect.sln.DotSettings index 6f47746..a0c00fe 100644 --- a/src/Prospect.sln.DotSettings +++ b/src/Prospect.sln.DotSettings @@ -3,4 +3,5 @@ SDK <NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> <NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> - <NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> \ No newline at end of file + <NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> + PF \ No newline at end of file