/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using System; using System.Collections.Generic; using System.IO; using GameCult.Aetheria.State.Verse; using GameCult.Mesh; using UnityEngine; public static class AetheriaUnityRuntimeClientProvider { private static readonly Dictionary RuntimeClients = new Dictionary(StringComparer.Ordinal); private static CultMeshReactiveDocument _playerSettingsDocument; private static RuntimePlayerSettings _runtimePlayerSettings; private static bool _playerSettingsLoaded; public static RuntimePlayerSettings PlayerSettings { get { _runtimePlayerSettings ??= CreateDefaultPlayerSettings(); RefreshPlayerSettings(_runtimePlayerSettings); return _runtimePlayerSettings; } } private static AetheriaClient ResolveClient(string stateFilePath, string runtimeId = "") { var effectiveRuntimeId = string.IsNullOrWhiteSpace(runtimeId) ? "raven-unity" : runtimeId; var cacheKey = CacheKey(stateFilePath, effectiveRuntimeId); if (RuntimeClients.TryGetValue(cacheKey, out var runtimeClient)) return runtimeClient; runtimeClient = AetheriaClient .OpenAsync( stateFilePath, effectiveRuntimeId, "local", startServer: false, pullOnOpen: true) .GetAwaiter() .GetResult(); RuntimeClients[cacheKey] = runtimeClient; return runtimeClient; } private static AetheriaClient ResolveClient(AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { if (stateBoot == null) throw new ArgumentNullException(nameof(stateBoot)); return ResolveClient( stateBoot.StateFilePath, string.IsNullOrWhiteSpace(runtimeId) ? stateBoot.RuntimeId : runtimeId); } private static AetheriaClient RuntimeClient(string runtimeId = "") { return ResolveClient(AetheriaUnityRuntimePaths.RuntimeStateFilePath, runtimeId); } private static AetheriaClient RuntimeClient( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return ResolveClient(stateBoot, runtimeId); } public static AetheriaClientState RuntimeState(string runtimeId = "") { return RuntimeClient(runtimeId).State; } public static AetheriaClientState RuntimeState( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return RuntimeClient(stateBoot, runtimeId).State; } public static AetheriaControl Control(string runtimeId = "") { return RuntimeClient(runtimeId).Control; } public static AetheriaControl Control( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return RuntimeClient(stateBoot, runtimeId).Control; } public static AetheriaUi Ui(string runtimeId = "") { return RuntimeClient(runtimeId).Ui; } public static AetheriaUi Ui( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return RuntimeClient(stateBoot, runtimeId).Ui; } public static CultMeshReactiveDocument Reactive(string runtimeId = "") where TDocument : class { return RuntimeState(runtimeId).Reactive(); } public static CultMeshReactiveDocument ReactiveCurrentDocking( string runtimeId = "") { return RuntimeState(runtimeId).ReactiveCurrentDocking(); } public static CultMeshReactiveDocument ReactiveCurrentDocking( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return RuntimeState(stateBoot, runtimeId).ReactiveCurrentDocking(); } public static CultMeshReactiveDocument ReactiveCurrentEntity( string runtimeId = "") { return RuntimeState(runtimeId).ReactiveCurrentEntity(); } public static CultMeshReactiveDocument ReactiveCurrentEntity( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return RuntimeState(stateBoot, runtimeId).ReactiveCurrentEntity(); } public static CultMeshReactiveDocument ReactiveStationRefit( string runtimeId = "") { return RuntimeState(runtimeId).ReactiveStationRefit(); } public static CultMeshReactiveDocument ReactiveStationRefit( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return RuntimeState(stateBoot, runtimeId).ReactiveStationRefit(); } public static CultMeshReactiveDocument Reactive( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") where TDocument : class { return RuntimeState(stateBoot, runtimeId).Reactive(); } public static CultMeshReactiveDocument Reactive( AetheriaClientEveSurface surface, string runtimeId = "") where TDocument : class { return RuntimeState(runtimeId).Reactive(surface); } public static CultMeshReactiveDocument Reactive( AetheriaRuntimeStateBootReport stateBoot, AetheriaClientEveSurface surface, string runtimeId = "") where TDocument : class { return RuntimeState(stateBoot, runtimeId).Reactive(surface); } public static CultMeshReactiveDocument ReactiveEveSurface( string surfaceId, string runtimeId = "") { return RuntimeState(runtimeId).ReactiveEveSurface(surfaceId); } public static CultMeshReactiveDocument ReactiveEveSurface( AetheriaRuntimeStateBootReport stateBoot, string surfaceId, string runtimeId = "") { return RuntimeState(stateBoot, runtimeId).ReactiveEveSurface(surfaceId); } public static CultMeshReactiveDocument Reactive( AetheriaRuntimeRtsViewportBounds viewport, string runtimeId = "") where TDocument : class { return RuntimeState(runtimeId).Reactive(viewport); } public static CultMeshReactiveDocument Reactive( AetheriaRuntimeStateBootReport stateBoot, AetheriaRuntimeRtsViewportBounds viewport, string runtimeId = "") where TDocument : class { return RuntimeState(stateBoot, runtimeId).Reactive(viewport); } public static CultMeshReactiveDocument Reactive( int index, string runtimeId = "") where TDocument : class { return RuntimeState(runtimeId).Reactive(index); } public static CultMeshReactiveDocument Reactive( AetheriaRuntimeStateBootReport stateBoot, int index, string runtimeId = "") where TDocument : class { return RuntimeState(stateBoot, runtimeId).Reactive(index); } public static CultMeshStateRefResolver EveSurfaceStateRefResolver(string runtimeId = "") { return RuntimeState(runtimeId).CreateEveSurfaceCultMeshStateRefResolver(); } public static CultMeshStateRefResolver EveSurfaceStateRefResolver( AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") { return RuntimeState(stateBoot, runtimeId).CreateEveSurfaceCultMeshStateRefResolver(); } public static void Dispose() { _playerSettingsDocument?.Dispose(); _playerSettingsDocument = null; _runtimePlayerSettings = null; _playerSettingsLoaded = false; foreach (var client in RuntimeClients.Values) client.Dispose(); RuntimeClients.Clear(); } private static string CacheKey(string stateFilePath, string runtimeId) { return (stateFilePath ?? "") + "\n" + runtimeId; } private static void RefreshPlayerSettings(RuntimePlayerSettings settings) { try { _playerSettingsDocument ??= Reactive(); var stored = _playerSettingsDocument.Current; if (stored == null) return; ApplyPlayerSettings(settings, stored); if (!_playerSettingsLoaded) { Debug.Log("Loaded Aetheria Verse player settings from reactive typed state."); _playerSettingsLoaded = true; } } catch (FileNotFoundException) { } catch (Exception ex) { Debug.LogWarning($"Failed to load Aetheria Verse player settings from typed state; using defaults: {ex}"); } } private static RuntimePlayerSettings CreateDefaultPlayerSettings() { var settings = new RuntimePlayerSettings(); settings.Name = Environment.UserName; settings.InputSettings.SetActionBarInputEnabled("/leftShift", true); settings.InputSettings.SetActionBarInputEnabled("/leftButton", true); settings.InputSettings.SetActionBarInputEnabled("/rightButton", true); settings.InputSettings.SetActionBarInputEnabled("/middleButton", true); for (var i = 1; i < 6; i++) settings.InputSettings.SetActionBarInputEnabled($"/{i}", true); return settings; } private static void ApplyPlayerSettings( RuntimePlayerSettings settings, AetheriaRuntimePlayerSettingsDocument stored) { if (!string.IsNullOrWhiteSpace(stored.PlayerName)) settings.Name = stored.PlayerName; settings.TutorialPassed = stored.TutorialPassed; settings.HashedStoryFiles.Clear(); foreach (var storyFileHash in stored.StoryFileHashes) { if (!string.IsNullOrWhiteSpace(storyFileHash.StoryPath)) settings.HashedStoryFiles[storyFileHash.StoryPath] = storyFileHash.Hash; } if (Enum.TryParse(stored.TemperatureUnit, out TemperatureUnit temperatureUnit)) settings.GameplaySettings.TemperatureUnit = temperatureUnit; settings.GameplaySettings.SignificantDigits = Math.Max(0, stored.SignificantDigits); if (Enum.TryParse(stored.NebulaQuality, out Quality nebulaQuality)) settings.GraphicsSettings.NebulaQuality = nebulaQuality; settings.GraphicsSettings.ShowAsteroidsInMinimap = stored.ShowAsteroidsInMinimap; settings.InputSettings.InputActionMap.Clear(); foreach (var binding in stored.BindingOverrides) { if (string.IsNullOrWhiteSpace(binding.ActionName) || string.IsNullOrWhiteSpace(binding.BindingPath) || binding.BindingIndex < 0) { continue; } settings.InputSettings.SetBindingOverride(binding.ActionName, binding.BindingIndex, binding.BindingPath); } settings.InputSettings.ActionBarInputs.Clear(); foreach (var input in stored.ActionBarInputs) { if (!string.IsNullOrWhiteSpace(input)) settings.InputSettings.SetActionBarInputEnabled(input, true); } } }