feat: ability to set avatar icon .

This commit is contained in:
Michael Hannigan
2025-05-26 14:19:50 -05:00
parent 29547dbdb9
commit 0a6f304359
16 changed files with 1872 additions and 37 deletions
+29 -29
View File
@@ -34,37 +34,37 @@ This is not a comprehensive list of features, just the ones that really seem to
|Name|Area|Description|Implementation|What needs work|
|--|--|--|--|--|
|Login| Authnz ||Fully||
|Logout| Authnz ||Fully||
|Initial Training and Warframe Selection| Gameplay ||Fully||
|Complete Mastery Rank Challenges| Gameplay ||Fully||
|Attach system wide artifact| Gameplay ||Fully|This is not actually an api endpoint, but instead relies on returnign inventory correctly|
| Obtain daily login reward| ||Fully| This is customizable and uses older documentation for the rewards listing. Current state drop table percentage is not period accurate |
| Login | Authnz | Gameplay | Fully ||
| Logout | Authnz | Gameplay | Fully ||
| Initial Training and Warframe Selection | Gameplay || Fully ||
| Complete Mastery Rank Challenges| Gameplay || Fully ||
| Attach system wide artifact| Gameplay || Fully |This is not actually an api endpoint, but instead relies on returnign inventory correctly|
| Obtain daily login reward| || Fully | This is customizable and uses older documentation for the rewards listing. Current state drop table percentage is not period accurate |
| Get the world state | || Partially | Can be configured via db, alerts work for the most part. operations are not implemented|
| Validate daily mission bonus status| ||Fully| |
| Validate daily mission bonus status| || Fully | |
| purchase more revives ||No work done||
| update taunt state | ||Fully*| There may be a bug surrounding the initial lotus explanation after completing training |
|Get updated credits/plat| Economy ||Fully||
|Purchase Item | ||Some work started||
| Get updated credits/plat| Economy || Fully ||
| Purchase Item | ||Some work started||
| Craft item in Foundry | Foundry ||Partial|Not all recipes are parsed / available in db|
| Claim Foundry Item|Foundry ||Fully||
| Instantly complete item in foundry| Foundry ||Fully||
| Check build status in foundry | Foundry ||Fully||
|Get Player Profile (All Players)| Stats ||Partial| Some stats have not appeared in the metrics, so some stats might not be correct|
|Get Kill Leaderboard| Stats ||Fully||
|Get Kill Leaderboard With User Ranking| Stats ||Partial| leaderboard that is returned is the same as the general leaderboard|
|Get Inventory| Gear ||Partial||
|Update Inventory| Gear ||Partial| Some areas (goals/completions) might not be updated as they have yet to be encountered |
|Attach mods| Gear ||Fully||
|Upgrade Mods| Gear ||Fully||
|Save loadout| Gear ||Fully||
| Attach reactors / catalysts to frames/weapons| Gear||Fully||
| Claim Foundry Item|Foundry || Fully ||
| Instantly complete item in foundry| Foundry || Fully ||
| Check build status in foundry | Foundry || Fully ||
| Get Player Profile (All Players)| Stats ||Partial| Some stats have not appeared in the metrics, so some stats might not be correct|
| Get Kill Leaderboard| Stats || Fully ||
| Get Kill Leaderboard With User Ranking| Stats ||Partial| leaderboard that is returned is the same as the general leaderboard|
| Get Inventory| Gear ||Partial||
| Update Inventory| Gear ||Partial| Some areas (goals/completions) might not be updated as they have yet to be encountered |
| Attach mods| Gear || Fully ||
| Upgrade Mods| Gear || Fully ||
| Save loadout| Gear || Fully ||
| Attach reactors / catalysts to frames/weapons| Gear|| Fully ||
| Purchase new inventory slots (frames and weapons)| Gear ||No work started||
|Send friend request| Friends ||Fully||
|Remove friend| Friends ||Fully||
| Send friend request| Friends || Fully ||
| Remove friend| Friends || Fully ||
| Approve Friend request | Friends || Fully ||
| Get friend listing |Friends || Partial / Unknown | More data might need to be returned. but list works.|
| Set player avatar | Friends || No work done||
| Set player avatar | Friends || Fully ||
| Get Guild Member Listing |Guild || Very Little | right now has hardcoded return object |
| Create Guild| ||Nothing||
| Remove member from Guild | ||Nothing||
@@ -73,17 +73,17 @@ This is not a comprehensive list of features, just the ones that really seem to
| Invite to guild | ||Nothing||
| Accept guild invite | ||Nothing||
| change guild rank | ||Nothing||
|Get Messages (Api) | ||Nothing| No idea what this is and it might be a vestigial endpoint|
|Send Messages (Api) | ||Nothing| No idea what this is and it might be a vestigial endpoint|
| Get Messages (Api) | ||Nothing| No idea what this is and it might be a vestigial endpoint|
| Send Messages (Api) | ||Nothing| No idea what this is and it might be a vestigial endpoint|
## Web Features
|Name|Area|Description|Implementation|What needs work|
|-|-|-|-|-|
|Login|Web||Fully||
|Logout|Web||Fully||
|Registration|Web||Fully||
|Login|Web|| Fully ||
|Logout|Web|| Fully ||
|Registration|Web|| Fully ||
| View player profile | ||No work done||
| View individual player stats | ||No work done||
| Leaderboards (player) | ||No work done||
@@ -3,8 +3,10 @@ using System.Text.Json;
using WFClassic.Web.Logic.Friendship.Add;
using WFClassic.Web.Logic.Friendship.AddPending;
using WFClassic.Web.Logic.Friendship.Get;
using WFClassic.Web.Logic.Friendship.Icon;
using WFClassic.Web.Logic.Friendship.Remove;
using WFClassic.Web.Logic.Middleware;
using WFClassic.Web.Logic.Shared;
namespace WFClassic.Web.Controllers
{
@@ -16,22 +18,46 @@ namespace WFClassic.Web.Controllers
private GetFriendsRequestHandler _getFriendsRequestHandler;
private AcceptFriendRequestHandler _acceptFriendRequestHandler;
private RemoveFriendRequestHandler _removeFriendRequestHandler;
private SetAvatarIconRequestHandler _setAvatarIconRequestHandler;
public FriendsController(AddPendingFriendHandler addPendingFriendHandler, GetFriendsRequestHandler getFriendsRequestHandler,
AcceptFriendRequestHandler acceptFriendRequestHandler, RemoveFriendRequestHandler removeFriendRequestHandler)
AcceptFriendRequestHandler acceptFriendRequestHandler, RemoveFriendRequestHandler removeFriendRequestHandler,
SetAvatarIconRequestHandler setAvatarIconRequestHandler)
{
_addPendingFriendHandler = addPendingFriendHandler;
_getFriendsRequestHandler = getFriendsRequestHandler;
_acceptFriendRequestHandler = acceptFriendRequestHandler;
_removeFriendRequestHandler = removeFriendRequestHandler;
_setAvatarIconRequestHandler = setAvatarIconRequestHandler;
}
[HttpPost]
[HttpGet]
[Route("/api/addFriendImage.php")]
public ActionResult AddFriendImage([FromQuery] Guid accountId, [FromQuery] long nonce)
public ActionResult AddFriendImage( [FromQuery] SetAvatarIconRequest setAvatarIconRequest )
{
//GET /api/addFriendImage.php?accountId=c64c1e01-34d6-4311-ae40-7baa5eba3016&nonce=8779661927687983114&avatarImageType=/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4&avatarImage=Icon04.png HTTP/1.1
var result = _setAvatarIconRequestHandler.Handle(setAvatarIconRequest);
if (result.SetAvatarIconResultStatus == SetAvatarIconResultStatus.Success)
{
return Ok();
}
else if (result.SetAvatarIconResultStatus == SetAvatarIconResultStatus.ValidationErrors)
{
return BadRequest();
}
else if (result.SetAvatarIconResultStatus == SetAvatarIconResultStatus.DatabaseErrors)
{
return StatusCode(500);
}
Utils.GetRequestObjectAsString(this.HttpContext);
return new JsonResult("{}");
}
[HttpGet]
[Route("/api/addPendingFriend.php")]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace WFClassic.Web.Data.Migrations
{
/// <inheritdoc />
public partial class AddAvatarImageColumnToPlayer : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ActiveAvatarImageType",
table: "Players",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ActiveAvatarImageType",
table: "Players");
}
}
}
@@ -967,6 +967,9 @@ namespace WFClassic.Web.Data.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ActiveAvatarImageType")
.HasColumnType("TEXT");
b.Property<int>("AdditionalPlayerXP")
.HasColumnType("INTEGER");
+1
View File
@@ -21,6 +21,7 @@
public DateTime TrainingDate { get; set; }
public bool ReceivedStartingGear { get; set; }
public string Founder { get; set; }
public string ActiveAvatarImageType { get; set; }
public bool SubscribedToEmails { get; set; }
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace WFClassic.Web.Logic.Friendship.Icon
{
public class SetAvatarIconRequest
{
[JsonPropertyName("accountId")]
public Guid AccountId { get; set; }
[JsonPropertyName("nonce")]
public long Nonce { get; set; }
[JsonPropertyName("avatarImageType")]
public string AvatarImageType { get; set; }
[JsonPropertyName("avatarImage")]
public string AvatarImage { get; set; }
}
}
@@ -0,0 +1,67 @@
using Microsoft.EntityFrameworkCore;
using WFClassic.Web.Data;
using WFClassic.Web.Data.Models;
namespace WFClassic.Web.Logic.Friendship.Icon
{
public class SetAvatarIconRequestHandler
{
private ApplicationDbContext _applicationDbContext;
private ILogger<SetAvatarIconRequestHandler> _logger;
public SetAvatarIconRequestHandler(ApplicationDbContext applicationDbContext, ILogger<SetAvatarIconRequestHandler> logger)
{
_applicationDbContext = applicationDbContext;
_logger = logger;
}
public SetAvatarIconResult Handle(SetAvatarIconRequest setAvatarIconRequest)
{
SetAvatarIconResult result = new SetAvatarIconResult();
var validationResults = new SetAvatarIconRequestValidator().Validate(setAvatarIconRequest);
if (!validationResults.IsValid)
{
_logger.LogError("SetAvatarIconRequestHandler => accountId {AccountID} nonce {Nonce} => Validation failure {ValidationErrors}", setAvatarIconRequest.AccountId, setAvatarIconRequest.Nonce, string.Join(";", validationResults.Errors.Select(s => $"{s.ErrorCode} {s.ErrorMessage}")));
result.SetAvatarIconResultStatus = SetAvatarIconResultStatus.ValidationErrors;
return result;
}
bool playerHasAvatarInInventory = false ;
try
{
_logger.LogInformation("SetAvatarIconRequestHandler => accountId {AccountID} nonce {Nonce} => checking to see if player has icon in inventory ", setAvatarIconRequest.AccountId, setAvatarIconRequest.Nonce );
playerHasAvatarInInventory = _applicationDbContext.InventoryItems.Any(a => a.Player.ApplicationUserId == setAvatarIconRequest.AccountId && a.ItemType == setAvatarIconRequest.AvatarImageType);
}
catch (Exception ex)
{
_logger.LogError("SetAvatarIconRequestHandler => accountId {AccountID} nonce {Nonce} => Exception while checking for player inventory {Ex}", setAvatarIconRequest.AccountId, setAvatarIconRequest.Nonce, ex);
result.SetAvatarIconResultStatus = SetAvatarIconResultStatus.DatabaseErrors;
return result;
}
if(!playerHasAvatarInInventory)
{
_logger.LogError("SetAvatarIconRequestHandler => accountId {AccountID} nonce {Nonce} => Player does not have avatar image in inventory ", setAvatarIconRequest.AccountId, setAvatarIconRequest.Nonce);
result.SetAvatarIconResultStatus = SetAvatarIconResultStatus.ValidationErrors;
return result;
}
try
{
_logger.LogInformation("SetAvatarIconRequestHandler => accountId {AccountID} nonce {Nonce} => Player has item in inventory. updating player object ", setAvatarIconRequest.AccountId, setAvatarIconRequest.Nonce);
_applicationDbContext.Players.Where(w => w.ApplicationUserId == setAvatarIconRequest.AccountId).ExecuteUpdate(eu => eu.SetProperty(sp => sp.ActiveAvatarImageType, setAvatarIconRequest.AvatarImageType));
_logger.LogInformation("SetAvatarIconRequestHandler => accountId {AccountID} nonce {Nonce} => update successful ", setAvatarIconRequest.AccountId, setAvatarIconRequest.Nonce);
result.SetAvatarIconResultStatus = SetAvatarIconResultStatus.Success;
}
catch (Exception ex)
{
_logger.LogError("SetAvatarIconRequestHandler => accountId {AccountID} nonce {Nonce} => Exception while updating player object {Ex}", setAvatarIconRequest.AccountId, setAvatarIconRequest.Nonce, ex);
result.SetAvatarIconResultStatus = SetAvatarIconResultStatus.DatabaseErrors;
}
return result;
}
}
}
@@ -0,0 +1,15 @@
using FluentValidation;
namespace WFClassic.Web.Logic.Friendship.Icon
{
public class SetAvatarIconRequestValidator : AbstractValidator<SetAvatarIconRequest>
{
public SetAvatarIconRequestValidator()
{
RuleFor(r => r.AccountId).NotEmpty();
RuleFor(r => r.Nonce).GreaterThan(0);
RuleFor(r => r.AvatarImage).NotEmpty();
RuleFor(r => r.AvatarImageType).NotEmpty();
}
}
}
@@ -0,0 +1,14 @@
namespace WFClassic.Web.Logic.Friendship.Icon
{
public class SetAvatarIconResult
{
public SetAvatarIconResultStatus SetAvatarIconResultStatus { get; set; }
}
public enum SetAvatarIconResultStatus
{
ValidationErrors,
DatabaseErrors,
Success
}
}
@@ -58,7 +58,8 @@ namespace WFClassic.Web.Logic.Inventory.Get
MiscItems = GetJsonTypeCount(InternalInventoryItemType.MiscItems, player.InventoryItems),
Recipes = GetJsonTypeCount(InternalInventoryItemType.Recipes, player.InventoryItems),
//Components = GetJsonTypeCount(InternalInventoryItemType.MiscItems, player.InventoryItems),
FlavourItems = player.InventoryItems.Where(w=>w.InternalInventoryItemType == InternalInventoryItemType.FlavourItems).Select(s=> new GetInventoryResultJsonFlavourItem() { ItemType = s.ItemType}).ToList(),
ActiveAvatarImageType = player.ActiveAvatarImageType,
Missions = player.Missions == null ? new List<GetInventoryResultJsonMission>() : player.Missions.Select(s => new GetInventoryResultJsonMission() { BestRating = s.BestRatings, Completes = s.Completes, Tag = s.Tag }).ToList(),
PlayerLevel = player.PlayerLevel,
PlayerXP = player.PlayerXP,
@@ -113,8 +113,21 @@ namespace WFClassic.Web.Logic.Inventory.Get
[JsonPropertyName("Upgrades")]
public List<GetInventoryResultJsonUpgradeItem> Upgrades { get; set; }
[JsonPropertyName("FlavourItems")]
public List<GetInventoryResultJsonFlavourItem> FlavourItems { get;set; }
[JsonPropertyName("ActiveAvatarImageType")]
public string ActiveAvatarImageType { get; set; }
}
public class GetInventoryResultJsonFlavourItem
{
[JsonProperty("ItemType")]
public string ItemType { get; set; }
}
public class GetInventoryResultJsonXpInfoItem
{
[JsonProperty("ItemType")]
@@ -143,6 +156,12 @@ namespace WFClassic.Web.Logic.Inventory.Get
[JsonPropertyName("UpgradeFingerprint")]
public string UpgradeFingerPrint { get; set; }
[JsonPropertyName("Rank")]
public int Rank { get; set; } = 10;
[JsonPropertyName("AmountRemaining")]
public int AmountRemaining { get; set; } = 99999;
}
public class GetInventoryResultJsonTypeCount
+2 -2
View File
@@ -35,6 +35,7 @@ using WFClassic.Web.Logic.Sys.SystemLogout;
using WFClassic.Web.Logic.Sys.Scheduled;
using Coravel;
using WFClassic.Web.Logic.Bonus.Daily;
using WFClassic.Web.Logic.Friendship.Icon;
var builder = WebApplication.CreateBuilder(args);
@@ -76,7 +77,6 @@ builder.Services.AddTransient<GetFriendsRequestHandler>();
builder.Services.AddTransient<RemoveFriendRequestHandler>();
builder.Services.AddTransient<GetLeaderboardStatsHandler>();
builder.Services.AddTransient<GetProfileStatsHandler>();
builder.Services.AddTransient<CheckPendingRecipesQueryHandler>();
builder.Services.AddTransient<StartRecipeBuildHandler>();
builder.Services.AddTransient<ClaimCompletedRecipeHandler>();
@@ -92,7 +92,7 @@ builder.Services.AddScoped<ResetWarframeRevivesHandler>();
builder.Services.AddTransient<MassLogoutUsersHandler>();
builder.Services.AddTransient<ResetWarframeRevivesHandler>();
builder.Services.AddTransient<GetDailyMissionBonusHandler>();
builder.Services.AddTransient<SetAvatarIconRequestHandler>();
builder.Services.AddScheduler();
builder.Services.AddControllersWithViews();
-1
View File
@@ -24,7 +24,6 @@
<ItemGroup>
<Folder Include="Logic\Guild\Create\" />
<Folder Include="Logic\Inventory\AttachOrokin\" />
</ItemGroup>
<ItemGroup>
Binary file not shown.
+1 -1
View File
@@ -11,7 +11,7 @@
"AllowedHosts": "*",
"AutomaticallyCreateAccountUponInitialLogin": true,
"BuildLabel": "2013.04.01.16.24/"
"BuildLabel": "2013.05.03.18.06/"
/*
"2013.05.03.18.06/", // 7.10