Decompiled source of WristHub Quest v4.0.1
Mods\WristHub.Core.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using BoneHub.Api; using BoneHub.Avatars; using BoneHub.Configuration; using BoneHub.Downloads; using BoneHub.Installation; using BoneHub.Interaction; using BoneHub.Models; using BoneHub.Persistence; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyCompany("WristHub.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("4.0.1.0")] [assembly: AssemblyInformationalVersion("4.0.1")] [assembly: AssemblyProduct("WristHub.Core")] [assembly: AssemblyTitle("WristHub.Core")] [assembly: AssemblyVersion("4.0.1.0")] [module: RefSafetyRules(11)] namespace BoneHub { public sealed class BoneHubService : IDisposable { private readonly IModIoClient _client; private readonly DownloadManager _downloads; private readonly BoneHubOptions _options; private readonly SearchCache _cache; private readonly JsonFileStore<BoneHubState> _stateStore; private readonly SemaphoreSlim _stateGate = new SemaphoreSlim(1, 1); private BoneHubState _state = new BoneHubState(); public int FavoriteCount => _state.FavoriteModIds.Count; public IReadOnlyCollection<BoneHub.Persistence.InstalledModRecord> Installed => (IReadOnlyCollection<BoneHub.Persistence.InstalledModRecord>)(object)_state.InstalledMods.Values.ToArray(); public BoneHubPreferences Preferences => _state.Preferences; public IReadOnlyList<string> RecentlyEquippedAvatarBarcodes => _state.RecentlyEquippedAvatarBarcodes.ToArray(); public string LastEquippedAvatarBarcode { get { if (!string.IsNullOrWhiteSpace(_state.LastEquippedAvatarBarcode)) { return _state.LastEquippedAvatarBarcode; } return _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty; } } public TrashedModRecord? LastTrashed => _state.TrashHistory.LastOrDefault(); public IReadOnlyCollection<long> PendingDeletionModIds => (IReadOnlyCollection<long>)(object)_state.PendingDeletions.Select((PendingDeletionRecord item) => item.Mod.ModId).ToArray(); public bool ModIoReady => _options.CanUseModIo; public string ApiKeyFilePath => _options.ApiKeyFilePath; public event Action<DownloadJob>? DownloadChanged; public event Action? InstalledChanged; public BoneHubService(IModIoClient client, DownloadManager downloads, BoneHubOptions options) { _client = client; _downloads = downloads; _options = options; _cache = new SearchCache(Path.Combine(options.DataDirectory, "cache", "search")); _stateStore = new JsonFileStore<BoneHubState>(Path.Combine(options.DataDirectory, "state.json")); _downloads.JobChanged += OnJobChanged; } public async Task InitializeAsync(CancellationToken cancellationToken = default(CancellationToken)) { _state = await _stateStore.LoadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (_state.Preferences.MiniMapRange == MiniMapRange.TwentyFiveMeters) { _state.Preferences.MiniMapRange = MiniMapRange.ThirtyMeters; } } public async Task<ModIoPage<ModIoMod>> SearchAsync(ModSearchRequest request, CancellationToken cancellationToken = default(CancellationToken)) { ModSearchRequest normalized = request with { Query = request.Query.Trim(), Limit = Math.Clamp(request.Limit, 1, 100), Offset = Math.Max(request.Offset, 0) }; string key = $"{normalized.Query}|{normalized.Tag}|{normalized.Creator}|{normalized.Sort}|{normalized.Offset}|{normalized.Limit}|{_options.TargetPlatform}"; ModIoPage<ModIoMod> modIoPage = await _cache.ReadAsync(key, TimeSpan.FromMinutes(_options.CacheMinutes), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (modIoPage != null) { return modIoPage; } try { ModIoPage<ModIoMod> page = await _client.SearchAsync(normalized, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); await _cache.WriteAsync(key, page, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); return page; } catch when (!cancellationToken.IsCancellationRequested) { ModIoPage<ModIoMod> modIoPage2 = await _cache.ReadAsync(key, TimeSpan.FromDays(7.0), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (modIoPage2 != null) { return modIoPage2; } throw; } } public DownloadJob Install(ModIoMod mod, CancellationToken cancellationToken = default(CancellationToken)) { return _downloads.Enqueue(mod, cancellationToken); } public async Task<ModInstallPlan> GetInstallPlanAsync(ModIoMod root, CancellationToken cancellationToken = default(CancellationToken)) { if (!ModIoReady) { throw new InvalidOperationException("WristHub's online avatar service is unavailable."); } List<ModIoMod> ordered = new List<ModIoMod>(); HashSet<long> installed = new HashSet<long>(); HashSet<long> visited = new HashSet<long>(); HashSet<long> visiting = new HashSet<long>(); await VisitAsync(root, isRoot: true).ConfigureAwait(continueOnCapturedContext: false); return new ModInstallPlan(root, ordered, installed.OrderBy((long id) => id).ToArray()); async Task VisitAsync(ModIoMod mod, bool isRoot) { cancellationToken.ThrowIfCancellationRequested(); if (!visited.Contains(mod.Id)) { if (!visiting.Add(mod.Id)) { throw new InvalidDataException($"mod.io reported a dependency cycle involving mod {mod.Id}."); } if (visited.Count + visiting.Count > 32) { throw new InvalidDataException("This dependency graph exceeds WristHub's 32-mod safety limit."); } foreach (ModIoDependency item in (await _client.GetDependenciesAsync(mod.Id, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Data.Where((ModIoDependency item) => item.ModId > 0).DistinctBy((ModIoDependency item) => item.ModId)) { if (_state.InstalledMods.ContainsKey(item.ModId)) { installed.Add(item.ModId); } else { await VisitAsync(await _client.GetModAsync(item.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), isRoot: false).ConfigureAwait(continueOnCapturedContext: false); } } visiting.Remove(mod.Id); visited.Add(mod.Id); if (isRoot || !_state.InstalledMods.ContainsKey(mod.Id)) { ordered.Add(mod); } else { installed.Add(mod.Id); } } } } public async Task<IReadOnlyList<DownloadJob>> InstallWithDependenciesAsync(ModIoMod root, CancellationToken cancellationToken = default(CancellationToken)) { return (await GetInstallPlanAsync(root, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).OrderedMods.Select((ModIoMod mod) => Install(mod, cancellationToken)).ToArray(); } public bool CancelDownload(Guid jobId) { return _downloads.Cancel(jobId); } public bool IsFavorite(long modId) { return _state.FavoriteModIds.Contains(modId); } public bool IsInstalled(long modId) { return _state.InstalledMods.ContainsKey(modId); } public async Task ConfigureApiKeyAsync(string value, CancellationToken cancellationToken = default(CancellationToken)) { string normalized = value?.Trim() ?? string.Empty; if (!BoneHubOptions.IsValidApiKey(normalized)) { throw new InvalidDataException("The mod.io API key must contain exactly 32 letters or numbers."); } if (string.IsNullOrWhiteSpace(_options.ApiKeyFilePath)) { throw new InvalidOperationException("WristHub's API-key file path is unavailable."); } string path = Path.GetFullPath(_options.ApiKeyFilePath); Directory.CreateDirectory(Path.GetDirectoryName(path) ?? throw new InvalidOperationException("The API-key file has no parent directory.")); string temporary = path + ".tmp"; await File.WriteAllTextAsync(temporary, normalized, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); File.Move(temporary, path, overwrite: true); _options.ApiKey = normalized; } public async Task SetFavoriteAsync(long modId, bool favorite, CancellationToken cancellationToken = default(CancellationToken)) { await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { if (favorite) { _state.FavoriteModIds.Add(modId); } else { _state.FavoriteModIds.Remove(modId); } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } } public async Task<IReadOnlyList<ModIoMod>> GetFavoriteModsAsync(CancellationToken cancellationToken = default(CancellationToken)) { await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); long[] array; try { array = _state.FavoriteModIds.Take(50).ToArray(); } finally { _stateGate.Release(); } List<ModIoMod> results = new List<ModIoMod>(); long[] array2 = array; foreach (long modId in array2) { cancellationToken.ThrowIfCancellationRequested(); try { List<ModIoMod> list = results; list.Add(await _client.GetModAsync(modId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)); } catch when (!cancellationToken.IsCancellationRequested) { } } return results.OrderBy((ModIoMod mod) => mod.Name).ToArray(); } public Task<ModIoMod> GetModDetailsAsync(long modId, CancellationToken cancellationToken = default(CancellationToken)) { return _client.GetModAsync(modId, cancellationToken); } public async Task MarkViewedAsync(long modId, CancellationToken cancellationToken = default(CancellationToken)) { await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { _state.RecentlyViewedModIds.Remove(modId); _state.RecentlyViewedModIds.Insert(0, modId); if (_state.RecentlyViewedModIds.Count > 50) { _state.RecentlyViewedModIds.RemoveRange(50, _state.RecentlyViewedModIds.Count - 50); } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } } public async Task MarkAvatarEquippedAsync(string barcode, CancellationToken cancellationToken = default(CancellationToken)) { string normalized = barcode?.Trim() ?? string.Empty; if (normalized.Length == 0) { return; } await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { _state.RecentlyEquippedAvatarBarcodes.RemoveAll((string item) => string.Equals(item, normalized, StringComparison.OrdinalIgnoreCase)); _state.RecentlyEquippedAvatarBarcodes.Insert(0, normalized); _state.LastEquippedAvatarBarcode = normalized; if (_state.RecentlyEquippedAvatarBarcodes.Count > 50) { _state.RecentlyEquippedAvatarBarcodes.RemoveRange(50, _state.RecentlyEquippedAvatarBarcodes.Count - 50); } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } } public async Task ForgetAvatarBarcodesAsync(IEnumerable<string> barcodes, CancellationToken cancellationToken = default(CancellationToken)) { HashSet<string> removed = (from item in barcodes where !string.IsNullOrWhiteSpace(item) select item.Trim()).ToHashSet<string>(StringComparer.OrdinalIgnoreCase); if (removed.Count == 0) { return; } await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { _state.RecentlyEquippedAvatarBarcodes.RemoveAll(removed.Contains); if (removed.Contains(_state.LastEquippedAvatarBarcode)) { _state.LastEquippedAvatarBarcode = _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty; } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } } public async Task SavePreferencesAsync(CancellationToken cancellationToken = default(CancellationToken)) { await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { if (!Enum.IsDefined(_state.Preferences.ComfortPreset)) { _state.Preferences.ComfortPreset = BoneHubComfortPreset.Default; } if (!Enum.IsDefined(_state.Preferences.AvatarBrowserStartupMode)) { _state.Preferences.AvatarBrowserStartupMode = BrowserStartupMode.AllInstalled; } if (!Enum.IsDefined(_state.Preferences.LastAvatarBrowseScope)) { _state.Preferences.LastAvatarBrowseScope = AvatarBrowseScope.AllInstalled; } if (!Enum.IsDefined(_state.Preferences.MiniMapRange)) { _state.Preferences.MiniMapRange = MiniMapRange.ThirtyMeters; } if (!Enum.IsDefined(_state.Preferences.MiniMapOrientation)) { _state.Preferences.MiniMapOrientation = MiniMapOrientation.HeadingUp; } if (!Enum.IsDefined(_state.Preferences.HologramTheme)) { _state.Preferences.HologramTheme = WristHubTheme.Cyan; } _state.Preferences.WatchScale = Math.Clamp(_state.Preferences.WatchScale, 0.65f, 1.6f); _state.Preferences.DwellSeconds = 1f; _state.Preferences.OutwardOffset = Math.Clamp(_state.Preferences.OutwardOffset, 0.015f, 0.06f); _state.Preferences.FingerOffset = Math.Clamp(_state.Preferences.FingerOffset, -0.055f, 0.025f); _state.Preferences.WatchForearmOffset = Math.Clamp(_state.Preferences.WatchForearmOffset, -0.08f, 0.03f); _state.Preferences.CarouselDistance = Math.Clamp(_state.Preferences.CarouselDistance, 0.45f, 0.95f); _state.Preferences.HologramScale = Math.Clamp(_state.Preferences.HologramScale, 0.7f, 1.4f); _state.Preferences.EquipZoneScale = Math.Clamp(_state.Preferences.EquipZoneScale, 0.7f, 1.6f); _state.Preferences.HologramBrightness = Math.Clamp(_state.Preferences.HologramBrightness, 0.35f, 1.5f); foreach (CompassCalibrationRecord compassCalibration in _state.Preferences.CompassCalibrations) { compassCalibration.NorthYawDegrees = CompassHeadingMath.Normalize(compassCalibration.NorthYawDegrees); } while (_state.Preferences.CompassCalibrations.Count > 64) { CompassCalibrationRecord item = _state.Preferences.CompassCalibrations.OrderBy((CompassCalibrationRecord record) => record.LastUsedUtc).First(); _state.Preferences.CompassCalibrations.Remove(item); } TrimDistinct(_state.Preferences.FavoriteSpawnableBarcodes, 128); TrimDistinct(_state.Preferences.RecentSpawnableBarcodes, 64); await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } } private static void TrimDistinct(List<string> values, int maximum) { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); for (int num = values.Count - 1; num >= 0; num--) { string text = values[num]?.Trim() ?? string.Empty; if (text.Length == 0 || !hashSet.Add(text)) { values.RemoveAt(num); } else { values[num] = text; } } if (values.Count > maximum) { values.RemoveRange(maximum, values.Count - maximum); } } public InstalledModHealth VerifyInstalled(long modId) { if (!_state.InstalledMods.TryGetValue(modId, out BoneHub.Persistence.InstalledModRecord value)) { return new InstalledModHealth(Healthy: false, new string[1] { "WristHub has no installation record for this mod." }); } List<string> list = new List<string>(); foreach (string installedDirectory in value.InstalledDirectories) { string text; try { text = ValidateInstalledPath(installedDirectory); } catch (Exception ex) { list.Add(ex.Message); continue; } if (!Directory.Exists(text)) { list.Add("Missing folder: " + Path.GetFileName(text)); } else if (!PalletManifestLocator.HasManifest(text)) { list.Add("Missing pallet manifest: " + Path.GetFileName(text)); } } return new InstalledModHealth(list.Count == 0, list); } public async Task<ModUpdateInfo> CheckForUpdateAsync(long modId, CancellationToken cancellationToken = default(CancellationToken)) { if (!_state.InstalledMods.TryGetValue(modId, out BoneHub.Persistence.InstalledModRecord installed)) { throw new InvalidOperationException("This mod is not installed."); } ModIoMod mod = await _client.GetModAsync(modId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); ModIoFile modIoFile = await ModIoFileResolver.ResolveAsync(_client, mod, _options, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); return new ModUpdateInfo(mod, modIoFile.Id != installed.FileId, installed.FileId, modIoFile.Id); } public async Task<IReadOnlyList<ModUpdateInfo>> CheckAllUpdatesAsync(CancellationToken cancellationToken = default(CancellationToken)) { List<ModUpdateInfo> results = new List<ModUpdateInfo>(); foreach (BoneHub.Persistence.InstalledModRecord item in _state.InstalledMods.Values.OrderBy((BoneHub.Persistence.InstalledModRecord item) => item.Name)) { cancellationToken.ThrowIfCancellationRequested(); List<ModUpdateInfo> list = results; list.Add(await CheckForUpdateAsync(item.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)); } return results; } public async Task<DownloadJob> RepairOrUpdateAsync(long modId, CancellationToken cancellationToken = default(CancellationToken)) { return Install((await CheckForUpdateAsync(modId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Mod, cancellationToken); } public async Task<int> RepairSharingRegistrationsAsync(CancellationToken cancellationToken = default(CancellationToken)) { BoneHub.Persistence.InstalledModRecord[] array = _state.InstalledMods.Values.Where((BoneHub.Persistence.InstalledModRecord installedModRecord) => installedModRecord.ModId > 0 && installedModRecord.InstalledDirectories.Any(Directory.Exists) && (installedModRecord.Sharing == null || installedModRecord.Sharing.RegistrationVersion < 3 || installedModRecord.Sharing.SidecarManifestPaths.Count == 0 || installedModRecord.Sharing.SidecarManifestPaths.Any((string path) => !File.Exists(path)))).ToArray(); int repaired = 0; BoneHub.Persistence.InstalledModRecord[] array2 = array; foreach (BoneHub.Persistence.InstalledModRecord record in array2) { cancellationToken.ThrowIfCancellationRequested(); try { ModIoMod mod = await _client.GetModAsync(record.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); ModIoFile modIoFile = ((record.FileId <= 0) ? (await ModIoFileResolver.ResolveAsync(_client, mod, _options, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)) : (await _client.GetFileAsync(record.ModId, record.FileId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false))); ModIoFile file = modIoFile; ModIoSharingMetadata sharing = await NetworkerRegistrationWriter.WriteAsync(mod, file, await ModIoFileResolver.ResolvePlatformFilesAsync(_client, record.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), record.InstalledDirectories, _options.ModsDirectory, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { if (_state.InstalledMods.TryGetValue(record.ModId, out BoneHub.Persistence.InstalledModRecord value)) { value.Sharing = sharing; } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } repaired++; } catch when (!cancellationToken.IsCancellationRequested) { } } if (repaired > 0) { this.InstalledChanged?.Invoke(); } return repaired; } public Task<TrashedModRecord> UninstallAsync(long modId, CancellationToken cancellationToken = default(CancellationToken)) { return UninstallCoreAsync(modId, null, null, wasSubscribed: false, cancellationToken); } public Task<TrashedModRecord> UninstallAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, CancellationToken cancellationToken = default(CancellationToken)) { return UninstallCoreAsync(modId, localName, localDirectories, wasSubscribed: false, cancellationToken); } public Task<TrashedModRecord> UninstallSubscribedAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, bool wasSubscribed, CancellationToken cancellationToken = default(CancellationToken)) { return UninstallCoreAsync(modId, localName, localDirectories, wasSubscribed, cancellationToken); } public async Task<PendingDeletionRecord> QueueDeletionAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, bool wasSubscribed, IEnumerable<string>? avatarBarcodes, CancellationToken cancellationToken = default(CancellationToken)) { await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); PendingDeletionRecord queued; try { PendingDeletionRecord pendingDeletionRecord = _state.PendingDeletions.FirstOrDefault((PendingDeletionRecord item) => item.Mod.ModId == modId); if (pendingDeletionRecord != null) { return pendingDeletionRecord; } _state.InstalledMods.TryGetValue(modId, out BoneHub.Persistence.InstalledModRecord value); IEnumerable<string> enumerable = localDirectories; object obj = enumerable; if (obj == null) { enumerable = value?.InstalledDirectories; obj = enumerable ?? Array.Empty<string>(); } List<string> list = ((IEnumerable<string>)obj).Where((string path) => !string.IsNullOrWhiteSpace(path)).Select(ValidateInstalledPath).Where(Directory.Exists) .Distinct<string>(StringComparer.OrdinalIgnoreCase) .ToList(); if (list.Count == 0) { throw new DirectoryNotFoundException("No installed avatar-pack folders were found to queue."); } if (value == null) { value = new BoneHub.Persistence.InstalledModRecord { ModId = modId, Name = (string.IsNullOrWhiteSpace(localName) ? "Local avatar pack" : localName.Trim()), InstalledDirectories = list, InstalledAt = DateTimeOffset.UtcNow }; } List<string> sidecarFiles = (from path in (value.Sharing?.SidecarManifestPaths ?? new List<string>()).Concat(FindRegistrationSidecars(list)) where !string.IsNullOrWhiteSpace(path) select path).Select(ValidateInstalledFile).Where(File.Exists).Distinct<string>(StringComparer.OrdinalIgnoreCase) .ToList(); List<string> barcodes = (from text in avatarBarcodes ?? Array.Empty<string>() where !string.IsNullOrWhiteSpace(text) select text.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); queued = new PendingDeletionRecord { Mod = value, Directories = list, SidecarFiles = sidecarFiles, AvatarBarcodes = barcodes, WasSubscribed = wasSubscribed }; _state.PendingDeletions.Add(queued); _state.InstalledMods.Remove(modId); _state.RecentlyEquippedAvatarBarcodes.RemoveAll((string value2) => barcodes.Contains<string>(value2, StringComparer.OrdinalIgnoreCase)); if (barcodes.Contains<string>(_state.LastEquippedAvatarBarcode, StringComparer.OrdinalIgnoreCase)) { _state.LastEquippedAvatarBarcode = _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty; } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } this.InstalledChanged?.Invoke(); return queued; } public async Task<PendingDeletionProcessResult> ProcessPendingDeletionsAsync(CancellationToken cancellationToken = default(CancellationToken)) { int completed = 0; List<string> problems = new List<string>(); await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { PendingDeletionRecord[] array = _state.PendingDeletions.ToArray(); foreach (PendingDeletionRecord pending in array) { cancellationToken.ThrowIfCancellationRequested(); string text = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar)) ?? throw new InvalidOperationException("Mods directory has no parent."), ".wristhub-trash", $"{pending.Mod.ModId}-{DateTimeOffset.UtcNow:yyyyMMddHHmmss}-{Guid.NewGuid():N}"); Directory.CreateDirectory(text); List<TrashedDirectoryRecord> list = new List<TrashedDirectoryRecord>(); List<TrashedFileRecord> list2 = new List<TrashedFileRecord>(); try { for (int j = 0; j < pending.Directories.Count; j++) { string text2 = ValidateInstalledPath(pending.Directories[j]); if (Directory.Exists(text2)) { string text3 = Path.Combine(text, $"{j:D2}-{Path.GetFileName(text2)}"); Directory.Move(text2, text3); list.Add(new TrashedDirectoryRecord { OriginalPath = text2, TrashPath = text3 }); } } for (int k = 0; k < pending.SidecarFiles.Count; k++) { string text4 = ValidateInstalledFile(pending.SidecarFiles[k]); if (File.Exists(text4)) { string text5 = Path.Combine(text, $"manifest-{k:D2}-{Path.GetFileName(text4)}"); File.Move(text4, text5); list2.Add(new TrashedFileRecord { OriginalPath = text4, TrashPath = text5 }); } } if (list.Count > 0 || list2.Count > 0) { _state.TrashHistory.Add(new TrashedModRecord { Mod = pending.Mod, Directories = list, Files = list2, WasSubscribed = pending.WasSubscribed }); if (_state.TrashHistory.Count > 20) { _state.TrashHistory.RemoveRange(0, _state.TrashHistory.Count - 20); } } else if (Directory.Exists(text)) { Directory.Delete(text, recursive: false); } _state.PendingDeletions.Remove(pending); _state.InstalledMods.Remove(pending.Mod.ModId); _state.RecentlyEquippedAvatarBarcodes.RemoveAll((string value) => pending.AvatarBarcodes.Contains<string>(value, StringComparer.OrdinalIgnoreCase)); if (pending.AvatarBarcodes.Contains<string>(_state.LastEquippedAvatarBarcode, StringComparer.OrdinalIgnoreCase)) { _state.LastEquippedAvatarBarcode = _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty; } completed++; } catch (Exception ex) { for (int num = list.Count - 1; num >= 0; num--) { TrashedDirectoryRecord trashedDirectoryRecord = list[num]; if (Directory.Exists(trashedDirectoryRecord.TrashPath) && !Directory.Exists(trashedDirectoryRecord.OriginalPath)) { Directory.Move(trashedDirectoryRecord.TrashPath, trashedDirectoryRecord.OriginalPath); } } for (int num2 = list2.Count - 1; num2 >= 0; num2--) { TrashedFileRecord trashedFileRecord = list2[num2]; if (File.Exists(trashedFileRecord.TrashPath) && !File.Exists(trashedFileRecord.OriginalPath)) { File.Move(trashedFileRecord.TrashPath, trashedFileRecord.OriginalPath); } } try { if (Directory.Exists(text) && !Directory.EnumerateFileSystemEntries(text).Any()) { Directory.Delete(text); } } catch { } problems.Add(pending.Mod.Name + ": " + ex.Message); } } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } if (completed > 0) { this.InstalledChanged?.Invoke(); } return new PendingDeletionProcessResult(completed, _state.PendingDeletions.Count, problems); } private async Task<TrashedModRecord> UninstallCoreAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, bool wasSubscribed, CancellationToken cancellationToken) { await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { BoneHub.Persistence.InstalledModRecord installed; bool wasTracked = _state.InstalledMods.TryGetValue(modId, out installed); if (!wasTracked) { List<string> list = (localDirectories ?? Array.Empty<string>()).Where((string path) => !string.IsNullOrWhiteSpace(path)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); if (list.Count == 0) { throw new InvalidOperationException("WristHub could not identify this avatar pack's local folder."); } installed = new BoneHub.Persistence.InstalledModRecord { ModId = modId, Name = (string.IsNullOrWhiteSpace(localName) ? "Local avatar pack" : localName.Trim()), InstalledDirectories = list, InstalledAt = DateTimeOffset.UtcNow }; } BoneHub.Persistence.InstalledModRecord installedModRecord = installed ?? throw new InvalidOperationException("The avatar pack record is unavailable."); string text = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar)) ?? throw new InvalidOperationException("Mods directory has no parent."), ".wristhub-trash", $"{modId}-{DateTimeOffset.UtcNow:yyyyMMddHHmmss}-{Guid.NewGuid():N}"); Directory.CreateDirectory(text); List<TrashedDirectoryRecord> moved = new List<TrashedDirectoryRecord>(); List<TrashedFileRecord> movedFiles = new List<TrashedFileRecord>(); TrashedModRecord stateRecord = null; try { for (int num = 0; num < installedModRecord.InstalledDirectories.Count; num++) { cancellationToken.ThrowIfCancellationRequested(); string text2 = ValidateInstalledPath(installedModRecord.InstalledDirectories[num]); if (Directory.Exists(text2)) { string text3 = Path.Combine(text, $"{num:D2}-{Path.GetFileName(text2)}"); Directory.Move(text2, text3); moved.Add(new TrashedDirectoryRecord { OriginalPath = text2, TrashPath = text3 }); } } string[] array = (installedModRecord.Sharing?.SidecarManifestPaths ?? new List<string>()).Concat(FindRegistrationSidecars(installedModRecord.InstalledDirectories)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); for (int num2 = 0; num2 < array.Length; num2++) { cancellationToken.ThrowIfCancellationRequested(); string text4 = ValidateInstalledFile(array[num2]); if (File.Exists(text4)) { string text5 = Path.Combine(text, $"manifest-{num2:D2}-{Path.GetFileName(text4)}"); File.Move(text4, text5); movedFiles.Add(new TrashedFileRecord { OriginalPath = text4, TrashPath = text5 }); } } if (moved.Count == 0) { throw new DirectoryNotFoundException("No installed avatar-pack folders were found to remove."); } TrashedModRecord record = new TrashedModRecord { Mod = installedModRecord, Directories = moved, Files = movedFiles, WasSubscribed = wasSubscribed }; stateRecord = record; _state.InstalledMods.Remove(modId); _state.TrashHistory.Add(record); if (_state.TrashHistory.Count > 20) { _state.TrashHistory.RemoveRange(0, _state.TrashHistory.Count - 20); } await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); this.InstalledChanged?.Invoke(); return record; } catch { if (wasTracked) { _state.InstalledMods[modId] = installed; } else { _state.InstalledMods.Remove(modId); } if (stateRecord != null) { _state.TrashHistory.Remove(stateRecord); } for (int num3 = moved.Count - 1; num3 >= 0; num3--) { TrashedDirectoryRecord trashedDirectoryRecord = moved[num3]; if (Directory.Exists(trashedDirectoryRecord.TrashPath) && !Directory.Exists(trashedDirectoryRecord.OriginalPath)) { Directory.Move(trashedDirectoryRecord.TrashPath, trashedDirectoryRecord.OriginalPath); } } for (int num4 = movedFiles.Count - 1; num4 >= 0; num4--) { TrashedFileRecord trashedFileRecord = movedFiles[num4]; if (File.Exists(trashedFileRecord.TrashPath) && !File.Exists(trashedFileRecord.OriginalPath)) { File.Move(trashedFileRecord.TrashPath, trashedFileRecord.OriginalPath); } } throw; } } finally { _stateGate.Release(); } } public async Task<BoneHub.Persistence.InstalledModRecord> UndoLastUninstallAsync(CancellationToken cancellationToken = default(CancellationToken)) { await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { TrashedModRecord record = _state.TrashHistory.LastOrDefault() ?? throw new InvalidOperationException("There is no WristHub uninstall to undo."); if (_state.InstalledMods.ContainsKey(record.Mod.ModId)) { throw new InvalidOperationException("That mod is already installed again."); } List<TrashedDirectoryRecord> restored = new List<TrashedDirectoryRecord>(); List<TrashedFileRecord> restoredFiles = new List<TrashedFileRecord>(); try { foreach (TrashedDirectoryRecord directory in record.Directories) { cancellationToken.ThrowIfCancellationRequested(); string text = ValidateInstalledPath(directory.OriginalPath); string text2 = ValidateTrashPath(directory.TrashPath); if (Directory.Exists(text)) { throw new IOException("Cannot restore " + Path.GetFileName(text) + " because that folder already exists."); } if (!Directory.Exists(text2)) { throw new DirectoryNotFoundException("Trash data is missing for " + record.Mod.Name + "."); } Directory.Move(text2, text); restored.Add(new TrashedDirectoryRecord { OriginalPath = text, TrashPath = text2 }); } foreach (TrashedFileRecord file in record.Files) { cancellationToken.ThrowIfCancellationRequested(); string text3 = ValidateInstalledFile(file.OriginalPath); string text4 = ValidateTrashPath(file.TrashPath); if (File.Exists(text3)) { throw new IOException("Cannot restore " + Path.GetFileName(text3) + " because it already exists."); } if (!File.Exists(text4)) { throw new FileNotFoundException("Trash sidecar data is missing.", text4); } File.Move(text4, text3); restoredFiles.Add(new TrashedFileRecord { OriginalPath = text3, TrashPath = text4 }); } _state.InstalledMods[record.Mod.ModId] = record.Mod; _state.TrashHistory.Remove(record); await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); this.InstalledChanged?.Invoke(); return record.Mod; } catch { _state.InstalledMods.Remove(record.Mod.ModId); if (!_state.TrashHistory.Contains(record)) { _state.TrashHistory.Add(record); } for (int num = restored.Count - 1; num >= 0; num--) { TrashedDirectoryRecord trashedDirectoryRecord = restored[num]; if (Directory.Exists(trashedDirectoryRecord.OriginalPath) && !Directory.Exists(trashedDirectoryRecord.TrashPath)) { Directory.Move(trashedDirectoryRecord.OriginalPath, trashedDirectoryRecord.TrashPath); } } for (int num2 = restoredFiles.Count - 1; num2 >= 0; num2--) { TrashedFileRecord trashedFileRecord = restoredFiles[num2]; if (File.Exists(trashedFileRecord.OriginalPath) && !File.Exists(trashedFileRecord.TrashPath)) { File.Move(trashedFileRecord.OriginalPath, trashedFileRecord.TrashPath); } } throw; } } finally { _stateGate.Release(); } } private void OnJobChanged(DownloadJob job) { if (job.State == DownloadState.Completed && job.Result != null) { PersistCompletedJobAsync(job, job.Result); } else { this.DownloadChanged?.Invoke(job); } } private async Task PersistCompletedJobAsync(DownloadJob job, BoneHub.Downloads.InstalledModRecord result) { await _stateGate.WaitAsync().ConfigureAwait(continueOnCapturedContext: false); try { _state.InstalledMods[result.ModId] = new BoneHub.Persistence.InstalledModRecord { ModId = result.ModId, FileId = result.FileId, Name = result.Name, Version = result.Version, Md5 = result.Md5, InstalledDirectories = result.InstalledDirectories.ToList(), Sharing = result.Sharing, InstalledAt = DateTimeOffset.UtcNow }; await _stateStore.SaveAsync(_state).ConfigureAwait(continueOnCapturedContext: false); this.DownloadChanged?.Invoke(job); this.InstalledChanged?.Invoke(); } finally { _stateGate.Release(); } } private string ValidateInstalledPath(string path) { string text = Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar); string text2 = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar); if (!text2.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) || string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) { throw new InvalidDataException("An installed-mod path falls outside BONELAB's Mods directory."); } return text2; } private string ValidateInstalledFile(string path) { string text = Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar); string fullPath = Path.GetFullPath(path); if (!fullPath.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) || !string.Equals(Path.GetExtension(fullPath), ".manifest", StringComparison.OrdinalIgnoreCase)) { throw new InvalidDataException("A mod registration sidecar falls outside BONELAB's Mods directory."); } return fullPath; } private IReadOnlyList<string> FindRegistrationSidecars(IEnumerable<string> packageDirectories) { string[] source = packageDirectories.Select(Path.GetFullPath).ToArray(); List<string> list = new List<string>(); if (!Directory.Exists(_options.ModsDirectory)) { return list; } foreach (string item in Directory.EnumerateFiles(_options.ModsDirectory, "*.manifest", SearchOption.TopDirectoryOnly)) { try { using JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(item)); JsonElement rootElement = jsonDocument.RootElement; string propertyName = rootElement.GetProperty("root").GetProperty("ref").GetString() ?? "1"; string text = rootElement.GetProperty("objects").GetProperty(propertyName).GetProperty("palletPath") .GetString(); if (!string.IsNullOrWhiteSpace(text)) { string fullPallet = Path.GetFullPath(text); if (source.Any((string package) => fullPallet.StartsWith(package.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))) { list.Add(item); } } } catch { } } return list; } private string ValidateTrashPath(string path) { string? path2 = Path.GetDirectoryName(Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar)) ?? throw new InvalidOperationException("Mods directory has no parent."); string text = Path.GetFullPath(Path.Combine(path2, ".wristhub-trash")).TrimEnd(Path.DirectorySeparatorChar); string text2 = Path.GetFullPath(Path.Combine(path2, ".bonehub-trash")).TrimEnd(Path.DirectorySeparatorChar); string text3 = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar); bool num = text3.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) && !string.Equals(text3, text, StringComparison.OrdinalIgnoreCase); bool flag = text3.StartsWith(text2 + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) && !string.Equals(text3, text2, StringComparison.OrdinalIgnoreCase); if (!num && !flag) { throw new InvalidDataException("A restore path falls outside WristHub Trash."); } return text3; } public void Dispose() { _downloads.JobChanged -= OnJobChanged; _downloads.Dispose(); (_client as IDisposable)?.Dispose(); _stateGate.Dispose(); } } } namespace BoneHub.Persistence { public sealed class BoneHubState { public HashSet<long> FavoriteModIds { get; init; } = new HashSet<long>(); public List<long> RecentlyViewedModIds { get; init; } = new List<long>(); public List<string> RecentlyEquippedAvatarBarcodes { get; init; } = new List<string>(); public string LastEquippedAvatarBarcode { get; set; } = string.Empty; public Dictionary<long, InstalledModRecord> InstalledMods { get; init; } = new Dictionary<long, InstalledModRecord>(); public BoneHubPreferences Preferences { get; init; } = new BoneHubPreferences(); public List<TrashedModRecord> TrashHistory { get; init; } = new List<TrashedModRecord>(); public List<PendingDeletionRecord> PendingDeletions { get; init; } = new List<PendingDeletionRecord>(); } public sealed class BoneHubPreferences { public BoneHubComfortPreset ComfortPreset { get; set; } public bool WatchEnabled { get; set; } = true; public bool RaiseToOpen { get; set; } = true; public bool UseLeftWrist { get; set; } = true; public float WatchScale { get; set; } = 1f; public float DwellSeconds { get; set; } = 1f; public float OutwardOffset { get; set; } = 0.029f; public float FingerOffset { get; set; } = -0.018f; public float WatchForearmOffset { get; set; } public bool ReducedMotion { get; set; } public bool HologramEffects { get; set; } = true; public bool HapticsEnabled { get; set; } = true; public bool SoundsEnabled { get; set; } = true; public float CarouselDistance { get; set; } = 0.62f; public float HologramScale { get; set; } = 1f; public float EquipZoneScale { get; set; } = 1f; public float HologramBrightness { get; set; } = 1f; public WristHubTheme HologramTheme { get; set; } public bool Use24HourClock { get; set; } = true; public BrowserStartupMode AvatarBrowserStartupMode { get; set; } = BrowserStartupMode.AllInstalled; public AvatarBrowseScope LastAvatarBrowseScope { get; set; } public List<CompassCalibrationRecord> CompassCalibrations { get; init; } = new List<CompassCalibrationRecord>(); public MiniMapRange MiniMapRange { get; set; } = MiniMapRange.ThirtyMeters; public MiniMapOrientation MiniMapOrientation { get; set; } public List<string> FavoriteSpawnableBarcodes { get; init; } = new List<string>(); public List<string> RecentSpawnableBarcodes { get; init; } = new List<string>(); } public enum WristHubTheme { Cyan, Graphite, Crimson, Emerald, Violet, Amber } public sealed class CompassCalibrationRecord { public string SceneKey { get; init; } = string.Empty; public float NorthYawDegrees { get; set; } public DateTimeOffset LastUsedUtc { get; set; } = DateTimeOffset.UtcNow; } public enum BoneHubComfortPreset { Default, Seated, LargeTargets, ReducedMotion } public sealed class InstalledModRecord { public long ModId { get; init; } public long FileId { get; init; } public string Name { get; init; } = string.Empty; public string Version { get; init; } = string.Empty; public string Md5 { get; init; } = string.Empty; public List<string> InstalledDirectories { get; init; } = new List<string>(); public ModIoSharingMetadata? Sharing { get; set; } public DateTimeOffset InstalledAt { get; init; } } public sealed class TrashedModRecord { public Guid Id { get; init; } = Guid.NewGuid(); public InstalledModRecord Mod { get; init; } = new InstalledModRecord(); public List<TrashedDirectoryRecord> Directories { get; init; } = new List<TrashedDirectoryRecord>(); public List<TrashedFileRecord> Files { get; init; } = new List<TrashedFileRecord>(); public bool WasSubscribed { get; init; } public DateTimeOffset TrashedAt { get; init; } = DateTimeOffset.UtcNow; } public sealed class TrashedFileRecord { public string OriginalPath { get; init; } = string.Empty; public string TrashPath { get; init; } = string.Empty; } public sealed class TrashedDirectoryRecord { public string OriginalPath { get; init; } = string.Empty; public string TrashPath { get; init; } = string.Empty; } public sealed class PendingDeletionRecord { public InstalledModRecord Mod { get; init; } = new InstalledModRecord(); public List<string> Directories { get; init; } = new List<string>(); public List<string> SidecarFiles { get; init; } = new List<string>(); public List<string> AvatarBarcodes { get; init; } = new List<string>(); public bool WasSubscribed { get; init; } public DateTimeOffset QueuedAt { get; init; } = DateTimeOffset.UtcNow; } public sealed record PendingDeletionProcessResult(int Completed, int Remaining, IReadOnlyList<string> Problems); public sealed class JsonFileStore<T> where T : class, new() { private readonly string _path; private readonly SemaphoreSlim _gate = new SemaphoreSlim(1, 1); private readonly JsonSerializerOptions _json = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true }; public JsonFileStore(string path) { _path = Path.GetFullPath(path); } public async Task<T> LoadAsync(CancellationToken cancellationToken = default(CancellationToken)) { await _gate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { if (!File.Exists(_path)) { return new T(); } T result; await using (FileStream stream = File.OpenRead(_path)) { result = (await JsonSerializer.DeserializeAsync<T>(stream, _json, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)) ?? new T(); } return result; } catch (JsonException) { string destFileName = _path + $".corrupt-{DateTimeOffset.UtcNow:yyyyMMddHHmmss}"; File.Move(_path, destFileName, overwrite: false); return new T(); } finally { _gate.Release(); } } public async Task SaveAsync(T value, CancellationToken cancellationToken = default(CancellationToken)) { await _gate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); try { Directory.CreateDirectory(Path.GetDirectoryName(_path)); string temporary = _path + ".tmp"; await using (FileStream stream = new FileStream(temporary, FileMode.Create, FileAccess.Write, FileShare.None)) { await JsonSerializer.SerializeAsync(stream, value, _json, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); await stream.FlushAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } File.Move(temporary, _path, overwrite: true); } finally { _gate.Release(); } } } public sealed class SearchCache { public sealed class CachedSearch { public DateTimeOffset SavedAt { get; init; } public ModIoPage<ModIoMod> Page { get; init; } = new ModIoPage<ModIoMod>(); } private readonly string _directory; public SearchCache(string directory) { _directory = Path.GetFullPath(directory); } public async Task<ModIoPage<ModIoMod>?> ReadAsync(string key, TimeSpan maximumAge, CancellationToken cancellationToken = default(CancellationToken)) { CachedSearch cachedSearch = await new JsonFileStore<CachedSearch>(PathFor(key)).LoadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); return (cachedSearch.SavedAt != default(DateTimeOffset) && DateTimeOffset.UtcNow - cachedSearch.SavedAt <= maximumAge) ? cachedSearch.Page : null; } public Task WriteAsync(string key, ModIoPage<ModIoMod> page, CancellationToken cancellationToken = default(CancellationToken)) { return new JsonFileStore<CachedSearch>(PathFor(key)).SaveAsync(new CachedSearch { SavedAt = DateTimeOffset.UtcNow, Page = page }, cancellationToken); } private string PathFor(string key) { Directory.CreateDirectory(_directory); byte[] inArray = SHA256.HashData(Encoding.UTF8.GetBytes(key)); return Path.Combine(_directory, Convert.ToHexString(inArray).ToLowerInvariant() + ".json"); } } } namespace BoneHub.Models { public sealed class ModIoPage<T> { [JsonPropertyName("data")] public List<T> Data { get; init; } = new List<T>(); [JsonPropertyName("result_count")] public int ResultCount { get; init; } [JsonPropertyName("result_offset")] public int ResultOffset { get; init; } [JsonPropertyName("result_limit")] public int ResultLimit { get; init; } [JsonPropertyName("result_total")] public int ResultTotal { get; init; } } public sealed class ModIoMod { [JsonPropertyName("id")] public long Id { get; init; } [JsonPropertyName("name")] public string Name { get; init; } = string.Empty; [JsonPropertyName("name_id")] public string NameId { get; init; } = string.Empty; [JsonPropertyName("summary")] public string Summary { get; init; } = string.Empty; [JsonPropertyName("description_plaintext")] public string Description { get; init; } = string.Empty; [JsonPropertyName("profile_url")] public string ProfileUrl { get; init; } = string.Empty; [JsonPropertyName("date_updated")] public long DateUpdated { get; init; } [JsonPropertyName("submitted_by")] public ModIoUser Author { get; init; } = new ModIoUser(); [JsonPropertyName("logo")] public ModIoLogo Logo { get; init; } = new ModIoLogo(); [JsonPropertyName("modfile")] public ModIoFile? Modfile { get; init; } [JsonPropertyName("stats")] public ModIoStats Stats { get; init; } = new ModIoStats(); [JsonPropertyName("tags")] public List<ModIoTag> Tags { get; init; } = new List<ModIoTag>(); [JsonPropertyName("maturity_option")] public int MaturityOption { get; init; } public bool HasTag(string value) { return Tags.Any((ModIoTag tag) => string.Equals(tag.Name, value, StringComparison.OrdinalIgnoreCase)); } } public sealed class ModIoUser { [JsonPropertyName("username")] public string Username { get; init; } = string.Empty; [JsonPropertyName("display_name_portal")] public string? DisplayNamePortal { get; init; } public string DisplayName { get { if (!string.IsNullOrWhiteSpace(DisplayNamePortal)) { return DisplayNamePortal; } return Username; } } } public sealed class ModIoLogo { [JsonPropertyName("original")] public string Original { get; init; } = string.Empty; [JsonPropertyName("thumb_320x180")] public string Thumbnail { get; init; } = string.Empty; } public sealed class ModIoStats { [JsonPropertyName("downloads_total")] public long DownloadsTotal { get; init; } [JsonPropertyName("subscribers_total")] public long SubscribersTotal { get; init; } [JsonPropertyName("ratings_weighted_aggregate")] public double Rating { get; init; } } public sealed class ModIoTag { [JsonPropertyName("name")] public string Name { get; init; } = string.Empty; } public sealed class ModIoFile { [JsonPropertyName("id")] public long Id { get; init; } [JsonPropertyName("mod_id")] public long ModId { get; init; } [JsonPropertyName("filename")] public string Filename { get; init; } = string.Empty; [JsonPropertyName("version")] public string Version { get; init; } = string.Empty; [JsonPropertyName("date_added")] public long DateAdded { get; init; } [JsonPropertyName("filesize")] public long FileSize { get; init; } [JsonPropertyName("filesize_uncompressed")] public long UncompressedSize { get; init; } [JsonPropertyName("virus_status")] public int VirusStatus { get; init; } [JsonPropertyName("virus_positive")] public int VirusPositive { get; init; } [JsonPropertyName("filehash")] public ModIoFileHash Hash { get; init; } = new ModIoFileHash(); [JsonPropertyName("download")] public ModIoDownload Download { get; init; } = new ModIoDownload(); [JsonPropertyName("platforms")] public List<ModIoPlatform> Platforms { get; init; } = new List<ModIoPlatform>(); } public sealed class ModIoFileHash { [JsonPropertyName("md5")] public string Md5 { get; init; } = string.Empty; } public sealed class ModIoDownload { [JsonPropertyName("binary_url")] public string BinaryUrl { get; init; } = string.Empty; [JsonPropertyName("date_expires")] public long DateExpires { get; init; } } public sealed class ModIoPlatform { [JsonPropertyName("platform")] public string Platform { get; init; } = string.Empty; [JsonPropertyName("status")] public int Status { get; init; } } public sealed class ModIoDependency { [JsonPropertyName("mod_id")] public long ModId { get; init; } [JsonPropertyName("date_added")] public long DateAdded { get; init; } } public enum ModSort { Trending, Newest, MostDownloaded, RecentlyUpdated } public sealed record ModSearchRequest(string Query, string? Tag = null, string? Creator = null, ModSort Sort = ModSort.Trending, int Offset = 0, int Limit = 20); public sealed record ModUpdateInfo(ModIoMod Mod, bool UpdateAvailable, long InstalledFileId, long LatestFileId); public sealed record ModInstallPlan(ModIoMod Root, IReadOnlyList<ModIoMod> OrderedMods, IReadOnlyList<long> AlreadyInstalledIds); public sealed record InstalledModHealth(bool Healthy, IReadOnlyList<string> Problems); } namespace BoneHub.Interaction { public enum AvatarBrowseScope { AllInstalled, Groups } public enum BrowserStartupMode { RememberLast, AllInstalled, Groups } public static class AvatarBrowseStartup { public static AvatarBrowseScope Resolve(BrowserStartupMode mode, AvatarBrowseScope lastScope) { return mode switch { BrowserStartupMode.RememberLast => Enum.IsDefined(lastScope) ? lastScope : AvatarBrowseScope.AllInstalled, BrowserStartupMode.Groups => AvatarBrowseScope.Groups, _ => AvatarBrowseScope.AllInstalled, }; } } public enum AvatarBrowserViewState { PushPrompt, Hub, Library, Pack, Downloads, Compass, CodeMods, MiniMap, Portals, PortalLevels, FusionServers, Spawnables, PackOptions, Options, Keyboard, Searching, Result, Downloading, PreparingPreview, Error } public enum AvatarPackSort { RecentlyUsed, Name, Creator } public sealed record AvatarPackEntry(long ModId, string Name, string Creator, string ThumbnailUrl, IReadOnlyList<AvatarEntry> Avatars, bool Installed, AvatarDownloadState DownloadState = AvatarDownloadState.None, double DownloadProgress = 0.0, string? Status = null) { public string Identity => "pack:" + ModId; public int AvatarCount => Avatars.Count((AvatarEntry entry) => entry.Installed && !string.IsNullOrWhiteSpace(entry.Barcode)); } public static class AvatarPackLibrary { public static IReadOnlyList<AvatarPackEntry> Group(IEnumerable<AvatarEntry> entries, IEnumerable<string>? recentBarcodes = null, AvatarPackSort sort = AvatarPackSort.RecentlyUsed) { Dictionary<string, int> recent = (recentBarcodes ?? Array.Empty<string>()).Select((string barcode, int index) => (barcode: barcode, index: index)).ToDictionary<(string, int), string, int>(((string barcode, int index) item) => item.barcode, ((string barcode, int index) item) => item.index, StringComparer.OrdinalIgnoreCase); IEnumerable<AvatarPackEntry> source = (from entry in entries group entry by entry.ModId).Select(delegate(IGrouping<long, AvatarEntry> group) { AvatarEntry[] array = group.OrderBy<AvatarEntry, string>((AvatarEntry item) => item.AvatarName, StringComparer.OrdinalIgnoreCase).ToArray(); AvatarEntry avatarEntry = array.OrderByDescending((AvatarEntry item) => item.Installed).First(); AvatarDownloadState downloadState = array.Select((AvatarEntry item) => item.DownloadState).OrderByDescending(DownloadRank).FirstOrDefault(); return new AvatarPackEntry(group.Key, avatarEntry.ModName, avatarEntry.Creator, avatarEntry.ThumbnailUrl, array, array.Any((AvatarEntry item) => item.Installed), downloadState, array.Max((AvatarEntry item) => item.DownloadProgress), array.Select((AvatarEntry item) => item.Status).FirstOrDefault((string value) => !string.IsNullOrWhiteSpace(value))); }); return sort switch { AvatarPackSort.Name => source.OrderBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Name, StringComparer.OrdinalIgnoreCase).ToArray(), AvatarPackSort.Creator => source.OrderBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Creator, StringComparer.OrdinalIgnoreCase).ThenBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Name, StringComparer.OrdinalIgnoreCase).ToArray(), _ => source.OrderBy((AvatarPackEntry pack) => RecentIndex(pack, recent)).ThenBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Name, StringComparer.OrdinalIgnoreCase).ToArray(), }; } private static int RecentIndex(AvatarPackEntry pack, IReadOnlyDictionary<string, int> recent) { int[] array = (from item in pack.Avatars where recent.ContainsKey(item.Barcode) select recent[item.Barcode]).ToArray(); if (array.Length != 0) { return array.Min(); } return int.MaxValue; } private static int DownloadRank(AvatarDownloadState state) { return state switch { AvatarDownloadState.Failed => 7, AvatarDownloadState.Installing => 6, AvatarDownloadState.Downloading => 5, AvatarDownloadState.Queued => 4, AvatarDownloadState.Cancelled => 3, AvatarDownloadState.Installed => 2, _ => 1, }; } } public sealed class FourRowPageCursor { public const int RowsPerPage = 4; public int Page { get; private set; } public int Count { get; private set; } public int PageCount => Math.Max(1, (Count + 4 - 1) / 4); public string PositionText => $"{Page + 1} / {PageCount}"; public int Reset(int count) { Count = Math.Max(0, count); Page = 0; return Page; } public int Keep(int count) { Count = Math.Max(0, count); Page = Math.Clamp(Page, 0, PageCount - 1); return Page; } public int Move(int direction) { if (PageCount <= 1 || direction == 0) { return Page; } Page = (Page + Math.Sign(direction) + PageCount) % PageCount; return Page; } } public sealed class FocusedResultCursor { public int Index { get; private set; } public int Count { get; private set; } public string PositionText { get { if (Count != 0) { return $"{Index + 1} / {Count}"; } return "0 / 0"; } } public bool CanMovePrevious => Count > 1; public bool CanMoveNext => Count > 1; public int Reset(int count) { Count = Math.Max(0, count); Index = 0; return Index; } public int Keep(int count, int preferredIndex) { Count = Math.Max(0, count); Index = ((Count != 0) ? Math.Clamp(preferredIndex, 0, Count - 1) : 0); return Index; } public int Move(int direction) { if (Count <= 1 || direction == 0) { return Index; } Index = (Index + Math.Sign(direction) + Count) % Count; return Index; } } public readonly record struct TabletRect(float X, float Y, float Width, float Height) { public float Left => X - Width * 0.5f; public float Right => X + Width * 0.5f; public float Bottom => Y - Height * 0.5f; public float Top => Y + Height * 0.5f; public bool IsInside(TabletRect parent) { if (Left >= parent.Left && Right <= parent.Right && Bottom >= parent.Bottom) { return Top <= parent.Top; } return false; } public bool Overlaps(TabletRect other) { if (Left < other.Right && Right > other.Left && Bottom < other.Top) { return Top > other.Bottom; } return false; } } public static class AvatarTabletLayout { public static readonly TabletRect Tablet = new TabletRect(0f, 0f, 340f, 210f); public static readonly TabletRect Search = new TabletRect(-34f, 82f, 150f, 30f); public static readonly TabletRect Close = new TabletRect(148f, 82f, 30f, 30f); public static readonly TabletRect Artwork = new TabletRect(-66f, -4f, 124f, 128f); public static readonly TabletRect Details = new TabletRect(65f, 3f, 126f, 112f); public static readonly TabletRect Previous = new TabletRect(-151f, 14f, 26f, 54f); public static readonly TabletRect Next = new TabletRect(151f, 14f, 26f, 54f); public static readonly TabletRect Action = new TabletRect(65f, -69f, 126f, 30f); public static readonly TabletRect Trash = new TabletRect(-66f, -91f, 116f, 18f); public static readonly TabletRect Progress = new TabletRect(65f, -91f, 126f, 14f); public static IReadOnlyList<TabletRect> ContentRects => new TabletRect[9] { Search, Close, Artwork, Details, Previous, Next, Action, Trash, Progress }; public static TabletRect PhysicalTouchRect(TabletRect rect) { return new TabletRect(rect.X, rect.Y, rect.Width * 1.1f, rect.Height * 1.2f); } } public static class ArcDashboardLayout { public static readonly TabletRect Presentation = new TabletRect(0f, 0f, 300f, 190f); public static readonly TabletRect Stage = new TabletRect(0f, 7f, 170f, 124f); public static readonly TabletRect LeftWing = new TabletRect(-122f, 16f, 50f, 146f); public static readonly TabletRect RightWing = new TabletRect(122f, 16f, 50f, 146f); public static readonly TabletRect Back = new TabletRect(-122f, 72f, 42f, 22f); public static readonly TabletRect Search = new TabletRect(-122f, 44f, 46f, 24f); public static readonly TabletRect Previous = new TabletRect(-122f, 7f, 42f, 42f); public static readonly TabletRect Scope = new TabletRect(-122f, -35f, 46f, 25f); public static readonly TabletRect Close = new TabletRect(122f, 72f, 30f, 26f); public static readonly TabletRect Pack = new TabletRect(122f, 44f, 46f, 24f); public static readonly TabletRect Next = new TabletRect(122f, 7f, 42f, 42f); public static readonly TabletRect Options = new TabletRect(122f, -35f, 46f, 25f); public static readonly TabletRect Name = new TabletRect(0f, 82f, 166f, 20f); public static readonly TabletRect Creator = new TabletRect(-45f, 65f, 78f, 14f); public static readonly TabletRect PackName = new TabletRect(45f, 65f, 92f, 14f); public static readonly TabletRect Height = new TabletRect(-54f, -53f, 94f, 16f); public static readonly TabletRect Position = new TabletRect(0f, -53f, 48f, 16f); public static readonly TabletRect Status = new TabletRect(54f, -53f, 94f, 18f); public static readonly TabletRect Action = new TabletRect(0f, -80f, 116f, 28f); public static readonly TabletRect Progress = Action; public static IReadOnlyList<TabletRect> TouchControls => new TabletRect[9] { Back, Search, Previous, Scope, Close, Pack, Next, Options, Action }; } public static class WristOsHomeHeaderLayout { public static readonly TabletRect Download = new TabletRect(-86f, 70f, 28f, 23f); public static readonly TabletRect Wordmark = new TabletRect(-25f, 70f, 88f, 20f); public static readonly TabletRect OsMark = new TabletRect(31f, 70f, 20f, 20f); public static readonly TabletRect AccentRail = new TabletRect(-19f, 56f, 120f, 1.5f); public static readonly TabletRect Settings = new TabletRect(66f, 70f, 28f, 23f); public static readonly TabletRect Close = new TabletRect(106f, 70f, 24f, 24f); public static IReadOnlyList<TabletRect> InteractiveControls => new TabletRect[3] { Download, Settings, Close }; } public static class AvatarPackTabletLayout { public static readonly IReadOnlyList<TabletRect> HeaderControls = new TabletRect[5] { new TabletRect(-145f, 83f, 42f, 28f), new TabletRect(-90f, 83f, 64f, 28f), new TabletRect(-34f, 83f, 46f, 28f), new TabletRect(25f, 83f, 68f, 28f), new TabletRect(148f, 83f, 26f, 28f) }; public static readonly IReadOnlyList<TabletRect> Rows = (from index in Enumerable.Range(0, 4) select new TabletRect(-36f, 50f - (float)index * 34f, 228f, 30f)).ToArray(); public static readonly IReadOnlyList<TabletRect> DeleteRows = (from index in Enumerable.Range(0, 4) select new TabletRect(125f, 50f - (float)index * 34f, 52f, 30f)).ToArray(); public static readonly TabletRect Status = new TabletRect(0f, -72f, 250f, 10f); public static readonly IReadOnlyList<TabletRect> FooterControls = new TabletRect[3] { new TabletRect(-135f, -91f, 50f, 26f), new TabletRect(0f, -91f, 90f, 24f), new TabletRect(135f, -91f, 50f, 26f) }; public static IReadOnlyList<TabletRect> AllControls => HeaderControls.Concat(Rows).Concat(DeleteRows).Append(Status) .Concat(FooterControls) .ToArray(); } public sealed class PresentationGeneration { public int Value { get; private set; } public int Next() { return ++Value; } public bool IsCurrent(int generation) { return generation == Value; } } public sealed class SurfaceInputRearmGate { private readonly double _clearSeconds; private double _clearSince = -1.0; public bool Required { get; private set; } public SurfaceInputRearmGate(double clearSeconds = 0.1) { _clearSeconds = Math.Max(0.0, clearSeconds); } public void Require() { Required = true; _clearSince = -1.0; } public bool CanInteract(bool touchingAnyActiveControl, double nowSeconds) { if (!Required) { return true; } if (touchingAnyActiveControl) { _clearSince = -1.0; return false; } if (_clearSince < 0.0) { _clearSince = nowSeconds; return _clearSeconds <= 0.0; } if (nowSeconds - _clearSince < _clearSeconds) { return false; } Required = false; _clearSince = -1.0; return true; } } public sealed class AvatarSearchInputSession { public int Generation { get; private set; } public bool IsOpen { get; private set; } public string InitialValue { get; private set; } = string.Empty; public int Open(string? initialValue) { InitialValue = initialValue?.Trim() ?? string.Empty; IsOpen = true; return ++Generation; } public bool TrySubmit(int generation, string? value, out string query) { query = value?.Trim() ?? string.Empty; if (!IsOpen || generation != Generation) { return false; } IsOpen = false; return true; } public bool Cancel(int generation) { if (!IsOpen || generation != Generation) { return false; } IsOpen = false; return true; } } public sealed class TabletKeyboardBuffer { public const int MaximumLength = 64; public string Value { get; private set; } = string.Empty; public bool Shifted { get; private set; } public void Set(string? value) { Value = (((value ?? string.Empty).Length <= 64) ? (value ?? string.Empty) : value.Substring(0, 64)); } public bool Append(char character) { if (Value.Length >= 64 || char.IsControl(character)) { return false; } Value += (Shifted ? char.ToUpperInvariant(character) : char.ToLowerInvariant(character)); return true; } public bool AppendSpace() { if (Value.Length >= 64 || Value.EndsWith(' ')) { return false; } Value += " "; return true; } public bool Backspace() { if (Value.Length == 0) { return false; } string value = Value; Value = value.Substring(0, value.Length - 1); return true; } public void Clear() { Value = string.Empty; } public void ToggleShift() { Shifted = !Shifted; } public string VisibleValue(int maximumCharacters = 36) { int num = Math.Clamp(maximumCharacters, 4, 64); if (Value.Length > num) { string value = Value; int num2 = num - 1; int length = value.Length; int num3 = length - num2; return "…" + value.Substring(num3, length - num3); } return Value; } } public static class PreviewDragMath { public static bool IsInside(Vector3 localPoint, Vector3 halfExtents) { if (MathF.Abs(localPoint.X) <= MathF.Max(0f, halfExtents.X) && MathF.Abs(localPoint.Y) <= MathF.Max(0f, halfExtents.Y)) { return MathF.Abs(localPoint.Z) <= MathF.Max(0f, halfExtents.Z); } return false; } public static bool PinchActive(float thumbIndexDistance, bool alreadyDragging, float beginDistance = 0.03f, float releaseDistance = 0.045f) { if (float.IsFinite(thumbIndexDistance)) { return thumbIndexDistance <= (alreadyDragging ? MathF.Max(beginDistance, releaseDistance) : beginDistance); } return false; } public static bool GripActive(float gripForce, bool alreadyDragging, float beginForce = 0.62f, float releaseForce = 0.28f) { if (float.IsFinite(gripForce)) { return gripForce >= (alreadyDragging ? MathF.Min(beginForce, releaseForce) : beginForce); } return false; } public static bool IsChestRelease(Vector3 previewPosition, Vector3 chestPosition, float radius) { if (radius > 0f) { return Vector3.Distance(previewPosition, chestPosition) <= radius; } return false; } public static Vector3 LimitAnchorOffset(Vector3 offset, float maximumDistance) { if (!float.IsFinite(offset.X) || !float.IsFinite(offset.Y) || !float.IsFinite(offset.Z)) { return Vector3.Zero; } float num = Math.Clamp(maximumDistance, 0.01f, 0.2f); float num2 = offset.LengthSquared(); if (!float.IsFinite(num2) || num2 <= 0f) { return Vector3.Zero; } if (num2 <= num * num) { return offset; } return Vector3.Normalize(offset) * num; } } public static class ChestEquipGuideMath { public static bool ResolveLocked(float distance, bool wasLocked, float radius, float releaseMultiplier = 1.12f) { if (!float.IsFinite(distance) || !float.IsFinite(radius) || radius <= 0f) { return false; } float num = (wasLocked ? (radius * Math.Clamp(releaseMultiplier, 1f, 1.5f)) : radius); return distance <= num; } public static float Proximity(float distance, float radius, float visibleRadiusMultiplier = 2.75f) { if (!float.IsFinite(distance) || !float.IsFinite(radius) || radius <= 0f) { return 0f; } float num = radius * Math.Clamp(visibleRadiusMultiplier, 1.1f, 6f); if (distance <= radius) { return 1f; } return Math.Clamp(1f - (distance - radius) / (num - radius), 0f, 1f); } } public sealed class LookAwayDismissal { public double AwaySeconds { get; private set; } public bool Update(bool observed, double deltaSeconds, double delaySeconds = 0.35) { if (observed) { AwaySeconds = 0.0; return false; } AwaySeconds += Math.Clamp(deltaSeconds, 0.0, 0.25); return AwaySeconds >= Math.Max(0.05, delaySeconds); } public void Reset() { AwaySeconds = 0.0; } } public readonly record struct AvatarPreviewMetrics(string Barcode, float GameplayHeightMeters, bool Valid) { public string DisplayText { get { if (!Valid || !float.IsFinite(GameplayHeightMeters) || !(GameplayHeightMeters > 0f)) { return "Height unavailable"; } if (!(GameplayHeightMeters < 10f)) { if (GameplayHeightMeters < 100f) { return $"{GameplayHeightMeters:0.0} m"; } return $"{GameplayHeightMeters:#,##0} m"; } return $"{GameplayHeightMeters:0.00} m"; } } public string HeightDisplayText { get { if (!Valid) { return "Avatar height unavailable"; } return "Avatar height " + DisplayText; } } } public enum AvatarPreviewVisualSource { Unavailable, FullColor, NativePreviewMesh } public enum AvatarPreviewHeightSource { Unavailable, AvatarMetadata, RendererBounds, PreviewMeshBounds } public readonly record struct AvatarResolvedHeight(float Meters, AvatarPreviewHeightSource Source) { public bool Valid => AvatarPreviewMath.IsDisplayableGameplayHeight(Meters); } public readonly record struct AvatarPreviewRouteMetadata(AvatarPreviewVisualSource VisualSource, AvatarPreviewHeightSource HeightSource, bool InteractionReady); public readonly record struct AvatarPreviewPlacement(float UniformScale, Vector3 Offset) { public bool Valid { get { if (float.IsFinite(UniformScale) && UniformScale > 0f && float.IsFinite(Offset.X) && float.IsFinite(Offset.Y)) { return float.IsFinite(Offset.Z); } return false; } } } public static class AvatarPreviewMath { public const float DisplayHeightMeters = 0.16f; public const float DisplayWidthMeters = 0.135f; public const float DisplayDepthMeters = 0.1f; public const float FrontFacingYawDegrees = 180f; public static float NormalizeScale(float visualHeightMeters, float displayHeightMeters = 0.16f) { if (!IsUsableHeight(visualHeightMeters)) { return 1f; } return Math.Clamp(displayHeightMeters, 0.05f, 0.3f) / visualHeightMeters; } public static bool IsUsableHeight(float value) { if (float.IsFinite(value)) { if (value >= 0.05f) { return value <= 100f; } return false; } return false; } public static bool IsDisplayableGameplayHeight(float value) { if (float.IsFinite(value)) { if (value >= 0.01f) { return value <= 10000f; } return false; } return false; } public static float NormalizeBoundsScale(Vector3 visualSize) { if (!float.IsFinite(visualSize.Y) || visualSize.Y <= 0.0001f) { return 1f; } return 0.16f / visualSize.Y; } public static AvatarPreviewPlacement FitBounds(Vector3 visualCenter, Vector3 visualSize) { float num = NormalizeBoundsScale(visualSize); return new AvatarPreviewPlacement(num, -visualCenter * num); } public static AvatarResolvedHeight ResolveHeight(float avatarMetadataHeight, float rendererBoundsHeight, float previewMeshBoundsHeight = 0f) { if (IsUsableHeight(avatarMetadataHeight)) { return new AvatarResolvedHeight(avatarMetadataHeight, AvatarPreviewHeightSource.AvatarMetadata); } if (IsUsableHeight(rendererBoundsHeight)) { return new AvatarResolvedHeight(rendererBoundsHeight, AvatarPreviewHeightSource.RendererBounds); } if (IsUsableHeight(previewMeshBoundsHeight)) { return new AvatarResolvedHeight(previewMeshBoundsHeight, AvatarPreviewHeightSource.PreviewMeshBounds); } return new AvatarResolvedHeight(0f, AvatarPreviewHeightSource.Unavailable); } public static AvatarResolvedHeight ResolveGameplayHeight(float selectedAvatarHeight, float fallbackAvatarHeight) { if (IsDisplayableGameplayHeight(selectedAvatarHeight)) { return new AvatarResolvedHeight(selectedAvatarHeight, AvatarPreviewHeightSource.AvatarMetadata); } if (IsDisplayableGameplayHeight(fallbackAvatarHeight)) { return new AvatarResolvedHeight(fallbackAvatarHeight, AvatarPreviewHeightSource.AvatarMetadata); } return new AvatarResolvedHeight(0f, AvatarPreviewHeightSource.Unavailable); } public static bool IsVisualRendererType(string? typeName) { if (!string.IsNullOrWhiteSpace(typeName) && !typeName.Contains("Particle", StringComparison.OrdinalIgnoreCase) && !typeName.Contains("Trail", StringComparison.OrdinalIgnoreCase)) { return !typeName.Contains("LineRenderer", StringComparison.OrdinalIgnoreCase); } return false; } public static bool IsSafePreview(float visualHeightMeters, int rendererCount, int vertexCount, bool constrained) { int num = (constrained ? 24 : 64); int num2 = (constrained ? 350000 : 1250000); if (IsUsableHeight(visualHeightMeters) && rendererCount > 0 && rendererCount <= num && vertexCount >= 0) { return vertexCount <= num2; } return false; } } public static class AvatarUiScaleMath { public const float ReferencePlayerHeightMeters = 1.75f; public static float ResolveMenuScale(float playerHeightMeters) { if (!AvatarPreviewMath.IsUsableHeight(playerHeightMeters)) { return 1f; } return playerHeightMeters / 1.75f; } public static float ResolveDetachedContentScale(float tabletWorldScale) { if (!float.IsFinite(tabletWorldScale) || !(tabletWorldScale > 0.0001f)) { return 1f; } return tabletWorldScale; } } public readonly record struct NativeMenuPlacement(Vector3 Position, Vector3 Forward, float DistanceMeters); public static class NativeMenuPlacementMath { public const float ReferenceDistanceMeters = 1.05f; public static Vector3 ResolveForward(Vector3 headForward, Vector3 fallbackForward) { Vector3 value = new Vector3(headForward.X, 0f, headForward.Z); if (value.LengthSquared() < 0.04f) { value = new Vector3(fallbackForward.X, 0f, fallbackForward.Z); } if (value.LengthSquared() < 0.0001f) { value = Vector3.UnitZ; } return Vector3.Normalize(value); } public static NativeMenuPlacement Resolve(Vector3 headPosition, Vector3 headForward, float avatarHeightMeters) { Vector3 vector = ResolveForward(headForward, Vector3.UnitZ); float num = Math.Clamp(AvatarUiScaleMath.ResolveMenuScale(avatarHeightMeters), 0.65f, 1.75f); float num2 = 1.05f * num; return new NativeMenuPlacement(headPosition + vector * num2 - Vector3.UnitY * (0.06f * num), vector, num2); } } public static class DownloadPresentationMath { public static double Smooth(double displayed, double authoritative, double deltaSeconds, double speed = 8.0) { double num = Math.Clamp(displayed, 0.0, 1.0); double num2 = Math.Clamp(authoritative, 0.0, 1.0); if (num2 <= num || deltaSeconds <= 0.0) { return Math.Min(num, (num2 < num) ? num : num2); } double num3 = 1.0 - Math.Exp((0.0 - Math.Max(0.1, speed)) * Math.Min(deltaSeconds, 0.25)); double num4 = num + (num2 - num) * num3; if (!(num2 - num4 < 0.0005)) { return Math.Min(num4, num2); } return num2; } } public static class FingertipProbeMath { public static Vector3 ControllerTip(Vector3 position, Vector3 forward, float reachMeters = 0.075f) { Vector3 vector = ((forward.LengthSquared() > 0.0001f) ? Vector3.Normalize(forward) : Vector3.UnitZ); return position + vector * Math.Clamp(reachMeters, 0f, 0.15f); } public static Vector3 Extrapolate(Vector3 intermediate, Vector3 distal, float distalFraction = 0.65f) { Vector3 value = distal - intermediate; float num = value.Length(); if (!(num > 0.0001f)) { return distal; } return distal + Vector3.Normalize(value) * num * Math.Clamp(distalFraction, 0f, 1.5f); } } public static class WorldTextFacingMath { public const float FrontFacingLocalYawDegrees = 0f; public static bool IsFrontFacing(float localYawDegrees) { return Math.Abs(Math.IEEERemainder(localYawDegrees - 0f, 360.0)) < 0.0010000000474974513; } public static bool FacesViewer(Vector3 rootForward, Vector3 directionToViewer, float localYawDegrees) { if (rootForward.LengthSquared() < 0.0001f || directionToViewer.LengthSquared() < 0.0001f) { return false; } Vector3 vector = Vector3.Normalize(rootForward); Vector3 value = Vector3.Cross(Vector3.UnitY, vector); value = ((!(value.LengthSquared() < 0.0001f)) ? Vector3.Normalize(value) : Vector3.UnitX); float x = localYawDegrees * (float)Math.PI / 180f; return Vector3.Dot(Vector3.Normalize(-vector * MathF.Cos(x) - value * MathF.Sin(x)), Vector3.Normalize(directionToViewer)) > 0.999f; } } public enum AvatarRestoreDecision { NativeSaveOwnsSelection, WaitForNativeSave, RestorePrivateFallback, NoRestore } public static class AvatarRestorePolicy { public static AvatarRestoreDecision Decide(bool nativeSaveReadable, bool nativeSaveWaitExpired, string? privateFallbackBarcode) { if (nativeSaveReadable) { return AvatarRestoreDecision.NativeSaveOwnsSelection; } if (!nativeSaveWaitExpired) { return AvatarRestoreDecision.WaitForNativeSave; } if (!string.IsNullOrWhiteSpace(privateFallbackBarcode)) { return AvatarRestoreDecision.RestorePrivateFallback; } return AvatarRestoreDecision.NoRestore; } } public enum AvatarRigCompatibility { UnverifiedAllowed, Verified, Invalid } public readonly record struct AvatarRigValidationResult(AvatarRigCompatibility Compatibility, string Reason) { public bool CanEquip => Compatibility != AvatarRigCompatibility.Invalid; public bool IsVerified => Compatibility == AvatarRigCompatibility.Verified; public static AvatarRigValidationResult Ready() { return new AvatarRigValidationResult(AvatarRigCompatibility.Verified, string.Empty); } public static AvatarRigValidationResult Allow(string reason) { return new AvatarRigValidationResult(AvatarRigCompatibility.UnverifiedAllowed, reason); } public static AvatarRigValidationResult Reject(string reason) { return new AvatarRigValidationResult(AvatarRigCompatibility.Invalid, reason); } } public enum AvatarSharingState { Unregistered, Registering, Ready, Announced, PlatformLimited, Failed } public readonly record struct AvatarPlatformAvailability(long? WindowsFileId, long? AndroidFileId) { public bool HasWindows { get { long? windowsFileId = WindowsFileId; if (windowsFileId.HasValue) { return windowsFileId.GetValueOrDefault() > 0; } return false; } } public bool HasAndroid { get { long? androidFileId = AndroidFileId; if (androidFileId.HasValue) { return androidFileId.GetValueOrDefault() > 0; } return false; } } public bool HasAny { get { if (!HasWindows) { return HasAndroid; } return true; } } public bool IsLimited { get { if (HasAny) { if (HasWindows) { return !HasAndroid; } return true; } return false; } } public string Badge { get { if (!HasWindows || !HasAndroid) { if (!HasWindows) { if (!HasAndroid) { return "LOCAL ONLY"; } return "QUEST ONLY"; } return "PC ONLY"; } return "PC + QUEST"; } } } public readonly record struct CompassPresentation(float HeadingDegrees, float SmoothedHeadingDegrees, float NorthOffsetDegrees, string Cardinal, string SceneKey, bool HasCalibration); public static class CompassHeadingMath { private static readonly string[] Cardinals = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" }; public static float Normalize(float degrees) { if (!float.IsFinite(degrees)) { return 0f; } float num = degrees % 360f; if (!(num < 0f)) { return num; } return num + 360f; } public static float Heading(float worldYawDegrees, float northOffsetDegrees) { return Normalize(worldYawDegrees - northOffsetDegrees); } public static float SignedDelta(float fromDegrees, float toDegrees) { float num = Normalize(toDegrees) - Normalize(fromDegrees); if (num > 180f) { num -= 360f; } if (num < -180f) { num += 360f; } return num; } public static float Smooth(float current, float target, float deltaSeconds, float response = 12f) { if (!float.IsFinite(deltaSeconds) || deltaSeconds <= 0f) { return Normalize(current); } float num = 1f - MathF.Exp((0f - MathF.Max(0.01f, response)) * deltaSeconds); return Normalize(current + SignedDelta(current, target) * num); } public static string Cardinal(float headingDegrees) { int num = (int)MathF.Floor((Normalize(headingDegrees) + 22.5f) / 45f) % Cardinals.Length; return Cardinals[num]; } public static float NeedleRotation(float headingDegrees) { return 0f - Normalize(headingDegrees); } } public static class CompassCalibrationStore { public const int MaximumRecords = 64; public static CompassCalibrationRecord? Find(IReadOnlyList<CompassCalibrationRecord> records, string sceneKey) { return records.FirstOrDefault((CompassCalibrationRecord record) => string.Equals(record.SceneKey, sceneKey, StringComparison.OrdinalIgnoreCase)); } public static void Upsert(List<CompassCalibrationRecord> records, string sceneKey, float northYaw, DateTimeOffset usedAt) { string sceneKey2 = (string.IsNullOrWhiteSpace(sceneKey) ? "unknown-scene" : sceneKey.Trim()); CompassCalibrationRecord compassCalibrationRecord = Find(records, sceneKey2); if (compassCalibrationRecord != null) { compassCalibrationRecord.NorthYawDegrees = CompassHeadingMath.Normalize(northYaw); compassCalibrationRecord.LastUsedUtc = usedAt; } else { records.Add(new CompassCalibrationRecord { SceneKey = sceneKey2, NorthYawDegrees = CompassHeadingMath.Normalize(northYaw), LastUsedUtc = usedAt }); } while (records.Count > 64) { CompassCalibrationRecord item = records.OrderBy((CompassCalibrationRecord record) => record.LastUsedUtc).First(); records.Remove(item); } } } public enum HologramCarouselRole { Previous, Selected, Next } public enum HologramAvatarMaterialMode { BlueHologram, FullColor } public readonly record struct HologramCarouselSlot(HologramCarouselRole Role, int Index, string Identity, float HorizontalOffset, float Scale, HologramAvatarMaterialMode MaterialMode, bool InteractionReady); public static class HologramCarouselPresentation { public const float SideOffsetMeters = 0.105f; public const float SideScale = 0.62f; public static IReadOnlyList<HologramCarouselSlot> Build(IReadOnlyList<string> identities, int selectedIndex) { if (identities.Count == 0) { return Array.Empty<HologramCarouselSlot>(); } int num = Wrap(selectedIndex, identities.Count); if (identities.Count != 1) { int index = Wrap(num - 1, identities.Count); int index2 = Wrap(num + 1, identities.Count); return new HologramCarouselSlot[3] { Slot(HologramCarouselRole.Previous, index, identities[index], -0.105f, 0.62f, interactive: false), Slot(HologramCarouselRole.Selected, num, identities[num], 0f, 1f, interactive: true), Slot(HologramCarouselRole.Next, index2, identities[index2], 0.105f, 0.62f, interactive: false) }; } return new HologramCarouselSlot[1] { Slot(HologramCarouselRole.Selected, num, identities[num], 0f, 1f, interactive: true) }; } public static int Wrap(int index, int count) { if (count <= 0) { return 0; } int num = index % count; if (num >= 0) { return num; } return num + count; } private static HologramCarouselSlot Slot(HologramCarouselRole role, int index, string identity, float offset, float scale, bool interactive) { return new HologramCarouselSlot(role, index, identity, offset, scale, interactive ? HologramAvatarMaterialMode.FullColor : HologramAvatarMaterialMode.BlueHologram, interactive); } } public enum CinematicProjectionPhase { Hidden, Charging, RingExpansion, SideAvatarAssembly, CenterAvatarAssembly, Stable, CarouselTransition, PanelTransition, Collapsing, TrackingGrace } public readonly record struct CinematicProjectionProgress(CinematicProjectionPhase Phase, float Emitter, float Rings, float SideAvatars, float CenterAvatar, float Controls); public static class CinematicProjectionTimeline { public const float ChargeEnd = 0.15f; public const float RingEnd = 0.35f; public const float SideEnd = 0.45f; public const float CenterEnd = 0.55f; public const float CollapseSeconds = 0.35f; public static CinematicProjectionProgress Resolve(float elapsed, bool reducedMotion) { if (reducedMotion) { return new CinematicProjectionProgress(CinematicProjectionPhase.Stable, 1f, 1f, 1f, 1f, 1f); } float num = Math.Max(0f, elapsed); return new CinematicProjectionProgress((num < 0.15f) ? CinematicProjectionPhase.Charging : ((num < 0.35f) ? CinematicProjectionPhase.RingExpansion : ((num < 0.45f) ? CinematicProjectionPhase.SideAvatarAssembly : ((num < 0.55f) ? CinematicProjectionPhase.CenterAvatarAssembly : CinematicProjectionPhase.Stable))), Smooth(num / 0.15f), Smooth((num - 0.0825f) / 0.26749998f), Smooth((num - 0.22749999f) / 0.2225f), Smooth((num - 0.35f) / 0.20000002f), Smooth((num - 0.35099998f) / 0.19900003f)); } private static float Smooth(float value) { float num = Math.Clamp(value, 0f, 1f); return num * num * (3f - 2f * num); } } public static class HoloInteractionMath { public static int WrapIndex(int current, int delta, int count) { if (count <= 0) { return 0; } return ((current + delta) % count + count) % count; } public static float SmoothStep01(float value) { float num = Math.Clamp(value, 0f, 1f); return num * num * (3f - 2f * num); } public static bool IsIntentionalFlick(float lateralVelocity, float threshold) { return Math.Abs(lateralVelocity) >= Math.Max(0f, threshold); } } public enum HoloPerformanceTier { Full, Balanced, Minimal } public sealed class HoloPerformanceGovernor { private readonly bool _constrained; private double _averageFrameMs; private int _slowSamples; private int _fastSamples; public HoloPerformanceTier Tier { get; private set; } public int RendererLimit => Tier switch { HoloPerformanceTier.Full => 64, HoloPerformanceTier.Balanced => 24, _ => 12, }; public bool EffectsAllowed => Tier != HoloPerformanceTier.Minimal; public double AverageFrameMs => _averageFrameMs; public HoloPerformanceGovernor(bool constrained) { _constrained = constrained; Tier = (constrained ? HoloPerformanceTier.Balanced : HoloPerformanceTier.Full); } public bool Sample(double frameMilliseconds) { if (!_constrained || double.IsNaN(frameMilliseconds) || double.IsInfinity(frameMilliseconds)) { return false; } double num = Math.Clamp(frameMilliseconds, 1.0, 100.0); _averageFrameMs = ((_averageFrameMs <= 0.0) ? num : (_averageFrameMs * 0.94 + num * 0.06)); if (_averageFrameMs >= 21.0) { _slowSamples++; _fastSamples = 0; } else if (_averageFrameMs <= 15.5) { _fastSamples++; _slowSamples = 0; } else { _slowSamples = Math.Max(0, _slowSamples - 1); _fastSamples = Math.Max(0, _fastSamples - 1); } if (Tier == HoloPerformanceTier.Balanced && _slowSamples >= 45) { Tier = HoloPerformanceTier.Minimal; _slowSamples = 0; return true; } if (Tier == HoloPerformanceTier.Minimal && _fastSamples >= 240) { Tier = HoloPerformanceTier.Balanced; _fastSamples = 0; return true; } return false; } } public enum InstalledPackFocusState { None, Downloading, WaitingForPallet, Ready, TimedOut } public sealed class InstalledPackFocus { public long ModId { get; private set; } public int Generation { get; private set; } public double DeadlineSeconds { get; private set; } public InstalledPackFocusState State { get; private set; } public string SelectedBarcode { get; private set; } = string.Empty; public IReadOnlyList<string> InstallDirectories { get; private set; } = Array.Empty<string>(); public bool Active => State != InstalledPackFocusState.None; public int Begin(long modId, double nowSeconds, IEnumerable<string>? installDirectories = null, double timeoutSeconds = 12.0) { Generation++; ModId = modId; DeadlineSeconds = nowSeconds + Math.Max(1.0, timeoutSeconds); State = InstalledPackFocusState.WaitingForPallet; SelectedBarcode = string.Empty; InstallDirectories = (installDirectories ?? Array.Empty<string>()).Where((string path) => !string.IsNullOrWhiteSpace(path)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); return Generation; } public int BeginDownload(long modId) { Generation++; ModId = modId; DeadlineSeconds = double.PositiveInfinity; State = InstalledPackFocusState.Downloading; SelectedBarcode = string.Empty; InstallDirectories = Array.Empty<string>(); return Generation; } public void MarkDownloaded(long modId, double nowSeconds, IEnumerable<string>? installDirectories, double timeoutSeconds = 12.0) { if (modId == ModId && State != InstalledPackFocusState.None) { UpdateDirectories(modId, installDirectories); DeadlineSeconds = nowSeconds + Math.Max(1.0, timeoutSeconds); State = InstalledPackFocusState.WaitingForPallet; } } public void UpdateDirectories(long modId, IEnumerable<string>? installDirectories) { if (modId == ModId && State != InstalledPackFocusState.None) { InstallDirectories = (installDirectories ?? Array.Empty<string>()).Where((string path) => !string.IsNullOrWhiteSpace(path)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); } } public bool TryBind(long modId, IEnumerable<string?> barcodes) { if (modId != ModId || State == InstalledPackFocusState.None) { return false; } string text = barcodes.FirstOrDefault((string value) => !string.IsNullOrWhiteSpace(value))?.Trim(); if (string.IsNullOrWhiteSpace(text)) { return false; } SelectedBarcode = text; State = InstalledPackFocusState.Ready; return true; } public bool UpdateTimeout(double nowSeconds) { if (State != InstalledPackFocusState.WaitingForPallet || nowSeconds < DeadlineSeconds) { return false; } State = InstalledPackFocusState.TimedOut; return true; } public void Retry(double nowSeconds, double timeoutSeconds = 12.0) { if (Active) { DeadlineSeconds = nowSeconds + Math.Max(1.0, timeoutSeconds); State = InstalledPackFocusState.WaitingForPallet; } } public void Clear() { ModId = 0L; DeadlineSeconds = 0.0; State = InstalledPackFocusState.None; SelectedBarcode = string.Empty; InstallDirectories = Array.Empty<string>(); } } public sealed class LibraryRefreshGate { public bool Pending { get; private set; } public double NotBeforeSeconds { get; private set; } public LibraryRefreshGate(bool initiallyPending = true) { Pending = initiallyPending; } public void Request(double nowSeconds, double debounceSeconds = 0.35) { double num = nowSeconds + Math.Max(0.0, debounceSeconds); NotBeforeSeconds = (Pending ? Math.Max(NotBeforeSeconds, num) : num); Pending = true; } public bool TryConsume(double nowSeconds) { if (!Pending || nowSeconds < NotBeforeSeconds) { return false; } Pending = false; NotBeforeSeconds = 0.0; return true; } } public readonly record struct MiniMapOverlayColor(byte R, byte G, byte B, byte A = byte.MaxValue) { public static readonly MiniMapOverlayColor Outline = new MiniMapOverlayColor(0, 15, 24, 235); } public enum MiniMapPeerRole { Player, Operator, Owner } public static class MiniMapPeerColors { public static readonly MiniMapOverlayColor Cyan = new MiniMapOverlayColor(44, 220, byte.MaxValue); public static readonly MiniMapOverlayColor OperatorViolet = new MiniMapOverlayColor(190, 112, byte.MaxValue); public static readonly MiniMapOverlayColor OwnerAmber = new MiniMapOverlayColor(byte.MaxValue, 184, 46); public static readonly MiniMapOverlayColor Above = new MiniMapOverlayColor(117, byte.MaxValue, 184); public static readonly MiniMapOverlayColor Below = new MiniMapOverlayColor(92, 158, byte.MaxValue, 235); public static MiniMapOverlayColor Resolve(MiniMapPeerRole role, MiniMapAltitudeBand altitude) { return role switch { MiniMapPeerRole.Owner => OwnerAmber, MiniMapPeerRole.Operator => OperatorViolet, _ => altitude switch { MiniMapAltitudeBand.Above => Above, MiniMapAltitudeBand.Below => Below, _ => Cyan, }, }; } } public readonly record struct MiniMapOverlayMarker(Vector2 NormalizedPosition, float RotationDegrees, MiniMapOverlayColor Color, float Scale = 1f); public static class MiniMapPeerOverlayRaster { public const int DefaultTextureSize = 192; private const int ReferenceTextureSize = 96; private const int AntiAliasSamples = 3; public static int Render(Span<byte> rgba, int width, int height, ReadOnlySpan<MiniMapOverlayMarker> markers) { if (width <= 0 || height <= 0 || rgba.Length < width * height * 4) { throw new ArgumentOutOfRangeException("rgba"); } rgba.Slice(0, width * height * 4).Clear(); int written = 0; float num = MathF.Min(width, height) * 0.4f; float num2 = MathF.Min(width, height) / 96f; for (int i = 0; i < markers.Length; i++) { MiniMapOverlayMarker miniMapOverlayMarker = markers[i]; Vector2 normalizedPosition = miniMapOverlayMarker.NormalizedPosition; float num3 = normalizedPosition.Length(); if (float.IsFinite(num3)) { if (num3 > 1f && num3 > float.Epsilon) { normalizedPosition /= num3; } float centerX = (float)(width - 1) * 0.5f + normalizedPosition.X * num; float centerY = (float)(height - 1) * 0.5f + normalizedPosition.Y * num; float scale = Math.Clamp(float.IsFinite(miniMapOverlayMarker.Scale) ? miniMapOverlayMarker.Scale : 1f, 0.45f, 1.65f) * num2; DrawPlane(rgba, width, height, centerX, centerY, miniMapOverlayMarker.RotationDegrees, scale, miniMapOverlayMarker.Color, ref written); } } return written; } private static void DrawPlane(Span<byte> pixels, int width, int height, float centerX, float centerY, float rotationDegrees, float scale, MiniMapOverlayColor color, ref int written) { float x = rotationDegrees * (float)Math.PI / 180f; float cos = MathF.Cos(x); float sin = MathF.Sin(x); Vector2 a = Rotate(new Vector2(0f, 6.4f) * scale, cos, sin) + new Vector2(centerX, centerY); Vector2 b = Rotate(new Vector2(-5f, -4.8f) * scale, cos, sin) + new Vector2(centerX, centerY); Vector2 vector = Rotate(new Vector2(0f, -1.4f) * scale, cos, sin) + new Vector2(centerX, centerY); Vector2 c = Rotate(new Vector2(5f, -4.8f) * scale, cos, sin) + new Vector2(centerX, centerY); DrawTriangle(pixels, width, height, a, b, vector, color, ref written); DrawTriangle(pixels, width, height, a, vector, c, color, ref written); } private static Vector2 Rotate(Vector2 value, float cos, float sin) { return new Vector2(value.X * cos - value.Y * sin, value.X * sin + value.Y * cos); } private static void DrawTriangle(Span<byte> pixels, int width, int height, Vector2 a, Vector2 b, Vector2 c, MiniMapOverlayColor color, ref int written) { int num = Math.Clamp((int)MathF.Floor(MathF.Min(a.X, MathF.Min(b.X, c.X))), 0, width - 1); int num2 = Math.Clamp((int)MathF.Ceiling(MathF.Max(a.X, MathF.Max(b.X, c.X))), 0, width - 1); int num3 = Math.Clamp((int)MathF.Floor(MathF.Min(a.Y, MathF.Min(b.Y, c.Y))), 0, height - 1); int num4 = Math.Clamp((int)MathF.Ceiling(MathF.Max(a.Y, MathF.Max(b.Y, c.Y))), 0, height - 1); for (int i = num3; i <= num4; i++) { for (int j = num; j <= num2; j++) { int num5 = 0; for (int k = 0; k < 3; k++) { for (int l = 0; l < 3; l++) { if (InsideTriangle(new Vector2((float)j + ((float)l + 0.5f) / 3f, (float)i + ((float)k + 0.5f) / 3f), a, b, c)) { num5++; } } } if (num5 != 0) { int offset = (i * width + j) * 4; int sourceAlpha = (color.A * num5 + 4) / 9; BlendPixel(pixels, offset, color, sourceAlpha, ref written); } } } } private static bool InsideTriangle(Vector2 point, Vector2 a, Vector2 b, Vector2 c) { float num = Cross(b - a, point - a); float num2 = Cross(c - b, point - b); float num3 = Cross(a - c, point - c); if (!(num >= 0f) || !(num2 >= 0f) || !(num3 >= 0f)) { if (num <= 0f && num2 <= 0f) { return num3 <= 0f; } return false; } return true; } private static void BlendPixel(Span<byte> pixels, int offset, MiniMapOverlayColor color, int sourceAlpha, ref int written) { if (sourceAlpha > 0) { byte b = pixels[offset + 3]; if (b == 0) { written++; } int num = 255 - sourceAlpha; int num2 = sourceAlpha + (b * num + 127) / 255; if (num2 > 0) { pixels[offset] = BlendChannel(color.R, pixels[offset], sourceAlpha, b, num, num2); pixels[offset + 1] = BlendChannel(color.G, pixels[offset + 1], sourceAlpha, b, num, num2); pixels[offset + 2] = BlendChannel(color.B, pixels[offset + 2], sourceAlpha, b, num, num2); pixels[offset + 3] = (byte)num2; } } } private static byte BlendChannel(byte source, byte destination, int sourceAlpha, int destinationAlpha, int inverseAlpha, int outputAlpha) { return (byte)Math.Clamp((source * sourceAlpha + (destination * destinationAlpha * inverseAlpha + 127) / 255 + outputAlpha / 2) / outputAlpha, 0, 255); } private static float Cross(Vector2 left, Vector2 right) { return left.X * right.Y - left.Y * right.X; } } public enum MiniMapRange { TenMeters = 10, TwentyMeters = 20, TwentyFiveMeters = 25, ThirtyMeters = 30, FortyMeters = 40, FiftyMeters = 50 } public enum MiniMapOrientation { HeadingUp, NorthUp } public enum MiniMapMarkerKind { LocalPlayer, FusionPeer, Waypoint } public enum MiniMap
Mods\WristHub.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using BoneHub.Api; using BoneHub.Avatars; using BoneHub.Compatibility; using BoneHub.Configuration; using BoneHub.Downloads; using BoneHub.Installation; using BoneHub.Interaction; using BoneHub.Mod; using BoneHub.Models; using BoneHub.Persistence; using BoneLib; using BoneLib.BoneMenu; using Il2CppCysharp.Threading.Tasks; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Bonelab; using Il2CppSLZ.Bonelab.SaveData; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Data; using Il2CppSLZ.Marrow.Forklift.Model; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.SaveData; using Il2CppSLZ.Marrow.SceneStreaming; using Il2CppSLZ.Marrow.Utilities; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSLZ.VRMK; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppTMPro; using LabFusion.Data; using LabFusion.Entities; using LabFusion.Marrow; using LabFusion.Marrow.Patching; using LabFusion.Menu; using LabFusion.Network; using LabFusion.Player; using LabFusion.RPC; using LabFusion.Representation; using LabFusion.SDK.Lobbies; using LabFusion.SDK.Modules; using LabFusion.Scene; using MelonLoader; using MelonLoader.Utils; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; using WristHub.SDK; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(BoneHubMod), "WristHubV4", "4.0.1", "WristHub contributors", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: AssemblyMetadata("WristHubPlatform", "QuestStandalone")] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyCompany("WristHub")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("4.0.1.0")] [assembly: AssemblyInformationalVersion("4.0.1")] [assembly: AssemblyProduct("WristHub")] [assembly: AssemblyTitle("WristHub")] [assembly: AssemblyVersion("4.0.1.0")] [module: RefSafetyRules(11)] namespace BoneHub.Mod; internal sealed class BoneHubAvatarBrowser : IDisposable { private enum ButtonIconKind { None, Back, Forward, Close, Search, Settings, Download, Avatar, Apps, Map, Compass, Portal, Fusion, Players, Gamemode, QuickJoin, Home, Refresh, Equip, Delete, Join } private sealed class TouchButton { public Transform Space { get; init; } public GameObject Root { get; init; } public Image Panel { get; init; } public TMP_Text Label { get; init; } public Vector3 Center { get; init; } public Vector3 Half { get; init; } public Action Action { get; init; } public TouchDebounce Debounce { get; } = new TouchDebounce(); public bool IsAlive { get { try { return (Object)(object)Root != (Object)null && (Object)(object)Space != (Object)null && (Object)(object)Panel != (Object)null && (Object)(object)Label != (Object)null; } catch { return false; } } } public bool Contains(Vector3 worldPoint) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Root == (Object)null || (Object)(object)Space == (Object)null || !Root.activeInHierarchy) { return false; } Vector3 val = Space.InverseTransformPoint(worldPoint) - Center; return Mathf.Abs(val.x) <= Half.x && Mathf.Abs(val.y) <= Half.y && Mathf.Abs(val.z) <= Half.z; } catch { return false; } } } private sealed class PackRow { public TouchButton Button { get; init; } public TouchButton DeleteButton { get; init; } public RawImage Artwork { get; init; } public TMP_Text Name { get; init; } public TMP_Text Creator { get; init; } public TMP_Text State { get; init; } public string Identity { get; set; } = string.Empty; public int Generation { get; set; } public bool IsAlive { get { if (Button.IsAlive && DeleteButton.IsAlive && (Object)(object)Artwork != (Object)null && (Object)(object)Name != (Object)null && (Object)(object)Creator != (Object)null) { return (Object)(object)State != (Object)null; } return false; } } } private enum IconPrimitiveType { Line, Ring, Disc, Box } private readonly record struct IconPrimitive(IconPrimitiveType Type, Vector2 A, Vector2 B, float Radius, float Stroke); private enum PortalPlacementStage { None, LocalEntrance, LocalExit, WorldEntrance, FusionServerEntrance } private enum WristKeyboardPurpose { Avatars, CodeMods, Spawnables, PortalLevels, FusionServerCode, CodeModString, SdkText } private enum SpawnableBrowseMode { All, Groups, Recent, Favorites } private enum CodeModCatalogMode { All, Community, Managers, BoneMenu, Loaded } private sealed class OsControlRow { public TouchButton Main { get; init; } public TouchButton Minus { get; init; } public TouchButton Plus { get; init; } public TMP_Text Detail { get; init; } public object? Source { get; set; } } private sealed class MiniMapPeerVisual { public int PeerId { get; set; } = -1; public bool Active { get; set; } public Vector3 TargetWorldPosition { get; set; } public Vector3 WorldPosition { get; set; } public float TargetHeadingDegrees { get; set; } public float HeadingDegrees { get; set; } public MiniMapPeerRole Role { get; set; } public Transform? TrackingTransform { get; set; } public bool HasDisplayPose { get; set; } public float LastSeenAt { get; set; } } private sealed record CodeModSnapshot(string Name, string Detail, object? Page, object? Element = null); private sealed record LoadedCodeModSnapshot(string Name, string Author, string Version); private sealed class ReflectedManagerControl { private readonly Func<string> _detail; private readonly Action _activate; private readonly Action<int>? _adjust; public string Label { get; } public string Detail => _detail(); public bool Adjustable => _adjust != null; public ReflectedManagerControl(string label, Func<string> detail, Action activate, Action<int>? adjust = null) { Label = label; _detail = detail; _activate = activate; _adjust = adjust; } public void Activate() { _activate(); } public void Adjust(int direction) { _adjust?.Invoke(direction); } } private sealed class RuntimeSdkHost : IWristHubHost { private readonly BoneHubAvatarBrowser _owner; public WristHubRuntimeInfo Runtime => new WristHubRuntimeInfo(true, _owner._constrainedRendering, IsFusionConnected(), ((object)_owner._preferences.HologramTheme/*cast due to .constrained prefix*/).ToString(), 2); public RuntimeSdkHost(BoneHubAvatarBrowser owner) { _owner = owner; } public void Notify(string appId, string message, WristHubNotificationKind kind) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) _owner._sdkNotifications.Enqueue(new SdkNotification(appId, message, kind)); } public void RequestOpen(string appId) { _owner._sdkOpenRequests.Enqueue(appId); } } private readonly record struct SdkNotification(string AppId, string Message, WristHubNotificationKind Kind) { [CompilerGenerated] public void Deconstruct(out string AppId, out string Message, out WristHubNotificationKind Kind) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected I4, but got Unknown AppId = this.AppId; Message = this.Message; Kind = (WristHubNotificationKind)(int)this.Kind; } } private sealed class RuntimeWristOsApp : IWristOsApp { private readonly Action _open; public WristOsAppId Id { get; } public string DisplayName { get; } public bool IsAvailable => true; public int NotificationCount => 0; public RuntimeWristOsApp(WristOsAppId id, string name, Action open) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) Id = id; DisplayName = name; _open = open; } public void Open() { _open(); } public void Close() { } public void Tick(double nowSeconds) { } public bool HandleBack() { return false; } } private const float ChestRadius = 0.22f; private static readonly Color Glass = new Color(0.012f, 0.027f, 0.04f, 1f); private static readonly Color Surface = new Color(0.025f, 0.075f, 0.105f, 0.96f); private static readonly Color Green = new Color(0.32f, 0.9f, 0.62f, 1f); private static readonly Color Amber = new Color(1f, 0.67f, 0.22f, 1f); private static readonly Color Red = new Color(1f, 0.38f, 0.46f, 1f); private readonly BoneHubService _service; private readonly BoneHubRuntime _runtime; private readonly BoneHubThumbnailCache _thumbnails; private readonly ConcurrentQueue<Action> _mainThread; private readonly BoneHubPreferences _preferences; private readonly bool _constrainedRendering; private readonly Instance _log; private readonly LobbyAvatarSharingBridge _sharing; private readonly List<TouchButton> _buttons = new List<TouchButton>(); private readonly Dictionary<int, TouchButton> _touchCaptures = new Dictionary<int, TouchButton>(); private readonly HashSet<int> _activeProbeIds = new HashSet<int>(); private readonly List<int> _staleCaptureIds = new List<int>(2); private readonly Dictionary<long, ModIoMod> _onlineMods = new Dictionary<long, ModIoMod>(); private readonly Dictionary<long, DownloadJob> _jobs = new Dictionary<long, DownloadJob>(); private readonly Dictionary<long, IReadOnlyList<Guid>> _downloadBatches = new Dictionary<long, IReadOnlyList<Guid>>(); private readonly Dictionary<string, AvatarPreviewMetrics> _previewMetrics = new Dictionary<string, AvatarPreviewMetrics>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, AvatarPreviewRouteMetadata> _previewRoutes = new Dictionary<string, AvatarPreviewRouteMetadata>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, AvatarRigValidationResult> _rigValidations = new Dictionary<string, AvatarRigValidationResult>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<long> _startingDownloads = new HashSet<long>(); private readonly BoneHubFingertipProbeProvider _fingertips = BoneHubFingertipProbeProvider.Shared; private readonly TabletKeyboardBuffer _keyboard = new TabletKeyboardBuffer(); private readonly LookAwayDismissal _promptDismissal = new LookAwayDismissal(); private readonly FocusedResultCursor _cursor = new FocusedResultCursor(); private readonly FourRowPageCursor _packCursor = new FourRowPageCursor(); private readonly PresentationGeneration _artworkGeneration = new PresentationGeneration(); private readonly ErrorLogThrottle _runtimeErrorThrottle = new ErrorLogThrottle(); private readonly SurfaceInputRearmGate _inputRearm = new SurfaceInputRearmGate(0.1); private readonly InstalledPackFocus _installedFocus = new InstalledPackFocus(); private readonly BoneHubForearmProjectionAnchorProvider _projectionAnchors = new BoneHubForearmProjectionAnchorProvider(); private readonly List<PackRow> _packRows = new List<PackRow>(); private readonly Action _projectionTickAction; private readonly Action _projectionRecoveryAction; private readonly Action _touchTickAction; private readonly Action _touchRecoveryAction; private readonly Action _installationFocusTickAction; private readonly Action _previewTickAction; private readonly Action _previewRecoveryAction; private readonly Action _downloadTickAction; private readonly Action _sharingTickAction; private readonly Action _compassTickAction; private readonly Action<PortalLinkState> _portalStateReceiver; private readonly Action<int> _portalRemoveReceiver; private IReadOnlyList<AvatarEntry> _entries = Array.Empty<AvatarEntry>(); private IReadOnlyList<AvatarEntry> _mergedEntries = Array.Empty<AvatarEntry>(); private IReadOnlyList<AvatarEntry> _onlineEntries = Array.Empty<AvatarEntry>(); private IReadOnlyList<AvatarPackEntry> _packs = Array.Empty<AvatarPackEntry>(); private GameObject? _root; private GameObject? _launcher; private GameObject? _hub; private GameObject? _browser; private GameObject? _canvasRoot; private GameObject? _packPage; private GameObject? _detailPage; private GameObject? _optionsPage; private GameObject? _watchPlacementPage; private GameObject? _downloadsPage; private GameObject? _packOptionsPage; private GameObject? _keyboardPage; private GameObject? _deletePage; private GameObject? _compassPage; private GameObject? _denseGlassChrome; private GameObject? _denseGlassBackground; private GameObject? _denseGlassOverlay; private bool _wristLocalPoseValid; private int _wristLocalHandId; private Vector3 _wristLocalPosition; private float _wristLocalHeightRatio; private float _wristLocalWatchScale; private float _wristLocalOutwardOffset; private float _wristLocalFingerOffset; private GameObject? _leftArcWing; private GameObject? _rightArcWing; private GameObject? _metadataArc; private CanvasGroup? _canvasGroup; private TMP_Text? _packTitle; private TMP_Text? _packStatus; private TMP_Text? _packPosition; private TMP_Text? _packSearchText; private TMP_Text? _sortText; private TMP_Text? _wristText; private TMP_Text? _scaleText; private TMP_Text? _motionText; private TMP_Text? _themeText; private TMP_Text? _clockText; private TMP_Text? _startingViewText; private TMP_Text? _watchForearmText; private TMP_Text? _scopeText; private TMP_Text? _downloadsSummary; private TMP_Text? _deleteTitle; private TMP_Text? _deleteDetails; private TMP_Text? _keyboardText; private TMP_Text? _keyboardTitle; private TMP_Text? _keyboardShiftText; private TMP_Text? _searchText; private TMP_Text? _compassHeadingText; private TMP_Text? _compassCalibrationText; private RectTransform? _compassNeedle; private RectTransform? _compassDecorRing; private TMP_Text? _positionText; private TMP_Text? _selectedName; private TMP_Text? _creatorText; private TMP_Text? _modText; private TMP_Text? _statusText; private TMP_Text? _heightText; private GameObject? _heightRuler; private GameObject? _progressRoot; private RectTransform? _progressFill; private TMP_Text? _progressText; private TMP_Text? _actionLabel; private Image? _actionPanel; private TouchButton? _trashButton; private RawImage? _centralArtwork; private Texture2D? _placeholderTexture; private Texture2D? _roundedTexture; private Sprite? _roundedSprite; private BoneHubForearmProjection? _hologramProjection; private BoneHubHologramCarousel? _hologramCarousel; private BoneHubChestEquipGuide? _chestEquipGuide; private TouchButton? _actionButton; private GameObject? _previewShell; private GameObject? _previewFallback; private GameObject? _previewModel; private readonly List<Mesh> _ownedPreviewMeshes = new List<Mesh>(); private BoneHubAvatar? _previewAvatar; private BoneHubAvatar? _pendingPreviewAvatar; private string _loadedPreviewBarcode = string.Empty; private string _requestedPreviewBarcode = string.Empty; private float _previewLoadAt; private bool _previewReady; private bool _previewInteractionReady; private AvatarPreviewVisualSource _previewVisualSource; private bool _previewHeld; private Hand? _dragHand; private bool _dragIsLeft; private bool _dragUsesPinch; private Vector3 _dragLocalOffset; private Quaternion _dragRotationOffset = Quaternion.identity; private float _dragTrackingLostAt = -1f; private bool _dragWasInsideChest; private bool _previewReturning; private float _returnStarted; private Vector3 _returnFrom; private Quaternion _returnRotation; private int _previewGeneration; private int _searchGeneration; private string _query = string.Empty; private bool _downloadsOnly; private string _selectedIdentity = string.Empty; private string _trashConfirmIdentity = string.Empty; private bool _deleteReturnToPackPage; private bool _trashInProgress; private long? _selectedPackId; private AvatarPackSort _packSort; private AvatarBrowseScope _scope; private AvatarBrowseScope _scopeBeforeSearch; private Guid _progressJobId; private double _displayedProgress; private double _targetProgress; private int _lastProgressPercent = -1; private AvatarDownloadState? _lastProgressState; private AvatarBrowserViewState _viewState = (AvatarBrowserViewState)2; private AvatarBrowserViewState _viewStateBeforeKeyboard = (AvatarBrowserViewState)2; private bool _disposed; private bool _wasPlayerDead; private AvatarSharingState _sharingState; private BoneHubAvatar? _pendingSharingAvatar; private ModIoSharingMetadata? _pendingSharingMetadata; private string _sharingRetryBarcode = string.Empty; private float _nextSharingCheckAt; private float _sharingDeadline; private bool _sharingRefreshRequested; private string _carouselIdentity = string.Empty; private float _hubControlsReadyAt; private HologramDashboardLifecycle _lifecycle; private float _lifecycleStartedAt; private bool _closeNotificationPending; private HologramDashboardProgress _closeVisualStart = new HologramDashboardProgress((HologramProjectionPhase)5, 1f, 1f, 1f, 1f, 1f, true); private string _compassSceneKey = "unknown-scene"; private float _compassSmoothedHeading; private bool _compassHeadingValid; private int _lastCompassDegree = -1; private GameObject? _fusionHomePage; private GameObject? _fusionPlayersPage; private TMP_Text? _fusionHomeTitle; private TMP_Text? _fusionHomeSummary; private TMP_Text? _fusionHomeStatus; private TouchButton? _fusionDisconnectButton; private readonly List<TouchButton> _fusionPlayerRows = new List<TouchButton>(4); private readonly List<RawImage> _fusionPlayerImages = new List<RawImage>(4); private readonly int[] _fusionPlayerImageGenerations = new int[4]; private readonly long[] _fusionPlayerImageModIds = new long[4]; private FusionServerEntry? _fusionPlayersSource; private int _fusionPlayerPageIndex; private TMP_Text? _fusionPlayerPosition; private TMP_Text? _fusionPlayerStatus; private readonly Dictionary<ButtonIconKind, Sprite> _buttonIconSprites = new Dictionary<ButtonIconKind, Sprite>(); private readonly List<Texture2D> _buttonIconTextures = new List<Texture2D>(); private Sprite? _miniMapRingSprite; private Sprite? _miniMapMaskSprite; private readonly RaycastHit[] _portalRayHits = (RaycastHit[])(object)new RaycastHit[12]; private readonly PortalTraversalGuard _portalEntranceGuard = new PortalTraversalGuard(); private readonly PortalTraversalGuard _portalExitGuard = new PortalTraversalGuard(); private readonly List<PortalDestinationEntry> _portalLevels = new List<PortalDestinationEntry>(); private readonly List<PortalDestinationEntry> _installedPortalLevels = new List<PortalDestinationEntry>(); private readonly List<FusionServerEntry> _fusionServers = new List<FusionServerEntry>(); private readonly List<TouchButton> _fusionServerRows = new List<TouchButton>(); private readonly List<RawImage> _fusionServerRowArtwork = new List<RawImage>(); private readonly string[] _fusionServerRowArtworkCodes = new string[4]; private readonly int[] _fusionServerRowArtworkGenerations = new int[4]; private readonly Dictionary<string, LevelCrate> _portalLevelCrates = new Dictionary<string, LevelCrate>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<long, ModIoMod> _portalOnlineMods = new Dictionary<long, ModIoMod>(); private GameObject? _portalsPage; private GameObject? _portalLevelsPage; private GameObject? _fusionServersPage; private GameObject? _fusionServerDetailsPage; private TMP_Text? _portalStatus; private TMP_Text? _portalModeText; private TouchButton? _portalLocalButton; private TouchButton? _portalWorldButton; private TouchButton? _portalRemoveButton; private TouchButton? _portalLevelsButton; private TouchButton? _portalServersButton; private TouchButton? _portalLevelActionButton; private TMP_Text? _portalLevelName; private TMP_Text? _portalLevelDetails; private TMP_Text? _portalLevelPosition; private TMP_Text? _portalLevelStatus; private TouchButton? _fusionServerActionButton; private TouchButton? _fusionServerJoinButton; private RawImage? _fusionServerArtwork; private TMP_Text? _fusionServerListPosition; private TMP_Text? _fusionServerListStatus; private TMP_Text? _fusionServerName; private TMP_Text? _fusionServerDetails; private TMP_Text? _fusionServerPosition; private TMP_Text? _fusionServerStatus; private TMP_Text? _fusionServerDescription; private TMP_Text? _fusionServerMembers; private PortalPlacementStage _portalPlacementStage; private PortalEndpoint? _pendingPortalEntrance; private PortalLinkState? _activePortal; private bool _activePortalLocallyCreated; private int _portalGeneration; private bool _portalTriggerHeld; private float _portalPointerReadyAt; private LineRenderer? _portalPointerLine; private GameObject? _portalPointerReticle; private Renderer? _portalPointerRenderer; private Material? _portalMaterial; private MaterialPropertyBlock? _portalPropertyBlock; private BoneHubPortalVisual? _portalVisual; private Vector3 _previousPortalHead; private bool _hasPreviousPortalHead; private float _nextPortalLevelScan; private string _selectedPortalLevelBarcode = string.Empty; private int _portalLevelIndex; private string _portalLevelQuery = string.Empty; private int _portalLevelSearchGeneration; private int _fusionServerRequestGeneration; private int _fusionServerIndex; private int _fusionServerPageIndex; private string _selectedFusionServerCode = string.Empty; private string _fusionServerCodeInput = string.Empty; private int _fusionServerArtworkGeneration; private string _fusionServerArtworkCode = string.Empty; private long _pendingPortalLevelModId; private float _pendingPortalLevelDeadline; private float _nextPortalInstallScan; private readonly HashSet<string> _portalLevelBarcodesBeforeDownload = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static TMP_FontAsset? _osWordmarkFont; private static bool _osWordmarkFontResolved; private readonly List<OsControlRow> _codeModRows = new List<OsControlRow>(4); private readonly List<CodeModSnapshot> _codeModItems = new List<CodeModSnapshot>(); private readonly List<LoadedCodeModSnapshot> _loadedCodeMods = new List<LoadedCodeModSnapshot>(); private readonly ConcurrentQueue<string> _sdkOpenRequests = new ConcurrentQueue<string>(); private readonly ConcurrentQueue<SdkNotification> _sdkNotifications = new ConcurrentQueue<SdkNotification>(); private const int MiniMapTextureSize = 32; private readonly Color[] _miniMapPixels = (Color[])(object)new Color[1024]; private readonly float[] _miniMapHeights = new float[1024]; private readonly byte[] _miniMapCellState = new byte[1024]; private readonly List<MiniMapPeerVisual> _miniMapPeerVisuals = new List<MiniMapPeerVisual>(16); private readonly List<GameObject> _miniMapPortalDots = new List<GameObject>(2); private readonly List<SpawnableEntry> _spawnableEntries = new List<SpawnableEntry>(); private readonly List<SpawnableEntry> _spawnableCatalog = new List<SpawnableEntry>(); private readonly Dictionary<string, SpawnableCrate> _spawnableCrates = new Dictionary<string, SpawnableCrate>(StringComparer.OrdinalIgnoreCase); private readonly RaycastHit[] _miniMapHits = (RaycastHit[])(object)new RaycastHit[12]; private readonly RaycastHit[] _spawnPointerHits = (RaycastHit[])(object)new RaycastHit[16]; private readonly List<GameObject> _spawnedObjects = new List<GameObject>(32); private readonly WristOsRouter _osRouter = new WristOsRouter(); private readonly FusionMiniMapBridge _miniMapFusion = new FusionMiniMapBridge(); private readonly List<FusionMiniMapPeer> _miniMapPeers = new List<FusionMiniMapPeer>(16); private readonly List<Renderer> _miniMapSuppressedRenderers = new List<Renderer>(64); private GameObject? _codeModsPage; private GameObject? _miniMapPage; private GameObject? _spawnablesPage; private GameObject? _notificationsPage; private TMP_Text? _osStatusText; private TMP_Text? _codeModTitle; private TMP_Text? _codeModPosition; private TouchButton? _codeModCommunityButton; private TMP_Text? _miniMapHeading; private TMP_Text? _miniMapRangeText; private TMP_Text? _miniMapScanText; private TouchButton? _miniMapModeButton; private TMP_Text? _miniMapRangeValue; private TMP_Text? _spawnableName; private TMP_Text? _spawnableDetails; private TMP_Text? _spawnablePosition; private TMP_Text? _spawnableStatus; private TMP_Text? _notificationText; private GameObject? _spawnablePreview; private GameObject? _spawnableNativePreview; private GameObject? _spawnPlacementPreview; private GameObject? _spawnPointerReticle; private LineRenderer? _spawnPointerLine; private TouchButton? _spawnActionButton; private TouchButton? _spawnableModeButton; private TouchButton? _spawnableFavoriteButton; private TouchButton? _spawnDeleteButton; private Mesh? _spawnablePreviewMesh; private MiniMapPeerOverlay? _miniMapPeerOverlay; private RectTransform? _miniMapViewport; private RectTransform? _miniMapGeometryRoot; private Image? _miniMapLayoutImage; private Texture2D? _miniMapLayoutTexture; private Sprite? _miniMapLayoutSprite; private GameObject? _miniMapCaptureObject; private Camera? _miniMapCaptureCamera; private RenderTexture? _miniMapCaptureTexture; private RenderTexture? _miniMapCaptureBackTexture; private RawImage? _miniMapSceneImage; private RectTransform? _miniMapBearingRoot; private RectTransform? _miniMapMarkerRoot; private object? _activeBoneMenuPage; private object? _managerControlCachePage; private IReadOnlyList<ReflectedManagerControl> _managerControlCache = Array.Empty<ReflectedManagerControl>(); private float _managerControlCacheUntil; private object? _activeCodeModStringElement; private WristHubApp? _activeSdkApp; private WristHubPage? _activeSdkPage; private WristHubTextInput? _activeSdkText; private RuntimeSdkHost? _sdkHost; private bool _showLegacyBoneMenu; private bool _optionsReturnToHub; private string _latestSdkNotification = string.Empty; private WristKeyboardPurpose _wristKeyboardPurpose; private string _codeModQuery = string.Empty; private CodeModCatalogMode _codeModCatalogMode; private string _spawnableQuery = string.Empty; private TouchButton? _keyboardSubmitButton; private int _codeModPageIndex; private int _spawnableIndex; private int _spawnablePreviewGeneration; private int _spawnableManifestCount = -1; private bool _spawnPlacementActive; private bool _spawnDeleteActive; private bool _spawnTriggerHeld; private Hand? _spawnPlacementHand; private float _spawnPlacementYaw; private float _spawnPlacementDistance = 1.5f; private GameObject? _spawnDeleteTarget; private GameObject? _pendingGrabObject; private Hand? _pendingGrabHand; private float _pendingGrabDeadline; private float _nextPendingGrabAttempt; private float _spawnPointerReadyAt; private Material? _spawnPointerMaterial; private MaterialPropertyBlock? _spawnPointerBlock; private Renderer? _spawnPointerRenderer; private SpawnableBrowseMode _spawnableMode; private float _nextCodeModRefresh; private float _nextLoadedCodeModScan; private string _lastCodeModCatalogSignature = string.Empty; private float _nextMiniMapPeerRefresh; private float _nextMiniMapOverlayRender; private float _nextMiniMapOverlayRecovery; private bool _miniMapOverlayDiagnosticLogged; private bool _miniMapOverlayRecovering; private float _nextSpawnableRefresh; private int _lastMiniMapDegree = -1; private int _lastMiniMapPeerCount = -1; private int _miniMapRasterCursor; private int _miniMapRasterSamplesRemaining; private bool _miniMapTextureDirty; private float _nextMiniMapTextureUpload; private bool _miniMapCaptureValid; private Vector3 _miniMapCaptureCenter; private Vector3 _miniMapPendingCaptureCenter; private Vector3 _miniMapScanOrigin; private float _miniMapReferenceFloorY; private bool _miniMapScanOriginValid; private Color Cyan => WristHubThemePalette.Accent(_preferences.HologramTheme); private Color DimCyan => WristHubThemePalette.Dim(_preferences.HologramTheme); public bool IsOpen { get { if ((Object)(object)_browser != (Object)null) { return _browser.activeSelf; } return false; } } public bool IsLauncherOpen { get { if ((Object)(object)_root != (Object)null) { return _root.activeSelf; } return false; } } public bool IsClosing => (int)_lifecycle == 3; public string Query => _query; public event Action? Closed; public BoneHubAvatarBrowser(BoneHubService service, BoneHubRuntime runtime, BoneHubThumbnailCache thumbnails, ConcurrentQueue<Action> mainThread, BoneHubPreferences preferences, bool constrainedRendering, Instance log, LobbyAvatarSharingBridge? sharing = null) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Expected O, but got Unknown //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Expected O, but got Unknown //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Expected O, but got Unknown _service = service; _runtime = runtime; _thumbnails = thumbnails; _mainThread = mainThread; _preferences = preferences; _constrainedRendering = constrainedRendering; _log = log; _sharing = sharing ?? new LobbyAvatarSharingBridge(log); _projectionTickAction = TickProjectionStage; _projectionRecoveryAction = RecoverProjectionStage; _touchTickAction = TickTouchStage; _touchRecoveryAction = ResetTouchButtons; _installationFocusTickAction = UpdateInstalledPackFocus; _previewTickAction = ProcessPendingPreview; _previewRecoveryAction = RecoverPreviewStage; _downloadTickAction = UpdateDownloadProgress; _sharingTickAction = TickSharingRegistration; _compassTickAction = TickCompass; _portalStateReceiver = ReceivePortalState; _portalRemoveReceiver = ReceivePortalRemoval; } public void OpenLauncher() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) EnsureBuilt(); if (!((Object)(object)_root == (Object)null) && !((Object)(object)_hub == (Object)null) && !((Object)(object)_browser == (Object)null)) { _wristLocalPoseValid = false; FollowWrist(immediate: true); _root.SetActive(true); if ((Object)(object)_launcher != (Object)null) { _launcher.SetActive(false); } _hub.SetActive(true); _browser.SetActive(false); _hologramCarousel?.SetVisible(visible: false); _viewState = (AvatarBrowserViewState)1; _osRouter.ShowHome(); _promptDismissal.Reset(); RefreshEntries(); StartHologramProjection(); _hubControlsReadyAt = Time.unscaledTime + (_preferences.ReducedMotion ? 0f : 0.55f); RequireInputRearm(); _log.Msg("WristHub compact module hub projection opened."); } } public void Open() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) OpenLauncher(); if (!((Object)(object)_browser == (Object)null)) { if ((Object)(object)_launcher != (Object)null) { _launcher.SetActive(false); } if ((Object)(object)_hub != (Object)null) { _hub.SetActive(false); } _browser.SetActive(true); StartHologramProjection(); _query = string.Empty; _downloadsOnly = false; _selectedPackId = null; _scope = AvatarBrowseStartup.Resolve(_preferences.AvatarBrowserStartupMode, _preferences.LastAvatarBrowseScope); _scopeBeforeSearch = _scope; _viewState = (AvatarBrowserViewState)2; RememberScope(); RefreshEntries(); ShowScopePage(); } } public void OpenSearch() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) OpenLauncher(); if (!((Object)(object)_browser == (Object)null)) { if ((Object)(object)_launcher != (Object)null) { _launcher.SetActive(false); } if ((Object)(object)_hub != (Object)null) { _hub.SetActive(false); } _browser.SetActive(true); StartHologramProjection(); _selectedPackId = null; _scope = AvatarBrowseStartup.Resolve(_preferences.AvatarBrowserStartupMode, _preferences.LastAvatarBrowseScope); _scopeBeforeSearch = _scope; RefreshEntries(); OpenKeyboard(); } } public void Close() { BeginClose(_preferences.ReducedMotion); } private void BeginClose(bool immediate) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) HologramDashboardLifecycle lifecycle = _lifecycle; if (((int)lifecycle != 0 && (int)lifecycle != 3) || 1 == 0) { if (_previewHeld) { EndPreviewDrag(equipIfAtChest: false); } CancelSpawnPlacement(); _osRouter.BeginClose(); ResetTouchButtons(); ResetTrashConfirmation(); CancelPendingSharingRegistration(); _closeVisualStart = CurrentVisibleProgress(); _lifecycle = (HologramDashboardLifecycle)3; _lifecycleStartedAt = Time.unscaledTime; _closeNotificationPending = true; _hologramProjection?.Hide(immediate); _chestEquipGuide?.Hide(immediate: true); _artworkGeneration.Next(); _previewGeneration++; _requestedPreviewBarcode = string.Empty; _pendingPreviewAvatar = null; _promptDismissal.Reset(); if (immediate) { FinalizeClose(); } } } private void FinalizeClose() { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } if ((Object)(object)_browser != (Object)null) { _browser.SetActive(false); } if ((Object)(object)_launcher != (Object)null) { _launcher.SetActive(false); } if ((Object)(object)_hub != (Object)null) { _hub.SetActive(false); } if ((Object)(object)_previewShell != (Object)null) { _previewShell.SetActive(false); } _hologramCarousel?.SetVisible(visible: false); _hologramProjection?.Hide(immediate: true); _loadedPreviewBarcode = string.Empty; _previewReady = false; _previewInteractionReady = false; _lifecycle = (HologramDashboardLifecycle)0; if (_closeNotificationPending) { _closeNotificationPending = false; this.Closed?.Invoke(); } } public void SetQuery(string value) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 1f; } if ((Object)(object)_root != (Object)null) { _root.SetActive(true); } if ((Object)(object)_launcher != (Object)null) { _launcher.SetActive(false); } if ((Object)(object)_hub != (Object)null) { _hub.SetActive(false); } if ((Object)(object)_browser != (Object)null) { _browser.SetActive(true); } _query = value?.Trim() ?? string.Empty; _downloadsOnly = false; _onlineEntries = Array.Empty<AvatarEntry>(); _viewState = (AvatarBrowserViewState)((_query.Length == 0) ? 2 : 15); _log.Msg($"Avatar search submitted ({_query.Length} characters). Query text is not logged."); RefreshEntries(); if (_query.Length == 0) { ShowScopePage(); } else { _scope = (AvatarBrowseScope)0; _selectedPackId = null; RefreshEntries(); ShowDetailPage(); } int generation = ++_searchGeneration; if (_query.Length != 0 && _service.ModIoReady) { SetStatus("Searching BONELAB mod.io…", Cyan); SearchOnlineAsync(_query, generation); } } public void Refresh() { RefreshEntries(); RefreshVisiblePage(); } public void Select(string identity) { AvatarEntry val = ((IEnumerable<AvatarEntry>)_entries).FirstOrDefault((Func<AvatarEntry, bool>)((AvatarEntry item) => string.Equals(item.Identity, identity, StringComparison.OrdinalIgnoreCase))); if (!(val == (AvatarEntry)null)) { _selectedIdentity = val.Identity; _cursor.Keep(_entries.Count, IndexOf(val.Identity)); ShowDetailPage(); RefreshSelection(val); } } public void OnDownloadChanged(DownloadJob job) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Invalid comparison between Unknown and I4 //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Invalid comparison between Unknown and I4 //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Invalid comparison between Unknown and I4 //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected I4, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) _jobs[job.Mod.Id] = job; if ((int)job.State == 6) { _log.Warning("WristHub download failed: " + Readable(job.Error ?? "Unknown download failure.")); } else if ((int)job.State == 5) { _log.Msg("WristHub download and safe installation completed."); } GameObject? downloadsPage = _downloadsPage; if (downloadsPage != null && downloadsPage.activeSelf) { RefreshDownloadsPage(); } DownloadState state = job.State; if (state - 5 <= 2) { _startingDownloads.Remove(job.Mod.Id); } if ((int)job.State == 5 && _installedFocus.Active && _installedFocus.ModId == job.Mod.Id) { InstalledPackFocus installedFocus = _installedFocus; long id = job.Mod.Id; double num = Time.unscaledTime; InstalledModRecord result = job.Result; installedFocus.MarkDownloaded(id, num, (IEnumerable<string>)((result != null) ? result.InstalledDirectories : null), 12.0); _log.Msg("Downloaded root pack completed; waiting for its runtime avatar crates."); } else { state = job.State; bool flag = state - 6 <= 1; if (flag && _installedFocus.ModId == job.Mod.Id) { _installedFocus.Clear(); } } RefreshEntries(); AvatarEntry? obj = Selected(); if (((obj != null) ? new long?(obj.ModId) : ((long?)null)) == job.Mod.Id) { state = job.State; _viewState = (AvatarBrowserViewState)((state - 5) switch { 1 => 19, 0 => 18, 2 => 16, _ => 17, }); RefreshVisiblePage(); } } public void OnRuntimeStatusChanged(string status) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) GameObject? browser = _browser; if (browser != null && browser.activeSelf && !string.IsNullOrWhiteSpace(status)) { bool flag = status.Contains("failed", StringComparison.OrdinalIgnoreCase) || status.Contains("could not", StringComparison.OrdinalIgnoreCase) || status.Contains("rejected", StringComparison.OrdinalIgnoreCase); SetStatus(status, flag ? Red : (status.Contains("sharing", StringComparison.OrdinalIgnoreCase) ? Cyan : Green)); } } public void OnEquipFinished(BoneHubAvatar avatar, bool success) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (success) { AvatarEntry? obj = Selected(); if (string.Equals((obj != null) ? obj.Barcode : null, avatar.Barcode, StringComparison.OrdinalIgnoreCase) && _previewMetrics.TryGetValue(avatar.Barcode, out var value)) { SetHeight(((AvatarPreviewMetrics)(ref value)).HeightDisplayText, ((AvatarPreviewMetrics)(ref value)).Valid ? Green : Red); } } } public void OnAvatarsChanged(long modId, IReadOnlyList<BoneHubAvatar> avatars) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) _previewMetrics.Clear(); _previewRoutes.Clear(); _rigValidations.Clear(); if (avatars.Any((BoneHubAvatar avatar) => string.Equals(avatar.Barcode, _loadedPreviewBarcode, StringComparison.OrdinalIgnoreCase))) { _loadedPreviewBarcode = string.Empty; _requestedPreviewBarcode = string.Empty; _pendingPreviewAvatar = null; _previewReady = false; _previewInteractionReady = false; _previewVisualSource = (AvatarPreviewVisualSource)0; } _fingertips.Invalidate(); _projectionAnchors.Invalidate(); _hologramProjection?.BeginTrackingGrace(); RefreshEntries(); if (_installedFocus.TryBind(modId, avatars.Select((BoneHubAvatar avatar) => avatar.Barcode))) { _selectedPackId = modId; _query = string.Empty; _onlineEntries = Array.Empty<AvatarEntry>(); _downloadsOnly = false; IReadOnlyList<BoneHubAvatar> avatars2 = _runtime.GetAvatars(modId); _entries = ((avatars2.Count > 0) ? avatars2 : avatars).Select((BoneHubAvatar avatar) => avatar.ToEntry()).OrderBy<AvatarEntry, string>((AvatarEntry entry) => entry.AvatarName, StringComparer.OrdinalIgnoreCase).ToArray(); _selectedIdentity = _entries.First((AvatarEntry entry) => string.Equals(entry.Barcode, _installedFocus.SelectedBarcode, StringComparison.OrdinalIgnoreCase)).Identity; _cursor.Keep(_entries.Count, IndexOf(_selectedIdentity)); ShowDetailPage(); SetStatus("Downloaded - ready to equip", Green); _log.Msg($"Downloaded avatar pack bound to {avatars.Count} runtime crate{((avatars.Count == 1) ? string.Empty : "s")}; Equip is ready."); return; } if (avatars.Count > 0) { AvatarEntry? obj = Selected(); if (obj != null && obj.ModId == modId) { AvatarEntry? obj2 = Selected(); if (string.IsNullOrWhiteSpace((obj2 != null) ? obj2.Barcode : null)) { _selectedIdentity = avatars[0].ToEntry().Identity; RefreshEntries(); } } } RefreshVisiblePage(); } public void OnSceneChanged(string sceneName = "") { //IL_00be: Unknown result type (might be due to invalid IL or missing references) _compassSceneKey = NormalizeSceneKey(sceneName); _compassHeadingValid = false; _lastCompassDegree = -1; _fingertips.Invalidate(); _projectionAnchors.Invalidate(); _wristLocalPoseValid = false; _hologramProjection?.BeginTrackingGrace(); ResetTouchButtons(); OnWristOsSceneChanged(); if ((Object)(object)_root == (Object)null) { return; } bool reportRecovery = PreviewShellRecovery.RequiresRebuild((Object)(object)_previewShell != (Object)null, (Object)(object)_previewFallback != (Object)null); if (EnsurePreviewShell(reportRecovery)) { ClearPreviewModel(); _loadedPreviewBarcode = string.Empty; _requestedPreviewBarcode = string.Empty; _pendingPreviewAvatar = null; _previewReady = false; _previewInteractionReady = false; _previewVisualSource = (AvatarPreviewVisualSource)0; GameObject? detailPage = _detailPage; if (detailPage != null && detailPage.activeSelf) { RefreshFocusedResult(); } } } public void Tick() { //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) _hologramProjection?.Tick(_preferences.ReducedMotion); BoneHubHologramCarousel? hologramCarousel = _hologramCarousel; if (hologramCarousel != null) { float assemblyProgress = _hologramProjection?.SideAvatarProgress ?? 1f; bool reducedMotion = _preferences.ReducedMotion; BoneHubForearmProjection? hologramProjection = _hologramProjection; hologramCarousel.Tick(assemblyProgress, reducedMotion, (HoloPerformanceTier)((hologramProjection == null) ? (_constrainedRendering ? 1 : 0) : ((int)hologramProjection.PerformanceTier))); } _chestEquipGuide?.Tick(_preferences.ReducedMotion); if (IsLocalPlayerDead()) { if (!_wasPlayerDead && _activePortal != (PortalLinkState)null) { RemovePortal(IsFusionHost(), hideImmediately: true); } if (!_wasPlayerDead && IsLauncherOpen) { _log.Msg("WristHub closed because the local player died."); BeginClose(immediate: true); } _wasPlayerDead = true; return; } _wasPlayerDead = false; try { FusionPortalBridge.Attach(_portalStateReceiver, _portalRemoveReceiver); _portalVisual?.Tick(Time.unscaledTime, _preferences.ReducedMotion); if (_activePortal != (PortalLinkState)null) { TickPortals(); } } catch (Exception ex) { string text = "portal-global|" + ex.GetType().FullName; if (_runtimeErrorThrottle.ShouldLog(text, (double)Environment.TickCount64 / 1000.0, 5.0)) { _log.Error("WristHub portal update recovered (repeats suppressed): " + ex.Message); } } if (!IsLauncherOpen || (Object)(object)Player.Head == (Object)null) { return; } if ((int)_lifecycle == 3) { FollowWrist(); UpdateLifecycleVisuals(); if ((double)(Time.unscaledTime - _lifecycleStartedAt) >= 0.35) { FinalizeClose(); } } else { RunGuarded("projection", _projectionTickAction, _projectionRecoveryAction); RunGuarded("touch", _touchTickAction, _touchRecoveryAction); RunGuarded("installation-focus", _installationFocusTickAction); RunGuarded("preview", _previewTickAction, _previewRecoveryAction); RunGuarded("download", _downloadTickAction); RunGuarded("sharing", _sharingTickAction, CancelPendingSharingRegistration); RunGuarded("compass", _compassTickAction); RunGuarded("wrist-os", TickWristOs); } } private void TickProjectionStage() { FollowWrist(); UpdateHologramProjection(); UpdateLifecycleVisuals(); } private void RecoverProjectionStage() { _projectionAnchors.Invalidate(); _hologramProjection?.BeginTrackingGrace(); } private void TickTouchStage() { UpdatePushPromptVisibility(); PollButtons(); } private void RecoverPreviewStage() { EndPreviewDrag(equipIfAtChest: false); } public void LateTick() { if (!IsLauncherOpen || (Object)(object)Player.Head == (Object)null) { return; } try { FollowWrist(immediate: true); UpdateHologramProjection(finalPoseOnly: true); UpdatePreview(); } catch (Exception ex) { string text = "late-drag|" + ex.GetType().FullName; if (_runtimeErrorThrottle.ShouldLog(text, (double)Environment.TickCount64 / 1000.0, 5.0)) { _log.Error($"WristHub preview drag recovered: {ex}"); } EndPreviewDrag(equipIfAtChest: false); } } private void RunGuarded(string subsystem, Action action, Action? recover = null) { try { action(); } catch (Exception ex) { string text = ex.StackTrace?.Split('\n').FirstOrDefault()?.Trim() ?? "no-stack"; string text2 = subsystem + "|" + ex.GetType().FullName + "|" + text; if (_runtimeErrorThrottle.ShouldLog(text2, (double)Environment.TickCount64 / 1000.0, 5.0)) { _log.Error($"WristHub {subsystem} update recovered (repeats suppressed): {ex}"); } try { recover?.Invoke(); } catch { } } } public void Dispose() { if (!_disposed) { _disposed = true; _searchGeneration++; DisposeWristOs(); ResetUiReferences(destroyOwnedObjects: true); } } private void EnsureBuilt() { if (!((Object)(object)_root != (Object)null)) { ResetUiReferences(destroyOwnedObjects: true); BuildTablet(); } } private void ResetUiReferences(bool destroyOwnedObjects) { //IL_043e: Unknown result type (might be due to invalid IL or missing references) ResetWristOsReferences(destroyOwnedObjects); foreach (PackRow packRow in _packRows) { packRow.Generation++; } ResetTouchButtons(); _buttons.Clear(); _packRows.Clear(); _touchCaptures.Clear(); _activeProbeIds.Clear(); _staleCaptureIds.Clear(); _artworkGeneration.Next(); _previewGeneration++; if (destroyOwnedObjects) { try { if ((Object)(object)_previewModel != (Object)null) { Object.Destroy((Object)(object)_previewModel); } } catch { } foreach (Mesh ownedPreviewMesh in _ownedPreviewMeshes) { try { if ((Object)(object)ownedPreviewMesh != (Object)null) { Object.Destroy((Object)(object)ownedPreviewMesh); } } catch { } } try { if ((Object)(object)_previewShell != (Object)null) { Object.Destroy((Object)(object)_previewShell); } } catch { } try { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } } catch { } try { if ((Object)(object)_placeholderTexture != (Object)null) { Object.Destroy((Object)(object)_placeholderTexture); } } catch { } try { if ((Object)(object)_roundedSprite != (Object)null) { Object.Destroy((Object)(object)_roundedSprite); } } catch { } try { if ((Object)(object)_roundedTexture != (Object)null) { Object.Destroy((Object)(object)_roundedTexture); } } catch { } DisposeButtonIconSprites(); try { _hologramProjection?.Dispose(); } catch { } try { _hologramCarousel?.Dispose(); } catch { } try { _chestEquipGuide?.Dispose(); } catch { } } _ownedPreviewMeshes.Clear(); _hologramProjection = null; _hologramCarousel = null; _chestEquipGuide = null; _root = null; _launcher = null; _hub = null; _browser = null; _canvasRoot = null; _packPage = null; _detailPage = null; _optionsPage = null; _watchPlacementPage = null; _downloadsPage = null; _packOptionsPage = null; _keyboardPage = null; _deletePage = null; _compassPage = null; _denseGlassChrome = null; _denseGlassBackground = null; _denseGlassOverlay = null; _wristLocalPoseValid = false; _wristLocalHandId = 0; _leftArcWing = null; _rightArcWing = null; _metadataArc = null; _canvasGroup = null; _carouselIdentity = string.Empty; _packTitle = null; _packStatus = null; _packPosition = null; _packSearchText = null; _sortText = null; _wristText = null; _scaleText = null; _motionText = null; _themeText = null; _clockText = null; _startingViewText = null; _watchForearmText = null; _scopeText = null; _downloadsSummary = null; _deleteTitle = null; _deleteDetails = null; _keyboardText = null; _keyboardTitle = null; _keyboardShiftText = null; _searchText = null; _compassHeadingText = null; _compassCalibrationText = null; _compassNeedle = null; _compassDecorRing = null; _positionText = null; _selectedName = null; _creatorText = null; _modText = null; _statusText = null; _heightText = null; _heightRuler = null; _progressRoot = null; _progressFill = null; _progressText = null; _actionLabel = null; _actionPanel = null; _trashButton = null; _centralArtwork = null; _placeholderTexture = null; _roundedTexture = null; _roundedSprite = null; _actionButton = null; _previewShell = null; _previewFallback = null; _previewModel = null; _previewAvatar = null; _pendingPreviewAvatar = null; _loadedPreviewBarcode = string.Empty; _requestedPreviewBarcode = string.Empty; _previewReady = false; _previewInteractionReady = false; _previewHeld = false; _dragHand = null; _dragWasInsideChest = false; _previewReturning = false; _lifecycle = (HologramDashboardLifecycle)0; _closeNotificationPending = false; CancelPendingSharingRegistration(); } private void BuildTablet() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Expected O, but got Unknown //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Unknown result type (might be due to invalid IL or missing references) //IL_0491: Unknown result type (might be due to invalid IL or missing references) //IL_0497: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Unknown result type (might be due to invalid IL or missing references) //IL_04f2: Unknown result type (might be due to invalid IL or missing references) //IL_04f8: Unknown result type (might be due to invalid IL or missing references) //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_0553: Unknown result type (might be due to invalid IL or missing references) //IL_0558: Unknown result type (might be due to invalid IL or missing references) //IL_05b7: Unknown result type (might be due to invalid IL or missing references) //IL_05cb: Unknown result type (might be due to invalid IL or missing references) //IL_05d1: Unknown result type (might be due to invalid IL or missing references) //IL_0612: Unknown result type (might be due to invalid IL or missing references) //IL_061d: Unknown result type (might be due to invalid IL or missing references) //IL_065d: Unknown result type (might be due to invalid IL or missing references) //IL_0671: Unknown result type (might be due to invalid IL or missing references) //IL_0677: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: Unknown result type (might be due to invalid IL or missing references) //IL_06d6: Unknown result type (might be due to invalid IL or missing references) //IL_06f1: Unknown result type (might be due to invalid IL or missing references) //IL_06fb: Expected O, but got Unknown //IL_071d: Unknown result type (might be due to invalid IL or missing references) //IL_0727: Expected O, but got Unknown //IL_0754: Unknown result type (might be due to invalid IL or missing references) //IL_076d: Unknown result type (might be due to invalid IL or missing references) //IL_07b8: Unknown result type (might be due to invalid IL or missing references) //IL_07be: Unknown result type (might be due to invalid IL or missing references) //IL_07c9: Unknown result type (might be due to invalid IL or missing references) //IL_07d4: Unknown result type (might be due to invalid IL or missing references) //IL_07e3: Unknown result type (might be due to invalid IL or missing references) //IL_07f4: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Expected O, but got Unknown //IL_082b: Unknown result type (might be due to invalid IL or missing references) //IL_0844: Unknown result type (might be due to invalid IL or missing references) //IL_088f: Unknown result type (might be due to invalid IL or missing references) //IL_0895: Unknown result type (might be due to invalid IL or missing references) //IL_08a0: Unknown result type (might be due to invalid IL or missing references) //IL_08ab: Unknown result type (might be due to invalid IL or missing references) //IL_08ba: Unknown result type (might be due to invalid IL or missing references) //IL_08cb: Unknown result type (might be due to invalid IL or missing references) //IL_08d5: Expected O, but got Unknown //IL_0916: Unknown result type (might be due to invalid IL or missing references) //IL_092f: Unknown result type (might be due to invalid IL or missing references) //IL_095f: Unknown result type (might be due to invalid IL or missing references) //IL_0978: Unknown result type (might be due to invalid IL or missing references) //IL_0994: Unknown result type (might be due to invalid IL or missing references) //IL_09ad: Unknown result type (might be due to invalid IL or missing references) //IL_09d4: Unknown result type (might be due to invalid IL or missing references) //IL_09d9: Unknown result type (might be due to invalid IL or missing references) //IL_09de: Unknown result type (might be due to invalid IL or missing references) //IL_09e3: Unknown result type (might be due to invalid IL or missing references) //IL_09e9: Unknown result type (might be due to invalid IL or missing references) //IL_0a21: Unknown result type (might be due to invalid IL or missing references) //IL_0a26: Unknown result type (might be due to invalid IL or missing references) //IL_0a2b: Unknown result type (might be due to invalid IL or missing references) //IL_0a30: Unknown result type (might be due to invalid IL or missing references) //IL_0a36: Unknown result type (might be due to invalid IL or missing references) //IL_0a7c: Unknown result type (might be due to invalid IL or missing references) //IL_0a81: Unknown result type (might be due to invalid IL or missing references) //IL_0a86: Unknown result type (might be due to invalid IL or missing references) //IL_0a8b: Unknown result type (might be due to invalid IL or missing references) //IL_0a91: Unknown result type (might be due to invalid IL or missing references) //IL_0ac9: Unknown result type (might be due to invalid IL or missing references) //IL_0ace: Unknown result type (might be due to invalid IL or missing references) //IL_0ad3: Unknown result type (might be due to invalid IL or missing references) //IL_0ad8: Unknown result type (might be due to invalid IL or missing references) //IL_0ade: Unknown result type (might be due to invalid IL or missing references) //IL_0b24: Unknown result type (might be due to invalid IL or missing references) //IL_0b29: Unknown result type (might be due to invalid IL or missing references) //IL_0b2e: Unknown result type (might be due to invalid IL or missing references) //IL_0b33: Unknown result type (might be due to invalid IL or missing references) //IL_0b39: Unknown result type (might be due to invalid IL or missing references) //IL_0b71: Unknown result type (might be due to invalid IL or missing references) //IL_0b76: Unknown result type (might be due to invalid IL or missing references) //IL_0b7b: Unknown result type (might be due to invalid IL or missing references) //IL_0b80: Unknown result type (might be due to invalid IL or missing references) //IL_0b85: Unknown result type (might be due to invalid IL or missing references) //IL_0bbd: Unknown result type (might be due to invalid IL or missing references) //IL_0bc2: Unknown result type (might be due to invalid IL or missing references) //IL_0bc7: Unknown result type (might be due to invalid IL or missing references) //IL_0bcc: Unknown result type (might be due to invalid IL or missing references) //IL_0bd2: Unknown result type (might be due to invalid IL or missing references) //IL_0c0a: Unknown result type (might be due to invalid IL or missing references) //IL_0c0f: Unknown result type (might be due to invalid IL or missing references) //IL_0c14: Unknown result type (might be due to invalid IL or missing references) //IL_0c19: Unknown result type (might be due to invalid IL or missing references) //IL_0c1f: Unknown result type (might be due to invalid IL or missing references) //IL_0c60: Unknown result type (might be due to invalid IL or missing references) //IL_0c6b: Unknown result type (might be due to invalid IL or missing references) //IL_0c97: Unknown result type (might be due to invalid IL or missing references) //IL_0ca1: Unknown result type (might be due to invalid IL or missing references) //IL_0cc7: Unknown result type (might be due to invalid IL or missing references) //IL_0cd2: Unknown result type (might be due to invalid IL or missing references) //IL_0cf8: Unknown result type (might be due to invalid IL or missing references) //IL_0d16: Unknown result type (might be due to invalid IL or missing references) //IL_0d3c: Unknown result type (might be due to invalid IL or missing references) //IL_0d47: Unknown result type (might be due to invalid IL or missing references) //IL_0d73: Unknown result type (might be due to invalid IL or missing references) //IL_0d7e: Unknown result type (might be due to invalid IL or missing references) //IL_0db0: Unknown result type (might be due to invalid IL or missing references) //IL_0db5: Unknown result type (might be due to invalid IL or missing references) //IL_0dba: Unknown result type (might be due to invalid IL or missing references) //IL_0dbf: Unknown result type (might be due to invalid IL or missing references) //IL_0dc5: Unknown result type (might be due to invalid IL or missing references) _roundedTexture = CreateRoundedTexture(); _roundedSprite = Sprite.Create(_roundedTexture, new Rect(0f, 0f, (float)((Texture)_roundedTexture).width, (float)((Texture)_roundedTexture).height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(16f, 16f, 16f, 16f)); ((Object)_roundedSprite).name = "WristHub Rounded UI Sprite"; _root = new GameObject("[WristHub] Wrist Avatar Browser"); Object.DontDestroyOnLoad((Object)(object)_root); BuildHologramProjection(); _hub = new GameObject("WristHub Module Hub"); _hub.transform.SetParent(_root.transform, false); BuildModuleStage(_hub.transform); GameObject val = CreateCanvas(_hub.transform, "Radial Module UI", 230f, 176f, 0.009f); BuildWristOsHome(val.transform); _hub.SetActive(false); _placeholderTexture = CreatePlaceholderTexture(); _browser = new GameObject("Compact 30x19cm Arc Hologram Dashboard"); _browser.transform.SetParent(_root.transform, false); _browser.transform.localScale = Vector3.one * 0.88f; _denseGlassChrome = new GameObject("Dense Glass Panel Chrome"); _denseGlassChrome.transform.SetParent(_browser.transform, false); CreatePanel(_denseGlassChrome.transform, "Glass Panel Shadow", new Vector3(0.006f, -0.007f, -0.006f), new Vector3(0.35f, 0.22f, 0.009f), new Color(0f, 0f, 0f, 0.62f), (PrimitiveType)3); CreatePanel(_denseGlassChrome.transform, "Curved Glass Panel", Vector3.zero, new Vector3(0.34f, 0.21f, 0.007f), new Color(Glass.r, Glass.g, Glass.b, 0.94f), (PrimitiveType)3); _canvasRoot = CreateCanvas(_browser.transform, "WristHub Tablet Canvas", 340f, 210f, 0.006f); _canvasGroup = _canvasRoot.AddComponent<CanvasGroup>(); RawImage val2 = CreateRawImage(_canvasRoot.transform, "WristHub Background", AvatarTabletLayout.Tablet, BoneHubTheme.Background ?? _placeholderTexture, Color.white); _denseGlassBackground = ((Component)val2).gameObject; ((Graphic)val2).raycastTarget = false; Image val3 = CreateImage(_canvasRoot.transform, "Calm Dark Overlay", AvatarTabletLayout.Tablet, new Color(0.005f, 0.024f, 0.038f, 0.72f)); _denseGlassOverlay = ((Component)val3).gameObject; ((Graphic)val3).raycastTarget = false; _packPage = new GameObject("Mod Pack Library Page"); _packPage.transform.SetParent(_canvasRoot.transform, false); _packTitle = CreateText(_packPage.transform, "GROUPS", new TabletRect(92f, 83f, 64f, 25f), 15f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); CreateButton(_browser.transform, _packPage.transform, "BACK", new Vector3(-0.145f, 0.083f, 0.007f), new Vector3(0.042f, 0.028f, 0.014f), DimCyan, BackFromPackPage, 9f); TouchButton touchButton = CreateButton(_browser.transform, _packPage.transform, "SEARCH", new Vector3(-0.09f, 0.083f, 0.007f), new Vector3(0.064f, 0.028f, 0.014f), DimCyan, OpenKeyboard, 11f); _packSearchText = touchButton.Label; CreateButton(_browser.transform, _packPage.transform, "ALL", new Vector3(-0.034f, 0.083f, 0.007f), new Vector3(0.046f, 0.028f, 0.014f), Cyan, ShowAllAvatars, 12f); CreateButton(_browser.transform, _packPage.transform, "OPTIONS", new Vector3(0.025f, 0.083f, 0.007f), new Vector3(0.068f, 0.028f, 0.014f), DimCyan, ShowOptionsPage, 9f); CreateButton(_browser.transform, _packPage.transform, "X", new Vector3(0.148f, 0.083f, 0.007f), new Vector3(0.026f, 0.028f, 0.014f), Red, Close, 19f); for (int i = 0; i < 4; i++) { CreatePackRow(i); } CreateButton(_browser.transform, _packPage.transform, "<", new Vector3(-0.135f, -0.091f, 0.007f), new Vector3(0.05f, 0.026f, 0.014f), DimCyan, delegate { MovePackPage(-1); }, 22f); _packPosition = CreateText(_packPage.transform, "1 / 1", new TabletRect(0f, -91f, 90f, 24f), 14f, Cyan, (TextAlignmentOptions)514, (FontStyles)1); CreateButton(_browser.transform, _packPage.transform, ">", new Vector3(0.135f, -0.091f, 0.007f), new Vector3(0.05f, 0.026f, 0.014f), DimCyan, delegate { MovePackPage(1); }, 22f); _packStatus = CreateText(_packPage.transform, string.Empty, new TabletRect(0f, -72f, 250f, 10f), 11f, new Color(0.7f, 0.82f, 0.88f, 1f), (TextAlignmentOptions)514, (FontStyles)0); _detailPage = new GameObject("Arc Dashboard Avatar Page"); _detailPage.transform.SetParent(_canvasRoot.transform, false); _leftArcWing = new GameObject("Left Curved Glass Wing"); _leftArcWing.transform.SetParent(_detailPage.transform, false); CreateImage(_leftArcWing.transform, "Left Wing Glass", ArcDashboardLayout.LeftWing, new Color(0.006f, 0.026f, 0.038f, 0.92f)); CreateImage(_leftArcWing.transform, "Left Wing Cyan Edge", new TabletRect(((TabletRect)(ref ArcDashboardLayout.LeftWing)).Right - 1.5f, ((TabletRect)(ref ArcDashboardLayout.LeftWing)).Y, 3f, ((TabletRect)(ref ArcDashboardLayout.LeftWing)).Height - 14f), new Color(Cyan.r, Cyan.g, Cyan.b, 0.55f)); _rightArcWing = new GameObject("Right Curved Glass Wing"); _rightArcWing.transform.SetParent(_detailPage.transform, false); CreateImage(_rightArcWing.transform, "Right Wing Glass", ArcDashboardLayout.RightWing, new Color(0.006f, 0.026f, 0.038f, 0.92f)); CreateImage(_rightArcWing.transform, "Right Wing Cyan Edge", new TabletRect(((TabletRect)(ref ArcDashboardLayout.RightWing)).Left + 1.5f, ((TabletRect)(ref ArcDashboardLayout.RightWing)).Y, 3f, ((TabletRect)(ref ArcDashboardLayout.RightWing)).Height - 14f), new Color(Cyan.r, Cyan.g, Cyan.b, 0.55f)); _metadataArc = new GameObject("Floating Avatar Metadata Arc"); _metadataArc.transform.SetParent(_detailPage.transform, false); CreateImage(_metadataArc.transform, "Upper Metadata Glass", new TabletRect(0f, 73f, 184f, 38f), new Color(0.006f, 0.026f, 0.038f, 0.78f)); CreateImage(_metadataArc.transform, "Lower Metadata Glass", new TabletRect(0f, -53f, 206f, 24f), new Color(0.006f, 0.026f, 0.038f, 0.82f)); CreateImage(_detailPage.transform, "Circular Hologram Stage", ArcDashboardLayout.Stage, new Color(0.01f, 0.18f, 0.24f, 0.1f)); CreateButton(_browser.transform, _detailPage.transform, "BACK", PixelCenter(ArcDashboardLayout.Back), PixelSize(ArcDashboardLayout.Back), DimCyan, BackFromDetailPage, 8f); TouchButton touchButton2 = CreateButton(_browser.transform, _detailPage.transform, "SEARCH", PixelCenter(ArcDashboardLayout.Search), PixelSize(ArcDashboardLayout.Search), DimCyan, OpenKeyboard, 9f); _searchText = touchButton2.Label; CreateButton(_browser.transform, _detailPage.transform, "PACK", PixelCenter(ArcDashboardLayout.Pack), PixelSize(ArcDashboardLayout.Pack), DimCyan, ShowPackOptionsPage, 9f); TouchButton touchButton3 = CreateButton(_browser.transform, _detailPage.transform, "ALL", PixelCenter(ArcDashboardLayout.Scope), PixelSize(ArcDashboardLayout.Scope), Cyan, ToggleScopeOrDownloads, 10f); _scopeText = touchButton3.Label; CreateButton(_browser.transform, _detailPage.transform, "OPTIONS", PixelCenter(ArcDashboardLayout.Options), PixelSize(ArcDashboardLayout.Options), DimCyan, ShowOptionsPage, 8f); CreateButton(_browser.transform, _detailPage.transform, "X", PixelCenter(ArcDashboardLayout.Close), PixelSize(ArcDashboardLayout.Close), Red, Close, 18f); CreateButton(_browser.transform, _detailPage.transform, "<", PixelCenter(ArcDashboardLayout.Previous), PixelSize(ArcDashboardLayout.Previous), DimCyan, delegate { MoveFocused(-1); }, 25f); CreateButton(_browser.transform, _detailPage.transform, ">", PixelCenter(ArcDashboardLayout.Next), PixelSize(ArcDashboardLayout.Next), DimCyan, delegate { MoveFocused(1); }, 25f); _centralArtwork = CreateRawImage(_detailPage.transform, "Selected Artwork", new TabletRect(0f, 5f, 88f, 96f), _placeholderTexture, Color.white); ((Graphic)_centralArtwork).raycastTarget = false; _selectedName = CreateText(_metadataArc.transform, "Select an avatar", ArcDashboardLayout.Name, 15f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); _creatorText = CreateText(_metadataArc.transform, string.Empty, ArcDashboardLayout.Creator, 9f, Cyan, (TextAlignmentOptions)514, (FontStyles)0); _modText = CreateText(_metadataArc.transform, string.Empty, ArcDashboardLayout.PackName, 9f, new Color(0.72f, 0.82f, 0.88f, 1f), (TextAlignmentOptions)514, (FontStyles)0); _positionText = CreateText(_metadataArc.transform, "0 / 0", ArcDashboardLayout.Position, 9f, Cyan, (TextAlignmentOptions)514, (FontStyles)1); CreateTabletHeightRuler(); _statusText = CreateText(_metadataArc.transform, "Ready", ArcDashboardLayout.Status, 8.5f, Cyan, (TextAlignmentOptions)514, (FontStyles)1); _actionButton = CreateButton(_browser.transform, _detailPage.transform, "EQUIP", PixelCenter(ArcDashboardLayout.Action), PixelSize(ArcDashboardLayout.Action), Cyan, ActivateSelected, 16f); _actionLabel = _actionButton.Label; _actionPanel = _actionButton.Panel; _trashButton = null; CreateTabletProgressBar(); BuildOptionsPage(); BuildWatchPlacementPage(); BuildDownloadsPage(); BuildPackOptionsPage(); BuildCompassPage(); BuildWristOsPages(); BuildKeyboardPage(); BuildDeletePage(); _packPage.SetActive(true); _detailPage.SetActive(false); GameObject? optionsPage = _optionsPage; if (optionsPage != null) { optionsPage.SetActive(false); } GameObject? watchPlacementPage = _watchPlacementPage; if (watchPlacementPage != null) { watchPlacementPage.SetActive(false); } GameObject? downloadsPage = _downloadsPage; if (downloadsPage != null) { downloadsPage.SetActive(false); } GameObject? packOptionsPage = _packOptionsPage; if (packOptionsPage != null) { packOptionsPage.SetActive(false); } GameObject? compassPage = _compassPage; if (compassPage != null) { compassPage.SetActive(false); } HideWristOsAppPages(); GameObject? keyboardPage = _keyboardPage; if (keyboardPage != null) { keyboardPage.SetActive(false); } GameObject? deletePage = _deletePage; if (deletePage != null) { deletePage.SetActive(false); } _browser.SetActive(false); CreatePreviewShell(); if ((Object)(object)_root != (Object)null) { _hologramCarousel = null; _root.SetActive(false); } } private void BuildHologramProjection() { if (_hologramProjection == null) { _hologramProjection = new BoneHubForearmProjection(_log, _constrainedRendering); } } private void BuildModuleStage(Transform parent) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 32; i++) { float num = (float)i / 32f * (float)Math.PI * 2f; ((Component)CreatePanel(parent, "Projected Module Ring", new Vector3(Mathf.Sin(num) * 0.061f, Mathf.Cos(num) * 0.061f, 0.002f), new Vector3(0.007f, 0.0011f, 0.0011f), (i % 4 == 0) ? Cyan : DimCyan, (PrimitiveType)3)).transform.localRotation = Quaternion.Euler(0f, 0f, (0f - num) * 57.29578f); } ((Component)CreatePanel(parent, "Module Emitter Core", new Vector3(0f, 0f, -0.002f), new Vector3(0.012f, 0.012f, 0.001f), new Color(0.08f, 0.6f, 0.72f, 0.45f), (PrimitiveType)2)).transform.localRotation = Quaternion.Euler(90f, 0f, 0f); } private void BuildDownloadsPage() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null)) { _downloadsPage = new GameObject("WristHub Downloads Glass Panel"); _downloadsPage.transform.SetParent(_canvasRoot.transform, false); CreateImage(_downloadsPage.transform, "Downloads Glass", new TabletRect(0f, 0f, 286f, 168f), new Color(0.008f, 0.03f, 0.043f, 0.88f)); CreateText(_downloadsPage.transform, "DOWNLOADS", new TabletRect(-55f, 66f, 150f, 26f), 18f, Color.white, (TextAlignmentOptions)4097, (FontStyles)1); CreateButton(_browser.transform, _downloadsPage.transform, "BACK", new Vector3(0.092f, 0.066f, 0.007f), new Vector3(0.06f, 0.026f, 0.014f), DimCyan, ShowHub, 10f); CreateButton(_browser.transform, _downloadsPage.transform, "X", new Vector3(0.13f, 0.066f, 0.007f), new Vector3(0.024f, 0.026f, 0.014f), Red, Close, 17f); _downloadsSummary = CreateText(_downloadsPage.transform, "No downloads yet", new TabletRect(0f, 5f, 252f, 88f), 12f, Color.white, (TextAlignmentOptions)257, (FontStyles)0); CreateButton(_browser.transform, _downloadsPage.transform, "CANCEL ACTIVE", new Vector3(0f, -0.065f, 0.007f), new Vector3(0.13f, 0.03f, 0.014f), Amber, CancelActiveDownload, 10f); _downloadsPage.SetActive(false); } } private void BuildPackOptionsPage() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null)) { _packOptionsPage = new GameObject("Avatar Pack Options Glass Panel"); _packOptionsPage.transform.SetParent(_canvasRoot.transform, false); CreateImage(_packOptionsPage.transform, "Pack Options Glass", new TabletRect(0f, 0f, 250f, 150f), new Color(0.008f, 0.03f, 0.043f, 0.9f)); CreateText(_packOptionsPage.transform, "PACK OPTIONS", new TabletRect(0f, 53f, 190f, 25f), 17f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); CreateButton(_browser.transform, _packOptionsPage.transform, "BACK", new Vector3(-0.076f, -0.052f, 0.007f), new Vector3(0.08f, 0.03f, 0.014f), DimCyan, ShowDetailPage, 11f); CreateButton(_browser.transform, _packOptionsPage.transform, "SETTINGS", new Vector3(0f, 0.012f, 0.007f), new Vector3(0.15f, 0.034f, 0.014f), DimCyan, ShowOptionsPage, 12f); CreateButton(_browser.transform, _packOptionsPage.transform, "DELETE PACK", new Vector3(0f, -0.029f, 0.007f), new Vector3(0.15f, 0.034f, 0.014f), Red, ShowDeleteConfirmation, 11f); CreateButton(_browser.transform, _packOptionsPage.transform, "X", new Vector3(0.108f, 0.054f, 0.007f), new Vector3(0.024f, 0.026f, 0.014f), Red, Close, 17f); _packOptionsPage.SetActive(false); } } private void BuildCompassPage() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_03fa: Expected O, but got Unknown //IL_0437: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_04ab: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04db: Unknown result type (might be due to invalid IL or missing references) //IL_04eb: Unknown result type (might be due to invalid IL or missing references) //IL_04f1: Expected O, but got Unknown //IL_052e: Unknown result type (might be due to invalid IL or missing references) //IL_0558: Unknown result type (might be due to invalid IL or missing references) //IL_055e: Unknown result type (might be due to invalid IL or missing references) //IL_0569: Unknown result type (might be due to invalid IL or missing references) //IL_0574: Unknown result type (might be due to invalid IL or missing references) //IL_0583: Unknown result type (might be due to invalid IL or missing references) //IL_05b3: Unknown result type (might be due to invalid IL or missing references) //IL_05be: Unknown result type (might be due to invalid IL or missing references) //IL_05f8: Unknown result type (might be due to invalid IL or missing references) //IL_0616: Unknown result type (might be due to invalid IL or missing references) //IL_0656: Unknown result type (might be due to invalid IL or missing references) //IL_066a: Unknown result type (might be due to invalid IL or missing references) //IL_0670: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Unknown result type (might be due to invalid IL or missing references) //IL_06cb: Unknown result type (might be due to invalid IL or missing references) //IL_06d0: Unknown result type (might be due to invalid IL or missing references) //IL_0717: Unknown result type (might be due to invalid IL or missing references) //IL_072b: Unknown result type (might be due to invalid IL or missing references) //IL_0744: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null)) { _compassPage = new GameObject("Projected Compass App"); _compassPage.transform.SetParent(_canvasRoot.transform, false); CreateImage(_compassPage.transform, "Compact Compass Glass", new TabletRect(0f, 0f, 154f, 178f), new Color(0.004f, 0.022f, 0.032f, 0.9f)); CreateText(_compassPage.transform, "COMPASS", new TabletRect(0f, 78f, 90f, 18f), 13f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); CreateImage(_compassPage.transform, "Eleven Centimeter Dial", new TabletRect(0f, 7f, 110f, 110f), new Color(0.008f, 0.075f, 0.095f, 0.72f)); for (int i = 0; i < 32; i++) { float num = (float)i / 32f * (float)Math.PI * 2f; float num2 = 51f; bool flag = i % 4 == 0; ((Transform)((Graphic)CreateImage(_compassPage.transform, flag ? "Major Bearing Tick" : "Bearing Tick", new TabletRect(Mathf.Sin(num) * num2, 7f + Mathf.Cos(num) * num2, flag ? 2.2f : 1.2f, flag ? 8f : 5f), (Color)(flag ? Cyan : new Color(Cyan.r, Cyan.g, Cyan.b, 0.46f)))).rectTransform).localRotation = Quaternion.Euler(0f, 0f, (0f - num) * 57.29578f); } CreateText(_compassPage.transform, "N", new TabletRect(0f, 48f, 18f, 16f), 12f, Cyan, (TextAlignmentOptions)514, (FontStyles)1); CreateText(_compassPage.transform, "E", new TabletRect(44f, 7f, 18f, 16f), 10f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); CreateText(_compassPage.transform, "S", new TabletRect(0f, -34f, 18f, 16f), 10f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); CreateText(_compassPage.transform, "W", new TabletRect(-44f, 7f, 18f, 16f), 10f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); (string, float, float)[] array = new(string, float, float)[4] { ("NE", 31f, 36f), ("SE", 31f, -22f), ("SW", -31f, -22f), ("NW", -31f, 36f) }; for (int j = 0; j < array.Length; j++) { (string, float, float) tuple = array[j]; CreateText(_compassPage.transform, tuple.Item1, new TabletRect(tuple.Item2, tuple.Item3, 17f, 10f), 6.5f, new Color(0.6f, 0.82f, 0.88f, 1f), (TextAlignmentOptions)514, (FontStyles)1); } GameObject val = new GameObject("Smooth North Needle"); val.transform.SetParent(_compassPage.transform, false); _compassNeedle = val.AddComponent<RectTransform>(); SetRect(_compassNeedle, new TabletRect(0f, 7f, 110f, 110f)); CreateImage((Transform)(object)_compassNeedle, "North Arrow", new TabletRect(0f, 22f, 4f, 40f), Cyan); CreateImage((Transform)(object)_compassNeedle, "Needle Tail", new TabletRect(0f, -15f, 2f, 27f), new Color(0.2f, 0.48f, 0.54f, 0.8f)); CreateImage((Transform)(object)_compassNeedle, "Needle Hub", new TabletRect(0f, 0f, 9f, 9f), Color.white); GameObject val2 = new GameObject("Slow Compass Detail Ring"); val2.transform.SetParent(_compassPage.transform, false); _compassDecorRing = val2.AddComponent<RectTransform>(); SetRect(_compassDecorRing, new TabletRect(0f, 7f, 116f, 116f)); CreateImage((Transform)(object)_compassDecorRing, "Decor North Arc", new TabletRect(0f, 55f, 30f, 2f), new Color(Cyan.r, Cyan.g, Cyan.b, 0.7f)); _compassHeadingText = CreateText(_compassPage.transform, "N • 000°", new TabletRect(0f, -52f, 118f, 18f), 12f, Cyan, (TextAlignmentOptions)514, (FontStyles)1); _compassCalibrationText = CreateText(_compassPage.transform, "WORLD NORTH", new TabletRect(0f, -65f, 112f, 10f), 7f, new Color(0.62f, 0.78f, 0.84f, 1f), (TextAlignmentOptions)514, (FontStyles)1); CreateButton(_browser.transform, _compassPage.transform, "BACK", new Vector3(-0.058f, 0.077f, 0.007f), new Vector3(0.048f, 0.022f, 0.014f), DimCyan, ShowHub, 8f); CreateButton(_browser.transform, _compassPage.transform, "X", new Vector3(0.061f, 0.077f, 0.007f), new Vector3(0.026f, 0.022f, 0.014f), Red, Close, 14f); CreateButton(_browser.transform, _compassPage.transform, "SET NORTH", new Vector3(0f, -0.08f, 0.007f), new Vector3(0.084f, 0.024f, 0.014f), new Color(0.025f, 0.28f, 0.34f, 1f), SetCompassNorth, 8f); _compassPage.SetActive(false); } } private void StartHologramProjection() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) UpdatePresentationMode(); _lifecycleStartedAt = Time.unscaledTime; _lifecycle = (HologramDashboardLifecycle)((!_preferences.ReducedMotion) ? 1 : 2); _closeNotificationPending = false; if (!_preferences.HologramEffects) { if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 1f; } _hologramProjection?.Hide(immediate: true); _lifecycle = (HologramDashboardLifecycle)2; UpdateLifecycleVisuals(); } else { if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = (_preferences.ReducedMotion ? 1f : 0.02f); } _hologramProjection?.Show(); UpdateLifecycleVisuals(); } } private void UpdateHologramProjection(bool finalPoseOnly = false) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (_hologramProjection == null) { return; } GameObject? browser = _browser; object obj; if (browser == null || !browser.activeSelf) { GameObject? hub = _hub; obj = ((hub != null && hub.activeSelf) ? _hub.transform : null); } else { obj = _browser.transform; } Transform val = (Transform)obj; if (!_preferences.HologramEffects) { if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 1f; } if ((Object)(object)val != (Object)null) { val.localPosition = Vector3.zero; val.localScale = Vector3.one; } _hologramProjection.Hide(immediate: true); _hologramCarousel?.SetVisible(visible: false); return; } if ((Object)(object)val == (Object)null) { _hologramProjection.Hide(); return; } _hologramProjection.SetAccent(Cyan); float num = AvatarUiScaleMath.ResolveMenuScale(CurrentPlayerHeight()); if (!_projectionAnchors.TryGet(_preferences.UseLeftWrist, num, out var anchor)) { _hologramProjection.BeginTrackingGrace(); _hologramProjection.UpdateTrackingLost(); return; } anchor = anchor with { EmitterPosition = anchor.EmitterPosition + anchor.ArmDirection * (_preferences.WatchForearmOffset * num) }; _hologramProjection.UpdatePose(anchor, val, _preferences.ReducedMotion, _preferences.HologramBrightness, finalPoseOnly); if (!finalPoseOnly) { float num2 = (_preferences.ReducedMotion ? 1f : _hologramProjection.RevealProgress); if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = Mathf.Lerp(0.02f, 1f, num2); } } } private void UpdateLifecycleVisuals() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Invalid comparison between Unknown and I4 //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) HologramDashboardProgress val3 = default(HologramDashboardProgress); if ((int)_lifecycle == 3) { HologramDashboardProgress val = HologramDashboardTimeline.Closing((double)(Time.unscaledTime - _lifecycleStartedAt), _preferences.ReducedMotion); HologramDashboardProgress val2 = val; ((HologramDashboardProgress)(ref val2)).set_Rays(((HologramDashboardProgress)(ref val)).Rays * ((HologramDashboardProgress)(ref _closeVisualStart)).Rays); ((HologramDashboardProgress)(ref val2)).set_Stage(((HologramDashboardProgress)(ref val)).Stage * ((HologramDashboardProgress)(ref _closeVisualStart)).Stage); ((HologramDashboardProgress)(ref val2)).set_Wings(((HologramDashboardProgress)(ref val)).Wings * ((HologramDashboardProgress)(ref _closeVisualStart)).Wings); ((HologramDashboardProgress)(ref val2)).set_Avatars(((HologramDashboardProgress)(ref val)).Avatars * ((HologramDashboardProgress)(ref _closeVisualStart)).Avatars); ((HologramDashboardProgress)(ref val2)).set_Labels(((HologramDashboardProgress)(ref val)).Labels * ((HologramDashboardProgress)(ref _closeVisualStart)).Labels); val3 = val2; } else if ((int)_lifecycle == 1) { val3 = HologramDashboardTimeline.Opening((double)(Time.unscaledTime - _lifecycleStartedAt), _preferences.ReducedMotion); if (((HologramDashboardProgress)(ref val3)).ControlsReady) { _lifecycle = (HologramDashboardLifecycle)2; } } else { ((HologramDashboardProgress)(ref val3))..ctor((HologramProjectionPhase)5, 1f, 1f, 1f, 1f, 1f, (int)_lifecycle == 2); } float num = Mathf.Lerp(0.015f, 1f, ((HologramDashboardProgress)(ref val3)).Stage); GameObject val4 = ActiveVisualPage(); if ((Object)(object)val4 != (Object)null) { val4.transform.localScale = new Vector3(num, num, 1f); val4.transform.localPosition = new Vector3(0f, Mathf.Lerp(-54f, 0f, ((HologramDashboardProgress)(ref val3)).Stage), 0f); } if ((Object)(object)_hub != (Object)null && _hub.activeSelf) { _hub.transform.localScale = Vector3.one * num; } if ((Object)(object)_leftArcWing != (Object)null) { _leftArcWing.transform.localScale = new Vector3(Mathf.Max(0.02f, ((HologramDashboardProgress)(ref val3)).Wings), 1f, 1f); } if ((Object)(object)_rightArcWing != (Object)null) { _rightArcWing.transform.localScale = new Vector3(Mathf.Max(0.02f, ((HologramDashboardProgress)(ref val3)).Wings), 1f, 1f); } if ((Object)(object)_metadataArc != (Object)null) { _metadataArc.transform.localScale = new Vector3(Mathf.Max(0.02f, ((HologramDashboardProgress)(ref val3)).Labels), 1f, 1f); } if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = Mathf.Clamp01(Mathf.Max(((HologramDashboardProgress)(ref val3)).Stage * 0.82f, ((HologramDashboardProgress)(ref val3)).Labels)); } BoneHubHologramCarousel? hologramCarousel = _hologramCarousel; if (hologram
Mods\WristHub.SDK.dll
Decompiled 3 hours agousing System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("WristHub")] [assembly: InternalsVisibleTo("BoneHub.Tests")] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyCompany("WristHub.SDK")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Stable public app and control API for WristHubOS.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("4.0.1")] [assembly: AssemblyProduct("WristHub.SDK")] [assembly: AssemblyTitle("WristHub.SDK")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace WristHub.SDK; public static class WristHubApi { public const int ApiVersion = 2; private static readonly object Sync = new object(); private static readonly Dictionary<string, WristHubApp> Apps = new Dictionary<string, WristHubApp>(StringComparer.OrdinalIgnoreCase); private static IWristHubHost? _host; public static bool IsWristHubAvailable { get { lock (Sync) { return _host != null; } } } public static WristHubRuntimeInfo Runtime { get { lock (Sync) { return _host?.Runtime ?? WristHubRuntimeInfo.Unavailable; } } } public static int RegisteredAppCount { get { lock (Sync) { return Apps.Count; } } } public static event Action? AppsChanged; public static event Action<WristHubRuntimeInfo>? RuntimeChanged; public static WristHubApp RegisterApp(WristHubAppDefinition definition) { ArgumentNullException.ThrowIfNull(definition, "definition"); definition.Validate(); WristHubApp wristHubApp; lock (Sync) { if (Apps.ContainsKey(definition.Id)) { throw new InvalidOperationException("A WristHub app with id '" + definition.Id + "' is already registered."); } wristHubApp = new WristHubApp(definition, OnAppChanged, Unregister); Apps.Add(definition.Id, wristHubApp); } SafeRaise(WristHubApi.AppsChanged); return wristHubApp; } public static WristHubApp RegisterApp(string id, string name, string author, string version, string description = "", string icon = "APP") { return RegisterApp(new WristHubAppDefinition(id, name, author, version, description, icon)); } public static IReadOnlyList<WristHubApp> GetRegisteredApps() { lock (Sync) { return new ReadOnlyCollection<WristHubApp>(Apps.Values.OrderBy<WristHubApp, string>((WristHubApp app) => app.Name, StringComparer.OrdinalIgnoreCase).ToArray()); } } public static void Notify(string appId, string message, WristHubNotificationKind kind = WristHubNotificationKind.Info) { if (string.IsNullOrWhiteSpace(message)) { return; } IWristHubHost host; lock (Sync) { host = _host; } try { host?.Notify(appId?.Trim() ?? string.Empty, message.Trim(), kind); } catch { } } public static void OpenApp(string appId) { IWristHubHost host; lock (Sync) { host = _host; } try { host?.RequestOpen(appId?.Trim() ?? string.Empty); } catch { } } internal static void AttachHost(IWristHubHost host) { ArgumentNullException.ThrowIfNull(host, "host"); lock (Sync) { _host = host; } SafeRaise(WristHubApi.RuntimeChanged, host.Runtime); SafeRaise(WristHubApi.AppsChanged); } internal static void DetachHost(IWristHubHost host) { lock (Sync) { if (_host != host) { return; } _host = null; } SafeRaise(WristHubApi.RuntimeChanged, WristHubRuntimeInfo.Unavailable); } private static void Unregister(WristHubApp app) { lock (Sync) { if (Apps.TryGetValue(app.Id, out WristHubApp value) && value == app) { Apps.Remove(app.Id); } } SafeRaise(WristHubApi.AppsChanged); } private static void OnAppChanged() { SafeRaise(WristHubApi.AppsChanged); } private static void SafeRaise(Action? callback) { if (callback == null) { return; } Delegate[] invocationList = callback.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action action = (Action)invocationList[i]; try { action(); } catch { } } } private static void SafeRaise<T>(Action<T>? callback, T value) { if (callback == null) { return; } Delegate[] invocationList = callback.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<T> action = (Action<T>)invocationList[i]; try { action(value); } catch { } } } } internal interface IWristHubHost { WristHubRuntimeInfo Runtime { get; } void Notify(string appId, string message, WristHubNotificationKind kind); void RequestOpen(string appId); } public readonly record struct WristHubRuntimeInfo(bool Available, bool IsQuest, bool IsFusionConnected, string Theme, int ApiVersion) { public static WristHubRuntimeInfo Unavailable => new WristHubRuntimeInfo(Available: false, IsQuest: false, IsFusionConnected: false, "Cyan", 2); } public enum WristHubNotificationKind { Info, Success, Warning, Error } public enum WristHubAccent { Theme, Neutral, Success, Warning, Danger } public enum WristHubCommunityCategory { Utility, Gameplay, Social, Accessibility, Developer, Other } public sealed record WristHubCommunityProfile(WristHubCommunityCategory Category, string Summary, string Homepage = "", string SupportContact = "", IReadOnlyList<string>? Tags = null) { internal void Validate() { WristHubAppDefinition.ValidateText(Summary, "Summary", 160); ValidateOptional(Homepage, "Homepage", 240); ValidateOptional(SupportContact, "SupportContact", 96); IReadOnlyList<string> tags = Tags; if (tags != null && tags.Count > 8) { throw new ArgumentOutOfRangeException("Tags"); } if (Tags == null) { return; } foreach (string tag in Tags) { WristHubAppDefinition.ValidateText(tag, "Tags", 24); } } private static void ValidateOptional(string? value, string name, int length) { if (!string.IsNullOrEmpty(value) && value.Length > length) { throw new ArgumentOutOfRangeException(name); } } } public sealed record WristHubAppDefinition(string Id, string Name, string Author, string Version, string Description = "", string Icon = "APP") { internal void Validate() { ValidateToken(Id, "Id", 64); ValidateText(Name, "Name", 48); ValidateText(Author, "Author", 48); ValidateText(Version, "Version", 24); string description = Description; if (description != null && description.Length > 240) { throw new ArgumentOutOfRangeException("Description"); } string icon = Icon; if (icon != null && icon.Length > 8) { throw new ArgumentOutOfRangeException("Icon"); } } internal static void ValidateToken(string value, string name, int length) { ValidateText(value, name, length); if (value.Any(delegate(char character) { bool flag = char.IsLetterOrDigit(character); if (!flag) { bool flag2 = ((character == '-' || character == '.' || character == '_') ? true : false); flag = flag2; } return !flag; })) { throw new ArgumentException("Use only letters, numbers, dots, dashes, and underscores.", name); } } internal static void ValidateText(string value, string name, int length) { if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("Value is required.", name); } if (value.Length > length) { throw new ArgumentOutOfRangeException(name); } } } public sealed class WristHubApp : IDisposable { private readonly Action _changed; private readonly Action<WristHubApp> _dispose; private int _disposed; public string Id { get; } public string Name { get; } public string Author { get; } public string Version { get; } public string Description { get; } public string Icon { get; } public WristHubPage Root { get; } public bool IsDisposed => _disposed != 0; public Func<bool>? IsAvailable { get; set; } public Func<int>? NotificationCount { get; set; } public WristHubCommunityProfile? Community { get; private set; } public bool Available { get { try { return !IsDisposed && (IsAvailable?.Invoke() ?? true); } catch { return false; } } } public int Notifications { get { try { return Math.Clamp(NotificationCount?.Invoke() ?? 0, 0, 99); } catch { return 0; } } } internal WristHubApp(WristHubAppDefinition definition, Action changed, Action<WristHubApp> dispose) { Id = definition.Id.Trim(); Name = definition.Name.Trim(); Author = definition.Author.Trim(); Version = definition.Version.Trim(); Description = definition.Description?.Trim() ?? string.Empty; Icon = (string.IsNullOrWhiteSpace(definition.Icon) ? "APP" : definition.Icon.Trim()); _changed = changed; _dispose = dispose; Root = new WristHubPage("root", Name, null, changed); } public WristHubApp PublishToCommunity(WristHubCommunityProfile profile) { ArgumentNullException.ThrowIfNull(profile, "profile"); profile.Validate(); Community = profile with { Summary = profile.Summary.Trim(), Homepage = (profile.Homepage?.Trim() ?? string.Empty), SupportContact = (profile.SupportContact?.Trim() ?? string.Empty), Tags = profile.Tags?.Select((string tag) => tag.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray() }; _changed(); return this; } public void RemoveFromCommunity() { if (!(Community == null)) { Community = null; _changed(); } } public void Refresh() { _changed(); } public void Dispose() { if (Interlocked.Exchange(ref _disposed, 1) == 0) { _dispose(this); } } } public sealed class WristHubPage { private readonly object _sync = new object(); private readonly List<WristHubControl> _controls = new List<WristHubControl>(); private readonly Action _changed; public string Id { get; } public string Title { get; set; } public WristHubPage? Parent { get; } public IReadOnlyList<WristHubControl> Controls { get { lock (_sync) { return _controls.ToArray(); } } } internal WristHubPage(string id, string title, WristHubPage? parent, Action changed) { WristHubAppDefinition.ValidateToken(id, "id", 64); WristHubAppDefinition.ValidateText(title, "title", 64); Id = id.Trim(); Title = title.Trim(); Parent = parent; _changed = changed; } public WristHubPage AddPage(string id, string title, string description = "") { WristHubPage wristHubPage = new WristHubPage(id, title, this, _changed); Add(new WristHubPageLink(id, title, wristHubPage, description)); return wristHubPage; } public WristHubAction AddButton(string id, string label, Action action, string description = "", WristHubAccent accent = WristHubAccent.Theme) { return Add(new WristHubAction(id, label, action, description, accent)); } public WristHubToggle AddToggle(string id, string label, Func<bool> read, Action<bool> write, string description = "") { return Add(new WristHubToggle(id, label, read, write, description)); } public WristHubSlider AddSlider(string id, string label, double minimum, double maximum, double step, Func<double> read, Action<double> write, string description = "") { return Add(new WristHubSlider(id, label, minimum, maximum, step, read, write, description)); } public WristHubChoice AddChoice(string id, string label, IReadOnlyList<string> choices, Func<int> read, Action<int> write, string description = "") { return Add(new WristHubChoice(id, label, choices, read, write, description)); } public WristHubTextInput AddText(string id, string label, Func<string> read, Action<string> write, string description = "") { return Add(new WristHubTextInput(id, label, read, write, description)); } public WristHubColorInput AddColor(string id, string label, Func<WristHubColorValue> read, Action<WristHubColorValue> write, IReadOnlyList<WristHubColorValue>? palette = null, string description = "") { return Add(new WristHubColorInput(id, label, read, write, palette, description)); } public WristHubLabel AddLabel(string id, string label, Func<string> read, string description = "") { return Add(new WristHubLabel(id, label, read, description)); } public T Add<T>(T control) where T : WristHubControl { ArgumentNullException.ThrowIfNull(control, "control"); lock (_sync) { if (_controls.Any((WristHubControl existing) => existing.Id.Equals(control.Id, StringComparison.OrdinalIgnoreCase))) { throw new InvalidOperationException($"Control id '{control.Id}' already exists on page '{Id}'."); } _controls.Add(control); } _changed(); return control; } public bool Remove(string id) { bool flag; lock (_sync) { flag = _controls.RemoveAll((WristHubControl control) => control.Id.Equals(id, StringComparison.OrdinalIgnoreCase)) > 0; } if (flag) { _changed(); } return flag; } } public abstract class WristHubControl { public string Id { get; } public string Label { get; set; } public string Description { get; set; } public WristHubAccent Accent { get; set; } public Func<bool>? IsEnabled { get; set; } public bool Enabled { get { try { return IsEnabled?.Invoke() ?? true; } catch { return false; } } } protected WristHubControl(string id, string label, string description, WristHubAccent accent = WristHubAccent.Theme) { WristHubAppDefinition.ValidateToken(id, "id", 64); WristHubAppDefinition.ValidateText(label, "label", 64); Id = id.Trim(); Label = label.Trim(); Description = description?.Trim() ?? string.Empty; Accent = accent; } } public sealed class WristHubAction : WristHubControl { private readonly Action _action; internal WristHubAction(string id, string label, Action action, string description, WristHubAccent accent) : base(id, label, description, accent) { _action = action ?? throw new ArgumentNullException("action"); } public void Invoke() { _action(); } } public sealed class WristHubToggle : WristHubControl { private readonly Func<bool> _read; private readonly Action<bool> _write; public bool Value => _read(); internal WristHubToggle(string id, string label, Func<bool> read, Action<bool> write, string description) : base(id, label, description) { _read = read; _write = write; } public void Toggle() { _write(!Value); } } public sealed class WristHubSlider : WristHubControl { private readonly Func<double> _read; private readonly Action<double> _write; public double Minimum { get; } public double Maximum { get; } public double Step { get; } public double Value => Math.Clamp(_read(), Minimum, Maximum); internal WristHubSlider(string id, string label, double min, double max, double step, Func<double> read, Action<double> write, string description) : base(id, label, description) { if (!double.IsFinite(min) || !double.IsFinite(max) || max <= min) { throw new ArgumentOutOfRangeException("max"); } if (!double.IsFinite(step) || step <= 0.0) { throw new ArgumentOutOfRangeException("step"); } Minimum = min; Maximum = max; Step = step; _read = read; _write = write; } public void Adjust(int direction) { _write(Math.Clamp(Value + (double)Math.Sign(direction) * Step, Minimum, Maximum)); } } public sealed class WristHubChoice : WristHubControl { private readonly Func<int> _read; private readonly Action<int> _write; public IReadOnlyList<string> Choices { get; } public int Index => Math.Clamp(_read(), 0, Choices.Count - 1); public string Value => Choices[Index]; internal WristHubChoice(string id, string label, IReadOnlyList<string> choices, Func<int> read, Action<int> write, string description) : base(id, label, description) { Choices = (from choice in choices?.Where((string choice) => !string.IsNullOrWhiteSpace(choice)) select choice.Trim()).ToArray() ?? throw new ArgumentNullException("choices"); if (Choices.Count == 0) { throw new ArgumentException("At least one choice is required.", "choices"); } _read = read; _write = write; } public void Adjust(int direction) { _write((Index + Math.Sign(direction) + Choices.Count) % Choices.Count); } } public sealed class WristHubTextInput : WristHubControl { private readonly Func<string> _read; private readonly Action<string> _write; public string Value => _read() ?? string.Empty; internal WristHubTextInput(string id, string label, Func<string> read, Action<string> write, string description) : base(id, label, description) { _read = read; _write = write; } public void Set(string value) { _write(value ?? string.Empty); } } public readonly record struct WristHubColorValue(byte Red, byte Green, byte Blue, byte Alpha = byte.MaxValue) { public string Hex => $"#{Red:X2}{Green:X2}{Blue:X2}{Alpha:X2}"; public static WristHubColorValue Cyan => new WristHubColorValue(38, 221, byte.MaxValue); public static WristHubColorValue Graphite => new WristHubColorValue(173, 184, 194); public static WristHubColorValue Crimson => new WristHubColorValue(byte.MaxValue, 61, 79); public static WristHubColorValue Emerald => new WristHubColorValue(61, 240, 140); public static WristHubColorValue Violet => new WristHubColorValue(173, 107, byte.MaxValue); public static WristHubColorValue Amber => new WristHubColorValue(byte.MaxValue, 168, 46); } public sealed class WristHubColorInput : WristHubControl { private static readonly WristHubColorValue[] DefaultPalette = new WristHubColorValue[6] { WristHubColorValue.Cyan, WristHubColorValue.Graphite, WristHubColorValue.Crimson, WristHubColorValue.Emerald, WristHubColorValue.Violet, WristHubColorValue.Amber }; private readonly Func<WristHubColorValue> _read; private readonly Action<WristHubColorValue> _write; public IReadOnlyList<WristHubColorValue> Palette { get; } public WristHubColorValue Value => _read(); internal WristHubColorInput(string id, string label, Func<WristHubColorValue> read, Action<WristHubColorValue> write, IReadOnlyList<WristHubColorValue>? palette, string description) : base(id, label, description) { _read = read; _write = write; Palette = ((palette != null && palette.Count > 0) ? palette.ToArray() : DefaultPalette); } public void Set(WristHubColorValue value) { _write(value); } public void Adjust(int direction) { WristHubColorValue value = Value; int val = -1; for (int i = 0; i < Palette.Count; i++) { if (Palette[i].Equals(value)) { val = i; break; } } val = (Math.Max(0, val) + Math.Sign(direction) + Palette.Count) % Palette.Count; _write(Palette[val]); } } public sealed class WristHubLabel : WristHubControl { private readonly Func<string> _read; public string Value => _read() ?? string.Empty; internal WristHubLabel(string id, string label, Func<string> read, string description) : base(id, label, description, WristHubAccent.Neutral) { _read = read; } } public sealed class WristHubPageLink : WristHubControl { public WristHubPage Page { get; } internal WristHubPageLink(string id, string label, WristHubPage page, string description) : base(id, label, description) { Page = page; } }