mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
1.22.4 (#927)
## Minor features - New option setting "sequential pre-caching" will prevent running out of memory when creating slide reel caches, at the cost of reduced performance when loading. ## Improvements - Added log message in the event that `QueryBody` could not find a body ## Bug fixes - Fixed `QueryBody` documentation to match API - Discovered that if you modify the default SolarySystem, `QuerySystem` will fail due to an IO error as the relative path is not set. While not an ideal solution, we have settled on just setting the relativePath if a mod adjusts the system. This will mean that the last mod loaded that makes these changes will have set the relative path. - Fixed ship HUD marker vanishing when Visible Stranger is installed Thank you Magnus for the `QueryBody` fixes!
This commit is contained in:
commit
d792d15dfd
@ -45,6 +45,8 @@ namespace NewHorizons.Builder.Props
|
|||||||
|
|
||||||
private static bool _isInit;
|
private static bool _isInit;
|
||||||
|
|
||||||
|
public static bool CacheExists(IModBehaviour mod) => Directory.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER));
|
||||||
|
|
||||||
internal static void InitPrefabs()
|
internal static void InitPrefabs()
|
||||||
{
|
{
|
||||||
if (_isInit) return;
|
if (_isInit) return;
|
||||||
@ -151,6 +153,9 @@ namespace NewHorizons.Builder.Props
|
|||||||
|
|
||||||
var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, true);
|
var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, true);
|
||||||
|
|
||||||
|
// If the cache doesn't exist it will be created here, slide reels only use the base image loader for cache creation so delete the images after to free memory
|
||||||
|
imageLoader.deleteTexturesWhenDone = !CacheExists(mod);
|
||||||
|
|
||||||
var key = GetUniqueSlideReelID(mod, info.slides);
|
var key = GetUniqueSlideReelID(mod, info.slides);
|
||||||
|
|
||||||
if (invImageLoader != null && atlasImageLoader != null)
|
if (invImageLoader != null && atlasImageLoader != null)
|
||||||
@ -220,7 +225,6 @@ namespace NewHorizons.Builder.Props
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Else when you put them down you can't pick them back up
|
// Else when you put them down you can't pick them back up
|
||||||
slideReelObj.GetComponent<OWCollider>()._physicsRemoved = false;
|
slideReelObj.GetComponent<OWCollider>()._physicsRemoved = false;
|
||||||
|
|
||||||
@ -345,6 +349,10 @@ namespace NewHorizons.Builder.Props
|
|||||||
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
|
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
|
||||||
|
|
||||||
var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false);
|
var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false);
|
||||||
|
|
||||||
|
// Autoprojector only uses the inverted images so the original can be destroyed if they are loaded (when creating the cached inverted images)
|
||||||
|
imageLoader.deleteTexturesWhenDone = true;
|
||||||
|
|
||||||
if (invImageLoader != null)
|
if (invImageLoader != null)
|
||||||
{
|
{
|
||||||
// Loaded directly from cache
|
// Loaded directly from cache
|
||||||
@ -505,7 +513,7 @@ namespace NewHorizons.Builder.Props
|
|||||||
|
|
||||||
var atlasKey = GetUniqueSlideReelID(mod, slides);
|
var atlasKey = GetUniqueSlideReelID(mod, slides);
|
||||||
|
|
||||||
var cacheExists = Directory.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER));
|
var cacheExists = CacheExists(mod);
|
||||||
|
|
||||||
NHLogger.Log($"Does cache exist for slide reels? {cacheExists}");
|
NHLogger.Log($"Does cache exist for slide reels? {cacheExists}");
|
||||||
|
|
||||||
@ -531,6 +539,7 @@ namespace NewHorizons.Builder.Props
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Used to then make cached stuff
|
||||||
imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png")));
|
imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -554,26 +563,31 @@ namespace NewHorizons.Builder.Props
|
|||||||
|
|
||||||
if (cacheExists)
|
if (cacheExists)
|
||||||
{
|
{
|
||||||
|
NHLogger.Log("Loading slide reels from cache");
|
||||||
|
|
||||||
if (useAtlasCache)
|
if (useAtlasCache)
|
||||||
{
|
{
|
||||||
atlasImageLoader.Start(false);
|
atlasImageLoader.Start(false, false);
|
||||||
}
|
}
|
||||||
// When using the inverted cache we never need the regular images
|
// When using the inverted cache we never need the regular images
|
||||||
if (useInvertedCache)
|
if (useInvertedCache)
|
||||||
{
|
{
|
||||||
invertedImageLoader.Start(true);
|
invertedImageLoader.Start(true, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
imageLoader.Start(true);
|
imageLoader.Start(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (invertedImageLoader, atlasImageLoader, imageLoader);
|
return (invertedImageLoader, atlasImageLoader, imageLoader);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
NHLogger.Log("Generating slide reel cache");
|
||||||
|
|
||||||
// Will be slow and create the cache if needed
|
// Will be slow and create the cache if needed
|
||||||
imageLoader.Start(true);
|
// Will run sequentially to ensure we don't run out of memory
|
||||||
|
imageLoader.Start(true, true);
|
||||||
|
|
||||||
return (null, null, imageLoader);
|
return (null, null, imageLoader);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,9 +46,10 @@ namespace NewHorizons
|
|||||||
// Settings
|
// Settings
|
||||||
public static bool Debug { get; private set; }
|
public static bool Debug { get; private set; }
|
||||||
public static bool VerboseLogs { get; private set; }
|
public static bool VerboseLogs { get; private set; }
|
||||||
private static bool _useCustomTitleScreen;
|
public static bool SequentialPreCaching { get; private set; }
|
||||||
|
public static bool CustomTitleScreen { get; private set; }
|
||||||
|
public static string DefaultSystemOverride { get; private set; }
|
||||||
private static bool _wasConfigured = false;
|
private static bool _wasConfigured = false;
|
||||||
private static string _defaultSystemOverride;
|
|
||||||
|
|
||||||
public static Dictionary<string, NewHorizonsSystem> SystemDict = new();
|
public static Dictionary<string, NewHorizonsSystem> SystemDict = new();
|
||||||
public static Dictionary<string, List<NewHorizonsBody>> BodyDict = new();
|
public static Dictionary<string, List<NewHorizonsBody>> BodyDict = new();
|
||||||
@ -59,7 +60,7 @@ namespace NewHorizons
|
|||||||
|
|
||||||
public static bool IsSystemReady { get; private set; }
|
public static bool IsSystemReady { get; private set; }
|
||||||
|
|
||||||
public string DefaultStarSystem => SystemDict.ContainsKey(_defaultSystemOverride) ? _defaultSystemOverride : _defaultStarSystem;
|
public string DefaultStarSystem => SystemDict.ContainsKey(DefaultSystemOverride) ? DefaultSystemOverride : _defaultStarSystem;
|
||||||
public string CurrentStarSystem
|
public string CurrentStarSystem
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -130,8 +131,9 @@ namespace NewHorizons
|
|||||||
|
|
||||||
var currentScene = SceneManager.GetActiveScene().name;
|
var currentScene = SceneManager.GetActiveScene().name;
|
||||||
|
|
||||||
Debug = config.GetSettingsValue<bool>("Debug");
|
Debug = config.GetSettingsValue<bool>(nameof(Debug));
|
||||||
VerboseLogs = config.GetSettingsValue<bool>("Verbose Logs");
|
VerboseLogs = config.GetSettingsValue<bool>(nameof(VerboseLogs));
|
||||||
|
SequentialPreCaching = config.GetSettingsValue<bool>(nameof(SequentialPreCaching));
|
||||||
|
|
||||||
if (currentScene == "SolarSystem")
|
if (currentScene == "SolarSystem")
|
||||||
{
|
{
|
||||||
@ -143,19 +145,19 @@ namespace NewHorizons
|
|||||||
else if (Debug) NHLogger.UpdateLogLevel(NHLogger.LogType.Log);
|
else if (Debug) NHLogger.UpdateLogLevel(NHLogger.LogType.Log);
|
||||||
else NHLogger.UpdateLogLevel(NHLogger.LogType.Error);
|
else NHLogger.UpdateLogLevel(NHLogger.LogType.Error);
|
||||||
|
|
||||||
var oldDefaultSystemOverride = _defaultSystemOverride;
|
var oldDefaultSystemOverride = DefaultSystemOverride;
|
||||||
_defaultSystemOverride = config.GetSettingsValue<string>("Default System Override");
|
DefaultSystemOverride = config.GetSettingsValue<string>(nameof(DefaultSystemOverride));
|
||||||
if (oldDefaultSystemOverride != _defaultSystemOverride)
|
if (oldDefaultSystemOverride != DefaultSystemOverride)
|
||||||
{
|
{
|
||||||
ResetCurrentStarSystem();
|
ResetCurrentStarSystem();
|
||||||
NHLogger.Log($"Changed default star system override to {_defaultSystemOverride}");
|
NHLogger.Log($"Changed default star system override to {DefaultSystemOverride}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var wasUsingCustomTitleScreen = _useCustomTitleScreen;
|
var wasUsingCustomTitleScreen = CustomTitleScreen;
|
||||||
_useCustomTitleScreen = config.GetSettingsValue<bool>("Custom title screen");
|
CustomTitleScreen = config.GetSettingsValue<bool>(nameof(CustomTitleScreen));
|
||||||
// Reload the title screen if this was updated on it
|
// Reload the title screen if this was updated on it
|
||||||
// Don't reload if we haven't configured yet (called on game start)
|
// Don't reload if we haven't configured yet (called on game start)
|
||||||
if (wasUsingCustomTitleScreen != _useCustomTitleScreen && SceneManager.GetActiveScene().name == "TitleScreen" && _wasConfigured)
|
if (wasUsingCustomTitleScreen != CustomTitleScreen && SceneManager.GetActiveScene().name == "TitleScreen" && _wasConfigured)
|
||||||
{
|
{
|
||||||
NHLogger.LogVerbose("Reloading");
|
NHLogger.LogVerbose("Reloading");
|
||||||
SceneManager.LoadScene("TitleScreen", LoadSceneMode.Single);
|
SceneManager.LoadScene("TitleScreen", LoadSceneMode.Single);
|
||||||
@ -428,7 +430,7 @@ namespace NewHorizons
|
|||||||
|
|
||||||
IsChangingStarSystem = false;
|
IsChangingStarSystem = false;
|
||||||
|
|
||||||
if (isTitleScreen && _useCustomTitleScreen)
|
if (isTitleScreen && CustomTitleScreen)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -692,8 +694,25 @@ namespace NewHorizons
|
|||||||
|
|
||||||
if (SystemDict.ContainsKey(starSystemName))
|
if (SystemDict.ContainsKey(starSystemName))
|
||||||
{
|
{
|
||||||
|
// Both changing the Mod and RelativePath are weird and will likely cause incompat issues if two mods both affected the same system
|
||||||
|
// It's mostly just to fix up the config compared to the default one NH makes for the base StarSystem
|
||||||
|
|
||||||
if (SystemDict[starSystemName].Config.GlobalMusic == null && SystemDict[starSystemName].Config.Skybox == null)
|
if (SystemDict[starSystemName].Config.GlobalMusic == null && SystemDict[starSystemName].Config.Skybox == null)
|
||||||
|
{
|
||||||
SystemDict[starSystemName].Mod = mod;
|
SystemDict[starSystemName].Mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a mod contains a change to the default system, set the relative path.
|
||||||
|
// Warning: If multiple systems make changes to the default system, only the relativePath will be set to the last mod loaded.
|
||||||
|
if (string.IsNullOrEmpty(SystemDict[starSystemName].RelativePath))
|
||||||
|
{
|
||||||
|
SystemDict[starSystemName].RelativePath = relativePath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NHLogger.LogWarning($"Two (or more) mods are making system changes to {starSystemName} which may result in errors");
|
||||||
|
}
|
||||||
|
|
||||||
SystemDict[starSystemName].Config.Merge(starSystemConfig);
|
SystemDict[starSystemName].Config.Merge(starSystemConfig);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1037,16 +1056,16 @@ namespace NewHorizons
|
|||||||
|
|
||||||
private void ResetCurrentStarSystem()
|
private void ResetCurrentStarSystem()
|
||||||
{
|
{
|
||||||
if (SystemDict.ContainsKey(_defaultSystemOverride))
|
if (SystemDict.ContainsKey(DefaultSystemOverride))
|
||||||
{
|
{
|
||||||
CurrentStarSystem = _defaultSystemOverride;
|
CurrentStarSystem = DefaultSystemOverride;
|
||||||
|
|
||||||
// #738 - Sometimes the override will not support spawning regularly, so always warp in if possible
|
// #738 - Sometimes the override will not support spawning regularly, so always warp in if possible
|
||||||
if (SystemDict[_defaultSystemOverride].Config.Vessel?.spawnOnVessel == true)
|
if (SystemDict[DefaultSystemOverride].Config.Vessel?.spawnOnVessel == true)
|
||||||
{
|
{
|
||||||
IsWarpingFromVessel = true;
|
IsWarpingFromVessel = true;
|
||||||
}
|
}
|
||||||
else if (BodyDict.TryGetValue(_defaultSystemOverride, out var bodies) && bodies.Any(x => x.Config?.Spawn?.shipSpawn != null))
|
else if (BodyDict.TryGetValue(DefaultSystemOverride, out var bodies) && bodies.Any(x => x.Config?.Spawn?.shipSpawn != null))
|
||||||
{
|
{
|
||||||
IsWarpingFromShip = true;
|
IsWarpingFromShip = true;
|
||||||
}
|
}
|
||||||
@ -1058,9 +1077,9 @@ namespace NewHorizons
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Ignore first load because it doesn't even know what systems we have
|
// Ignore first load because it doesn't even know what systems we have
|
||||||
if (!_firstLoad && !string.IsNullOrEmpty(_defaultSystemOverride))
|
if (!_firstLoad && !string.IsNullOrEmpty(DefaultSystemOverride))
|
||||||
{
|
{
|
||||||
NHLogger.LogError($"The given default system override {_defaultSystemOverride} is invalid - no system exists with that name");
|
NHLogger.LogError($"The given default system override {DefaultSystemOverride} is invalid - no system exists with that name");
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentStarSystem = _defaultStarSystem;
|
CurrentStarSystem = _defaultStarSystem;
|
||||||
|
|||||||
@ -143,9 +143,11 @@ namespace NewHorizons
|
|||||||
public object QueryBody(Type outType, string bodyName, string jsonPath)
|
public object QueryBody(Type outType, string bodyName, string jsonPath)
|
||||||
{
|
{
|
||||||
var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName);
|
var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName);
|
||||||
return planet == null
|
if (planet == null){
|
||||||
? null
|
NHLogger.LogError($"Could not find planet with body name {bodyName}.");
|
||||||
: QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath);
|
return null;
|
||||||
|
}
|
||||||
|
return QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T QueryBody<T>(string bodyName, string jsonPath)
|
public T QueryBody<T>(string bodyName, string jsonPath)
|
||||||
|
|||||||
@ -14,11 +14,11 @@ namespace NewHorizons.Patches.HUDPatches
|
|||||||
bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye();
|
bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye();
|
||||||
bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsShipInside());
|
bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsShipInside());
|
||||||
bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside;
|
bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside;
|
||||||
bool insideIP = Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak;
|
bool insideIPMatches = Locator.GetCloakFieldController() == null || Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak;
|
||||||
bool insideCloak = CloakSectorController.isPlayerInside == CloakSectorController.isShipInside;
|
bool insideCloakMatches = CloakSectorController.isPlayerInside == CloakSectorController.isShipInside;
|
||||||
bool sameInterference = InterferenceHandler.IsPlayerSameAsShip();
|
bool sameInterference = InterferenceHandler.IsPlayerSameAsShip();
|
||||||
|
|
||||||
__instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIP && insideCloak && sameInterference;
|
__instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIPMatches && insideCloakMatches && sameInterference;
|
||||||
|
|
||||||
if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible);
|
if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible);
|
||||||
|
|
||||||
|
|||||||
@ -72,6 +72,11 @@ namespace NewHorizons.Utility.Files
|
|||||||
{
|
{
|
||||||
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename);
|
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename);
|
||||||
var key = GetKey(path);
|
var key = GetKey(path);
|
||||||
|
DeleteTexture(key, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteTexture(string key, Texture2D texture)
|
||||||
|
{
|
||||||
if (_textureCache.ContainsKey(key))
|
if (_textureCache.ContainsKey(key))
|
||||||
{
|
{
|
||||||
if (_textureCache[key] == texture)
|
if (_textureCache[key] == texture)
|
||||||
@ -103,6 +108,16 @@ namespace NewHorizons.Utility.Files
|
|||||||
public static Texture2D InvertSlideReel(IModBehaviour mod, Texture2D texture, string originalPath)
|
public static Texture2D InvertSlideReel(IModBehaviour mod, Texture2D texture, string originalPath)
|
||||||
{
|
{
|
||||||
var key = $"{texture.name} > invert";
|
var key = $"{texture.name} > invert";
|
||||||
|
var cachedPath = "";
|
||||||
|
|
||||||
|
// If we're going to end up caching the texture we must make sure it will end up using the same key
|
||||||
|
// Not sure why we check if the originalPath is null but it did that before so
|
||||||
|
if (!string.IsNullOrEmpty(originalPath))
|
||||||
|
{
|
||||||
|
cachedPath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.INVERTED_SLIDE_CACHE_FOLDER, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, ""));
|
||||||
|
key = GetKey(cachedPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture;
|
if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture;
|
||||||
|
|
||||||
var pixels = texture.GetPixels();
|
var pixels = texture.GetPixels();
|
||||||
@ -138,12 +153,11 @@ namespace NewHorizons.Utility.Files
|
|||||||
|
|
||||||
// Since doing this is expensive we cache the results to the disk
|
// Since doing this is expensive we cache the results to the disk
|
||||||
// Preloading cached values is done in ProjectionBuilder
|
// Preloading cached values is done in ProjectionBuilder
|
||||||
if (!string.IsNullOrEmpty(originalPath))
|
if (!string.IsNullOrEmpty(cachedPath))
|
||||||
{
|
{
|
||||||
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.INVERTED_SLIDE_CACHE_FOLDER, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, ""));
|
NHLogger.LogVerbose($"Caching inverted image to {cachedPath}");
|
||||||
NHLogger.LogVerbose($"Caching inverted image to {path}");
|
Directory.CreateDirectory(Path.GetDirectoryName(cachedPath));
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
File.WriteAllBytes(cachedPath, newTexture.EncodeToPNG());
|
||||||
File.WriteAllBytes(path, newTexture.EncodeToPNG());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newTexture;
|
return newTexture;
|
||||||
|
|||||||
@ -17,12 +17,22 @@ public class SlideReelAsyncImageLoader
|
|||||||
{
|
{
|
||||||
public List<(int index, string path)> PathsToLoad { get; private set; } = new();
|
public List<(int index, string path)> PathsToLoad { get; private set; } = new();
|
||||||
|
|
||||||
|
private Dictionary<string, Texture2D> _loadedTextures = new();
|
||||||
|
|
||||||
public class ImageLoadedEvent : UnityEvent<Texture2D, int, string> { }
|
public class ImageLoadedEvent : UnityEvent<Texture2D, int, string> { }
|
||||||
public ImageLoadedEvent imageLoadedEvent = new();
|
public ImageLoadedEvent imageLoadedEvent = new();
|
||||||
|
|
||||||
public bool FinishedLoading { get; private set; }
|
public bool FinishedLoading { get; private set; }
|
||||||
private int _loadedCount = 0;
|
private int _loadedCount = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If we are loading images where:
|
||||||
|
/// 1) The slide reel cache does not exist
|
||||||
|
/// 2) The loader would only use the images to make the cache
|
||||||
|
/// Then we want to delete them immediately after loading, in order to save memory
|
||||||
|
/// </summary>
|
||||||
|
public bool deleteTexturesWhenDone = false;
|
||||||
|
|
||||||
// TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader,
|
// TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader,
|
||||||
// and make use of that for at least for projector stuff (require player to be in the same sector as the slides
|
// and make use of that for at least for projector stuff (require player to be in the same sector as the slides
|
||||||
// for them to start loading, and unload when the player leaves)
|
// for them to start loading, and unload when the player leaves)
|
||||||
@ -31,7 +41,7 @@ public class SlideReelAsyncImageLoader
|
|||||||
private bool _started;
|
private bool _started;
|
||||||
private bool _clamp;
|
private bool _clamp;
|
||||||
|
|
||||||
public void Start(bool clamp)
|
public void Start(bool clamp, bool sequential)
|
||||||
{
|
{
|
||||||
if (_started) return;
|
if (_started) return;
|
||||||
|
|
||||||
@ -46,17 +56,43 @@ public class SlideReelAsyncImageLoader
|
|||||||
|
|
||||||
NHLogger.LogVerbose("Loading new slide reel");
|
NHLogger.LogVerbose("Loading new slide reel");
|
||||||
imageLoadedEvent.AddListener(OnImageLoaded);
|
imageLoadedEvent.AddListener(OnImageLoaded);
|
||||||
SingletonSlideReelAsyncImageLoader.Instance.Load(this);
|
SingletonSlideReelAsyncImageLoader.Instance.Load(this, sequential);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnImageLoaded(Texture texture, int index, string originalPath)
|
private void OnImageLoaded(Texture texture, int index, string originalPath)
|
||||||
{
|
{
|
||||||
_loadedCount++;
|
_loadedCount++;
|
||||||
|
|
||||||
|
var key = ImageUtilities.GetKey(originalPath);
|
||||||
|
_loadedTextures[key] = texture as Texture2D;
|
||||||
|
|
||||||
if (_loadedCount >= PathsToLoad.Count)
|
if (_loadedCount >= PathsToLoad.Count)
|
||||||
{
|
{
|
||||||
NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}");
|
NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}");
|
||||||
FinishedLoading = true;
|
FinishedLoading = true;
|
||||||
|
|
||||||
|
if (deleteTexturesWhenDone)
|
||||||
|
{
|
||||||
|
DeleteLoadedImages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteLoadedImages()
|
||||||
|
{
|
||||||
|
foreach (var (key, texture) in _loadedTextures)
|
||||||
|
{
|
||||||
|
ImageUtilities.DeleteTexture(key, texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator DownloadTextures()
|
||||||
|
{
|
||||||
|
foreach (var (index, path) in PathsToLoad)
|
||||||
|
{
|
||||||
|
NHLogger.LogVerbose($"Loaded slide reel {index} of {PathsToLoad.Count}");
|
||||||
|
|
||||||
|
yield return DownloadTexture(path, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +143,7 @@ public class SlideReelAsyncImageLoader
|
|||||||
|
|
||||||
var time = DateTime.Now;
|
var time = DateTime.Now;
|
||||||
imageLoadedEvent?.Invoke(texture, index, url);
|
imageLoadedEvent?.Invoke(texture, index, url);
|
||||||
|
|
||||||
NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms");
|
NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,6 +152,10 @@ public class SlideReelAsyncImageLoader
|
|||||||
{
|
{
|
||||||
public static SingletonSlideReelAsyncImageLoader Instance { get; private set; }
|
public static SingletonSlideReelAsyncImageLoader Instance { get; private set; }
|
||||||
|
|
||||||
|
private Queue<SlideReelAsyncImageLoader> _loaders = new();
|
||||||
|
|
||||||
|
private bool _isLoading;
|
||||||
|
|
||||||
public void Awake()
|
public void Awake()
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
@ -124,20 +165,48 @@ public class SlideReelAsyncImageLoader
|
|||||||
private void OnSceneUnloaded(Scene _)
|
private void OnSceneUnloaded(Scene _)
|
||||||
{
|
{
|
||||||
StopAllCoroutines();
|
StopAllCoroutines();
|
||||||
|
_loaders.Clear();
|
||||||
|
_isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Load(SlideReelAsyncImageLoader loader)
|
public void Load(SlideReelAsyncImageLoader loader, bool sequential)
|
||||||
{
|
{
|
||||||
// Delay at least one frame to let things subscribe to the event before it fires
|
// Delay at least one frame to let things subscribe to the event before it fires
|
||||||
Delay.FireOnNextUpdate(() =>
|
Delay.FireOnNextUpdate(() =>
|
||||||
{
|
{
|
||||||
foreach (var (index, path) in loader.PathsToLoad)
|
if (sequential && Main.SequentialPreCaching)
|
||||||
{
|
{
|
||||||
NHLogger.LogVerbose($"Loaded slide reel {index} of {loader.PathsToLoad.Count}");
|
// Sequential
|
||||||
|
_loaders.Enqueue(loader);
|
||||||
|
if (!_isLoading)
|
||||||
|
{
|
||||||
|
StartCoroutine(LoadAllSequential());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var (index, path) in loader.PathsToLoad)
|
||||||
|
{
|
||||||
|
NHLogger.LogVerbose($"Loaded slide reel {index} of {loader.PathsToLoad.Count}");
|
||||||
|
|
||||||
StartCoroutine(loader.DownloadTexture(path, index));
|
StartCoroutine(loader.DownloadTexture(path, index));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerator LoadAllSequential()
|
||||||
|
{
|
||||||
|
NHLogger.Log("Loading slide reels");
|
||||||
|
_isLoading = true;
|
||||||
|
while (_loaders.Count > 0)
|
||||||
|
{
|
||||||
|
var loader = _loaders.Dequeue();
|
||||||
|
yield return loader.DownloadTextures();
|
||||||
|
NHLogger.Log($"Finished a slide reel, {_loaders.Count} left");
|
||||||
|
}
|
||||||
|
_isLoading = false;
|
||||||
|
NHLogger.Log("Done loading slide reels");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,38 @@
|
|||||||
{
|
{
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"settings": {
|
"settings": {
|
||||||
"Debug": false,
|
"CustomTitleScreen": {
|
||||||
"Custom title screen": true,
|
"title": "Custom Title Screen",
|
||||||
"Default System Override": "",
|
"type": "toggle",
|
||||||
"Verbose Logs": false
|
"value": true,
|
||||||
}
|
"tooltip": "Displays planets from installed mods on the title screen."
|
||||||
|
},
|
||||||
|
"DebugSeparator": {
|
||||||
|
"type": "separator"
|
||||||
|
},
|
||||||
|
"Debug": {
|
||||||
|
"title": "Debug",
|
||||||
|
"type": "toggle",
|
||||||
|
"value": false,
|
||||||
|
"tooltip": "Enables the debug raycast, visible quantum object colliders, and debug options menu."
|
||||||
|
},
|
||||||
|
"VerboseLogs": {
|
||||||
|
"title": "Verbose Logs",
|
||||||
|
"type": "toggle",
|
||||||
|
"value": false,
|
||||||
|
"tooltip": "Makes logs much more detailed. Useful when debugging."
|
||||||
|
},
|
||||||
|
"SequentialPreCaching": {
|
||||||
|
"title": "Sequential Pre-caching",
|
||||||
|
"type": "toggle",
|
||||||
|
"value": false,
|
||||||
|
"tooltip": "This is a debug option intended for mod creators. Prevents running out of memory when computing slide reel caches, but is slower and will cause in-game lag."
|
||||||
|
},
|
||||||
|
"DefaultSystemOverride": {
|
||||||
|
"title": "Default System Override",
|
||||||
|
"type": "text",
|
||||||
|
"value": "",
|
||||||
|
"tooltip": "Forces the player to spawn in this system after death. Must match the unique name of a system, eg, xen.NewHorizonsExamples"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -4,10 +4,10 @@
|
|||||||
"author": "xen, Bwc9876, JohnCorby, MegaPiggy, Clay, Trifid, and friends",
|
"author": "xen, Bwc9876, JohnCorby, MegaPiggy, Clay, Trifid, and friends",
|
||||||
"name": "New Horizons",
|
"name": "New Horizons",
|
||||||
"uniqueName": "xen.NewHorizons",
|
"uniqueName": "xen.NewHorizons",
|
||||||
"version": "1.22.3",
|
"version": "1.22.4",
|
||||||
"owmlVersion": "2.12.1",
|
"owmlVersion": "2.12.1",
|
||||||
"dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ],
|
"dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ],
|
||||||
"conflicts": [ "PacificEngine.OW_CommonResources" ],
|
"conflicts": [ "PacificEngine.OW_CommonResources" ],
|
||||||
"pathsToPreserve": [ "planets", "systems", "translations" ],
|
"pathsToPreserve": [ "planets", "systems", "translations" ],
|
||||||
"donateLink": "https://www.patreon.com/ownh"
|
"donateLink": "https://www.patreon.com/xen42"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ Then, use the `QueryBody` method:
|
|||||||
var api = ModHelper.Interactions.TryGetModApi<INewHorizons>("xen.NewHorizons");
|
var api = ModHelper.Interactions.TryGetModApi<INewHorizons>("xen.NewHorizons");
|
||||||
api.GetBodyLoadedEvent().AddListener((name) => {
|
api.GetBodyLoadedEvent().AddListener((name) => {
|
||||||
ModHelper.Console.WriteLine($"Body: {name} Loaded!");
|
ModHelper.Console.WriteLine($"Body: {name} Loaded!");
|
||||||
var data = api.QueryBody<MyCoolExtensionData>("$.extras.myCoolExtensionData", name);
|
var data = api.QueryBody<MyCoolExtensionData>(name, "$.extras.myCoolExtensionData");
|
||||||
// Makes sure the module is not null
|
// Makes sure the module is not null
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!");
|
ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user