SanAndreasUnity/Assets/Scripts/Behaviours/Loader.cs

552 lines
14 KiB
C#
Raw Normal View History

2020-05-31 17:07:22 +00:00
using System.IO;
using SanAndreasUnity.Importing.Animation;
using SanAndreasUnity.Importing.Archive;
using SanAndreasUnity.Importing.Collision;
using SanAndreasUnity.Importing.Conversion;
using SanAndreasUnity.Importing.Items;
using SanAndreasUnity.Importing.Vehicles;
using SanAndreasUnity.Utilities;
using SanAndreasUnity.Behaviours.World;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using SanAndreasUnity.Importing.GXT;
2020-05-31 17:07:22 +00:00
namespace SanAndreasUnity.Behaviours
{
public class Loader : MonoBehaviour
{
public static bool HasLoaded { get; private set; }
public static bool IsLoading { get; private set; }
public static string LoadingStatus { get; private set; }
private static int m_currentStepIndex = 0;
private static float m_totalEstimatedLoadingTime = 0;
private static bool m_hasErrors = false;
private static System.Exception m_loadException;
public class LoadingStep
{
public IEnumerator Coroutine { get; private set; }
public System.Action LoadFunction { get; private set; }
public string Description { get; set; }
public bool StopLoadingOnException { get; private set; }
public float TimeElapsed { get; internal set; }
public float EstimatedTime { get; private set; }
public LoadingStep (System.Action loadFunction, string description, float estimatedTime = 0f, bool stopLoadingOnException = true)
{
this.LoadFunction = loadFunction;
this.Description = description;
this.EstimatedTime = estimatedTime;
this.StopLoadingOnException = stopLoadingOnException;
}
public LoadingStep (IEnumerator coroutine, string description, float estimatedTime = 0f, bool stopLoadingOnException = true)
{
this.Coroutine = coroutine;
this.Description = description;
this.EstimatedTime = estimatedTime;
this.StopLoadingOnException = stopLoadingOnException;
}
}
private static List<LoadingStep> m_loadingSteps = new List<LoadingStep> ();
public static Texture2D CurrentSplashTex { get; set; }
public static Texture2D SplashTex1 { get; set; }
public static Texture2D SplashTex2 { get; set; }
private static bool m_showFileBrowser = false;
private static FileBrowser m_fileBrowser = null;
2020-04-21 22:28:09 +00:00
public static event System.Action onLoadSpecialTextures = delegate { };
2020-05-31 17:07:22 +00:00
void Start ()
{
AddLoadingSteps ();
StartCoroutine (LoadCoroutine ());
}
private static void AddLoadingSteps ()
{
LoadingStep[] steps = new LoadingStep[] {
2021-01-26 22:29:01 +00:00
new LoadingStep ( StepConfigure, "Configuring", 0f ),
2020-05-31 17:07:22 +00:00
new LoadingStep ( StepSelectGTAPath(), "Select path to GTA", 0.0f ),
new LoadingStep ( StepLoadArchives, "Loading archives", 1.7f ),
new LoadingStep ( StepLoadSplashScreen, "Loading splash screen", 0.06f ),
new LoadingStep ( StepSetSplash1, "Set splash 1" ),
new LoadingStep ( StepLoadAudio, "Loading audio" ),
//new LoadingStep ( StepLoadFonts,"Loading fonts"),
2020-05-31 17:07:22 +00:00
new LoadingStep ( StepLoadCollision, "Loading collision files", 0.9f ),
new LoadingStep ( StepLoadItemInfo, "Loading item info", 2.4f ),
new LoadingStep ( StepLoadHandling, "Loading handling", 0.01f ),
//new LoadingStep ( () => { throw new System.Exception ("testing error handling"); }, "testing error handling", 0.01f ),
new LoadingStep ( StepLoadAnimGroups, "Loading animation groups", 0.02f ),
new LoadingStep ( StepLoadCarColors, "Loading car colors", 0.04f ),
new LoadingStep ( StepLoadWeaponsData, "Loading weapons data", 0.05f ),
new LoadingStep ( StepSetSplash2, "Set splash 2" ),
new LoadingStep ( StepLoadMap, "Loading map", 2.1f ),
new LoadingStep ( StepLoadSpecialTextures, "Loading special textures", 0.01f ),
// new LoadingStep ( StepLoadGXT, "Loading GXT", 0.15f),
new LoadingStep ( StepLoadPaths, "Loading paths"),
2020-05-31 17:07:22 +00:00
};
for (int i = 0; i < steps.Length; i++) {
AddLoadingStep (steps [i]);
}
if (Cell.Instance != null) {
// add steps for cell
AddLoadingStep( new LoadingStep( () => Cell.Instance.CreateStaticGeometry (), "Creating static geometry", 5.8f ) );
AddLoadingStep( new LoadingStep( () => Cell.Instance.InitStaticGeometry (), "Init static geometry", 0.35f ) );
AddLoadingStep( new LoadingStep( () => Cell.Instance.LoadParkedVehicles (), "Loading parked vehicles", 0.2f ) );
2019-11-20 16:11:49 +00:00
AddLoadingStep( new LoadingStep( () => Cell.Instance.CreateEnexes (), "Creating enexes", 0.1f ) );
2020-05-31 17:07:22 +00:00
AddLoadingStep( new LoadingStep( () => Cell.Instance.LoadWater (), "Loading water", 0.08f ) );
AddLoadingStep( new LoadingStep( () => Cell.Instance.FinalizeLoad (), "Finalize world loading", 0.01f ) );
}
}
private static void AddLoadingStep (LoadingStep step)
{
m_loadingSteps.AddIfNotPresent (step);
}
private static IEnumerator LoadCoroutine ()
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew ();
IsLoading = true;
Debug.Log("Started loading GTA");
// wait a few frames - to "unblock" the program, and to let other scripts initialize before
// registering their loading steps
yield return null;
yield return null;
// calculate total loading time
m_totalEstimatedLoadingTime = m_loadingSteps.Sum( step => step.EstimatedTime );
var stopwatchForSteps = new System.Diagnostics.Stopwatch ();
new world loading system (#110) * wip * much faster world creation * add StaticGeometryInspector * disable child/parent logic and fading * rename * (de)activate objects based on parent * set draw distance based on layers * ... * wip * wip * wip * remove unused param * prevent concurrent modification * ... * catch exceptions when notifying * ... * notify about area, not objects * limit public access to Area * ... * ... * allow public access * add public api * adapt code * pass callback to ctor * adapt focus points * fix * fix intersection check * support rectangles * adjust parameters in prefab * this should fix IsInsideOf() * ... * ... * fix getting area by index * create area if not exists * ... * ... * ... * wip for distance levels * remove constraint on generic parameter * add some validation * fix * fix draw distance per level * change time of day in which lights are visible * add todos * don't use id for UnRegisterFocusPoint() * use hash set for storing focus points * add 1 more level * mark area for update only if visibility changes * profile WorldSystem calls * add some profiling sections * limit time per frame for LoadingThread * switch custom concurrent queue * copy jobs to buffer * rename * change max draw distance setting * wait one more frame * try to remove 801 distance level to remove holes * attempt to hide interiors, but failed * delete no longer needed script * optimization * some error checking * add camera as focus point * dont add camera as focus point in headless mode * working on load priority * fix bug - load priority finished * ... * small optimizations * ... * ... * remove unneeded variable * add fading * dont do fading in headless mode * fadeRate available in inspector * change fade rate * take into account if geometry is loaded when checking if object should be visible, and if fading should be done * small optimization * cache IsInHeadlessMode * display Instance info in inspector * move interiors up in the sky * rename * adapt code to different y pos of interiors * refactor * fix finding matched enex for enexes that lead to the same interior level * display new world stats * rename * rename class * ... * ... * extract function * extract parameters into a struct * add focus point to dead body * add focus point to vehicle * add focus point to vehicle detached parts * remove OutOfRangeDestroyer from vehicle, and destroy vehicle if it falls below the map * dont use focus points on vehicle and vehicle detached parts, when not on server * add focus point for npc peds * add possibility to set timeout during which focus point keeps revealing after it's destroyed * adapt UnRegisterFocusPoint() to timeout * rename * adapt code * cleanup MapObject class * ... * converting to `lock()` * optimize method: use 1 lock instead of 3 * call OnObjectFinishedLoading() instead of AddToLoadedObjects() * ... * make sure it's main thread * AsyncLoader is no longer thread safe * convert static members to non-static in LoadingThread * fix * ... * store indexes for each area * impl GetAreaCenter() * calculate load priority based on distance to area, not objects ; limit time per frame ; sort area in Cell, not in concurrent SortedSet ; * add support for changing draw distance at runtime * delay setting the new value by 0.2 s * have a separate default max draw distance for mobile platforms * adjust y axis world params so that number of visible areas is reduced * remove "camera far clip plane" setting * rename * document flags * rename * disable shadow casting and receiving for some objects * allow casting shadows for LODs with large draw distance * remove "WorldSystem" layer * revert layer
2021-07-18 04:03:43 +00:00
foreach (var step in m_loadingSteps)
{
// wait some more time before going to next step, because sometimes Unity does something
// in the background at the end of a frame, eg. it updates Collider positions if you changed them
yield return null;
2020-05-31 17:07:22 +00:00
// update description
LoadingStatus = step.Description;
yield return null;
stopwatchForSteps.Restart ();
var en = step.Coroutine;
if (en != null) {
// this step uses coroutine
bool hasNext = true;
while (hasNext) {
hasNext = false;
try {
hasNext = en.MoveNext ();
} catch (System.Exception ex) {
HandleExceptionDuringLoad (ex);
if (step.StopLoadingOnException) {
yield break;
}
}
// update description
LoadingStatus = step.Description;
yield return null;
}
} else {
// this step uses a function
try {
step.LoadFunction ();
} catch(System.Exception ex) {
HandleExceptionDuringLoad (ex);
if (step.StopLoadingOnException) {
yield break;
}
}
}
// step finished it's work
step.TimeElapsed = stopwatchForSteps.ElapsedMilliseconds;
m_currentStepIndex++;
Debug.LogFormat ("{0} - finished in {1} ms", step.Description, step.TimeElapsed);
}
// all steps finished loading
HasLoaded = true;
IsLoading = false;
Debug.Log("GTA loading finished in " + stopwatch.Elapsed.TotalSeconds + " seconds");
// notify all scripts
F.SendMessageToObjectsOfType<MonoBehaviour>( "OnLoaderFinished" );
}
private static void HandleExceptionDuringLoad (System.Exception ex)
{
m_hasErrors = true;
m_loadException = ex;
Debug.LogException (ex);
}
2021-01-26 22:29:01 +00:00
private static void StepConfigure ()
{
2021-10-21 08:50:23 +00:00
TextureDictionary.DontLoadTextures = Config.GetBool("dontLoadTextures");
2020-05-31 17:07:22 +00:00
}
private static IEnumerator StepSelectGTAPath ()
{
yield return null;
string path = Config.GetPath(Config.const_game_dir);
if (string.IsNullOrEmpty (path)) {
// path is not set
// show file browser to user to select path
m_showFileBrowser = true;
} else {
yield break;
}
// wait until user selects a path
while (m_showFileBrowser) {
yield return null;
}
// refresh path
path = Config.GetPath(Config.const_game_dir);
if (string.IsNullOrEmpty (path)) {
// path was not set
throw new System.Exception ("Path to GTA was not set");
}
}
private static void StepLoadArchives ()
{
string[] directoriesToCheck = { "models", "data" };
2020-05-31 17:07:22 +00:00
foreach (string directoryToCheck in directoriesToCheck)
2020-05-31 17:07:22 +00:00
{
string[] caseVariations =
2020-05-31 17:07:22 +00:00
{
directoryToCheck,
directoryToCheck.FirstCharToUpper(),
directoryToCheck.ToUpperInvariant(),
};
2019-10-09 21:29:34 +00:00
if (caseVariations.All(d => !Directory.Exists(Path.Combine(Config.GamePath, d))))
throw new System.Exception($"Game folder seems to be invalid - failed to find '{directoryToCheck}' folder inside game folder");
2020-05-31 17:07:22 +00:00
}
ArchiveManager.LoadLooseArchive(Config.GamePath);
foreach (string imgFilePath in ArchiveManager.GetFilePathsFromLooseArchivesWithExtension(".img"))
{
ArchiveManager.LoadImageArchive(imgFilePath);
}
Debug.Log($"num archives loaded: {ArchiveManager.GetNumArchives()}, num entries loaded: {ArchiveManager.GetTotalNumLoadedEntries()}");
2020-05-31 17:07:22 +00:00
}
private static void StepLoadSplashScreen ()
{
var txd = TextureDictionary.Load ("LOADSCS");
int index1 = Random.Range (1, 15);
int index2 = Random.Range (1, 15);
SplashTex1 = txd.GetDiffuse ("loadsc" + index1).Texture;
SplashTex2 = txd.GetDiffuse ("loadsc" + index2).Texture;
}
private static void StepSetSplash1 ()
{
CurrentSplashTex = SplashTex1;
}
private static void StepSetSplash2 ()
{
CurrentSplashTex = SplashTex2;
}
private static void StepLoadAudio ()
{
Audio.AudioManager.InitFromLoader ();
}
2021-01-26 22:30:36 +00:00
private static void StepLoadFonts()
{
2021-01-31 19:58:44 +00:00
Importing.FontsImporter.LoadFonts();
2021-01-26 22:30:36 +00:00
}
2020-05-31 17:07:22 +00:00
private static void StepLoadCollision ()
{
int numCollisionFiles = 0;
foreach (var colFile in ArchiveManager.GetFileNamesWithExtension(".col"))
2020-05-31 17:07:22 +00:00
{
CollisionFile.Load(colFile);
numCollisionFiles++;
2020-05-31 17:07:22 +00:00
}
Debug.Log("Number of collision files " + numCollisionFiles);
}
private static void StepLoadItemInfo ()
{
2019-10-09 22:18:28 +00:00
foreach (var p in Config.GetPaths("item_paths"))
2020-05-31 17:07:22 +00:00
{
2019-10-09 22:18:28 +00:00
string path = ArchiveManager.PathToCaseSensitivePath(p);
2020-05-31 17:07:22 +00:00
var ext = Path.GetExtension(path).ToLower();
switch (ext)
{
case ".dat":
Item.ReadLoadList(path);
break;
case ".ide":
Item.ReadIde(path);
break;
case ".ipl":
Item.ReadIpl(path);
break;
}
}
}
private static void StepLoadHandling ()
{
2019-10-09 22:22:35 +00:00
Handling.Load(ArchiveManager.PathToCaseSensitivePath(Config.GetPath("handling_path")));
2020-05-31 17:07:22 +00:00
}
private static void StepLoadAnimGroups ()
{
AnimationGroup.Load("animgrp.dat");
// load custom anim groups from resources
TextAsset textAsset = Resources.Load<TextAsset>("Data/auxanimgrp");
AnimationGroup.LoadFromStreamReader( new StreamReader(new MemoryStream(textAsset.bytes)) );
2020-05-31 17:07:22 +00:00
}
private static void StepLoadCarColors ()
{
CarColors.Load(ArchiveManager.PathToCaseSensitivePath(Config.GetPath("car_colors_path")));
2020-05-31 17:07:22 +00:00
}
private static void StepLoadWeaponsData ()
{
Importing.Weapons.WeaponData.Load(ArchiveManager.PathToCaseSensitivePath(Config.GetPath("weapons_path")));
2020-05-31 17:07:22 +00:00
}
private static void StepLoadMap ()
{
2020-05-01 17:28:01 +00:00
MiniMap.Instance.Load ();
2020-05-31 17:07:22 +00:00
}
private static void StepLoadSpecialTextures ()
{
// Load mouse cursor texture
F.RunExceptionSafe(() =>
{
Texture2D mouse = TextureDictionary.Load("fronten_pc").GetDiffuse("mouse",
new TextureLoadParams(){makeNoLongerReadable = false}).Texture;
Texture2D mouseFix = new Texture2D(mouse.width, mouse.height);
for (int x = 0; x < mouse.width; x++)
for (int y = 0; y < mouse.height; y++)
mouseFix.SetPixel(x, mouse.height - y - 1, mouse.GetPixel(x, y));
2020-05-31 17:07:22 +00:00
mouseFix.Apply();
2020-05-31 17:07:22 +00:00
Cursor.SetCursor(mouseFix, Vector2.zero, CursorMode.Auto);
});
2020-05-31 17:07:22 +00:00
// fist texture
Weapon.FistTexture = TextureDictionary.Load("hud").GetDiffuse("fist").Texture;
2020-04-21 22:28:09 +00:00
onLoadSpecialTextures();
}
2019-07-23 14:29:16 +00:00
private static void StepLoadGXT()
{
GXT.Load();
2020-05-31 17:07:22 +00:00
}
private static void StepLoadPaths()
{
2021-09-06 22:47:54 +00:00
Importing.Paths.NodeReader.Load();
}
2020-05-31 17:07:22 +00:00
public static float GetProgressPerc ()
2020-05-31 17:07:22 +00:00
{
if (m_currentStepIndex <= 0)
return 0f;
if (m_currentStepIndex >= m_loadingSteps.Count)
return 1f;
float estimatedTimePassed = 0f;
for (int i = 0; i < m_currentStepIndex; i++) {
estimatedTimePassed += m_loadingSteps [i].EstimatedTime;
}
return Mathf.Clamp01 (estimatedTimePassed / m_totalEstimatedLoadingTime);
}
private void Update()
{
}
private void OnGUI()
{
if (HasLoaded)
return;
// background
if (CurrentSplashTex != null) {
GUIUtils.DrawTextureWithYFlipped (new Rect (0, 0, Screen.width, Screen.height), CurrentSplashTex);
} else {
GUIUtils.DrawRect (new Rect (0, 0, Screen.width, Screen.height), Color.black);
}
// display loading progress
GUILayout.BeginArea(new Rect(10, 5, 400, Screen.height - 5));
2020-05-31 17:07:22 +00:00
// current status
GUILayout.Label("<size=25>" + LoadingStatus + "</size>");
// progress bar
GUILayout.Space (10);
DisplayProgressBar ();
// display error
if (m_hasErrors) {
GUILayout.Space (20);
GUILayout.Label("<size=20>" + "The following exception occured during the current step:" + "</size>");
GUILayout.TextArea( m_loadException.ToString () );
GUILayout.Space (30);
if (GUIUtils.ButtonWithCalculatedSize("Exit", 80, 30)) {
GameManager.ExitApplication();
}
GUILayout.Space(5);
2020-05-31 17:07:22 +00:00
}
// display all steps
// GUILayout.Space (10);
// DisplayAllSteps ();
GUILayout.EndArea();
DisplayFileBrowser ();
}
private static void DisplayAllSteps ()
{
int i=0;
foreach (var step in m_loadingSteps) {
GUILayout.Label( step.Description + (m_currentStepIndex > i ? (" - " + step.TimeElapsed + " ms") : "") );
i++;
}
}
private static void DisplayProgressBar ()
{
float width = 200;
float height = 12;
// Rect rect = GUILayoutUtility.GetLastRect ();
// rect.position += new Vector2 (0, rect.height);
// rect.size = new Vector2 (width, height);
Rect rect = GUILayoutUtility.GetRect( width, height );
rect.width = width;
float progressPerc = GetProgressPerc ();
GUIUtils.DrawBar( rect, progressPerc, new Vector4(149, 185, 244, 255) / 256.0f, new Vector4(92, 147, 237, 255) / 256.0f, 2f );
}
private static void DisplayFileBrowser ()
{
if (!m_showFileBrowser)
return;
if (null == m_fileBrowser) {
2019-07-28 21:08:34 +00:00
Rect rect = GUIUtils.GetCenteredRect (FileBrowser.GetRecommendedSize());
2020-05-31 17:07:22 +00:00
m_fileBrowser = new FileBrowser(rect, "Select path to GTA", GUI.skin.window, (string path) => {
2020-05-31 17:07:22 +00:00
m_showFileBrowser = false;
Config.SetString (Config.const_game_dir, path);
Config.SaveUserConfigSafe ();
} );
m_fileBrowser.BrowserType = FileBrowserType.Directory;
}
m_fileBrowser.OnGUI ();
}
}
}