Skip to content

Commit

Permalink
Create prefabs for all tags on scene load
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoco007 committed Mar 24, 2024
1 parent 6a5e103 commit efb08c4
Show file tree
Hide file tree
Showing 48 changed files with 439 additions and 444 deletions.
110 changes: 79 additions & 31 deletions BeatSaberMarkupLanguage/BSMLParser.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
Expand All @@ -11,6 +12,7 @@
using BeatSaberMarkupLanguage.Tags;
using BeatSaberMarkupLanguage.TypeHandlers;
using BeatSaberMarkupLanguage.Util;
using IPA.Loader;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
Expand All @@ -19,7 +21,7 @@

namespace BeatSaberMarkupLanguage
{
public class BSMLParser : PersistentSingleton<BSMLParser>, IInitializable
public class BSMLParser : PersistentSingleton<BSMLParser>, ILateDisposable
{
internal static readonly string MacroPrefix = "macro.";
internal static readonly string RetrieveValuePrefix = "~";
Expand All @@ -35,6 +37,8 @@ public class BSMLParser : PersistentSingleton<BSMLParser>, IInitializable
IgnoreComments = true,
};

private bool sceneContextResolved;

public BSMLParser()
{
foreach (BSMLTag tag in Utilities.GetInstancesOfDescendants<BSMLTag>())
Expand All @@ -59,37 +63,9 @@ public BSMLParser()
}
}

public void Initialize()
public void LateDispose()
{
foreach (BSMLTag tag in tags.Values)
{
if (!tag.isInitialized)
{
tag.Setup();
tag.isInitialized = true;
}
}

#if false//don't worry about this, it's for the docs
string contents = "";
foreach (BSMLTag tag in Utilities.GetListOfType<BSMLTag>())
{
tag.Setup();
contents += $"- type: {tag.GetType().Name}\n";
contents += $" aliases:\n";
foreach (string alias in tag.Aliases)
contents += $" - {alias}\n";
contents += $" components:\n";
GameObject currentNode = tag.CreateObject(transform);
foreach (TypeHandler typeHandler in typeHandlers)
{
Type type = typeHandler.GetType().GetCustomAttribute<ComponentHandler>(true).type;
if (GetExternalComponent(currentNode, type) != null)
contents += $" - {type.Name}\n";
}
}
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "Tags.yml"), contents);
#endif
sceneContextResolved = false;
}

public void RegisterTag(BSMLTag tag)
Expand Down Expand Up @@ -126,6 +102,11 @@ public BSMLParserParams Parse(XmlNode parentNode, GameObject parent, object host
return null;
}

if (!sceneContextResolved)
{
Logger.Log.Warn($"BSMLParser.Parse called by {GetCallerMods()} before Zenject initialization was completed! This may lead to unexpected results and will throw an exception in a future release.");
}

BSMLParserParams parserParams = new(host);

FieldAccessOption fieldAccessOptions = FieldAccessOption.Auto;
Expand Down Expand Up @@ -291,6 +272,51 @@ public void HandleNode(XmlNode node, GameObject parent, BSMLParserParams parserP
}
}

internal void SceneContext_PreResolve()
{
Stopwatch stopwatch = Stopwatch.StartNew();
foreach (BSMLTag tag in tags.Values.Distinct())
{
#pragma warning disable CS0612, CS0618
if (!tag.isInitialized)
{
tag.Setup();
tag.isInitialized = true;
}
#pragma warning restore CS0612, CS0618

tag.SetUp();
}

Logger.Log.Notice("Setup completed in " + stopwatch.Elapsed);

#if false//don't worry about this, it's for the docs
string contents = "";
foreach (BSMLTag tag in Utilities.GetListOfType<BSMLTag>())
{
tag.Setup();
contents += $"- type: {tag.GetType().Name}\n";
contents += $" aliases:\n";
foreach (string alias in tag.Aliases)
contents += $" - {alias}\n";
contents += $" components:\n";
GameObject currentNode = tag.CreateObject(transform);
foreach (TypeHandler typeHandler in typeHandlers)
{
Type type = typeHandler.GetType().GetCustomAttribute<ComponentHandler>(true).type;
if (GetExternalComponent(currentNode, type) != null)
contents += $" - {type.Name}\n";
}
}
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "Tags.yml"), contents);
#endif
}

internal void SceneContext_PostResolve()
{
sceneContextResolved = true;
}

private void HandleTagNode(XmlNode node, GameObject parent, BSMLParserParams parserParams, out IEnumerable<ComponentTypeWithData> componentInfo)
{
if (!this.tags.TryGetValue(node.Name, out BSMLTag currentTag))
Expand Down Expand Up @@ -467,6 +493,28 @@ private bool IsMainMenuSceneLoaded()
return instance.IsValid() && ((SceneProvider.SceneOp)instance.m_InternalOp).m_DepOp.Result.Select(op => op.Result).OfType<AssetBundleResource>().All(ab => ab.m_AssetBundle != null);
}

private string GetCallerMods()
{
List<string> callers = new StackTrace()
.GetFrames()
.Select(f => f.GetMethod().DeclaringType.Assembly)
.Distinct()
.Where(a => a != Assembly.GetExecutingAssembly())
.Select(a => PluginManager.EnabledPlugins.FirstOrDefault(p => p.Assembly == a))
.Where(m => m != null)
.Select(m => $"{m.Name} ({m.Assembly.GetName()?.Name})")
.ToList();

if (callers.Count > 0)
{
return string.Join(", ", callers);
}
else
{
return "<unknown>";
}
}

public struct ComponentTypeWithData
{
public TypeHandler typeHandler;
Expand Down
8 changes: 8 additions & 0 deletions BeatSaberMarkupLanguage/Components/CustomCellListTableData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public int NumberOfCells()
{
return data.Count;
}

private void Awake()
{
if (tableView != null)
{
tableView.SetDataSource(this, false);
}
}
}

public class CustomCellTableCell : TableCell
Expand Down
8 changes: 8 additions & 0 deletions BeatSaberMarkupLanguage/Components/CustomListTableData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ public int NumberOfCells()
return data.Count();
}

private void Awake()
{
if (tableView != null)
{
tableView.SetDataSource(this, false);
}
}

public class CustomCellInfo
{
public string text;
Expand Down
4 changes: 2 additions & 2 deletions BeatSaberMarkupLanguage/Components/TabSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void Setup()
tab.selector = this;
}

if (leftButtonTag != null)
if (!string.IsNullOrEmpty(leftButtonTag))
{
leftButton = parserParams.GetObjectsWithTag(leftButtonTag).FirstOrDefault().GetComponent<Button>();
}
Expand All @@ -58,7 +58,7 @@ public void Setup()
leftButton.onClick.AddListener(PageLeft);
}

if (rightButtonTag != null)
if (!string.IsNullOrEmpty(rightButtonTag))
{
rightButton = parserParams.GetObjectsWithTag(rightButtonTag).FirstOrDefault().GetComponent<Button>();
}
Expand Down
10 changes: 8 additions & 2 deletions BeatSaberMarkupLanguage/Harmony Patches/MainMenuInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ private static void Prefix(MainSettingsMenuViewControllersInstaller __instance)

BeatSaberUI.DiContainer = container;

// some mods call Parse() in/around Construct (BAD) so we need to run our stuff early
container.Resolve<SceneContext>().PreResolve += () => container.Resolve<BSMLParser>().SceneContext_PreResolve();
container.Resolve<SceneContext>().PostResolve += () => container.Resolve<BSMLParser>().SceneContext_PostResolve();

// Eventually this should go in an installer & not use static instances but for now this is good enough. This is kind of janky since the
// instance persists across restarts (like PersistentSingleton did) so Initialize/Dispose can be called multiple times on the same instance.
container.Bind(typeof(AnimationController), typeof(ITickable)).FromInstance(AnimationController.instance);
container.Bind(typeof(BSMLSettings), typeof(IInitializable), typeof(ILateDisposable)).FromInstance(BSMLSettings.instance);
container.Bind(typeof(BSMLParser), typeof(IInitializable)).FromInstance(BSMLParser.instance);
container.Bind(typeof(BSMLParser), typeof(ILateDisposable)).FromInstance(BSMLParser.instance);
container.Bind(typeof(MenuButtons.MenuButtons), typeof(ILateDisposable)).FromInstance(MenuButtons.MenuButtons.instance);
container.Bind(typeof(GameplaySetup.GameplaySetup), typeof(IInitializable), typeof(IDisposable), typeof(ILateDisposable)).FromInstance(GameplaySetup.GameplaySetup.instance);

Expand All @@ -31,9 +35,11 @@ private static void Prefix(MainSettingsMenuViewControllersInstaller __instance)
container.QueueForInject(MenuButtons.MenuButtons.instance);
container.QueueForInject(GameplaySetup.GameplaySetup.instance);

// LateDispose later
container.BindLateDisposableExecutionOrder<BSMLParser>(-1000);

// initialize all our stuff late
container.BindInitializableExecutionOrder<BSMLSettings>(1000);
container.BindInitializableExecutionOrder<BSMLParser>(1000);
container.BindInitializableExecutionOrder<GameplaySetup.GameplaySetup>(1000);

ModSettingsFlowCoordinator modSettingsFlowCoordinator = BeatSaberUI.CreateFlowCoordinator<ModSettingsFlowCoordinator>();
Expand Down
7 changes: 7 additions & 0 deletions BeatSaberMarkupLanguage/Tags/BSMLTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,27 @@ namespace BeatSaberMarkupLanguage.Tags
{
public abstract class BSMLTag
{
[Obsolete]
public bool isInitialized = false;

public abstract string[] Aliases { get; }

public virtual bool AddChildren { get => true; }

[Obsolete("Use BeatSaberUI.DiContainer instead")]
protected DiContainer DiContainer => BeatSaberUI.DiContainer;

public abstract GameObject CreateObject(Transform parent);

[Obsolete("This method is only called once in the entire lifetime of the application. Please use SetUp instead, which is called on internal restarts as well.")]
public virtual void Setup()
{
}

public virtual void SetUp()
{
}

protected LocalizedTextMeshProUGUI CreateLocalizableText(GameObject gameObject)
{
if (!gameObject.TryGetComponent(out TextMeshProUGUI textMesh))
Expand Down
15 changes: 5 additions & 10 deletions BeatSaberMarkupLanguage/Tags/BackgroundTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,22 @@

namespace BeatSaberMarkupLanguage.Tags
{
public class BackgroundTag : BSMLTag
public class BackgroundTag : PrefabBSMLTag
{
public override string[] Aliases => new[] { "bg", "background", "div" };

public override GameObject CreateObject(Transform parent)
protected override PrefabParams CreatePrefab()
{
GameObject gameObject = new("BSMLBackground")
{
layer = 5,
};

gameObject.transform.SetParent(parent, false);
GameObject gameObject = new("BSMLBackground");
gameObject.AddComponent<ContentSizeFitter>();
gameObject.AddComponent<Backgroundable>();

RectTransform rectTransform = gameObject.transform as RectTransform;
RectTransform rectTransform = (RectTransform)gameObject.transform;
rectTransform.anchorMin = new Vector2(0, 0);
rectTransform.anchorMax = new Vector2(1, 1);
rectTransform.sizeDelta = new Vector2(0, 0);

return gameObject;
return new PrefabParams(gameObject);
}
}
}
16 changes: 4 additions & 12 deletions BeatSaberMarkupLanguage/Tags/ButtonTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,19 @@

namespace BeatSaberMarkupLanguage.Tags
{
public class ButtonTag : BSMLTag
public class ButtonTag : PrefabBSMLTag
{
private Button buttonPrefab;

public override string[] Aliases => new[] { "button" };

public virtual Button PrefabButton => BeatSaberUI.DiContainer.Resolve<StandardLevelDetailViewController>()._standardLevelDetailView.practiceButton;

public override GameObject CreateObject(Transform parent)
protected override PrefabParams CreatePrefab()
{
if (buttonPrefab == null)
{
buttonPrefab = PrefabButton;
}

Button button = Object.Instantiate(buttonPrefab, parent, false);
Button button = Object.Instantiate(PrefabButton);
button.name = "BSMLButton";
button.interactable = true;

GameObject gameObject = button.gameObject;
gameObject.SetActive(true);

ExternalComponents externalComponents = gameObject.AddComponent<ExternalComponents>();
GameObject textObject = button.transform.Find("Content/Text").gameObject;
Expand All @@ -51,7 +43,7 @@ public override GameObject CreateObject(Transform parent)
externalComponents.components.Add(stackLayoutGroup);
}

return gameObject;
return new PrefabParams(gameObject);
}
}
}
Loading

0 comments on commit efb08c4

Please sign in to comment.