mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.RecNet.Accounts.Local;
|
||||
|
||||
namespace RadicadeServer.API.Config
|
||||
{
|
||||
[Route("api/settings")]
|
||||
[Route("playersettings")]
|
||||
public class PlayerSettingsAPI : Controller
|
||||
{
|
||||
private readonly RecDBContext db;
|
||||
|
||||
public PlayerSettingsAPI(RecDBContext context)
|
||||
{
|
||||
db = context;
|
||||
}
|
||||
|
||||
[HttpGet(""), Authorize]
|
||||
[HttpGet("v2"), Authorize]
|
||||
[HttpGet("playersettings"), Authorize]
|
||||
public List<Setting> GetSettings()
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
if (userId == null)
|
||||
return new List<Setting>();
|
||||
|
||||
var settings = db.PlayerSettings.FirstOrDefault(x => x.AccountId == userId.Value);
|
||||
var list = settings?.DeserializeSettings() ?? GetDefaultSettings();
|
||||
ApplyDefaultSettings(list);
|
||||
var player = db.Players.AsNoTracking().FirstOrDefault(x => x.AccountId == userId.Value);
|
||||
if (player != null && string.IsNullOrWhiteSpace(player.Hashword))
|
||||
{
|
||||
SetOrReplace(list, "Recroom.AccountCreation.HasCreatedPassword", "False");
|
||||
SetOrReplace(list, "Recroom.AccountCreation.HasFinished", "False");
|
||||
SetOrReplace(list, "Recroom.OOBE", "0");
|
||||
}
|
||||
ApplyResponseMetadata(list, userId.Value);
|
||||
if (settings == null)
|
||||
{
|
||||
db.PlayerSettings.Add(new PlayerSettings
|
||||
{
|
||||
AccountId = userId.Value,
|
||||
Settings = list.ToJson()
|
||||
});
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void SetOrReplace(List<Setting> list, string key, string value)
|
||||
{
|
||||
var existing = list.FirstOrDefault(x => string.Equals(x.Key, key, StringComparison.OrdinalIgnoreCase));
|
||||
if (existing != null)
|
||||
existing.Value = value;
|
||||
else
|
||||
list.Add(new Setting { Key = key, Value = value });
|
||||
}
|
||||
|
||||
private static void ApplyDefaultSettings(List<Setting> list)
|
||||
{
|
||||
bool needsOrientation = list.Any(setting =>
|
||||
string.Equals(setting.Key, "Radicade.NeedsOrientation", StringComparison.OrdinalIgnoreCase) &&
|
||||
(setting.Value == "1" || string.Equals(setting.Value, "true", StringComparison.OrdinalIgnoreCase)));
|
||||
foreach (var setting in GetDefaultSettings())
|
||||
{
|
||||
if (needsOrientation &&
|
||||
(string.Equals(setting.Key, "HAS_COMPLETED_ORIENTATION", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(setting.Key, "OrientationCompletionTime", StringComparison.OrdinalIgnoreCase)))
|
||||
continue;
|
||||
if (list.All(existing => !string.Equals(existing.Key, setting.Key, StringComparison.OrdinalIgnoreCase)))
|
||||
list.Add(setting);
|
||||
}
|
||||
|
||||
SetOrReplace(list, "SplitTestAssignedSegments", "1|{}");
|
||||
}
|
||||
|
||||
private static void ApplyResponseMetadata(List<Setting> list, long accountId)
|
||||
{
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
list[i].Id = i + 1;
|
||||
list[i].AccountId = accountId;
|
||||
}
|
||||
}
|
||||
|
||||
public const int InviteLinkLifetimeInMinutes = 60;
|
||||
|
||||
[HttpGet("partyinvite")]
|
||||
[HttpGet("~/chat/settings/partyinvite")]
|
||||
public IActionResult GetPartyInviteSettings() => Ok(new { InviteLinkLifetimeInMinutes });
|
||||
|
||||
[HttpPost("v2/set"), Authorize]
|
||||
[HttpPost(""), Authorize]
|
||||
[HttpPost("playersettings/set"), Authorize]
|
||||
public async Task<IActionResult> SetSettingRequest()
|
||||
{
|
||||
if (User.GetUserId() == null) return Unauthorized();
|
||||
var body = await ReadJsonBodyAsync();
|
||||
var updates = body.HasValue ? ReadSettings(body.Value) : new List<Setting>();
|
||||
if (updates.Count != 1) return BadRequest("Expected one setting with Key and Value.");
|
||||
return await SetSetting(updates[0]);
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public async Task<IActionResult> SetSetting(Setting val)
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
if (userId == null)
|
||||
return Unauthorized();
|
||||
if (val == null || string.IsNullOrWhiteSpace(val.Key) || val.Value == null)
|
||||
return BadRequest("Expected a setting with Key and Value.");
|
||||
|
||||
var settings = db.PlayerSettings.FirstOrDefault(x => x.AccountId == userId.Value);
|
||||
if (settings == null)
|
||||
{
|
||||
settings = new PlayerSettings
|
||||
{
|
||||
AccountId = userId.Value,
|
||||
Settings = DBConfig.DefaultSettings2023
|
||||
};
|
||||
await db.PlayerSettings.AddAsync(settings);
|
||||
}
|
||||
|
||||
var actualSettings = settings.DeserializeSettings();
|
||||
if (!await CanSaveOnboardingMarkerAsync(userId.Value, val, actualSettings, new[] { val }))
|
||||
return Ok(Array.Empty<object>());
|
||||
|
||||
SetOrReplace(actualSettings, val.Key, val.Value);
|
||||
if (IsOnboardingFinishedMarker(val))
|
||||
SetOrReplace(actualSettings, "Radicade.NeedsOrientation", "1");
|
||||
|
||||
settings.Settings = actualSettings.ToJson();
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await RecordCodeOfConductAsync(userId.Value, new[] { val }, actualSettings);
|
||||
|
||||
return Ok(Array.Empty<object>());
|
||||
}
|
||||
|
||||
[HttpPut(""), Authorize]
|
||||
[HttpPut("v2"), Authorize]
|
||||
[HttpPut("playersettings"), Authorize]
|
||||
public async Task<IActionResult> PutSettings()
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
if (userId == null)
|
||||
return Unauthorized();
|
||||
|
||||
var body = await ReadJsonBodyAsync();
|
||||
var updates = body.HasValue ? ReadSettings(body.Value) : new List<Setting>();
|
||||
if (updates.Count == 0)
|
||||
return BadRequest("No valid settings were supplied.");
|
||||
|
||||
var settings = db.PlayerSettings.FirstOrDefault(x => x.AccountId == userId.Value);
|
||||
if (settings == null)
|
||||
{
|
||||
settings = new PlayerSettings
|
||||
{
|
||||
AccountId = userId.Value,
|
||||
Settings = DBConfig.DefaultSettings2023
|
||||
};
|
||||
await db.PlayerSettings.AddAsync(settings);
|
||||
}
|
||||
|
||||
var actualSettings = settings.DeserializeSettings();
|
||||
foreach (var update in updates)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(update.Key) || update.Value == null ||
|
||||
!await CanSaveOnboardingMarkerAsync(userId.Value, update, actualSettings, updates))
|
||||
continue;
|
||||
|
||||
SetOrReplace(actualSettings, update.Key, update.Value);
|
||||
if (IsOnboardingFinishedMarker(update))
|
||||
SetOrReplace(actualSettings, "Radicade.NeedsOrientation", "1");
|
||||
}
|
||||
|
||||
settings.Settings = actualSettings.ToJson();
|
||||
await db.SaveChangesAsync();
|
||||
await RecordCodeOfConductAsync(userId.Value, updates, actualSettings);
|
||||
ApplyResponseMetadata(actualSettings, userId.Value);
|
||||
|
||||
return Ok(actualSettings);
|
||||
}
|
||||
|
||||
private async Task RecordCodeOfConductAsync(
|
||||
long accountId,
|
||||
IEnumerable<Setting> incomingSettings,
|
||||
IEnumerable<Setting> savedSettings)
|
||||
{
|
||||
bool accepted = incomingSettings.Concat(savedSettings).Any(setting =>
|
||||
string.Equals(setting.Key, "Recroom.AccountCreation.AcceptedCodeOfConduct", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(setting.Value, "true", StringComparison.OrdinalIgnoreCase));
|
||||
if (!accepted) return;
|
||||
|
||||
var player = await db.Players.FirstOrDefaultAsync(x => x.AccountId == accountId);
|
||||
if (player == null || player.CodeOfConductAccepted) return;
|
||||
|
||||
player.CodeOfConductAccepted = true;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static bool IsOnboardingFinishedMarker(Setting setting) =>
|
||||
(string.Equals(setting.Key, "Recroom.OOBE", StringComparison.OrdinalIgnoreCase) && setting.Value == "100") ||
|
||||
(string.Equals(setting.Key, "Recroom.AccountCreation.HasFinished", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(setting.Value, "true", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
private async Task<bool> CanSaveOnboardingMarkerAsync(
|
||||
long accountId,
|
||||
Setting update,
|
||||
IReadOnlyCollection<Setting> savedSettings,
|
||||
IReadOnlyCollection<Setting> incomingSettings)
|
||||
{
|
||||
bool marksPasswordCreated =
|
||||
string.Equals(update.Key, "Recroom.AccountCreation.HasCreatedPassword", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(update.Value, "true", StringComparison.OrdinalIgnoreCase);
|
||||
if (!marksPasswordCreated && !IsOnboardingFinishedMarker(update))
|
||||
return true;
|
||||
|
||||
var player = await db.Players.AsNoTracking().FirstOrDefaultAsync(x => x.AccountId == accountId);
|
||||
if (player == null || string.IsNullOrWhiteSpace(player.Hashword))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (marksPasswordCreated)
|
||||
return true;
|
||||
|
||||
var allSettings = incomingSettings.Concat(savedSettings).ToList();
|
||||
var profile = await db.Profiles.AsNoTracking().FirstOrDefaultAsync(x => x.accountId == accountId);
|
||||
bool hasBirthday = player.Birthday.HasValue && player.Birthday.Value.Year > 1900;
|
||||
bool hasChosenUsername = allSettings.Any(setting =>
|
||||
string.Equals(setting.Key, "Recroom.AccountCreation.HasChosenUsername", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(setting.Value, "true", StringComparison.OrdinalIgnoreCase));
|
||||
bool hasAcceptedCodeOfConduct = player.CodeOfConductAccepted || allSettings.Any(setting =>
|
||||
string.Equals(setting.Key, "Recroom.AccountCreation.AcceptedCodeOfConduct", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(setting.Value, "true", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (profile == null || string.IsNullOrWhiteSpace(profile.username))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!hasChosenUsername)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!hasAcceptedCodeOfConduct)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<Setting> GetDefaultSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
return DBConfig.DefaultSettings2023.FromJson<List<Setting>>() ?? new List<Setting>();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new List<Setting>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<JsonElement?> ReadJsonBodyAsync()
|
||||
{
|
||||
if (Request.Body == null)
|
||||
return null;
|
||||
|
||||
if (Request.ContentLength == 0)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (Request.HasFormContentType)
|
||||
{
|
||||
var form = await Request.ReadFormAsync();
|
||||
return JsonSerializer.SerializeToElement(form.ToDictionary(pair => pair.Key, pair => pair.Value.ToString()));
|
||||
}
|
||||
using var doc = await JsonDocument.ParseAsync(Request.Body);
|
||||
return doc.RootElement.Clone();
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Setting> ReadSettings(JsonElement body)
|
||||
{
|
||||
if (body.ValueKind == JsonValueKind.Array)
|
||||
return ReadSettingsArray(body);
|
||||
|
||||
if (body.ValueKind != JsonValueKind.Object)
|
||||
return new List<Setting>();
|
||||
|
||||
foreach (var key in new[] { "settings", "Settings", "playerSettings", "PlayerSettings" })
|
||||
{
|
||||
if (body.TryGetProperty(key, out var nested) && nested.ValueKind == JsonValueKind.Array)
|
||||
return ReadSettingsArray(nested);
|
||||
}
|
||||
|
||||
var setting = ReadSettingObject(body);
|
||||
return setting == null ? new List<Setting>() : new List<Setting> { setting };
|
||||
}
|
||||
|
||||
private static List<Setting> ReadSettingsArray(JsonElement array)
|
||||
{
|
||||
var settings = new List<Setting>();
|
||||
foreach (var item in array.EnumerateArray())
|
||||
{
|
||||
var setting = ReadSettingObject(item);
|
||||
if (setting != null)
|
||||
settings.Add(setting);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
private static Setting? ReadSettingObject(JsonElement item)
|
||||
{
|
||||
if (item.ValueKind != JsonValueKind.Object)
|
||||
return null;
|
||||
|
||||
var key = ReadString(item, "Key", "key", "Name", "name");
|
||||
var value = ReadString(item, "Value", "value");
|
||||
if (string.IsNullOrWhiteSpace(key) || value == null)
|
||||
return null;
|
||||
|
||||
return new Setting { Key = key, Value = value };
|
||||
}
|
||||
|
||||
private static string? ReadString(JsonElement item, params string[] names)
|
||||
{
|
||||
foreach (var name in names)
|
||||
{
|
||||
if (!item.TryGetProperty(name, out var value))
|
||||
continue;
|
||||
|
||||
return value.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => value.GetString(),
|
||||
JsonValueKind.Number or JsonValueKind.True or JsonValueKind.False => value.ToString(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user