Files
Silly-Home/Packages/com.vrchat.worlds/Integrations/ClientSim/Runtime/ClientSimRuntimeLoader.cs
2026-06-07 16:58:24 +01:00

155 lines
5.3 KiB
C#

using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using VRC.Core;
namespace VRC.SDK3.ClientSim
{
public static class ClientSimRuntimeLoader
{
private const string EDITOR_ONLY_TAG = "EditorOnly";
// Used in tests to prevent the runtime initialized methods from executing.
private static bool _isInTestMode = false;
private static ClientSimSettings _testSettingsOverride;
private static ClientSimEventDispatcher _testEventDispatcherOverride;
#region ClientSim Initialization
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void OnBeforeSceneLoad()
{
StartClientSim(GetSettings(), GetEventDispatcher());
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static void OnAfterSceneLoad()
{
// Delete all editor only objects before creating ClientSim.
DestroyEditorOnly(GetSettings());
#if VRC_ENABLE_PLAYER_PERSISTENCE
if (ClientSimMain.TryGetInstance(out var instance))
instance.EnablePlayerObjects();
#endif
}
#endregion
#region Test Methods
[PublicAPI]
public static void BeginUnityTesting(
ClientSimSettings testSettingsOverride,
ClientSimEventDispatcher testEventDispatcherOverride = null)
{
_isInTestMode = true;
_testSettingsOverride = testSettingsOverride;
_testEventDispatcherOverride = testEventDispatcherOverride;
if (_testSettingsOverride == null)
{
_testSettingsOverride = new ClientSimSettings();
}
}
[PublicAPI]
public static void EndUnityTesting()
{
_isInTestMode = false;
_testSettingsOverride = null;
}
[PublicAPI]
public static bool IsInUnityTest()
{
return _isInTestMode;
}
#endregion
private static ClientSimSettings GetSettings()
{
return IsInUnityTest() ? _testSettingsOverride : ClientSimSettings.Instance;
}
private static ClientSimEventDispatcher GetEventDispatcher()
{
return IsInUnityTest() ? _testEventDispatcherOverride : null;
}
private static bool IsClientSimEnabled(ClientSimSettings settings)
{
return
settings.enableClientSim &&
Application.isPlaying;
}
// Start client sim with the given settings.
// Optional event dispatcher can be passed in to listen to startup events. Mainly used in tests.
public static void StartClientSim(
ClientSimSettings settings,
IClientSimEventDispatcher eventDispatcher = null)
{
if (!IsClientSimEnabled(settings))
{
return;
}
// Delete all editor only objects before creating ClientSim.
DestroyEditorOnly(settings);
ClientSimMain.CreateInstance(settings, eventDispatcher);
// TODO: Below is disabled for now because the rest of the ClientSim initialization code doesn't work if it's called with a delay.
// Currently, not loading RemoteConfig will not cause any issues, but it may in the future, so this is left in as a reminder.
// Create ClientSim Instance later
/*void CreateClientSimInstance() => ClientSimMain.CreateInstance(settings, eventDispatcher);
// If the Remote Config is not initialized, attempt init before starting ClientSim
// Start ClientSim after attempt, regardless of success or failure
if (!ConfigManager.RemoteConfig.IsInitialized())
{
API.SetOnlineMode(true);
ConfigManager.RemoteConfig.Init(CreateClientSimInstance, CreateClientSimInstance);
}
// Otherwise, start ClientSim immediately
else
{
CreateClientSimInstance();
}*/
}
private static void DestroyEditorOnly(ClientSimSettings settings)
{
if (!settings.enableClientSim || !settings.deleteEditorOnly)
{
return;
}
List<GameObject> rootObjects = new List<GameObject>();
Scene scene = SceneManager.GetActiveScene();
scene.GetRootGameObjects(rootObjects);
Queue<GameObject> queue = new Queue<GameObject>(rootObjects);
while (queue.Count > 0)
{
GameObject obj = queue.Dequeue();
if (obj.CompareTag(EDITOR_ONLY_TAG))
{
obj.Log($"Deleting editor only object: {Tools.GetGameObjectPath(obj)}");
Object.DestroyImmediate(obj);
}
else
{
for (int child = 0; child < obj.transform.childCount; ++child)
{
queue.Enqueue(obj.transform.GetChild(child).gameObject);
}
}
}
}
}
}