finish migration, added [Authorize]

This commit is contained in:
Lapis
2026-06-07 16:34:19 -04:00
parent e77309d0ec
commit c7437e9f84
18 changed files with 1918 additions and 2652 deletions
+1
View File
@@ -9,6 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.8" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.2.1" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.2.1" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.2.1" />
+36 -36
View File
@@ -1,38 +1,38 @@
{ {
"Accounts": "https://accounts.lapis.codes", "Accounts": "https://cannednet.lapis.codes/accounts",
"AI": "https://ai.lapis.codes", "AI": "https://cannednet.lapis.codes/ai",
"API": "https://api.lapis.codes", "API": "https://cannednet.lapis.codes/api",
"Auth": "https://auth.lapis.codes", "Auth": "https://cannednet.lapis.codes/auth",
"BugReporting": "https://bugreporting.lapis.codes", "BugReporting": "https://cannednet.lapis.codes/bugreporting",
"Cards": "https://cards.lapis.codes", "Cards": "https://cannednet.lapis.codes/cards",
"CDN": "https://cdn.lapis.codes", "CDN": "https://cannednet.lapis.codes/cdn",
"Chat": "https://chatrr.lapis.codes", "Chat": "https://cannednet.lapis.codes/chat",
"Clubs": "https://clubs.lapis.codes", "Clubs": "https://cannednet.lapis.codes/clubs",
"CMS": "https://cms.lapis.codes", "CMS": "https://cannednet.lapis.codes/cms",
"Commerce": "https://commerce.lapis.codes", "Commerce": "https://cannednet.lapis.codes/commerce",
"Data": "https://data.lapis.codes", "Data": "https://cannednet.lapis.codes/data",
"DataCollection": "https://datacollection.lapis.codes", "DataCollection": "https://cannednet.lapis.codes/datacollection",
"Discovery": "https://discovery.lapis.codes", "Discovery": "https://cannednet.lapis.codes/discovery",
"Econ": "https://econ.lapis.codes", "Econ": "https://cannednet.lapis.codes/econ",
"GameLogs": "https://gamelogs.lapis.codes", "GameLogs": "https://cannednet.lapis.codes/gamelogs",
"Geo": "https://geo.lapis.codes", "Geo": "https://cannednet.lapis.codes/geo",
"Images": "https://img.lapis.codes", "Images": "https://cannednet.lapis.codes/img",
"Leaderboard": "https://leaderboard.lapis.codes", "Leaderboard": "https://cannednet.lapis.codes/leaderboard",
"Link": "https://link.lapis.codes", "Link": "https://cannednet.lapis.codes/link",
"Lists": "https://lists.lapis.codes", "Lists": "https://cannednet.lapis.codes/lists",
"Matchmaking": "https://match.lapis.codes", "Matchmaking": "https://cannednet.lapis.codes/match",
"Moderation": "https://moderation.lapis.codes", "Moderation": "https://cannednet.lapis.codes/moderation",
"Notifications": "https://notify.lapis.codes", "Notifications": "https://cannednet.lapis.codes/notify",
"PlatformNotifications": "https://platformnotifications.lapis.codes", "PlatformNotifications": "https://cannednet.lapis.codes/platformnotifications",
"PlayerSettings": "https://playersettings.lapis.codes", "PlayerSettings": "https://cannednet.lapis.codes/playersettings",
"RoomComments": "https://roomcomments.lapis.codes", "RoomComments": "https://cannednet.lapis.codes/roomcomments",
"RoomieIntegrations": "https://roomieintegrations.lapis.codes", "RoomieIntegrations": "https://cannednet.lapis.codes/roomieintegrations",
"Rooms": "https://rooms.lapis.codes", "Rooms": "https://cannednet.lapis.codes/rooms",
"Storage": "https://storage.lapis.codes", "Storage": "https://cannednet.lapis.codes/storage",
"Strings": "https://strings.lapis.codes", "Strings": "https://cannednet.lapis.codes/strings",
"StringsCDN": "https://strings-cdn.lapis.codes", "StringsCDN": "https://cannednet.lapis.codes/stringscdn",
"Studio": "https://studio.lapis.codes", "Studio": "https://cannednet.lapis.codes/studio",
"Thorn": "https://thorn.lapis.codes", "Thorn": "https://cannednet.lapis.codes/thorn",
"Videos": "https://videos.lapis.codes", "Videos": "https://cannednet.lapis.codes/videos",
"WWW": "https://lapis.codes" "WWW": "https://cannednet.lapis.codes"
} }
+34
View File
@@ -2,8 +2,10 @@ using CannedNet.Data;
using CannedNet.Hubs; using CannedNet.Hubs;
using CannedNet.Services; using CannedNet.Services;
using CannedNet.Services.Infrastructure; using CannedNet.Services.Infrastructure;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
namespace CannedNet; namespace CannedNet;
@@ -23,6 +25,7 @@ public static class Program
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.PropertyNamingPolicy = null); builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.PropertyNamingPolicy = null);
builder.Services.AddDbContext<AppDbContext>(options => options.UseNpgsql(connectionString)); builder.Services.AddDbContext<AppDbContext>(options => options.UseNpgsql(connectionString));
builder.Services.AddSingleton<NotificationService>(); builder.Services.AddSingleton<NotificationService>();
@@ -30,12 +33,43 @@ public static class Program
builder.Services.AddScoped<JwtTokenService>(); builder.Services.AddScoped<JwtTokenService>();
builder.Services.AddSignalR(); builder.Services.AddSignalR();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
var rsa = Signatures.GetRsaInstance();
var securityKey = new RsaSecurityKey(rsa) { KeyId = "7C2F041398671515B0862CB23FAF95B03" };
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = securityKey,
ValidateIssuer = true,
ValidIssuer = "https://lapis.codes",
ValidateAudience = true,
ValidAudience = "https://lapis.codes/resources",
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
// sub is account id
NameClaimType = "sub",
RoleClaimType = "role"
};
});
builder.Services.AddAuthorization();
WebApplication app = builder.Build(); WebApplication app = builder.Build();
app.MapHub<NotificationsHub>("/hub/v1"); app.MapHub<NotificationsHub>("/hub/v1");
app.UseHttpsRedirection(); app.UseHttpsRedirection();
//app.UseRequestLogging(); //app.UseRequestLogging();
app.MapControllers(); app.MapControllers();
app.UseAuthentication();
app.UseAuthorization();
IHubContext<NotificationsHub> hubContext = app.Services.GetRequiredService<IHubContext<NotificationsHub>>(); IHubContext<NotificationsHub> hubContext = app.Services.GetRequiredService<IHubContext<NotificationsHub>>();
NotificationService.SetHubContext(hubContext); NotificationService.SetHubContext(hubContext);
+779 -1397
View File
@@ -2,6 +2,7 @@ using CannedNet.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Text.Json; using System.Text.Json;
using CannedNet.Models; using CannedNet.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
@@ -111,798 +112,281 @@ public class APIController : ControllerBase
return Results.Json(progressions); return Results.Json(progressions);
} }
[HttpGet("avatar/v4/items")]
public async Task<IResult> GetAvatarItemsV4(HttpRequest request, AppDbContext db, JwtTokenService jwtService) {
string authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
string token = authHeader.Substring("Bearer ".Length); [HttpPost("avatar/v2/set")]
string? accountId = jwtService.ValidateAndGetAccountId(token); [Authorize]
public async Task<IResult> SetAvatarV2(HttpRequest request, AppDbContext db, JwtTokenService jwtService)
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
request.EnableBuffering();
var avatarUpdate = await System.Text.Json.JsonSerializer.DeserializeAsync<PlayerAvatar>(request.Body);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id)) if (avatarUpdate == null)
return Results.Unauthorized(); return Results.BadRequest();
List<AvatarItem> ownedItems = await db.AvatarItems var avatar = await db.PlayerAvatars
.Where(i => i.OwnerAccountId == id) .FirstOrDefaultAsync(a => a.OwnerAccountId == id);
.ToListAsync();
List<object> defaultItems = new List<object>
{
new { AvatarItemType = 0, AvatarItemDesc = "5d13a7a2-8213-40e6-90a6-efdd76a3fdcb,,,", PlatformMask = -1, FriendlyName = "Flowing Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "1d27b674-f9e2-4ffc-9d8c-a58a1be06457,,,", PlatformMask = -1, FriendlyName = "Afro Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d84c0ff9-8fbe-4ed8-abf3-7996e81888ab,,,", PlatformMask = -1, FriendlyName = "Large Afro Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e5b83dfc-b2e1-4dcb-a4ab-9d3a4c8a34ae,,,", PlatformMask = -1, FriendlyName = "Long Wavy Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7dd6f7b0-7ba0-429f-a04f-e32d3a79ee61,,,", PlatformMask = -1, FriendlyName = "Short Wavy Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "eb9611c6-bb50-41a2-93e9-7f959815a846,,,", PlatformMask = -1, FriendlyName = "Dreads Long Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "1fd69ef8-0b74-4962-af5a-67f0bf0358f2,,,", PlatformMask = -1, FriendlyName = "Ponytail Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "a12f724f-4a73-4ab8-aad4-6bfc662b4dd6,,,", PlatformMask = -1, FriendlyName = "Undercut Long Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0753d7a4-8247-4fca-a6fc-359c26086140,,,", PlatformMask = -1, FriendlyName = "Fonzie Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "77d3c585-4928-4471-a425-89036efe7299,,,", PlatformMask = -1, FriendlyName = "Spiky Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "92302d9d-c527-418c-ac5d-1fa869727505,,,", PlatformMask = -1, FriendlyName = "Part Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "f9dd08f8-16d3-4c39-af4f-89f7bb6e80d3,,,", PlatformMask = -1, FriendlyName = "Undercut Short Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b148cb1e-df81-442f-aea6-ab1727aad00e,,,", PlatformMask = -1, FriendlyName = "Chunky Afro Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e36bcd98-7e85-43fa-89f8-57e4ec33823a,,,", PlatformMask = -1, FriendlyName = "Bob with Bangs Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "880a3cc0-7407-4b61-b759-f9dd890fe9e5,,,", PlatformMask = -1, FriendlyName = "Bob Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21599b51-c50f-43d8-ac5f-62c30cd02ca5,,,", PlatformMask = -1, FriendlyName = "Lori Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "193a3bf9-abc0-4d78-8d63-92046908b1c5,,,", PlatformMask = -1, FriendlyName = "Emo Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "79b90274-6eec-4664-acfb-4a123334661e,,,", PlatformMask = -1, FriendlyName = "Pig Tails Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "da4e7b34-2095-4a9e-801e-4f409039e0dd,,,", PlatformMask = -1, FriendlyName = "Buzz Cut Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9d9fadb6-97eb-480e-a224-4e0179082071,,,", PlatformMask = -1, FriendlyName = "Meatball Buns Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d8280c0c-d803-4513-be10-a0ba96d8821e,,,", PlatformMask = -1, FriendlyName = "Flowhawk Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e286863c-2967-4d00-b837-b49487b9484a,,,", PlatformMask = -1, FriendlyName = "Fauxhawk Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2cb4f372-3372-4583-8b57-c4e3988e3c28,,,", PlatformMask = -1, FriendlyName = "Punky Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "06306723-ca20-4aa6-b7b3-917113f41ac3,,,", PlatformMask = -1, FriendlyName = "Cat-Eye Glasses (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c70005d5-6276-4a98-acb3-6a77bc19379a,,,", PlatformMask = -1, FriendlyName = "Glasses (Teal)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8d10cc78-6b00-45f3-affb-205e9cc5b03f,,,", PlatformMask = -1, FriendlyName = "Beard (Close)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "cc96f8a5-bc5b-4f89-83b7-ecd53905ada7,,,", PlatformMask = -1, FriendlyName = "Beard (Thick)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c6c08eb5-381a-4193-9722-80da95d62abe,,,", PlatformMask = -1, FriendlyName = "Business Tie (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "4d507dfa-4a99-4ac0-8537-229e9dc0eb4a,,,", PlatformMask = -1, FriendlyName = "Rec Room Tank Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d0a9262f-5504-46a7-bb10-7507503db58e,,,", PlatformMask = -1, FriendlyName = "Rec Room Shirt (Crew Neck, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d0a9262f-5504-46a7-bb10-7507503db58e,95e4cc30-cb68-473d-a395-feadf5b51512,0440f08f-ef1d-49d8-942b-523056e8bb45,", PlatformMask = -1, FriendlyName = "Rec Room T-Shirt (Crew Neck, Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,,,", PlatformMask = -1, FriendlyName = "Collared Shirt (Plaid, Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,,,", PlatformMask = -1, FriendlyName = "T-Shirt", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8aa79563-ace1-4ba7-ad0c-f3210a78142f,,,", PlatformMask = -1, FriendlyName = "Rec Room Shirt (V-Neck, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8aa79563-ace1-4ba7-ad0c-f3210a78142f,95e4cc30-cb68-473d-a395-feadf5b51512,05f0ee6e-c824-470e-9178-5ed576c6fe0c,", PlatformMask = -1, FriendlyName = "Rec Room T-Shirt (V-Neck, Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,,,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,c5deba2a-6e35-4b13-8e94-8ba5457f39df,b75ef67d-00c3-4ac1-9b72-212032460294,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,758752bd-db2f-43d2-b580-55b3e1efffd5,b75ef67d-00c3-4ac1-9b72-212032460294,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,,,", PlatformMask = -1, FriendlyName = "Zipper Dress (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,,,", PlatformMask = -1, FriendlyName = "Wristbands (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,,,", PlatformMask = -1, FriendlyName = "Wrist Tape (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,55901f12-d5b5-4fa8-b4c8-e479689ee39d,f600037d-c9c0-43fa-b45b-02f456f9dd5f,", PlatformMask = -1, FriendlyName = "Collared Shirt (Denim)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,bf82f2f6-9af8-431e-a296-0890dea48ba7,d015cae7-a905-49e4-8823-6dec069689a6,", PlatformMask = -1, FriendlyName = "Collared Shirt (Argyle)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,EfdMcnfHt0mr0PQ_maaYOg,DRJcNhkqvkKFEaZpOguR6w,", PlatformMask = -1, FriendlyName = "Collared Shirt (Flowers, Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,6d703981-2734-4c45-8983-cdd5f328902f,a0271cd0-e172-4d3f-aa2f-9806f21a82d2,", PlatformMask = -1, FriendlyName = "Tank Top (Camo)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,5c4a2b35-0e1c-44de-8c3a-96d4a6458b1b,9c03f381-7357-4d0f-8cda-8737d4c43d25,", PlatformMask = -1, FriendlyName = "Tank Top (Rainbow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,d2a692e6-e1a9-4cfe-8154-10b52be7f8c8,", PlatformMask = -1, FriendlyName = "Jersey (Orange)", Tooltip = "", Rarity = 10 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,ad61c418-6d77-4a99-8ac5-9f10f5a3d42f,b292eb4b-07e3-4a48-99b5-3c6587a1e02e,", PlatformMask = -1, FriendlyName = "Tank Top (Dots)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,48abd952-214f-48b2-a8f1-1146f6f69aa2,b78008e8-abbd-4ece-be34-9a911f721fcc,", PlatformMask = -1, FriendlyName = "Tank Top (Zebra)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Orange, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Pink, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Blue, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,6dd95046-acf8-42fe-ab78-80a334096a9d,56a92c8d-af53-413e-929e-4a9a3cfad780,", PlatformMask = -1, FriendlyName = "Headband (Red, White, Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fcfcaf63-deb4-45f7-b711-c051c9ea45cb,,,", PlatformMask = -1, FriendlyName = "Top Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,,,", PlatformMask = -1, FriendlyName = "Blazer (Blue, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,6f2e74bf-1e95-463d-97db-d5d1a53b2c28,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Black, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,9374bf66-2ee5-493b-8439-efce4b201904,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Grey, Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,272fe8eb-5061-4729-a7a8-414ff667a82f,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Grey, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,0ffad843-d6c9-425a-8686-7217009c867e,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Green, Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,e0397982-c2c2-4733-9a40-46e18675b5af,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,,,", PlatformMask = -1, FriendlyName = "Button Top (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,49f5864f-9d40-497c-88c8-e87f64d41d74,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Tan)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,c5deba2a-6e35-4b13-8e94-8ba5457f39df,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,f750de46-3758-4f7d-9709-0a84b1027009,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,d66aa400-aa5a-4539-a25d-5f8ce94dc281,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,6564acf1-4d70-4f92-92ac-08e2b76dbb6b,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,,,", PlatformMask = -1, FriendlyName = "Collared Dress (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "241506f6-bf88-4b46-b5fe-513a225421f4,,,", PlatformMask = -1, FriendlyName = "Half Up Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,d6edbc00-3c1d-4f49-8412-3ef8c7c5f4c2,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,6d703981-2734-4c45-8983-cdd5f328902f,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,830be2fa-60a5-48cc-931f-34b670eae4bd,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,bbfa08e3-8e6b-4e0f-b264-1b398d7cd44a,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,f8b0cfe8-e129-4578-8bb5-f60af5d38599,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Gold)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,5c4a2b35-0e1c-44de-8c3a-96d4a6458b1b,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,6dd95046-acf8-42fe-ab78-80a334096a9d,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,f8b0cfe8-e129-4578-8bb5-f60af5d38599,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,,,", PlatformMask = -1, FriendlyName = "Headband (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "24a240f4-1574-420b-b898-a7e91f170759,,,", PlatformMask = -1, FriendlyName = "Back Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c45ed7b8-99bd-4a4b-a9ff-e16edf5d7a18,,,", PlatformMask = -1, FriendlyName = "High Pony Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,,,", PlatformMask = -1, FriendlyName = "Baseball Cap (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,,,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,55901f12-d5b5-4fa8-b4c8-e479689ee39d,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,4828b50c-95b6-466a-bb25-514891d78202,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,d6823e01-69f0-4f85-b94a-74894356a2cf,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Maroon)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "09177621-9ecd-4f6a-b6a5-64490139141d,,,", PlatformMask = -1, FriendlyName = "Flat Top Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "95ab7a7c-c35d-4da5-9955-0921064470b6,,,", PlatformMask = -1, FriendlyName = "Gekko Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,0ecb8a2a-cffc-47db-aeda-fb0684aef1e5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,8377ab96-c908-457f-9fee-b784c9a759f3,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Red, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,,,", PlatformMask = -1, FriendlyName = "Slim Blazer (Teal)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,cd5d7285-202d-42d0-b93f-04245875793e,0f36bb97-c61b-4281-929f-ff1d0d11be86,", PlatformMask = -1, FriendlyName = "Slim Blazer (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,ad61c418-6d77-4a99-8ac5-9f10f5a3d42f,0f36bb97-c61b-4281-929f-ff1d0d11be86,", PlatformMask = -1, FriendlyName = "Slim Blazer (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8b9f1413-e786-4a30-946c-9292f207875a,,,", PlatformMask = -1, FriendlyName = "Pulp Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "5cd08cfb-c729-4c30-96d9-6a99bb934d91,,,", PlatformMask = -1, FriendlyName = "Rec Room Sash", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "1a71064b-794f-40fa-9109-8ad36602b6e1,,,", PlatformMask = -1, FriendlyName = "Shagg Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,761a3193-60f0-4190-80c7-285b8192e794,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,a819f49b-6c7a-49d3-9e6a-d9d79ef5019f,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,64850553-cdfe-455a-ac00-dafbe63d613e,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,,,", PlatformMask = -1, FriendlyName = "Stoll Dress (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wrist Tape (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wrist Tape (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,0ecb8a2a-cffc-47db-aeda-fb0684aef1e5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,,,", PlatformMask = -1, FriendlyName = "Long Scarf (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,0iSsaY-HgkmLaRHCn5vEdw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,jGj28vhq8EGwP2RuM074aQ,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,kmj5zOjcwku_WWKroCeiVQ,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,FAviMCQ_EE2Mpt6QPo5OEw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,MFrcSQ1DYUm8imvy4ypgvw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,05ac07e1-67f0-486c-abf5-a62866475abb,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Black, Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0088603e-ec3b-4478-8694-e6fb1989b3f2,,,", PlatformMask = -1, FriendlyName = "Angled Bob Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ffea7a65-613f-4835-921e-6dd15f357b7e,,,", PlatformMask = -1, FriendlyName = "Long Bangs Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "45f5e714-8a5f-4385-a97f-675066167011,,,", PlatformMask = -1, FriendlyName = "Seventies Stache", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9bf5d259-7774-4cbe-a90f-7f188cc0dce7,,,", PlatformMask = -1, FriendlyName = "Thick Goatee", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "a6cbfe76-534a-4655-a8a8-3fed13d001c7,,,", PlatformMask = -1, FriendlyName = "Bald Top Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "CTcrvbo3OEepIV4oW8bx4w,,,", PlatformMask = -1, FriendlyName = "Receding Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "-twtjyBdQ02EAdOfBGTiEw,,,", PlatformMask = -1, FriendlyName = "Van Dyke Beard", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "45eaab67-19c2-4601-8f80-3565a4dceba4,,,", PlatformMask = -1, FriendlyName = "Pompadour Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c855dcc3-96cb-470d-b159-d37a025a47d1,,,", PlatformMask = -1, FriendlyName = "Dutch Braid Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d7730a9e-78a1-4356-bc09-6b066615850b,,,", PlatformMask = -1, FriendlyName = "Afro Updo Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8c35c804-e8d5-49d2-8d5a-ea19fb70bfa6,,,", PlatformMask = -1, FriendlyName = "Pencil Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "5beeb4c4-f276-4eae-87aa-9302e45b05b7,,,", PlatformMask = -1, FriendlyName = "Cornrows Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b6rLwzD4NkKV7xKn9ZYVkA,sxUE0iOSZEmezm54T7xI3Q,tlpa7195x0CkmSjpR1RArQ,", PlatformMask = -1, FriendlyName = "Rec Room Hoodie - Pride (Rainbow Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fe15ca53-c5b8-4acf-9309-ff3f4e610fc9,,,", PlatformMask = -1, FriendlyName = "Winged Hat - Pride (Rainbow Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b6rLwzD4NkKV7xKn9ZYVkA,D_Xmo0rOzkS-kgq1CYXt3g,tnCJp2eDI0SwjVfJMhk3LQ,", PlatformMask = -1, FriendlyName = "Rec Room Hoodie - Pride (Trans Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fe15ca53-c5b8-4acf-9309-ff3f4e610fc9,knXPidb-Rkayfc3kSHfZeQ,1yMyo6oTjU-VAygoeWaohQ,", PlatformMask = -1, FriendlyName = "Winged Hat - Pride (Trans Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "88b6ddeb-a455-460d-91d9-a4569ef6903c,,,", PlatformMask = -1, FriendlyName = "Square Earrings ", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0abb6b08-20ce-444f-879e-0d1344df096c,,,", PlatformMask = -1, FriendlyName = "Round Earrings", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9b5bde11-7408-4798-9fcb-c7ec175444df,,,", PlatformMask = -1, FriendlyName = "Hoop Earrings", Tooltip = "", Rarity = 0 }
};
List<object> allItems = ownedItems.Concat(defaultItems).ToList();
return Results.Json(allItems);
}
[HttpGet("avatar/v2")]
public async Task<IResult> GetAvatarV2(HttpRequest request, AppDbContext db, JwtTokenService jwtService) {
string authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
string token = authHeader.Substring("Bearer ".Length);
string? accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
PlayerAvatar? avatar = await db.PlayerAvatars.FirstOrDefaultAsync(a => a.OwnerAccountId == id);
if (avatar == null) if (avatar == null)
{ {
avatar = new PlayerAvatar avatar = new PlayerAvatar { OwnerAccountId = id };
{
OwnerAccountId = id,
OutfitSelections = "",
FaceFeatures = "{}",
SkinColor = "",
HairColor = ""
};
db.PlayerAvatars.Add(avatar); db.PlayerAvatars.Add(avatar);
await db.SaveChangesAsync();
} }
return Results.Json(new avatar.OutfitSelections = avatarUpdate.OutfitSelections;
avatar.FaceFeatures = avatarUpdate.FaceFeatures;
avatar.SkinColor = avatarUpdate.SkinColor;
avatar.HairColor = avatarUpdate.HairColor;
await db.SaveChangesAsync();
return Results.Ok(avatar);
}
[HttpGet("PlayerReporting/v1/moderationBlockDetails")]
public IResult GetModerationBlockDetails()
{
return Results.Content("{\"ReportCategory\":0,\"Duration\":0,\"GameSessionId\":0,\"IsHostKick\":false,\"Message\":\"\",\"PlayerIdReporter\":null,\"IsBan\":false}", "application/json");
}
[HttpGet("PlayerReporting/v1/voteToKickReasons")]
public IResult GetVoteToKickReasons()
{
var json = System.IO.File.ReadAllText("JSON/vtkreasons.json");
return Results.Content(json, "application/json");
}
[HttpGet("settings/v2")]
[Authorize]
public async Task<IResult> GetSettings(HttpRequest request, AppDbContext db, JwtTokenService jwtService)
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
var settings = await db.PlayerSettings
.Where(s => s.PlayerId == id)
.ToListAsync();
if (!settings.Any())
{ {
OutfitSelections = avatar.OutfitSelections, var defaults = new List<PlayerSetting>
FaceFeatures = avatar.FaceFeatures, {
SkinColor = avatar.SkinColor, new() { PlayerId = id, Key = "Recroom.OOBE", Value = "77" },
HairColor = avatar.HairColor new() { PlayerId = id, Key = "SplitTestAssignedSegments", Value = "1|{\"SplitTesting+PhotonMaxDatagrams_2021_01_11\":\"Off\",\"SplitTesting+Curated_Rooms_2020_08_06\":\"Off\",\"SplitTesting+RoomRecommendationsType_2020_08_14\":\"Aug14MinVisitors35000\"}" },
}); new() { PlayerId = id, Key = "PlayerSessionCount", Value = "13" },
new() { PlayerId = id, Key = "TUTORIAL_COMPLETE_MASK", Value = "11" },
new() { PlayerId = id, Key = "BACKPACK_FAVORITE_TOOL", Value = "1" },
new() { PlayerId = id, Key = "VoiceChat", Value = "2" },
new() { PlayerId = id, Key = "VRAUTOSPRINT", Value = "1" },
new() { PlayerId = id, Key = "VR_MOVEMENT_MODE", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_SPRINT", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_WALK", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_VEHICLES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FLY", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_ROTATE", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FORCES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FALL", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_TELEPORT", Value = "0" },
new() { PlayerId = id, Key = "ROTATE_IN_PLACE_ENABLED", Value = "1" },
new() { PlayerId = id, Key = "ROTATION_INCREMENT", Value = "2" },
new() { PlayerId = id, Key = "CONTINUOUS_ROTATION_MODE", Value = "1" },
new() { PlayerId = id, Key = "DONT_LOCK_TOOLS_TO_HAND", Value = "0" },
new() { PlayerId = id, Key = "QualitySettings", Value = "2" },
new() { PlayerId = id, Key = "TeleportBuffer", Value = "0" },
new() { PlayerId = id, Key = "IgnoreBuffer", Value = "1" },
new() { PlayerId = id, Key = "FIRST_TIME_IN_FLAGS", Value = "0" },
new() { PlayerId = id, Key = "ShowRoomCenter", Value = "1" },
new() { PlayerId = id, Key = "USER_TRACKING", Value = "1" },
new() { PlayerId = id, Key = "STABILIZE_HANDS", Value = "0" },
new() { PlayerId = id, Key = "MakerPen_SnappingMode", Value = "2" },
new() { PlayerId = id, Key = "Recroom.ChallengeMap", Value = "17" },
new() { PlayerId = id, Key = "VoiceFilter2", Value = "1" },
new() { PlayerId = id, Key = "SFX_VOLUME_PERCENT_PREF", Value = "1" },
};
db.PlayerSettings.AddRange(defaults);
await db.SaveChangesAsync();
settings = defaults;
}
return Results.Json(settings);
}
[HttpGet("accounts/v1/getBio")]
[Authorize]
public async Task<IResult> GetAccountBio(HttpRequest request, AppDbContext db)
{
int accountId = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
var bio = await db.PlayerBios
.Where(s => accountId == accountId)
.ToListAsync();
return Results.Json(bio);
} }
[HttpGet("communityboard/v2/current")]
public async Task<IResult> CommunityBoardCurrent() {
var json = System.IO.File.ReadAllText("JSON/communityboard.json");
return Results.Content(json, "application/json");
}
public void MapEndpoints(WebApplication app) { [HttpGet("playerevents/v1/all")]
var jwtService = app.Services.GetRequiredService<JwtTokenService>(); public async Task<IResult> PlayerEventsAll() {
var storefrontService = app.Services.GetRequiredService<StorefrontFillService>(); return Results.Content("{\"Created\":[],\"Responses\":[]}", "application/json");
var notificationService = app.Services.GetRequiredService<NotificationService>(); }
app.MapPost("/api/avatar/v2/set", async (HttpRequest request, AppDbContext db) => [HttpGet("storefronts/v1/p2p/betaEnabled")]
public IResult StorefrontsP2PBetaEnabled() {
return Results.Content("false", "application/json");
}
[HttpGet("announcement/v1/get")]
public async Task<IResult> AnnouncementGet() {
var json = System.IO.File.ReadAllText("JSON/announcements.json");
return Results.Content(json, "application/json");
}
[HttpGet("quickPlay/v1/getandclear")]
public async Task<IResult> QuickPlayGetAndClear() {
return Results.Content("{\"RoomName\":null,\"ActionCode\":null,\"TargetPlayerId\":null}", "application/json");
}
[HttpGet("roomkeys/v1/room")]
public async Task<IResult> RoomKeysRoom() {
var roomid = Request.Query["roomId"];
return Results.Content("[]", "application/json");
}
[HttpGet("/roomserver/rooms/bulk")]
public async Task<IResult> RoomserverRoomsBulk(AppDbContext db) {
var idParam = Request.Query["id"].FirstOrDefault();
var nameParam = Request.Query["name"].FirstOrDefault();
if (string.IsNullOrEmpty(idParam) && string.IsNullOrEmpty(nameParam))
return Results.BadRequest("Either 'id' or 'name' query parameter is required");
List<Room> results = new();
if (!string.IsNullOrEmpty(idParam))
{ {
var authHeader = request.Headers.Authorization.ToString(); var ids = idParam.Split(',').Select(s => int.TryParse(s.Trim(), out var i) ? i : -1).Where(i => i != -1);
results.AddRange(db.Rooms.Where(r => ids.Contains(r.RoomId)).ToList());
}
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(nameParam))
return Results.Unauthorized(); {
var names = nameParam.Split(',').Select(s => s.Trim().ToLower()).Where(s => !string.IsNullOrEmpty(s));
results.AddRange(db.Rooms.Where(r => names.Any(n => r.Name.ToLower().Contains(n))).ToList());
}
var token = authHeader.Substring("Bearer ".Length); var roomIds = results.Select(r => r.RoomId).ToList();
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId)) if (roomIds.Count == 0)
return Results.Unauthorized(); return Results.Json(new object[0]);
if (!int.TryParse(accountId.AsSpan(), out var id)) var subRooms = db.SubRooms.Where(s => roomIds.Contains(s.RoomId)).ToList();
return Results.Unauthorized(); var roles = db.RoomRoles.Where(r => roomIds.Contains(r.RoomId)).ToList();
var loadScreens = db.LoadScreens.Where(l => roomIds.Contains(l.RoomId)).ToList();
var promoImages = db.PromoImages.Where(p => roomIds.Contains(p.RoomId)).ToList();
var promoExternalContents = db.PromoExternalContents.Where(p => roomIds.Contains(p.RoomId)).ToList();
request.EnableBuffering(); var response = results.Distinct().Select(r => new
var avatarUpdate = await System.Text.Json.JsonSerializer.DeserializeAsync<PlayerAvatar>(request.Body); {
r.RoomId,
if (avatarUpdate == null) r.Name,
return Results.BadRequest(); r.Description,
r.CreatorAccountId,
var avatar = await db.PlayerAvatars r.ImageName,
.FirstOrDefaultAsync(a => a.OwnerAccountId == id); r.State,
r.Accessibility,
if (avatar == null) r.SupportsLevelVoting,
r.IsRRO,
r.IsDorm,
r.CloningAllowed,
r.SupportsVRLow,
r.SupportsQuest2,
r.SupportsMobile,
r.SupportsScreens,
r.SupportsWalkVR,
r.SupportsTeleportVR,
r.SupportsJuniors,
r.MinLevel,
r.WarningMask,
r.CustomWarning,
r.DisableMicAutoMute,
r.DisableRoomComments,
r.EncryptVoiceChat,
r.CreatedAt,
Stats = new { CheerCount = 0, FavoriteCount = 0, VisitorCount = 0, VisitCount = 0 },
SubRooms = subRooms.Where(s => s.RoomId == r.RoomId).Select(s => new
{ {
avatar = new PlayerAvatar { OwnerAccountId = id }; s.SubRoomId,
db.PlayerAvatars.Add(avatar); s.Name,
} s.DataBlob,
s.IsSandbox,
avatar.OutfitSelections = avatarUpdate.OutfitSelections; s.MaxPlayers,
avatar.FaceFeatures = avatarUpdate.FaceFeatures; s.Accessibility,
avatar.SkinColor = avatarUpdate.SkinColor; s.UnitySceneId,
avatar.HairColor = avatarUpdate.HairColor; DataSavedAt = s.DataSavedAt
}).ToList(),
await db.SaveChangesAsync(); Roles = roles.Where(ro => ro.RoomId == r.RoomId).Select(ro => new
return Results.Ok(avatar);
});
app.MapGet("/api/PlayerReporting/v1/moderationBlockDetails", () =>
Results.Content("{\"ReportCategory\":0,\"Duration\":0,\"GameSessionId\":0,\"IsHostKick\":false,\"Message\":\"\",\"PlayerIdReporter\":null,\"IsBan\":false}", "application/json"));
app.MapGet("/api/PlayerReporting/v1/voteToKickReasons", async (HttpRequest request, AppDbContext db) =>
{
var json = System.IO.File.ReadAllText("JSON/vtkreasons.json");
return Results.Content(json, "application/json");
});
app.MapGet("/api/settings/v2" +
"", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var settings = await db.PlayerSettings
.Where(s => s.PlayerId == id)
.ToListAsync();
if (!settings.Any())
{ {
var defaults = new List<PlayerSetting> ro.AccountId,
{ ro.Role,
new() { PlayerId = id, Key = "Recroom.OOBE", Value = "77" }, ro.InvitedRole
new() { PlayerId = id, Key = "SplitTestAssignedSegments", Value = "1|{\"SplitTesting+PhotonMaxDatagrams_2021_01_11\":\"Off\",\"SplitTesting+Curated_Rooms_2020_08_06\":\"Off\",\"SplitTesting+RoomRecommendationsType_2020_08_14\":\"Aug14MinVisitors35000\"}" }, }).ToList(),
new() { PlayerId = id, Key = "PlayerSessionCount", Value = "13" }, LoadScreens = loadScreens.Where(l => l.RoomId == r.RoomId).Select(l => new
new() { PlayerId = id, Key = "TUTORIAL_COMPLETE_MASK", Value = "11" },
new() { PlayerId = id, Key = "BACKPACK_FAVORITE_TOOL", Value = "1" },
new() { PlayerId = id, Key = "VoiceChat", Value = "2" },
new() { PlayerId = id, Key = "VRAUTOSPRINT", Value = "1" },
new() { PlayerId = id, Key = "VR_MOVEMENT_MODE", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_SPRINT", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_WALK", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_VEHICLES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FLY", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_ROTATE", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FORCES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FALL", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_TELEPORT", Value = "0" },
new() { PlayerId = id, Key = "ROTATE_IN_PLACE_ENABLED", Value = "1" },
new() { PlayerId = id, Key = "ROTATION_INCREMENT", Value = "2" },
new() { PlayerId = id, Key = "CONTINUOUS_ROTATION_MODE", Value = "1" },
new() { PlayerId = id, Key = "DONT_LOCK_TOOLS_TO_HAND", Value = "0" },
new() { PlayerId = id, Key = "QualitySettings", Value = "2" },
new() { PlayerId = id, Key = "TeleportBuffer", Value = "0" },
new() { PlayerId = id, Key = "IgnoreBuffer", Value = "1" },
new() { PlayerId = id, Key = "FIRST_TIME_IN_FLAGS", Value = "0" },
new() { PlayerId = id, Key = "ShowRoomCenter", Value = "1" },
new() { PlayerId = id, Key = "USER_TRACKING", Value = "1" },
new() { PlayerId = id, Key = "STABILIZE_HANDS", Value = "0" },
new() { PlayerId = id, Key = "MakerPen_SnappingMode", Value = "2" },
new() { PlayerId = id, Key = "Recroom.ChallengeMap", Value = "17" },
new() { PlayerId = id, Key = "VoiceFilter2", Value = "1" },
new() { PlayerId = id, Key = "SFX_VOLUME_PERCENT_PREF", Value = "1" },
};
db.PlayerSettings.AddRange(defaults);
await db.SaveChangesAsync();
settings = defaults;
}
return Results.Json(settings);
});
app.MapPost("/api/settings/v2/set", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body);
var body = await reader.ReadToEndAsync();
Console.WriteLine($"Settings request body: {body}");
var settings = new List<PlayerSetting>();
if (body.TrimStart().StartsWith("["))
{ {
settings = System.Text.Json.JsonSerializer.Deserialize<List<PlayerSetting>>(body) ?? []; l.ImageUrl,
} l.Tooltip,
else l.IsThumbnail
}).ToList(),
PromoImages = promoImages.Where(p => p.RoomId == r.RoomId).Select(p => new
{ {
var single = System.Text.Json.JsonSerializer.Deserialize<PlayerSetting>(body); p.ImageUrl,
if (single != null) settings.Add(single); p.Tooltip,
} p.SortOrder
}).ToList(),
settings = settings.Where(s => !string.IsNullOrEmpty(s.Key)).ToList(); PromoExternalContent = promoExternalContents.Where(p => p.RoomId == r.RoomId).Select(p => new
if (!settings.Any())
return Results.Ok();
db.PlayerSettings.RemoveRange(db.PlayerSettings.Where(s => s.PlayerId == id));
foreach (var setting in settings)
{ {
setting.PlayerId = id; p.Type,
setting.Key = setting.Key ?? ""; p.Url,
setting.Value = setting.Value ?? ""; p.Tooltip
db.PlayerSettings.Add(setting); }).ToList(),
} Tags = new object[0]
}).ToList();
await db.SaveChangesAsync(); return Results.Json(response);
return Results.Ok(); }
});
app.MapGet("/api/objectives/v1/myprogress", async (HttpRequest request, AppDbContext db) => [HttpGet("/roomserver/rooms/hot")]
public async Task<IResult> RoomserverRoomsHot(AppDbContext db) {
var tagFilter = Request.Query["tag"].FirstOrDefault()?.ToLower();
var allRooms = await db.Rooms.ToListAsync();
if (!string.IsNullOrEmpty(tagFilter))
{ {
// TODO ADD FUNCTIONALITY allRooms = allRooms.Where(r =>
var json = System.IO.File.ReadAllText("JSON/tempmyprogress.json");
return Results.Content(json, "application/json");
});
app.MapGet("/api/communityboard/v2/current", async (HttpRequest request, AppDbContext db) =>
{
var json = System.IO.File.ReadAllText("JSON/communityboard.json");
return Results.Content(json, "application/json");
});
app.MapGet("/api/playerevents/v1/all", async (HttpRequest request, AppDbContext db) =>
{
// TODO ADD FUNCTIONALITY
return Results.Content("{\"Created\":[],\"Responses\":[]}", "application/json");
});
app.MapGet("/api/storefronts/v1/p2p/betaEnabled", async (HttpRequest request, AppDbContext db) =>
{
return "false";
});
app.MapGet("/api/announcement/v1/get", async (HttpRequest request, AppDbContext db) =>
{
var json = System.IO.File.ReadAllText("JSON/announcements.json");
return Results.Content(json, "application/json");
});
app.MapGet("/api/quickPlay/v1/getandclear", async (HttpRequest request, AppDbContext db) =>
{
// TODO ADD FUNCTIONALITY
return Results.Content("{\"RoomName\":null,\"ActionCode\":null,\"TargetPlayerId\":null}", "application/json");
});
app.MapGet("/api/roomkeys/v1/room", async (HttpRequest request, AppDbContext db) =>
{
// TODO ADD FUNCTIONALITY
var roomid = request.Query["roomId"];
return "[]";
});
app.MapGet("/api/accounts/v1/getBio", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var bio = await db.PlayerBios
.Where(s => accountId == accountId)
.ToListAsync();
return Results.Json(bio);
});
app.MapGet("/roomserver/rooms/bulk", async (HttpRequest request, AppDbContext db) =>
{
var idParam = request.Query["id"].FirstOrDefault();
var nameParam = request.Query["name"].FirstOrDefault();
if (string.IsNullOrEmpty(idParam) && string.IsNullOrEmpty(nameParam))
return Results.BadRequest("Either 'id' or 'name' query parameter is required");
List<Room> results = new();
if (!string.IsNullOrEmpty(idParam))
{ {
var ids = idParam.Split(',').Select(s => int.TryParse(s.Trim(), out var i) ? i : -1).Where(i => i != -1); var roomTags = TryDeserializeRoomTags(r.Tags);
results.AddRange(db.Rooms.Where(r => ids.Contains(r.RoomId)).ToList()); if (roomTags == null || roomTags.Length == 0) return false;
} return roomTags.Any(t => t.Tag.Equals(tagFilter, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(nameParam))
{
var names = nameParam.Split(',').Select(s => s.Trim().ToLower()).Where(s => !string.IsNullOrEmpty(s));
results.AddRange(db.Rooms.Where(r => names.Any(n => r.Name.ToLower().Contains(n))).ToList());
}
var roomIds = results.Select(r => r.RoomId).ToList();
if (roomIds.Count == 0)
{
return Results.Json(new object[0]);
}
var subRooms = db.SubRooms.Where(s => roomIds.Contains(s.RoomId)).ToList();
var roles = db.RoomRoles.Where(r => roomIds.Contains(r.RoomId)).ToList();
var loadScreens = db.LoadScreens.Where(l => roomIds.Contains(l.RoomId)).ToList();
var promoImages = db.PromoImages.Where(p => roomIds.Contains(p.RoomId)).ToList();
var promoExternalContents = db.PromoExternalContents.Where(p => roomIds.Contains(p.RoomId)).ToList();
var response = results.Distinct().Select(r => new
{
r.RoomId,
r.Name,
r.Description,
r.CreatorAccountId,
r.ImageName,
r.State,
r.Accessibility,
r.SupportsLevelVoting,
r.IsRRO,
r.IsDorm,
r.CloningAllowed,
r.SupportsVRLow,
r.SupportsQuest2,
r.SupportsMobile,
r.SupportsScreens,
r.SupportsWalkVR,
r.SupportsTeleportVR,
r.SupportsJuniors,
r.MinLevel,
r.WarningMask,
r.CustomWarning,
r.DisableMicAutoMute,
r.DisableRoomComments,
r.EncryptVoiceChat,
r.CreatedAt,
Stats = new { CheerCount = 0, FavoriteCount = 0, VisitorCount = 0, VisitCount = 0 },
SubRooms = subRooms.Where(s => s.RoomId == r.RoomId).Select(s => new
{
s.SubRoomId,
s.Name,
s.DataBlob,
s.IsSandbox,
s.MaxPlayers,
s.Accessibility,
s.UnitySceneId,
DataSavedAt = s.DataSavedAt
}).ToList(),
Roles = roles.Where(ro => ro.RoomId == r.RoomId).Select(ro => new
{
ro.AccountId,
ro.Role,
ro.InvitedRole
}).ToList(),
LoadScreens = loadScreens.Where(l => l.RoomId == r.RoomId).Select(l => new
{
l.ImageUrl,
l.Tooltip,
l.IsThumbnail
}).ToList(),
PromoImages = promoImages.Where(p => p.RoomId == r.RoomId).Select(p => new
{
p.ImageUrl,
p.Tooltip,
p.SortOrder
}).ToList(),
PromoExternalContent = promoExternalContents.Where(p => p.RoomId == r.RoomId).Select(p => new
{
p.Type,
p.Url,
p.Tooltip
}).ToList(),
Tags = new object[0]
}).ToList(); }).ToList();
}
return Results.Json(response); var hotRooms = allRooms.OrderByDescending(r => r.Id).ToList();
}); var results = new List<object>();
app.MapGet("/roomserver/rooms/hot", async (HttpRequest request, AppDbContext db) => foreach (var room in hotRooms)
{ {
var tagFilter = request.Query["tag"].FirstOrDefault()?.ToLower(); var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var allRooms = await db.Rooms.ToListAsync(); var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
if (!string.IsNullOrEmpty(tagFilter)) var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
{
allRooms = allRooms.Where(r =>
{
var roomTags = TryDeserializeRoomTags(r.Tags);
if (roomTags == null || roomTags.Length == 0) return false;
return roomTags.Any(t => t.Tag.Equals(tagFilter, StringComparison.OrdinalIgnoreCase));
}).ToList();
}
var hotRooms = allRooms
.OrderByDescending(r => r.Id)
.ToList();
var results = new List<object>();
foreach (var room in hotRooms)
{
var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
var tags = TryDeserializeTags(room.Tags) ?? new object[0];
var roomResponse = new
{
room.RoomId,
room.Name,
room.Description,
room.CreatorAccountId,
room.ImageName,
room.State,
room.Accessibility,
room.SupportsLevelVoting,
room.IsRRO,
room.IsDorm,
room.CloningAllowed,
room.SupportsVRLow,
room.SupportsQuest2,
room.SupportsMobile,
room.SupportsScreens,
room.SupportsWalkVR,
room.SupportsTeleportVR,
room.SupportsJuniors,
room.MinLevel,
room.WarningMask,
room.CustomWarning,
room.DisableMicAutoMute,
room.DisableRoomComments,
room.EncryptVoiceChat,
room.CreatedAt,
Stats = (object?)null,
SubRooms = subRooms.Select(s => new
{
s.SubRoomId,
s.RoomId,
s.Name,
s.DataBlob,
s.IsSandbox,
s.MaxPlayers,
s.Accessibility,
s.UnitySceneId,
s.DataSavedAt
}),
Roles = roles.Select(r => new
{
r.AccountId,
r.Role,
r.InvitedRole
}),
LoadScreens = loadScreens.Select(ls => new
{
ls.ImageUrl,
ls.Tooltip,
ls.IsThumbnail
}),
PromoImages = promoImages.Select(pi => new
{
pi.ImageUrl,
pi.Tooltip,
pi.SortOrder
}),
PromoExternalContent = external.Select(ec => new
{
ec.Type,
ec.Url,
ec.Tooltip
}),
Tags = tags
};
results.Add(roomResponse);
}
var response = new
{
Results = results,
TotalResults = results.Count
};
return Results.Json(response);
});
app.MapGet("/roomserver/roomsandplaylists/hot", async (HttpRequest request, AppDbContext db) =>
{
// should sort by popularity but idk how to yet so we just d it by id order
var allRooms = await db.Rooms
.Where(r => !r.IsDorm)
.OrderByDescending(r => r.Id)
.ToListAsync();
var results = new List<object>();
foreach (var room in allRooms)
{
var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
var tags = TryDeserializeTags(room.Tags) ?? new object[0];
var roomResponse = new
{
room.RoomId,
room.Name,
room.Description,
room.CreatorAccountId,
room.ImageName,
room.State,
room.Accessibility,
room.SupportsLevelVoting,
room.IsRRO,
room.IsDorm,
room.CloningAllowed,
room.SupportsVRLow,
room.SupportsQuest2,
room.SupportsMobile,
room.SupportsScreens,
room.SupportsWalkVR,
room.SupportsTeleportVR,
room.SupportsJuniors,
room.MinLevel,
room.WarningMask,
room.CustomWarning,
room.DisableMicAutoMute,
room.DisableRoomComments,
room.EncryptVoiceChat,
room.CreatedAt,
Stats = (object?)null,
SubRooms = subRooms.Select(s => new
{
s.SubRoomId,
s.RoomId,
s.Name,
s.DataBlob,
s.IsSandbox,
s.MaxPlayers,
s.Accessibility,
s.UnitySceneId,
s.DataSavedAt
}),
Roles = roles.Select(r => new
{
r.AccountId,
r.Role,
r.InvitedRole
}),
LoadScreens = loadScreens.Select(ls => new
{
ls.ImageUrl,
ls.Tooltip,
ls.IsThumbnail
}),
PromoImages = promoImages.Select(pi => new
{
pi.ImageUrl,
pi.Tooltip,
pi.SortOrder
}),
PromoExternalContent = external.Select(ec => new
{
ec.Type,
ec.Url,
ec.Tooltip
}),
Tags = tags
};
results.Add(roomResponse);
}
var response = new
{
Results = results,
TotalResults = results.Count
};
return Results.Json(response);
});
app.MapGet("/roomserver/rooms/{id}", async (HttpRequest request, AppDbContext db, string id) =>
{
// this sucked to do
if (!int.TryParse(id, out var roomId))
return Results.NotFound();
var room = await db.Rooms
.FirstOrDefaultAsync(r => r.RoomId == roomId);
if (room == null)
return Results.NotFound();
var subRooms = true
? await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync()
: new List<SubRoom>();
var roles = true
? await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync()
: new List<RoomRole>();
var loadScreens = true
? await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync()
: new List<LoadScreen>();
var promoImages = true
? await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync()
: new List<PromoImage>();
var external = true
? await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync()
: new List<PromoExternalContent>();
var tags = TryDeserializeTags(room.Tags) ?? new object[0]; var tags = TryDeserializeTags(room.Tags) ?? new object[0];
var response = new results.Add(new
{ {
room.RoomId, room.RoomId,
room.Name, room.Name,
@@ -911,12 +395,10 @@ public class APIController : ControllerBase
room.ImageName, room.ImageName,
room.State, room.State,
room.Accessibility, room.Accessibility,
room.SupportsLevelVoting, room.SupportsLevelVoting,
room.IsRRO, room.IsRRO,
room.IsDorm, room.IsDorm,
room.CloningAllowed, room.CloningAllowed,
room.SupportsVRLow, room.SupportsVRLow,
room.SupportsQuest2, room.SupportsQuest2,
room.SupportsMobile, room.SupportsMobile,
@@ -924,702 +406,602 @@ public class APIController : ControllerBase
room.SupportsWalkVR, room.SupportsWalkVR,
room.SupportsTeleportVR, room.SupportsTeleportVR,
room.SupportsJuniors, room.SupportsJuniors,
room.MinLevel, room.MinLevel,
room.WarningMask, room.WarningMask,
room.CustomWarning, room.CustomWarning,
room.DisableMicAutoMute, room.DisableMicAutoMute,
room.DisableRoomComments, room.DisableRoomComments,
room.EncryptVoiceChat, room.EncryptVoiceChat,
room.CreatedAt, room.CreatedAt,
Stats = (object?)null,
Stats = new RoomStats SubRooms = subRooms.Select(s => new { s.SubRoomId, s.RoomId, s.Name, s.DataBlob, s.IsSandbox, s.MaxPlayers, s.Accessibility, s.UnitySceneId, s.DataSavedAt }),
{ Roles = roles.Select(r => new { r.AccountId, r.Role, r.InvitedRole }),
CheerCount = 0, LoadScreens = loadScreens.Select(ls => new { ls.ImageUrl, ls.Tooltip, ls.IsThumbnail }),
FavoriteCount = 0, PromoImages = promoImages.Select(pi => new { pi.ImageUrl, pi.Tooltip, pi.SortOrder }),
VisitorCount = 1, PromoExternalContent = external.Select(ec => new { ec.Type, ec.Url, ec.Tooltip }),
VisitCount = 1
},
SubRooms = subRooms.Select(s => new
{
s.SubRoomId,
s.RoomId,
s.Name,
s.DataBlob,
s.IsSandbox,
s.MaxPlayers,
s.Accessibility,
s.UnitySceneId,
s.DataSavedAt
}).ToList(),
Roles = roles.Select(r => new
{
r.AccountId,
r.Role,
r.InvitedRole
}).ToList(),
LoadScreens = loadScreens.Select(l => new
{
l.ImageUrl,
l.Tooltip,
l.IsThumbnail
}).ToList(),
PromoImages = promoImages.Select(p => new
{
p.ImageUrl,
p.Tooltip,
p.SortOrder
}).ToList(),
PromoExternalContent = external.Select(e => new
{
e.Type,
e.Url,
e.Tooltip
}).ToList(),
Tags = tags Tags = tags
}; });
}
return Results.Json(response); return Results.Json(new { Results = results, TotalResults = results.Count });
}); }
app.MapGet("/api/images/v2/named", async (HttpRequest request, AppDbContext db) =>
[HttpGet("/roomserver/roomsandplaylists/hot")]
public async Task<IResult> RoomserverRoomsAndPlaylistsHot(AppDbContext db) {
var allRooms = await db.Rooms.Where(r => !r.IsDorm).OrderByDescending(r => r.Id).ToListAsync();
var results = new List<object>();
foreach (var room in allRooms)
{ {
var json = System.IO.File.ReadAllText("JSON/namedimages.json"); var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
var tags = TryDeserializeTags(room.Tags) ?? new object[0];
results.Add(new
{
room.RoomId,
room.Name,
room.Description,
room.CreatorAccountId,
room.ImageName,
room.State,
room.Accessibility,
room.SupportsLevelVoting,
room.IsRRO,
room.IsDorm,
room.CloningAllowed,
room.SupportsVRLow,
room.SupportsQuest2,
room.SupportsMobile,
room.SupportsScreens,
room.SupportsWalkVR,
room.SupportsTeleportVR,
room.SupportsJuniors,
room.MinLevel,
room.WarningMask,
room.CustomWarning,
room.DisableMicAutoMute,
room.DisableRoomComments,
room.EncryptVoiceChat,
room.CreatedAt,
Stats = (object?)null,
SubRooms = subRooms.Select(s => new { s.SubRoomId, s.RoomId, s.Name, s.DataBlob, s.IsSandbox, s.MaxPlayers, s.Accessibility, s.UnitySceneId, s.DataSavedAt }),
Roles = roles.Select(r => new { r.AccountId, r.Role, r.InvitedRole }),
LoadScreens = loadScreens.Select(ls => new { ls.ImageUrl, ls.Tooltip, ls.IsThumbnail }),
PromoImages = promoImages.Select(pi => new { pi.ImageUrl, pi.Tooltip, pi.SortOrder }),
PromoExternalContent = external.Select(ec => new { ec.Type, ec.Url, ec.Tooltip }),
Tags = tags
});
}
return Results.Json(new { Results = results, TotalResults = results.Count });
}
[HttpGet("/roomserver/rooms/{id}")]
public async Task<IResult> RoomserverRoomsId(string id, AppDbContext db) {
if (!int.TryParse(id, out var roomId))
return Results.NotFound();
var room = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
if (room == null)
return Results.NotFound();
var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
var tags = TryDeserializeTags(room.Tags) ?? new object[0];
return Results.Json(new
{
room.RoomId,
room.Name,
room.Description,
room.CreatorAccountId,
room.ImageName,
room.State,
room.Accessibility,
room.SupportsLevelVoting,
room.IsRRO,
room.IsDorm,
room.CloningAllowed,
room.SupportsVRLow,
room.SupportsQuest2,
room.SupportsMobile,
room.SupportsScreens,
room.SupportsWalkVR,
room.SupportsTeleportVR,
room.SupportsJuniors,
room.MinLevel,
room.WarningMask,
room.CustomWarning,
room.DisableMicAutoMute,
room.DisableRoomComments,
room.EncryptVoiceChat,
room.CreatedAt,
Stats = new RoomStats { CheerCount = 0, FavoriteCount = 0, VisitorCount = 1, VisitCount = 1 },
SubRooms = subRooms.Select(s => new { s.SubRoomId, s.RoomId, s.Name, s.DataBlob, s.IsSandbox, s.MaxPlayers, s.Accessibility, s.UnitySceneId, s.DataSavedAt }).ToList(),
Roles = roles.Select(r => new { r.AccountId, r.Role, r.InvitedRole }).ToList(),
LoadScreens = loadScreens.Select(l => new { l.ImageUrl, l.Tooltip, l.IsThumbnail }).ToList(),
PromoImages = promoImages.Select(p => new { p.ImageUrl, p.Tooltip, p.SortOrder }).ToList(),
PromoExternalContent = external.Select(e => new { e.Type, e.Url, e.Tooltip }).ToList(),
Tags = tags
});
}
[HttpGet("images/v2/named")]
public async Task<IResult> NamedImages() {
var json = System.IO.File.ReadAllText("JSON/namedimages.json");
return Results.Content(json, "application/json");
}
[HttpPost("objectives/v1/updateobjective")]
public async Task<IResult> UpdateObjective() {
return Results.Ok();
}
[HttpPost("PlayerReporting/v1/hile")]
public async Task<IResult> PlayerReportingHile() {
return Results.Ok();
}
[HttpGet("storefronts/v3/giftdropstore/300")]
public async Task<IResult> GiftDropStore300(StorefrontFillService storefrontService) {
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 300 && s.Name == "rc_cafe_storefront");
if (storefront == null)
{
var json = System.IO.File.ReadAllText("JSON/cafestorefront.json");
return Results.Content(json, "application/json"); return Results.Content(json, "application/json");
}); }
app.MapPost("/api/objectives/v1/updateobjective", async (HttpRequest request, AppDbContext db) => var storeItems = storefront.Items.Select(item => new
{ {
// TODO: implement saving daily item.Id,
return Results.Ok(); item.StorefrontId,
}); item.PurchasableItemId,
app.MapPost("/api/PlayerReporting/v1/hile", async (HttpRequest request, AppDbContext db) => item.Type,
{ item.IsFeatured,
// stops crashing the game due to bepinex winhttp.dll ( or melonloader version.dll) item.NewUntil,
return Results.Ok(); GiftDrops = item.GiftDrops.Select(gd => new
});
app.MapGet("/api/storefronts/v3/giftdropstore/300", async (HttpRequest request, AppDbContext db) =>
{
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 300 && s.Name == "rc_cafe_storefront");
if (storefront == null)
{ {
var json = System.IO.File.ReadAllText("JSON/cafestorefront.json"); gd.Id, gd.StorefrontItemId, gd.GiftDropId, gd.FriendlyName, gd.Tooltip,
return Results.Content(json, "application/json"); gd.ConsumableItemDesc, gd.AvatarItemDesc, gd.AvatarItemType,
gd.EquipmentPrefabName, gd.EquipmentModificationGuid, gd.IsQuery,
gd.Unique, gd.SubscribersOnly, gd.Level, gd.Rarity, gd.CurrencyType,
gd.Currency, gd.Context, gd.ItemSetId, gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new { p.Id, p.StorefrontItemId, p.CurrencyType, p.Price }).ToList()
}).ToList();
return Results.Ok(new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems });
}
[HttpGet("storefronts/v3/giftdropstore/2")]
public async Task<IResult> GiftDropStore2(StorefrontFillService storefrontService) {
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 2 && s.Name == "rec_center_store");
if (storefront == null)
{
var json = System.IO.File.ReadAllText("JSON/storefront12.json");
return Results.Content(json, "application/json");
}
var storeItems = storefront.Items.Select(item => new
{
item.Id,
item.StorefrontId,
item.PurchasableItemId,
item.Type,
item.IsFeatured,
item.NewUntil,
GiftDrops = item.GiftDrops.Select(gd => new
{
gd.Id, gd.StorefrontItemId, gd.GiftDropId, gd.FriendlyName, gd.Tooltip,
gd.ConsumableItemDesc, gd.AvatarItemDesc, gd.AvatarItemType,
gd.EquipmentPrefabName, gd.EquipmentModificationGuid, gd.IsQuery,
gd.Unique, gd.SubscribersOnly, gd.Level, gd.Rarity, gd.CurrencyType,
gd.Currency, gd.Context, gd.ItemSetId, gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new { p.Id, p.StorefrontItemId, p.CurrencyType, p.Price }).ToList()
}).ToList();
return Results.Ok(new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems });
}
[HttpPost("storefronts/v2/buyItem")]
[Authorize]
public async Task<IResult> BuyItem(BuyItemRequest buyRequest, AppDbContext db, NotificationService notificationService) {
try
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
var storefrontItem = await db.StorefrontItems
.Include(si => si.Prices)
.Include(si => si.GiftDrops)
.FirstOrDefaultAsync(si => si.PurchasableItemId == buyRequest.PurchasableItemId);
if (storefrontItem == null)
return Results.NotFound(new { error = "Item not found" });
var price = storefrontItem.Prices.FirstOrDefault(p => p.CurrencyType == buyRequest.CurrencyType);
if (price == null)
return Results.BadRequest(new { error = "Currency type not available for this item" });
var tokenBalance = await db.TokenBalances
.FirstOrDefaultAsync(tb => tb.Id == id && tb.CurrencyType == buyRequest.CurrencyType && tb.BalanceType == -1);
if (tokenBalance == null)
return Results.BadRequest(new { error = "Account has no token balance" });
if (tokenBalance.Balance < price.Price)
return Results.BadRequest(new { error = "Insufficient balance" });
tokenBalance.Balance -= price.Price;
db.TokenBalances.Update(tokenBalance);
await db.SaveChangesAsync();
var receiverAccountId = id;
if (buyRequest.Gift != null)
{
receiverAccountId = buyRequest.Gift.ToPlayerId;
} }
var storeItems = storefront.Items.Select(item => new
var storedGifts = new List<ReceivedGift>();
foreach (var giftDrop in storefrontItem.GiftDrops)
{ {
item.Id,
item.StorefrontId,
item.PurchasableItemId,
item.Type,
item.IsFeatured,
item.NewUntil,
GiftDrops = item.GiftDrops.Select(gd => new
{
gd.Id,
gd.StorefrontItemId,
gd.GiftDropId,
gd.FriendlyName,
gd.Tooltip,
gd.ConsumableItemDesc,
gd.AvatarItemDesc,
gd.AvatarItemType,
gd.EquipmentPrefabName,
gd.EquipmentModificationGuid,
gd.IsQuery,
gd.Unique,
gd.SubscribersOnly,
gd.Level,
gd.Rarity,
gd.CurrencyType,
gd.Currency,
gd.Context,
gd.ItemSetId,
gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new
{
p.Id,
p.StorefrontItemId,
p.CurrencyType,
p.Price
}).ToList()
}).ToList();
var response = new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems };
return Results.Ok(response);
});
app.MapGet("/api/storefronts/v3/giftdropstore/2", async (HttpRequest request, AppDbContext db) =>
{
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 2 && s.Name == "rec_center_store");
if (storefront == null)
{
var json = System.IO.File.ReadAllText("JSON/storefront12.json");
return Results.Content(json, "application/json");
}
var storeItems = storefront.Items.Select(item => new
{
item.Id,
item.StorefrontId,
item.PurchasableItemId,
item.Type,
item.IsFeatured,
item.NewUntil,
GiftDrops = item.GiftDrops.Select(gd => new
{
gd.Id,
gd.StorefrontItemId,
gd.GiftDropId,
gd.FriendlyName,
gd.Tooltip,
gd.ConsumableItemDesc,
gd.AvatarItemDesc,
gd.AvatarItemType,
gd.EquipmentPrefabName,
gd.EquipmentModificationGuid,
gd.IsQuery,
gd.Unique,
gd.SubscribersOnly,
gd.Level,
gd.Rarity,
gd.CurrencyType,
gd.Currency,
gd.Context,
gd.ItemSetId,
gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new
{
p.Id,
p.StorefrontItemId,
p.CurrencyType,
p.Price
}).ToList()
}).ToList();
var response = new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems };
return Results.Ok(response);
});
app.MapPost("/api/storefronts/v2/buyItem", async (HttpRequest request, BuyItemRequest buyRequest, AppDbContext db, JwtTokenService jwtService) =>
{
try
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var storefrontItem = await db.StorefrontItems
.Include(si => si.Prices)
.Include(si => si.GiftDrops)
.FirstOrDefaultAsync(si => si.PurchasableItemId == buyRequest.PurchasableItemId);
if (storefrontItem == null)
{
return Results.NotFound(new { error = "Item not found" });
}
var price = storefrontItem.Prices.FirstOrDefault(p => p.CurrencyType == buyRequest.CurrencyType);
if (price == null)
{
return Results.BadRequest(new { error = "Currency type not available for this item" });
}
var tokenBalance = await db.TokenBalances
.FirstOrDefaultAsync(tb => tb.Id == id &&
tb.CurrencyType == buyRequest.CurrencyType &&
tb.BalanceType == -1);
if (tokenBalance == null)
{
return Results.BadRequest(new { error = "Account has no token balance" });
}
if (tokenBalance.Balance < price.Price)
{
return Results.BadRequest(new { error = "Insufficient balance" });
}
tokenBalance.Balance -= price.Price;
db.TokenBalances.Update(tokenBalance);
await db.SaveChangesAsync();
var receiverAccountId = id;
int? senderPlayerId = null;
if (buyRequest.Gift != null)
{
receiverAccountId = buyRequest.Gift.ToPlayerId;
if (!buyRequest.Gift.Anonymous)
{
senderPlayerId = id;
}
}
var storedGifts = new List<ReceivedGift>();
foreach (var giftDrop in storefrontItem.GiftDrops)
{
var receivedGift = new ReceivedGift
{
ReceiverAccountId = receiverAccountId,
FromPlayerId = 1,
Message = buyRequest.Gift?.Message ?? "A gift for you <3",
ConsumableItemDesc = giftDrop.ConsumableItemDesc ?? string.Empty,
AvatarItemDesc = giftDrop.AvatarItemDesc ?? string.Empty,
// FriendlyName = giftDrop.FriendlyName ?? string.Empty,
AvatarItemType = giftDrop.AvatarItemType,
EquipmentPrefabName = giftDrop.EquipmentPrefabName ?? string.Empty,
EquipmentModificationGuid = giftDrop.EquipmentModificationGuid ?? string.Empty,
CurrencyType = giftDrop.CurrencyType,
Currency = giftDrop.Currency,
Xp = 0,
Level = giftDrop.Level,
Platform = -1,
PlatformsToSpawnOn = -1,
BalanceType = 0,
GiftContext = buyRequest.Gift?.GiftContext ?? giftDrop.Context,
GiftRarity = giftDrop.Rarity,
ReceivedAt = DateTime.UtcNow,
IsConsumed = false
};
db.ReceivedGifts.Add(receivedGift);
storedGifts.Add(receivedGift);
}
await db.SaveChangesAsync();
var giftData = storedGifts.Select(rg => new GiftData
{
Id = rg.Id,
FromPlayerId = null,
ConsumableItemDesc = rg.ConsumableItemDesc,
AvatarItemDesc = rg.AvatarItemDesc,
FriendlyName = rg.FriendlyName,
AvatarItemType = rg.AvatarItemType,
EquipmentPrefabName = rg.EquipmentPrefabName,
EquipmentModificationGuid = rg.EquipmentModificationGuid,
CurrencyType = rg.CurrencyType,
Currency = rg.Currency,
Xp = rg.Xp,
Level = rg.Level,
Platform = rg.Platform,
PlatformsToSpawnOn = rg.PlatformsToSpawnOn,
BalanceType = rg.BalanceType,
GiftContext = rg.GiftContext,
GiftRarity = rg.GiftRarity,
Message = rg.Message
}).ToList();
var balanceUpdate = new BalanceUpdate
{
UpdateResponse = 0,
Data = giftData
};
var response = new BuyItemResponse
{
BalanceUpdates = new List<BalanceUpdate> { balanceUpdate },
Balance = tokenBalance.Balance,
CurrencyType = buyRequest.CurrencyType,
BalanceType = -1,
Platform = -1
};
try
{
var purchaseNotification = notificationService.CreateNotification(
PushNotificationId.StorefrontBalancePurchase,
id: storefrontItem.Id,
toAccountId: id,
data: new Dictionary<string, object>
{
{ "BalanceAddType", 1400 },
{ "Delta", -price.Price },
{ "Balance", tokenBalance.Balance },
{ "CurrencyType", buyRequest.CurrencyType }
}
);
await notificationService.SendNotificationToPlayer(id, purchaseNotification);
}
catch (Exception notifEx)
{
Console.WriteLine($"Error sending purchase notification: {notifEx.Message}");
}
return Results.Ok(response);
}
catch (Exception ex)
{
return Results.Problem($"Error processing purchase: {ex.Message}");
}
});
app.MapPost("/api/avatar/v2/gifts/generate", async (HttpRequest request, AppDbContext db, JwtTokenService jwtService) =>
{
try
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body);
var body = await reader.ReadToEndAsync();
int giftContext = 0;
bool isGameGift = false;
string message = "";
int xp = 0;
if (!string.IsNullOrWhiteSpace(body))
{
foreach (var pair in body.Split('&'))
{
var keyValue = pair.Split('=');
if (keyValue.Length == 2)
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "GiftContext" && int.TryParse(value, out var context))
giftContext = context;
else if (key == "IsGameGift")
isGameGift = value.Equals("true", StringComparison.OrdinalIgnoreCase);
else if (key == "Message")
message = value;
else if (key == "Xp" && int.TryParse(value, out var xpVal))
xp = xpVal;
}
}
}
var earnableRewards = await db.EarnableRewards
.Where(er => er.RewardContext == giftContext)
.ToListAsync();
var random = new Random();
string avatarItemDesc = "";
string friendlyName = "";
int avatarItemType = 0;
int currencyType = 0;
int currency = 0;
int giftRarity = 0;
string consumableItemDesc = "";
bool isEarnableItem = random.Next(0, 100) < 60;
if (isEarnableItem && earnableRewards.Any())
{
var avatarRewards = earnableRewards.Where(er => !string.IsNullOrEmpty(er.AvatarItemDesc)).ToList();
if (avatarRewards.Any())
{
var ownedAvatarItems = await db.AvatarItems
.Where(ai => ai.OwnerAccountId == id)
.Select(ai => ai.AvatarItemDesc)
.ToListAsync();
var unownedRewards = avatarRewards
.Where(ar => !ownedAvatarItems.Contains(ar.AvatarItemDesc))
.ToList();
if (unownedRewards.Any())
{
var selectedReward = unownedRewards[random.Next(unownedRewards.Count)];
avatarItemDesc = selectedReward.AvatarItemDesc;
friendlyName = selectedReward.FriendlyName;
avatarItemType = selectedReward.AvatarItemType;
giftRarity = selectedReward.GiftRarity;
}
else
{
isEarnableItem = false;
}
}
else
{
isEarnableItem = false;
}
}
if (!isEarnableItem)
{
int[] tokenAmounts = {10, 25, 50, 100, 250, 500};
currency = tokenAmounts[random.Next(tokenAmounts.Length)];
currencyType = 2;
giftRarity = 20;
}
var receivedGift = new ReceivedGift var receivedGift = new ReceivedGift
{ {
ReceiverAccountId = id, ReceiverAccountId = receiverAccountId,
FromPlayerId = 1, FromPlayerId = 1,
Message = message, Message = buyRequest.Gift?.Message ?? "A gift for you <3",
ConsumableItemDesc = consumableItemDesc, ConsumableItemDesc = giftDrop.ConsumableItemDesc ?? string.Empty,
AvatarItemDesc = avatarItemDesc, AvatarItemDesc = giftDrop.AvatarItemDesc ?? string.Empty,
FriendlyName = friendlyName, AvatarItemType = giftDrop.AvatarItemType,
AvatarItemType = avatarItemType, EquipmentPrefabName = giftDrop.EquipmentPrefabName ?? string.Empty,
EquipmentPrefabName = "", EquipmentModificationGuid = giftDrop.EquipmentModificationGuid ?? string.Empty,
EquipmentModificationGuid = "", CurrencyType = giftDrop.CurrencyType,
CurrencyType = currencyType, Currency = giftDrop.Currency,
Currency = currency, Xp = 0,
Xp = xp, Level = giftDrop.Level,
Level = 0,
Platform = -1, Platform = -1,
PlatformsToSpawnOn = -1, PlatformsToSpawnOn = -1,
BalanceType = 0, BalanceType = 0,
GiftContext = giftContext, GiftContext = buyRequest.Gift?.GiftContext ?? giftDrop.Context,
GiftRarity = giftRarity, GiftRarity = giftDrop.Rarity,
ReceivedAt = DateTime.UtcNow, ReceivedAt = DateTime.UtcNow,
IsConsumed = false IsConsumed = false
}; };
db.ReceivedGifts.Add(receivedGift); db.ReceivedGifts.Add(receivedGift);
await db.SaveChangesAsync(); storedGifts.Add(receivedGift);
var giftData = new
{
Id = receivedGift.Id,
FromPlayerId = 1,
ConsumableItemDesc = consumableItemDesc,
AvatarItemDesc = avatarItemDesc,
FriendlyName = friendlyName,
AvatarItemType = avatarItemType,
EquipmentPrefabName = "",
EquipmentModificationGuid = "",
CurrencyType = currencyType,
Currency = currency,
Xp = xp,
Level = 0,
Platform = -1,
PlatformsToSpawnOn = -1,
BalanceType = 0,
GiftContext = giftContext,
GiftRarity = giftRarity,
Message = message
};
return Results.Ok(giftData);
} }
catch (Exception ex) await db.SaveChangesAsync();
var giftData = storedGifts.Select(rg => new GiftData
{ {
return Results.Problem($"Error generating gift: {ex.Message}"); Id = rg.Id, FromPlayerId = null,
} ConsumableItemDesc = rg.ConsumableItemDesc, AvatarItemDesc = rg.AvatarItemDesc,
}); FriendlyName = rg.FriendlyName, AvatarItemType = rg.AvatarItemType,
EquipmentPrefabName = rg.EquipmentPrefabName, EquipmentModificationGuid = rg.EquipmentModificationGuid,
CurrencyType = rg.CurrencyType, Currency = rg.Currency, Xp = rg.Xp, Level = rg.Level,
Platform = rg.Platform, PlatformsToSpawnOn = rg.PlatformsToSpawnOn, BalanceType = rg.BalanceType,
GiftContext = rg.GiftContext, GiftRarity = rg.GiftRarity, Message = rg.Message
}).ToList();
var balanceUpdate = new BalanceUpdate { UpdateResponse = 0, Data = giftData };
var response = new BuyItemResponse
{
BalanceUpdates = new List<BalanceUpdate> { balanceUpdate },
Balance = tokenBalance.Balance, CurrencyType = buyRequest.CurrencyType,
BalanceType = -1, Platform = -1
};
app.MapPost("/api/avatar/v2/gifts/consume/", async (HttpRequest request, AppDbContext db, JwtTokenService jwtService) =>
{
try try
{ {
var authHeader = request.Headers.Authorization.ToString(); var purchaseNotification = notificationService.CreateNotification(
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) PushNotificationId.StorefrontBalancePurchase, id: storefrontItem.Id, toAccountId: id,
return Results.Unauthorized(); data: new Dictionary<string, object>
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body);
var body = await reader.ReadToEndAsync();
var giftId = 0;
var unlockedLevel = 0;
if (!string.IsNullOrWhiteSpace(body))
{
foreach (var pair in body.Split('&'))
{ {
var keyValue = pair.Split('='); { "BalanceAddType", 1400 }, { "Delta", -price.Price },
if (keyValue.Length == 2) { "Balance", tokenBalance.Balance }, { "CurrencyType", buyRequest.CurrencyType }
{ });
if (keyValue[0] == "Id") await notificationService.SendNotificationToPlayer(id, purchaseNotification);
int.TryParse(Uri.UnescapeDataString(keyValue[1]), out giftId); }
else if (keyValue[0] == "UnlockedLevel") catch (Exception notifEx)
int.TryParse(Uri.UnescapeDataString(keyValue[1]), out unlockedLevel); {
} Console.WriteLine($"Error sending purchase notification: {notifEx.Message}");
}
return Results.Ok(response);
}
catch (Exception ex)
{
return Results.Problem($"Error processing purchase: {ex.Message}");
}
}
[HttpPost("avatar/v2/gifts/generate")]
[Authorize]
public async Task<IResult> AvatarGiftsGenerate(AppDbContext db) {
try
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
Request.EnableBuffering();
Request.Body.Position = 0;
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
int giftContext = 0;
bool isGameGift = false;
string message = "";
int xp = 0;
if (!string.IsNullOrWhiteSpace(body))
{
foreach (var pair in body.Split('&'))
{
var keyValue = pair.Split('=');
if (keyValue.Length == 2)
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "GiftContext" && int.TryParse(value, out var context))
giftContext = context;
else if (key == "IsGameGift")
isGameGift = value.Equals("true", StringComparison.OrdinalIgnoreCase);
else if (key == "Message")
message = value;
else if (key == "Xp" && int.TryParse(value, out var xpVal))
xp = xpVal;
} }
} }
}
if (giftId == 0) var earnableRewards = await db.EarnableRewards
.Where(er => er.RewardContext == giftContext)
.ToListAsync();
var random = new Random();
string avatarItemDesc = "";
string friendlyName = "";
int avatarItemType = 0;
int currencyType = 0;
int currency = 0;
int giftRarity = 0;
string consumableItemDesc = "";
bool isEarnableItem = random.Next(0, 100) < 60;
if (isEarnableItem && earnableRewards.Any())
{
var avatarRewards = earnableRewards.Where(er => !string.IsNullOrEmpty(er.AvatarItemDesc)).ToList();
if (avatarRewards.Any())
{ {
return Results.BadRequest(new { success = false, error = "Invalid gift ID" }); var ownedAvatarItems = await db.AvatarItems
} .Where(ai => ai.OwnerAccountId == id)
.Select(ai => ai.AvatarItemDesc)
.ToListAsync();
var receivedGift = await db.ReceivedGifts var unownedRewards = avatarRewards.Where(ar => !ownedAvatarItems.Contains(ar.AvatarItemDesc)).ToList();
.FirstOrDefaultAsync(rg => rg.Id == giftId && rg.ReceiverAccountId == id); if (unownedRewards.Any())
if (receivedGift == null)
{
return Results.NotFound(new { success = false, error = "Gift not found" });
}
if (!string.IsNullOrEmpty(receivedGift.ConsumableItemDesc))
{
var existingConsumable = await db.ConsumableItems
.FirstOrDefaultAsync(c => c.OwnerAccountId == id && c.ConsumableItemDesc == receivedGift.ConsumableItemDesc);
if (existingConsumable == null)
{ {
var newConsumable = new ConsumableItem var selectedReward = unownedRewards[random.Next(unownedRewards.Count)];
{ avatarItemDesc = selectedReward.AvatarItemDesc;
OwnerAccountId = id, friendlyName = selectedReward.FriendlyName;
Ids = new List<int> { giftId }, avatarItemType = selectedReward.AvatarItemType;
CreatedAts = new List<DateTime> { DateTime.UtcNow }, giftRarity = selectedReward.GiftRarity;
ConsumableItemDesc = receivedGift.ConsumableItemDesc,
Count = 1,
InitialCount = 1,
IsActive = false,
ActiveDurationMinutes = 0,
IsTransferable = false
};
db.ConsumableItems.Add(newConsumable);
} }
else else { isEarnableItem = false; }
}
else { isEarnableItem = false; }
}
if (!isEarnableItem)
{
int[] tokenAmounts = { 10, 25, 50, 100, 250, 500 };
currency = tokenAmounts[random.Next(tokenAmounts.Length)];
currencyType = 2;
giftRarity = 20;
}
var receivedGift = new ReceivedGift
{
ReceiverAccountId = id, FromPlayerId = 1, Message = message,
ConsumableItemDesc = consumableItemDesc, AvatarItemDesc = avatarItemDesc,
FriendlyName = friendlyName, AvatarItemType = avatarItemType,
EquipmentPrefabName = "", EquipmentModificationGuid = "",
CurrencyType = currencyType, Currency = currency, Xp = xp, Level = 0,
Platform = -1, PlatformsToSpawnOn = -1, BalanceType = 0,
GiftContext = giftContext, GiftRarity = giftRarity,
ReceivedAt = DateTime.UtcNow, IsConsumed = false
};
db.ReceivedGifts.Add(receivedGift);
await db.SaveChangesAsync();
return Results.Ok(new
{
Id = receivedGift.Id, FromPlayerId = 1,
ConsumableItemDesc = consumableItemDesc, AvatarItemDesc = avatarItemDesc,
FriendlyName = friendlyName, AvatarItemType = avatarItemType,
EquipmentPrefabName = "", EquipmentModificationGuid = "",
CurrencyType = currencyType, Currency = currency, Xp = xp, Level = 0,
Platform = -1, PlatformsToSpawnOn = -1, BalanceType = 0,
GiftContext = giftContext, GiftRarity = giftRarity, Message = message
});
}
catch (Exception ex)
{
return Results.Problem($"Error generating gift: {ex.Message}");
}
}
[HttpPost("avatar/v2/gifts/consume/")]
[Authorize]
public async Task<IResult> AvatarGiftsConsume(AppDbContext db) {
try
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
Request.EnableBuffering();
Request.Body.Position = 0;
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var giftId = 0;
var unlockedLevel = 0;
if (!string.IsNullOrWhiteSpace(body))
{
foreach (var pair in body.Split('&'))
{
var keyValue = pair.Split('=');
if (keyValue.Length == 2)
{ {
existingConsumable.Ids.Add(giftId); if (keyValue[0] == "Id")
existingConsumable.CreatedAts.Add(DateTime.UtcNow); int.TryParse(Uri.UnescapeDataString(keyValue[1]), out giftId);
existingConsumable.Count += 1; else if (keyValue[0] == "UnlockedLevel")
db.ConsumableItems.Update(existingConsumable); int.TryParse(Uri.UnescapeDataString(keyValue[1]), out unlockedLevel);
} }
} }
}
if (!string.IsNullOrEmpty(receivedGift.AvatarItemDesc)) if (giftId == 0)
return Results.BadRequest(new { success = false, error = "Invalid gift ID" });
var receivedGift = await db.ReceivedGifts
.FirstOrDefaultAsync(rg => rg.Id == giftId && rg.ReceiverAccountId == id);
if (receivedGift == null)
return Results.NotFound(new { success = false, error = "Gift not found" });
if (!string.IsNullOrEmpty(receivedGift.ConsumableItemDesc))
{
var existingConsumable = await db.ConsumableItems
.FirstOrDefaultAsync(c => c.OwnerAccountId == id && c.ConsumableItemDesc == receivedGift.ConsumableItemDesc);
if (existingConsumable == null)
{ {
var avatarItem = new AvatarItem db.ConsumableItems.Add(new ConsumableItem
{ {
OwnerAccountId = id, OwnerAccountId = id,
AvatarItemDesc = receivedGift.AvatarItemDesc, Ids = new List<int> { giftId },
FriendlyName = receivedGift.FriendlyName ?? "" CreatedAts = new List<DateTime> { DateTime.UtcNow },
}; ConsumableItemDesc = receivedGift.ConsumableItemDesc,
db.AvatarItems.Add(avatarItem); Count = 1, InitialCount = 1, IsActive = false,
ActiveDurationMinutes = 0, IsTransferable = false
});
} }
else
if (!string.IsNullOrEmpty(receivedGift.Currency.ToString()) && receivedGift.CurrencyType == 2)
{ {
var tokenBalance = await db.TokenBalances existingConsumable.Ids.Add(giftId);
.FirstOrDefaultAsync(tb => tb.Id == id && existingConsumable.CreatedAts.Add(DateTime.UtcNow);
tb.CurrencyType == receivedGift.CurrencyType && existingConsumable.Count += 1;
tb.BalanceType == -1); db.ConsumableItems.Update(existingConsumable);
if (tokenBalance == null)
{
tokenBalance = new TokenBalance
{
Id = id,
CurrencyType = receivedGift.CurrencyType,
BalanceType = -1,
Balance = receivedGift.Currency
};
db.TokenBalances.Add(tokenBalance);
}
else
{
tokenBalance.Balance += receivedGift.Currency;
db.TokenBalances.Update(tokenBalance);
}
} }
receivedGift.IsConsumed = true;
receivedGift.ConsumedAt = DateTime.UtcNow;
db.ReceivedGifts.Update(receivedGift);
await db.SaveChangesAsync();
return Results.Ok(RecNetResult.Ok());
} }
catch (Exception ex)
if (!string.IsNullOrEmpty(receivedGift.AvatarItemDesc))
{ {
return Results.Problem($"Error consuming gift: {ex.Message}"); db.AvatarItems.Add(new AvatarItem
}
});
app.MapGet("/api/rooms/v1/filters", async (HttpRequest request, AppDbContext db) =>
{
var json = System.IO.File.ReadAllText("JSON/roomfilters.json");
return Results.Content(json, "application/json");
});
app.MapGet("/roomserver/rooms/{id}/interactionby/me", async (HttpRequest request, AppDbContext db, string id) =>
{
// TODO: implement
return Results.Content("{\"Cheered\":false,\"Favorited\":false}", "application/json");
});
app.MapPost("/api/images/v4/uploadsaved", async (HttpContext context) =>
{
try
{
var form = await context.Request.ReadFormAsync();
var file = form.Files.FirstOrDefault();
if (file == null)
{ {
return Results.BadRequest(new { error = "No file found in request" }); OwnerAccountId = id,
} AvatarItemDesc = receivedGift.AvatarItemDesc,
FriendlyName = receivedGift.FriendlyName ?? ""
var imageId = Guid.NewGuid().ToString("N");
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
var validExtensions = new[] { ".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp" };
if (string.IsNullOrEmpty(extension) || !validExtensions.Contains(extension))
{
extension = ".png";
}
var savedFileName = imageId + extension;
var filePath = Path.Combine(ImagesDir, savedFileName);
using (var fileStream = System.IO.File.Create(filePath))
{
await file.CopyToAsync(fileStream);
}
return Results.Ok(new
{
ImageName = savedFileName
}); });
} }
catch (Exception ex)
{
return Results.Problem($"Error uploading image: {ex.Message}");
}
});
app.MapGet("/api/config/v1/backtrace", () => Results.Ok(new if (!string.IsNullOrEmpty(receivedGift.Currency.ToString()) && receivedGift.CurrencyType == 2)
{ {
ReportBudget = 125, var tokenBalance = await db.TokenBalances
FilterType = 0, .FirstOrDefaultAsync(tb => tb.Id == id && tb.CurrencyType == receivedGift.CurrencyType && tb.BalanceType == -1);
SampleRate = 0.025,
LogLineCount = 50, if (tokenBalance == null)
CaptureNativeCrashes = 1, {
AMRThresholdMS = 0, db.TokenBalances.Add(new TokenBalance
MessageCount = 1000, {
MessageRegex = "^Cannot set the parent of the GameObject .* while its new parent|^\\\\>\\\\x2010x\\\\:\\\\x20|\\\\'LabelTheme\\\\' contains missing PaletteTheme reference on", Id = id, CurrencyType = receivedGift.CurrencyType,
VersionRegex = ".*" BalanceType = -1, Balance = receivedGift.Currency
})); });
}
else
{
tokenBalance.Balance += receivedGift.Currency;
db.TokenBalances.Update(tokenBalance);
}
}
receivedGift.IsConsumed = true;
receivedGift.ConsumedAt = DateTime.UtcNow;
db.ReceivedGifts.Update(receivedGift);
await db.SaveChangesAsync();
return Results.Ok(RecNetResult.Ok());
}
catch (Exception ex)
{
return Results.Problem($"Error consuming gift: {ex.Message}");
}
} }
[HttpGet("rooms/v1/filters")]
public async Task<IResult> RoomsFilters() {
var json = System.IO.File.ReadAllText("JSON/roomfilters.json");
return Results.Content(json, "application/json");
}
[HttpGet("/roomserver/rooms/{id}/interactionby/me")]
public async Task<IResult> RoomInteractionByMe(string id) {
return Results.Content("{\"Cheered\":false,\"Favorited\":false}", "application/json");
}
[HttpPost("images/v4/uploadsaved")]
public async Task<IResult> ImageUploadSaved() {
try
{
var form = await HttpContext.Request.ReadFormAsync();
var file = form.Files.FirstOrDefault();
if (file == null)
return Results.BadRequest(new { error = "No file found in request" });
var imageId = Guid.NewGuid().ToString("N");
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
var validExtensions = new[] { ".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp" };
if (string.IsNullOrEmpty(extension) || !validExtensions.Contains(extension))
extension = ".png";
var savedFileName = imageId + extension;
var filePath = Path.Combine(ImagesDir, savedFileName);
using (var fileStream = System.IO.File.Create(filePath))
await file.CopyToAsync(fileStream);
return Results.Ok(new { ImageName = savedFileName });
}
catch (Exception ex)
{
return Results.Problem($"Error uploading image: {ex.Message}");
}
}
[HttpGet("config/v1/backtrace")]
public IResult BacktraceConfig() {
return Results.Ok(new
{
ReportBudget = 125,
FilterType = 0,
SampleRate = 0.025,
LogLineCount = 50,
CaptureNativeCrashes = 1,
AMRThresholdMS = 0,
MessageCount = 1000,
MessageRegex = "^Cannot set the parent of the GameObject .* while its new parent|^\\\\>\\\\x2010x\\\\:\\\\x20|\\\\'LabelTheme\\\\' contains missing PaletteTheme reference on",
VersionRegex = ".*"
});
}
private static object[]? TryDeserializeTags(string json) private static object[]? TryDeserializeTags(string json)
{ {
try try
@@ -9,11 +9,6 @@ namespace CannedNet.Services.Controllers;
[ApiController, Route("account")] [ApiController, Route("account")]
public class AccountsController : ControllerBase public class AccountsController : ControllerBase
{ {
//[HttpGet]
//public Task<IResult> Root() {
// return Task.FromResult(Results.Ok("FUCKL MYSJDIOJD"));
//}
[HttpGet("me")] [HttpGet("me")]
public async Task<IResult> Me(HttpRequest request, AppDbContext db, JwtTokenService jwtService) { public async Task<IResult> Me(HttpRequest request, AppDbContext db, JwtTokenService jwtService) {
string authHeader = request.Headers.Authorization.ToString(); string authHeader = request.Headers.Authorization.ToString();
@@ -106,7 +101,7 @@ public class AccountsController : ControllerBase
return Results.Json(result); return Results.Json(result);
} }
[HttpPost("cerate")] [HttpPost("create")]
public async Task<IResult> Create(HttpRequest httpRequest, AppDbContext db) { public async Task<IResult> Create(HttpRequest httpRequest, AppDbContext db) {
int platform = 0; int platform = 0;
string platformId = ""; string platformId = "";
+55 -58
View File
@@ -1,76 +1,73 @@
using CannedNet.Services.Infrastructure; using CannedNet.Services.Infrastructure;
using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
[ApiController, Route("cdn")]
public class CDNController public class CDNController
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpGet("config/LoadingScreenTipData")]
public Task<IResult> GetLoadingScreenTipData() {
var json = File.ReadAllText("JSON/loadingscreentipdata.json");
return Task.FromResult(Results.Content(json, "application/json"));
}
public void MapEndpoints(WebApplication app) [HttpGet("sigs/{sigName}")]
public async Task<IResult> GetSig(string sigName)
{ {
app.MapGet("/config/LoadingScreenTipData", (HttpRequest request) => var filePath = Path.Combine("Sigs", sigName);
if (File.Exists(filePath))
{ {
var json = File.ReadAllText("JSON/loadingscreentipdata.json"); var sigBytes = await File.ReadAllBytesAsync(filePath);
return Results.Content(json, "application/json"); return Results.File(sigBytes, "application/octet-stream");
}); }
else
app.MapGet("/sigs/{sigName}", async (HttpContext context, string sigName) =>
{ {
var filePath = Path.Combine("Sigs", sigName); return Results.NotFound();
}
}
if (File.Exists(filePath)) [HttpPost("upload")]
{ public async Task<IResult> Upload(IFormFile file)
var sigBytes = await File.ReadAllBytesAsync(filePath); {
return Results.File(sigBytes, "application/octet-stream"); try
}
else
{
return Results.NotFound();
}
});
app.MapPost("/upload", async (HttpContext context) =>
{ {
try if (file == null)
{ {
var form = await context.Request.ReadFormAsync(); return Results.BadRequest(new { error = "No file found in request" });
var file = form.Files.FirstOrDefault();
if (file == null)
{
return Results.BadRequest(new { error = "No file found in request" });
}
var imageId = Guid.NewGuid().ToString("N");
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
var validExtensions = new[] { ".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp" };
if (string.IsNullOrEmpty(extension) || !validExtensions.Contains(extension))
{
extension = ".png";
}
var savedFileName = imageId + extension;
var filePath = Path.Combine("Images", savedFileName);
if (!Directory.Exists("Images"))
{
Directory.CreateDirectory("Images");
}
using (var fileStream = File.Create(filePath))
{
await file.CopyToAsync(fileStream);
}
return Results.Ok(new
{
filename = savedFileName
});
} }
catch (Exception ex)
var imageId = Guid.NewGuid().ToString("N");
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
var validExtensions = new[] { ".png", ".jpg", ".jpeg" };
if (string.IsNullOrEmpty(extension) || !validExtensions.Contains(extension))
{ {
return Results.Problem($"Error uploading image: {ex.Message}"); extension = ".png";
} }
});
var savedFileName = imageId + extension;
var filePath = Path.Combine("Images", savedFileName);
if (!Directory.Exists("Images"))
{
Directory.CreateDirectory("Images");
}
using (var fileStream = File.Create(filePath))
{
await file.CopyToAsync(fileStream);
}
return Results.Ok(new
{
filename = savedFileName
});
}
catch (Exception ex)
{
return Results.Problem($"Error uploading image: {ex.Message}");
}
} }
} }
@@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
[ApiController] [ApiController, Route("chat")]
public class ChatController : ControllerBase public class ChatController : ControllerBase
{ {
[HttpGet("thread")] [HttpGet("thread")]
@@ -1,16 +1,15 @@
using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class ClubsController [ApiController, Route("club")]
public class ClubsController : ControllerBase
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpGet("home/me")]
[Authorize]
public void MapEndpoints(WebApplication app) public async Task<IResult> GetClubHomeMe()
{ {
app.MapGet("/club/home/me", () => return Results.NotFound();
{
return Results.NotFound();
});
} }
} }
@@ -1,16 +1,13 @@
using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class CommerceController [ApiController, Route("commerce")]
public class CommerceController : ControllerBase
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpGet("purchase/v1/hasspentmoney")]
public async Task<IResult> HasSpentMoney()
public void MapEndpoints(WebApplication app)
{ {
app.MapGet("/purchase/v1/hasspentmoney", (HttpRequest request) => return Results.NotFound();
{
return Results.NotFound();
});
} }
} }
+418 -475
View File
@@ -1,495 +1,438 @@
using CannedNet.Data; using CannedNet.Data;
using CannedNet.Services.Infrastructure; using CannedNet.Services.Infrastructure;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class EconController [ApiController, Route("econ")]
public class EconController : ControllerBase
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpGet("api/avatar/v1/defaultunlocked")]
public async Task<IResult> DefaultUnlocked()
public void MapEndpoints(WebApplication app)
{ {
var jwtService = app.Services.GetRequiredService<JwtTokenService>(); var json = System.IO.File.ReadAllText("JSON/defaultAvatarItems.json");
var notificationService = app.Services.GetRequiredService<NotificationService>(); return Results.Content(json, "application/json");
var storefrontService = app.Services.GetRequiredService<StorefrontFillService>(); }
app.MapGet("/api/avatar/v1/defaultunlocked", () => [HttpGet("api/avatar/v1/defaultbaseavataritems")]
public async Task<IResult> DefaultBaseAvatarItems()
{
var json = System.IO.File.ReadAllText("JSON/defaultAvatarItems.json");
return Results.Content(json, "application/json");
}
[HttpGet("api/avatar/v4/items")]
[Authorize]
public async Task<IResult> GetAvatarItemsV4(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var ownedItems = await db.AvatarItems
.Where(i => i.OwnerAccountId == id)
.ToListAsync();
var defaultItems = new List<object>
{ {
var json = File.ReadAllText("JSON/defaultAvatarItems.json"); new { AvatarItemType = 0, AvatarItemDesc = "5d13a7a2-8213-40e6-90a6-efdd76a3fdcb,,,", PlatformMask = -1, FriendlyName = "Flowing Hair", Tooltip = "", Rarity = 0 },
return Results.Content(json, "application/json"); new { AvatarItemType = 0, AvatarItemDesc = "1d27b674-f9e2-4ffc-9d8c-a58a1be06457,,,", PlatformMask = -1, FriendlyName = "Afro Hair", Tooltip = "", Rarity = 0 },
}); new { AvatarItemType = 0, AvatarItemDesc = "d84c0ff9-8fbe-4ed8-abf3-7996e81888ab,,,", PlatformMask = -1, FriendlyName = "Large Afro Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e5b83dfc-b2e1-4dcb-a4ab-9d3a4c8a34ae,,,", PlatformMask = -1, FriendlyName = "Long Wavy Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7dd6f7b0-7ba0-429f-a04f-e32d3a79ee61,,,", PlatformMask = -1, FriendlyName = "Short Wavy Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "eb9611c6-bb50-41a2-93e9-7f959815a846,,,", PlatformMask = -1, FriendlyName = "Dreads Long Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "1fd69ef8-0b74-4962-af5a-67f0bf0358f2,,,", PlatformMask = -1, FriendlyName = "Ponytail Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "a12f724f-4a73-4ab8-aad4-6bfc662b4dd6,,,", PlatformMask = -1, FriendlyName = "Undercut Long Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0753d7a4-8247-4fca-a6fc-359c26086140,,,", PlatformMask = -1, FriendlyName = "Fonzie Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "77d3c585-4928-4471-a425-89036efe7299,,,", PlatformMask = -1, FriendlyName = "Spiky Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "92302d9d-c527-418c-ac5d-1fa869727505,,,", PlatformMask = -1, FriendlyName = "Part Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "f9dd08f8-16d3-4c39-af4f-89f7bb6e80d3,,,", PlatformMask = -1, FriendlyName = "Undercut Short Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b148cb1e-df81-442f-aea6-ab1727aad00e,,,", PlatformMask = -1, FriendlyName = "Chunky Afro Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e36bcd98-7e85-43fa-89f8-57e4ec33823a,,,", PlatformMask = -1, FriendlyName = "Bob with Bangs Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "880a3cc0-7407-4b61-b759-f9dd890fe9e5,,,", PlatformMask = -1, FriendlyName = "Bob Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21599b51-c50f-43d8-ac5f-62c30cd02ca5,,,", PlatformMask = -1, FriendlyName = "Lori Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "193a3bf9-abc0-4d78-8d63-92046908b1c5,,,", PlatformMask = -1, FriendlyName = "Emo Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "79b90274-6eec-4664-acfb-4a123334661e,,,", PlatformMask = -1, FriendlyName = "Pig Tails Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "da4e7b34-2095-4a9e-801e-4f409039e0dd,,,", PlatformMask = -1, FriendlyName = "Buzz Cut Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9d9fadb6-97eb-480e-a224-4e0179082071,,,", PlatformMask = -1, FriendlyName = "Meatball Buns Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d8280c0c-d803-4513-be10-a0ba96d8821e,,,", PlatformMask = -1, FriendlyName = "Flowhawk Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e286863c-2967-4d00-b837-b49487b9484a,,,", PlatformMask = -1, FriendlyName = "Fauxhawk Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2cb4f372-3372-4583-8b57-c4e3988e3c28,,,", PlatformMask = -1, FriendlyName = "Punky Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "06306723-ca20-4aa6-b7b3-917113f41ac3,,,", PlatformMask = -1, FriendlyName = "Cat-Eye Glasses (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c70005d5-6276-4a98-acb3-6a77bc19379a,,,", PlatformMask = -1, FriendlyName = "Glasses (Teal)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8d10cc78-6b00-45f3-affb-205e9cc5b03f,,,", PlatformMask = -1, FriendlyName = "Beard (Close)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "cc96f8a5-bc5b-4f89-83b7-ecd53905ada7,,,", PlatformMask = -1, FriendlyName = "Beard (Thick)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c6c08eb5-381a-4193-9722-80da95d62abe,,,", PlatformMask = -1, FriendlyName = "Business Tie (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "4d507dfa-4a99-4ac0-8537-229e9dc0eb4a,,,", PlatformMask = -1, FriendlyName = "Rec Room Tank Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d0a9262f-5504-46a7-bb10-7507503db58e,,,", PlatformMask = -1, FriendlyName = "Rec Room Shirt (Crew Neck, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d0a9262f-5504-46a7-bb10-7507503db58e,95e4cc30-cb68-473d-a395-feadf5b51512,0440f08f-ef1d-49d8-942b-523056e8bb45,", PlatformMask = -1, FriendlyName = "Rec Room T-Shirt (Crew Neck, Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,,,", PlatformMask = -1, FriendlyName = "Collared Shirt (Plaid, Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,,,", PlatformMask = -1, FriendlyName = "T-Shirt", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8aa79563-ace1-4ba7-ad0c-f3210a78142f,,,", PlatformMask = -1, FriendlyName = "Rec Room Shirt (V-Neck, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8aa79563-ace1-4ba7-ad0c-f3210a78142f,95e4cc30-cb68-473d-a395-feadf5b51512,05f0ee6e-c824-470e-9178-5ed576c6fe0c,", PlatformMask = -1, FriendlyName = "Rec Room T-Shirt (V-Neck, Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,,,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,c5deba2a-6e35-4b13-8e94-8ba5457f39df,b75ef67d-00c3-4ac1-9b72-212032460294,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,758752bd-db2f-43d2-b580-55b3e1efffd5,b75ef67d-00c3-4ac1-9b72-212032460294,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,,,", PlatformMask = -1, FriendlyName = "Zipper Dress (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,,,", PlatformMask = -1, FriendlyName = "Wristbands (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,,,", PlatformMask = -1, FriendlyName = "Wrist Tape (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,55901f12-d5b5-4fa8-b4c8-e479689ee39d,f600037d-c9c0-43fa-b45b-02f456f9dd5f,", PlatformMask = -1, FriendlyName = "Collared Shirt (Denim)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,bf82f2f6-9af8-431e-a296-0890dea48ba7,d015cae7-a905-49e4-8823-6dec069689a6,", PlatformMask = -1, FriendlyName = "Collared Shirt (Argyle)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,EfdMcnfHt0mr0PQ_maaYOg,DRJcNhkqvkKFEaZpOguR6w,", PlatformMask = -1, FriendlyName = "Collared Shirt (Flowers, Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,6d703981-2734-4c45-8983-cdd5f328902f,a0271cd0-e172-4d3f-aa2f-9806f21a82d2,", PlatformMask = -1, FriendlyName = "Tank Top (Camo)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,5c4a2b35-0e1c-44de-8c3a-96d4a6458b1b,9c03f381-7357-4d0f-8cda-8737d4c43d25,", PlatformMask = -1, FriendlyName = "Tank Top (Rainbow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,d2a692e6-e1a9-4cfe-8154-10b52be7f8c8,", PlatformMask = -1, FriendlyName = "Jersey (Orange)", Tooltip = "", Rarity = 10 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,ad61c418-6d77-4a99-8ac5-9f10f5a3d42f,b292eb4b-07e3-4a48-99b5-3c6587a1e02e,", PlatformMask = -1, FriendlyName = "Tank Top (Dots)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,48abd952-214f-48b2-a8f1-1146f6f69aa2,b78008e8-abbd-4ece-be34-9a911f721fcc,", PlatformMask = -1, FriendlyName = "Tank Top (Zebra)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Orange, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Pink, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Blue, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,6dd95046-acf8-42fe-ab78-80a334096a9d,56a92c8d-af53-413e-929e-4a9a3cfad780,", PlatformMask = -1, FriendlyName = "Headband (Red, White, Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fcfcaf63-deb4-45f7-b711-c051c9ea45cb,,,", PlatformMask = -1, FriendlyName = "Top Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,,,", PlatformMask = -1, FriendlyName = "Blazer (Blue, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,6f2e74bf-1e95-463d-97db-d5d1a53b2c28,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Black, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,9374bf66-2ee5-493b-8439-efce4b201904,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Grey, Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,272fe8eb-5061-4729-a7a8-414ff667a82f,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Grey, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,0ffad843-d6c9-425a-8686-7217009c867e,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Green, Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,e0397982-c2c2-4733-9a40-46e18675b5af,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,,,", PlatformMask = -1, FriendlyName = "Button Top (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,49f5864f-9d40-497c-88c8-e87f64d41d74,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Tan)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,c5deba2a-6e35-4b13-8e94-8ba5457f39df,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,f750de46-3758-4f7d-9709-0a84b1027009,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,d66aa400-aa5a-4539-a25d-5f8ce94dc281,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,6564acf1-4d70-4f92-92ac-08e2b76dbb6b,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,,,", PlatformMask = -1, FriendlyName = "Collared Dress (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "241506f6-bf88-4b46-b5fe-513a225421f4,,,", PlatformMask = -1, FriendlyName = "Half Up Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,d6edbc00-3c1d-4f49-8412-3ef8c7c5f4c2,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,6d703981-2734-4c45-8983-cdd5f328902f,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,830be2fa-60a5-48cc-931f-34b670eae4bd,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,bbfa08e3-8e6b-4e0f-b264-1b398d7cd44a,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,f8b0cfe8-e129-4578-8bb5-f60af5d38599,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Gold)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,5c4a2b35-0e1c-44de-8c3a-96d4a6458b1b,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,6dd95046-acf8-42fe-ab78-80a334096a9d,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,f8b0cfe8-e129-4578-8bb5-f60af5d38599,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,,,", PlatformMask = -1, FriendlyName = "Headband (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "24a240f4-1574-420b-b898-a7e91f170759,,,", PlatformMask = -1, FriendlyName = "Back Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c45ed7b8-99bd-4a4b-a9ff-e16edf5d7a18,,,", PlatformMask = -1, FriendlyName = "High Pony Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,,,", PlatformMask = -1, FriendlyName = "Baseball Cap (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,,,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,55901f12-d5b5-4fa8-b4c8-e479689ee39d,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,4828b50c-95b6-466a-bb25-514891d78202,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,d6823e01-69f0-4f85-b94a-74894356a2cf,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Maroon)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "09177621-9ecd-4f6a-b6a5-64490139141d,,,", PlatformMask = -1, FriendlyName = "Flat Top Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "95ab7a7c-c35d-4da5-9955-0921064470b6,,,", PlatformMask = -1, FriendlyName = "Gekko Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,0ecb8a2a-cffc-47db-aeda-fb0684aef1e5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,8377ab96-c908-457f-9fee-b784c9a759f3,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Red, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,,,", PlatformMask = -1, FriendlyName = "Slim Blazer (Teal)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,cd5d7285-202d-42d0-b93f-04245875793e,0f36bb97-c61b-4281-929f-ff1d0d11be86,", PlatformMask = -1, FriendlyName = "Slim Blazer (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,ad61c418-6d77-4a99-8ac5-9f10f5a3d42f,0f36bb97-c61b-4281-929f-ff1d0d11be86,", PlatformMask = -1, FriendlyName = "Slim Blazer (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8b9f1413-e786-4a30-946c-9292f207875a,,,", PlatformMask = -1, FriendlyName = "Pulp Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "5cd08cfb-c729-4c30-96d9-6a99bb934d91,,,", PlatformMask = -1, FriendlyName = "Rec Room Sash", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "1a71064b-794f-40fa-9109-8ad36602b6e1,,,", PlatformMask = -1, FriendlyName = "Shagg Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,761a3193-60f0-4190-80c7-285b8192e794,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,a819f49b-6c7a-49d3-9e6a-d9d79ef5019f,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,64850553-cdfe-455a-ac00-dafbe63d613e,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,,,", PlatformMask = -1, FriendlyName = "Stoll Dress (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wrist Tape (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wrist Tape (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,0ecb8a2a-cffc-47db-aeda-fb0684aef1e5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,,,", PlatformMask = -1, FriendlyName = "Long Scarf (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,0iSsaY-HgkmLaRHCn5vEdw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,jGj28vhq8EGwP2RuM074aQ,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,kmj5zOjcwku_WWKroCeiVQ,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,FAviMCQ_EE2Mpt6QPo5OEw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,MFrcSQ1DYUm8imvy4ypgvw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,05ac07e1-67f0-486c-abf5-a62866475abb,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Black, Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0088603e-ec3b-4478-8694-e6fb1989b3f2,,,", PlatformMask = -1, FriendlyName = "Angled Bob Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ffea7a65-613f-4835-921e-6dd15f357b7e,,,", PlatformMask = -1, FriendlyName = "Long Bangs Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "45f5e714-8a5f-4385-a97f-675066167011,,,", PlatformMask = -1, FriendlyName = "Seventies Stache", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9bf5d259-7774-4cbe-a90f-7f188cc0dce7,,,", PlatformMask = -1, FriendlyName = "Thick Goatee", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "a6cbfe76-534a-4655-a8a8-3fed13d001c7,,,", PlatformMask = -1, FriendlyName = "Bald Top Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "CTcrvbo3OEepIV4oW8bx4w,,,", PlatformMask = -1, FriendlyName = "Receding Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "-twtjyBdQ02EAdOfBGTiEw,,,", PlatformMask = -1, FriendlyName = "Van Dyke Beard", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "45eaab67-19c2-4601-8f80-3565a4dceba4,,,", PlatformMask = -1, FriendlyName = "Pompadour Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c855dcc3-96cb-470d-b159-d37a025a47d1,,,", PlatformMask = -1, FriendlyName = "Dutch Braid Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d7730a9e-78a1-4356-bc09-6b066615850b,,,", PlatformMask = -1, FriendlyName = "Afro Updo Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8c35c804-e8d5-49d2-8d5a-ea19fb70bfa6,,,", PlatformMask = -1, FriendlyName = "Pencil Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "5beeb4c4-f276-4eae-87aa-9302e45b05b7,,,", PlatformMask = -1, FriendlyName = "Cornrows Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b6rLwzD4NkKV7xKn9ZYVkA,sxUE0iOSZEmezm54T7xI3Q,tlpa7195x0CkmSjpR1RArQ,", PlatformMask = -1, FriendlyName = "Rec Room Hoodie - Pride (Rainbow Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fe15ca53-c5b8-4acf-9309-ff3f4e610fc9,,,", PlatformMask = -1, FriendlyName = "Winged Hat - Pride (Rainbow Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b6rLwzD4NkKV7xKn9ZYVkA,D_Xmo0rOzkS-kgq1CYXt3g,tnCJp2eDI0SwjVfJMhk3LQ,", PlatformMask = -1, FriendlyName = "Rec Room Hoodie - Pride (Trans Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fe15ca53-c5b8-4acf-9309-ff3f4e610fc9,knXPidb-Rkayfc3kSHfZeQ,1yMyo6oTjU-VAygoeWaohQ,", PlatformMask = -1, FriendlyName = "Winged Hat - Pride (Trans Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "88b6ddeb-a455-460d-91d9-a4569ef6903c,,,", PlatformMask = -1, FriendlyName = "Square Earrings ", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0abb6b08-20ce-444f-879e-0d1344df096c,,,", PlatformMask = -1, FriendlyName = "Round Earrings", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9b5bde11-7408-4798-9fcb-c7ec175444df,,,", PlatformMask = -1, FriendlyName = "Hoop Earrings", Tooltip = "", Rarity = 0 }
};
app.MapGet("/api/avatar/v1/defaultbaseavataritems", () => var allItems = ownedItems.Cast<object>().Concat(defaultItems).ToList();
return Results.Json(allItems);
}
[HttpGet("api/objectives/v1/myprogress")]
public async Task<IResult> ObjectivesMyProgress()
{
var json = System.IO.File.ReadAllText("JSON/tempmyprogress.json");
return Results.Content(json, "application/json");
}
[HttpGet("api/avatar/v2")]
[Authorize]
public async Task<IResult> GetAvatarV2(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var avatar = await db.PlayerAvatars
.FirstOrDefaultAsync(a => a.OwnerAccountId == id);
if (avatar == null)
{ {
var json = File.ReadAllText("JSON/defaultAvatarItems.json"); avatar = new PlayerAvatar
return Results.Content(json, "application/json");
});
app.MapGet("/api/avatar/v4/items", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var ownedItems = await db.AvatarItems
.Where(i => i.OwnerAccountId == id)
.ToListAsync();
var defaultItems = new List<object>
{ {
new { AvatarItemType = 0, AvatarItemDesc = "5d13a7a2-8213-40e6-90a6-efdd76a3fdcb,,,", PlatformMask = -1, FriendlyName = "Flowing Hair", Tooltip = "", Rarity = 0 }, OwnerAccountId = id,
new { AvatarItemType = 0, AvatarItemDesc = "1d27b674-f9e2-4ffc-9d8c-a58a1be06457,,,", PlatformMask = -1, FriendlyName = "Afro Hair", Tooltip = "", Rarity = 0 }, OutfitSelections = "",
new { AvatarItemType = 0, AvatarItemDesc = "d84c0ff9-8fbe-4ed8-abf3-7996e81888ab,,,", PlatformMask = -1, FriendlyName = "Large Afro Hair", Tooltip = "", Rarity = 0 }, FaceFeatures = "{}",
new { AvatarItemType = 0, AvatarItemDesc = "e5b83dfc-b2e1-4dcb-a4ab-9d3a4c8a34ae,,,", PlatformMask = -1, FriendlyName = "Long Wavy Hair", Tooltip = "", Rarity = 0 }, SkinColor = "",
new { AvatarItemType = 0, AvatarItemDesc = "7dd6f7b0-7ba0-429f-a04f-e32d3a79ee61,,,", PlatformMask = -1, FriendlyName = "Short Wavy Hair", Tooltip = "", Rarity = 0 }, HairColor = ""
new { AvatarItemType = 0, AvatarItemDesc = "eb9611c6-bb50-41a2-93e9-7f959815a846,,,", PlatformMask = -1, FriendlyName = "Dreads Long Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "1fd69ef8-0b74-4962-af5a-67f0bf0358f2,,,", PlatformMask = -1, FriendlyName = "Ponytail Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "a12f724f-4a73-4ab8-aad4-6bfc662b4dd6,,,", PlatformMask = -1, FriendlyName = "Undercut Long Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0753d7a4-8247-4fca-a6fc-359c26086140,,,", PlatformMask = -1, FriendlyName = "Fonzie Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "77d3c585-4928-4471-a425-89036efe7299,,,", PlatformMask = -1, FriendlyName = "Spiky Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "92302d9d-c527-418c-ac5d-1fa869727505,,,", PlatformMask = -1, FriendlyName = "Part Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "f9dd08f8-16d3-4c39-af4f-89f7bb6e80d3,,,", PlatformMask = -1, FriendlyName = "Undercut Short Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b148cb1e-df81-442f-aea6-ab1727aad00e,,,", PlatformMask = -1, FriendlyName = "Chunky Afro Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e36bcd98-7e85-43fa-89f8-57e4ec33823a,,,", PlatformMask = -1, FriendlyName = "Bob with Bangs Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "880a3cc0-7407-4b61-b759-f9dd890fe9e5,,,", PlatformMask = -1, FriendlyName = "Bob Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21599b51-c50f-43d8-ac5f-62c30cd02ca5,,,", PlatformMask = -1, FriendlyName = "Lori Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "193a3bf9-abc0-4d78-8d63-92046908b1c5,,,", PlatformMask = -1, FriendlyName = "Emo Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "79b90274-6eec-4664-acfb-4a123334661e,,,", PlatformMask = -1, FriendlyName = "Pig Tails Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "da4e7b34-2095-4a9e-801e-4f409039e0dd,,,", PlatformMask = -1, FriendlyName = "Buzz Cut Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9d9fadb6-97eb-480e-a224-4e0179082071,,,", PlatformMask = -1, FriendlyName = "Meatball Buns Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d8280c0c-d803-4513-be10-a0ba96d8821e,,,", PlatformMask = -1, FriendlyName = "Flowhawk Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "e286863c-2967-4d00-b837-b49487b9484a,,,", PlatformMask = -1, FriendlyName = "Fauxhawk Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2cb4f372-3372-4583-8b57-c4e3988e3c28,,,", PlatformMask = -1, FriendlyName = "Punky Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "06306723-ca20-4aa6-b7b3-917113f41ac3,,,", PlatformMask = -1, FriendlyName = "Cat-Eye Glasses (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c70005d5-6276-4a98-acb3-6a77bc19379a,,,", PlatformMask = -1, FriendlyName = "Glasses (Teal)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8d10cc78-6b00-45f3-affb-205e9cc5b03f,,,", PlatformMask = -1, FriendlyName = "Beard (Close)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "cc96f8a5-bc5b-4f89-83b7-ecd53905ada7,,,", PlatformMask = -1, FriendlyName = "Beard (Thick)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c6c08eb5-381a-4193-9722-80da95d62abe,,,", PlatformMask = -1, FriendlyName = "Business Tie (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "4d507dfa-4a99-4ac0-8537-229e9dc0eb4a,,,", PlatformMask = -1, FriendlyName = "Rec Room Tank Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d0a9262f-5504-46a7-bb10-7507503db58e,,,", PlatformMask = -1, FriendlyName = "Rec Room Shirt (Crew Neck, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d0a9262f-5504-46a7-bb10-7507503db58e,95e4cc30-cb68-473d-a395-feadf5b51512,0440f08f-ef1d-49d8-942b-523056e8bb45,", PlatformMask = -1, FriendlyName = "Rec Room T-Shirt (Crew Neck, Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,,,", PlatformMask = -1, FriendlyName = "Collared Shirt (Plaid, Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,,,", PlatformMask = -1, FriendlyName = "T-Shirt", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8aa79563-ace1-4ba7-ad0c-f3210a78142f,,,", PlatformMask = -1, FriendlyName = "Rec Room Shirt (V-Neck, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8aa79563-ace1-4ba7-ad0c-f3210a78142f,95e4cc30-cb68-473d-a395-feadf5b51512,05f0ee6e-c824-470e-9178-5ed576c6fe0c,", PlatformMask = -1, FriendlyName = "Rec Room T-Shirt (V-Neck, Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,,,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,c5deba2a-6e35-4b13-8e94-8ba5457f39df,b75ef67d-00c3-4ac1-9b72-212032460294,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "21caa68e-c3fa-474c-af5e-af1e742b7a60,758752bd-db2f-43d2-b580-55b3e1efffd5,b75ef67d-00c3-4ac1-9b72-212032460294,", PlatformMask = -1, FriendlyName = "Tennis Skirt (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,,,", PlatformMask = -1, FriendlyName = "Zipper Dress (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,,,", PlatformMask = -1, FriendlyName = "Wristbands (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,,,", PlatformMask = -1, FriendlyName = "Wrist Tape (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,55901f12-d5b5-4fa8-b4c8-e479689ee39d,f600037d-c9c0-43fa-b45b-02f456f9dd5f,", PlatformMask = -1, FriendlyName = "Collared Shirt (Denim)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,bf82f2f6-9af8-431e-a296-0890dea48ba7,d015cae7-a905-49e4-8823-6dec069689a6,", PlatformMask = -1, FriendlyName = "Collared Shirt (Argyle)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,EfdMcnfHt0mr0PQ_maaYOg,DRJcNhkqvkKFEaZpOguR6w,", PlatformMask = -1, FriendlyName = "Collared Shirt (Flowers, Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,6d703981-2734-4c45-8983-cdd5f328902f,a0271cd0-e172-4d3f-aa2f-9806f21a82d2,", PlatformMask = -1, FriendlyName = "Tank Top (Camo)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,5c4a2b35-0e1c-44de-8c3a-96d4a6458b1b,9c03f381-7357-4d0f-8cda-8737d4c43d25,", PlatformMask = -1, FriendlyName = "Tank Top (Rainbow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,d2a692e6-e1a9-4cfe-8154-10b52be7f8c8,", PlatformMask = -1, FriendlyName = "Jersey (Orange)", Tooltip = "", Rarity = 10 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,ad61c418-6d77-4a99-8ac5-9f10f5a3d42f,b292eb4b-07e3-4a48-99b5-3c6587a1e02e,", PlatformMask = -1, FriendlyName = "Tank Top (Dots)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,48abd952-214f-48b2-a8f1-1146f6f69aa2,b78008e8-abbd-4ece-be34-9a911f721fcc,", PlatformMask = -1, FriendlyName = "Tank Top (Zebra)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Orange, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Pink, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Blue, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,6dd95046-acf8-42fe-ab78-80a334096a9d,56a92c8d-af53-413e-929e-4a9a3cfad780,", PlatformMask = -1, FriendlyName = "Headband (Red, White, Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fcfcaf63-deb4-45f7-b711-c051c9ea45cb,,,", PlatformMask = -1, FriendlyName = "Top Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,,,", PlatformMask = -1, FriendlyName = "Blazer (Blue, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,6f2e74bf-1e95-463d-97db-d5d1a53b2c28,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Black, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,9374bf66-2ee5-493b-8439-efce4b201904,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Grey, Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,272fe8eb-5061-4729-a7a8-414ff667a82f,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Grey, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,0ffad843-d6c9-425a-8686-7217009c867e,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Green, Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,e0397982-c2c2-4733-9a40-46e18675b5af,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,,,", PlatformMask = -1, FriendlyName = "Button Top (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,49f5864f-9d40-497c-88c8-e87f64d41d74,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Tan)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9c8fc7f0-8f99-4aad-a34f-8d979f6ae352,c5deba2a-6e35-4b13-8e94-8ba5457f39df,dafa658e-753b-46cb-bd85-85c1de5e6ea7,", PlatformMask = -1, FriendlyName = "Button Top (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,f750de46-3758-4f7d-9709-0a84b1027009,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,d66aa400-aa5a-4539-a25d-5f8ce94dc281,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,6564acf1-4d70-4f92-92ac-08e2b76dbb6b,2c8924aa-68f8-4912-9759-18992f72f08a,", PlatformMask = -1, FriendlyName = "Collared Dress (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6d815b35-6f68-4ed4-817d-70f141e1a571,,,", PlatformMask = -1, FriendlyName = "Collared Dress (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "241506f6-bf88-4b46-b5fe-513a225421f4,,,", PlatformMask = -1, FriendlyName = "Half Up Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,d6edbc00-3c1d-4f49-8412-3ef8c7c5f4c2,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,6d703981-2734-4c45-8983-cdd5f328902f,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,830be2fa-60a5-48cc-931f-34b670eae4bd,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2296ed0d-df56-4d46-b33a-aae9230a47fc,bbfa08e3-8e6b-4e0f-b264-1b398d7cd44a,cfabdefe-0890-436e-b2a3-b5c712e22955,", PlatformMask = -1, FriendlyName = "Zipper Dress (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,f8b0cfe8-e129-4578-8bb5-f60af5d38599,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Gold)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "7b857a8c-92ad-4028-a2c2-b3c20cdab5f2,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Tank Top (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,5c4a2b35-0e1c-44de-8c3a-96d4a6458b1b,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (Purple)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,6dd95046-acf8-42fe-ab78-80a334096a9d,cf119781-5bd9-4b85-9a0b-12e82e988c23,", PlatformMask = -1, FriendlyName = "Long Scarf (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,dee70c38-7a99-4c2b-9181-665f1bf75aca,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,f8b0cfe8-e129-4578-8bb5-f60af5d38599,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,8377ab96-c908-457f-9fee-b784c9a759f3,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,,,", PlatformMask = -1, FriendlyName = "Headband (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "24a240f4-1574-420b-b898-a7e91f170759,,,", PlatformMask = -1, FriendlyName = "Back Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c45ed7b8-99bd-4a4b-a9ff-e16edf5d7a18,,,", PlatformMask = -1, FriendlyName = "High Pony Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,,,", PlatformMask = -1, FriendlyName = "Baseball Cap (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "14ef6b00-debf-4a85-9755-b4d37df496d3,67bcca75-4ab1-4964-8688-9908c464d355,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Baseball Cap (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,,,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,55901f12-d5b5-4fa8-b4c8-e479689ee39d,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,4828b50c-95b6-466a-bb25-514891d78202,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "896c2491-2f96-4986-9cbd-b3b31ef5d8c5,d6823e01-69f0-4f85-b94a-74894356a2cf,d344b8cc-85a8-4ace-9f92-38c84f396e99,", PlatformMask = -1, FriendlyName = "Equestrian Coat (Maroon)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "09177621-9ecd-4f6a-b6a5-64490139141d,,,", PlatformMask = -1, FriendlyName = "Flat Top Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "95ab7a7c-c35d-4da5-9955-0921064470b6,,,", PlatformMask = -1, FriendlyName = "Gekko Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,0ecb8a2a-cffc-47db-aeda-fb0684aef1e5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,484b6c13-af22-4ad5-8c43-34c0de095d49,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Light Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,51ef8d39-2b94-4f9e-9620-07b6b0a913a5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,cbe29e9f-f2ac-47fb-97e1-8bad16abb89d,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Headband (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "40528de7-38a3-4a7c-8f93-6d3bfa5573f2,8377ab96-c908-457f-9fee-b784c9a759f3,018a5c07-e956-457d-a540-a5e2cd68da09,", PlatformMask = -1, FriendlyName = "Headband (Red, White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,,,", PlatformMask = -1, FriendlyName = "Slim Blazer (Teal)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,cd5d7285-202d-42d0-b93f-04245875793e,0f36bb97-c61b-4281-929f-ff1d0d11be86,", PlatformMask = -1, FriendlyName = "Slim Blazer (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "62ce4109-8dee-4895-bf1b-bfa143db4c7e,ad61c418-6d77-4a99-8ac5-9f10f5a3d42f,0f36bb97-c61b-4281-929f-ff1d0d11be86,", PlatformMask = -1, FriendlyName = "Slim Blazer (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8b9f1413-e786-4a30-946c-9292f207875a,,,", PlatformMask = -1, FriendlyName = "Pulp Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "5cd08cfb-c729-4c30-96d9-6a99bb934d91,,,", PlatformMask = -1, FriendlyName = "Rec Room Sash", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "1a71064b-794f-40fa-9109-8ad36602b6e1,,,", PlatformMask = -1, FriendlyName = "Shagg Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,761a3193-60f0-4190-80c7-285b8192e794,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,a819f49b-6c7a-49d3-9e6a-d9d79ef5019f,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Green)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,64850553-cdfe-455a-ac00-dafbe63d613e,91a451c1-b285-4c48-b14d-59ded8cc006f,", PlatformMask = -1, FriendlyName = "Stoll Dress (Orange)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "84cd594c-1cd8-4b4d-8409-85c8fd5fb02a,,,", PlatformMask = -1, FriendlyName = "Stoll Dress (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,1b1d08f2-12ca-43dd-a44f-ea2820b919b4,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wrist Tape (Black)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "71921831-ba6f-408b-a00e-2fd97663636f,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wrist Tape (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,0ecb8a2a-cffc-47db-aeda-fb0684aef1e5,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Grey)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ecc1dbe6-ca06-4564-b2a6-30956194d1e9,7d8e55fe-3c34-4b4b-9753-0021f6cc6454,0b2395e1-ebcc-47e9-aaf1-faf9e9cec4cd,", PlatformMask = -1, FriendlyName = "Wristbands (Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "6b9e022c-0b68-48fd-8eca-da8573c18900,,,", PlatformMask = -1, FriendlyName = "Long Scarf (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,0iSsaY-HgkmLaRHCn5vEdw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Blue)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,jGj28vhq8EGwP2RuM074aQ,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Yellow)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,kmj5zOjcwku_WWKroCeiVQ,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Pink)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,FAviMCQ_EE2Mpt6QPo5OEw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (Red)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "2e59d8d0-91a0-4449-bfdc-a5d663fd9343,MFrcSQ1DYUm8imvy4ypgvw,PioQ0o3yP0a6szPZ4EKs2A,", PlatformMask = -1, FriendlyName = "Collared Shirt (White)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "de0ac50d-2adb-4114-bd2e-68953b13d706,05ac07e1-67f0-486c-abf5-a62866475abb,be2b9293-1d3c-4b1c-b4c5-fad3ab16cf54,", PlatformMask = -1, FriendlyName = "Blazer (Black, Cream)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0088603e-ec3b-4478-8694-e6fb1989b3f2,,,", PlatformMask = -1, FriendlyName = "Angled Bob Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "ffea7a65-613f-4835-921e-6dd15f357b7e,,,", PlatformMask = -1, FriendlyName = "Long Bangs Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "45f5e714-8a5f-4385-a97f-675066167011,,,", PlatformMask = -1, FriendlyName = "Seventies Stache", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9bf5d259-7774-4cbe-a90f-7f188cc0dce7,,,", PlatformMask = -1, FriendlyName = "Thick Goatee", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "a6cbfe76-534a-4655-a8a8-3fed13d001c7,,,", PlatformMask = -1, FriendlyName = "Bald Top Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "CTcrvbo3OEepIV4oW8bx4w,,,", PlatformMask = -1, FriendlyName = "Receding Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "-twtjyBdQ02EAdOfBGTiEw,,,", PlatformMask = -1, FriendlyName = "Van Dyke Beard", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "45eaab67-19c2-4601-8f80-3565a4dceba4,,,", PlatformMask = -1, FriendlyName = "Pompadour Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "c855dcc3-96cb-470d-b159-d37a025a47d1,,,", PlatformMask = -1, FriendlyName = "Dutch Braid Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "d7730a9e-78a1-4356-bc09-6b066615850b,,,", PlatformMask = -1, FriendlyName = "Afro Updo Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "8c35c804-e8d5-49d2-8d5a-ea19fb70bfa6,,,", PlatformMask = -1, FriendlyName = "Pencil Bun Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "5beeb4c4-f276-4eae-87aa-9302e45b05b7,,,", PlatformMask = -1, FriendlyName = "Cornrows Hair", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b6rLwzD4NkKV7xKn9ZYVkA,sxUE0iOSZEmezm54T7xI3Q,tlpa7195x0CkmSjpR1RArQ,", PlatformMask = -1, FriendlyName = "Rec Room Hoodie - Pride (Rainbow Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fe15ca53-c5b8-4acf-9309-ff3f4e610fc9,,,", PlatformMask = -1, FriendlyName = "Winged Hat - Pride (Rainbow Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "b6rLwzD4NkKV7xKn9ZYVkA,D_Xmo0rOzkS-kgq1CYXt3g,tnCJp2eDI0SwjVfJMhk3LQ,", PlatformMask = -1, FriendlyName = "Rec Room Hoodie - Pride (Trans Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "fe15ca53-c5b8-4acf-9309-ff3f4e610fc9,knXPidb-Rkayfc3kSHfZeQ,1yMyo6oTjU-VAygoeWaohQ,", PlatformMask = -1, FriendlyName = "Winged Hat - Pride (Trans Pride)", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "88b6ddeb-a455-460d-91d9-a4569ef6903c,,,", PlatformMask = -1, FriendlyName = "Square Earrings ", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "0abb6b08-20ce-444f-879e-0d1344df096c,,,", PlatformMask = -1, FriendlyName = "Round Earrings", Tooltip = "", Rarity = 0 },
new { AvatarItemType = 0, AvatarItemDesc = "9b5bde11-7408-4798-9fcb-c7ec175444df,,,", PlatformMask = -1, FriendlyName = "Hoop Earrings", Tooltip = "", Rarity = 0 }
}; };
db.PlayerAvatars.Add(avatar);
var allItems = ownedItems.Cast<object>().Concat(defaultItems).ToList();
return Results.Json(allItems);
});
app.MapGet("/api/objectives/v1/myprogress", async (HttpRequest request, AppDbContext db) =>
{
// TODO ADD FUNCTIONALITY
var json = File.ReadAllText("JSON/tempmyprogress.json");
return Results.Content(json, "application/json");
});
app.MapGet("/api/avatar/v2", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var avatar = await db.PlayerAvatars
.FirstOrDefaultAsync(a => a.OwnerAccountId == id);
if (avatar == null)
{
avatar = new PlayerAvatar
{
OwnerAccountId = id,
OutfitSelections = "",
FaceFeatures = "{}",
SkinColor = "",
HairColor = ""
};
db.PlayerAvatars.Add(avatar);
await db.SaveChangesAsync();
}
return Results.Json(new
{
OutfitSelections = avatar.OutfitSelections,
FaceFeatures = avatar.FaceFeatures,
SkinColor = avatar.SkinColor,
HairColor = avatar.HairColor
});
});
app.MapGet("/econ/customAvatarItems/v1/owned", async (HttpRequest request) =>
{
return Results.Json(new { items = new List<object>() });
});
app.MapPost("/api/settings/v2/set", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body);
var body = await reader.ReadToEndAsync();
Console.WriteLine($"Settings request body: {body}");
var settings = new List<PlayerSetting>();
if (body.TrimStart().StartsWith("["))
{
settings = System.Text.Json.JsonSerializer.Deserialize<List<PlayerSetting>>(body) ?? [];
}
else
{
var single = System.Text.Json.JsonSerializer.Deserialize<PlayerSetting>(body);
if (single != null) settings.Add(single);
}
settings = settings.Where(s => !string.IsNullOrEmpty(s.Key)).ToList();
if (!settings.Any())
return Results.Ok();
db.PlayerSettings.RemoveRange(db.PlayerSettings.Where(s => s.PlayerId == id));
foreach (var setting in settings)
{
setting.PlayerId = id;
setting.Key = setting.Key ?? "";
setting.Value = setting.Value ?? "";
db.PlayerSettings.Add(setting);
}
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return Results.Ok(); }
});
app.MapGet("/api/equipment/v2/getUnlocked", async (HttpRequest request, AppDbContext db) => return Results.Json(new
{ {
// TODO ADD FUNCTIONALITY OutfitSelections = avatar.OutfitSelections,
return "[]"; FaceFeatures = avatar.FaceFeatures,
}); SkinColor = avatar.SkinColor,
HairColor = avatar.HairColor
app.MapGet("/api/consumables/v2/getUnlocked", async (HttpRequest request, AppDbContext db, JwtTokenService jwtService) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var consumables = await db.ConsumableItems
.Where(c => c.OwnerAccountId == id)
.ToListAsync();
return Results.Json(consumables);
});
app.MapGet("/api/storefronts/v4/balance/2", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var balance = await db.TokenBalances
.Where(s => s.Id == id)
.ToListAsync();
return Results.Json(balance);
});
app.MapGet("/api/storefronts/v3/giftdropstore/3", async (HttpRequest request, AppDbContext db) =>
{
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 2 && s.Name == "watch_store");
if (storefront == null)
{
var json = File.ReadAllText("JSON/storefront3.json");
return Results.Content(json, "application/json");
}
var storeItems = storefront.Items.Select(item => new
{
item.Id,
item.StorefrontId,
item.PurchasableItemId,
item.Type,
item.IsFeatured,
item.NewUntil,
GiftDrops = item.GiftDrops.Select(gd => new
{
gd.Id,
gd.StorefrontItemId,
gd.GiftDropId,
gd.FriendlyName,
gd.Tooltip,
gd.ConsumableItemDesc,
gd.AvatarItemDesc,
gd.AvatarItemType,
gd.EquipmentPrefabName,
gd.EquipmentModificationGuid,
gd.IsQuery,
gd.Unique,
gd.SubscribersOnly,
gd.Level,
gd.Rarity,
gd.CurrencyType,
gd.Currency,
gd.Context,
gd.ItemSetId,
gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new
{
p.Id,
p.StorefrontItemId,
p.CurrencyType,
p.Price
}).ToList()
}).ToList();
var response = new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems };
return Results.Ok(response);
});
app.MapGet("/api/challenge/v2/getCurrent", async (HttpRequest request, AppDbContext db) =>
{
var json = File.ReadAllText("JSON/weeklychallenge.json");
return Results.Content(json, "application/json");
//return "{}";
});
app.MapGet("/api/avatar/v3/saved", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
var items = await db.SavedOutfits
.Where(i => i.OwnerAccountId == id)
.ToListAsync();
return Results.Json(items);
});
app.MapGet("/api/avatar/v2/gifts", async (HttpRequest request, AppDbContext db, JwtTokenService jwtService) =>
{
try
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
// Get all pending (unconsumed) gifts for this player
var pendingGifts = await db.ReceivedGifts
.Where(rg => rg.ReceiverAccountId == id && !rg.IsConsumed)
.ToListAsync();
return Results.Json(pendingGifts);
}
catch (Exception ex)
{
return Results.Problem($"Error retrieving gifts: {ex.Message}");
}
});
app.MapGet("/api/gamerewards/v1/pending", async (HttpRequest request, AppDbContext db) =>
{
// TODO ADD FUNCTIONALITY
return "[]";
});
app.MapGet("/api/roomkeys/v1/mine", async (HttpRequest request, AppDbContext db) =>
{
// TODO ADD FUNCTIONALITY
return "[]";
});
app.MapPost("/api/CampusCard/v1/UpdateAndGetSubscription", async (HttpRequest request, AppDbContext db) =>
{
return Results.Json(new { subscription = (object?)null, platformAccountSubscribedPlayerId = (object?)null });
}); });
} }
[HttpPost("api/settings/v2/set")]
[Authorize]
public async Task<IResult> SetSettings(HttpRequest request, AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body);
var body = await reader.ReadToEndAsync();
Console.WriteLine($"Settings request body: {body}");
var settings = new List<PlayerSetting>();
if (body.TrimStart().StartsWith("["))
{
settings = System.Text.Json.JsonSerializer.Deserialize<List<PlayerSetting>>(body) ?? [];
}
else
{
var single = System.Text.Json.JsonSerializer.Deserialize<PlayerSetting>(body);
if (single != null) settings.Add(single);
}
settings = settings.Where(s => !string.IsNullOrEmpty(s.Key)).ToList();
if (!settings.Any())
return Results.Ok();
db.PlayerSettings.RemoveRange(db.PlayerSettings.Where(s => s.PlayerId == id));
foreach (var setting in settings)
{
setting.PlayerId = id;
setting.Key = setting.Key ?? "";
setting.Value = setting.Value ?? "";
db.PlayerSettings.Add(setting);
}
await db.SaveChangesAsync();
return Results.Ok();
}
[HttpGet("econ/customAvatarItems/v1/owned")]
public async Task<IResult> CustomAvatarItemsOwned()
{
return Results.Json(new { items = new List<object>() });
}
[HttpGet("api/equipment/v2/getUnlocked")]
public async Task<IResult> GetEquipmentUnlocked()
{
return Results.Content("[]", "application/json");
}
[HttpGet("api/consumables/v2/getUnlocked")]
[Authorize]
public async Task<IResult> GetConsumablesUnlocked(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var consumables = await db.ConsumableItems
.Where(c => c.OwnerAccountId == id)
.ToListAsync();
return Results.Json(consumables);
}
[HttpGet("api/storefronts/v4/balance/2")]
[Authorize]
public async Task<IResult> GetStorefrontBalance(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var balance = await db.TokenBalances
.Where(s => s.Id == id)
.ToListAsync();
return Results.Json(balance);
}
[HttpGet("api/storefronts/v3/giftdropstore/3")]
public async Task<IResult> GetGiftDropStore3(AppDbContext db, StorefrontFillService storefrontService)
{
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 2 && s.Name == "watch_store");
if (storefront == null)
{
var json = System.IO.File.ReadAllText("JSON/storefront3.json");
return Results.Content(json, "application/json");
}
var storeItems = storefront.Items.Select(item => new
{
item.Id,
item.StorefrontId,
item.PurchasableItemId,
item.Type,
item.IsFeatured,
item.NewUntil,
GiftDrops = item.GiftDrops.Select(gd => new
{
gd.Id,
gd.StorefrontItemId,
gd.GiftDropId,
gd.FriendlyName,
gd.Tooltip,
gd.ConsumableItemDesc,
gd.AvatarItemDesc,
gd.AvatarItemType,
gd.EquipmentPrefabName,
gd.EquipmentModificationGuid,
gd.IsQuery,
gd.Unique,
gd.SubscribersOnly,
gd.Level,
gd.Rarity,
gd.CurrencyType,
gd.Currency,
gd.Context,
gd.ItemSetId,
gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new
{
p.Id,
p.StorefrontItemId,
p.CurrencyType,
p.Price
}).ToList()
}).ToList();
var response = new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems };
return Results.Ok(response);
}
[HttpGet("api/challenge/v2/getCurrent")]
public async Task<IResult> GetCurrentChallenge()
{
var json = System.IO.File.ReadAllText("JSON/weeklychallenge.json");
return Results.Content(json, "application/json");
}
[HttpGet("api/avatar/v3/saved")]
[Authorize]
public async Task<IResult> GetSavedAvatars(HttpRequest request, AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
request.EnableBuffering();
var items = await db.SavedOutfits
.Where(i => i.OwnerAccountId == id)
.ToListAsync();
return Results.Json(items);
}
[HttpGet("api/avatar/v2/gifts")]
[Authorize]
public async Task<IResult> GetGifts(AppDbContext db)
{
try
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var pendingGifts = await db.ReceivedGifts
.Where(rg => rg.ReceiverAccountId == id && !rg.IsConsumed)
.ToListAsync();
return Results.Json(pendingGifts);
}
catch (Exception ex)
{
return Results.Problem($"Error retrieving gifts: {ex.Message}");
}
}
[HttpGet("api/gamerewards/v1/pending")]
public async Task<IResult> GetGameRewardsPending()
{
return Results.Content("[]", "application/json");
}
[HttpGet("api/roomkeys/v1/mine")]
public async Task<IResult> GetRoomKeysMine()
{
return Results.Content("[]", "application/json");
}
[HttpPost("api/CampusCard/v1/UpdateAndGetSubscription")]
public async Task<IResult> UpdateAndGetSubscription()
{
return Results.Json(new { subscription = (object?)null, platformAccountSubscribedPlayerId = (object?)null });
}
} }
@@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
[ApiController, Route("")] [ApiController, Route("img")]
public class ImageController : ControllerBase public class ImageController : ControllerBase
{ {
private static readonly byte[] PlaceholderJpeg; private static readonly byte[] PlaceholderJpeg;
@@ -22,8 +22,6 @@ public class ImageController : ControllerBase
PlaceholderJpeg = ms.ToArray(); PlaceholderJpeg = ms.ToArray();
} }
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args);
[HttpGet("{imageName}")] [HttpGet("{imageName}")]
public async Task<IResult> GetImage(HttpContext context, string imageName) { public async Task<IResult> GetImage(HttpContext context, string imageName) {
string imagesDir = Path.Combine("Images"); string imagesDir = Path.Combine("Images");
@@ -2,462 +2,56 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using CannedNet.Data; using CannedNet.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using CannedNet.Services; using Microsoft.AspNetCore.Authorization;
using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class MatchmakingController [ApiController, Route("match")]
public class MatchmakingController : ControllerBase
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpPost("player/login")]
public async Task<IResult> PlayerLogin()
public void MapEndpoints(WebApplication app)
{ {
var jwtService = app.Services.GetRequiredService<JwtTokenService>(); return Results.Ok();
}
app.MapPost("/player/login", () => Results.Ok()); [HttpPost("player/exclusivelogin")]
public async Task<IResult> PlayerExclusiveLogin()
{
return Results.Json(new { errorCode = 0 });
}
app.MapPost("/player/exclusivelogin", (HttpRequest request) => [HttpGet("player")]
public async Task<IResult> GetPlayer(HttpRequest request, AppDbContext db)
{
var id = request.Query["id"].FirstOrDefault();
if (string.IsNullOrEmpty(id) || !int.TryParse(id, out var accountId))
{ {
return Results.Json(new { errorCode = 0 }); var json = System.IO.File.ReadAllText("JSON/getplayer.json");
});
app.MapGet("/player", async (HttpRequest request, AppDbContext db) =>
{
var id = request.Query["id"].FirstOrDefault();
if (string.IsNullOrEmpty(id) || !int.TryParse(id, out var accountId))
{
var json = File.ReadAllText("JSON/getplayer.json");
return Results.Content(json, "application/json"); return Results.Content(json, "application/json");
} }
var account = await db.Accounts.FindAsync(accountId); var account = await db.Accounts.FindAsync(accountId);
if (account == null) if (account == null)
{ {
var json = File.ReadAllText("JSON/getplayer.json"); var json = System.IO.File.ReadAllText("JSON/getplayer.json");
return Results.Content(json, "application/json"); return Results.Content(json, "application/json");
} }
// Get the player's active room instance var roomInstance = await db.RoomInstances
var roomInstance = await db.RoomInstances .Where(r => r.OwnerAccountId == accountId)
.Where(r => r.OwnerAccountId == accountId) .OrderByDescending(r => r.Id)
.OrderByDescending(r => r.Id) .FirstOrDefaultAsync();
.FirstOrDefaultAsync();
var result = new List<object> var result = new List<object>
{
new
{
playerId = account.AccountId,
statusVisibility = 0,
deviceClass = 0,
vrMovementMode = 1,
roomInstance = roomInstance != null ? new
{
roomInstanceId = roomInstance.Id > 0 ? roomInstance.Id : roomInstance.roomInstanceId,
roomId = roomInstance.roomId,
subRoomId = roomInstance.subRoomId,
roomInstanceType = roomInstance.roomInstanceType,
location = roomInstance.location,
dataBlob = roomInstance.dataBlob,
eventId = roomInstance.eventId,
clubId = roomInstance.clubId,
roomCode = roomInstance.roomCode,
photonRegionId = roomInstance.photonRegionId,
photonRoomId = roomInstance.photonRoomId,
name = roomInstance.name,
maxCapacity = roomInstance.maxCapacity,
isFull = roomInstance.isFull,
isPrivate = roomInstance.isPrivate,
isInProgress = roomInstance.isInProgress,
EncryptVoiceChat = roomInstance.EncryptVoiceChat
} : (object?)null,
isOnline = roomInstance != null,
appVersion = "",
platform = account.Platforms ?? 0
}
};
return Results.Json(result);
});
app.MapPost("/goto/room/{room}", async (HttpRequest request, string room, AppDbContext db) =>
{ {
var authHeader = request.Headers.Authorization.ToString(); new
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
var form = await request.ReadFormAsync();
int joinMode = 0;
if (int.TryParse(form["JoinMode"].ToString(), out var mode))
{ {
joinMode = mode; playerId = account.AccountId,
} statusVisibility = 0,
request.Body.Position = 0; deviceClass = 0,
vrMovementMode = 1,
Room? roomData = null;
var roomLower = room.ToLower();
if (roomLower == "dormroom")
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.IsDorm && r.CreatorAccountId == id);
}
else if (int.TryParse(room, out var roomId))
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
}
if (roomData == null)
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.Name.ToLower() == roomLower);
}
if (roomData == null)
{
return Results.NotFound("Room not found");
}
var subRoom = await db.SubRooms.FirstOrDefaultAsync(s => s.RoomId == roomData.RoomId);
string location = subRoom?.UnitySceneId ?? "";
int subRoomId = subRoom?.SubRoomId ?? 0;
int maxCapacity = subRoom?.MaxPlayers ?? 4;
RoomInstance? instanceToUse;
string photonRoomId;
bool isPrivateInstance = joinMode == 2 || roomData.IsDorm;
var existingInstance = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
if (existingInstance != null)
{
photonRoomId = existingInstance.photonRoomId ?? Guid.NewGuid().ToString();
existingInstance.roomInstanceId = existingInstance.Id;
existingInstance.roomId = roomData.RoomId;
existingInstance.subRoomId = subRoomId;
existingInstance.location = location;
existingInstance.dataBlob = "";
existingInstance.photonRegionId = "us";
existingInstance.photonRoomId = photonRoomId;
existingInstance.name = roomData.Name;
existingInstance.maxCapacity = maxCapacity;
existingInstance.isFull = false;
existingInstance.isPrivate = isPrivateInstance;
instanceToUse = existingInstance;
}
else
{
photonRoomId = Guid.NewGuid().ToString();
var oldInstances = await db.RoomInstances
.Where(r => r.OwnerAccountId == id && r.roomId != roomData.RoomId)
.ToListAsync();
db.RoomInstances.RemoveRange(oldInstances);
var publicInstance = await db.RoomInstances
.FirstOrDefaultAsync(r => r.roomId == roomData.RoomId && !r.isPrivate && !r.isFull);
if (publicInstance != null)
{
photonRoomId = publicInstance.photonRoomId ?? Guid.NewGuid().ToString();
}
instanceToUse = new RoomInstance
{
OwnerAccountId = id,
roomInstanceId = 1,
roomId = roomData.RoomId,
subRoomId = subRoomId,
roomInstanceType = 2,
location = location,
dataBlob = "",
photonRegionId = "us",
photonRoomId = photonRoomId,
name = roomData.Name,
maxCapacity = maxCapacity,
isFull = false,
isPrivate = isPrivateInstance,
isInProgress = false,
EncryptVoiceChat = roomData.EncryptVoiceChat
};
db.RoomInstances.Add(instanceToUse);
}
await db.SaveChangesAsync();
if (instanceToUse.Id == 0)
{
instanceToUse = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
}
var response = new
{
errorCode = 0,
roomInstance = new
{
roomInstanceId = instanceToUse?.Id ?? 1,
roomId = instanceToUse?.roomId ?? roomData.RoomId,
subRoomId = instanceToUse?.subRoomId ?? subRoomId,
roomInstanceType = instanceToUse?.roomInstanceType ?? 2,
location = instanceToUse?.location ?? location,
dataBlob = instanceToUse?.dataBlob ?? "",
eventId = instanceToUse?.eventId ?? 0,
clubId = instanceToUse?.clubId ?? 0,
roomCode = instanceToUse?.roomCode ?? "",
photonRegionId = instanceToUse?.photonRegionId ?? "us",
photonRoomId = instanceToUse?.photonRoomId ?? photonRoomId,
name = instanceToUse?.name ?? roomData.Name,
maxCapacity = instanceToUse?.maxCapacity ?? maxCapacity,
isFull = instanceToUse?.isFull ?? false,
isPrivate = isPrivateInstance,
isInProgress = instanceToUse?.isInProgress ?? false,
EncryptVoiceChat = instanceToUse?.EncryptVoiceChat ?? roomData.EncryptVoiceChat
}
};
return Results.Json(response);
});
app.MapPost("/matchmake/{room}", async (HttpRequest request, string room, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
var form = await request.ReadFormAsync();
int joinMode = 0;
if (int.TryParse(form["JoinMode"].ToString(), out var mode))
{
joinMode = mode;
}
request.Body.Position = 0;
Room? roomData = null;
var roomLower = room.ToLower();
if (roomLower == "dorm")
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.IsDorm && r.CreatorAccountId == id);
}
else if (int.TryParse(room, out var roomId))
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
}
if (roomData == null)
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.Name.ToLower() == roomLower);
}
if (roomData == null)
{
return Results.NotFound("Room not found");
}
var subRoom = await db.SubRooms.FirstOrDefaultAsync(s => s.RoomId == roomData.RoomId);
string location = subRoom?.UnitySceneId ?? "";
int subRoomId = subRoom?.SubRoomId ?? 0;
int maxCapacity = subRoom?.MaxPlayers ?? 4;
RoomInstance? instanceToUse;
string photonRoomId;
bool isPrivateInstance = joinMode == 2 || roomData.IsDorm;
var existingInstance = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
if (existingInstance != null)
{
photonRoomId = existingInstance.photonRoomId ?? Guid.NewGuid().ToString();
existingInstance.roomInstanceId = existingInstance.Id;
existingInstance.roomId = roomData.RoomId;
existingInstance.subRoomId = subRoomId;
existingInstance.location = location;
existingInstance.dataBlob = "";
existingInstance.photonRegionId = "us";
existingInstance.photonRoomId = photonRoomId;
existingInstance.name = roomData.Name;
existingInstance.maxCapacity = maxCapacity;
existingInstance.isFull = false;
existingInstance.isPrivate = isPrivateInstance;
instanceToUse = existingInstance;
}
else
{
photonRoomId = Guid.NewGuid().ToString();
var oldInstances = await db.RoomInstances
.Where(r => r.OwnerAccountId == id && r.roomId != roomData.RoomId)
.ToListAsync();
db.RoomInstances.RemoveRange(oldInstances);
var publicInstance = await db.RoomInstances
.FirstOrDefaultAsync(r => r.roomId == roomData.RoomId && !r.isPrivate && !r.isFull);
if (publicInstance != null)
{
photonRoomId = publicInstance.photonRoomId ?? Guid.NewGuid().ToString();
}
instanceToUse = new RoomInstance
{
OwnerAccountId = id,
roomInstanceId = 1,
roomId = roomData.RoomId,
subRoomId = subRoomId,
roomInstanceType = 2,
location = location,
dataBlob = "",
photonRegionId = "us",
photonRoomId = photonRoomId,
name = roomData.Name,
maxCapacity = maxCapacity,
isFull = false,
isPrivate = isPrivateInstance,
isInProgress = false,
EncryptVoiceChat = roomData.EncryptVoiceChat
};
db.RoomInstances.Add(instanceToUse);
}
await db.SaveChangesAsync();
if (instanceToUse.Id == 0)
{
instanceToUse = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
}
var response = new
{
errorCode = 0,
roomInstance = new
{
roomInstanceId = instanceToUse?.Id ?? 1,
roomId = instanceToUse?.roomId ?? roomData.RoomId,
subRoomId = instanceToUse?.subRoomId ?? subRoomId,
roomInstanceType = instanceToUse?.roomInstanceType ?? 2,
location = instanceToUse?.location ?? location,
dataBlob = instanceToUse?.dataBlob ?? "",
eventId = instanceToUse?.eventId ?? 0,
clubId = instanceToUse?.clubId ?? 0,
roomCode = instanceToUse?.roomCode ?? "",
photonRegionId = instanceToUse?.photonRegionId ?? "us",
photonRoomId = instanceToUse?.photonRoomId ?? photonRoomId,
name = instanceToUse?.name ?? roomData.Name,
maxCapacity = instanceToUse?.maxCapacity ?? maxCapacity,
isFull = instanceToUse?.isFull ?? false,
isPrivate = isPrivateInstance,
isInProgress = instanceToUse?.isInProgress ?? false,
EncryptVoiceChat = instanceToUse?.EncryptVoiceChat ?? roomData.EncryptVoiceChat
}
};
return Results.Json(response);
});
app.MapPost("/matchmake/none", (HttpRequest request) =>
{
// dormroom location id : 76d98498-60a1-430c-ab76-b54a29b7a163
// offline dorm
return Results.Json(new
{
errorCode = 0,
roomInstance = new
{
roomInstanceId = 1,
roomId = 1,
subRoomId = 1,
roomInstanceType = 2,
location = "76d98498-60a1-430c-ab76-b54a29b7a163",
dataBlob = "",
eventId = 0,
clubId = 0,
photonRegionId = "us",
photonRoomId = Guid.NewGuid(),
name = "DormRoom",
maxCapacity = 4,
isFull = false,
isPrivate = true,
isInProgress = false,
EncryptVoiceChat = false
}
});
});
app.MapPost("/player/heartbeat", async (HttpRequest request, AppDbContext db) =>
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body);
var body = await reader.ReadToEndAsync();
request.Body.Position = 0;
var form = await request.ReadFormAsync();
var loginLock = form["LoginLock"].ToString();
HeartbeatRequest? heartbeat = null;
if (!string.IsNullOrWhiteSpace(body) && body.TrimStart().StartsWith("{"))
{
heartbeat = JsonSerializer.Deserialize<HeartbeatRequest>(body);
}
if (heartbeat == null)
heartbeat = new HeartbeatRequest();
// Get the player's active room instance (most recent one)
var roomInstance = await db.RoomInstances
.Where(r => r.OwnerAccountId == id)
.OrderByDescending(r => r.Id)
.FirstOrDefaultAsync();
return Results.Json(new
{
playerId = heartbeat.playerId != 0 ? heartbeat.playerId : id,
statusVisibility = heartbeat.statusVisibility,
deviceClass = heartbeat.deviceClass,
vrMovementMode = heartbeat.vrMovementMode != 0 ? heartbeat.vrMovementMode : 1,
roomInstance = roomInstance != null ? new roomInstance = roomInstance != null ? new
{ {
roomInstanceId = roomInstance.Id > 0 ? roomInstance.Id : roomInstance.roomInstanceId, roomInstanceId = roomInstance.Id > 0 ? roomInstance.Id : roomInstance.roomInstanceId,
@@ -477,22 +71,397 @@ public class MatchmakingController
isPrivate = roomInstance.isPrivate, isPrivate = roomInstance.isPrivate,
isInProgress = roomInstance.isInProgress, isInProgress = roomInstance.isInProgress,
EncryptVoiceChat = roomInstance.EncryptVoiceChat EncryptVoiceChat = roomInstance.EncryptVoiceChat
} : null, } : (object?)null,
isOnline = roomInstance != null, isOnline = roomInstance != null,
appVersion = heartbeat.appVersion ?? "", appVersion = "",
platform = heartbeat.platform platform = account.Platforms ?? 0
}); }
}); };
app.MapPut("/player/statusvisibility", async (HttpRequest request, AppDbContext db) =>
return Results.Json(result);
}
[HttpPost("goto/room/{room}")]
[Authorize]
public async Task<IResult> GotoRoom(HttpRequest request, string room, AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
var form = await request.ReadFormAsync();
int joinMode = 0;
if (int.TryParse(form["JoinMode"].ToString(), out var mode))
{ {
// TODO ADD FUNCTIONALITY joinMode = mode;
return Results.Ok(); }
}); request.Body.Position = 0;
app.MapPost("/roominstance/{id}/reportjoinresult", async (HttpRequest request, AppDbContext db) =>
Room? roomData = null;
var roomLower = room.ToLower();
if (roomLower == "dormroom")
{ {
return Results.Ok(); roomData = await db.Rooms.FirstOrDefaultAsync(r => r.IsDorm && r.CreatorAccountId == id);
}
else if (int.TryParse(room, out var roomId))
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
}
if (roomData == null)
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.Name.ToLower() == roomLower);
}
if (roomData == null)
{
return Results.NotFound("Room not found");
}
var subRoom = await db.SubRooms.FirstOrDefaultAsync(s => s.RoomId == roomData.RoomId);
string location = subRoom?.UnitySceneId ?? "";
int subRoomId = subRoom?.SubRoomId ?? 0;
int maxCapacity = subRoom?.MaxPlayers ?? 4;
RoomInstance? instanceToUse;
string photonRoomId;
bool isPrivateInstance = joinMode == 2 || roomData.IsDorm;
var existingInstance = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
if (existingInstance != null)
{
photonRoomId = existingInstance.photonRoomId ?? Guid.NewGuid().ToString();
existingInstance.roomInstanceId = existingInstance.Id;
existingInstance.roomId = roomData.RoomId;
existingInstance.subRoomId = subRoomId;
existingInstance.location = location;
existingInstance.dataBlob = "";
existingInstance.photonRegionId = "us";
existingInstance.photonRoomId = photonRoomId;
existingInstance.name = roomData.Name;
existingInstance.maxCapacity = maxCapacity;
existingInstance.isFull = false;
existingInstance.isPrivate = isPrivateInstance;
instanceToUse = existingInstance;
}
else
{
photonRoomId = Guid.NewGuid().ToString();
var oldInstances = await db.RoomInstances
.Where(r => r.OwnerAccountId == id && r.roomId != roomData.RoomId)
.ToListAsync();
db.RoomInstances.RemoveRange(oldInstances);
var publicInstance = await db.RoomInstances
.FirstOrDefaultAsync(r => r.roomId == roomData.RoomId && !r.isPrivate && !r.isFull);
if (publicInstance != null)
{
photonRoomId = publicInstance.photonRoomId ?? Guid.NewGuid().ToString();
}
instanceToUse = new RoomInstance
{
OwnerAccountId = id,
roomInstanceId = 1,
roomId = roomData.RoomId,
subRoomId = subRoomId,
roomInstanceType = 2,
location = location,
dataBlob = "",
photonRegionId = "us",
photonRoomId = photonRoomId,
name = roomData.Name,
maxCapacity = maxCapacity,
isFull = false,
isPrivate = isPrivateInstance,
isInProgress = false,
EncryptVoiceChat = roomData.EncryptVoiceChat
};
db.RoomInstances.Add(instanceToUse);
}
await db.SaveChangesAsync();
if (instanceToUse.Id == 0)
{
instanceToUse = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
}
var response = new
{
errorCode = 0,
roomInstance = new
{
roomInstanceId = instanceToUse?.Id ?? 1,
roomId = instanceToUse?.roomId ?? roomData.RoomId,
subRoomId = instanceToUse?.subRoomId ?? subRoomId,
roomInstanceType = instanceToUse?.roomInstanceType ?? 2,
location = instanceToUse?.location ?? location,
dataBlob = instanceToUse?.dataBlob ?? "",
eventId = instanceToUse?.eventId ?? 0,
clubId = instanceToUse?.clubId ?? 0,
roomCode = instanceToUse?.roomCode ?? "",
photonRegionId = instanceToUse?.photonRegionId ?? "us",
photonRoomId = instanceToUse?.photonRoomId ?? photonRoomId,
name = instanceToUse?.name ?? roomData.Name,
maxCapacity = instanceToUse?.maxCapacity ?? maxCapacity,
isFull = instanceToUse?.isFull ?? false,
isPrivate = isPrivateInstance,
isInProgress = instanceToUse?.isInProgress ?? false,
EncryptVoiceChat = instanceToUse?.EncryptVoiceChat ?? roomData.EncryptVoiceChat
}
};
return Results.Json(response);
}
[HttpPost("matchmake/{room}")]
[Authorize]
public async Task<IResult> MatchmakeRoom(HttpRequest request, string room, AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
var form = await request.ReadFormAsync();
int joinMode = 0;
if (int.TryParse(form["JoinMode"].ToString(), out var mode))
{
joinMode = mode;
}
request.Body.Position = 0;
Room? roomData = null;
var roomLower = room.ToLower();
if (roomLower == "dorm")
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.IsDorm && r.CreatorAccountId == id);
}
else if (int.TryParse(room, out var roomId))
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
}
if (roomData == null)
{
roomData = await db.Rooms.FirstOrDefaultAsync(r => r.Name.ToLower() == roomLower);
}
if (roomData == null)
{
return Results.NotFound("Room not found");
}
var subRoom = await db.SubRooms.FirstOrDefaultAsync(s => s.RoomId == roomData.RoomId);
string location = subRoom?.UnitySceneId ?? "";
int subRoomId = subRoom?.SubRoomId ?? 0;
int maxCapacity = subRoom?.MaxPlayers ?? 4;
RoomInstance? instanceToUse;
string photonRoomId;
bool isPrivateInstance = joinMode == 2 || roomData.IsDorm;
var existingInstance = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
if (existingInstance != null)
{
photonRoomId = existingInstance.photonRoomId ?? Guid.NewGuid().ToString();
existingInstance.roomInstanceId = existingInstance.Id;
existingInstance.roomId = roomData.RoomId;
existingInstance.subRoomId = subRoomId;
existingInstance.location = location;
existingInstance.dataBlob = "";
existingInstance.photonRegionId = "us";
existingInstance.photonRoomId = photonRoomId;
existingInstance.name = roomData.Name;
existingInstance.maxCapacity = maxCapacity;
existingInstance.isFull = false;
existingInstance.isPrivate = isPrivateInstance;
instanceToUse = existingInstance;
}
else
{
photonRoomId = Guid.NewGuid().ToString();
var oldInstances = await db.RoomInstances
.Where(r => r.OwnerAccountId == id && r.roomId != roomData.RoomId)
.ToListAsync();
db.RoomInstances.RemoveRange(oldInstances);
var publicInstance = await db.RoomInstances
.FirstOrDefaultAsync(r => r.roomId == roomData.RoomId && !r.isPrivate && !r.isFull);
if (publicInstance != null)
{
photonRoomId = publicInstance.photonRoomId ?? Guid.NewGuid().ToString();
}
instanceToUse = new RoomInstance
{
OwnerAccountId = id,
roomInstanceId = 1,
roomId = roomData.RoomId,
subRoomId = subRoomId,
roomInstanceType = 2,
location = location,
dataBlob = "",
photonRegionId = "us",
photonRoomId = photonRoomId,
name = roomData.Name,
maxCapacity = maxCapacity,
isFull = false,
isPrivate = isPrivateInstance,
isInProgress = false,
EncryptVoiceChat = roomData.EncryptVoiceChat
};
db.RoomInstances.Add(instanceToUse);
}
await db.SaveChangesAsync();
if (instanceToUse.Id == 0)
{
instanceToUse = await db.RoomInstances.FirstOrDefaultAsync(r => r.OwnerAccountId == id && r.roomId == roomData.RoomId);
}
var response = new
{
errorCode = 0,
roomInstance = new
{
roomInstanceId = instanceToUse?.Id ?? 1,
roomId = instanceToUse?.roomId ?? roomData.RoomId,
subRoomId = instanceToUse?.subRoomId ?? subRoomId,
roomInstanceType = instanceToUse?.roomInstanceType ?? 2,
location = instanceToUse?.location ?? location,
dataBlob = instanceToUse?.dataBlob ?? "",
eventId = instanceToUse?.eventId ?? 0,
clubId = instanceToUse?.clubId ?? 0,
roomCode = instanceToUse?.roomCode ?? "",
photonRegionId = instanceToUse?.photonRegionId ?? "us",
photonRoomId = instanceToUse?.photonRoomId ?? photonRoomId,
name = instanceToUse?.name ?? roomData.Name,
maxCapacity = instanceToUse?.maxCapacity ?? maxCapacity,
isFull = instanceToUse?.isFull ?? false,
isPrivate = isPrivateInstance,
isInProgress = instanceToUse?.isInProgress ?? false,
EncryptVoiceChat = instanceToUse?.EncryptVoiceChat ?? roomData.EncryptVoiceChat
}
};
return Results.Json(response);
}
[HttpPost("matchmake/none")]
public async Task<IResult> MatchmakeNone()
{
return Results.Json(new
{
errorCode = 0,
roomInstance = new
{
roomInstanceId = 1,
roomId = 1,
subRoomId = 1,
roomInstanceType = 2,
location = "76d98498-60a1-430c-ab76-b54a29b7a163",
dataBlob = "",
eventId = 0,
clubId = 0,
photonRegionId = "us",
photonRoomId = Guid.NewGuid(),
name = "DormRoom",
maxCapacity = 4,
isFull = false,
isPrivate = true,
isInProgress = false,
EncryptVoiceChat = false
}
}); });
} }
[HttpPost("player/heartbeat")]
[Authorize]
public async Task<IResult> PlayerHeartbeat(HttpRequest request, AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body);
var body = await reader.ReadToEndAsync();
request.Body.Position = 0;
var form = await request.ReadFormAsync();
var loginLock = form["LoginLock"].ToString();
HeartbeatRequest? heartbeat = null;
if (!string.IsNullOrWhiteSpace(body) && body.TrimStart().StartsWith("{"))
{
heartbeat = JsonSerializer.Deserialize<HeartbeatRequest>(body);
}
if (heartbeat == null)
heartbeat = new HeartbeatRequest();
var roomInstance = await db.RoomInstances
.Where(r => r.OwnerAccountId == id)
.OrderByDescending(r => r.Id)
.FirstOrDefaultAsync();
return Results.Json(new
{
playerId = heartbeat.playerId != 0 ? heartbeat.playerId : id,
statusVisibility = heartbeat.statusVisibility,
deviceClass = heartbeat.deviceClass,
vrMovementMode = heartbeat.vrMovementMode != 0 ? heartbeat.vrMovementMode : 1,
roomInstance = roomInstance != null ? new
{
roomInstanceId = roomInstance.Id > 0 ? roomInstance.Id : roomInstance.roomInstanceId,
roomId = roomInstance.roomId,
subRoomId = roomInstance.subRoomId,
roomInstanceType = roomInstance.roomInstanceType,
location = roomInstance.location,
dataBlob = roomInstance.dataBlob,
eventId = roomInstance.eventId,
clubId = roomInstance.clubId,
roomCode = roomInstance.roomCode,
photonRegionId = roomInstance.photonRegionId,
photonRoomId = roomInstance.photonRoomId,
name = roomInstance.name,
maxCapacity = roomInstance.maxCapacity,
isFull = roomInstance.isFull,
isPrivate = roomInstance.isPrivate,
isInProgress = roomInstance.isInProgress,
EncryptVoiceChat = roomInstance.EncryptVoiceChat
} : null,
isOnline = roomInstance != null,
appVersion = heartbeat.appVersion ?? "",
platform = heartbeat.platform
});
}
[HttpPut("player/statusvisibility")]
public async Task<IResult> PlayerStatusVisibility()
{
return Results.Ok();
}
[HttpPost("roominstance/{id}/reportjoinresult")]
public async Task<IResult> ReportJoinResult()
{
return Results.Ok();
}
} }
public class HeartbeatRequest public class HeartbeatRequest
@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
//TODO: proper NameServer configs //TODO: proper NameServer configs
[ApiController, Route("")] [ApiController, Route("ns")]
public class NSController : ControllerBase public class NSController : ControllerBase
{ {
[HttpGet] [HttpGet]
@@ -1,147 +1,122 @@
using CannedNet.Data; using CannedNet.Data;
using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class PlayerSettingsController [ApiController, Route("playersettings")]
public class PlayerSettingsController : ControllerBase
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpGet("playersettings")]
[Authorize]
public void MapEndpoints(WebApplication app) public async Task<IResult> GetPlayerSettings(AppDbContext db)
{ {
var jwtService = app.Services.GetRequiredService<JwtTokenService>(); if (!int.TryParse(User.Identity?.Name, out var id))
var notificationService = app.Services.GetRequiredService<NotificationService>(); return Results.Unauthorized();
app.MapGet("/playersettings" + var settings = await db.PlayerSettings
"", async (HttpRequest request, AppDbContext db) => .Where(s => s.PlayerId == id)
.ToListAsync();
if (!settings.Any())
{ {
var authHeader = request.Headers.Authorization.ToString(); var defaults = new List<PlayerSetting>
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var settings = await db.PlayerSettings
.Where(s => s.PlayerId == id)
.ToListAsync();
if (!settings.Any())
{ {
var defaults = new List<PlayerSetting> new() { PlayerId = id, Key = "Recroom.OOBE", Value = "77" },
{ new() { PlayerId = id, Key = "SplitTestAssignedSegments", Value = "1|{\"SplitTesting+PhotonMaxDatagrams_2021_01_11\":\"Off\",\"SplitTesting+Curated_Rooms_2020_08_06\":\"Off\",\"SplitTesting+RoomRecommendationsType_2020_08_14\":\"Aug14MinVisitors35000\"}" },
new() { PlayerId = id, Key = "Recroom.OOBE", Value = "77" }, new() { PlayerId = id, Key = "PlayerSessionCount", Value = "13" },
new() { PlayerId = id, Key = "SplitTestAssignedSegments", Value = "1|{\"SplitTesting+PhotonMaxDatagrams_2021_01_11\":\"Off\",\"SplitTesting+Curated_Rooms_2020_08_06\":\"Off\",\"SplitTesting+RoomRecommendationsType_2020_08_14\":\"Aug14MinVisitors35000\"}" }, new() { PlayerId = id, Key = "TUTORIAL_COMPLETE_MASK", Value = "11" },
new() { PlayerId = id, Key = "PlayerSessionCount", Value = "13" }, new() { PlayerId = id, Key = "BACKPACK_FAVORITE_TOOL", Value = "1" },
new() { PlayerId = id, Key = "TUTORIAL_COMPLETE_MASK", Value = "11" }, new() { PlayerId = id, Key = "VoiceChat", Value = "2" },
new() { PlayerId = id, Key = "BACKPACK_FAVORITE_TOOL", Value = "1" }, new() { PlayerId = id, Key = "VRAUTOSPRINT", Value = "1" },
new() { PlayerId = id, Key = "VoiceChat", Value = "2" }, new() { PlayerId = id, Key = "VR_MOVEMENT_MODE", Value = "0" },
new() { PlayerId = id, Key = "VRAUTOSPRINT", Value = "1" }, new() { PlayerId = id, Key = "COMFORT_SPRINT", Value = "0" },
new() { PlayerId = id, Key = "VR_MOVEMENT_MODE", Value = "0" }, new() { PlayerId = id, Key = "COMFORT_WALK", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_SPRINT", Value = "0" }, new() { PlayerId = id, Key = "COMFORT_VEHICLES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_WALK", Value = "0" }, new() { PlayerId = id, Key = "COMFORT_FLY", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_VEHICLES", Value = "0" }, new() { PlayerId = id, Key = "COMFORT_ROTATE", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FLY", Value = "0" }, new() { PlayerId = id, Key = "COMFORT_FORCES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_ROTATE", Value = "0" }, new() { PlayerId = id, Key = "COMFORT_FALL", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FORCES", Value = "0" }, new() { PlayerId = id, Key = "COMFORT_TELEPORT", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FALL", Value = "0" }, new() { PlayerId = id, Key = "ROTATE_IN_PLACE_ENABLED", Value = "1" },
new() { PlayerId = id, Key = "COMFORT_TELEPORT", Value = "0" }, new() { PlayerId = id, Key = "ROTATION_INCREMENT", Value = "2" },
new() { PlayerId = id, Key = "ROTATE_IN_PLACE_ENABLED", Value = "1" }, new() { PlayerId = id, Key = "CONTINUOUS_ROTATION_MODE", Value = "1" },
new() { PlayerId = id, Key = "ROTATION_INCREMENT", Value = "2" }, new() { PlayerId = id, Key = "DONT_LOCK_TOOLS_TO_HAND", Value = "0" },
new() { PlayerId = id, Key = "CONTINUOUS_ROTATION_MODE", Value = "1" }, new() { PlayerId = id, Key = "QualitySettings", Value = "2" },
new() { PlayerId = id, Key = "DONT_LOCK_TOOLS_TO_HAND", Value = "0" }, new() { PlayerId = id, Key = "TeleportBuffer", Value = "0" },
new() { PlayerId = id, Key = "QualitySettings", Value = "2" }, new() { PlayerId = id, Key = "IgnoreBuffer", Value = "1" },
new() { PlayerId = id, Key = "TeleportBuffer", Value = "0" }, new() { PlayerId = id, Key = "FIRST_TIME_IN_FLAGS", Value = "0" },
new() { PlayerId = id, Key = "IgnoreBuffer", Value = "1" }, new() { PlayerId = id, Key = "ShowRoomCenter", Value = "1" },
new() { PlayerId = id, Key = "FIRST_TIME_IN_FLAGS", Value = "0" }, new() { PlayerId = id, Key = "USER_TRACKING", Value = "1" },
new() { PlayerId = id, Key = "ShowRoomCenter", Value = "1" }, new() { PlayerId = id, Key = "STABILIZE_HANDS", Value = "0" },
new() { PlayerId = id, Key = "USER_TRACKING", Value = "1" }, new() { PlayerId = id, Key = "MakerPen_SnappingMode", Value = "2" },
new() { PlayerId = id, Key = "STABILIZE_HANDS", Value = "0" }, new() { PlayerId = id, Key = "Recroom.ChallengeMap", Value = "17" },
new() { PlayerId = id, Key = "MakerPen_SnappingMode", Value = "2" }, new() { PlayerId = id, Key = "VoiceFilter2", Value = "1" },
new() { PlayerId = id, Key = "Recroom.ChallengeMap", Value = "17" }, new() { PlayerId = id, Key = "SFX_VOLUME_PERCENT_PREF", Value = "1" },
new() { PlayerId = id, Key = "VoiceFilter2", Value = "1" }, };
new() { PlayerId = id, Key = "SFX_VOLUME_PERCENT_PREF", Value = "1" }, db.PlayerSettings.AddRange(defaults);
}; await db.SaveChangesAsync();
db.PlayerSettings.AddRange(defaults); settings = defaults;
await db.SaveChangesAsync(); }
settings = defaults;
}
return Results.Json(settings); return Results.Json(settings);
}); }
app.MapPut("/playersettings", async (HttpRequest request, AppDbContext db) => [HttpPut("playersettings")]
[Authorize]
public async Task<IResult> PutPlayerSettings(HttpRequest request, AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var settings = new List<PlayerSetting>();
if (request.ContentType?.Contains("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) == true)
{ {
var authHeader = request.Headers.Authorization.ToString(); var form = await request.ReadFormAsync();
var key = form["key"].FirstOrDefault();
var value = form["value"].FirstOrDefault();
if (!string.IsNullOrEmpty(key))
settings.Add(new PlayerSetting { Key = key, Value = value ?? "" });
}
else
{
request.EnableBuffering();
request.Body.Position = 0;
using var reader = new StreamReader(request.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync();
request.Body.Position = 0;
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) Console.WriteLine($"Settings request body: {body}");
return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length); if (body.TrimStart().StartsWith("["))
var accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized();
var settings = new List<PlayerSetting>();
if (request.ContentType?.Contains("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) == true)
{ {
var form = await request.ReadFormAsync(); settings = System.Text.Json.JsonSerializer.Deserialize<List<PlayerSetting>>(body) ?? [];
var key = form["key"].FirstOrDefault();
var value = form["value"].FirstOrDefault();
if (!string.IsNullOrEmpty(key))
settings.Add(new PlayerSetting { Key = key, Value = value ?? "" });
} }
else else
{ {
request.EnableBuffering(); var single = System.Text.Json.JsonSerializer.Deserialize<PlayerSetting>(body);
request.Body.Position = 0; if (single != null) settings.Add(single);
using var reader = new StreamReader(request.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync();
request.Body.Position = 0;
Console.WriteLine($"Settings request body: {body}");
if (body.TrimStart().StartsWith("["))
{
settings = System.Text.Json.JsonSerializer.Deserialize<List<PlayerSetting>>(body) ?? [];
}
else
{
var single = System.Text.Json.JsonSerializer.Deserialize<PlayerSetting>(body);
if (single != null) settings.Add(single);
}
} }
}
settings = settings.Where(s => !string.IsNullOrEmpty(s.Key)).ToList(); settings = settings.Where(s => !string.IsNullOrEmpty(s.Key)).ToList();
if (!settings.Any()) if (!settings.Any())
return Results.Ok();
db.PlayerSettings.RemoveRange(db.PlayerSettings.Where(s => s.PlayerId == id));
foreach (var setting in settings)
{
setting.PlayerId = id;
setting.Key = setting.Key ?? "";
setting.Value = setting.Value ?? "";
db.PlayerSettings.Add(setting);
}
await db.SaveChangesAsync();
return Results.Ok(); return Results.Ok();
});
db.PlayerSettings.RemoveRange(db.PlayerSettings.Where(s => s.PlayerId == id));
foreach (var setting in settings)
{
setting.PlayerId = id;
setting.Key = setting.Key ?? "";
setting.Value = setting.Value ?? "";
db.PlayerSettings.Add(setting);
}
await db.SaveChangesAsync();
return Results.Ok();
} }
} }
@@ -1,57 +1,55 @@
using CannedNet.Data; using CannedNet.Data;
using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class RoomsController [ApiController, Route("rooms")]
public class RoomsController : ControllerBase
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpGet("rooms")]
public async Task<IResult> GetRooms(HttpRequest request, AppDbContext db)
public void MapEndpoints(WebApplication app)
{ {
app.MapGet("/rooms", async (HttpRequest request, AppDbContext db) => var idParam = request.Query["id"].FirstOrDefault();
var nameParam = request.Query["name"].FirstOrDefault();
if (string.IsNullOrEmpty(idParam) && string.IsNullOrEmpty(nameParam))
return Results.BadRequest("Either 'id' or 'name' query parameter is required");
Room? result = null;
if (!string.IsNullOrEmpty(idParam))
{ {
var idParam = request.Query["id"].FirstOrDefault(); var ids = idParam.Split(',').Select(s => int.TryParse(s.Trim(), out var i) ? i : -1).Where(i => i != -1).ToList();
var nameParam = request.Query["name"].FirstOrDefault(); result = db.Rooms.FirstOrDefault(r => ids.Contains(r.RoomId));
}
if (string.IsNullOrEmpty(idParam) && string.IsNullOrEmpty(nameParam)) else if (!string.IsNullOrEmpty(nameParam))
return Results.BadRequest("Either 'id' or 'name' query parameter is required");
Room? result = null;
if (!string.IsNullOrEmpty(idParam))
{
var ids = idParam.Split(',').Select(s => int.TryParse(s.Trim(), out var i) ? i : -1).Where(i => i != -1).ToList();
result = db.Rooms.FirstOrDefault(r => ids.Contains(r.RoomId));
}
else if (!string.IsNullOrEmpty(nameParam))
{
var name = nameParam.Trim().ToLower();
result = db.Rooms.FirstOrDefault(r => r.Name.ToLower() == name);
}
if (result == null)
return Results.Json(new { });
return Results.Json(await BuildRoomResponse(result, db));
});
app.MapGet("/rooms/{roomId:int}", async (int roomId, AppDbContext db) =>
{ {
var result = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId); var name = nameParam.Trim().ToLower();
if (result == null) result = db.Rooms.FirstOrDefault(r => r.Name.ToLower() == name);
return Results.NotFound(); }
return Results.Json(await BuildRoomResponse(result, db)); if (result == null)
}); return Results.Json(new { });
app.MapGet("/roomserver/rooms/createdby/me", async (HttpRequest request, AppDbContext db) => return Results.Json(await BuildRoomResponse(result, db));
{ }
// TODO ADD FUNCTIONALITY
var json = File.ReadAllText("JSON/ownedrooms.json"); [HttpGet("rooms/{roomId:int}")]
return Results.Content(json, "application/json"); public async Task<IResult> GetRoomById(int roomId, AppDbContext db)
}); {
var result = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
if (result == null)
return Results.NotFound();
return Results.Json(await BuildRoomResponse(result, db));
}
[HttpGet("roomserver/rooms/createdby/me")]
public async Task<IResult> GetRoomsCreatedByMe()
{
var json = System.IO.File.ReadAllText("JSON/ownedrooms.json");
return Results.Content(json, "application/json");
} }
private static async Task<object> BuildRoomResponse(Room result, AppDbContext db) private static async Task<object> BuildRoomResponse(Room result, AppDbContext db)
@@ -1,33 +0,0 @@
using CannedNet.Services.Controllers;
namespace CannedNet.Services.Infrastructure;
public class ServiceRegistry
{
public required string Name { get; init; }
public required int Port { get; init; }
public required Type ServiceType { get; init; }
public required Action<WebApplicationBuilder> ConfigureBuilder { get; init; }
public required Action<WebApplication, JwtTokenService> MapEndpoints { get; init; }
}
public static class Services
{
public static readonly List<ServiceRegistry> All =
[
//new() { Name = "NS", Port = 5001, ServiceType = typeof(NSController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new NSController().MapEndpoints(app) },
new() { Name = "API", Port = 5000, ServiceType = typeof(APIController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new APIController().MapEndpoints(app) },
//new() { Name = "Auth", Port = 5002, ServiceType = typeof(AuthController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, jwt) => new AuthController().MapEndpoints(app) },
//new() { Name = "Chat", Port = 5003, ServiceType = typeof(ChatController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ChatController().MapEndpoints(app) },
new() { Name = "Match", Port = 5004, ServiceType = typeof(MatchmakingController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new MatchmakingController().MapEndpoints(app) },
//new() { Name = "Accounts", Port = 5005, ServiceType = typeof(AccountsController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, jwt) => new AccountsController().MapEndpoints(app, jwt) },
//new() { Name = "Notify", Port = 5006, ServiceType = typeof(NotifyController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new NotifyController().MapEndpoints(app) },
new() { Name = "CDN", Port = 5007, ServiceType = typeof(CDNController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new CDNController().MapEndpoints(app) },
//new() { Name = "Image", Port = 5008, ServiceType = typeof(ImageController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ImageController().MapEndpoints(app) },
new() { Name = "Clubs", Port = 5009, ServiceType = typeof(ClubsController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ClubsController().MapEndpoints(app) },
new() { Name = "Commerce", Port = 5010, ServiceType = typeof(CommerceController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new CommerceController().MapEndpoints(app) },
new() { Name = "Econ", Port = 5011, ServiceType = typeof(EconController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new EconController().MapEndpoints(app) },
new() { Name = "PlayerSettings", Port = 5012, ServiceType = typeof(PlayerSettingsController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new PlayerSettingsController().MapEndpoints(app) },
new() { Name = "RoomsController", Port = 5013, ServiceType = typeof(RoomsController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new RoomsController().MapEndpoints(app) }
];
}
+6 -1
View File
@@ -2,6 +2,7 @@
using System.Security.Claims; using System.Security.Claims;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using CannedNet.Services;
namespace CannedNet; namespace CannedNet;
@@ -14,7 +15,11 @@ public class JwtTokenService
public JwtTokenService() public JwtTokenService()
{ {
_securityKey = new RsaSecurityKey(_rsa) { KeyId = KeyId }; var rsa = Signatures.GetRsaInstance()
?? throw new InvalidOperationException("jwt - sigs service was not found");
_securityKey = new RsaSecurityKey(rsa) { KeyId = KeyId };
_signingCredentials = new SigningCredentials(_securityKey, SecurityAlgorithms.RsaSha256); _signingCredentials = new SigningCredentials(_securityKey, SecurityAlgorithms.RsaSha256);
} }
+6
View File
@@ -37,6 +37,12 @@ public static class Signatures
} }
} }
public static RSA? GetRsaInstance()
{
if (!_initialized) Init();
return _rsa;
}
public static string? Sign(byte[] data) public static string? Sign(byte[] data)
{ {
if (_rsa == null) return null; if (_rsa == null) return null;