Files
Campus-25/server/Server/2025-RecRoom-Server-E12354-main/Controllers/EconController.cs
T
2026-10-14 06:59:51 +02:00

191 lines
7.9 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Campus_live_rewrite.api.server;
using Campus_live_rewrite.api.server.Classes;
using Campus_live_rewrite.api.dbs;
namespace Campus_live_rewrite.Controllers
{
[ApiController]
public class EconController : ControllerBase
{
[HttpGet("/econ/customAvatarItems/v1/owned")]
public IActionResult OwnedCustomAvatarItems([FromQuery] int skip = 0, [FromQuery] int take = 100)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
// PAGINATED. The client downloads its locker in pages of 100 (skip/take) and keeps asking
// until it has TotalResults. Ignoring skip/take handed EVERY page the whole 3k-item dev
// locker (7.5 MB per request) - the headset timed out, retried a few times and gave up
// with "Failed to download unlocked avatar items", so the imported items never showed.
if (take > 1000) take = 1000;
if (take <= 0) take = 100;
if (skip < 0) skip = 0;
// The caller's OWN custom items (the ones they created), from the in-memory UGC index.
// Was streaming the ENTIRE catalog file - fine when empty, an 87MB response now. Items
// a player equips but didn't create still resolve via /api/customAvatarItems/v1/bulk.
//
// DEV LOCKER: a dev owns every IMPORTED UGC item (the ones whose assets we host, so they
// actually render) on top of their own creations - so imported clothing shows up in the
// wardrobe and equips like a normal item. Non-devs still only own what they created.
// Compact records; only the requested page is turned into JSON objects.
var mine = CustomItemsCache.ByCreatorRecs(account.Id);
// Bought / gifted UGC items.
{
var seenB = new HashSet<string>(mine.Select(x => x.Id), StringComparer.OrdinalIgnoreCase);
foreach (var caid in PlayerDB.OwnedCustomItems(account.Id))
{
var r = CustomItemsCache.GetRec(caid);
if (r != null && seenB.Add(r.Id)) mine.Add(r);
}
}
// The dev locker used to add every IMPORTED first-party item here as well. Off by
// default now: those records render only through a downloaded asset bundle, and the
// 20250718 PC client fails that download for every bundle we serve (the wardrobe
// tile shows, equipping dies in a NullReferenceException). The reference server has
// no such locker - its wardrobe is the LEGACY catalog (/api/avatar/v4/items), which the
// PC client renders from its own built-in assets, and a dev already owns all of that.
// So a dev's "Winter Scarf" that works is the legacy one; the imported twin only ever
// produced the broken duplicate tile. ConfigCampus.DevLockerImportedUgc turns it back on.
if (ConfigCampus.DevLockerImportedUgc && PlayerDB.CheckDevFlag(account.Id))
{
var seen = new HashSet<string>(mine.Select(x => x.Id), StringComparer.OrdinalIgnoreCase);
foreach (var it in CustomItemsCache.ImportedRecs)
if (seen.Add(it.Id)) mine.Add(it);
}
var page = mine.Skip(skip).Take(take).Select(r => r.ToJObject()).ToList();
if (skip == 0) Console.WriteLine($"[ugc] {account.Id} owns {mine.Count} custom item(s) (page size {take})");
return new ContentResult
{
Content = new Newtonsoft.Json.Linq.JObject
{
["Results"] = new Newtonsoft.Json.Linq.JArray(page),
["TotalResults"] = mine.Count
}.ToString(Newtonsoft.Json.Formatting.None),
ContentType = "application/json",
StatusCode = 200
};
}
[HttpGet("/econ/roomInventory/room/{roomId}")]
public IActionResult GetRoomInventoryForRoom(ulong roomId)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(ConfigCampus.Bracket);
}
[HttpGet("/econ/roomInventory/room/{roomId}/player")]
public IActionResult GetRoomInventoryForPlayer(ulong roomId)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(ConfigCampus.Bracket);
}
[HttpGet("/econ/roomInventoryItemTags/room/{roomId}")]
public IActionResult GetRoomInventoryItemTagsForRoom(ulong roomId)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(ConfigCampus.Bracket);
}
[HttpGet("/econ/roomEconConfig/{roomId}")]
public IActionResult GetRoomEconConfig(ulong roomId)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(new
{
RoomId = roomId,
EnableSortingTabs = false
});
}
[HttpGet("/econ/roomOffer/room/{roomId}")]
public IActionResult GetRoomOffer(ulong roomId)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(ConfigCampus.Bracket);
}
[HttpGet("/econ/roomOffer/room/{roomId}/purchaseCounts")]
public IActionResult GetRoomOfferPurchaseCounts(ulong roomId)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(new
{
value = Array.Empty<object>(),
success = true
});
}
[HttpGet("/econ/roomGiftDropShops/room/{roomId}")]
public IActionResult GetRoomGiftDropShops(ulong roomId)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(ConfigCampus.Bracket);
}
[HttpPost("/econ/roomInventory/v2/award")]
public IActionResult AwardRoomInventory([FromBody] List<AwardRoomInventoryRequest> request)
{
var (account, error) = AuthHelper.ValidateToken(HttpContext);
if (error != null)
return error;
return Ok(new
{
Result = 0,
PlayerRoomInventoryItem = new
{
AccountId = account.Id,
RoomInventoryItemId = request.FirstOrDefault().RoomInventoryItemOriginId,
RoomId = request.FirstOrDefault().RoomId,
RoomInventoryItemOriginId = request.FirstOrDefault().RoomInventoryItemOriginId,
Count = 0,
ConcurrencyCode = request.FirstOrDefault().ConcurrencyCodes.NewConcurrencyCode,
FirstReceivedAt = DateTime.UtcNow,
ModifiedAt = DateTime.UtcNow,
QuantityAwarded = request.FirstOrDefault().Quantity
}
});
}
public class AwardRoomInventoryRequest
{
public ConcurrencyCodes ConcurrencyCodes { get; set; }
public int Quantity { get; set; }
public ulong RoomId { get; set; }
public string RoomInventoryItemOriginId { get; set; }
}
public class ConcurrencyCodes
{
public string? CurrentConcurrencyCode { get; set; }
public string? NewConcurrencyCode { get; set; }
}
}
}