Convert to new namespace stuff and implicit usings

This commit is contained in:
AeonLucid
2022-01-07 06:07:05 +01:00
parent ce2ea7f295
commit 2f01cf7411
85 changed files with 2538 additions and 2696 deletions
+43 -44
View File
@@ -3,54 +3,53 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Prospect.Launcher.Invoke; using Prospect.Launcher.Invoke;
namespace Prospect.Launcher namespace Prospect.Launcher;
public static class Inject
{ {
public static class Inject const int PROCESS_CREATE_THREAD = 0x0002;
const int PROCESS_QUERY_INFORMATION = 0x0400;
const int PROCESS_VM_OPERATION = 0x0008;
const int PROCESS_VM_WRITE = 0x0020;
const int PROCESS_VM_READ = 0x0010;
const uint MEM_COMMIT = 0x00001000;
const uint MEM_RESERVE = 0x00002000;
const uint PAGE_READWRITE = 4;
// https://codingvision.net/c-inject-a-dll-into-a-process-w-createremotethread
public static unsafe bool Library(uint processId, string path)
{ {
const int PROCESS_CREATE_THREAD = 0x0002; IntPtr procHandle = Kernel32.OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, (int)processId);
const int PROCESS_QUERY_INFORMATION = 0x0400;
const int PROCESS_VM_OPERATION = 0x0008;
const int PROCESS_VM_WRITE = 0x0020;
const int PROCESS_VM_READ = 0x0010;
const uint MEM_COMMIT = 0x00001000; var loadLibraryAddr = Kernel32.GetProcAddress(Kernel32.GetModuleHandle("kernel32.dll"), "LoadLibraryA");
const uint MEM_RESERVE = 0x00002000; if (loadLibraryAddr == IntPtr.Zero)
const uint PAGE_READWRITE = 4;
// https://codingvision.net/c-inject-a-dll-into-a-process-w-createremotethread
public static unsafe bool Library(uint processId, string path)
{ {
IntPtr procHandle = Kernel32.OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, (int)processId); Console.WriteLine("Failed GetProcAddress.");
return false;
var loadLibraryAddr = Kernel32.GetProcAddress(Kernel32.GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (loadLibraryAddr == IntPtr.Zero)
{
Console.WriteLine("Failed GetProcAddress.");
return false;
}
var allocMemAddress = Kernel32.VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((path.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (allocMemAddress == IntPtr.Zero)
{
Console.WriteLine("Failed VirtualAllocEx.");
return false;
}
var libraryBytes = Encoding.Default.GetBytes(path);
fixed (byte* pLibraryBytes = libraryBytes)
{
Kernel32.WriteProcessMemory(procHandle, allocMemAddress, pLibraryBytes, libraryBytes.Length + 1, out _);
}
var threadHandle = Kernel32.CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero);
if (threadHandle == IntPtr.Zero)
{
Console.WriteLine("Failed CreateRemoteThread.");
return false;
}
return true;
} }
var allocMemAddress = Kernel32.VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((path.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (allocMemAddress == IntPtr.Zero)
{
Console.WriteLine("Failed VirtualAllocEx.");
return false;
}
var libraryBytes = Encoding.Default.GetBytes(path);
fixed (byte* pLibraryBytes = libraryBytes)
{
Kernel32.WriteProcessMemory(procHandle, allocMemAddress, pLibraryBytes, libraryBytes.Length + 1, out _);
}
var threadHandle = Kernel32.CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero);
if (threadHandle == IntPtr.Zero)
{
Console.WriteLine("Failed CreateRemoteThread.");
return false;
}
return true;
} }
} }
+38 -39
View File
@@ -2,52 +2,51 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Prospect.Launcher.Invoke.Structs; using Prospect.Launcher.Invoke.Structs;
namespace Prospect.Launcher.Invoke namespace Prospect.Launcher.Invoke;
internal static class Kernel32
{ {
internal static class Kernel32 [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
{ public static extern bool CreateProcess(string lpApplicationName,
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] string lpCommandLine, IntPtr lpProcessAttributes,
public static extern bool CreateProcess(string lpApplicationName, IntPtr lpThreadAttributes,
string lpCommandLine, IntPtr lpProcessAttributes, bool bInheritHandles, ProcessCreationFlags dwCreationFlags,
IntPtr lpThreadAttributes, IntPtr lpEnvironment, string lpCurrentDirectory,
bool bInheritHandles, ProcessCreationFlags dwCreationFlags, ref StartupInfo lpStartupInfo,
IntPtr lpEnvironment, string lpCurrentDirectory, out ProcessInformation lpProcessInformation);
ref StartupInfo lpStartupInfo,
out ProcessInformation lpProcessInformation);
public static bool CreateProcess( public static bool CreateProcess(
string lpApplicationName, string lpApplicationName,
string lpCommandLine, string lpCommandLine,
ProcessCreationFlags dwCreationFlags, ProcessCreationFlags dwCreationFlags,
ref StartupInfo lpStartupInfo, ref StartupInfo lpStartupInfo,
out ProcessInformation lpProcessInformation) => CreateProcess(lpApplicationName, lpCommandLine, IntPtr.Zero, out ProcessInformation lpProcessInformation) => CreateProcess(lpApplicationName, lpCommandLine, IntPtr.Zero,
IntPtr.Zero, false, dwCreationFlags, IntPtr.Zero, null, ref lpStartupInfo, out lpProcessInformation); IntPtr.Zero, false, dwCreationFlags, IntPtr.Zero, null, ref lpStartupInfo, out lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe bool WriteProcessMemory( public static extern unsafe bool WriteProcessMemory(
IntPtr hProcess, IntPtr hProcess,
IntPtr lpBaseAddress, IntPtr lpBaseAddress,
byte* lpBuffer, byte* lpBuffer,
Int32 nSize, Int32 nSize,
out IntPtr lpNumberOfBytesWritten out IntPtr lpNumberOfBytesWritten
); );
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName); public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
public static extern uint ResumeThread(IntPtr hThread); public static extern uint ResumeThread(IntPtr hThread);
}
} }
@@ -1,26 +1,25 @@
using System; using System;
namespace Prospect.Launcher.Invoke.Structs namespace Prospect.Launcher.Invoke.Structs;
[Flags]
internal enum ProcessCreationFlags : uint
{ {
[Flags] ZERO_FLAG = 0x00000000,
internal enum ProcessCreationFlags : uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
{ CREATE_DEFAULT_ERROR_MODE = 0x04000000,
ZERO_FLAG = 0x00000000, CREATE_NEW_CONSOLE = 0x00000010,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000, CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_DEFAULT_ERROR_MODE = 0x04000000, CREATE_NO_WINDOW = 0x08000000,
CREATE_NEW_CONSOLE = 0x00000010, CREATE_PROTECTED_PROCESS = 0x00040000,
CREATE_NEW_PROCESS_GROUP = 0x00000200, CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_NO_WINDOW = 0x08000000, CREATE_SEPARATE_WOW_VDM = 0x00001000,
CREATE_PROTECTED_PROCESS = 0x00040000, CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, CREATE_SUSPENDED = 0x00000004,
CREATE_SEPARATE_WOW_VDM = 0x00001000, CREATE_UNICODE_ENVIRONMENT = 0x00000400,
CREATE_SHARED_WOW_VDM = 0x00001000, DEBUG_ONLY_THIS_PROCESS = 0x00000002,
CREATE_SUSPENDED = 0x00000004, DEBUG_PROCESS = 0x00000001,
CREATE_UNICODE_ENVIRONMENT = 0x00000400, DETACHED_PROCESS = 0x00000008,
DEBUG_ONLY_THIS_PROCESS = 0x00000002, EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
DEBUG_PROCESS = 0x00000001, INHERIT_PARENT_AFFINITY = 0x00010000
DETACHED_PROCESS = 0x00000008,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
INHERIT_PARENT_AFFINITY = 0x00010000
}
} }
@@ -1,14 +1,13 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Prospect.Launcher.Invoke.Structs namespace Prospect.Launcher.Invoke.Structs;
[StructLayout(LayoutKind.Sequential)]
internal struct ProcessInformation
{ {
[StructLayout(LayoutKind.Sequential)] public IntPtr hProcess;
internal struct ProcessInformation public IntPtr hThread;
{ public uint dwProcessId;
public IntPtr hProcess; public uint dwThreadId;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
} }
@@ -1,28 +1,27 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Prospect.Launcher.Invoke.Structs namespace Prospect.Launcher.Invoke.Structs;
[StructLayout(LayoutKind.Sequential)]
internal struct StartupInfo
{ {
[StructLayout(LayoutKind.Sequential)] public uint cb;
internal struct StartupInfo public string lpReserved;
{ public string lpDesktop;
public uint cb; public string lpTitle;
public string lpReserved; public uint dwX;
public string lpDesktop; public uint dwY;
public string lpTitle; public uint dwXSize;
public uint dwX; public uint dwYSize;
public uint dwY; public uint dwXCountChars;
public uint dwXSize; public uint dwYCountChars;
public uint dwYSize; public uint dwFillAttribute;
public uint dwXCountChars; public uint dwFlags;
public uint dwYCountChars; public short wShowWindow;
public uint dwFillAttribute; public short cbReserved2;
public uint dwFlags; public IntPtr lpReserved2;
public short wShowWindow; public IntPtr hStdInput;
public short cbReserved2; public IntPtr hStdOutput;
public IntPtr lpReserved2; public IntPtr hStdError;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
} }
+65 -66
View File
@@ -4,75 +4,74 @@ using System.IO;
using Prospect.Launcher.Invoke; using Prospect.Launcher.Invoke;
using Prospect.Launcher.Invoke.Structs; using Prospect.Launcher.Invoke.Structs;
namespace Prospect.Launcher namespace Prospect.Launcher;
internal static class Program
{ {
internal static class Program /// <summary>
/// Steam AppId.
/// - https://steamdb.info/app/480/
/// - https://steamdb.info/app/1600360/
/// </summary>
private const string AppId = "480";
/// <summary>
/// Executable name of the game.
/// </summary>
private const string FileName = "Prospect-Win64-Shipping.exe";
private static void Main(string[] args)
{ {
/// <summary> if (args.Length != 1)
/// Steam AppId.
/// - https://steamdb.info/app/480/
/// - https://steamdb.info/app/1600360/
/// </summary>
private const string AppId = "480";
/// <summary>
/// Executable name of the game.
/// </summary>
private const string FileName = "Prospect-Win64-Shipping.exe";
private static void Main(string[] args)
{ {
if (args.Length != 1) Console.WriteLine("Usage: ./Prospect.Launcher <Game directory>");
{ return;
Console.WriteLine("Usage: ./Prospect.Launcher <Game directory>");
return;
}
var gamePath = Path.Combine(args[0], "Prospect", "Binaries", "Win64");
var gameBinary = Path.Combine(gamePath, FileName);
var gameArgs = new List<string>();
// 2EA46 = Default
// A22AB = The Cycle Playtest
gameArgs.Add("-empty");
gameArgs.Add("-log");
gameArgs.Add("-windowed");
gameArgs.Add("-noeac");
gameArgs.Add("-nointro");
gameArgs.Add("-steam_auth");
gameArgs.Add("PF_TITLEID=A22AB");
// Ensure "steam_appid.txt" exists, to fix steam authentication.
var steamAppId = Path.Combine(gamePath, "steam_appid.txt");
File.WriteAllText(steamAppId, AppId);
// Spawn.
var startupInfo = new StartupInfo();
if (!Kernel32.CreateProcess(gameBinary, string.Join(' ', gameArgs), ProcessCreationFlags.CREATE_SUSPENDED, ref startupInfo, out var processInfo))
{
Console.WriteLine("Failed to spawn game.");
return;
}
Console.WriteLine("Spawned.");
// Inject DLL.
var agentPath = Path.Combine(AppContext.BaseDirectory, "Prospect.Agent.dll");
if (!Inject.Library(processInfo.dwProcessId, agentPath))
{
Console.WriteLine("Failed to inject library.");
return;
}
Console.WriteLine("Injected.");
// Resume.
Kernel32.ResumeThread(processInfo.hThread);
Console.WriteLine("Resumed.");
} }
var gamePath = Path.Combine(args[0], "Prospect", "Binaries", "Win64");
var gameBinary = Path.Combine(gamePath, FileName);
var gameArgs = new List<string>();
// 2EA46 = Default
// A22AB = The Cycle Playtest
gameArgs.Add("-empty");
gameArgs.Add("-log");
gameArgs.Add("-windowed");
gameArgs.Add("-noeac");
gameArgs.Add("-nointro");
gameArgs.Add("-steam_auth");
gameArgs.Add("PF_TITLEID=A22AB");
// Ensure "steam_appid.txt" exists, to fix steam authentication.
var steamAppId = Path.Combine(gamePath, "steam_appid.txt");
File.WriteAllText(steamAppId, AppId);
// Spawn.
var startupInfo = new StartupInfo();
if (!Kernel32.CreateProcess(gameBinary, string.Join(' ', gameArgs), ProcessCreationFlags.CREATE_SUSPENDED, ref startupInfo, out var processInfo))
{
Console.WriteLine("Failed to spawn game.");
return;
}
Console.WriteLine("Spawned.");
// Inject DLL.
var agentPath = Path.Combine(AppContext.BaseDirectory, "Prospect.Agent.dll");
if (!Inject.Library(processInfo.dwProcessId, agentPath))
{
Console.WriteLine("Failed to inject library.");
return;
}
Console.WriteLine("Injected.");
// Resume.
Kernel32.ResumeThread(processInfo.hThread);
Console.WriteLine("Resumed.");
} }
} }
@@ -1,8 +1,7 @@
namespace Prospect.Server.Api.Config namespace Prospect.Server.Api.Config;
public class DatabaseSettings
{ {
public class DatabaseSettings public string ConnectionString { get; set; }
{ public string DatabaseName { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
} }
@@ -1,8 +1,7 @@
namespace Prospect.Server.Api.Config namespace Prospect.Server.Api.Config;
public class PlayFabSettings
{ {
public class PlayFabSettings public string PublisherId { get; set; }
{ public string TitleId { get; set; }
public string PublisherId { get; set; }
public string TitleId { get; set; }
}
} }
@@ -1,7 +1,4 @@
using System; using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Prospect.Server.Api.Config; using Prospect.Server.Api.Config;
@@ -15,256 +12,258 @@ using Prospect.Server.Api.Services.Database.Models;
using Prospect.Server.Api.Services.UserData; using Prospect.Server.Api.Services.UserData;
using Prospect.Server.Steam; using Prospect.Server.Steam;
namespace Prospect.Server.Api.Controllers namespace Prospect.Server.Api.Controllers;
[ApiController]
[Route("Client")]
public class ClientController : Controller
{ {
[ApiController] private const int AppIdDefault = 480;
[Route("Client")] private const int AppIdCycleBeta = 1600361;
public class ClientController : Controller
private readonly PlayFabSettings _settings;
private readonly AuthTokenService _authTokenService;
private readonly DbUserService _userService;
private readonly DbEntityService _entityService;
private readonly UserDataService _userDataService;
private readonly TitleDataService _titleDataService;
public ClientController(IOptions<PlayFabSettings> settings,
AuthTokenService authTokenService,
DbUserService userService,
DbEntityService entityService,
UserDataService userDataService,
TitleDataService titleDataService)
{ {
private readonly PlayFabSettings _settings; _settings = settings.Value;
private readonly AuthTokenService _authTokenService; _authTokenService = authTokenService;
private readonly DbUserService _userService; _userService = userService;
private readonly DbEntityService _entityService; _entityService = entityService;
private readonly UserDataService _userDataService; _userDataService = userDataService;
private readonly TitleDataService _titleDataService; _titleDataService = titleDataService;
}
public ClientController(IOptions<PlayFabSettings> settings, [AllowAnonymous]
AuthTokenService authTokenService, [HttpPost("LoginWithSteam")]
DbUserService userService, [Produces("application/json")]
DbEntityService entityService, public async Task<IActionResult> LoginWithSteam(ClientLoginWithSteamRequest request)
UserDataService userDataService, {
TitleDataService titleDataService) if (!string.IsNullOrEmpty(request.SteamTicket))
{ {
_settings = settings.Value; var ticket = AppTicket.Parse(request.SteamTicket);
_authTokenService = authTokenService; if (ticket.IsValid && ticket.HasValidSignature && (ticket.AppId == AppIdDefault || ticket.AppId == AppIdCycleBeta))
_userService = userService;
_entityService = entityService;
_userDataService = userDataService;
_titleDataService = titleDataService;
}
[AllowAnonymous]
[HttpPost("LoginWithSteam")]
[Produces("application/json")]
public async Task<IActionResult> LoginWithSteam(ClientLoginWithSteamRequest request)
{
if (!string.IsNullOrEmpty(request.SteamTicket))
{ {
var ticket = AppTicket.Parse(request.SteamTicket); var userSteamId = ticket.SteamId.ToString();
if (ticket.IsValid && ticket.HasValidSignature)
var user = await _userService.FindOrCreateAsync(PlayFabUserAuthType.Steam, userSteamId);
var entity = await _entityService.FindOrCreateAsync(user.Id);
var userTicket = _authTokenService.GenerateUser(entity);
var entityTicket = _authTokenService.GenerateEntity(entity);
await _userDataService.InitAsync(user.Id);
return Ok(new ClientResponse<FServerLoginResult>
{ {
var userSteamId = ticket.SteamId.ToString(); Code = 200,
Status = "OK",
var user = await _userService.FindOrCreateAsync(PlayFabUserAuthType.Steam, userSteamId); Data = new FServerLoginResult
var entity = await _entityService.FindOrCreateAsync(user.Id);
var userTicket = _authTokenService.GenerateUser(entity);
var entityTicket = _authTokenService.GenerateEntity(entity);
await _userDataService.InitAsync(user.Id);
return Ok(new ClientResponse<FServerLoginResult>
{ {
Code = 200, EntityToken = new FEntityTokenResponse
Status = "OK",
Data = new FServerLoginResult
{ {
EntityToken = new FEntityTokenResponse Entity = new FEntityKey
{ {
Entity = new FEntityKey Id = entity.Id,
{ Type = "title_player_account",
Id = entity.Id, TypeString = "title_player_account"
Type = "title_player_account",
TypeString = "title_player_account"
},
EntityToken = entityTicket,
TokenExpiration = DateTime.UtcNow.AddDays(6), // TODO:
}, },
InfoResultPayload = new FGetPlayerCombinedInfoResultPayload EntityToken = entityTicket,
TokenExpiration = DateTime.UtcNow.AddDays(6), // TODO:
},
InfoResultPayload = new FGetPlayerCombinedInfoResultPayload
{
CharacterInventories = new List<object>(),
PlayerProfile = new FPlayerProfileModel
{ {
CharacterInventories = new List<object>(), DisplayName = user.DisplayName,
PlayerProfile = new FPlayerProfileModel PlayerId = user.Id,
{ PublisherId = _settings.PublisherId,
DisplayName = user.DisplayName, TitleId = _settings.TitleId
PlayerId = user.Id,
PublisherId = _settings.PublisherId,
TitleId = _settings.TitleId
},
UserDataVersion = 0,
UserInventory = new List<object>(),
UserReadOnlyDataVersion = 0
}, },
LastLoginTime = DateTime.UtcNow, // TODO: UserDataVersion = 0,
NewlyCreated = false, // TODO: UserInventory = new List<object>(),
PlayFabId = user.Id, UserReadOnlyDataVersion = 0
SessionTicket = userTicket, },
SettingsForUser = new FUserSettings LastLoginTime = DateTime.UtcNow, // TODO:
{ NewlyCreated = false, // TODO:
GatherDeviceInfo = true, PlayFabId = user.Id,
GatherFocusInfo = true, SessionTicket = userTicket,
NeedsAttribution = false, SettingsForUser = new FUserSettings
}, {
TreatmentAssignment = new FTreatmentAssignment GatherDeviceInfo = true,
{ GatherFocusInfo = true,
Variables = new List<FVariable>(), NeedsAttribution = false,
Variants = new List<string>() },
} TreatmentAssignment = new FTreatmentAssignment
{
Variables = new List<FVariable>(),
Variants = new List<string>()
} }
}); }
} });
} }
return BadRequest(new ClientResponse
{
Code = 400,
Status = "BadRequest",
Error = "InvalidSteamTicket",
ErrorCode = 1010,
ErrorMessage = "Steam API AuthenticateUserTicket error response .."
});
} }
[HttpPost("AddGenericID")] return BadRequest(new ClientResponse
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult AddGenericId(FAddGenericIDRequest request)
{ {
return Ok(new ClientResponse<object> Code = 400,
{ Status = "BadRequest",
Code = 200, Error = "InvalidSteamTicket",
Status = "OK", ErrorCode = 1010,
Data = new object() ErrorMessage = "Steam API AuthenticateUserTicket error response .."
}); });
} }
[HttpPost("UpdateUserTitleDisplayName")] [HttpPost("AddGenericID")]
[Produces("application/json")] [Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult UpdateUserTitleDisplayName(FUpdateUserTitleDisplayNameRequest request) public IActionResult AddGenericId(FAddGenericIDRequest request)
{
return Ok(new ClientResponse<object>
{ {
return Ok(new ClientResponse<FUpdateUserTitleDisplayNameResult> Code = 200,
Status = "OK",
Data = new {}
});
}
[HttpPost("UpdateUserTitleDisplayName")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult UpdateUserTitleDisplayName(FUpdateUserTitleDisplayNameRequest request)
{
return Ok(new ClientResponse<FUpdateUserTitleDisplayNameResult>
{
Code = 200,
Status = "OK",
Data = new FUpdateUserTitleDisplayNameResult
{ {
Code = 200, DisplayName = request.DisplayName
Status = "OK", }
Data = new FUpdateUserTitleDisplayNameResult });
}
[HttpPost("UpdateUserData")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public async Task<IActionResult> UpdateUserData(FUpdateUserDataRequest request)
{
var userId = User.FindAuthUserId();
await _userDataService.UpdateAsync(userId, request.PlayFabId, request.Data);
return Ok(new ClientResponse<FUpdateUserDataResult>
{
Code = 200,
Status = "OK",
Data = new FUpdateUserDataResult
{
DataVersion = 0
}
});
}
[HttpPost("GetUserData")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public async Task<IActionResult> GetUserData(FGetUserDataRequest request)
{
var userId = User.FindAuthUserId();
var userData = await _userDataService.FindAsync(userId, request.PlayFabId, request.Keys);
return Ok(new ClientResponse<FGetUserDataResult>
{
Code = 200,
Status = "OK",
Data = new FGetUserDataResult
{
Data = userData,
DataVersion = 0
}
});
}
[HttpPost("GetUserReadOnlyData")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult GetUserReadOnlyData(FGetUserDataRequest request)
{
return Ok(new ClientResponse<FGetUserDataResult>
{
Code = 200,
Status = "OK",
Data = new FGetUserDataResult
{
Data = new Dictionary<string, FUserDataRecord>(),
DataVersion = 0
}
});
}
[HttpPost("GetUserInventory")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult GetUserReadOnlyData(FGetUserInventoryRequest request)
{
return Ok(new ClientResponse<FGetUserInventoryResult>
{
Code = 200,
Status = "OK",
Data = new FGetUserInventoryResult
{
Inventory = new List<FItemInstance>
{ {
DisplayName = request.DisplayName // new FItemInstance
} // {
}); // ItemId = "Helmet_03",
} // ItemInstanceId = "0102030405060708",
// ItemClass = "Helmet",
[HttpPost("UpdateUserData")] // PurchaseDate = DateTime.Now.AddDays(-1),
[Produces("application/json")] // CatalogVersion = "StaticItems",
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] // DisplayName = "Helmet",
public async Task<IActionResult> UpdateUserData(FUpdateUserDataRequest request) // UnitPrice = 0,
{ // CustomData = new Dictionary<string, string>
var userId = User.FindAuthUserId(); // {
await _userDataService.UpdateAsync(userId, request.PlayFabId, request.Data); // ["insurance"] = "None",
// ["mods"] = "{\"m\":[]}",
return Ok(new ClientResponse<FUpdateUserDataResult> // ["vanity_misc_data"] = "{\"v\":\"\",\"s\":\"\",\"l\":3,\"a\":1,\"d\":300}"
{ // }
Code = 200, // }
Status = "OK", },
Data = new FUpdateUserDataResult VirtualCurrency = new Dictionary<string, int>
{ {
DataVersion = 0 ["AE"] = 0,
} ["AS"] = 0,
}); ["AU"] = int.MaxValue,
} ["SC"] = int.MaxValue
},
VirtualCurrencyRechargeTimes = new Dictionary<string, FVirtualCurrencyRechargeTime>()
}
});
}
[HttpPost("GetUserData")] [HttpPost("GetTitleData")]
[Produces("application/json")] [Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public async Task<IActionResult> GetUserData(FGetUserDataRequest request) public IActionResult GetTitleData(FGetTitleDataRequest request)
{
return Ok(new ClientResponse<FGetTitleDataResult>
{ {
var userId = User.FindAuthUserId(); Code = 200,
var userData = await _userDataService.FindAsync(userId, request.PlayFabId, request.Keys); Status = "OK",
Data = new FGetTitleDataResult()
return Ok(new ClientResponse<FGetUserDataResult>
{ {
Code = 200, Data = _titleDataService.Find(request.Keys)
Status = "OK", }
Data = new FGetUserDataResult });
{
Data = userData,
DataVersion = 0
}
});
}
[HttpPost("GetUserReadOnlyData")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult GetUserReadOnlyData(FGetUserDataRequest request)
{
return Ok(new ClientResponse<FGetUserDataResult>
{
Code = 200,
Status = "OK",
Data = new FGetUserDataResult
{
Data = new Dictionary<string, FUserDataRecord>(),
DataVersion = 0
}
});
}
[HttpPost("GetUserInventory")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult GetUserReadOnlyData(FGetUserInventoryRequest request)
{
return Ok(new ClientResponse<FGetUserInventoryResult>
{
Code = 200,
Status = "OK",
Data = new FGetUserInventoryResult
{
Inventory = new List<FItemInstance>
{
// new FItemInstance
// {
// ItemId = "Helmet_03",
// ItemInstanceId = "0102030405060708",
// ItemClass = "Helmet",
// PurchaseDate = DateTime.Now.AddDays(-1),
// CatalogVersion = "StaticItems",
// DisplayName = "Helmet",
// UnitPrice = 0,
// CustomData = new Dictionary<string, string>
// {
// ["insurance"] = "None",
// ["mods"] = "{\"m\":[]}",
// ["vanity_misc_data"] = "{\"v\":\"\",\"s\":\"\",\"l\":3,\"a\":1,\"d\":300}"
// }
// }
},
VirtualCurrency = new Dictionary<string, int>
{
["AE"] = 0,
["AS"] = 0,
["AU"] = int.MaxValue,
["SC"] = int.MaxValue
},
VirtualCurrencyRechargeTimes = new Dictionary<string, FVirtualCurrencyRechargeTime>()
}
});
}
[HttpPost("GetTitleData")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
public IActionResult GetTitleData(FGetTitleDataRequest request)
{
return Ok(new ClientResponse<FGetTitleDataResult>
{
Code = 200,
Status = "OK",
Data = new FGetTitleDataResult()
{
Data = _titleDataService.Find(request.Keys)
}
});
}
} }
} }
@@ -1,306 +1,302 @@
using System; using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Prospect.Server.Api.Models.Client; using Prospect.Server.Api.Models.Client;
using Prospect.Server.Api.Models.CloudScript; using Prospect.Server.Api.Models.CloudScript;
using Prospect.Server.Api.Models.CloudScript.Data; using Prospect.Server.Api.Models.CloudScript.Data;
using Prospect.Server.Api.Services.Auth.Entity; using Prospect.Server.Api.Services.Auth.Entity;
using Prospect.Server.Api.Services.Auth.Extensions; using Prospect.Server.Api.Services.Auth.Extensions;
namespace Prospect.Server.Api.Controllers namespace Prospect.Server.Api.Controllers;
[Route("CloudScript")]
[ApiController]
[Authorize(AuthenticationSchemes = EntityAuthenticationOptions.DefaultScheme)]
public class CloudScriptController : Controller
{ {
[Route("CloudScript")] private readonly ILogger<CloudScriptController> _logger;
[ApiController]
[Authorize(AuthenticationSchemes = EntityAuthenticationOptions.DefaultScheme)] public CloudScriptController(ILogger<CloudScriptController> logger)
public class CloudScriptController : Controller
{ {
private readonly ILogger<CloudScriptController> _logger; _logger = logger;
}
public CloudScriptController(ILogger<CloudScriptController> logger) [HttpPost("ExecuteFunction")]
[Produces("application/json")]
public IActionResult ExecuteFunction(FExecuteFunctionRequest request)
{
_logger.LogInformation("Executing function {Function}", request.FunctionName);
object result = null;
switch (request.FunctionName)
{ {
_logger = logger; case "RequestMaintenanceModeState":
} result = new
{
[HttpPost("ExecuteFunction")] enabled = false
[Produces("application/json")] };
public IActionResult ExecuteFunction(FExecuteFunctionRequest request) break;
{ case "GetCharacterVanity":
_logger.LogInformation("Executing function {Function}", request.FunctionName); result = new
{
object result = null; userId = User.FindAuthUserId(),
error = (object) null,
switch (request.FunctionName) returnVanity = new
{
case "RequestMaintenanceModeState":
result = new
{
enabled = false
};
break;
case "GetCharacterVanity":
result = new
{ {
userId = User.FindAuthUserId(), userId = User.FindAuthUserId(),
error = (object) null, head_item = new
returnVanity = new
{ {
userId = User.FindAuthUserId(), id = "Black02M_Head1",
head_item = new materialIndex = 0,
{
id = "Black02M_Head1",
materialIndex = 0,
},
boots_item = new
{
id = "StarterOutfit01_Boots_M",
materialIndex = 0,
},
chest_item = new
{
id = "StarterOutfit01_Chest_M",
materialIndex = 0,
},
glove_item = new
{
id = "StarterOutfit01_Gloves_M",
materialIndex = 0,
},
base_suit_item = new
{
id = "StarterOutfit01M_BaseSuit",
materialIndex = 0,
},
melee_weapon_item = new
{
id = "Melee_Omega",
materialIndex = 0,
},
body_type = 1,
archetype_id = "TheProspector",
slot_index = 0
}
};
break;
case "SetMatchAllowJoin":
result = new
{
sessionId = (object)null,
success = false
};
break;
case "GetFriendList":
// TODO: ?
result = new
{
};
break;
case "GetPlayerSets":
result = new
{
success = true,
entries = new []
{
new
{
setData = new
{
id = "",
userId = User.FindAuthUserId(),
kit = "",
shield = "",
helmet = "",
weaponOne = "",
weaponTwo = "",
bag = "",
bagItemsAsJsonStr = "",
safeItemsAsJsonStr = ""
},
items = Array.Empty<object>()
}
}
};
break;
case "RequestFactionProgression":
result = new
{
factions = new []
{
new
{
factionId = "ICA",
currentProgression = 0
},
new
{
factionId = "Korolev",
currentProgression = 0
},
new
{
factionId = "Osiris",
currentProgression = 0
},
}, },
userId = User.FindAuthUserId(), boots_item = new
error = ""
};
break;
case "GetPlayersInventoriesLimits":
result = new
{
success = true,
entries = new []
{ {
new { id = "StarterOutfit01_Boots_M",
materialIndex = 0,
},
chest_item = new
{
id = "StarterOutfit01_Chest_M",
materialIndex = 0,
},
glove_item = new
{
id = "StarterOutfit01_Gloves_M",
materialIndex = 0,
},
base_suit_item = new
{
id = "StarterOutfit01M_BaseSuit",
materialIndex = 0,
},
melee_weapon_item = new
{
id = "Melee_Omega",
materialIndex = 0,
},
body_type = 1,
archetype_id = "TheProspector",
slot_index = 0
}
};
break;
case "SetMatchAllowJoin":
result = new
{
sessionId = (object)null,
success = false
};
break;
case "GetFriendList":
// TODO: ?
result = new
{
};
break;
case "GetPlayerSets":
result = new
{
success = true,
entries = new []
{
new
{
setData = new
{
id = "",
userId = User.FindAuthUserId(), userId = User.FindAuthUserId(),
inventoryStashLimit = 75, kit = "",
inventoryBagLimit = 300, shield = "",
inventorySafeLimit = 5 helmet = "",
} weaponOne = "",
weaponTwo = "",
bag = "",
bagItemsAsJsonStr = "",
safeItemsAsJsonStr = ""
},
items = Array.Empty<object>()
} }
}; }
break; };
case "UpdateRetentionBonus": break;
result = new case "RequestFactionProgression":
result = new
{
factions = new []
{ {
playerData = new new
{ {
daysClaimed = 0, factionId = "ICA",
lastClaimTime = new currentProgression = 0
{
seconds = 1635033600
}
}, },
userId = User.FindAuthUserId(), new
error = (object)null
};
break;
case "GetCraftingInProgressData":
result = new FYGetCraftingInProgressDataResult
{
UserId = User.FindAuthUserId(),
Error = null,
ItemCurrentlyBeingCrafted = new FYItemCurrentlyBeingCrafted
{ {
ItemId = null, factionId = "Korolev",
ItemRarity = -1, currentProgression = 0
PurchaseAmount = -1, },
UtcTimestampWhenCraftingStarted = new FYTimestamp new
{ {
Seconds = 0 factionId = "Osiris",
} currentProgression = 0
},
},
userId = User.FindAuthUserId(),
error = ""
};
break;
case "GetPlayersInventoriesLimits":
result = new
{
success = true,
entries = new []
{
new {
userId = User.FindAuthUserId(),
inventoryStashLimit = 75,
inventoryBagLimit = 300,
inventorySafeLimit = 5
} }
}; }
break; };
case "RequestPlayerContracts": break;
result = new FYGetPlayerContractsResult case "UpdateRetentionBonus":
result = new
{
playerData = new
{ {
UserId = User.FindAuthUserId(), daysClaimed = 0,
Error = null, lastClaimTime = new
ActiveContracts = new List<FYActiveContractPlayerData>(),
FactionsContracts = new FYFactionsContractsData
{ {
Boards = new List<FYFactionContractsData> seconds = 1635033600
}
},
userId = User.FindAuthUserId(),
error = (object)null
};
break;
case "GetCraftingInProgressData":
result = new FYGetCraftingInProgressDataResult
{
UserId = User.FindAuthUserId(),
Error = null,
ItemCurrentlyBeingCrafted = new FYItemCurrentlyBeingCrafted
{
ItemId = null,
ItemRarity = -1,
PurchaseAmount = -1,
UtcTimestampWhenCraftingStarted = new FYTimestamp
{
Seconds = 0
}
}
};
break;
case "RequestPlayerContracts":
result = new FYGetPlayerContractsResult
{
UserId = User.FindAuthUserId(),
Error = null,
ActiveContracts = new List<FYActiveContractPlayerData>(),
FactionsContracts = new FYFactionsContractsData
{
Boards = new List<FYFactionContractsData>
{
new FYFactionContractsData
{ {
new FYFactionContractsData FactionId = "ICA",
Contracts = new List<FYFactionContractData>
{ {
FactionId = "ICA", new FYFactionContractData
Contracts = new List<FYFactionContractData>
{ {
new FYFactionContractData ContractId = "NEW-Easy-ICA-Gather-1",
{ ContractIsLockedDueToLowFactionReputation = false
ContractId = "NEW-Easy-ICA-Gather-1", },
ContractIsLockedDueToLowFactionReputation = false new FYFactionContractData
},
new FYFactionContractData
{
ContractId = "NEW-Medium-ICA-Uplink-1",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Hard-ICA-Uplink-1",
ContractIsLockedDueToLowFactionReputation = false
}
}
},
new FYFactionContractsData
{
FactionId = "Korolev",
Contracts = new List<FYFactionContractData>
{ {
new FYFactionContractData ContractId = "NEW-Medium-ICA-Uplink-1",
{ ContractIsLockedDueToLowFactionReputation = false
ContractId = "NEW-Easy-KOR-Mine-4", },
ContractIsLockedDueToLowFactionReputation = false new FYFactionContractData
},
new FYFactionContractData
{
ContractId = "NEW-Medium-KOR-Mine-1",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Hard-KOR-PvP-6",
ContractIsLockedDueToLowFactionReputation = false
}
}
},
new FYFactionContractsData
{
FactionId = "Osiris",
Contracts = new List<FYFactionContractData>
{ {
new FYFactionContractData ContractId = "NEW-Hard-ICA-Uplink-1",
{ ContractIsLockedDueToLowFactionReputation = false
ContractId = "NEW-Easy-Osiris-Brightcaps-1",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Medium-Osiris-Gather-1",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Hard-Osiris-Gather-7",
ContractIsLockedDueToLowFactionReputation = false
}
} }
} }
}, },
LastBoardRefreshTimeUtc = new FYTimestamp new FYFactionContractsData
{ {
Seconds = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() FactionId = "Korolev",
Contracts = new List<FYFactionContractData>
{
new FYFactionContractData
{
ContractId = "NEW-Easy-KOR-Mine-4",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Medium-KOR-Mine-1",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Hard-KOR-PvP-6",
ContractIsLockedDueToLowFactionReputation = false
}
}
},
new FYFactionContractsData
{
FactionId = "Osiris",
Contracts = new List<FYFactionContractData>
{
new FYFactionContractData
{
ContractId = "NEW-Easy-Osiris-Brightcaps-1",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Medium-Osiris-Gather-1",
ContractIsLockedDueToLowFactionReputation = false
},
new FYFactionContractData
{
ContractId = "NEW-Hard-Osiris-Gather-7",
ContractIsLockedDueToLowFactionReputation = false
}
}
} }
}, },
RefreshHours24UtcFromBackend = 12 LastBoardRefreshTimeUtc = new FYTimestamp
}; {
break; Seconds = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()
default: }
_logger.LogWarning("Missing function {Function}", request.FunctionName); },
RefreshHours24UtcFromBackend = 12
};
break;
default:
_logger.LogWarning("Missing function {Function}", request.FunctionName);
result = new result = new
{
};
break;
}
return Ok(new ClientResponse<FExecuteFunctionResult>
{
Code = 200,
Status = "OK",
Data = new FExecuteFunctionResult
{ {
ExecutionTimeMilliseconds = 12,
FunctionName = request.FunctionName, };
FunctionResult = result break;
}
});
} }
return Ok(new ClientResponse<FExecuteFunctionResult>
{
Code = 200,
Status = "OK",
Data = new FExecuteFunctionResult
{
ExecutionTimeMilliseconds = 12,
FunctionName = request.FunctionName,
FunctionResult = result
}
});
} }
} }
@@ -1,24 +1,22 @@
using System; using System.Diagnostics;
using System.Diagnostics;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Converters namespace Prospect.Server.Api.Converters;
{
// By default System.Text.Json outputs and we want:
// Output: 2021-11-04T04:58:20.4232184Z
// Want : 2021-10-24T02:54:56.652Z
public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) // By default System.Text.Json outputs and we want:
{ // Output: 2021-11-04T04:58:20.4232184Z
writer.WriteStringValue(value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK")); // Want : 2021-10-24T02:54:56.652Z
} public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK"));
} }
} }
@@ -1,24 +1,22 @@
using System; using System.Runtime.Serialization;
using System.Runtime.Serialization;
namespace Prospect.Server.Api.Exceptions namespace Prospect.Server.Api.Exceptions;
public class ProspectException : Exception
{ {
public class ProspectException : Exception public ProspectException()
{ {
public ProspectException() }
{
}
protected ProspectException(SerializationInfo info, StreamingContext context) : base(info, context) protected ProspectException(SerializationInfo info, StreamingContext context) : base(info, context)
{ {
} }
public ProspectException(string message) : base(message) public ProspectException(string message) : base(message)
{ {
} }
public ProspectException(string message, Exception innerException) : base(message, innerException) public ProspectException(string message, Exception innerException) : base(message, innerException)
{ {
}
} }
} }
@@ -1,64 +1,59 @@
using System.IO; using System.Text;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Prospect.Server.Api.Middleware namespace Prospect.Server.Api.Middleware;
public class RequestLoggerMiddleware
{ {
public class RequestLoggerMiddleware private readonly ILogger<RequestLoggerMiddleware> _logger;
private readonly RequestDelegate _next;
public RequestLoggerMiddleware(ILogger<RequestLoggerMiddleware> logger, RequestDelegate next)
{ {
private readonly ILogger<RequestLoggerMiddleware> _logger; _logger = logger;
private readonly RequestDelegate _next; _next = next;
}
public RequestLoggerMiddleware(ILogger<RequestLoggerMiddleware> logger, RequestDelegate next) public async Task Invoke(HttpContext context)
{
var body = await RequestAsync(context.Request);
if (context.Request.Method == "POST")
{ {
_logger = logger; var requestId = "NULL";
_next = next;
if (context.Request.Headers.TryGetValue("X-RequestID", out var requestIdValues))
{
requestId = requestIdValues.ToString();
}
_logger.LogDebug("URL {Url} RequestId {RequestId} Body {Body}", context.Request.GetDisplayUrl(), requestId, body);
} }
public async Task Invoke(HttpContext context) await _next(context);
}
private static async Task<string> RequestAsync(HttpRequest request)
{
request.EnableBuffering();
try
{ {
var body = await RequestAsync(context.Request); using (var reader = new StreamReader(request.Body, Encoding.UTF8, false, 4096, true))
if (context.Request.Method == "POST")
{ {
var requestId = "NULL"; var body = await reader.ReadToEndAsync();
if (body.StartsWith("{"))
if (context.Request.Headers.TryGetValue("X-RequestID", out var requestIdValues))
{ {
requestId = requestIdValues.ToString(); return JsonConvert.SerializeObject(JsonConvert.DeserializeObject(body), Formatting.Indented);
} }
_logger.LogDebug("URL {Url} RequestId {RequestId} Body {Body}", context.Request.GetDisplayUrl(), requestId, body); return body;
} }
await _next(context);
} }
finally
private static async Task<string> RequestAsync(HttpRequest request)
{ {
request.EnableBuffering(); request.Body.Position = 0;
try
{
using (var reader = new StreamReader(request.Body, Encoding.UTF8, false, 4096, true))
{
var body = await reader.ReadToEndAsync();
if (body.StartsWith("{"))
{
return JsonConvert.SerializeObject(JsonConvert.DeserializeObject(body), Formatting.Indented);
}
return body;
}
}
finally
{
request.Body.Position = 0;
}
} }
} }
} }
@@ -1,59 +1,58 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class ClientLoginWithSteamRequest
{ {
public class ClientLoginWithSteamRequest [JsonPropertyName("CreateAccount")]
{ public bool CreateAccount { get; set; }
[JsonPropertyName("CreateAccount")]
public bool CreateAccount { get; set; }
[JsonPropertyName("CustomTags")] [JsonPropertyName("CustomTags")]
public JsonDocument CustomTags { get; set; } public JsonDocument CustomTags { get; set; }
[JsonPropertyName("EncryptedRequest")] [JsonPropertyName("EncryptedRequest")]
public string EncryptedRequest { get; set; } public string EncryptedRequest { get; set; }
[JsonPropertyName("InfoRequestParameters")] [JsonPropertyName("InfoRequestParameters")]
public InfoParameters InfoRequestParameters { get; set; } public InfoParameters InfoRequestParameters { get; set; }
[JsonPropertyName("PlayerSecret")] [JsonPropertyName("PlayerSecret")]
public string PlayerSecret { get; set; } public string PlayerSecret { get; set; }
[JsonPropertyName("SteamTicket")] [JsonPropertyName("SteamTicket")]
public string SteamTicket { get; set; } public string SteamTicket { get; set; }
} }
public class InfoParameters public class InfoParameters
{ {
[JsonPropertyName("GetCharacterInventories")] [JsonPropertyName("GetCharacterInventories")]
public bool GetCharacterInventories { get; set; } public bool GetCharacterInventories { get; set; }
[JsonPropertyName("GetCharacterList")] [JsonPropertyName("GetCharacterList")]
public bool GetCharacterList { get; set; } public bool GetCharacterList { get; set; }
[JsonPropertyName("GetPlayerProfile")] [JsonPropertyName("GetPlayerProfile")]
public bool GetPlayerProfile { get; set; } public bool GetPlayerProfile { get; set; }
[JsonPropertyName("GetPlayerStatistics")] [JsonPropertyName("GetPlayerStatistics")]
public bool GetPlayerStatistics { get; set; } public bool GetPlayerStatistics { get; set; }
[JsonPropertyName("GetTitleData")] [JsonPropertyName("GetTitleData")]
public bool GetTitleData { get; set; } public bool GetTitleData { get; set; }
[JsonPropertyName("GetUserAccountInfo")] [JsonPropertyName("GetUserAccountInfo")]
public bool GetUserAccountInfo { get; set; } public bool GetUserAccountInfo { get; set; }
[JsonPropertyName("GetUserData")] [JsonPropertyName("GetUserData")]
public bool GetUserData { get; set; } public bool GetUserData { get; set; }
[JsonPropertyName("GetUserInventory")] [JsonPropertyName("GetUserInventory")]
public bool GetUserInventory { get; set; } public bool GetUserInventory { get; set; }
[JsonPropertyName("GetUserReadOnlyData")] [JsonPropertyName("GetUserReadOnlyData")]
public bool GetUserReadOnlyData { get; set; } public bool GetUserReadOnlyData { get; set; }
[JsonPropertyName("GetUserVirtualCurrency")] [JsonPropertyName("GetUserVirtualCurrency")]
public bool GetUserVirtualCurrency { get; set; } public bool GetUserVirtualCurrency { get; set; }
}
} }
@@ -1,32 +1,31 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class ClientResponse
{ {
public class ClientResponse [JsonPropertyName("code")]
{ public int Code { get; set; }
[JsonPropertyName("code")]
public int Code { get; set; }
[JsonPropertyName("status")] [JsonPropertyName("status")]
public string Status { get; set; } public string Status { get; set; }
[JsonPropertyName("error")] [JsonPropertyName("error")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Error { get; set; } public string Error { get; set; }
[JsonPropertyName("errorCode")] [JsonPropertyName("errorCode")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int ErrorCode { get; set; } public int ErrorCode { get; set; }
[JsonPropertyName("errorMessage")] [JsonPropertyName("errorMessage")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string ErrorMessage { get; set; } public string ErrorMessage { get; set; }
} }
public class ClientResponse<T> : ClientResponse public class ClientResponse<T> : ClientResponse
{ {
[JsonPropertyName("data")] [JsonPropertyName("data")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public T Data { get; set; } public T Data { get; set; }
}
} }
@@ -1,9 +1,8 @@
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public enum CloudScriptRevisionOption
{ {
public enum CloudScriptRevisionOption Live,
{ Latest,
Live, Specific
Latest,
Specific
}
} }
@@ -1,23 +1,22 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FEntityKey
{ {
public class FEntityKey /// <summary>
{ /// Unique ID of the entity.
/// <summary> /// </summary>
/// Unique ID of the entity. [JsonPropertyName("Id")]
/// </summary> public string Id { get; set; }
[JsonPropertyName("Id")]
public string Id { get; set; }
/// <summary> /// <summary>
/// [optional] Entity type. See https://docs.microsoft.com/gaming/playfab/features/data/entities/available-built-in-entity-types /// [optional] Entity type. See https://docs.microsoft.com/gaming/playfab/features/data/entities/available-built-in-entity-types
/// </summary> /// </summary>
[JsonPropertyName("Type")] [JsonPropertyName("Type")]
public string Type { get; set; } public string Type { get; set; }
// Not in PlayFab SDK // Not in PlayFab SDK
[JsonPropertyName("TypeString")] [JsonPropertyName("TypeString")]
public string TypeString { get; set; } public string TypeString { get; set; }
}
} }
@@ -1,29 +1,27 @@
using System; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FEntityTokenResponse
{ {
public class FEntityTokenResponse /// <summary>
{ /// [optional] The entity id and type.
/// <summary> /// </summary>
/// [optional] The entity id and type. [JsonPropertyName("Entity")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("Entity")] public FEntityKey Entity { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FEntityKey Entity { get; set; }
/// <summary> /// <summary>
/// [optional] The token used to set X-EntityToken for all entity based API calls. /// [optional] The token used to set X-EntityToken for all entity based API calls.
/// </summary> /// </summary>
[JsonPropertyName("EntityToken")] [JsonPropertyName("EntityToken")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string EntityToken { get; set; } public string EntityToken { get; set; }
/// <summary> /// <summary>
/// [optional] The time the token will expire, if it is an expiring token, in UTC. /// [optional] The time the token will expire, if it is an expiring token, in UTC.
/// </summary> /// </summary>
[JsonPropertyName("TokenExpiration")] [JsonPropertyName("TokenExpiration")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public DateTime? TokenExpiration { get; set; } public DateTime? TokenExpiration { get; set; }
}
} }
@@ -1,29 +1,28 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FFunctionExecutionError
{ {
public class FFunctionExecutionError /// <summary>
{ /// [optional] Error code, such as CloudScriptAzureFunctionsExecutionTimeLimitExceeded, CloudScriptAzureFunctionsArgumentSizeExceeded,
/// <summary> /// CloudScriptAzureFunctionsReturnSizeExceeded or CloudScriptAzureFunctionsHTTPRequestError
/// [optional] Error code, such as CloudScriptAzureFunctionsExecutionTimeLimitExceeded, CloudScriptAzureFunctionsArgumentSizeExceeded, /// </summary>
/// CloudScriptAzureFunctionsReturnSizeExceeded or CloudScriptAzureFunctionsHTTPRequestError [JsonPropertyName("Error")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("Error")] public string Error { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Error { get; set; }
/// <summary> /// <summary>
/// [optional] Details about the error /// [optional] Details about the error
/// </summary> /// </summary>
[JsonPropertyName("Message")] [JsonPropertyName("Message")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Message { get; set; } public string Message { get; set; }
/// <summary> /// <summary>
/// [optional] Point during the execution of the function at which the error occurred, if any /// [optional] Point during the execution of the function at which the error occurred, if any
/// </summary> /// </summary>
[JsonPropertyName("StackTrace")] [JsonPropertyName("StackTrace")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string StackTrace { get; set; } public string StackTrace { get; set; }
}
} }
@@ -1,19 +1,18 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
{
public class FGenericServiceId
{
/// <summary>
/// Name of the service for which the player has a unique identifier.
/// </summary>
[JsonPropertyName("ServiceName")]
public string ServiceName { get; set; }
/// <summary> public class FGenericServiceId
/// Unique identifier of the player in that service. {
/// </summary> /// <summary>
[JsonPropertyName("UserId")] /// Name of the service for which the player has a unique identifier.
public string UserId { get; set; } /// </summary>
} [JsonPropertyName("ServiceName")]
public string ServiceName { get; set; }
/// <summary>
/// Unique identifier of the player in that service.
/// </summary>
[JsonPropertyName("UserId")]
public string UserId { get; set; }
} }
@@ -1,60 +1,58 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FGetPlayerCombinedInfoResultPayload
{ {
public class FGetPlayerCombinedInfoResultPayload // TSharedPtr<FUserAccountInfo> AccountInfo;
{
// TSharedPtr<FUserAccountInfo> AccountInfo;
/// <summary> /// <summary>
/// [optional] Inventories for each character for the user. /// [optional] Inventories for each character for the user.
/// </summary> /// </summary>
[JsonPropertyName("CharacterInventories")] [JsonPropertyName("CharacterInventories")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<object> CharacterInventories { get; set; } public List<object> CharacterInventories { get; set; }
// TArray<FCharacterResult> CharacterList; // TArray<FCharacterResult> CharacterList;
/// <summary> /// <summary>
/// [optional] The profile of the players. This profile is not guaranteed to be up-to-date. For a new player, this profile will not /// [optional] The profile of the players. This profile is not guaranteed to be up-to-date. For a new player, this profile will not
/// exist. /// exist.
/// </summary> /// </summary>
[JsonPropertyName("PlayerProfile")] [JsonPropertyName("PlayerProfile")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FPlayerProfileModel PlayerProfile { get; set; } public FPlayerProfileModel PlayerProfile { get; set; }
// TArray<FStatisticValue> PlayerStatistics; // TArray<FStatisticValue> PlayerStatistics;
// TMap<FString, FString> TitleData; // TMap<FString, FString> TitleData;
// TMap<FString, FUserDataRecord> UserData; // TMap<FString, FUserDataRecord> UserData;
/// <summary> /// <summary>
/// The version of the UserData that was returned. /// The version of the UserData that was returned.
/// </summary> /// </summary>
[JsonPropertyName("UserDataVersion")] [JsonPropertyName("UserDataVersion")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public uint? UserDataVersion { get; set; } public uint? UserDataVersion { get; set; }
/// <summary> /// <summary>
/// [optional] Array of inventory items in the user's current inventory. /// [optional] Array of inventory items in the user's current inventory.
/// </summary> /// </summary>
[JsonPropertyName("UserInventory")] [JsonPropertyName("UserInventory")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<object> UserInventory { get; set; } public List<object> UserInventory { get; set; }
// TMap<FString, FUserDataRecord> UserReadOnlyData; // TMap<FString, FUserDataRecord> UserReadOnlyData;
/// <summary> /// <summary>
/// The version of the Read-Only UserData that was returned. /// The version of the Read-Only UserData that was returned.
/// </summary> /// </summary>
[JsonPropertyName("UserReadOnlyDataVersion")] [JsonPropertyName("UserReadOnlyDataVersion")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public uint? UserReadOnlyDataVersion { get; set; } public uint? UserReadOnlyDataVersion { get; set; }
// TMap<FString, int32> UserVirtualCurrency; // TMap<FString, int32> UserVirtualCurrency;
// TMap<FString, FVirtualCurrencyRechargeTime> UserVirtualCurrencyRechargeTimes; // TMap<FString, FVirtualCurrencyRechargeTime> UserVirtualCurrencyRechargeTimes;
}
} }
@@ -1,116 +1,113 @@
using System; using System.Text.Json.Serialization;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FItemInstance
{ {
public class FItemInstance /// <summary>
{ /// [optional] Game specific comment associated with this instance when it was added to the user inventory.
/// <summary> /// </summary>
/// [optional] Game specific comment associated with this instance when it was added to the user inventory. [JsonPropertyName("Annotation")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("Annotation")] public string Annotation { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Annotation { get; set; }
/// <summary> /// <summary>
/// [optional] Array of unique items that were awarded when this catalog item was purchased. /// [optional] Array of unique items that were awarded when this catalog item was purchased.
/// </summary> /// </summary>
[JsonPropertyName("BundleContents")] [JsonPropertyName("BundleContents")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<string> BundleContents { get; set; } public List<string> BundleContents { get; set; }
/// <summary> /// <summary>
/// [optional] Unique identifier for the parent inventory item, as defined in the catalog, for object which were added from a bundle or /// [optional] Unique identifier for the parent inventory item, as defined in the catalog, for object which were added from a bundle or
/// container. /// container.
/// </summary> /// </summary>
[JsonPropertyName("BundleParent")] [JsonPropertyName("BundleParent")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string BundleParent { get; set; } public string BundleParent { get; set; }
/// <summary> /// <summary>
/// [optional] Catalog version for the inventory item, when this instance was created. /// [optional] Catalog version for the inventory item, when this instance was created.
/// </summary> /// </summary>
[JsonPropertyName("CatalogVersion")] [JsonPropertyName("CatalogVersion")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string CatalogVersion { get; set; } public string CatalogVersion { get; set; }
/// <summary> /// <summary>
/// [optional] A set of custom key-value pairs on the instance of the inventory item, which is not to be confused with the catalog /// [optional] A set of custom key-value pairs on the instance of the inventory item, which is not to be confused with the catalog
/// item's custom data. /// item's custom data.
/// </summary> /// </summary>
[JsonPropertyName("CustomData")] [JsonPropertyName("CustomData")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, string> CustomData { get; set; } public Dictionary<string, string> CustomData { get; set; }
/// <summary> /// <summary>
/// [optional] CatalogItem.DisplayName at the time this item was purchased. /// [optional] CatalogItem.DisplayName at the time this item was purchased.
/// </summary> /// </summary>
[JsonPropertyName("DisplayName")] [JsonPropertyName("DisplayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string DisplayName { get; set; } public string DisplayName { get; set; }
/// <summary> /// <summary>
/// [optional] Timestamp for when this instance will expire. /// [optional] Timestamp for when this instance will expire.
/// </summary> /// </summary>
[JsonPropertyName("Expiration")] [JsonPropertyName("Expiration")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public DateTime? Expiration { get; set; } public DateTime? Expiration { get; set; }
/// <summary> /// <summary>
/// [optional] Class name for the inventory item, as defined in the catalog. /// [optional] Class name for the inventory item, as defined in the catalog.
/// </summary> /// </summary>
[JsonPropertyName("ItemClass")] [JsonPropertyName("ItemClass")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string ItemClass { get; set; } public string ItemClass { get; set; }
/// <summary> /// <summary>
/// [optional] Unique identifier for the inventory item, as defined in the catalog. /// [optional] Unique identifier for the inventory item, as defined in the catalog.
/// </summary> /// </summary>
[JsonPropertyName("ItemId")] [JsonPropertyName("ItemId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string ItemId { get; set; } public string ItemId { get; set; }
/// <summary> /// <summary>
/// [optional] Unique item identifier for this specific instance of the item. /// [optional] Unique item identifier for this specific instance of the item.
/// </summary> /// </summary>
[JsonPropertyName("ItemInstanceId")] [JsonPropertyName("ItemInstanceId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string ItemInstanceId { get; set; } public string ItemInstanceId { get; set; }
/// <summary> /// <summary>
/// [optional] Timestamp for when this instance was purchased. /// [optional] Timestamp for when this instance was purchased.
/// </summary> /// </summary>
[JsonPropertyName("PurchaseDate")] [JsonPropertyName("PurchaseDate")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public DateTime? PurchaseDate { get; set; } public DateTime? PurchaseDate { get; set; }
/// <summary> /// <summary>
/// [optional] Total number of remaining uses, if this is a consumable item. /// [optional] Total number of remaining uses, if this is a consumable item.
/// </summary> /// </summary>
[JsonPropertyName("RemainingUses")] [JsonPropertyName("RemainingUses")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int? RemainingUses { get; set; } public int? RemainingUses { get; set; }
/// <summary> /// <summary>
/// [optional] Currency type for the cost of the catalog item. Not available when granting items. /// [optional] Currency type for the cost of the catalog item. Not available when granting items.
/// </summary> /// </summary>
[JsonPropertyName("UnitCurrency")] [JsonPropertyName("UnitCurrency")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string UnitCurrency { get; set; } public string UnitCurrency { get; set; }
/// <summary> /// <summary>
/// Cost of the catalog item in the given currency. Not available when granting items. /// Cost of the catalog item in the given currency. Not available when granting items.
/// </summary> /// </summary>
[JsonPropertyName("UnitPrice")] [JsonPropertyName("UnitPrice")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public uint? UnitPrice { get; set; } public uint? UnitPrice { get; set; }
/// <summary> /// <summary>
/// [optional] The number of uses that were added or removed to this item in this call. /// [optional] The number of uses that were added or removed to this item in this call.
/// </summary> /// </summary>
[JsonPropertyName("UsesIncrementedBy")] [JsonPropertyName("UsesIncrementedBy")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int? UsesIncrementedBy { get; set; } public int? UsesIncrementedBy { get; set; }
}
} }
@@ -1,28 +1,27 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FLogStatement
{ {
public class FLogStatement /// <summary>
{ /// [optional] Optional object accompanying the message as contextual information
/// <summary> /// </summary>
/// [optional] Optional object accompanying the message as contextual information [JsonPropertyName("Data")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("Data")] public object Data { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public object Data { get; set; }
/// <summary> /// <summary>
/// [optional] 'Debug', 'Info', or 'Error' /// [optional] 'Debug', 'Info', or 'Error'
/// </summary> /// </summary>
[JsonPropertyName("Level")] [JsonPropertyName("Level")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Level { get; set; } public string Level { get; set; }
/// <summary> /// <summary>
/// [optional] undefined /// [optional] undefined
/// </summary> /// </summary>
[JsonPropertyName("Message")] [JsonPropertyName("Message")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Message { get; set; } public string Message { get; set; }
}
} }
@@ -1,35 +1,34 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FPlayerProfileModel
{ {
public class FPlayerProfileModel /// <summary>
{ /// [optional] Player display name
/// <summary> /// </summary>
/// [optional] Player display name [JsonPropertyName("DisplayName")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("DisplayName")] public string DisplayName { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string DisplayName { get; set; }
/// <summary> /// <summary>
/// [optional] PlayFab player account unique identifier /// [optional] PlayFab player account unique identifier
/// </summary> /// </summary>
[JsonPropertyName("PlayerId")] [JsonPropertyName("PlayerId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string PlayerId { get; set; } public string PlayerId { get; set; }
/// <summary> /// <summary>
/// [optional] Publisher this player belongs to /// [optional] Publisher this player belongs to
/// </summary> /// </summary>
[JsonPropertyName("PublisherId")] [JsonPropertyName("PublisherId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string PublisherId { get; set; } public string PublisherId { get; set; }
/// <summary> /// <summary>
/// [optional] Title ID this player profile applies to /// [optional] Title ID this player profile applies to
/// </summary> /// </summary>
[JsonPropertyName("TitleId")] [JsonPropertyName("TitleId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string TitleId { get; set; } public string TitleId { get; set; }
}
} }
@@ -1,29 +1,28 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FScriptExecutionError
{ {
public class FScriptExecutionError /// <summary>
{ /// [optional] Error code, such as CloudScriptNotFound, JavascriptException, CloudScriptFunctionArgumentSizeExceeded,
/// <summary> /// CloudScriptAPIRequestCountExceeded, CloudScriptAPIRequestError, or CloudScriptHTTPRequestError
/// [optional] Error code, such as CloudScriptNotFound, JavascriptException, CloudScriptFunctionArgumentSizeExceeded, /// </summary>
/// CloudScriptAPIRequestCountExceeded, CloudScriptAPIRequestError, or CloudScriptHTTPRequestError [JsonPropertyName("Error")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("Error")] public string Error { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Error { get; set; }
/// <summary> /// <summary>
/// [optional] Details about the error /// [optional] Details about the error
/// </summary> /// </summary>
[JsonPropertyName("Message")] [JsonPropertyName("Message")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Message { get; set; } public string Message { get; set; }
/// <summary> /// <summary>
/// [optional] Point during the execution of the script at which the error occurred, if any /// [optional] Point during the execution of the script at which the error occurred, if any
/// </summary> /// </summary>
[JsonPropertyName("StackTrace")] [JsonPropertyName("StackTrace")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string StackTrace { get; set; } public string StackTrace { get; set; }
}
} }
@@ -1,22 +1,20 @@
using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
{
public class FTreatmentAssignment
{
/// <summary>
/// [optional] List of the experiment variables.
/// </summary>
[JsonPropertyName("Variables")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<FVariable> Variables { get; set; }
/// <summary> public class FTreatmentAssignment
/// [optional] List of the experiment variants. {
/// </summary> /// <summary>
[JsonPropertyName("Variants")] /// [optional] List of the experiment variables.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] /// </summary>
public List<string> Variants { get; set; } [JsonPropertyName("Variables")]
} [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<FVariable> Variables { get; set; }
/// <summary>
/// [optional] List of the experiment variants.
/// </summary>
[JsonPropertyName("Variants")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<string> Variants { get; set; }
} }
@@ -1,29 +1,27 @@
using System; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FUserDataRecord
{ {
public class FUserDataRecord /// <summary>
{ /// Timestamp for when this data was last updated.
/// <summary> /// </summary>
/// Timestamp for when this data was last updated. [JsonPropertyName("LastUpdated")]
/// </summary> public DateTime LastUpdated { get; set; }
[JsonPropertyName("LastUpdated")]
public DateTime LastUpdated { get; set; }
/// <summary> /// <summary>
/// [optional] Indicates whether this data can be read by all users (public) or only the user (private). This is used for GetUserData /// [optional] Indicates whether this data can be read by all users (public) or only the user (private). This is used for GetUserData
/// requests being made by one player about another player. /// requests being made by one player about another player.
/// </summary> /// </summary>
[JsonPropertyName("Permission")] [JsonPropertyName("Permission")]
[JsonConverter(typeof(JsonStringEnumConverter))] [JsonConverter(typeof(JsonStringEnumConverter))]
public UserDataPermission Permission { get; set; } public UserDataPermission Permission { get; set; }
/// <summary> /// <summary>
/// [optional] Data stored for the specified user data key. /// [optional] Data stored for the specified user data key.
/// </summary> /// </summary>
[JsonPropertyName("Value")] [JsonPropertyName("Value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Value { get; set; } public string Value { get; set; }
}
} }
@@ -1,25 +1,24 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FUserSettings
{ {
public class FUserSettings /// <summary>
{ /// Boolean for whether this player is eligible for gathering device info.
/// <summary> /// </summary>
/// Boolean for whether this player is eligible for gathering device info. [JsonPropertyName("GatherDeviceInfo")]
/// </summary> public bool GatherDeviceInfo { get; set; }
[JsonPropertyName("GatherDeviceInfo")]
public bool GatherDeviceInfo { get; set; }
/// <summary> /// <summary>
/// Boolean for whether this player should report OnFocus play-time tracking. /// Boolean for whether this player should report OnFocus play-time tracking.
/// </summary> /// </summary>
[JsonPropertyName("GatherFocusInfo")] [JsonPropertyName("GatherFocusInfo")]
public bool GatherFocusInfo { get; set; } public bool GatherFocusInfo { get; set; }
/// <summary> /// <summary>
/// Boolean for whether this player is eligible for ad tracking. /// Boolean for whether this player is eligible for ad tracking.
/// </summary> /// </summary>
[JsonPropertyName("NeedsAttribution")] [JsonPropertyName("NeedsAttribution")]
public bool NeedsAttribution { get; set; } public bool NeedsAttribution { get; set; }
}
} }
@@ -1,20 +1,19 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
{
public class FVariable
{
/// <summary>
/// Name of the variable.
/// </summary>
[JsonPropertyName("Name")]
public string Name { get; set; }
/// <summary> public class FVariable
/// [optional] Value of the variable. {
/// </summary> /// <summary>
[JsonPropertyName("Value")] /// Name of the variable.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] /// </summary>
public string Value { get; set; } [JsonPropertyName("Name")]
} public string Name { get; set; }
/// <summary>
/// [optional] Value of the variable.
/// </summary>
[JsonPropertyName("Value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string Value { get; set; }
} }
@@ -1,28 +1,26 @@
using System; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public class FVirtualCurrencyRechargeTime
{ {
public class FVirtualCurrencyRechargeTime /// <summary>
{ /// Maximum value to which the regenerating currency will automatically increment. Note that it can exceed this value
/// <summary> /// through use of the AddUserVirtualCurrency API call. However, it will not regenerate automatically until it has fallen
/// Maximum value to which the regenerating currency will automatically increment. Note that it can exceed this value /// below this value.
/// through use of the AddUserVirtualCurrency API call. However, it will not regenerate automatically until it has fallen /// </summary>
/// below this value. [JsonPropertyName("RechargeMax")]
/// </summary> public int RechargeMax { get; set; }
[JsonPropertyName("RechargeMax")]
public int RechargeMax { get; set; }
/// <summary> /// <summary>
/// Server timestamp in UTC indicating the next time the virtual currency will be incremented. /// Server timestamp in UTC indicating the next time the virtual currency will be incremented.
/// </summary> /// </summary>
[JsonPropertyName("RechargeTime")] [JsonPropertyName("RechargeTime")]
public DateTime RechargeTime { get; set; } public DateTime RechargeTime { get; set; }
/// <summary> /// <summary>
/// Time remaining (in seconds) before the next recharge increment of the virtual currency. /// Time remaining (in seconds) before the next recharge increment of the virtual currency.
/// </summary> /// </summary>
[JsonPropertyName("SecondsToRecharge")] [JsonPropertyName("SecondsToRecharge")]
public int SecondsToRecharge { get; set; } public int SecondsToRecharge { get; set; }
}
} }
@@ -1,8 +1,7 @@
namespace Prospect.Server.Api.Models.Client.Data namespace Prospect.Server.Api.Models.Client.Data;
public enum UserDataPermission
{ {
public enum UserDataPermission Public,
{ Private
Public,
Private
}
} }
@@ -1,11 +1,10 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FAddGenericIDRequest
{ {
public class FAddGenericIDRequest [JsonPropertyName("GenericId")]
{ public FGenericServiceId GenericId { get; set; }
[JsonPropertyName("GenericId")]
public FGenericServiceId GenericId { get; set; }
}
} }
@@ -1,85 +1,83 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FExecuteCloudScriptResult
{ {
public class FExecuteCloudScriptResult /// <summary>
{ /// Number of PlayFab API requests issued by the CloudScript function
/// <summary> /// </summary>
/// Number of PlayFab API requests issued by the CloudScript function [JsonPropertyName("APIRequestsIssued")]
/// </summary> public int APIRequestsIssued { get; set; }
[JsonPropertyName("APIRequestsIssued")]
public int APIRequestsIssued { get; set; }
/// <summary> /// <summary>
/// [optional] Information about the error, if any, that occurred during execution /// [optional] Information about the error, if any, that occurred during execution
/// </summary> /// </summary>
[JsonPropertyName("Error")] [JsonPropertyName("Error")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FScriptExecutionError Error { get; set; } public FScriptExecutionError Error { get; set; }
[JsonPropertyName("ExecutionTimeSeconds")] [JsonPropertyName("ExecutionTimeSeconds")]
public double? ExecutionTimeSeconds { get; set; } public double? ExecutionTimeSeconds { get; set; }
/// <summary> /// <summary>
/// [optional] The name of the function that executed /// [optional] The name of the function that executed
/// </summary> /// </summary>
[JsonPropertyName("FunctionName")] [JsonPropertyName("FunctionName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string FunctionName { get; set; } public string FunctionName { get; set; }
/// <summary> /// <summary>
/// [optional] The object returned from the CloudScript function, if any /// [optional] The object returned from the CloudScript function, if any
/// </summary> /// </summary>
[JsonPropertyName("FunctionResult")] [JsonPropertyName("FunctionResult")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public object FunctionResult { get; set; } public object FunctionResult { get; set; }
/// <summary> /// <summary>
/// [optional] Flag indicating if the FunctionResult was too large and was subsequently dropped from this event. This only occurs if /// [optional] Flag indicating if the FunctionResult was too large and was subsequently dropped from this event. This only occurs if
/// the total event size is larger than 350KB. /// the total event size is larger than 350KB.
/// </summary> /// </summary>
[JsonPropertyName("FunctionResultTooLarge")] [JsonPropertyName("FunctionResultTooLarge")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? FunctionResultTooLarge { get; set; } public bool? FunctionResultTooLarge { get; set; }
/// <summary> /// <summary>
/// Number of external HTTP requests issued by the CloudScript function /// Number of external HTTP requests issued by the CloudScript function
/// </summary> /// </summary>
[JsonPropertyName("HttpRequestsIssued")] [JsonPropertyName("HttpRequestsIssued")]
public int HttpRequestsIssued { get; set; } public int HttpRequestsIssued { get; set; }
/// <summary> /// <summary>
/// [optional] Entries logged during the function execution. These include both entries logged in the function code using log.info() /// [optional] Entries logged during the function execution. These include both entries logged in the function code using log.info()
/// and log.error() and error entries for API and HTTP request failures. /// and log.error() and error entries for API and HTTP request failures.
/// </summary> /// </summary>
[JsonPropertyName("Logs")] [JsonPropertyName("Logs")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<FLogStatement> Logs { get; set; } public List<FLogStatement> Logs { get; set; }
/// <summary> /// <summary>
/// [optional] Flag indicating if the logs were too large and were subsequently dropped from this event. This only occurs if the total /// [optional] Flag indicating if the logs were too large and were subsequently dropped from this event. This only occurs if the total
/// event size is larger than 350KB after the FunctionResult was removed. /// event size is larger than 350KB after the FunctionResult was removed.
/// </summary> /// </summary>
[JsonPropertyName("LogsTooLarge")] [JsonPropertyName("LogsTooLarge")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? LogsTooLarge { get; set; } public bool? LogsTooLarge { get; set; }
[JsonPropertyName("MemoryConsumedBytes")] [JsonPropertyName("MemoryConsumedBytes")]
public uint MemoryConsumedBytes { get; set; } public uint MemoryConsumedBytes { get; set; }
/// <summary> /// <summary>
/// Processor time consumed while executing the function. This does not include time spent waiting on API calls or HTTP /// Processor time consumed while executing the function. This does not include time spent waiting on API calls or HTTP
/// requests. /// requests.
/// </summary> /// </summary>
[JsonPropertyName("ProcessorTimeSeconds")] [JsonPropertyName("ProcessorTimeSeconds")]
public double ProcessorTimeSeconds { get; set; } public double ProcessorTimeSeconds { get; set; }
/// <summary> /// <summary>
/// The revision of the CloudScript that executed /// The revision of the CloudScript that executed
/// </summary> /// </summary>
[JsonPropertyName("Revision")] [JsonPropertyName("Revision")]
public int Revision { get; set; } public int Revision { get; set; }
}
} }
@@ -1,60 +1,58 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FExecuteCloudScriptServerRequest
{ {
public class FExecuteCloudScriptServerRequest /// <summary>
{ /// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.).
/// <summary> /// </summary>
/// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). [JsonPropertyName("CustomTags")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("CustomTags")] public Dictionary<string, string> CustomTags { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, string> CustomTags { get; set; }
/// <summary> /// <summary>
/// The name of the CloudScript function to execute /// The name of the CloudScript function to execute
/// </summary> /// </summary>
[JsonPropertyName("FunctionName")] [JsonPropertyName("FunctionName")]
public string FunctionName { get; set; } public string FunctionName { get; set; }
/// <summary> /// <summary>
/// [optional] Object that is passed in to the function as the first argument /// [optional] Object that is passed in to the function as the first argument
/// </summary> /// </summary>
[JsonPropertyName("FunctionParameter")] [JsonPropertyName("FunctionParameter")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string FunctionParameter { get; set; } public string FunctionParameter { get; set; }
/// <summary> /// <summary>
/// [optional] Generate a 'player_executed_cloudscript' PlayStream event containing the results of the function execution and other /// [optional] Generate a 'player_executed_cloudscript' PlayStream event containing the results of the function execution and other
/// contextual information. This event will show up in the PlayStream debugger console for the player in Game Manager. /// contextual information. This event will show up in the PlayStream debugger console for the player in Game Manager.
/// </summary> /// </summary>
[JsonPropertyName("GeneratePlayStreamEvent")] [JsonPropertyName("GeneratePlayStreamEvent")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? GeneratePlayStreamEvent { get; set; } public bool? GeneratePlayStreamEvent { get; set; }
/// <summary> /// <summary>
/// The unique user identifier for the player on whose behalf the script is being run /// The unique user identifier for the player on whose behalf the script is being run
/// </summary> /// </summary>
[JsonPropertyName("PlayFabId")] [JsonPropertyName("PlayFabId")]
public string PlayFabId { get; set; } public string PlayFabId { get; set; }
/// <summary> /// <summary>
/// [optional] Option for which revision of the CloudScript to execute. 'Latest' executes the most recently created revision, 'Live' /// [optional] Option for which revision of the CloudScript to execute. 'Latest' executes the most recently created revision, 'Live'
/// executes the current live, published revision, and 'Specific' executes the specified revision. The default value is /// executes the current live, published revision, and 'Specific' executes the specified revision. The default value is
/// 'Specific', if the SpeificRevision parameter is specified, otherwise it is 'Live'. /// 'Specific', if the SpeificRevision parameter is specified, otherwise it is 'Live'.
/// </summary> /// </summary>
[JsonPropertyName("RevisionSelection")] [JsonPropertyName("RevisionSelection")]
[JsonConverter(typeof(JsonStringEnumConverter))] [JsonConverter(typeof(JsonStringEnumConverter))]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public CloudScriptRevisionOption? RevisionSelection { get; set; } public CloudScriptRevisionOption? RevisionSelection { get; set; }
/// <summary> /// <summary>
/// [optional] The specivic revision to execute, when RevisionSelection is set to 'Specific' /// [optional] The specivic revision to execute, when RevisionSelection is set to 'Specific'
/// </summary> /// </summary>
[JsonPropertyName("SpecificRevision")] [JsonPropertyName("SpecificRevision")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int? SpecificRevision { get; set; } public int? SpecificRevision { get; set; }
}
} }
@@ -1,44 +1,42 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FExecuteFunctionRequest
{ {
public class FExecuteFunctionRequest /// <summary>
{ /// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.).
/// <summary> /// </summary>
/// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). [JsonPropertyName("CustomTags")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("CustomTags")] public Dictionary<string, string> CustomTags { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, string> CustomTags { get; set; }
/// <summary> /// <summary>
/// [optional] The entity to perform this action on. /// [optional] The entity to perform this action on.
/// </summary> /// </summary>
[JsonPropertyName("Entity")] [JsonPropertyName("Entity")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FEntityKey Entity { get; set; } public FEntityKey Entity { get; set; }
/// <summary> /// <summary>
/// The name of the CloudScript function to execute /// The name of the CloudScript function to execute
/// </summary> /// </summary>
[JsonPropertyName("FunctionName")] [JsonPropertyName("FunctionName")]
public string FunctionName { get; set; } public string FunctionName { get; set; }
/// <summary> /// <summary>
/// [optional] Object that is passed in to the function as the FunctionArgument field of the FunctionExecutionContext data structure /// [optional] Object that is passed in to the function as the FunctionArgument field of the FunctionExecutionContext data structure
/// </summary> /// </summary>
[JsonPropertyName("FunctionParameter")] [JsonPropertyName("FunctionParameter")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string FunctionParameter { get; set; } public string FunctionParameter { get; set; }
/// <summary> /// <summary>
/// [optional] Generate a 'entity_executed_cloudscript_function' PlayStream event containing the results of the function execution and /// [optional] Generate a 'entity_executed_cloudscript_function' PlayStream event containing the results of the function execution and
/// other contextual information. This event will show up in the PlayStream debugger console for the player in Game Manager. /// other contextual information. This event will show up in the PlayStream debugger console for the player in Game Manager.
/// </summary> /// </summary>
[JsonPropertyName("GeneratePlayStreamEvent")] [JsonPropertyName("GeneratePlayStreamEvent")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? GeneratePlayStreamEvent { get; set; } public bool? GeneratePlayStreamEvent { get; set; }
}
} }
@@ -1,41 +1,40 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FExecuteFunctionResult
{ {
public class FExecuteFunctionResult /// <summary>
{ /// [optional] Error from the CloudScript Azure Function.
/// <summary> /// </summary>
/// [optional] Error from the CloudScript Azure Function. [JsonPropertyName("Error")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("Error")] public FFunctionExecutionError Error { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FFunctionExecutionError Error { get; set; }
/// <summary> /// <summary>
/// The amount of time the function took to execute /// The amount of time the function took to execute
/// </summary> /// </summary>
[JsonPropertyName("ExecutionTimeMilliseconds")] [JsonPropertyName("ExecutionTimeMilliseconds")]
public int ExecutionTimeMilliseconds { get; set; } public int ExecutionTimeMilliseconds { get; set; }
/// <summary> /// <summary>
/// [optional] The name of the function that executed /// [optional] The name of the function that executed
/// </summary> /// </summary>
[JsonPropertyName("FunctionName")] [JsonPropertyName("FunctionName")]
public string FunctionName { get; set; } public string FunctionName { get; set; }
/// <summary> /// <summary>
/// [optional] The object returned from the function, if any /// [optional] The object returned from the function, if any
/// </summary> /// </summary>
[JsonPropertyName("FunctionResult")] [JsonPropertyName("FunctionResult")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public object FunctionResult { get; set; } public object FunctionResult { get; set; }
/// <summary> /// <summary>
/// [optional] Flag indicating if the FunctionResult was too large and was subsequently dropped from this event. /// [optional] Flag indicating if the FunctionResult was too large and was subsequently dropped from this event.
/// </summary> /// </summary>
[JsonPropertyName("FunctionResultTooLarge")] [JsonPropertyName("FunctionResultTooLarge")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? FunctionResultTooLarge { get; set; } public bool? FunctionResultTooLarge { get; set; }
}
} }
@@ -1,21 +1,19 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FGetTitleDataRequest
{ {
public class FGetTitleDataRequest /// <summary>
{ /// [optional] Specific keys to search for in the title data (leave null to get all keys)
/// <summary> /// </summary>
/// [optional] Specific keys to search for in the title data (leave null to get all keys) [JsonPropertyName("Keys")]
/// </summary> public List<string> Keys { get; set; }
[JsonPropertyName("Keys")]
public List<string> Keys { get; set; }
/// <summary> /// <summary>
/// [optional] Name of the override. This value is ignored when used by the game client; otherwise, the overrides are applied /// [optional] Name of the override. This value is ignored when used by the game client; otherwise, the overrides are applied
/// automatically to the title data. /// automatically to the title data.
/// </summary> /// </summary>
[JsonPropertyName("OverrideLabel")] [JsonPropertyName("OverrideLabel")]
public string OverrideLabel { get; set; } public string OverrideLabel { get; set; }
}
} }
@@ -1,14 +1,12 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FGetTitleDataResult
{ {
public class FGetTitleDataResult /// <summary>
{ /// [optional] a dictionary object of key / value pairs
/// <summary> /// </summary>
/// [optional] a dictionary object of key / value pairs [JsonPropertyName("Data")]
/// </summary> public Dictionary<string, string> Data { get; set; }
[JsonPropertyName("Data")]
public Dictionary<string, string> Data { get; set; }
}
} }
@@ -1,28 +1,26 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FGetUserDataRequest
{ {
public class FGetUserDataRequest /// <summary>
{ /// [optional] The version that currently exists according to the caller. The call will return the data for all of the keys if the
/// <summary> /// version in the system is greater than this.
/// [optional] The version that currently exists according to the caller. The call will return the data for all of the keys if the /// </summary>
/// version in the system is greater than this. [JsonPropertyName("IfChangedFromDataVersion")]
/// </summary> public List<uint> IfChangedFromDataVersion { get; set; }
[JsonPropertyName("IfChangedFromDataVersion")]
public List<uint> IfChangedFromDataVersion { get; set; }
/// <summary> /// <summary>
/// [optional] List of unique keys to load from. /// [optional] List of unique keys to load from.
/// </summary> /// </summary>
[JsonPropertyName("Keys")] [JsonPropertyName("Keys")]
public List<string> Keys { get; set; } public List<string> Keys { get; set; }
/// <summary> /// <summary>
/// [optional] Unique PlayFab identifier of the user to load data for. Optional, defaults to yourself if not set. When specified to a /// [optional] Unique PlayFab identifier of the user to load data for. Optional, defaults to yourself if not set. When specified to a
/// PlayFab id of another player, then this will only return public keys for that account. /// PlayFab id of another player, then this will only return public keys for that account.
/// </summary> /// </summary>
[JsonPropertyName("PlayFabId")] [JsonPropertyName("PlayFabId")]
public string PlayFabId { get; set; } public string PlayFabId { get; set; }
}
} }
@@ -1,22 +1,20 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
{
public class FGetUserDataResult
{
/// <summary>
/// [optional] User specific data for this title.
/// </summary>
[JsonPropertyName("Data")]
public Dictionary<string, FUserDataRecord> Data { get; set; }
/// <summary> public class FGetUserDataResult
/// [optional] Indicates the current version of the data that has been set. This is incremented with every set call for that type of {
/// data (read-only, internal, etc). This version can be provided in Get calls to find updated data. /// <summary>
/// </summary> /// [optional] User specific data for this title.
[JsonPropertyName("DataVersion")] /// </summary>
public uint DataVersion { get; set; } [JsonPropertyName("Data")]
} public Dictionary<string, FUserDataRecord> Data { get; set; }
/// <summary>
/// [optional] Indicates the current version of the data that has been set. This is incremented with every set call for that type of
/// data (read-only, internal, etc). This version can be provided in Get calls to find updated data.
/// </summary>
[JsonPropertyName("DataVersion")]
public uint DataVersion { get; set; }
} }
@@ -1,15 +1,13 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FGetUserInventoryRequest
{ {
public class FGetUserInventoryRequest /// <summary>
{ /// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.).
/// <summary> /// </summary>
/// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). [JsonPropertyName("CustomTags")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("CustomTags")] public Dictionary<string, string> CustomTags { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, string> CustomTags { get; set; }
}
} }
@@ -1,30 +1,28 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FGetUserInventoryResult
{ {
public class FGetUserInventoryResult /// <summary>
{ /// [optional] Array of inventory items belonging to the user.
/// <summary> /// </summary>
/// [optional] Array of inventory items belonging to the user. [JsonPropertyName("Inventory")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("Inventory")] public List<FItemInstance> Inventory { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public List<FItemInstance> Inventory { get; set; }
/// <summary> /// <summary>
/// [optional] Array of virtual currency balance(s) belonging to the user. /// [optional] Array of virtual currency balance(s) belonging to the user.
/// </summary> /// </summary>
[JsonPropertyName("VirtualCurrency")] [JsonPropertyName("VirtualCurrency")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, int> VirtualCurrency { get; set; } public Dictionary<string, int> VirtualCurrency { get; set; }
/// <summary> /// <summary>
/// [optional] Array of virtual currency balance(s) belonging to the user. /// [optional] Array of virtual currency balance(s) belonging to the user.
/// </summary> /// </summary>
[JsonPropertyName("VirtualCurrencyRechargeTimes")] [JsonPropertyName("VirtualCurrencyRechargeTimes")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, FVirtualCurrencyRechargeTime> VirtualCurrencyRechargeTimes { get; set; } public Dictionary<string, FVirtualCurrencyRechargeTime> VirtualCurrencyRechargeTimes { get; set; }
}
} }
@@ -1,64 +1,62 @@
using System; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FServerLoginResult
{ {
public class FServerLoginResult /// <summary>
{ /// [optional] If LoginTitlePlayerAccountEntity flag is set on the login request the title_player_account will also be logged in and
/// <summary> /// returned.
/// [optional] If LoginTitlePlayerAccountEntity flag is set on the login request the title_player_account will also be logged in and /// </summary>
/// returned. [JsonPropertyName("EntityToken")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("EntityToken")] public FEntityTokenResponse EntityToken { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FEntityTokenResponse EntityToken { get; set; }
/// <summary> /// <summary>
/// [optional] Results for requested info. /// [optional] Results for requested info.
/// </summary> /// </summary>
[JsonPropertyName("InfoResultPayload")] [JsonPropertyName("InfoResultPayload")]
public FGetPlayerCombinedInfoResultPayload InfoResultPayload { get; set; } public FGetPlayerCombinedInfoResultPayload InfoResultPayload { get; set; }
/// <summary> /// <summary>
/// [optional] The time of this user's previous login. If there was no previous login, then it's DateTime.MinValue /// [optional] The time of this user's previous login. If there was no previous login, then it's DateTime.MinValue
/// </summary> /// </summary>
[JsonPropertyName("LastLoginTime")] [JsonPropertyName("LastLoginTime")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public DateTime? LastLoginTime { get; set; } public DateTime? LastLoginTime { get; set; }
/// <summary> /// <summary>
/// True if the account was newly created on this login. /// True if the account was newly created on this login.
/// </summary> /// </summary>
[JsonPropertyName("NewlyCreated")] [JsonPropertyName("NewlyCreated")]
public bool NewlyCreated { get; set; } public bool NewlyCreated { get; set; }
/// <summary> /// <summary>
/// [optional] Player's unique PlayFabId. /// [optional] Player's unique PlayFabId.
/// </summary> /// </summary>
[JsonPropertyName("PlayFabId")] [JsonPropertyName("PlayFabId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string PlayFabId { get; set; } public string PlayFabId { get; set; }
/// <summary> /// <summary>
/// [optional] Unique token authorizing the user and game at the server level, for the current session. /// [optional] Unique token authorizing the user and game at the server level, for the current session.
/// </summary> /// </summary>
[JsonPropertyName("SessionTicket")] [JsonPropertyName("SessionTicket")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string SessionTicket { get; set; } public string SessionTicket { get; set; }
/// <summary> /// <summary>
/// [optional] Settings specific to this user. /// [optional] Settings specific to this user.
/// </summary> /// </summary>
[JsonPropertyName("SettingsForUser")] [JsonPropertyName("SettingsForUser")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FUserSettings SettingsForUser { get; set; } public FUserSettings SettingsForUser { get; set; }
/// <summary> /// <summary>
/// [optional] The experimentation treatments for this user at the time of login. /// [optional] The experimentation treatments for this user at the time of login.
/// </summary> /// </summary>
[JsonPropertyName("TreatmentAssignment")] [JsonPropertyName("TreatmentAssignment")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public FTreatmentAssignment TreatmentAssignment { get; set; } public FTreatmentAssignment TreatmentAssignment { get; set; }
}
} }
@@ -1,38 +1,36 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.Client.Data; using Prospect.Server.Api.Models.Client.Data;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FUpdateUserDataRequest
{ {
public class FUpdateUserDataRequest /// <summary>
{ /// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.).
/// <summary> /// </summary>
/// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). [JsonPropertyName("CustomTags")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("CustomTags")] public Dictionary<string, string> CustomTags { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, string> CustomTags { get; set; }
/// <summary> /// <summary>
/// [optional] Key-value pairs to be written to the custom data. Note that keys are trimmed of whitespace, are limited in size, and may /// [optional] Key-value pairs to be written to the custom data. Note that keys are trimmed of whitespace, are limited in size, and may
/// not begin with a '!' character or be null. /// not begin with a '!' character or be null.
/// </summary> /// </summary>
public Dictionary<string, string> Data { get; set; } public Dictionary<string, string> Data { get; set; }
/// <summary> /// <summary>
/// [optional] Optional list of Data-keys to remove from UserData. Some SDKs cannot insert null-values into Data due to language /// [optional] Optional list of Data-keys to remove from UserData. Some SDKs cannot insert null-values into Data due to language
/// constraints. Use this to delete the keys directly. /// constraints. Use this to delete the keys directly.
/// </summary> /// </summary>
public List<string> KeysToRemove { get; set; } public List<string> KeysToRemove { get; set; }
/// <summary> /// <summary>
/// [optional] Permission to be applied to all user data keys written in this request. Defaults to "private" if not set. /// [optional] Permission to be applied to all user data keys written in this request. Defaults to "private" if not set.
/// </summary> /// </summary>
public UserDataPermission? Permission { get; set; } public UserDataPermission? Permission { get; set; }
/// <summary> /// <summary>
/// Unique PlayFab assigned ID of the user on whom the operation will be performed. /// Unique PlayFab assigned ID of the user on whom the operation will be performed.
/// </summary> /// </summary>
public string PlayFabId { get; set; } public string PlayFabId { get; set; }
}
} }
@@ -1,11 +1,10 @@
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FUpdateUserDataResult
{ {
public class FUpdateUserDataResult /// <summary>
{ /// Indicates the current version of the data that has been set. This is incremented with every set call for that type of
/// <summary> /// data (read-only, internal, etc). This version can be provided in Get calls to find updated data.
/// Indicates the current version of the data that has been set. This is incremented with every set call for that type of /// </summary>
/// data (read-only, internal, etc). This version can be provided in Get calls to find updated data. public uint DataVersion { get; set; }
/// </summary>
public uint DataVersion { get; set; }
}
} }
@@ -1,21 +1,19 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FUpdateUserTitleDisplayNameRequest
{ {
public class FUpdateUserTitleDisplayNameRequest /// <summary>
{ /// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.).
/// <summary> /// </summary>
/// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). [JsonPropertyName("CustomTags")]
/// </summary> [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[JsonPropertyName("CustomTags")] public Dictionary<string, string> CustomTags { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public Dictionary<string, string> CustomTags { get; set; }
/// <summary> /// <summary>
/// New title display name for the user - must be between 3 and 25 characters. /// New title display name for the user - must be between 3 and 25 characters.
/// </summary> /// </summary>
[JsonPropertyName("DisplayName")] [JsonPropertyName("DisplayName")]
public string DisplayName { get; set; } public string DisplayName { get; set; }
}
} }
@@ -1,10 +1,9 @@
namespace Prospect.Server.Api.Models.Client namespace Prospect.Server.Api.Models.Client;
public class FUpdateUserTitleDisplayNameResult
{ {
public class FUpdateUserTitleDisplayNameResult /// <summary>
{ /// [optional] Current title display name for the user (this will be the original display name if the rename attempt failed).
/// <summary> /// </summary>
/// [optional] Current title display name for the user (this will be the original display name if the rename attempt failed). public string DisplayName { get; set; }
/// </summary>
public string DisplayName { get; set; }
}
} }
@@ -1,14 +1,12 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.CloudScript.Data namespace Prospect.Server.Api.Models.CloudScript.Data;
public class FYActiveContractPlayerData
{ {
public class FYActiveContractPlayerData [JsonPropertyName("contractId")]
{ public string ContractId { get; set; }
[JsonPropertyName("contractId")]
public string ContractId { get; set; }
[JsonPropertyName("progress")] [JsonPropertyName("progress")]
public List<int> Progress { get; set; } public List<int> Progress { get; set; }
}
} }
@@ -1,13 +1,12 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.CloudScript.Data namespace Prospect.Server.Api.Models.CloudScript.Data;
{
public class FYFactionContractData
{
[JsonPropertyName("contractId")]
public string ContractId { get; set; }
[JsonPropertyName("contractIsLockedDueToLowFactionReputation")] public class FYFactionContractData
public bool ContractIsLockedDueToLowFactionReputation { get; set; } {
} [JsonPropertyName("contractId")]
public string ContractId { get; set; }
[JsonPropertyName("contractIsLockedDueToLowFactionReputation")]
public bool ContractIsLockedDueToLowFactionReputation { get; set; }
} }
@@ -1,14 +1,12 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.CloudScript.Data namespace Prospect.Server.Api.Models.CloudScript.Data;
public class FYFactionContractsData
{ {
public class FYFactionContractsData [JsonPropertyName("factionId")]
{ public string FactionId { get; set; }
[JsonPropertyName("factionId")]
public string FactionId { get; set; }
[JsonPropertyName("contracts")] [JsonPropertyName("contracts")]
public List<FYFactionContractData> Contracts { get; set; } public List<FYFactionContractData> Contracts { get; set; }
}
} }
@@ -1,14 +1,12 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.CloudScript.Data namespace Prospect.Server.Api.Models.CloudScript.Data;
public class FYFactionsContractsData
{ {
public class FYFactionsContractsData [JsonPropertyName("boards")]
{ public List<FYFactionContractsData> Boards { get; set; }
[JsonPropertyName("boards")]
public List<FYFactionContractsData> Boards { get; set; }
[JsonPropertyName("lastBoardRefreshTimeUtc")] [JsonPropertyName("lastBoardRefreshTimeUtc")]
public FYTimestamp LastBoardRefreshTimeUtc { get; set; } public FYTimestamp LastBoardRefreshTimeUtc { get; set; }
}
} }
@@ -1,19 +1,18 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.CloudScript.Data namespace Prospect.Server.Api.Models.CloudScript.Data;
public class FYItemCurrentlyBeingCrafted
{ {
public class FYItemCurrentlyBeingCrafted [JsonPropertyName("itemId")]
{ public string ItemId { get; set; }
[JsonPropertyName("itemId")]
public string ItemId { get; set; }
[JsonPropertyName("itemRarity")] [JsonPropertyName("itemRarity")]
public int ItemRarity { get; set; } public int ItemRarity { get; set; }
[JsonPropertyName("purchaseAmount")] [JsonPropertyName("purchaseAmount")]
public int PurchaseAmount { get; set; } public int PurchaseAmount { get; set; }
[JsonPropertyName("utcTimestampWhenCraftingStarted")] [JsonPropertyName("utcTimestampWhenCraftingStarted")]
public FYTimestamp UtcTimestampWhenCraftingStarted { get; set; } public FYTimestamp UtcTimestampWhenCraftingStarted { get; set; }
}
} }
@@ -1,10 +1,9 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Prospect.Server.Api.Models.CloudScript.Data namespace Prospect.Server.Api.Models.CloudScript.Data;
public class FYTimestamp
{ {
public class FYTimestamp [JsonPropertyName("seconds")]
{ public int Seconds { get; set; }
[JsonPropertyName("seconds")]
public int Seconds { get; set; }
}
} }
@@ -1,17 +1,16 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.CloudScript.Data; using Prospect.Server.Api.Models.CloudScript.Data;
namespace Prospect.Server.Api.Models.CloudScript namespace Prospect.Server.Api.Models.CloudScript;
public class FYGetCraftingInProgressDataResult
{ {
public class FYGetCraftingInProgressDataResult [JsonPropertyName("userId")]
{ public string UserId { get; set; }
[JsonPropertyName("userId")]
public string UserId { get; set; }
[JsonPropertyName("error")] [JsonPropertyName("error")]
public string Error { get; set; } public string Error { get; set; }
[JsonPropertyName("itemCurrentlyBeingCrafted")] [JsonPropertyName("itemCurrentlyBeingCrafted")]
public FYItemCurrentlyBeingCrafted ItemCurrentlyBeingCrafted { get; set; } public FYItemCurrentlyBeingCrafted ItemCurrentlyBeingCrafted { get; set; }
}
} }
@@ -1,24 +1,22 @@
using System.Collections.Generic; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Prospect.Server.Api.Models.CloudScript.Data; using Prospect.Server.Api.Models.CloudScript.Data;
namespace Prospect.Server.Api.Models.CloudScript namespace Prospect.Server.Api.Models.CloudScript;
public class FYGetPlayerContractsResult
{ {
public class FYGetPlayerContractsResult [JsonPropertyName("userId")]
{ public string UserId { get; set; }
[JsonPropertyName("userId")]
public string UserId { get; set; }
[JsonPropertyName("error")] [JsonPropertyName("error")]
public string Error { get; set; } public string Error { get; set; }
[JsonPropertyName("activeContracts")] [JsonPropertyName("activeContracts")]
public List<FYActiveContractPlayerData> ActiveContracts { get; set; } public List<FYActiveContractPlayerData> ActiveContracts { get; set; }
[JsonPropertyName("factionsContracts")] [JsonPropertyName("factionsContracts")]
public FYFactionsContractsData FactionsContracts { get; set; } public FYFactionsContractsData FactionsContracts { get; set; }
[JsonPropertyName("refreshHours24UtcFromBackend")] [JsonPropertyName("refreshHours24UtcFromBackend")]
public int RefreshHours24UtcFromBackend { get; set; } public int RefreshHours24UtcFromBackend { get; set; }
}
} }
+33 -37
View File
@@ -1,46 +1,42 @@
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog; using Serilog;
using Serilog.Events; using Serilog.Events;
namespace Prospect.Server.Api namespace Prospect.Server.Api;
public static class Program
{ {
public static class Program public static int Main(string[] args)
{ {
public static int Main(string[] args) Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
try
{ {
Log.Logger = new LoggerConfiguration() Log.Information("Starting web host");
.MinimumLevel.Debug() CreateHostBuilder(args).Build().Run();
.MinimumLevel.Override("Microsoft", LogEventLevel.Information) return 0;
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) }
.Enrich.FromLogContext() catch (Exception ex)
.WriteTo.Console() {
.CreateLogger(); Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
try }
{ finally
Log.Information("Starting web host"); {
CreateHostBuilder(args).Build().Run(); Log.CloseAndFlush();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
} }
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
} }
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
} }
@@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>true</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -1,9 +1,8 @@
namespace Prospect.Server.Api.Services.Auth namespace Prospect.Server.Api.Services.Auth;
public static class AuthClaimTypes
{ {
public static class AuthClaimTypes public const string UserId = "user_id";
{ public const string EntityId = "entity_id";
public const string UserId = "user_id"; public const string Type = "type";
public const string EntityId = "entity_id";
public const string Type = "type";
}
} }
@@ -1,83 +1,80 @@
using System; using System.IdentityModel.Tokens.Jwt;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Prospect.Server.Api.Services.Database.Models; using Prospect.Server.Api.Services.Database.Models;
namespace Prospect.Server.Api.Services.Auth namespace Prospect.Server.Api.Services.Auth;
public class AuthTokenService
{ {
public class AuthTokenService private const string DefaultIssuer = "ProspectApi";
private const string DefaultAudience = "Prospect";
private readonly SymmetricSecurityKey _securityKey;
private readonly JwtSecurityTokenHandler _tokenHandler;
public AuthTokenService(IOptions<AuthTokenSettings> options)
{ {
private const string DefaultIssuer = "ProspectApi"; _securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(options.Value.Secret));
private const string DefaultAudience = "Prospect"; _tokenHandler = new JwtSecurityTokenHandler();
}
private readonly SymmetricSecurityKey _securityKey; private string CreateToken(IEnumerable<Claim> claims)
private readonly JwtSecurityTokenHandler _tokenHandler; {
var tokenDescriptor = new SecurityTokenDescriptor
public AuthTokenService(IOptions<AuthTokenSettings> options)
{ {
_securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(options.Value.Secret)); Subject = new ClaimsIdentity(claims),
_tokenHandler = new JwtSecurityTokenHandler(); Expires = DateTime.UtcNow.AddDays(7),
Issuer = DefaultIssuer,
Audience = DefaultAudience,
SigningCredentials = new SigningCredentials(_securityKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = _tokenHandler.CreateToken(tokenDescriptor);
return _tokenHandler.WriteToken(token);
}
public string GenerateUser(PlayFabEntity entity)
{
return CreateToken(new[]
{
new Claim(AuthClaimTypes.UserId, entity.UserId),
new Claim(AuthClaimTypes.EntityId, entity.Id),
new Claim(AuthClaimTypes.Type, AuthType.User),
});
}
public string GenerateEntity(PlayFabEntity entity)
{
return CreateToken(new[]
{
new Claim(AuthClaimTypes.UserId, entity.UserId),
new Claim(AuthClaimTypes.EntityId, entity.Id),
new Claim(AuthClaimTypes.Type, AuthType.Entity),
});
}
public ClaimsPrincipal Validate(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
try
{
return tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer = DefaultIssuer,
ValidAudience = DefaultAudience,
IssuerSigningKey = _securityKey
}, out _);
} }
catch
private string CreateToken(IEnumerable<Claim> claims)
{ {
var tokenDescriptor = new SecurityTokenDescriptor return null;
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddDays(7),
Issuer = DefaultIssuer,
Audience = DefaultAudience,
SigningCredentials = new SigningCredentials(_securityKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = _tokenHandler.CreateToken(tokenDescriptor);
return _tokenHandler.WriteToken(token);
}
public string GenerateUser(PlayFabEntity entity)
{
return CreateToken(new[]
{
new Claim(AuthClaimTypes.UserId, entity.UserId),
new Claim(AuthClaimTypes.EntityId, entity.Id),
new Claim(AuthClaimTypes.Type, AuthType.User),
});
}
public string GenerateEntity(PlayFabEntity entity)
{
return CreateToken(new[]
{
new Claim(AuthClaimTypes.UserId, entity.UserId),
new Claim(AuthClaimTypes.EntityId, entity.Id),
new Claim(AuthClaimTypes.Type, AuthType.Entity),
});
}
public ClaimsPrincipal Validate(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
try
{
return tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer = DefaultIssuer,
ValidAudience = DefaultAudience,
IssuerSigningKey = _securityKey
}, out _);
}
catch
{
return null;
}
} }
} }
} }
@@ -1,7 +1,6 @@
namespace Prospect.Server.Api.Services.Auth namespace Prospect.Server.Api.Services.Auth;
public class AuthTokenSettings
{ {
public class AuthTokenSettings public string Secret { get; set; }
{
public string Secret { get; set; }
}
} }
@@ -1,8 +1,7 @@
namespace Prospect.Server.Api.Services.Auth namespace Prospect.Server.Api.Services.Auth;
public static class AuthType
{ {
public static class AuthType public const string User = "User";
{ public const string Entity = "Entity";
public const string User = "User";
public const string Entity = "Entity";
}
} }
@@ -1,20 +1,18 @@
using System; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Prospect.Server.Api.Services.Auth.Entity; using Prospect.Server.Api.Services.Auth.Entity;
using Prospect.Server.Api.Services.Auth.User; using Prospect.Server.Api.Services.Auth.User;
namespace Prospect.Server.Api.Services.Auth namespace Prospect.Server.Api.Services.Auth;
{
public static class AuthenticationBuilderExtensions
{
public static AuthenticationBuilder AddUserAuthentication(this AuthenticationBuilder authenticationBuilder, Action<UserAuthenticationOptions> options)
{
return authenticationBuilder.AddScheme<UserAuthenticationOptions, UserAuthenticationHandler>(UserAuthenticationOptions.DefaultScheme, options);
}
public static AuthenticationBuilder AddEntityAuthentication(this AuthenticationBuilder authenticationBuilder, Action<EntityAuthenticationOptions> options) public static class AuthenticationBuilderExtensions
{ {
return authenticationBuilder.AddScheme<EntityAuthenticationOptions, EntityAuthenticationHandler>(EntityAuthenticationOptions.DefaultScheme, options); public static AuthenticationBuilder AddUserAuthentication(this AuthenticationBuilder authenticationBuilder, Action<UserAuthenticationOptions> options)
} {
return authenticationBuilder.AddScheme<UserAuthenticationOptions, UserAuthenticationHandler>(UserAuthenticationOptions.DefaultScheme, options);
}
public static AuthenticationBuilder AddEntityAuthentication(this AuthenticationBuilder authenticationBuilder, Action<EntityAuthenticationOptions> options)
{
return authenticationBuilder.AddScheme<EntityAuthenticationOptions, EntityAuthenticationHandler>(EntityAuthenticationOptions.DefaultScheme, options);
} }
} }
@@ -1,87 +1,81 @@
using System.Collections.Generic; using System.Security.Claims;
using System.Linq;
using System.Security.Claims;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Prospect.Server.Api.Models.Client; using Prospect.Server.Api.Models.Client;
using Prospect.Server.Api.Services.Auth.Extensions; using Prospect.Server.Api.Services.Auth.Extensions;
namespace Prospect.Server.Api.Services.Auth.Entity namespace Prospect.Server.Api.Services.Auth.Entity;
public class EntityAuthenticationHandler : AuthenticationHandler<EntityAuthenticationOptions>
{ {
public class EntityAuthenticationHandler : AuthenticationHandler<EntityAuthenticationOptions> private const string Header = "X-EntityToken";
private const string Type = AuthType.Entity;
private readonly AuthTokenService _authTokenService;
public EntityAuthenticationHandler(
IOptionsMonitor<EntityAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
AuthTokenService authTokenService) : base(options, logger, encoder, clock)
{ {
private const string Header = "X-EntityToken"; _authTokenService = authTokenService;
private const string Type = AuthType.Entity; }
private readonly AuthTokenService _authTokenService; private ClientResponse Res { get; set; } = new ClientResponse
{
Code = 401,
Status = "Unauthorized",
Error = "NotAuthenticated",
ErrorCode = 1074,
ErrorMessage = "This API method does not allow anonymous callers."
};
public EntityAuthenticationHandler( protected override Task<AuthenticateResult> HandleAuthenticateAsync()
IOptionsMonitor<EntityAuthenticationOptions> options, {
ILoggerFactory logger, if (!Request.Headers.TryGetValue(Header, out var headerValues) || headerValues.Count == 0)
UrlEncoder encoder,
ISystemClock clock,
AuthTokenService authTokenService) : base(options, logger, encoder, clock)
{ {
_authTokenService = authTokenService; return Task.FromResult(AuthenticateResult.Fail("No header"));
} }
private ClientResponse Res { get; set; } = new ClientResponse var headerValue = headerValues.FirstOrDefault();
if (string.IsNullOrWhiteSpace(headerValue))
{ {
Code = 401, return Task.FromResult(AuthenticateResult.Fail("Empty header value"));
Status = "Unauthorized",
Error = "NotAuthenticated",
ErrorCode = 1074,
ErrorMessage = "This API method does not allow anonymous callers."
};
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(Header, out var headerValues) || headerValues.Count == 0)
{
return Task.FromResult(AuthenticateResult.Fail("No header"));
}
var headerValue = headerValues.FirstOrDefault();
if (string.IsNullOrWhiteSpace(headerValue))
{
return Task.FromResult(AuthenticateResult.Fail("Empty header value"));
}
Res.Error = "EntityTokenInvalid";
Res.ErrorCode = 1335;
Res.ErrorMessage = "EntityTokenInvalid";
var token = _authTokenService.Validate(headerValue);
if (token != null)
{
var identity = new ClaimsIdentity(token.Claims, Options.AuthenticationType);
var identities = new List<ClaimsIdentity> { identity };
var principal = new ClaimsPrincipal(identities);
if (principal.FindAuthType() != Type)
{
return Task.FromResult(AuthenticateResult.Fail("Invalid auth type"));
}
var ticket = new AuthenticationTicket(principal, Options.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
return Task.FromResult(AuthenticateResult.Fail("Invalid JWT"));
} }
protected override async Task HandleChallengeAsync(AuthenticationProperties properties) Res.Error = "EntityTokenInvalid";
{ Res.ErrorCode = 1335;
Response.StatusCode = 401; Res.ErrorMessage = "EntityTokenInvalid";
Response.ContentType = "application/json";
await Response.WriteAsync(JsonSerializer.Serialize(Res)); var token = _authTokenService.Validate(headerValue);
if (token != null)
{
var identity = new ClaimsIdentity(token.Claims, Options.AuthenticationType);
var identities = new List<ClaimsIdentity> { identity };
var principal = new ClaimsPrincipal(identities);
if (principal.FindAuthType() != Type)
{
return Task.FromResult(AuthenticateResult.Fail("Invalid auth type"));
}
var ticket = new AuthenticationTicket(principal, Options.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
} }
return Task.FromResult(AuthenticateResult.Fail("Invalid JWT"));
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = 401;
Response.ContentType = "application/json";
await Response.WriteAsync(JsonSerializer.Serialize(Res));
} }
} }
@@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
namespace Prospect.Server.Api.Services.Auth.Entity namespace Prospect.Server.Api.Services.Auth.Entity;
public class EntityAuthenticationOptions : AuthenticationSchemeOptions
{ {
public class EntityAuthenticationOptions : AuthenticationSchemeOptions public const string DefaultScheme = "EntityAuth";
{ public string Scheme => DefaultScheme;
public const string DefaultScheme = "EntityAuth"; public string AuthenticationType = DefaultScheme;
public string Scheme => DefaultScheme;
public string AuthenticationType = DefaultScheme;
}
} }
@@ -1,34 +1,33 @@
using System.Security.Claims; using System.Security.Claims;
using Prospect.Server.Api.Exceptions; using Prospect.Server.Api.Exceptions;
namespace Prospect.Server.Api.Services.Auth.Extensions namespace Prospect.Server.Api.Services.Auth.Extensions;
public static class ClaimsPrincipalExtensions
{ {
public static class ClaimsPrincipalExtensions public static string FindAuthUserId(this ClaimsPrincipal principal)
{ {
public static string FindAuthUserId(this ClaimsPrincipal principal) return Find(principal, AuthClaimTypes.UserId);
}
public static string FindAuthEntityId(this ClaimsPrincipal principal)
{
return Find(principal, AuthClaimTypes.EntityId);
}
public static string FindAuthType(this ClaimsPrincipal principal)
{
return Find(principal, AuthClaimTypes.Type);
}
private static string Find(ClaimsPrincipal principal, string claimType)
{
var claim = principal.FindFirst(claimType);
if (claim == null)
{ {
return Find(principal, AuthClaimTypes.UserId); throw new ProspectException($"Failed to find claim {claimType}");
} }
public static string FindAuthEntityId(this ClaimsPrincipal principal) return claim.Value;
{
return Find(principal, AuthClaimTypes.EntityId);
}
public static string FindAuthType(this ClaimsPrincipal principal)
{
return Find(principal, AuthClaimTypes.Type);
}
private static string Find(ClaimsPrincipal principal, string claimType)
{
var claim = principal.FindFirst(claimType);
if (claim == null)
{
throw new ProspectException($"Failed to find claim {claimType}");
}
return claim.Value;
}
} }
} }
@@ -1,85 +1,79 @@
using System.Collections.Generic; using System.Security.Claims;
using System.Linq;
using System.Security.Claims;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Prospect.Server.Api.Models.Client; using Prospect.Server.Api.Models.Client;
using Prospect.Server.Api.Services.Auth.Extensions; using Prospect.Server.Api.Services.Auth.Extensions;
namespace Prospect.Server.Api.Services.Auth.User namespace Prospect.Server.Api.Services.Auth.User;
public class UserAuthenticationHandler : AuthenticationHandler<UserAuthenticationOptions>
{ {
public class UserAuthenticationHandler : AuthenticationHandler<UserAuthenticationOptions> private const string Header = "X-Authorization";
private const string Type = AuthType.User;
private readonly AuthTokenService _authTokenService;
public UserAuthenticationHandler(
IOptionsMonitor<UserAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
AuthTokenService authTokenService) : base(options, logger, encoder, clock)
{ {
private const string Header = "X-Authorization"; _authTokenService = authTokenService;
private const string Type = AuthType.User; }
private readonly AuthTokenService _authTokenService; private string Failure { get; set; } = "Not Authenticated";
public UserAuthenticationHandler( protected override Task<AuthenticateResult> HandleAuthenticateAsync()
IOptionsMonitor<UserAuthenticationOptions> options, {
ILoggerFactory logger, if (!Request.Headers.TryGetValue(Header, out var headerValues) || headerValues.Count == 0)
UrlEncoder encoder,
ISystemClock clock,
AuthTokenService authTokenService) : base(options, logger, encoder, clock)
{ {
_authTokenService = authTokenService; return Task.FromResult(AuthenticateResult.Fail("No header"));
} }
private string Failure { get; set; } = "Not Authenticated"; var headerValue = headerValues.FirstOrDefault();
protected override Task<AuthenticateResult> HandleAuthenticateAsync() if (string.IsNullOrWhiteSpace(headerValue))
{ {
if (!Request.Headers.TryGetValue(Header, out var headerValues) || headerValues.Count == 0) return Task.FromResult(AuthenticateResult.Fail("Empty header value"));
{
return Task.FromResult(AuthenticateResult.Fail("No header"));
}
var headerValue = headerValues.FirstOrDefault();
if (string.IsNullOrWhiteSpace(headerValue))
{
return Task.FromResult(AuthenticateResult.Fail("Empty header value"));
}
Failure = "X-Authentication HTTP header contains invalid ticket";
var token = _authTokenService.Validate(headerValue);
if (token != null)
{
var identity = new ClaimsIdentity(token.Claims, Options.AuthenticationType);
var identities = new List<ClaimsIdentity> { identity };
var principal = new ClaimsPrincipal(identities);
if (principal.FindAuthType() != Type)
{
return Task.FromResult(AuthenticateResult.Fail("Invalid auth type"));
}
var ticket = new AuthenticationTicket(principal, Options.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
return Task.FromResult(AuthenticateResult.Fail("Invalid JWT"));
} }
protected override async Task HandleChallengeAsync(AuthenticationProperties properties) Failure = "X-Authentication HTTP header contains invalid ticket";
{
Response.StatusCode = 401;
Response.ContentType = "application/json";
await Response.WriteAsync(JsonSerializer.Serialize(new ClientResponse var token = _authTokenService.Validate(headerValue);
if (token != null)
{
var identity = new ClaimsIdentity(token.Claims, Options.AuthenticationType);
var identities = new List<ClaimsIdentity> { identity };
var principal = new ClaimsPrincipal(identities);
if (principal.FindAuthType() != Type)
{ {
Code = 401, return Task.FromResult(AuthenticateResult.Fail("Invalid auth type"));
Status = "Unauthorized", }
Error = "NotAuthenticated",
ErrorCode = 1074, var ticket = new AuthenticationTicket(principal, Options.Scheme);
ErrorMessage = Failure
})); return Task.FromResult(AuthenticateResult.Success(ticket));
} }
return Task.FromResult(AuthenticateResult.Fail("Invalid JWT"));
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = 401;
Response.ContentType = "application/json";
await Response.WriteAsync(JsonSerializer.Serialize(new ClientResponse
{
Code = 401,
Status = "Unauthorized",
Error = "NotAuthenticated",
ErrorCode = 1074,
ErrorMessage = Failure
}));
} }
} }
@@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
namespace Prospect.Server.Api.Services.Auth.User namespace Prospect.Server.Api.Services.Auth.User;
public class UserAuthenticationOptions : AuthenticationSchemeOptions
{ {
public class UserAuthenticationOptions : AuthenticationSchemeOptions public const string DefaultScheme = "UserAuth";
{ public string Scheme => DefaultScheme;
public const string DefaultScheme = "UserAuth"; public string AuthenticationType = DefaultScheme;
public string Scheme => DefaultScheme;
public string AuthenticationType = DefaultScheme;
}
} }
@@ -2,19 +2,18 @@
using MongoDB.Driver; using MongoDB.Driver;
using Prospect.Server.Api.Config; using Prospect.Server.Api.Config;
namespace Prospect.Server.Api.Services.Database namespace Prospect.Server.Api.Services.Database;
public abstract class BaseDbService<T>
{ {
public abstract class BaseDbService<T> protected BaseDbService(IOptions<DatabaseSettings> options, string collection)
{ {
protected BaseDbService(IOptions<DatabaseSettings> options, string collection) var settings = options.Value;
{ var client = new MongoClient(settings.ConnectionString);
var settings = options.Value; var database = client.GetDatabase(settings.DatabaseName);
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
Collection = database.GetCollection<T>(collection); Collection = database.GetCollection<T>(collection);
}
protected IMongoCollection<T> Collection { get; }
} }
protected IMongoCollection<T> Collection { get; }
} }
@@ -1,38 +1,36 @@
using System.Threading.Tasks; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
using Prospect.Server.Api.Config; using Prospect.Server.Api.Config;
using Prospect.Server.Api.Services.Database.Models; using Prospect.Server.Api.Services.Database.Models;
namespace Prospect.Server.Api.Services.Database namespace Prospect.Server.Api.Services.Database;
public class DbEntityService : BaseDbService<PlayFabEntity>
{ {
public class DbEntityService : BaseDbService<PlayFabEntity> public DbEntityService(IOptions<DatabaseSettings> settings) : base(settings, nameof(PlayFabEntity))
{ {
public DbEntityService(IOptions<DatabaseSettings> settings) : base(settings, nameof(PlayFabEntity)) }
public async Task<PlayFabEntity> FindAsync(string userId)
{
return await Collection.Find(user => user.UserId == userId).SingleOrDefaultAsync();
}
private async Task<PlayFabEntity> CreateAsync(string userId)
{
var user = new PlayFabEntity
{ {
} UserId = userId
};
public async Task<PlayFabEntity> FindAsync(string userId) await Collection.InsertOneAsync(user);
{
return await Collection.Find(user => user.UserId == userId).SingleOrDefaultAsync();
}
private async Task<PlayFabEntity> CreateAsync(string userId) return user;
{ }
var user = new PlayFabEntity
{
UserId = userId
};
await Collection.InsertOneAsync(user); public async Task<PlayFabEntity> FindOrCreateAsync(string userId)
{
return user; return await FindAsync(userId) ??
} await CreateAsync(userId);
public async Task<PlayFabEntity> FindOrCreateAsync(string userId)
{
return await FindAsync(userId) ??
await CreateAsync(userId);
}
} }
} }
@@ -1,60 +1,57 @@
using System; using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
using Prospect.Server.Api.Config; using Prospect.Server.Api.Config;
using Prospect.Server.Api.Services.Database.Models; using Prospect.Server.Api.Services.Database.Models;
namespace Prospect.Server.Api.Services.Database namespace Prospect.Server.Api.Services.Database;
public class DbUserDataService : BaseDbService<PlayFabUserData>
{ {
public class DbUserDataService : BaseDbService<PlayFabUserData> public DbUserDataService(IOptions<DatabaseSettings> options) : base(options, nameof(PlayFabUserData))
{ {
public DbUserDataService(IOptions<DatabaseSettings> options) : base(options, nameof(PlayFabUserData)) }
public async Task<bool> HasAsync(string playFabId, string key)
{
return await Collection.Find(data => data.PlayFabId == playFabId && data.Key == key).AnyAsync();
}
public async Task<PlayFabUserData> FindAsync(string playFabId, string key)
{
return await Collection.Find(data => data.PlayFabId == playFabId && data.Key == key).SingleOrDefaultAsync();
}
public async Task<PlayFabUserData> InsertAsync(string playFabId, string key, string value, bool isPublic)
{
var data = new PlayFabUserData
{ {
} PlayFabId = playFabId,
Key = key,
Value = value,
Public = isPublic,
LastUpdated = DateTime.UtcNow
};
public async Task<bool> HasAsync(string playFabId, string key) await Collection.InsertOneAsync(data);
{
return await Collection.Find(data => data.PlayFabId == playFabId && data.Key == key).AnyAsync();
}
public async Task<PlayFabUserData> FindAsync(string playFabId, string key) return data;
{ }
return await Collection.Find(data => data.PlayFabId == playFabId && data.Key == key).SingleOrDefaultAsync();
}
public async Task<PlayFabUserData> InsertAsync(string playFabId, string key, string value, bool isPublic) public async Task<IAsyncCursor<PlayFabUserData>> AllAsync(string playFabId, bool publicOnly)
{ {
var data = new PlayFabUserData var query = publicOnly
{ ? Collection.Find(data => data.PlayFabId == playFabId && data.Public)
PlayFabId = playFabId, : Collection.Find(data => data.PlayFabId == playFabId);
Key = key,
Value = value,
Public = isPublic,
LastUpdated = DateTime.UtcNow
};
await Collection.InsertOneAsync(data); return await query.ToCursorAsync();
}
return data; public async Task UpdateValueAsync(string dataId, string value)
} {
var update = Builders<PlayFabUserData>.Update
.Set(data => data.Value, value)
.Set(data => data.LastUpdated, DateTime.UtcNow);
public async Task<IAsyncCursor<PlayFabUserData>> AllAsync(string playFabId, bool publicOnly) await Collection.UpdateOneAsync(data => data.Id == dataId, update);
{
var query = publicOnly
? Collection.Find(data => data.PlayFabId == playFabId && data.Public)
: Collection.Find(data => data.PlayFabId == playFabId);
return await query.ToCursorAsync();
}
public async Task UpdateValueAsync(string dataId, string value)
{
var update = Builders<PlayFabUserData>.Update
.Set(data => data.Value, value)
.Set(data => data.LastUpdated, DateTime.UtcNow);
await Collection.UpdateOneAsync(data => data.Id == dataId, update);
}
} }
} }
@@ -1,50 +1,46 @@
using System.Collections.Generic; using Microsoft.Extensions.Options;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
using Prospect.Server.Api.Config; using Prospect.Server.Api.Config;
using Prospect.Server.Api.Services.Database.Models; using Prospect.Server.Api.Services.Database.Models;
namespace Prospect.Server.Api.Services.Database namespace Prospect.Server.Api.Services.Database;
public class DbUserService : BaseDbService<PlayFabUser>
{ {
public class DbUserService : BaseDbService<PlayFabUser> public DbUserService(IOptions<DatabaseSettings> settings) : base(settings, nameof(PlayFabUser))
{ {
public DbUserService(IOptions<DatabaseSettings> settings) : base(settings, nameof(PlayFabUser)) }
{
}
public async Task<PlayFabUser> FindAsync(PlayFabUserAuthType type, string key) public async Task<PlayFabUser> FindAsync(PlayFabUserAuthType type, string key)
{ {
return await Collection.Find(user => user.Auth.Any(auth => return await Collection.Find(user => user.Auth.Any(auth =>
auth.Type == type && auth.Type == type &&
auth.Key == key)).FirstOrDefaultAsync(); auth.Key == key)).FirstOrDefaultAsync();
} }
private async Task<PlayFabUser> CreateAsync(PlayFabUserAuthType type, string key) private async Task<PlayFabUser> CreateAsync(PlayFabUserAuthType type, string key)
{
var user = new PlayFabUser
{ {
var user = new PlayFabUser DisplayName = "Unknown",
Auth = new List<PlayFabUserAuth>
{ {
DisplayName = "Unknown", new PlayFabUserAuth
Auth = new List<PlayFabUserAuth>
{ {
new PlayFabUserAuth Type = type,
{ Key = key
Type = type,
Key = key
}
} }
}; }
};
await Collection.InsertOneAsync(user); await Collection.InsertOneAsync(user);
return user; return user;
} }
public async Task<PlayFabUser> FindOrCreateAsync(PlayFabUserAuthType type, string key) public async Task<PlayFabUser> FindOrCreateAsync(PlayFabUserAuthType type, string key)
{ {
return await FindAsync(type, key) ?? return await FindAsync(type, key) ??
await CreateAsync(type, key); await CreateAsync(type, key);
}
} }
} }
@@ -1,23 +1,21 @@
using System; using System.Security.Cryptography;
using System.Security.Cryptography;
using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization;
namespace Prospect.Server.Api.Services.Database.Generator namespace Prospect.Server.Api.Services.Database.Generator;
public class PlayFabIdGenerator : IIdGenerator
{ {
public class PlayFabIdGenerator : IIdGenerator private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider();
public object GenerateId(object container, object document)
{ {
private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider(); Span<byte> data = stackalloc byte[8];
Random.GetBytes(data);
return Convert.ToHexString(data);
}
public object GenerateId(object container, object document) public bool IsEmpty(object id)
{ {
Span<byte> data = stackalloc byte[8]; return id is not string idStr || string.IsNullOrEmpty(idStr);
Random.GetBytes(data);
return Convert.ToHexString(data);
}
public bool IsEmpty(object id)
{
return id is not string idStr || string.IsNullOrEmpty(idStr);
}
} }
} }
@@ -1,18 +1,17 @@
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using Prospect.Server.Api.Services.Database.Generator; using Prospect.Server.Api.Services.Database.Generator;
namespace Prospect.Server.Api.Services.Database.Models namespace Prospect.Server.Api.Services.Database.Models;
{
/// <summary>
/// title_player_account
/// </summary>
public class PlayFabEntity
{
[BsonId(IdGenerator = typeof(PlayFabIdGenerator))]
public string Id { get; set; }
[BsonRequired] /// <summary>
[BsonElement("UserId")] /// title_player_account
public string UserId { get; set; } /// </summary>
} public class PlayFabEntity
{
[BsonId(IdGenerator = typeof(PlayFabIdGenerator))]
public string Id { get; set; }
[BsonRequired]
[BsonElement("UserId")]
public string UserId { get; set; }
} }
@@ -1,23 +1,21 @@
using System.Collections.Generic; using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Attributes;
using Prospect.Server.Api.Services.Database.Generator; using Prospect.Server.Api.Services.Database.Generator;
namespace Prospect.Server.Api.Services.Database.Models namespace Prospect.Server.Api.Services.Database.Models;
/// <summary>
/// master_player_account
/// </summary>
public class PlayFabUser
{ {
/// <summary> [BsonId(IdGenerator = typeof(PlayFabIdGenerator))]
/// master_player_account public string Id { get; set; }
/// </summary>
public class PlayFabUser
{
[BsonId(IdGenerator = typeof(PlayFabIdGenerator))]
public string Id { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("DisplayName")] [BsonElement("DisplayName")]
public string DisplayName { get; set; } public string DisplayName { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("Auth")] [BsonElement("Auth")]
public List<PlayFabUserAuth> Auth { get; set; } public List<PlayFabUserAuth> Auth { get; set; }
}
} }
@@ -1,13 +1,12 @@
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
namespace Prospect.Server.Api.Services.Database.Models namespace Prospect.Server.Api.Services.Database.Models;
{
public class PlayFabUserAuth
{
[BsonElement("Type")]
public PlayFabUserAuthType Type { get; set; }
[BsonElement("Key")] public class PlayFabUserAuth
public string Key { get; set; } {
} [BsonElement("Type")]
public PlayFabUserAuthType Type { get; set; }
[BsonElement("Key")]
public string Key { get; set; }
} }
@@ -1,8 +1,7 @@
namespace Prospect.Server.Api.Services.Database.Models namespace Prospect.Server.Api.Services.Database.Models;
public enum PlayFabUserAuthType
{ {
public enum PlayFabUserAuthType Epic,
{ Steam
Epic,
Steam
}
} }
@@ -1,36 +1,34 @@
using System; using MongoDB.Bson;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
namespace Prospect.Server.Api.Services.Database.Models namespace Prospect.Server.Api.Services.Database.Models;
public class PlayFabUserData
{ {
public class PlayFabUserData [BsonId]
{ [BsonRepresentation(BsonType.ObjectId)]
[BsonId] public string Id { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
/// <summary> /// <summary>
/// PlayFabUser.Id /// PlayFabUser.Id
/// </summary> /// </summary>
[BsonRequired] [BsonRequired]
[BsonElement("PlayFabId")] [BsonElement("PlayFabId")]
public string PlayFabId { get; set; } public string PlayFabId { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("Key")] [BsonElement("Key")]
public string Key { get; set; } public string Key { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("Value")] [BsonElement("Value")]
public string Value { get; set; } public string Value { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("Public")] [BsonElement("Public")]
public bool Public { get; set; } public bool Public { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("LastUpdated")] [BsonElement("LastUpdated")]
public DateTime LastUpdated { get; set; } public DateTime LastUpdated { get; set; }
}
} }
File diff suppressed because one or more lines are too long
@@ -1,33 +1,29 @@
using System.Collections.Generic; namespace Prospect.Server.Api.Services.UserData;
using Microsoft.Extensions.Logging;
namespace Prospect.Server.Api.Services.UserData public class TitleDataService
{ {
public class TitleDataService public TitleDataService(ILogger<TitleDataService> logger)
{ {
public TitleDataService(ILogger<TitleDataService> logger)
{
} }
public Dictionary<string, string> Find(List<string> keys) public Dictionary<string, string> Find(List<string> keys)
{ {
if (keys != null && keys.Count > 0) if (keys != null && keys.Count > 0)
{ {
var result = new Dictionary<string, string>(); var result = new Dictionary<string, string>();
foreach (var key in keys) foreach (var key in keys)
{ {
if (TitleDataDefault.Data.TryGetValue(key, out var value)) if (TitleDataDefault.Data.TryGetValue(key, out var value))
{ {
result.Add(key, value); result.Add(key, value);
} }
} }
return result; return result;
} }
return TitleDataDefault.Data; return TitleDataDefault.Data;
} }
}
} }
@@ -1,94 +1,106 @@
using System; using Prospect.Server.Api.Models.Client.Data;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Prospect.Server.Api.Models.Client.Data;
using Prospect.Server.Api.Services.Database; using Prospect.Server.Api.Services.Database;
namespace Prospect.Server.Api.Services.UserData namespace Prospect.Server.Api.Services.UserData;
public class UserDataService
{ {
public class UserDataService private readonly ILogger<UserDataService> _logger;
private readonly DbUserDataService _dbUserDataService;
public UserDataService(ILogger<UserDataService> logger, DbUserDataService dbUserDataService)
{ {
private readonly ILogger<UserDataService> _logger; _logger = logger;
private readonly DbUserDataService _dbUserDataService; _dbUserDataService = dbUserDataService;
}
public UserDataService(ILogger<UserDataService> logger, DbUserDataService dbUserDataService) /// <summary>
/// Initialize data for the given PlayFabId.
/// </summary>
public async Task InitAsync(string playFabId)
{
// TODO: Proper objects.
var defaultData = new Dictionary<string, (bool isPublic, string value)>
{ {
_logger = logger; ["Generators__2021_09_09"] = (true, "[{\"generatorId\":\"playerquarters_gen_aurum\",\"lastClaimTime\":{\"seconds\":0}},{\"generatorId\":\"playerquarters_gen_kmarks\",\"lastClaimTime\":{\"seconds\":0}},{\"generatorId\":\"playerquarters_gen_crate\",\"lastClaimTime\":{\"seconds\":0}}]"),
_dbUserDataService = dbUserDataService; ["InventoryInfo"] = (true, "{\"inventoryStashLimit\":75,\"inventoryBagLimit\":300,\"inventorySafeLimit\":5}"),
["LOADOUT"] = (true, $"{{\"id\":\"\",\"userId\":\"{playFabId}\",\"kit\":\"\",\"shield\":\"\",\"helmet\":\"\",\"weaponOne\":\"\",\"weaponTwo\":\"\",\"bag\":null,\"bagItemsAsJsonStr\":\"\",\"safeItemsAsJsonStr\":\"\"}}"),
["OnboardingProgression"] = (true, "{\"currentMissionID\":\"TalkToBadum\",\"progress\":0,\"showPopup\":true}"),
["PickaxeUpgradeLevel"] = (true, "0"),
["PlayerQuartersLevel"] = (true, "{\"level\":1,\"upgradeStartedTime\":{\"seconds\":0}}"),
};
foreach (var (key, (isPublic, value)) in defaultData)
{
if (!await _dbUserDataService.HasAsync(playFabId, key))
{
await _dbUserDataService.InsertAsync(playFabId, key, value, isPublic);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="currentUserId">
/// The authenticated user id.
/// </param>
/// <param name="requestUserId">
/// The requested user id.
/// </param>
/// <param name="keys"></param>
/// <returns></returns>
public async Task<Dictionary<string, FUserDataRecord>> FindAsync(
string currentUserId,
string requestUserId,
List<string> keys)
{
if (string.IsNullOrEmpty(currentUserId))
{
throw new ArgumentNullException(nameof(currentUserId));
} }
/// <summary> if (requestUserId == null)
/// Initialize data for the given PlayFabId.
/// </summary>
public async Task InitAsync(string playFabId)
{ {
// TODO: Proper objects. requestUserId = currentUserId;
var defaultData = new Dictionary<string, (bool isPublic, string value)> }
{
["Generators__2021_09_09"] = (true, "[{\"generatorId\":\"playerquarters_gen_aurum\",\"lastClaimTime\":{\"seconds\":0}},{\"generatorId\":\"playerquarters_gen_kmarks\",\"lastClaimTime\":{\"seconds\":0}},{\"generatorId\":\"playerquarters_gen_crate\",\"lastClaimTime\":{\"seconds\":0}}]"),
["InventoryInfo"] = (true, "{\"inventoryStashLimit\":75,\"inventoryBagLimit\":300,\"inventorySafeLimit\":5}"),
["LOADOUT"] = (true, $"{{\"id\":\"\",\"userId\":\"{playFabId}\",\"kit\":\"\",\"shield\":\"\",\"helmet\":\"\",\"weaponOne\":\"\",\"weaponTwo\":\"\",\"bag\":null,\"bagItemsAsJsonStr\":\"\",\"safeItemsAsJsonStr\":\"\"}}"),
["OnboardingProgression"] = (true, "{\"currentMissionID\":\"TalkToBadum\",\"progress\":0,\"showPopup\":true}"),
["PickaxeUpgradeLevel"] = (true, "0"),
["PlayerQuartersLevel"] = (true, "{\"level\":1,\"upgradeStartedTime\":{\"seconds\":0}}"),
};
foreach (var (key, (isPublic, value)) in defaultData) var other = currentUserId != requestUserId;
var result = new Dictionary<string, FUserDataRecord>();
if (keys != null && keys.Count > 0)
{
foreach (var key in keys)
{ {
if (!await _dbUserDataService.HasAsync(playFabId, key)) var data = await _dbUserDataService.FindAsync(requestUserId, key);
if (data == null)
{ {
await _dbUserDataService.InsertAsync(playFabId, key, value, isPublic); // TODO: Error?
continue;
} }
if (!data.Public && other)
{
// TODO: Error?
continue;
}
result.Add(data.Key, new FUserDataRecord
{
LastUpdated = data.LastUpdated,
Permission = data.Public ? UserDataPermission.Public : UserDataPermission.Private,
Value = data.Value
});
} }
} }
else
/// <summary>
///
/// </summary>
/// <param name="currentUserId">
/// The authenticated user id.
/// </param>
/// <param name="requestUserId">
/// The requested user id.
/// </param>
/// <param name="keys"></param>
/// <returns></returns>
public async Task<Dictionary<string, FUserDataRecord>> FindAsync(
string currentUserId,
string requestUserId,
List<string> keys)
{ {
if (string.IsNullOrEmpty(currentUserId)) var cursor = await _dbUserDataService.AllAsync(requestUserId, other);
{
throw new ArgumentNullException(nameof(currentUserId));
}
if (requestUserId == null) while (await cursor.MoveNextAsync())
{ {
requestUserId = currentUserId; foreach (var data in cursor.Current)
}
var other = currentUserId != requestUserId;
var result = new Dictionary<string, FUserDataRecord>();
if (keys != null && keys.Count > 0)
{
foreach (var key in keys)
{ {
var data = await _dbUserDataService.FindAsync(requestUserId, key);
if (data == null)
{
// TODO: Error?
continue;
}
if (!data.Public && other)
{
// TODO: Error?
continue;
}
result.Add(data.Key, new FUserDataRecord result.Add(data.Key, new FUserDataRecord
{ {
LastUpdated = data.LastUpdated, LastUpdated = data.LastUpdated,
@@ -97,86 +109,69 @@ namespace Prospect.Server.Api.Services.UserData
}); });
} }
} }
else
{
var cursor = await _dbUserDataService.AllAsync(requestUserId, other);
while (await cursor.MoveNextAsync())
{
foreach (var data in cursor.Current)
{
result.Add(data.Key, new FUserDataRecord
{
LastUpdated = data.LastUpdated,
Permission = data.Public ? UserDataPermission.Public : UserDataPermission.Private,
Value = data.Value
});
}
}
}
return result;
} }
/// <summary> return result;
/// }
/// </summary>
/// <param name="currentUserId"> /// <summary>
/// The authenticated user id. ///
/// </param> /// </summary>
/// <param name="requestUserId"> /// <param name="currentUserId">
/// The requested user id. /// The authenticated user id.
/// </param> /// </param>
/// <param name="changes"></param> /// <param name="requestUserId">
/// <returns></returns> /// The requested user id.
public async Task UpdateAsync( /// </param>
string currentUserId, /// <param name="changes"></param>
string requestUserId, /// <returns></returns>
Dictionary<string, string> changes) public async Task UpdateAsync(
string currentUserId,
string requestUserId,
Dictionary<string, string> changes)
{
if (changes.Count == 0)
{ {
if (changes.Count == 0) return;
{ }
return;
}
if (string.IsNullOrEmpty(currentUserId)) if (string.IsNullOrEmpty(currentUserId))
{ {
throw new ArgumentNullException(nameof(currentUserId)); throw new ArgumentNullException(nameof(currentUserId));
} }
if (requestUserId == null) if (requestUserId == null)
{ {
requestUserId = currentUserId; requestUserId = currentUserId;
} }
// Whether we are updating someone else. // Whether we are updating someone else.
var other = currentUserId != requestUserId; var other = currentUserId != requestUserId;
foreach (var (key, value) in changes) foreach (var (key, value) in changes)
{
var data = await _dbUserDataService.FindAsync(currentUserId, key);
if (data == null)
{ {
var data = await _dbUserDataService.FindAsync(currentUserId, key); if (other)
if (data == null)
{ {
if (other) _logger.LogWarning("User {PlayFabId} attempted to update non-existing key {Key} of another user {PlayFabIdOther}", currentUserId, key, requestUserId);
{ }
_logger.LogWarning("User {PlayFabId} attempted to update non-existing key {Key} of another user {PlayFabIdOther}", currentUserId, key, requestUserId); else
} {
else await _dbUserDataService.InsertAsync(currentUserId, key, value, false);
{
await _dbUserDataService.InsertAsync(currentUserId, key, value, false);
}
continue;
} }
if (!data.Public && other) continue;
{
_logger.LogWarning("User {PlayFabId} attempted to update non-public key {Key} of another user {PlayFabIdOther}", currentUserId, key, data.PlayFabId);
continue;
}
await _dbUserDataService.UpdateValueAsync(data.Id, value);
} }
if (!data.Public && other)
{
_logger.LogWarning("User {PlayFabId} attempted to update non-public key {Key} of another user {PlayFabIdOther}", currentUserId, key, data.PlayFabId);
continue;
}
await _dbUserDataService.UpdateValueAsync(data.Id, value);
} }
} }
} }
+49 -56
View File
@@ -1,72 +1,65 @@
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.Config;
using Prospect.Server.Api.Converters; using Prospect.Server.Api.Converters;
using Prospect.Server.Api.Middleware; using Prospect.Server.Api.Middleware;
using Prospect.Server.Api.Services.Auth; using Prospect.Server.Api.Services.Auth;
using Prospect.Server.Api.Services.Auth.User;
using Prospect.Server.Api.Services.Database; using Prospect.Server.Api.Services.Database;
using Prospect.Server.Api.Services.UserData; using Prospect.Server.Api.Services.UserData;
using Serilog; using Serilog;
namespace Prospect.Server.Api namespace Prospect.Server.Api;
public class Startup
{ {
public class Startup public Startup(IConfiguration configuration)
{ {
public Startup(IConfiguration configuration) Configuration = configuration;
}
private IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AuthTokenSettings>(Configuration.GetSection(nameof(AuthTokenSettings)));
services.Configure<DatabaseSettings>(Configuration.GetSection(nameof(DatabaseSettings)));
services.Configure<PlayFabSettings>(Configuration.GetSection(nameof(PlayFabSettings)));
services.AddSingleton<AuthTokenService>();
services.AddSingleton<UserDataService>();
services.AddSingleton<TitleDataService>();
services.AddSingleton<DbUserService>();
services.AddSingleton<DbEntityService>();
services.AddSingleton<DbUserDataService>();
services.AddAuthentication(_ =>
{
})
.AddUserAuthentication(_ => {})
.AddEntityAuthentication(_ => {});
services.AddControllers().AddJsonOptions(options =>
{ {
Configuration = configuration; options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} }
private IConfiguration Configuration { get; } app.UseSerilogRequestLogging();
app.UseMiddleware<RequestLoggerMiddleware>();
public void ConfigureServices(IServiceCollection services) app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{ {
services.Configure<AuthTokenSettings>(Configuration.GetSection(nameof(AuthTokenSettings))); endpoints.MapControllers();
services.Configure<DatabaseSettings>(Configuration.GetSection(nameof(DatabaseSettings))); });
services.Configure<PlayFabSettings>(Configuration.GetSection(nameof(PlayFabSettings)));
services.AddSingleton<AuthTokenService>();
services.AddSingleton<UserDataService>();
services.AddSingleton<TitleDataService>();
services.AddSingleton<DbUserService>();
services.AddSingleton<DbEntityService>();
services.AddSingleton<DbUserDataService>();
services.AddAuthentication(_ =>
{
})
.AddUserAuthentication(_ => {})
.AddEntityAuthentication(_ => {});
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSerilogRequestLogging();
app.UseMiddleware<RequestLoggerMiddleware>();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
} }
} }
+4 -7
View File
@@ -1,11 +1,8 @@
using System.Collections.Generic; namespace Prospect.Server.Steam;
namespace Prospect.Server.Steam public class AppDlc
{ {
public class AppDlc public uint AppId { get; set; }
{
public uint AppId { get; set; }
public List<uint> Licenses { get; set; } public List<uint> Licenses { get; set; }
}
} }
+143 -147
View File
@@ -1,167 +1,163 @@
using System; using System.Net;
using System.Collections.Generic;
using System.IO;
using System.Net;
namespace Prospect.Server.Steam namespace Prospect.Server.Steam;
public class AppTicket
{ {
public class AppTicket private AppTicket()
{ {
private AppTicket()
}
/// <summary>
/// Full appticket, with a GC token and session header.
/// </summary>
public byte[] AuthTicket { get; set; }
public ulong GcToken { get; set; }
public DateTimeOffset TokenGenerated { get; set; }
private IPAddress SessionExternalIp { get; set; }
/// <summary>
/// Time the client has been connected to Steam in ms.
/// </summary>
public uint ClientConnectionTime { get; set; }
/// <summary>
/// How many servers the client has connected to.
/// </summary>
public uint ClientConnectionCount { get; set; }
public uint Version { get; set; }
public ulong SteamId { get; set; }
public uint AppId { get; set; }
public IPAddress OwnershipTicketExternalIp { get; set; }
public IPAddress OwnershipTicketInternalIp { get; set; }
public uint OwnershipFlags { get; set; }
public DateTimeOffset OwnershipTicketGenerated { get; set; }
public DateTimeOffset OwnershipTicketExpires { get; set; }
public List<uint> Licenses { get; set; }
public List<AppDlc> Dlc { get; set; }
public byte[] Signature { get; set; }
public bool IsExpired { get; set; }
public bool HasValidSignature { get; set; }
public bool IsValid { get; set; }
public static AppTicket Parse(string ticketHex)
{
var result = new AppTicket();
var ticketBytes = Convert.FromHexString(ticketHex);
using (var stream = new MemoryStream(ticketBytes))
using (var ticketReader = new BinaryReader(stream))
{ {
// AuthTicket
} var initialLength = ticketReader.ReadUInt32();
if (initialLength == 20)
/// <summary>
/// Full appticket, with a GC token and session header.
/// </summary>
public byte[] AuthTicket { get; set; }
public ulong GcToken { get; set; }
public DateTimeOffset TokenGenerated { get; set; }
private IPAddress SessionExternalIp { get; set; }
/// <summary>
/// Time the client has been connected to Steam in ms.
/// </summary>
public uint ClientConnectionTime { get; set; }
/// <summary>
/// How many servers the client has connected to.
/// </summary>
public uint ClientConnectionCount { get; set; }
public uint Version { get; set; }
public ulong SteamId { get; set; }
public uint AppId { get; set; }
public IPAddress OwnershipTicketExternalIp { get; set; }
public IPAddress OwnershipTicketInternalIp { get; set; }
public uint OwnershipFlags { get; set; }
public DateTimeOffset OwnershipTicketGenerated { get; set; }
public DateTimeOffset OwnershipTicketExpires { get; set; }
public List<uint> Licenses { get; set; }
public List<AppDlc> Dlc { get; set; }
public byte[] Signature { get; set; }
public bool IsExpired { get; set; }
public bool HasValidSignature { get; set; }
public bool IsValid { get; set; }
public static AppTicket Parse(string ticketHex)
{
var result = new AppTicket();
var ticketBytes = Convert.FromHexString(ticketHex);
using (var stream = new MemoryStream(ticketBytes))
using (var ticketReader = new BinaryReader(stream))
{ {
// AuthTicket result.AuthTicket = ticketBytes.AsSpan(0, 52).ToArray();
var initialLength = ticketReader.ReadUInt32(); result.GcToken = ticketReader.ReadUInt64();
if (initialLength == 20)
{
result.AuthTicket = ticketBytes.AsSpan(0, 52).ToArray();
result.GcToken = ticketReader.ReadUInt64();
ticketReader.BaseStream.Position += 8; ticketReader.BaseStream.Position += 8;
result.TokenGenerated = DateTimeOffset.FromUnixTimeSeconds(ticketReader.ReadUInt32()); result.TokenGenerated = DateTimeOffset.FromUnixTimeSeconds(ticketReader.ReadUInt32());
if (ticketReader.ReadUInt32() != 24) if (ticketReader.ReadUInt32() != 24)
{
return null;
}
ticketReader.BaseStream.Position += 8;
result.SessionExternalIp = new IPAddress(BitConverter.GetBytes(ticketReader.ReadUInt32()));
ticketReader.BaseStream.Position += 4;
result.ClientConnectionTime = ticketReader.ReadUInt32();
result.ClientConnectionCount = ticketReader.ReadUInt32();
if (ticketReader.ReadUInt32() + ticketReader.BaseStream.Position != ticketReader.BaseStream.Length)
{
return null;
}
}
else
{
ticketReader.BaseStream.Position -= 4;
}
// Ownership ticket
var ownershipTicketOffset = ticketReader.BaseStream.Position;
var ownershipTicketLength = ticketReader.ReadUInt32();
if (ownershipTicketOffset + ownershipTicketLength != ticketReader.BaseStream.Length &&
ownershipTicketOffset + ownershipTicketLength + 128 != ticketReader.BaseStream.Length)
{ {
return null; return null;
} }
result.Version = ticketReader.ReadUInt32(); ticketReader.BaseStream.Position += 8;
result.SteamId = ticketReader.ReadUInt64();
result.AppId = ticketReader.ReadUInt32();
result.OwnershipTicketExternalIp = new IPAddress(BitConverter.GetBytes(ticketReader.ReadUInt32()));
result.OwnershipTicketInternalIp = new IPAddress(BitConverter.GetBytes(ticketReader.ReadUInt32()));
result.OwnershipFlags = ticketReader.ReadUInt32();
result.OwnershipTicketGenerated = DateTimeOffset.FromUnixTimeSeconds(ticketReader.ReadUInt32());
result.OwnershipTicketExpires = DateTimeOffset.FromUnixTimeSeconds(ticketReader.ReadUInt32());
result.Licenses = new List<uint>();
var licenseCount = ticketReader.ReadUInt16(); result.SessionExternalIp = new IPAddress(BitConverter.GetBytes(ticketReader.ReadUInt32()));
for (var i = 0; i < licenseCount; i++)
ticketReader.BaseStream.Position += 4;
result.ClientConnectionTime = ticketReader.ReadUInt32();
result.ClientConnectionCount = ticketReader.ReadUInt32();
if (ticketReader.ReadUInt32() + ticketReader.BaseStream.Position != ticketReader.BaseStream.Length)
{ {
result.Licenses.Add(ticketReader.ReadUInt32()); return null;
} }
}
result.Dlc = new List<AppDlc>(); else
{
var dlcCount = ticketReader.ReadUInt16(); ticketReader.BaseStream.Position -= 4;
for (var i = 0; i < dlcCount; i++)
{
var dlc = new AppDlc();
dlc.AppId = ticketReader.ReadUInt32();
dlc.Licenses = new List<uint>();
var dlcLicenseCount = ticketReader.ReadUInt16();
for (var j = 0; j < dlcLicenseCount; j++)
{
dlc.Licenses.Add(ticketReader.ReadUInt32());
}
result.Dlc.Add(dlc);
}
ticketReader.ReadUInt16();
if (ticketReader.BaseStream.Position + 128 == ticketReader.BaseStream.Length)
{
result.Signature = ticketBytes.AsSpan((int)ticketReader.BaseStream.Position, 128).ToArray();
}
var date = DateTimeOffset.UtcNow;
result.IsExpired = result.OwnershipTicketExpires < date;
result.HasValidSignature = result.Signature != null && SteamCrypto.VerifySignature(ticketBytes.AsSpan((int)ownershipTicketOffset, (int)ownershipTicketLength).ToArray(), result.Signature);
result.IsValid = !result.IsExpired && (result.Signature == null || result.HasValidSignature);
} }
return result; // Ownership ticket
var ownershipTicketOffset = ticketReader.BaseStream.Position;
var ownershipTicketLength = ticketReader.ReadUInt32();
if (ownershipTicketOffset + ownershipTicketLength != ticketReader.BaseStream.Length &&
ownershipTicketOffset + ownershipTicketLength + 128 != ticketReader.BaseStream.Length)
{
return null;
}
result.Version = ticketReader.ReadUInt32();
result.SteamId = ticketReader.ReadUInt64();
result.AppId = ticketReader.ReadUInt32();
result.OwnershipTicketExternalIp = new IPAddress(BitConverter.GetBytes(ticketReader.ReadUInt32()));
result.OwnershipTicketInternalIp = new IPAddress(BitConverter.GetBytes(ticketReader.ReadUInt32()));
result.OwnershipFlags = ticketReader.ReadUInt32();
result.OwnershipTicketGenerated = DateTimeOffset.FromUnixTimeSeconds(ticketReader.ReadUInt32());
result.OwnershipTicketExpires = DateTimeOffset.FromUnixTimeSeconds(ticketReader.ReadUInt32());
result.Licenses = new List<uint>();
var licenseCount = ticketReader.ReadUInt16();
for (var i = 0; i < licenseCount; i++)
{
result.Licenses.Add(ticketReader.ReadUInt32());
}
result.Dlc = new List<AppDlc>();
var dlcCount = ticketReader.ReadUInt16();
for (var i = 0; i < dlcCount; i++)
{
var dlc = new AppDlc();
dlc.AppId = ticketReader.ReadUInt32();
dlc.Licenses = new List<uint>();
var dlcLicenseCount = ticketReader.ReadUInt16();
for (var j = 0; j < dlcLicenseCount; j++)
{
dlc.Licenses.Add(ticketReader.ReadUInt32());
}
result.Dlc.Add(dlc);
}
ticketReader.ReadUInt16();
if (ticketReader.BaseStream.Position + 128 == ticketReader.BaseStream.Length)
{
result.Signature = ticketBytes.AsSpan((int)ticketReader.BaseStream.Position, 128).ToArray();
}
var date = DateTimeOffset.UtcNow;
result.IsExpired = result.OwnershipTicketExpires < date;
result.HasValidSignature = result.Signature != null && SteamCrypto.VerifySignature(ticketBytes.AsSpan((int)ownershipTicketOffset, (int)ownershipTicketLength).ToArray(), result.Signature);
result.IsValid = !result.IsExpired && (result.Signature == null || result.HasValidSignature);
} }
return result;
} }
} }
@@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>true</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
+22 -23
View File
@@ -1,32 +1,31 @@
using System.Security.Cryptography; using System.Security.Cryptography;
namespace Prospect.Server.Steam namespace Prospect.Server.Steam;
internal static class SteamCrypto
{ {
internal static class SteamCrypto private const string SystemCertificate = "-----BEGIN PUBLIC KEY-----\n" +
"MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDf7BrWLBBmLBc1OhSwfFkRf53T\n" +
"2Ct64+AVzRkeRuh7h3SiGEYxqQMUeYKO6UWiSRKpI2hzic9pobFhRr3Bvr/WARvY\n" +
"gdTckPv+T1JzZsuVcNfFjrocejN1oWI0Rrtgt4Bo+hOneoo3S57G9F1fOpn5nsQ6\n" +
"6WOiu4gZKODnFMBCiQIBEQ==\n" +
"-----END PUBLIC KEY-----" ;
public static bool VerifySignature(byte[] data, byte[] signature)
{ {
private const string SystemCertificate = "-----BEGIN PUBLIC KEY-----\n" + var rsa = new RSACryptoServiceProvider();
"MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDf7BrWLBBmLBc1OhSwfFkRf53T\n" +
"2Ct64+AVzRkeRuh7h3SiGEYxqQMUeYKO6UWiSRKpI2hzic9pobFhRr3Bvr/WARvY\n" +
"gdTckPv+T1JzZsuVcNfFjrocejN1oWI0Rrtgt4Bo+hOneoo3S57G9F1fOpn5nsQ6\n" +
"6WOiu4gZKODnFMBCiQIBEQ==\n" +
"-----END PUBLIC KEY-----" ;
public static bool VerifySignature(byte[] data, byte[] signature) rsa.ImportFromPem(SystemCertificate);
var dataOk = rsa.VerifyData(data, CryptoConfig.MapNameToOID("SHA1")!, signature);
if (!dataOk)
{ {
var rsa = new RSACryptoServiceProvider(); return false;
rsa.ImportFromPem(SystemCertificate);
var dataOk = rsa.VerifyData(data, CryptoConfig.MapNameToOID("SHA1")!, signature);
if (!dataOk)
{
return false;
}
var dataHashed = SHA1.HashData(data);
var dataVerified = rsa.VerifyHash(dataHashed, CryptoConfig.MapNameToOID("SHA1")!, signature);
return dataVerified;
} }
var dataHashed = SHA1.HashData(data);
var dataVerified = rsa.VerifyHash(dataHashed, CryptoConfig.MapNameToOID("SHA1")!, signature);
return dataVerified;
} }
} }