mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-27 07:59:03 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class AmplitudeConfig
|
||||
{
|
||||
public string AmplitudeKey { get; set; } = "";
|
||||
public bool UseRudderStack { get; set; } = false;
|
||||
public string RudderStackKey { get; set; } = "";
|
||||
public bool UseStatSig { get; set; } = false;
|
||||
public string StatSigKey { get; set; } = "";
|
||||
public int StatSigEnvironment { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.RecNet.Config;
|
||||
|
||||
namespace RadicadeServer.API.Config
|
||||
{
|
||||
[Route("api/config")]
|
||||
public class ConfigAPI : Controller
|
||||
{
|
||||
RecDBContext db;
|
||||
public ConfigAPI(RecDBContext context)
|
||||
{
|
||||
this.db = context;
|
||||
}
|
||||
|
||||
[HttpGet("v1/amplitude")]
|
||||
public IActionResult GetAmplitudeConfig()
|
||||
{
|
||||
AmplitudeConfig amplitudeConfig = new AmplitudeConfig
|
||||
{
|
||||
AmplitudeKey = "e1693a1003671058b6abc356c8ba8d59",
|
||||
UseRudderStack = true,
|
||||
RudderStackKey = "23NiJHIgu3koaGNCZIiuYvIQNCu",
|
||||
UseStatSig = true,
|
||||
StatSigKey = "client-SBZkOrjD3r1Cat3f3W8K6sBd11WKlXZXIlCWj6l4Aje",
|
||||
StatSigEnvironment = 0,
|
||||
};
|
||||
return Ok(amplitudeConfig);
|
||||
}
|
||||
|
||||
[HttpGet("v1/freegiftbutton")]
|
||||
public IActionResult GetFreeGiftButtonConfig()
|
||||
{
|
||||
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
[HttpGet("v1/azurespeech")]
|
||||
public IActionResult GetAzureSpeechConfig()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
Key = "dce8de5b297747d9b5bddcc7f19e8c5b",
|
||||
Region = "eastus",
|
||||
Enabled = false,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("v1/backtrace")]
|
||||
public IActionResult GetBacktraceConfig()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
ReportBudget = 125,
|
||||
FilterType = 0,
|
||||
SampleRate = 1,
|
||||
LogLineCount = 50,
|
||||
CaptureNativeCrashes = 1,
|
||||
AMRThresholdMS = 0,
|
||||
MessageCount = 1000,
|
||||
MessageRegex = "^.*$",
|
||||
VersionRegex = ".*",
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("v2")]
|
||||
public async Task<ActionResult<MainConfig>> GetConfig()
|
||||
{
|
||||
MainConfig? config = await db.Config.FirstOrDefaultAsync();
|
||||
if (config == null) return StatusCode(500);
|
||||
|
||||
bool didChanges = false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(config.CdnBaseUri) || config.CdnBaseUri.Contains("localhost", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
config.CdnBaseUri = DBConfig.NameServer.CDN;
|
||||
didChanges = true;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(config.ShareBaseUrl) || config.ShareBaseUrl.Contains("localhost", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
config.ShareBaseUrl = DBConfig.NameServer.WWW + "{0}";
|
||||
didChanges = true;
|
||||
}
|
||||
|
||||
if (config.ExpectingReload) {
|
||||
didChanges = true;
|
||||
config.SetAsActive();
|
||||
config.ExpectingReload = false;
|
||||
}
|
||||
|
||||
if (config.RefreshConfig())
|
||||
didChanges = true;
|
||||
|
||||
if (didChanges)
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return Ok(config?.ToActualConfig());
|
||||
}
|
||||
|
||||
[HttpPost("~/statsigUserProperties")]
|
||||
public IActionResult GetStatsigUserProperties()
|
||||
{
|
||||
return Ok(new { success = true });
|
||||
}
|
||||
|
||||
[HttpGet("~/voice/config")]
|
||||
[HttpGet("~/moderation/voice/config")]
|
||||
public IActionResult GetVoiceConfig()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
KOLPDOKFMGH = "",
|
||||
OKHMLNBOBNP = (object?)null,
|
||||
BEGBMPFDPIE = (object?)null,
|
||||
BNOIPAAFAMJ = new
|
||||
{
|
||||
_AOMMGOMJHPE = "",
|
||||
_NPEJEBGENBC = "",
|
||||
PEFLHAPIDIF = "",
|
||||
ALOMNDOKBLH = ""
|
||||
},
|
||||
PAFBLJGNBLN = new string[0],
|
||||
HAOCNOKPKDH = new string[0],
|
||||
MBEGDKGEOMJ = false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class ConfigVal
|
||||
{
|
||||
public ConfigVal() { }
|
||||
public ConfigVal(string key, string val) { Key = key; Value = val; }
|
||||
|
||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public string Key { get; set; } = null!;
|
||||
public string Value { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.RecNet.Config;
|
||||
using RadicadeServer.RecNet.Keepsakes;
|
||||
|
||||
namespace RadicadeServer.API.Config
|
||||
{
|
||||
[Route("api/gameconfigs/")]
|
||||
public class GameConfigAPI : Controller
|
||||
{
|
||||
private static readonly Dictionary<string, string> Local2023Overrides = new()
|
||||
{
|
||||
["RRUI.UseRRUIHomeScreen"] = "true",
|
||||
["USE_NEW_HOME_SCREEN"] = "true",
|
||||
["new_watch_architecture_enabled"] = "true",
|
||||
["watch_layer.new_watch_architecture_enabled"] = "true",
|
||||
["HAS_SEEN_HOME_SCREEN_CHOICE"] = "true",
|
||||
["AntiHile.QD"] = "false",
|
||||
["Radicade.Invites.UseRRUIDialog"] = "true",
|
||||
["Radicade.Invites.ShowOnlineFriendsBeforeJoining"] = "true",
|
||||
["Lifecycle.PSF.FriendRoomInviteRateLimitingCount"] = "2147483647",
|
||||
["AndroidMobile.enforceFileWatcher"] = "0",
|
||||
["AndroidMobile.enforceHashCheck"] = "0",
|
||||
["AndroidMobile.runFileWatcher"] = "0",
|
||||
["AndroidMobile.runHashCheck"] = "0",
|
||||
["Desktop_Oculus.enforceFileWatcher"] = "0",
|
||||
["Desktop_Oculus.enforceHashCheck"] = "0",
|
||||
["Desktop_Oculus.runFileWatcher"] = "0",
|
||||
["Desktop_Oculus.runHashCheck"] = "0",
|
||||
["Desktop_Steam.enforceFileWatcher"] = "0",
|
||||
["Desktop_Steam.enforceHashCheck"] = "0",
|
||||
["Desktop_Steam.runFileWatcher"] = "0",
|
||||
["Desktop_Steam.runHashCheck"] = "0",
|
||||
["OculusQuest.enforceFileWatcher"] = "0",
|
||||
["OculusQuest.enforceHashCheck"] = "0",
|
||||
["OculusQuest.runFileWatcher"] = "0",
|
||||
["OculusQuest.runHashCheck"] = "0",
|
||||
["Steam.enforceFileWatcher"] = "0",
|
||||
["Steam.enforceHashCheck"] = "0",
|
||||
["Steam.runFileWatcher"] = "0",
|
||||
["Steam.runHashCheck"] = "0",
|
||||
};
|
||||
|
||||
private readonly RecDBContext db;
|
||||
|
||||
public GameConfigAPI(RecDBContext context)
|
||||
{
|
||||
db = context;
|
||||
}
|
||||
|
||||
private static IEnumerable<KeyValuePair<string, string>> KeepsakeConfigValues()
|
||||
{
|
||||
var enabled = KeepsakeFeature.Enabled;
|
||||
yield return new("KeepsakeFeatureEnabled", enabled ? "true" : "false");
|
||||
yield return new("KeepsakeRoomLimit", enabled ? "100" : "0");
|
||||
yield return new("ProgressionEvents.EnableKeepsakeManager", enabled ? "true" : "false");
|
||||
}
|
||||
|
||||
[HttpGet("v1/all")]
|
||||
public IActionResult GetAllGameConfigs()
|
||||
{
|
||||
var overrides = new Dictionary<string, string>(Local2023Overrides);
|
||||
foreach (var (key, value) in KeepsakeConfigValues()) overrides[key] = value;
|
||||
|
||||
var configs = db.GameConfig
|
||||
.AsEnumerable()
|
||||
.Select(x => new ConfigVal(
|
||||
x.Key,
|
||||
overrides.TryGetValue(x.Key, out var value)
|
||||
? value
|
||||
: SplitTestConfigs.FilterHardOverridesFor2023Client(x.Key, x.Value)))
|
||||
.GroupBy(x => x.Key)
|
||||
.Select(group =>
|
||||
{
|
||||
if (overrides.TryGetValue(group.Key, out var value))
|
||||
return new ConfigVal(group.Key, value);
|
||||
|
||||
return group.Last();
|
||||
})
|
||||
.ToList();
|
||||
|
||||
foreach (var (key, value) in overrides)
|
||||
{
|
||||
if (!configs.Any(x => x.Key == key))
|
||||
configs.Add(new ConfigVal(key, value));
|
||||
}
|
||||
|
||||
return Ok(configs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.RecNet.Config;
|
||||
|
||||
namespace RadicadeServer.Utils
|
||||
{
|
||||
public static class GameConfigSeeder
|
||||
{
|
||||
public static async Task<ConfigVal?> FindAsync(RecDBContext db, string key)
|
||||
=> db.GameConfig.Local.FirstOrDefault(row => row.Key == key)
|
||||
?? await db.GameConfig.FirstOrDefaultAsync(row => row.Key == key);
|
||||
|
||||
public static async Task<ConfigVal> UpsertAsync(RecDBContext db, string key, string value)
|
||||
{
|
||||
var existing = await FindAsync(db, key);
|
||||
if (existing == null)
|
||||
{
|
||||
var added = new ConfigVal(key, value);
|
||||
await db.GameConfig.AddAsync(added);
|
||||
return added;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(existing.Value))
|
||||
existing.Value = value;
|
||||
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Rubidium2018.Utils.Objectives;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.RecNet.Leveling;
|
||||
using RadicadeServer.RecNet.Maintenance;
|
||||
using RadicadeServer.RecNet.Progression.Objectives;
|
||||
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class MainConfig
|
||||
{
|
||||
[Key]
|
||||
public string FUCKYOUMICROSOFT { get; set; } = null!;
|
||||
public string ShareBaseUrl { get; set; } = null!;
|
||||
public string CdnBaseUri { get; set; } = null!;
|
||||
public string LevelProgressionMaps { get; set; } = null!;
|
||||
public string ServerMaintenance { get; set; } = null!;
|
||||
public string DailyObjectives { get; set; } = null!;
|
||||
public string AutoMicMutingConfig { get; set; } = null!;
|
||||
public string StorefrontConfig { get; set; } = null!;
|
||||
public string RoomKeyConfig { get; set; } = null!;
|
||||
public string RoomCurrencyConfig { get; set; } = null!;
|
||||
public string GiftDropStoreItems { get; set; } = null!;
|
||||
|
||||
public bool ExpectingReload { get; set; }
|
||||
public DateTime LastRefresh_DailyObjectives { get; set; } = DateTime.Now;
|
||||
|
||||
public static MainConfig? ActiveConfig = null;
|
||||
internal void SetAsActive()
|
||||
{
|
||||
ActiveConfig = this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MainConfigExt
|
||||
{
|
||||
public static bool RefreshConfig(this MainConfig config, bool force = false)
|
||||
{
|
||||
if (config == null)
|
||||
return false;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (DateTime.Now >= config.LastRefresh_DailyObjectives || force)
|
||||
{
|
||||
changed = true;
|
||||
|
||||
Objective[][] dailys =
|
||||
[
|
||||
[RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective()],
|
||||
[RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective()],
|
||||
[RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective()],
|
||||
[RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective()],
|
||||
[RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective()],
|
||||
[RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective()],
|
||||
[RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective(), RbPresetDailys.CreateRandomObjective()]
|
||||
];
|
||||
|
||||
config.DailyObjectives = dailys.ToJson();
|
||||
config.LastRefresh_DailyObjectives = DateTime.Now.AddDays(7);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static RealMainConfig ToActualConfig(this MainConfig config)
|
||||
{
|
||||
return new RealMainConfig
|
||||
{
|
||||
ShareBaseUrl = config.ShareBaseUrl ?? string.Empty,
|
||||
CdnBaseUri = config.CdnBaseUri ?? string.Empty,
|
||||
StorefrontConfig = config.StorefrontConfig.FromJson<StorefrontConfig>() ?? new StorefrontConfig(),
|
||||
AutoMicMutingConfig = config.AutoMicMutingConfig.FromJson<MicMuting>() ?? new MicMuting(),
|
||||
RoomCurrencyConfig = config.RoomCurrencyConfig.FromJson<RoomCurrencyConfig>() ?? new RoomCurrencyConfig(),
|
||||
RoomKeyConfig = config.RoomKeyConfig.FromJson<RoomKeyConfig>() ?? new RoomKeyConfig(),
|
||||
DailyObjectives = config.DailyObjectives.FromJson<Objective[][]>() ?? Array.Empty<Objective[]>(),
|
||||
LevelProgressionMaps = config.LevelProgressionMaps.FromJson<LevelProgressionMap[]>() ?? Array.Empty<LevelProgressionMap>(),
|
||||
ServerMaintenance = config.ServerMaintenance.FromJson<ServerMaintenance>() ?? new ServerMaintenance { StartsInMinutes = 0 }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class MicMuting
|
||||
{
|
||||
public float MicSpamVolumeThreshold { get; set; }
|
||||
public float MicVolumeSampleInterval { get; set; }
|
||||
public float MicVolumeSampleRollingWindowLength { get; set; }
|
||||
public float MicSpamSamplePercentageForWarning { get; set; }
|
||||
public float MicSpamSamplePercentageForWarningToEnd { get; set; }
|
||||
public float MicSpamSamplePercentageForForceMute { get; set; }
|
||||
public float MicSpamSamplePercentageForForceMuteToEnd { get; set; }
|
||||
public float MicSpamWarningStateVolumeMultiplier { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using RadicadeServer.RecNet.Leveling;
|
||||
using RadicadeServer.RecNet.Maintenance;
|
||||
using RadicadeServer.RecNet.Progression.Objectives;
|
||||
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class RealMainConfig
|
||||
{
|
||||
public string ShareBaseUrl { get; set; } = null!;
|
||||
public string CdnBaseUri { get; set; } = null!;
|
||||
public LevelProgressionMap[] LevelProgressionMaps { get; set; } = null!;
|
||||
public ServerMaintenance ServerMaintenance { get; set; } = null!;
|
||||
public Objective[][] DailyObjectives { get; set; } = null!;
|
||||
public MicMuting AutoMicMutingConfig { get; set; } = null!;
|
||||
public StorefrontConfig StorefrontConfig { get; set; } = null!;
|
||||
public RoomKeyConfig RoomKeyConfig { get; set; } = null!;
|
||||
public RoomCurrencyConfig RoomCurrencyConfig { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class RoomCurrencyConfig
|
||||
{
|
||||
public float AwardCurrencyCooldownSeconds { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class RoomKeyConfig
|
||||
{
|
||||
public int MaxKeysPerRoom { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class ServerConfigVal
|
||||
{
|
||||
public ServerConfigVal() { }
|
||||
public ServerConfigVal(string key, string val) { Key = key; Value = val; }
|
||||
|
||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public string Key { get; set; } = null!;
|
||||
public string Value { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public static class SplitTestConfigs
|
||||
{
|
||||
public const string HardOverrides =
|
||||
"IOSAppPermissions_2019_10_02=Immediate;GottaGoFastNUX_2020_09_04=Off;RecNetImageCacheRefCount_2020_09_18=On;Curated_Rooms_2020_08_06=On;PlayMenuRRUI_2021_01_11=On;PlayMenuRRUI_HotRoomsCarousel_2021_02_09=On;PlayMenuRRUI_CarouselOrdering_2021_02_09=PlayMenuCarouselOrdering_1;CreationCommands_2021_02_16=Off;RecRoomPlus_DormMirrorButton_2021_03_09=ButtonOn;CurrentContestWinnersCarouselVisibility_2021_04_15=Off;MutualFriends_2021_04_22=On;PlayMenuRRUI_DefaultTab_2021_05_13=Highlight;DefaultNewPlayersToPlayMenu_2021_05_24=On;ShowNewRRPlusIcon_2021_06_01=NewIcon;ParallelNetworkingInitialRoomLoad_2021_06_28=ParallelLoad;ShowNewRRPlusBenefitsPage_2021_06_14=OldPage;WatchStoreHomepageLayout_2021_06_14=OldStoreLayout;NotificationPermissionsExplanationText_2021_06_25=Control;PlayMenuRRUI_TopCreatorsCarousel_2021_09_01=Off;UseSets_2021_08_06=UseSets;RoomSimilarities_2021_10_29=On;ProfileOnClick_2021_04_30=On;RoomRecommendations_2020_05_06=On;FriendImportLogin_2021_09_23=On;RudderStackAnalyticsDestinations_2022_02_10=RudderStackOnly;ObjectModel_EnableForRoom_2022_07_01=Disallowed;PlayMenuCarouselOrdering_2021_11_01=PlayMenuCarouselOrdering_1;PlayMenuBanner_2021_11_01=Off;";
|
||||
|
||||
public const string TestsAvailable =
|
||||
"RoomRecommendations_2020_05_06=On,Off;Curated_Rooms_2020_08_06=Off,On;RecNetImageCacheRefCount_2020_09_18=On,Off;GottaGoFastNUX_2020_09_04=Off,On;Curated_Rooms_Tab_Placement_2020_09_22=First,Second;PlayMenuRRUI_2021_01_11=Off,On;PlayMenuRRUI_HotRoomsCarousel_2021_02_09=Off,On;PlayMenuRRUI_CarouselOrdering_2021_02_09=PlayMenuCarouselOrdering_1,PlayMenuCarouselOrdering_2,PlayMenuCarouselOrdering_3;CreationCommands_2021_02_16=Off,On;RecRoomPlus_DormMirrorButton_2021_03_09=ButtonOn,ButtonOff;CurrentContestWinnersCarouselVisibility_2021_04_15=Off,On;MutualFriends_2021_04_22=Off,On;ProfileOnClick_2021_04_30=Off,On;PlayMenuRRUI_DefaultTab_2021_05_13=Highlight,RRO,Hot;DefaultNewPlayersToPlayMenu_2021_05_24=Off,On;ShowNewRRPlusIcon_2021_06_01=OldIcon,NewIcon;ShowNewRRPlusBenefitsPage_2021_06_14=OldPage,NewPage;WatchStoreHomepageLayout_2021_06_14=OldStoreLayout,NewStoreLayout;NotificationPermissionsExplanationText_2021_06_25=Control,SpecificValueText;ParallelNetworkingInitialRoomLoad_2021_06_28=SequentialLoad,ParallelLoad;UseSets_2021_08_06=IgnoreSets,UseSets;PlayMenuRRUI_TopCreatorsCarousel_2021_09_01=Off,On;FriendImportLogin_2021_09_23=Off,On;Purchase_Campaigns_AB_2021_09_13=Purchase_Campaign_AB_A,Purchase_Campaign_AB_B;Purchase_Campaigns_ABCD_2021_09_13=Purchase_Campaign_ABCD_A,Purchase_Campaign_ABCD_B,Purchase_Campaign_ABCD_C,Purchase_Campaign_ABCD_D;RoomSimilarities_2021_10_29=On,Off;PlayMenuBanner_2021_11_01=Off,On;PlayMenuCarouselOrdering_2021_11_01=PlayMenuCarouselOrdering_1,PlayMenuCarouselOrdering_2,PlayMenuCarouselOrdering_3;RudderStackAnalyticsDestinations_2022_02_10=Disabled,RudderStackOnly,Both;ObjectModel_EnableForRoom_2022_07_01=Disallowed,Allowed;";
|
||||
|
||||
public const string SegmentProbabilities =
|
||||
"{\"RoomRecommendations_2020_05_06\":[{\"SegmentName\":\"On\",\"Probability\":0.25},{\"SegmentName\":\"Off\",\"Probability\":0.75}],\"CurrentContestWinnersCarouselVisibility_2021_04_15\":[{\"SegmentName\":\"Off\",\"Probability\":1.0},{\"SegmentName\":\"On\",\"Probability\":0.0}],\"PlayMenuBanner_2021_11_01\":[{\"SegmentName\":\"Off\",\"Probability\":0.5},{\"SegmentName\":\"On\",\"Probability\":0.5}],\"FriendImportLogin_2021_09_23\":[{\"SegmentName\":\"Off\",\"Probability\":0.5},{\"SegmentName\":\"On\",\"Probability\":0.5}],\"PlayMenuCarouselOrdering_2021_11_01\":[{\"SegmentName\":\"PlayMenuCarouselOrdering_1\",\"Probability\":0.33},{\"SegmentName\":\"PlayMenuCarouselOrdering_2\",\"Probability\":0.33},{\"SegmentName\":\"PlayMenuCarouselOrdering_3\",\"Probability\":0.34}]}";
|
||||
|
||||
public static readonly IReadOnlyDictionary<string, IReadOnlyList<string>> Client2023Catalog =
|
||||
new Dictionary<string, IReadOnlyList<string>>(StringComparer.Ordinal)
|
||||
{
|
||||
["Example_Test_2020_06_06"] = new[] { "On", "Off" },
|
||||
["RoomRecommendations_2020_05_06"] = new[] { "On", "Off" },
|
||||
["RoomRecommendationsType_2020_08_14"] = new[] { "June22", "Aug14JuniorSpecificModel", "Aug14JuniorAccessibleModelForAll", "Aug14MinVisitors350", "Aug14MinVisitors3500", "Aug14MinVisitors35000" },
|
||||
["GottaGoFastNUX_2020_09_04"] = new[] { "Off", "On" },
|
||||
["PlayMenuRRUI_HotRoomsCarousel_2021_02_09"] = new[] { "Off", "On" },
|
||||
["PlayMenuRRUI_CarouselOrdering_2021_02_09"] = new[] { "PlayMenuCarouselOrdering_1", "PlayMenuCarouselOrdering_2", "PlayMenuCarouselOrdering_3" },
|
||||
["MutualFriends_2021_04_22"] = new[] { "Off", "On" },
|
||||
["CurrentContestWinnersCarouselVisibility_2021_04_15"] = new[] { "Off", "On" },
|
||||
["PlayMenuRRUI_DefaultTab_2021_05_13"] = new[] { "Highlight", "RRO", "Hot" },
|
||||
["ProfileOnClick_2021_04_30"] = new[] { "Off", "On" },
|
||||
["DefaultNewPlayersToPlayMenu_2021_05_24"] = new[] { "Off", "On" },
|
||||
["ParallelNetworkingInitialRoomLoad_2021_06_28"] = new[] { "SequentialLoad", "ParallelLoad" },
|
||||
["PlayMenuRRUI_TopCreatorsCarousel_2021_09_01"] = new[] { "Off", "On" },
|
||||
["FriendImportLogin_2021_09_23"] = new[] { "Off", "On" },
|
||||
["RoomSimilarities_2021_10_29"] = new[] { "On", "Off" },
|
||||
["PlayMenuBanner_2021_11_01"] = new[] { "Off", "On" },
|
||||
["PlayMenuCarouselOrdering_2021_11_01"] = new[] { "PlayMenuCarouselOrdering_1", "PlayMenuCarouselOrdering_2", "PlayMenuCarouselOrdering_3" },
|
||||
["RudderStackAnalyticsDestinations_2022_02_10"] = new[] { "Disabled", "RudderStackOnly", "Both" },
|
||||
["ObjectModel_EnableForRoom_2022_07_01"] = new[] { "Disallowed", "Allowed" },
|
||||
};
|
||||
|
||||
public static string FilterHardOverridesFor2023Client(string key, string value)
|
||||
{
|
||||
if (key != "splitTestHardOverrides" || string.IsNullOrWhiteSpace(value))
|
||||
return value;
|
||||
|
||||
var kept = value.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Select(entry => entry.Split('=', 2))
|
||||
.Where(pair => pair.Length == 2
|
||||
&& Client2023Catalog.TryGetValue(pair[0], out var segments)
|
||||
&& segments.Contains(pair[1], StringComparer.Ordinal))
|
||||
.Select(pair => pair[0] + "=" + pair[1]);
|
||||
|
||||
var keptList = kept.ToList();
|
||||
return keptList.Count > 0 ? string.Join(";", keptList) + ";" : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace RadicadeServer.RecNet.Config
|
||||
{
|
||||
public class StorefrontConfig
|
||||
{
|
||||
public int MinPlayerLevelForGifting { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.Utils;
|
||||
|
||||
namespace RadicadeServer.API.Config
|
||||
{
|
||||
[Route("api/voicechat")]
|
||||
public class VoiceChatRestrictionAPI(RecDBContext db) : Controller
|
||||
{
|
||||
[HttpGet("restriction"), Authorize]
|
||||
public async Task<IActionResult> GetRestriction()
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
if (userId == null)
|
||||
return Unauthorized(new { restricted = false });
|
||||
|
||||
var restriction = await VoiceChatRestrictions.ActiveAsync(db, userId.Value);
|
||||
return Ok(VoiceChatRestrictions.Payload(restriction));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user