diff --git a/Mapify/Map/MapLifeCycle.cs b/Mapify/Map/MapLifeCycle.cs index ebe42eb..b63cec2 100644 --- a/Mapify/Map/MapLifeCycle.cs +++ b/Mapify/Map/MapLifeCycle.cs @@ -19,6 +19,7 @@ using UnityEngine; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; +using Random = UnityEngine.Random; namespace Mapify.Map { @@ -29,8 +30,7 @@ public static class MapLifeCycle public static Action OnCleanup; private static bool isMapLoaded; - private static List assets_assetBundles; - private static AssetBundle scenes; + private static List loadedAssetBundles; private static string originalRailwayScenePath; private static string originalGameContentScenePath; private static int scenesToLoad; @@ -51,13 +51,60 @@ public static IEnumerator LoadMap(BasicMapInfo basicMapInfo) yield return null; // Load asset bundles + loadedAssetBundles = new List(); string mapDir = Maps.GetDirectory(basicMapInfo); - string[] assets_assetBundlePaths = Maps.GetMapAssets(Names.ASSETS_ASSET_BUNDLES_PREFIX+"*", mapDir); - assets_assetBundles = new List(assets_assetBundlePaths.Length); - foreach (var ass in assets_assetBundlePaths) + // Register mapinfo + Mapify.LogDebug(() => $"Loading AssetBundle '{Names.MAP_INFO_ASSET_BUNDLE}'"); + AssetBundleCreateRequest mapInfoRequest = AssetBundle.LoadFromFileAsync(Maps.GetMapAsset(Names.MAP_INFO_ASSET_BUNDLE, mapDir)); + do { - var assetFileName = Path.GetFileName(ass); + loadingInfo.UpdateLoadingStatus(loadingMapLogMsg, Mathf.RoundToInt(mapInfoRequest.progress * 100)); + yield return null; + } while (!mapInfoRequest.isDone); + + MapInfo mapInfo = null; + if (mapInfoRequest.assetBundle is null) + { + // Warning and not Error because this occurs if the map is built with an older version of Mapify, and then its not a problem + Debug.LogWarning("Failed to load the mapinfo bundle"); + } + else + { + loadedAssetBundles.Add(mapInfoRequest.assetBundle); + var idk = mapInfoRequest.assetBundle.LoadAllAssets(); + foreach (var aaa in idk) + { + if (aaa is MapInfo info) + { + mapInfo = info; + break; + } + } + + if (mapInfo is null) + { + Mapify.LogError($"Failed to find {nameof(MapInfo)}!"); + SceneSwitcher.SwitchToScene(DVScenes.MainMenu); + yield break; + } + + //LoadingScreenImages will be null if the map was built with an older version of Mapify + if (mapInfo.LoadingScreenImages != null && mapInfo.LoadingScreenImages.Length > 0) + { + ShowLoadingScreenImage(mapInfo); + } + if (mapInfo.LoadingScreenMusic != null) + { + PlayLoadingScreenMusic(mapInfo.LoadingScreenMusic); + } + } + + string[] miscAssets_bundlePaths = Maps.GetMapAssets(Names.MISC_ASSETS_ASSET_BUNDLES_PREFIX+"*", mapDir); + + foreach (var bundlePath in miscAssets_bundlePaths) + { + var assetFileName = Path.GetFileName(bundlePath); if (assetFileName.EndsWith(".manifest")) { continue; } @@ -70,7 +117,14 @@ public static IEnumerator LoadMap(BasicMapInfo basicMapInfo) yield return null; } while (!assetsReq.isDone); - assets_assetBundles.Add(assetsReq.assetBundle); + loadedAssetBundles.Add(assetsReq.assetBundle); + + // in maps exported with older versions of Mapify the mapInfo is in the misc assets assetbundle + if (mapInfo is null) + { + mapInfo = assetsReq.assetBundle.LoadAllAssets()[0]; + Maps.RegisterLoadedMap(mapInfo); + } } Mapify.LogDebug(() => $"Loading AssetBundle '{Names.SCENES_ASSET_BUNDLE}'"); @@ -82,24 +136,7 @@ public static IEnumerator LoadMap(BasicMapInfo basicMapInfo) yield return null; } while (!scenesReq.isDone); - scenes = scenesReq.assetBundle; - - // Register mapinfo - Mapify.LogDebug(() => $"Loading AssetBundle '{Names.MAP_INFO_ASSET_BUNDLE}'"); - AssetBundleCreateRequest mapInfoRequest = AssetBundle.LoadFromFileAsync(Maps.GetMapAsset(Names.MAP_INFO_ASSET_BUNDLE, mapDir)); - do - { - loadingInfo.UpdateLoadingStatus(loadingMapLogMsg, Mathf.RoundToInt(mapInfoRequest.progress * 100)); - yield return null; - } while (!mapInfoRequest.isDone); - - var mapInfo = mapInfoRequest.assetBundle.LoadAllAssets()[0]; - if (mapInfo is null) - { - Debug.LogError($"Failed to find {nameof(MapInfo)}!"); - SceneSwitcher.SwitchToScene(DVScenes.MainMenu); - yield break; - } + loadedAssetBundles.Add(scenesReq.assetBundle); Maps.RegisterLoadedMap(mapInfo); @@ -140,11 +177,25 @@ public static IEnumerator LoadMap(BasicMapInfo basicMapInfo) Mapify.Log("Vanilla scenes unloaded"); MonoBehaviourDisablerPatch.EnableAll(); - // Set LevelInfo + SetLevelInfo(mapInfo); + SetupStreamer(wsi.gameObject, mapInfo); + + InitializeLists(); + WorldStreamingInit_Awake_Patch.CanInitialize = true; + + foreach (VanillaAsset nonInstantiatableAsset in Enum.GetValues(typeof(VanillaAsset)).Cast().Where(e => !AssetCopier.InstantiatableAssets.Contains(e))) + Mapify.LogError($"VanillaAsset {nonInstantiatableAsset} wasn't set in the AssetCopier! You MUST fix this!"); + + // Auto-save won't work without this line. + SaveGameManager.Instance.disableAutosave = false; + } + + private static void SetLevelInfo(MapInfo mapInfo) + { LevelInfo levelInfo = SingletonBehaviour.Instance; levelInfo.terrainSize = mapInfo.terrainSize; levelInfo.waterLevel = mapInfo.waterLevel; - levelInfo.worldSize = mapInfo.worldSize; + levelInfo.worldSize = new Vector3(mapInfo.worldSize, levelInfo.worldSize.y, mapInfo.worldSize); levelInfo.worldOffset = Vector3.zero; levelInfo.defaultSpawnPosition = mapInfo.defaultSpawnPosition; levelInfo.defaultSpawnRotation = mapInfo.defaultSpawnRotation; @@ -152,17 +203,34 @@ public static IEnumerator LoadMap(BasicMapInfo basicMapInfo) levelInfo.newCareerSpawnRotation = mapInfo.defaultSpawnRotation; levelInfo.enforceBoundary = true; levelInfo.worldBoundaryMargin = mapInfo.worldBoundaryMargin; + } - SetupStreamer(wsi.gameObject, mapInfo); + private static void ShowLoadingScreenImage(MapInfo mapInfo) + { + Mapify.Log("Showing custom loading screen image"); - InitializeLists(); - WorldStreamingInit_Awake_Patch.CanInitialize = true; + var randomInt = Random.Range(0, mapInfo.LoadingScreenImages.Length); + var customImage = mapInfo.LoadingScreenImages[randomInt]; - foreach (VanillaAsset nonInstantiatableAsset in Enum.GetValues(typeof(VanillaAsset)).Cast().Where(e => !AssetCopier.InstantiatableAssets.Contains(e))) - Mapify.LogError($"VanillaAsset {nonInstantiatableAsset} wasn't set in the AssetCopier! You MUST fix this!"); + var canvasGameObject = Object.FindObjectsOfType().FirstOrDefault(gameObject => gameObject.name.Contains("LoadImage_Background_")); - // Auto-save won't work without this line. - SaveGameManager.Instance.disableAutosave = false; + if (canvasGameObject is null) + { + Mapify.LogError("cant find canvasGameObject"); + return; + } + + // set the image + canvasGameObject.GetComponent().SetTexture(customImage); + } + + private static void PlayLoadingScreenMusic(AudioClip mapInfoLoadingScreenMusic) + { + Mapify.Log("Playing custom loading screen music"); + var mainMenuMusicSource = GameObject.Find("Audio Source - main menu music").GetComponent(); + mainMenuMusicSource.Pause(); + mainMenuMusicSource.clip = mapInfoLoadingScreenMusic; + mainMenuMusicSource.Play(); } private static void SetupStreamer(GameObject parent, MapInfo mapInfo) @@ -245,7 +313,7 @@ private static void InitializeLists() private static void Cleanup() { OnCleanup(); - Maps.UnreigsterLoadedMap(); + Maps.UnregisterLoadedMap(); SceneManager.sceneLoaded -= OnSceneLoad; WorldStreamingInit_Awake_Patch.CanInitialize = false; AssetCopier.Cleanup(); @@ -253,7 +321,7 @@ private static void Cleanup() originalGameContentScenePath = null; scenesToLoad = 0; - foreach (AssetBundle bundle in assets_assetBundles) + foreach (AssetBundle bundle in loadedAssetBundles) { if (bundle != null) { @@ -261,14 +329,7 @@ private static void Cleanup() } } - assets_assetBundles = null; - - if (scenes != null) - { - scenes.Unload(true); - scenes = null; - } - + loadedAssetBundles = null; isMapLoaded = false; } } diff --git a/Mapify/Map/Maps.cs b/Mapify/Map/Maps.cs index ccf2428..4d64672 100644 --- a/Mapify/Map/Maps.cs +++ b/Mapify/Map/Maps.cs @@ -12,6 +12,7 @@ namespace Mapify.Map { public static class Maps { + //the base DV map public static readonly BasicMapInfo DEFAULT_MAP_INFO = new BasicMapInfo(Names.DEFAULT_MAP_NAME, $"{BuildInfo.BUILD_VERSION_MAJOR}", null); public static Action OnMapsUpdated; @@ -118,7 +119,7 @@ public static void RegisterLoadedMap(MapInfo mapInfo) LoadedMap = mapInfo; } - public static void UnreigsterLoadedMap() + public static void UnregisterLoadedMap() { IsDefaultMap = true; LoadedMap = null; diff --git a/Mapify/Mapify.csproj b/Mapify/Mapify.csproj index 350321b..5d88dff 100644 --- a/Mapify/Mapify.csproj +++ b/Mapify/Mapify.csproj @@ -59,6 +59,7 @@ + @@ -66,6 +67,7 @@ + diff --git a/Mapify/Patches/MainMenuPatch.cs b/Mapify/Patches/MainMenuPatch.cs new file mode 100644 index 0000000..18cdeec --- /dev/null +++ b/Mapify/Patches/MainMenuPatch.cs @@ -0,0 +1,14 @@ +using DV.UI; +using HarmonyLib; + +namespace Mapify.Patches +{ + [HarmonyPatch(typeof(MainMenu), nameof(MainMenu.GoBackToMainMenu))] + public static class MainMenuPatch + { + private static void Postfix() + { + StreamedObjectInitPatch.ResetStreamers(); + } + } +} diff --git a/Mapify/Patches/StreamedObjectInitPatch.cs b/Mapify/Patches/StreamedObjectInitPatch.cs index bcacc7c..756c229 100644 --- a/Mapify/Patches/StreamedObjectInitPatch.cs +++ b/Mapify/Patches/StreamedObjectInitPatch.cs @@ -14,16 +14,21 @@ namespace Mapify.Patches [HarmonyPatch(typeof(StreamedObjectInit), nameof(StreamedObjectInit.Start))] public static class StreamedObjectInitPatch { + private static bool streamersSet = false; private static Streamer[] streamers; private static void Postfix(StreamedObjectInit __instance) { - if (streamers == null) + if (!streamersSet) + { streamers = GameObject.FindGameObjectsWithTag(Streamer.STREAMERTAG) .Select(go => go.GetComponent()) .Where(s => s != null) .ToArray(); + streamersSet = true; + } + Streamer streamer = Array.Find(streamers, s => s.sceneCollection.names.Contains(__instance.sceneName)); if (streamer == null) { @@ -33,5 +38,11 @@ private static void Postfix(StreamedObjectInit __instance) streamer.AddSceneGO(__instance.sceneName, __instance.gameObject); } + + public static void ResetStreamers() + { + streamersSet = false; + streamers = null; + } } } diff --git a/Mapify/SceneInitializers/GameContent/VanillaAssetSetup.cs b/Mapify/SceneInitializers/GameContent/VanillaAssetSetup.cs index d210e84..54760d7 100644 --- a/Mapify/SceneInitializers/GameContent/VanillaAssetSetup.cs +++ b/Mapify/SceneInitializers/GameContent/VanillaAssetSetup.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using Mapify.Editor; -using Mapify.Editor.Utils; +using Mapify.Editor; using Mapify.Utils; using UnityEngine; diff --git a/Mapify/SceneInitializers/Terrain/DistantTerrainSetup.cs b/Mapify/SceneInitializers/Terrain/DistantTerrainSetup.cs index bb0e93a..6957601 100644 --- a/Mapify/SceneInitializers/Terrain/DistantTerrainSetup.cs +++ b/Mapify/SceneInitializers/Terrain/DistantTerrainSetup.cs @@ -15,7 +15,7 @@ public override void Run() DistantTerrain distantTerrain = distantTerrainObject.gameObject.AddComponent(); distantTerrain.trackingReference = SingletonBehaviour.Instance.playerTracker.GetTrackerTransform(); distantTerrain.singleTerrainSize = levelInfo.terrainSize; - distantTerrain.worldScale = levelInfo.worldSize; + distantTerrain.worldScale = levelInfo.worldSize.x; distantTerrain.step = 128; // No idea what this means but this is what it's set to in the game. } } diff --git a/MapifyEditor/Export/MapExporter.cs b/MapifyEditor/Export/MapExporter.cs index ab5a663..86fe7ef 100644 --- a/MapifyEditor/Export/MapExporter.cs +++ b/MapifyEditor/Export/MapExporter.cs @@ -10,7 +10,6 @@ using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; -using UnityEngine.SceneManagement; using CompressionLevel = System.IO.Compression.CompressionLevel; using Object = UnityEngine.Object; @@ -74,6 +73,13 @@ private static void ExportRelease(string mapName) { EditorUtility.DisplayProgressBar("Mapify", "Creating zip file", 0); + if (File.Exists(exportFilePath)) + { + File.Delete(exportFilePath); + } + + ZipFile.CreateFromDirectory(mapExportFolder, exportFilePath, CompressionLevel.Fastest, true); + if (File.Exists(exportFilePath)) { EditorFileUtil.MoveToTrash(exportFilePath); @@ -118,7 +124,7 @@ private static void ExportDebug(string mapName) private static bool Export(string rootExportDir, bool uncompressed) { MapInfo mapInfo = EditorAssets.FindAsset(); - mapInfo.mapifyVersion = File.ReadLines("Assets/Mapify/version.txt").First().Trim(); + mapInfo.mapifyVersion = File.ReadLines(Names.MAPIFY_VERSION_FILE).First().Trim(); string mapExportDir = Path.Combine(rootExportDir, mapInfo.name); @@ -180,10 +186,11 @@ private static AssetBundleBuild[] CreateBuilds() var builds = CreateTerrainBuilds(); string[] allAssetPaths = AssetDatabase.GetAllAssetPaths(); - List assetPaths = new List(allAssetPaths.Length - builds.Count); - List scenePaths = new List(); + var miscAssetsPaths = new List(allAssetPaths.Length - builds.Count); + var scenePaths = new List(); + var mapInfoPaths = new List(); - var mapInfoPath = AssetDatabase.GetAssetPath(EditorAssets.FindAsset()); + var mapInfo = EditorAssets.FindAsset(); for (var i = 0; i < allAssetPaths.Length; i++) { @@ -200,13 +207,15 @@ private static AssetBundleBuild[] CreateBuilds() { scenePaths.Add(assetPath); } - else if (assetPath == mapInfoPath) + else if (obj is MapInfo || + mapInfo.LoadingScreenImages.Contains(obj) || + obj == mapInfo.LoadingScreenMusic) { - CreateMapInfoBuild(assetPath, ref builds); + mapInfoPaths.Add(assetPath); } else { - assetPaths.Add(assetPath); + miscAssetsPaths.Add(assetPath); } EditorUtility.DisplayProgressBar("Gathering assets", assetPath, i / (float)allAssetPaths.Length); @@ -214,14 +223,17 @@ private static AssetBundleBuild[] CreateBuilds() EditorUtility.ClearProgressBar(); + CreateMapInfoBuild(mapInfoPaths, ref builds); CreateSceneBuilds(scenePaths, ref builds); - CreateAssetsBuilds(assetPaths, ref builds); + CreateMiscAssetsBuilds(miscAssetsPaths, ref builds); return builds.ToArray(); } private static List CreateTerrainBuilds() { + const string progressBarText = "Building terrain scene"; + EditorUtility.DisplayProgressBar(progressBarText, "", 0); var terrainScene = EditorSceneManager.GetSceneByPath(Scenes.TERRAIN); Terrain[] sortedTerrain = terrainScene.GetAllComponents() @@ -233,22 +245,27 @@ private static List CreateTerrainBuilds() for (int i = 0; i < sortedTerrain.Length; i++) { + string terrainName = AssetDatabase.GetAssetPath(sortedTerrain[i].terrainData); + builds.Add(new AssetBundleBuild { assetBundleName = $"terraindata_{i}", - assetNames = new[] { AssetDatabase.GetAssetPath(sortedTerrain[i].terrainData) } + assetNames = new[] { terrainName } }); + + EditorUtility.DisplayProgressBar(progressBarText, terrainName, i / (float)sortedTerrain.Length); } + EditorUtility.ClearProgressBar(); return builds; } - private static void CreateMapInfoBuild(string mapInfoPath, ref List builds) + private static void CreateMapInfoBuild(List mapInfoAssetPaths, ref List builds) { builds.Add(new AssetBundleBuild { assetBundleName = Names.MAP_INFO_ASSET_BUNDLE, - assetNames = new[] { mapInfoPath } + assetNames = mapInfoAssetPaths.ToArray() }); } @@ -260,7 +277,7 @@ private static void CreateSceneBuilds(List scenePaths, ref List assetPaths, ref List builds) + private static void CreateMiscAssetsBuilds(List assetPaths, ref List builds) { //put big assets in their own assetbundle to avoid the combined assetbundle getting too big. //Unity cannot load assetbundles larger then 4GB. @@ -285,7 +302,7 @@ private static void CreateAssetsBuilds(List assetPaths, ref List 0 && assetBundleSize + fileSize > maxBundleSize) { builds.Add(new AssetBundleBuild { - assetBundleName = Names.ASSETS_ASSET_BUNDLES_PREFIX+'_'+assetBundleNumber, + assetBundleName = Names.MISC_ASSETS_ASSET_BUNDLES_PREFIX+'_'+assetBundleNumber, assetNames = asssetBundleFiles.ToArray() }); @@ -301,15 +318,13 @@ private static void CreateAssetsBuilds(List assetPaths, ref List 0 && i >= assetPaths.Count-1) { builds.Add(new AssetBundleBuild { - assetBundleName = Names.ASSETS_ASSET_BUNDLES_PREFIX+'_'+assetBundleNumber, + assetBundleName = Names.MISC_ASSETS_ASSET_BUNDLES_PREFIX+'_'+assetBundleNumber, assetNames = asssetBundleFiles.ToArray() }); } } } - - private static void CreateMapInfo(string filePath, MapInfo mapInfo) { File.WriteAllText(filePath, JsonUtility.ToJson(BasicMapInfo.FromMapInfo(mapInfo))); @@ -323,7 +338,8 @@ private static void CreateModInfo(string filePath, MapInfo mapInfo) DisplayName = mapInfo.name, ManagerVersion = "0.27.13", Requirements = new[] { "Mapify" }, - HomePage = mapInfo.homePage + HomePage = mapInfo.homePage, + Repository = mapInfo.repository }; File.WriteAllText(filePath, JsonUtility.ToJson(modInfo)); } diff --git a/MapifyEditor/Export/Validators/Project/MapInfoValidator.cs b/MapifyEditor/Export/Validators/Project/MapInfoValidator.cs index 0c9e398..de7dc97 100644 --- a/MapifyEditor/Export/Validators/Project/MapInfoValidator.cs +++ b/MapifyEditor/Export/Validators/Project/MapInfoValidator.cs @@ -1,5 +1,6 @@ #if UNITY_EDITOR using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; using Mapify.Editor; using Mapify.Editor.Utils; @@ -28,6 +29,14 @@ protected override IEnumerator Validate(Scenes scenes) if (mapInfo.name == Names.DEFAULT_MAP_NAME) yield return Result.Error($"Your map name cannot be {Names.DEFAULT_MAP_NAME}"); + //Loading Screen + //LoadingScreenImages will be null if the map was built with an older version of Mapify + if (mapInfo.LoadingScreenImages != null && mapInfo.LoadingScreenImages.Any(image => image == null)) + { + yield return Result.Error("Loading screen image is null", mapInfo); + } + + //World if (mapInfo.waterLevel < -1) yield return Result.Error("Water level cannot be lower than -1", mapInfo); diff --git a/MapifyEditor/Export/Validators/Validator.cs b/MapifyEditor/Export/Validators/Validator.cs index 05b3bc1..86cfbc6 100644 --- a/MapifyEditor/Export/Validators/Validator.cs +++ b/MapifyEditor/Export/Validators/Validator.cs @@ -35,9 +35,10 @@ public static IEnumerator Validate() Scenes scenes = Scenes.FromEnumerable(sceneStates.Keys.ToList()); for (int i = 0; i < validators.Length; i++) { - EditorUtility.DisplayProgressBar("Validating map", null, i / (float)validators.Length); - Validator validator = validators[i]; + + EditorUtility.DisplayProgressBar("Validating map", validator.GetType().ToString(), i / (float)validators.Length); + List results; try { diff --git a/MapifyEditor/MapInfo.cs b/MapifyEditor/MapInfo.cs index 89eab22..48baa2d 100644 --- a/MapifyEditor/MapInfo.cs +++ b/MapifyEditor/MapInfo.cs @@ -12,6 +12,12 @@ public class MapInfo : ScriptableObject public string version = "0.1.0"; [Tooltip("The home page of your mod, most likely being the Nexus Mods page")] public string homePage = "https://www.nexusmods.com/derailvalley/mods/MOD-ID-HERE"; + [Tooltip("Link to your repository.json file (for updates)")] + public string repository = ""; + + [Header("Loading Screen")] + public Texture2D[] LoadingScreenImages; + public AudioClip LoadingScreenMusic; [Header("Loading Gauge")] [Tooltip("The height of the loading gauge, in meters")] diff --git a/MapifyEditor/Names.cs b/MapifyEditor/Names.cs index 9622247..d08b622 100644 --- a/MapifyEditor/Names.cs +++ b/MapifyEditor/Names.cs @@ -3,7 +3,7 @@ namespace Mapify.Editor public static class Names { public const string DEFAULT_MAP_NAME = "Default"; - public const string ASSETS_ASSET_BUNDLES_PREFIX = "assets"; + public const string MISC_ASSETS_ASSET_BUNDLES_PREFIX = "assets"; public const string SCENES_ASSET_BUNDLE = "scenes"; public const string MAP_INFO_ASSET_BUNDLE = "mapInfo"; public const string MAP_INFO_FILE = "mapInfo.json"; diff --git a/MapifyEditor/UnityModManagerInfo.cs b/MapifyEditor/UnityModManagerInfo.cs index e1e502c..416f464 100644 --- a/MapifyEditor/UnityModManagerInfo.cs +++ b/MapifyEditor/UnityModManagerInfo.cs @@ -11,5 +11,6 @@ public struct UnityModManagerInfo public string ManagerVersion; public string[] Requirements; public string HomePage; + public string Repository; } }