Audit fixes: Fortuna pass level, rename persist, loadout & craft-timer #6
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -274,8 +274,12 @@ public class ClientController : Controller
|
|||||||
[HttpPost("UpdateUserTitleDisplayName")]
|
[HttpPost("UpdateUserTitleDisplayName")]
|
||||||
[Produces(MediaTypeNames.Application.Json)]
|
[Produces(MediaTypeNames.Application.Json)]
|
||||||
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
|
[Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)]
|
||||||
public IActionResult UpdateUserTitleDisplayName(FUpdateUserTitleDisplayNameRequest request)
|
public async Task<IActionResult> UpdateUserTitleDisplayName(FUpdateUserTitleDisplayNameRequest request)
|
||||||
{
|
{
|
||||||
|
// Persist the rename, otherwise it is echoed back but lost on next login.
|
||||||
|
var userId = User.FindAuthUserId();
|
||||||
|
await _userService.UpdateDisplayNameAsync(userId, request.DisplayName);
|
||||||
|
|
||||||
return Ok(new ClientResponse<FUpdateUserTitleDisplayNameResult>
|
return Ok(new ClientResponse<FUpdateUserTitleDisplayNameResult>
|
||||||
{
|
{
|
||||||
Code = 200,
|
Code = 200,
|
||||||
|
|||||||
@@ -81,9 +81,16 @@ public class ClaimActiveContract : ICloudScriptFunction<FYClaimCompletedActiveCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
var factionKey = "FactionProgression" + contract.Faction;
|
var factionKey = "FactionProgression" + contract.Faction;
|
||||||
|
// Fortuna pass season XP is stored per season; the client derives the pass level
|
||||||
|
// from this key. No challenge system is emulated, so contracts are the XP source.
|
||||||
|
#if SEASON_3_RELEASE || SEASON_3_DEBUG
|
||||||
|
const string seasonXpKey = "FortunaPass3_SeasonXp";
|
||||||
|
#else
|
||||||
|
const string seasonXpKey = "FortunaPass2_SeasonXp";
|
||||||
|
#endif
|
||||||
var userData = await _userDataService.FindAsync(
|
var userData = await _userDataService.FindAsync(
|
||||||
userId, userId,
|
userId, userId,
|
||||||
new List<string>{"ContractsActive", "ContractsOneTimeCompleted", "Balance", "Inventory", factionKey, "JobBoardsData" }
|
new List<string>{"ContractsActive", "ContractsOneTimeCompleted", "Balance", "Inventory", factionKey, "JobBoardsData", seasonXpKey }
|
||||||
);
|
);
|
||||||
|
|
||||||
var factionProgression = JsonSerializer.Deserialize<int>(userData[factionKey].Value);
|
var factionProgression = JsonSerializer.Deserialize<int>(userData[factionKey].Value);
|
||||||
@@ -270,6 +277,12 @@ public class ClaimActiveContract : ICloudScriptFunction<FYClaimCompletedActiveCo
|
|||||||
newContracts.Add(newContract);
|
newContracts.Add(newContract);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grant Fortuna pass season XP for completing this contract and persist it, so the
|
||||||
|
// pass level (derived client-side from FortunaPass{season}_SeasonXp) actually moves.
|
||||||
|
// No exact rate ships in the data, so scale it to the contract's reputation value.
|
||||||
|
var seasonXp = int.TryParse(userData[seasonXpKey].Value, out var currentSeasonXp) ? currentSeasonXp : 0;
|
||||||
|
seasonXp += contract.ReputationIncrease * 10;
|
||||||
|
|
||||||
await _userDataService.UpdateAsync(
|
await _userDataService.UpdateAsync(
|
||||||
userId, userId,
|
userId, userId,
|
||||||
new Dictionary<string, string>{
|
new Dictionary<string, string>{
|
||||||
@@ -279,6 +292,7 @@ public class ClaimActiveContract : ICloudScriptFunction<FYClaimCompletedActiveCo
|
|||||||
["Inventory"] = JsonSerializer.Serialize(newInventory),
|
["Inventory"] = JsonSerializer.Serialize(newInventory),
|
||||||
[factionKey] = JsonSerializer.Serialize(factionProgression),
|
[factionKey] = JsonSerializer.Serialize(factionProgression),
|
||||||
["JobBoardsData"] = JsonSerializer.Serialize(jobBoardsData),
|
["JobBoardsData"] = JsonSerializer.Serialize(jobBoardsData),
|
||||||
|
[seasonXpKey] = JsonSerializer.Serialize(seasonXp),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -297,7 +311,7 @@ public class ClaimActiveContract : ICloudScriptFunction<FYClaimCompletedActiveCo
|
|||||||
CurrentProgression = factionProgression,
|
CurrentProgression = factionProgression,
|
||||||
},
|
},
|
||||||
Status = 18, // EYClaimContractRewardsStatus::OK
|
Status = 18, // EYClaimContractRewardsStatus::OK
|
||||||
UpdatedSeasonXp = 0 // TODO: Probably a separate season XP rate configured by server?
|
UpdatedSeasonXp = seasonXp // new season XP total (client derives the pass level from it)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-8
@@ -1,6 +1,8 @@
|
|||||||
using Prospect.Server.Api.Services.Auth.Extensions;
|
using System.Text.Json;
|
||||||
|
using Prospect.Server.Api.Services.Auth.Extensions;
|
||||||
using Prospect.Server.Api.Services.CloudScript.Models;
|
using Prospect.Server.Api.Services.CloudScript.Models;
|
||||||
using Prospect.Server.Api.Services.CloudScript.Models.Data;
|
using Prospect.Server.Api.Services.CloudScript.Models.Data;
|
||||||
|
using Prospect.Server.Api.Services.UserData;
|
||||||
|
|
||||||
namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
||||||
|
|
||||||
@@ -8,13 +10,15 @@ namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
|||||||
public class GetCraftingInProgressData : ICloudScriptFunction<FYGetCraftingInProgressDataRequest, FYGetCraftingInProgressDataResult>
|
public class GetCraftingInProgressData : ICloudScriptFunction<FYGetCraftingInProgressDataRequest, FYGetCraftingInProgressDataResult>
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
private readonly UserDataService _userDataService;
|
||||||
|
|
||||||
public GetCraftingInProgressData(IHttpContextAccessor httpContextAccessor)
|
public GetCraftingInProgressData(IHttpContextAccessor httpContextAccessor, UserDataService userDataService)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
_userDataService = userDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<FYGetCraftingInProgressDataResult> ExecuteAsync(FYGetCraftingInProgressDataRequest request)
|
public async Task<FYGetCraftingInProgressDataResult> ExecuteAsync(FYGetCraftingInProgressDataRequest request)
|
||||||
{
|
{
|
||||||
var context = _httpContextAccessor.HttpContext;
|
var context = _httpContextAccessor.HttpContext;
|
||||||
if (context == null)
|
if (context == null)
|
||||||
@@ -22,11 +26,20 @@ public class GetCraftingInProgressData : ICloudScriptFunction<FYGetCraftingInPro
|
|||||||
throw new CloudScriptException("CloudScript was not called within a http request");
|
throw new CloudScriptException("CloudScript was not called within a http request");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(new FYGetCraftingInProgressDataResult
|
var userId = context.User.FindAuthUserId();
|
||||||
|
|
||||||
|
// Return the persisted in-progress craft (written by StartItemCraftingClient)
|
||||||
|
// instead of an empty stub, so the crafting timer survives a menu reopen / relog.
|
||||||
|
var userData = await _userDataService.FindAsync(userId, userId, new List<string> { "CraftingTimer__2022_05_12" });
|
||||||
|
// Fully qualified: a different FYItemCurrentlyBeingCrafted also exists in this namespace.
|
||||||
|
var craft = JsonSerializer.Deserialize<Models.Data.FYItemCurrentlyBeingCrafted>(userData["CraftingTimer__2022_05_12"].Value)
|
||||||
|
?? new Models.Data.FYItemCurrentlyBeingCrafted();
|
||||||
|
|
||||||
|
return new FYGetCraftingInProgressDataResult
|
||||||
{
|
{
|
||||||
UserId = context.User.FindAuthUserId(),
|
UserId = userId,
|
||||||
Error = string.Empty,
|
Error = string.Empty,
|
||||||
ItemCurrentlyBeingCrafted = {},
|
ItemCurrentlyBeingCrafted = craft,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Prospect.Server.Api.Services.Auth.Extensions;
|
using System.Text.Json;
|
||||||
|
using Prospect.Server.Api.Services.Auth.Extensions;
|
||||||
using Prospect.Server.Api.Services.CloudScript.Models;
|
using Prospect.Server.Api.Services.CloudScript.Models;
|
||||||
|
using Prospect.Server.Api.Services.UserData;
|
||||||
|
|
||||||
namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
||||||
|
|
||||||
@@ -7,43 +9,53 @@ namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
|||||||
public class GetPlayerSets : ICloudScriptFunction<FYGetPlayersSets, object?>
|
public class GetPlayerSets : ICloudScriptFunction<FYGetPlayersSets, object?>
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
private readonly UserDataService _userDataService;
|
||||||
|
|
||||||
public GetPlayerSets(IHttpContextAccessor httpContextAccessor)
|
public GetPlayerSets(IHttpContextAccessor httpContextAccessor, UserDataService userDataService)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
_userDataService = userDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<object?> ExecuteAsync(FYGetPlayersSets request)
|
public async Task<object?> ExecuteAsync(FYGetPlayersSets request)
|
||||||
{
|
{
|
||||||
var context = _httpContextAccessor.HttpContext;
|
var context = _httpContextAccessor.HttpContext;
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
return Task.FromResult<object?>(null);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult<object?>(new
|
var userId = context.User.FindAuthUserId();
|
||||||
|
|
||||||
|
// Return the player's persisted loadout (written by CompleteInventoryUpdate /
|
||||||
|
// RequestUpdateStationInventory) instead of a blank set, so saved gear survives
|
||||||
|
// a menu reopen / relog. Same response shape as before, just populated.
|
||||||
|
var userData = await _userDataService.FindAsync(userId, userId, new List<string> { "LOADOUT" });
|
||||||
|
var loadout = JsonSerializer.Deserialize<LoadoutData>(userData["LOADOUT"].Value) ?? new LoadoutData();
|
||||||
|
|
||||||
|
return new
|
||||||
{
|
{
|
||||||
success = true,
|
success = true,
|
||||||
entries = new []
|
entries = new[]
|
||||||
{
|
{
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
setData = new
|
setData = new
|
||||||
{
|
{
|
||||||
id = "",
|
id = "",
|
||||||
userId = context.User.FindAuthUserId(),
|
userId,
|
||||||
kit = "",
|
kit = "",
|
||||||
shield = "",
|
shield = loadout.Shield ?? "",
|
||||||
helmet = "",
|
helmet = loadout.Helmet ?? "",
|
||||||
weaponOne = "",
|
weaponOne = loadout.WeaponOne ?? "",
|
||||||
weaponTwo = "",
|
weaponTwo = loadout.WeaponTwo ?? "",
|
||||||
bag = "",
|
bag = loadout.Bag ?? "",
|
||||||
bagItemsAsJsonStr = "",
|
bagItemsAsJsonStr = loadout.BagItemsAsJsonStr ?? "",
|
||||||
safeItemsAsJsonStr = ""
|
safeItemsAsJsonStr = loadout.SafeItemsAsJsonStr ?? ""
|
||||||
},
|
},
|
||||||
items = Array.Empty<object>()
|
items = Array.Empty<object>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ public class DbUserService : BaseDbService<PlayFabUser>
|
|||||||
return await Collection.Find(user => user.Id == id).FirstOrDefaultAsync();
|
return await Collection.Find(user => user.Id == id).FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Persist a display-name change (UpdateUserTitleDisplayName). Without this the rename
|
||||||
|
// is echoed back to the client but lost on next login.
|
||||||
|
public async Task UpdateDisplayNameAsync(string id, string displayName)
|
||||||
|
{
|
||||||
|
var update = Builders<PlayFabUser>.Update.Set(user => user.DisplayName, displayName);
|
||||||
|
await Collection.UpdateOneAsync(user => user.Id == id, update);
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
Reference in New Issue
Block a user