Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compile progress indicator #88

Merged
merged 6 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Assembly-CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<Compile Include="Assets.Scripts.Core.Audio\AudioInfo.cs" />
<Compile Include="Assets.Scripts.Core.Audio\AudioLayerUnity.cs" />
<Compile Include="Assets.Scripts.Core.Audio\AudioType.cs" />
<Compile Include="MOD.Scripts.AssetManager\MODAssetManager.cs" />
<Compile Include="MOD.Scripts.Core.Audio\MODAudioSet.cs" />
<Compile Include="MOD.Scripts.Core.Audio\MODAudioTracking.cs" />
<Compile Include="Assets.Scripts.Core.Audio\OnFinishLoad.cs" />
Expand Down
212 changes: 58 additions & 154 deletions Assets.Scripts.Core.AssetManagement/AssetManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
using Assets.Scripts.Core.Buriko;
using BGICompiler.Compiler;
using MOD.Scripts.Core.Audio;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using UnityEngine;

namespace Assets.Scripts.Core.AssetManagement
Expand Down Expand Up @@ -66,6 +64,12 @@ public class AssetManager {

private string assetPath = Application.streamingAssetsPath;

public int CurrentLoading;

public int MaxLoading;

public bool AbortLoading;

private List<string> scriptList = new List<string>();

public static AssetManager Instance => _instance ?? (_instance = GameSystem.Instance.AssetManager);
Expand Down Expand Up @@ -227,183 +231,83 @@ public string PathToAssetWithName(string name, PathCascadeList artset)
return null;
}

class ScriptInfo
// The arguments AbortLoading, MaxLoading, and CurrentLoading are only used for meakashi onwards
// MaxLoading, and CurrentLoading are are used to display the Script Compilation Progress Text
// I'm not sure if AbortLoading is ever used
private void CompileFolder(string srcDir, string destDir)
{
public string name;
public DateTime lastWriteTime;
public long length;
public string md5String;

public static ScriptInfo TryGetOrNull(string path)
{
try
{
string md5String;
using (var md5 = MD5.Create())
{
using (var stream = File.OpenRead(path))
{
md5String = BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "");
}
}

FileInfo fileInfo = new FileInfo(path);

return new ScriptInfo
{
name = Path.GetFileNameWithoutExtension(fileInfo.Name),
lastWriteTime = fileInfo.LastWriteTime,
length = fileInfo.Length,
md5String = md5String,
};
}
catch (Exception e)
{
Debug.LogError($"Failed to get script info for {path}: {e}");
}
MODCompileRequiredDetector detector = new MODCompileRequiredDetector(destDir);
detector.Load();

return null;
}
}
MaxLoading = 0;
CurrentLoading = 0;

private bool ScriptNeedsCompile(Dictionary<string, ScriptInfo> oldInfoDictionary, ScriptInfo txt, ScriptInfo mg, string textDescription, string mgDescription)
{
// If the mg file doensn't exist or can't be accessed, do re-compile
if(mg == null)
{
Debug.Log($"ScriptNeedsCompile(): Compiling {textDescription} as mg file {mgDescription} does not exist or can't be accessed");
return true;
}

// This implies the txt file doesn't exist, which should never happen but just try to recompile anyway in this case
if(txt == null)
{
Debug.Log($"ScriptNeedsCompile(): WARNING: {textDescription} can't be accessed or does not exist - trying to compile anyway");
return true;
}
string[] txtList1 = Directory.GetFiles(srcDir, "*.txt");
string[] mgList1 = Directory.GetFiles(destDir, "*.mg");
List<string> scriptNames = new List<string>();

//// Compile if the .txt is newer than the .mg file
//if (txt.lastWriteTime > mg.lastWriteTime)
//{
// Debug.Log($"ScriptNeedsCompile(): Compiling {textDescription} as it is newer than mg file {mgDescription} (txt: {txt.lastWriteTime}), mg: {mg.lastWriteTime}");
// return true;
//}
List<string> txtToCompileList = new List<string>();
List<string> mgToCompileList = new List<string>();

// Compare the current .txt file against the last recorded information about the .txt file
if(oldInfoDictionary.TryGetValue(txt.name, out ScriptInfo oldInfo))
string[] txtList = txtList1;
foreach (string txtPath1 in txtList)
{
// Compile if the file size has changed since last time
if(oldInfo.length != txt.length)
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(txtPath1);
if (fileNameWithoutExtension == null)
{
Debug.Log($"ScriptNeedsCompile(): Compiling {textDescription} as size differs from previous (old: {oldInfo.length} bytes, new: {txt.length} bytes)");
return true;
continue;
}

// Compile if the file's MD5 has changed
if (oldInfo.md5String != txt.md5String)
scriptNames.Add(fileNameWithoutExtension);
string txtPath = txtPath1;
string mgPath = Path.Combine(destDir, fileNameWithoutExtension) + ".mg";
if(!detector.SaveScriptInfoAndCheckScriptNeedsCompile(txtPath, mgPath))
{
Debug.Log($"ScriptNeedsCompile(): Compiling {textDescription} as MD5 differs from previous (old: {oldInfo.md5String}, new: {txt.md5String})");
return true;
detector.MarkScriptCompiled(fileNameWithoutExtension);
continue;
}
txtToCompileList.Add(txtPath);
mgToCompileList.Add(mgPath);
}
else
{
// If the file hasn't been recorded in the dictionary, re-compile it
Debug.Log($"ScriptNeedsCompile(): Compiling {textDescription} as it doesn't exist in the .txt info dictionary.");
return true;
}

return false;
}

public void CompileFolder(string srcDir, string destDir)
{
JsonSerializer jsonSerializer = new JsonSerializer();
string txtInfoDictionaryPath = Path.Combine(destDir, "txtInfoDictionary.json");
string[] txtList1 = Directory.GetFiles(srcDir, "*.txt");
string[] mgList1 = Directory.GetFiles(destDir, "*.mg");
List<string> txtFilenameNoExtensionList = new List<string>();

Dictionary<string, ScriptInfo> oldTxtInfoDictionary = new Dictionary<string, ScriptInfo>();
Dictionary<string, ScriptInfo> newTxtInfoDictionary = new Dictionary<string, ScriptInfo>();

try
MaxLoading = txtToCompileList.Count;
for (int j = 0; j < txtToCompileList.Count; j++)
{
if(File.Exists(txtInfoDictionaryPath))
CurrentLoading = j + 1;
string text4 = txtToCompileList[j];
string outname = mgToCompileList[j];
string fileNameWithoutExtension2 = Path.GetFileNameWithoutExtension(text4);
Debug.Log("Compiling file " + text4);
try
{
using (StreamReader sw = new StreamReader(txtInfoDictionaryPath))
using (JsonReader reader = new JsonTextReader(sw))
{
List<ScriptInfo> scriptInfoList = jsonSerializer.Deserialize<List<ScriptInfo>>(reader);
foreach(ScriptInfo info in scriptInfoList)
{
oldTxtInfoDictionary[info.name] = info;
}
}

new BGItoMG(text4, outname);
numCompileOK++;
detector.MarkScriptCompiled(fileNameWithoutExtension2);
}
}
catch(Exception e)
{
Debug.LogError($"CompileFolder(): Failed to deserialize {txtInfoDictionaryPath}: {e}");
}

string[] txtList = txtList1;
foreach (string txtPath1 in txtList)
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(txtPath1);
if (fileNameWithoutExtension != null)
catch (Exception arg)
{
txtFilenameNoExtensionList.Add(fileNameWithoutExtension);

string txtPath = txtPath1;
string mgPath = Path.Combine(destDir, fileNameWithoutExtension) + ".mg";

ScriptInfo txtInfo = ScriptInfo.TryGetOrNull(txtPath);
ScriptInfo mgInfo = File.Exists(mgPath) ? ScriptInfo.TryGetOrNull(mgPath) : null;

if (!ScriptNeedsCompile(oldTxtInfoDictionary, txtInfo, mgInfo, Path.GetFileName(txtPath), Path.GetFileName(mgPath)))
{
newTxtInfoDictionary[txtInfo.name] = txtInfo;
continue;
}

// Try to compile the file, but if an exception occurs just ignore it and move on to the next script
Debug.Log("Compiling file " + txtPath);
try
{
new BGItoMG(txtPath, mgPath);
numCompileOK++;
newTxtInfoDictionary[txtInfo.name] = txtInfo;
}
catch (Exception arg)
{
Debug.LogError($"Failed to compile script {fileNameWithoutExtension}!\r\n{arg}");
numCompileFail++;
}
Debug.LogError($"Failed to compile script {fileNameWithoutExtension2}!\r\n{arg}");
numCompileFail++;
}
if (AbortLoading)
{
return;
}
}

if(numCompileOK > 0 && numCompileFail == 0)
// Only update .txt compile status if at least one file compiled
if (numCompileOK > 0)
{
// save scriptInfoDictionary to file as at least one .txt file has changed and succesfully been compiled
// we don't want to update the dictionary if any script file failed to compile as from then on it would never be updated
using (StreamWriter sw = new StreamWriter(txtInfoDictionaryPath))
using (JsonTextWriter writer = new JsonTextWriter(sw))
{
jsonSerializer.Serialize(writer, newTxtInfoDictionary.Values.ToList());
}
detector.Save();
}

// Delete .mg files with no corresponding .txt file
string[] mgList = mgList1;
foreach (string mgPath in mgList)
foreach (string path in mgList)
{
string mgFileNameWithoutExtension = Path.GetFileNameWithoutExtension(mgPath);
if (!txtFilenameNoExtensionList.Contains(mgFileNameWithoutExtension))
string fileNameWithoutExtension3 = Path.GetFileNameWithoutExtension(path);
if (!scriptNames.Contains(fileNameWithoutExtension3))
{
Debug.Log("Compiled script " + mgFileNameWithoutExtension + " has no matching script file. Removing...");
File.Delete(mgPath);
Debug.Log("Compiled script " + fileNameWithoutExtension3 + " has no matching script file. Removing...");
File.Delete(path);
}
}
}
Expand Down
Loading