Files
Radicade/Server/Rooms/RoomEconomyPayload.cs
2026-10-16 23:33:13 -07:00

229 lines
6.6 KiB
C#

using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using RadicadeServer.DB;
using RadicadeServer.Extensions;
namespace RadicadeServer.API.Rooms
{
internal static class RoomEconomyPayload
{
private const string RawKey = "RoomEconomyPayload.Raw";
public static async Task<JsonElement?> ReadBodyAsync(HttpRequest request)
{
if (request.HasFormContentType)
{
try
{
var form = await request.ReadFormAsync();
if (form.Count > 0)
{
var fields = new Dictionary<string, object?>();
foreach (var field in form)
fields[field.Key] = field.Value.ToString();
foreach (var file in form.Files)
fields[file.Name] = file.FileName;
return JsonSerializer.SerializeToElement(fields);
}
}
catch (Exception ex)
{
request.HttpContext.Items[RawKey] = $"<form body could not be read: {ex.Message}>";
}
}
string raw;
using (var reader = new StreamReader(request.Body))
raw = await reader.ReadToEndAsync();
var text = raw.TrimStart('\uFEFF', ' ', '\t', '\r', '\n');
if (text.Length == 0) return null;
if (text[0] is '{' or '[')
{
try
{
using var doc = JsonDocument.Parse(text);
return doc.RootElement.Clone();
}
catch (JsonException)
{
}
}
var pairs = ParsePairs(raw);
if (pairs.Count > 0)
{
request.HttpContext.Items[RawKey] = raw;
return JsonSerializer.SerializeToElement(pairs);
}
request.HttpContext.Items[RawKey] = raw;
return null;
}
public static string RawBody(HttpRequest request) =>
request.HttpContext.Items.TryGetValue(RawKey, out var raw) ? raw as string ?? "" : "";
public static JsonElement? Value(JsonElement? body, params string[] names)
{
if (body == null) return null;
var stack = new Stack<JsonElement>();
stack.Push(body.Value);
var visited = 0;
while (stack.Count > 0 && visited++ < 2000)
{
var element = stack.Pop();
if (element.ValueKind == JsonValueKind.Array)
{
foreach (var item in element.EnumerateArray()) stack.Push(item);
continue;
}
if (element.ValueKind != JsonValueKind.Object) continue;
foreach (var property in element.EnumerateObject())
{
if (property.Value.ValueKind is JsonValueKind.Object or JsonValueKind.Array)
{
stack.Push(property.Value);
continue;
}
if (Matches(property.Name, names)) return property.Value;
}
}
return null;
}
public static string? String(JsonElement? body, params string[] names)
{
var value = Value(body, names);
if (value == null) return null;
if (value.Value.ValueKind == JsonValueKind.String) return value.Value.GetString();
if (value.Value.ValueKind is JsonValueKind.Number or JsonValueKind.True or JsonValueKind.False)
return value.Value.ToString();
return null;
}
public static long? Long(JsonElement? body, params string[] names)
{
var value = Value(body, names);
if (value == null) return null;
if (value.Value.ValueKind == JsonValueKind.Number)
{
if (value.Value.TryGetInt64(out var number)) return number;
if (value.Value.TryGetDouble(out var real)) return (long)real;
}
if (value.Value.ValueKind == JsonValueKind.String && long.TryParse(value.Value.GetString(), out var parsed))
return parsed;
return null;
}
public static bool? Bool(JsonElement? body, params string[] names)
{
var value = Value(body, names);
if (value == null) return null;
if (value.Value.ValueKind == JsonValueKind.True) return true;
if (value.Value.ValueKind == JsonValueKind.False) return false;
return null;
}
public static long? LongFromRequest(HttpRequest request, params string[] names)
{
foreach (var pair in request.Query)
{
if (!Matches(pair.Key, names)) continue;
if (long.TryParse(pair.Value.FirstOrDefault(), out var fromQuery)) return fromQuery;
}
foreach (var pair in request.RouteValues)
{
if (!Matches(pair.Key, names)) continue;
if (long.TryParse(pair.Value?.ToString(), out var fromRoute)) return fromRoute;
}
return null;
}
public static Dictionary<string, object?> Fields(JsonElement? body)
{
var fields = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
if (body == null || body.Value.ValueKind != JsonValueKind.Object) return fields;
foreach (var property in body.Value.EnumerateObject())
{
fields[property.Name] = property.Value.ValueKind switch
{
JsonValueKind.String => property.Value.GetString(),
JsonValueKind.Number => property.Value.TryGetInt64(out var number) ? number : property.Value.GetDouble(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Null => null,
_ => property.Value.Clone()
};
}
return fields;
}
public static async Task<long?> RoomIdAsync(RecDBContext db, HttpRequest request, JsonElement? body, params string[] names)
{
var roomId = Long(body, names) ?? LongFromRequest(request, names);
if (roomId is > 0) return roomId;
var accountId = request.HttpContext.User.GetUserId();
if (accountId is not > 0) return null;
var presence = await db.Presences.AsNoTracking()
.FirstOrDefaultAsync(item => item.playerId == accountId.Value);
if (presence?.roomInstance is not > 0) return null;
return await db.RoomInstances.AsNoTracking()
.Where(instance => instance.roomInstanceId == presence.roomInstance.Value)
.Select(instance => (long?)instance.roomId)
.FirstOrDefaultAsync();
}
public static string NewId()
{
var bytes = RandomNumberGenerator.GetBytes(16);
return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_').TrimEnd('=');
}
private static Dictionary<string, object?> ParsePairs(string raw)
{
var pairs = new Dictionary<string, object?>();
foreach (var pair in raw.Split('&', StringSplitOptions.RemoveEmptyEntries))
{
var at = pair.IndexOf('=');
var key = at < 0 ? pair : pair[..at];
var value = at < 0 ? "" : pair[(at + 1)..];
key = key.Trim();
if (key.Length == 0) continue;
try
{
pairs[Uri.UnescapeDataString(key.Replace('+', ' '))] =
Uri.UnescapeDataString(value.Replace('+', ' '));
}
catch (UriFormatException)
{
pairs[key] = value;
}
}
return pairs;
}
private static bool Matches(string name, string[] names)
{
foreach (var wanted in names)
{
if (string.Equals(Normalize(name), Normalize(wanted), StringComparison.Ordinal)) return true;
}
return false;
}
private static string Normalize(string name) =>
string.Concat(name.Where(char.IsLetterOrDigit)).ToLowerInvariant();
}
}