using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.IO; using Cysharp.Threading.Tasks; using UnityEngine; using VRC.Core; using VRC.Economy; using VRC.SDK3.Components; using VRC.SDK3.Network; using VRC.SDK3.Platform; using VRC.SDK3.Rendering; using VRC.SDK3.Video.Components.AVPro; using VRC.SDKBase; using VRC.SDKBase.Network; using VRC.SDKBase.Platform; using VRC.Udon; using VRC.Udon.Security; using VRCNetworkBehaviour = VRC.SDK3.Network.VRCNetworkBehaviour; using VRCStation = VRC.SDKBase.VRCStation; using VRC.SDK3.UdonNetworkCalling; using VRC.Utility; #if VRC_ENABLE_PLAYER_PERSISTENCE using VRC.SDK3.ClientSim.Persistence; #endif namespace VRC.SDK3.ClientSim { /// /// The main system for ClientSim. This handles the spawning of the ClientSim system and initialization. /// /// /// Sends Events: /// - ClientSimReadyEvent /// [AddComponentMenu("")] public class ClientSimMain : ClientSimBehaviour { private static readonly string _systemPrefabPath = Path.Combine("ClientSim", "Prefabs", "ClientSimSystem"); private static ClientSimMain _instance; [SerializeField] private ClientSimMenu menu; [SerializeField] private ClientSimBaseInput baseInput; [SerializeField] private ClientSimInputModule inputModule; [SerializeField] private ClientSimSyncedObjectManager syncedObjectManager; [SerializeField] private ClientSimInputManager inputManager; [SerializeField] private ClientSimUdonInputBehaviour udonInput; [SerializeField] private ClientSimHighlightManager highlightManager; [SerializeField] private ClientSimTooltipManager tooltipManager; [SerializeField] private ClientSimPlayerSpawner playerSpawner; [SerializeField] private ClientSimStackedVRCameraSystem stackedCameraSystem; [SerializeField] private ClientSimStoreManager storeManager; [SerializeField] private GameObject proxyObjectPrefab; private ClientSimProxyObjects _proxyObject; private IClientSimEventDispatcher _eventDispatcher; private ClientSimPlayerManager _playerManager; private ClientSimUdonManager _udonManager; private ClientSimSceneManager _sceneManager; private ClientSimBlacklistManager _blacklistManager; private ClientSimInteractiveLayerProvider _interactiveLayerProvider; private IClientSimSessionState _sessionState; private IClientSimUdonEventSender _udonEventSender; // public so that height can be adjusted via the settings window public ClientSimPlayerHeightManager HeightManager { get; private set; } private ClientSimSettings _settings; private ClientSimPlayer _player; private bool _isReady; public static void CreateInstance( ClientSimSettings settings, IClientSimEventDispatcher eventDispatcher = null) { if (HasInstance()) { throw new ClientSimException("Cannot create an instance of ClientSim while one already exists."); } // Configure Delegates for Allowlisted URL Validation VRCUrl.DomainExplicitAllowlistDelegate = () => UrlAllowlistConfig.DomainExplicitAllowlist; VRCUrl.DomainWildcardAllowlistDelegate = () => UrlAllowlistConfig.DomainWildcardAllowlist; GameObject systemPrefab = Resources.Load(_systemPrefabPath); if (systemPrefab == null) { throw new ClientSimException("Failed to start Client Sim! Main system prefab was not found."); } GameObject systemInstance = Instantiate(systemPrefab); systemInstance.name = $"__{systemPrefab.name}"; ClientSimMain main = systemInstance.GetComponent(); if (main == null) { throw new ClientSimException("Failed to start Client Sim! Main system component not found."); } try { main.Initialize(settings, eventDispatcher); } catch (ClientSimException e) { #if UNITY_EDITOR // Tests expect certain exceptions, don't exit play mode if (ClientSimRuntimeLoader.IsInUnityTest()) { throw e; } else { Debug.LogError($"Play mode Stopped because: {e.Message}"); UnityEditor.EditorApplication.isPlaying = false; } #else Debug.LogError($"Tried to initialize ClientSim outside of the Unity Editor: {e.Message}"); #endif } } public static bool HasInstance() { return _instance; } internal static bool TryGetInstance(out ClientSimMain instance) { if (_instance != null) { instance = _instance; return true; } instance = null; return false; } public static void SpawnRemotePlayer(string name = null) { if (HasInstance()) { _instance.SpawnRemotePlayerAndInitialize(name); } } public static void RemovePlayer(VRCPlayerApi player) { if (HasInstance()) { _instance._playerManager.RemovePlayer(player); } } // Used in tests public static void RemoveInstance() { if (HasInstance()) { DestroyImmediate(_instance.gameObject); } } protected override void Awake() { base.Awake(); if (_instance) { DestroyImmediate(this); throw new ClientSimException("Multiple instances of ClientSim running!"); } _instance = this; this.Log("Starting ClientSim"); DontDestroyOnLoad(this); SpawnProxyObjects(); } private void OnDestroy() { if (_proxyObject) { _proxyObject.DestroyProxy(); } _playerManager?.Dispose(); if (_instance != this) { return; } RemoveSDKLinks(); _instance = null; } private void Initialize( ClientSimSettings settings, IClientSimEventDispatcher eventDispatcher) { _sceneManager = new ClientSimSceneManager(); if (!_sceneManager.HasSceneDescriptor()) { _instance = null; Destroy(gameObject); Debug.LogWarning("Cannot start ClientSim if there is no scene descriptor!"); return; } ClientSimNetworkingUtilities.ConfigureNetworkOnScene(VRC_SceneDescriptor.Instance); _settings = settings; // Event Dispatcher is provided during tests to listen and send events before everything has been initialized. _eventDispatcher = eventDispatcher ?? new ClientSimEventDispatcher(); _blacklistManager = new ClientSimBlacklistManager(); _blacklistManager.AddObjectAndChildrenToBlackList(gameObject); UdonManager.Instance.LightReservedLayerMask = LayerHelper.Mask_ReservedRenderLayers.value; Texture2DDefaultTextureHolder.BlacklistDefaultTextures(UdonManager.Instance); _interactiveLayerProvider = new ClientSimInteractiveLayerProvider(_eventDispatcher); _sessionState = new ClientSimSessionState(); inputManager.Initialize(_settings); IClientSimInput input = inputManager.GetInput(); _udonEventSender = new ClientSimUdonManagerEventSender(UdonManager.Instance); HeightManager = new ClientSimPlayerHeightManager(_eventDispatcher, _udonEventSender); udonInput.Initialize(_eventDispatcher, input); menu.Initialize(_eventDispatcher, input, _settings, _sessionState, HeightManager); baseInput.Initialize(_eventDispatcher, input, _settings); inputModule.Initialize(_interactiveLayerProvider); _playerManager = new ClientSimPlayerManager(_eventDispatcher, HeightManager); // ObjectManager must be initialized before UdonManager to ensure object ownership for leaving players // is handled first. syncedObjectManager.Initialize(_eventDispatcher, _sceneManager, _playerManager); _udonManager = new ClientSimUdonManager(_eventDispatcher, syncedObjectManager, _udonEventSender); playerSpawner.Initialize(_sceneManager, _playerManager, _blacklistManager, _eventDispatcher, null); // Option to allow for spawning remote players first to prevent the local player from being master. // TODO replace this with networking test system to save previous player count in the instance. if (!_settings.localPlayerIsMaster) { SpawnRemotePlayerAndInitialize(); } // Spawn player controller to ensure that Local Player specific methods initialize properly. // This included getting the Player's camera. SpawnLocalPlayer(_settings.customLocalPlayerName); _player.Initialize( _eventDispatcher, input, _settings, highlightManager, tooltipManager, _interactiveLayerProvider, baseInput, _sceneManager, _proxyObject, _udonEventSender, _blacklistManager, _udonManager, syncedObjectManager, _playerManager, HeightManager); Camera playerCamera = _player.GetCameraProvider().GetCamera(); tooltipManager.Initialize(_settings, _player.GetTrackingProvider()); highlightManager.Initialize(playerCamera); stackedCameraSystem.Initialize(playerCamera, menu, _eventDispatcher); storeManager = new ClientSimStoreManager(_eventDispatcher, new ClientSimUdonManagerEventSender(UdonManager.Instance), _playerManager); // Initialize SDK links after everything has been created and initialized. SetupSDKLinks(); } #region Platform Management private Vector2 CurrentResolution; private VRCOrientation CurrentOrientation; private const int ScreenChangePollRate = 100; private async UniTaskVoid CheckForScreenChange() { while (_isReady) { if ((int)CurrentResolution.x != UnityEngine.Device.Screen.width || (int)CurrentResolution.y != UnityEngine.Device.Screen.height) { CurrentResolution = new Vector2(UnityEngine.Device.Screen.width, UnityEngine.Device.Screen.height); _udonEventSender.RunEvent("_onVRCCameraSettingsChanged", ("camera", VRCCameraSettings.ScreenCamera)); } var currentOrientation = DetermineOrientation(); if (currentOrientation != CurrentOrientation) { CurrentOrientation = DetermineOrientation(); // send ScreenUpdate event var screenUpdateEvent = new ClientSimScreenUpdateEvent() { data = new ScreenUpdateData() { type = ScreenUpdateType.OrientationChanged, orientation = CurrentOrientation, resolution = CurrentResolution } }; _eventDispatcher.SendEvent(screenUpdateEvent); } await UniTask.Delay(ScreenChangePollRate); } } private VRCOrientation DetermineOrientation() { return Screen.width < Screen.height ? VRCOrientation.Portrait : VRCOrientation.Landscape; } #endregion private void Start() { if (!_sceneManager.HasSceneDescriptor()) { throw new ClientSimException("Cannot start ClientSim if there is no world descriptor!"); } if (_settings.setTargetFrameRate) { Application.targetFrameRate = Mathf.Max(_settings.targetFrameRate, 1); Time.fixedDeltaTime = 1f / Application.targetFrameRate; } else { Application.targetFrameRate = -1; } StartCoroutine(InitializeClientSim()); } private IEnumerator InitializeClientSim() { // VRChatBug: VRChat does not initialize SDK components immediately. This delay is to provide Unity // components time to work before Udon starts to simulate how it is in client. float startDelay = Mathf.Max(_settings.initializationDelay, 0); yield return new WaitForSeconds(startDelay); _isReady = true; // Enable the player if set to spawn in the settings. if (_settings.spawnPlayer) { // Player can be invalid if we weren't able to spawn it if (_player) { _player.isInstanceOwner = _settings.isInstanceOwner; _player.isVRCPlus = _settings.isVRCPlus; _sceneManager.ResetSpawnOrder(); // Avoids any spawn offsets from pre-initialization of players _player.EnablePlayer(_sceneManager.GetSpawnPoint(false), _sceneManager.GetSpawnRadius()); } } // Notify UdonManager that ClientSim is ready. This will then notify all registered UdonBehaviours that // they can begin running. Udon will initialize in the next frame in the next Update call. yield return _udonManager.OnClientSimReady(); // Notify PlayerManager to send OnPlayerJoined events. // This must happen after Udon has been initialized to ensure UdonBehaviours are active to receive the event. // This must happen before ClientSimReadyEvent as some listeners depend on this. _playerManager.OnClientSimReady(); // Send event indicating ClientSim is initialized and ready. _eventDispatcher.SendEvent(new ClientSimReadyEvent()); stackedCameraSystem.Ready(); CheckForScreenChange().Forget(); this.Log("ClientSim Initialized"); } private void SpawnProxyObjects() { if (!proxyObjectPrefab) { throw new ClientSimException("Failed to start Client Sim! Proxy object prefab was not found."); } GameObject proxyInstance = Instantiate(proxyObjectPrefab); proxyInstance.name = "__" + proxyObjectPrefab.name; DontDestroyOnLoad(proxyInstance); _proxyObject = proxyInstance.GetComponent(); if (!_proxyObject) { throw new ClientSimException("Failed to start Client Sim! Proxy object script was not found."); } // Do not add this object to the blacklist since proxy objects are not blacklisted in udon. } private void SpawnLocalPlayer(string playerName = "") { _player = playerSpawner.SpawnPlayer(playerName, true); } private void SpawnRemotePlayerAndInitialize(string playerName = "") { playerSpawner.SpawnPlayer(playerName, false); } private bool IsObjectReady(GameObject obj) { return _isReady; } public bool IsNetworkReady() { return _isReady; } private string GetUniqueStringForObject(GameObject obj) { return obj.GetInstanceID().ToString(); } #if VRC_ENABLE_PLAYER_PERSISTENCE private GameObject[] GetPlayerPersistence(VRCPlayerApi playerApi) { ClientSimPlayer player = playerApi.GetClientSimPlayer(); if (player == null || player.PlayerPersistenceRootObjects == null) return System.Array.Empty(); return player.PlayerPersistenceRootObjects; } private Component FindComponentInPlayerObjects(VRCPlayerApi target, Component referenceComponent) { if (referenceComponent == null) throw new ArgumentNullException(nameof(referenceComponent)); VRCPlayerObject sourceObject = referenceComponent.GetComponentInParent(true); if (sourceObject == null) throw new ArgumentException($"{nameof(referenceComponent)} must be on a player object."); GameObject networkGameObject = ((Component)referenceComponent.gameObject.GetComponentInParent(true)).gameObject; VRCSceneDescriptor sdk3Descriptor = (VRCSceneDescriptor)VRC_SceneDescriptor.Instance; int idx = sdk3Descriptor.NetworkIDCollection.FindIndex((x) => x.gameObject == networkGameObject); if (idx < 0) throw new ArgumentException($"{nameof(referenceComponent)} must be on a player object in the NetworkIDCollection."); VRC.SDKBase.Network.NetworkIDPair networkIdPair = sdk3Descriptor.NetworkIDCollection[idx]; // See ClientSimPlayer.SetupPlayerPersistence for how the ID is calculated int desiredID = ClientSimNetworkingUtilities.FirstPlayerPersistenceID + networkIdPair.ID; GameObject[] objs = GetPlayerPersistence(target); if (objs == null) { Debug.LogError("Player persistence is missing for " + target.displayName); return null; } var possibleMatches = VRC.Tools.FindComponentInPossibleClones(sourceObject.gameObject, referenceComponent, objs); Debug.Log($"Found {possibleMatches.Count()} possible matches for {referenceComponent.name} in player objects for {target.displayName}");; foreach (Component possibleMatch in possibleMatches) { if (!possibleMatch) continue; ClientSimNetworkIdHolder networkIdHolder = possibleMatch.GetComponentInParent(true); if (!networkIdHolder) continue; int thisID = networkIdHolder.GetNetworkView()?.GetNetworkId() ?? 0; if (thisID <= 0) { Debug.LogError($"Network ID is invalid for {possibleMatch.name}"); continue; } int matchID = ClientSimNetworkingUtilities.FlattenPlayerViewId(thisID); if (matchID == desiredID) return possibleMatch; Debug.LogError($"Network ID {matchID} for {possibleMatch.name} does not match desired ID {desiredID}"); } Debug.LogError($"Failed to find component {referenceComponent.GetType().Name} in player objects for {target.displayName}"); return null; } public void EnablePlayerObjects() { _player.EnablePlayerObjects(); } public int GetPlayerDataUsageLimit() => 1024 * 100; public int GetPlayerObjectsUsageLimit() => 1024 * 100; public int GetPlayerDataUsage(VRCPlayerApi player) { Debug.LogWarning("Player Data usage is not accurate in ClientSim."); return ClientSimPlayerDataWrapper.GetUsage(player); } public int GetPlayerObjectsUsage(VRCPlayerApi player) { Debug.LogWarning("Player Object usage is not accurate in ClientSim."); ClientSimPlayer simPlayer = player.GetClientSimPlayer(); return simPlayer.PlayerObjectData == null ? 0 : simPlayer.PlayerObjectData.Size; } public void RequestStorageUsageUpdate() { UdonManager.Instance.RunEvent(UdonManager.UDON_EVENT_ONPERSISTENCEUSAGEUPDATED); } #endif private static void CallNetworkConfigure(VRCNetworkBehaviour behaviour) { behaviour.NetworkConfigure(); } private static bool IsSuffering() => false; // we did it chat, no more suffering private static void SetShadowDistance(float low, float medium, float high, float mobile) => QualitySettings.shadowDistance = high; private static void SetShadowDistanceAll(float distance) => QualitySettings.shadowDistance = distance; private static void ResetShadowDistance() => QualitySettings.shadowDistance = 150; // TODO: base on project quality settings? private void OnCameraSettingsChanged(Camera camera) { camera.farClipPlane = Mathf.Max(camera.farClipPlane, camera.nearClipPlane + 0.1f); // don't allow turning off MirrorReflection or stacked layers (InternalUI) camera.cullingMask &= ~(1 << LayerMask.NameToLayer("MirrorReflection")); camera.cullingMask &= ~stackedCameraSystem.RequestedStackLayers; } private static void OpenAvatarListing(string id) => Debug.Log($"opening avatar listing for {id}"); #region VRChat SDK Links // If adding to this list, be sure to also remove the link in the RemoveSDKLinks method private void SetupSDKLinks() { UdonBehaviour.OnInit += _udonManager.InitUdon; Networking._IsMaster += _playerManager.IsLocalPlayerMaster; Networking._LocalPlayer += _playerManager.LocalPlayer; Networking._GetOwner += _playerManager.GetOwner; Networking._IsOwner += _playerManager.IsOwner; Networking._SetOwner += ClientSimPlayerManager.SetOwner; Networking._GetMaster += _playerManager.GetMaster; Networking._GetInstanceOwner += _playerManager.GetInstanceOwner; Networking._IsInstanceOwner += _playerManager.IsInstanceOwner; Networking._IsObjectReady += IsObjectReady; Networking._IsNetworkSettled += IsNetworkReady; Networking._IsSuffering += IsSuffering; Networking._GetUniqueName += GetUniqueStringForObject; #if VRC_ENABLE_PLAYER_PERSISTENCE ClientSimPlayerDataWrapper.ConfigureSDK(); Networking._GetPlayerPersistence += GetPlayerPersistence; Networking._GetPlayerDataStorageUsage += GetPlayerDataUsage; Networking._GetPlayerObjectStorageUsage += GetPlayerObjectsUsage; Networking._GetPlayerDataStorageLimit += GetPlayerDataUsageLimit; Networking._GetPlayerObjectStorageLimit += GetPlayerObjectsUsageLimit; Networking._FindComponentInPlayerObjects += FindComponentInPlayerObjects; #endif #if VRC_ENABLE_PLAYER_PERSISTENCE || VRC_ENABLE_INSTANCE_PERSISTENCE Networking._RequestStorageUsageUpdate += RequestStorageUsageUpdate; #endif VRCNetworkBehaviour.OnNetworkBehaviourAwake += CallNetworkConfigure; VRCStation.Initialize += ClientSimStationHelper.InitializeStations; VRCStation.useStationDelegate += ClientSimStationHelper.UseStation; VRCStation.exitStationDelegate += ClientSimStationHelper.ExitStation; VRCPlayerApi._UseAttachedStation += ClientSimStationHelper.UseAttachedStation; if (_player != null) { VRC_UiShape.GetEventCamera += _player.GetCameraProvider().GetCameraForObject; VRCCameraSettings.ScreenCameraRef = _player.GetCameraProvider().GetCamera(); VRCCameraSettings.ScreenCamera.GetCameraMode = () => VRCCameraMode.Screen; } VRC_Pickup.OnAwake += ClientSimPickupHelper.InitializePickup; VRC_Pickup.ForceDrop += ClientSimPickupHelper.ForceDrop; VRC_Pickup._GetCurrentPlayer += ClientSimPickupHelper.GetCurrentPlayer; VRC_Pickup._GetPickupHand += ClientSimPickupHelper.GetPickupHand; VRC_Pickup.OnDestroyed += ClientSimPickupHelper.PickupDestroy; VRC_Pickup.HapticEvent += ClientSimPickupHelper.PlayHapticForPickup; Components.VRCObjectPool.OnInit += syncedObjectManager.InitializeObjectPool; Components.VRCObjectPool.OnReturn += ClientSimObjectPoolHelper.OnReturn; Components.VRCObjectPool.OnSpawn += ClientSimObjectPoolHelper.OnSpawn; Components.VRCObjectSync.OnAwake += syncedObjectManager.InitializeObjectSync; Components.VRCObjectSync.FlagDiscontinuityHook += ClientSimObjectSyncHelper.FlagDiscontinuityHook; Components.VRCObjectSync.RespawnHandler += ClientSimObjectSyncHelper.RespawnObject; Components.VRCObjectSync.TeleportHandler += ClientSimObjectSyncHelper.TeleportTo; Components.VRCObjectSync.SetGravityHook += ClientSimObjectSyncHelper.SetUseGravity; Components.VRCObjectSync.SetKinematicHook += ClientSimObjectSyncHelper.SetIsKinematic; VRCPlayerApi._GetPlayerId += _playerManager.GetPlayerID; VRCPlayerApi._GetPlayerById += _playerManager.GetPlayerByID; VRCPlayerApi._isMasterDelegate += _playerManager.IsMaster; VRCPlayerApi._isSuspendedDelegate += _playerManager.IsSuspended; VRCPlayerApi._isVRCPlusDelegate += _playerManager.IsVRCPlus; VRCPlayerApi._TakeOwnership += ClientSimPlayerManager.SetOwner; VRCPlayerApi._IsOwner += _playerManager.IsOwner; VRCPlayerApi._isInstanceOwnerDelegate += _playerManager.IsInstanceOwner; VRCPlayerApi._EnablePickups += ClientSimPlayerManager.EnablePickups; VRCPlayerApi._Immobilize += ClientSimPlayerManager.Immobilize; VRCPlayerApi._TeleportTo += ClientSimPlayerManager.TeleportTo; VRCPlayerApi._TeleportToOrientation += ClientSimPlayerManager.TeleportToOrientation; VRCPlayerApi._TeleportToOrientationLerp += ClientSimPlayerManager.TeleportToOrientationLerp; VRCPlayerApi._PlayHapticEventInHand += ClientSimPlayerManager.PlayHapticEventInHand; VRCPlayerApi._GetPlayerByGameObject += ClientSimPlayerManager.GetPlayerByGameObject; VRCPlayerApi._GetPickupInHand += ClientSimPlayerManager.GetPickupInHand; VRCPlayerApi._GetTrackingData += ClientSimPlayerManager.GetTrackingData; VRCPlayerApi._GetBonePosition += ClientSimPlayerManager.GetBonePosition; VRCPlayerApi._GetBoneRotation += ClientSimPlayerManager.GetBoneRotation; VRCPlayerApi._ClearPlayerTags += ClientSimPlayerManager.ClearPlayerTags; VRCPlayerApi._SetPlayerTag += ClientSimPlayerManager.SetPlayerTag; VRCPlayerApi._GetPlayerTag += ClientSimPlayerManager.GetPlayerTag; VRCPlayerApi._GetPlayersWithTag += ClientSimPlayerManager.GetPlayersWithTag; VRCPlayerApi._SetSilencedToTagged += ClientSimPlayerManager.SetSilencedToTagged; VRCPlayerApi._SetSilencedToUntagged += ClientSimPlayerManager.SetSilencedToUntagged; VRCPlayerApi._ClearSilence += ClientSimPlayerManager.ClearSilence; VRCPlayerApi._IsUserInVR += ClientSimPlayerManager.IsUserInVR; VRCPlayerApi._GetRunSpeed += ClientSimPlayerManager.GetRunSpeed; VRCPlayerApi._SetRunSpeed += ClientSimPlayerManager.SetRunSpeed; VRCPlayerApi._GetWalkSpeed += ClientSimPlayerManager.GetWalkSpeed; VRCPlayerApi._SetWalkSpeed += ClientSimPlayerManager.SetWalkSpeed; VRCPlayerApi._GetJumpImpulse += ClientSimPlayerManager.GetJumpImpulse; VRCPlayerApi._SetJumpImpulse += ClientSimPlayerManager.SetJumpImpulse; VRCPlayerApi._GetStrafeSpeed += ClientSimPlayerManager.GetStrafeSpeed; VRCPlayerApi._SetStrafeSpeed += ClientSimPlayerManager.SetStrafeSpeed; VRCPlayerApi._GetVelocity += ClientSimPlayerManager.GetVelocity; VRCPlayerApi._SetVelocity += ClientSimPlayerManager.SetVelocity; VRCPlayerApi._GetPosition += ClientSimPlayerManager.GetPosition; VRCPlayerApi._GetRotation += ClientSimPlayerManager.GetRotation; VRCPlayerApi._GetGravityStrength += ClientSimPlayerManager.GetGravityStrength; VRCPlayerApi._SetGravityStrength += ClientSimPlayerManager.SetGravityStrength; VRCPlayerApi.IsGrounded += ClientSimPlayerManager.IsGrounded; VRCPlayerApi._UseLegacyLocomotion += ClientSimPlayerManager.UseLegacyLocomotion; VRCPlayerApi._Respawn += ClientSimPlayerManager.Respawn; VRCPlayerApi._RespawnWithIndex += ClientSimPlayerManager.RespawnWithIndex; VRCPlayerApi._GetManualAvatarScalingAllowed += ClientSimPlayerManager.GetManualAvatarScalingAllowed; VRCPlayerApi._SetManualAvatarScalingAllowed += ClientSimPlayerManager.SetManualAvatarScalingAllowed; VRCPlayerApi._GetAvatarEyeHeightMinimumAsMeters += ClientSimPlayerManager.GetAvatarEyeHeightMinimumAsMeters; VRCPlayerApi._GetAvatarEyeHeightMaximumAsMeters += ClientSimPlayerManager.GetAvatarEyeHeightMaximumAsMeters; VRCPlayerApi._SetAvatarEyeHeightMinimumByMeters += ClientSimPlayerManager.SetAvatarEyeHeightMinimumByMeters; VRCPlayerApi._SetAvatarEyeHeightMaximumByMeters += ClientSimPlayerManager.SetAvatarEyeHeightMaximumByMeters; VRCPlayerApi._GetAvatarEyeHeightAsMeters += ClientSimPlayerManager.GetAvatarEyeHeightAsMeters; VRCPlayerApi._SetAvatarEyeHeightByMeters += ClientSimPlayerManager.SetAvatarEyeHeightByMeters; VRCPlayerApi._SetAvatarEyeHeightByMultiplier += ClientSimPlayerManager.SetAvatarEyeHeightByMultiplier; VRCPlayerApi._CombatSetup += ClientSimCombatSystemHelper.CombatSetup; VRCPlayerApi._CombatSetMaxHitpoints += ClientSimCombatSystemHelper.CombatSetMaxHitpoints; VRCPlayerApi._CombatGetCurrentHitpoints += ClientSimCombatSystemHelper.CombatGetCurrentHitpoints; VRCPlayerApi._CombatSetRespawn += ClientSimCombatSystemHelper.CombatSetRespawn; VRCPlayerApi._CombatSetDamageGraphic += ClientSimCombatSystemHelper.CombatSetDamageGraphic; VRCPlayerApi._CombatGetDestructible += ClientSimCombatSystemHelper.CombatGetDestructible; VRCPlayerApi._CombatSetCurrentHitpoints += ClientSimCombatSystemHelper.CombatSetCurrentHitpoints; VRCPlayerApi._SetAvatarAudioVolumetricRadius += ClientSimPlayerManager.SetAvatarAudioVolumetricRadius; VRCPlayerApi._SetAvatarAudioNearRadius += ClientSimPlayerManager.SetAvatarAudioNearRadius; VRCPlayerApi._SetAvatarAudioFarRadius += ClientSimPlayerManager.SetAvatarAudioFarRadius; VRCPlayerApi._SetAvatarAudioGain += ClientSimPlayerManager.SetAvatarAudioGain; VRCPlayerApi._SetAvatarAudioForceSpatial += ClientSimPlayerManager.SetAvatarAudioForceSpatial; VRCPlayerApi._SetAvatarAudioCustomCurve += ClientSimPlayerManager.SetAvatarAudioCustomCurve; VRCPlayerApi._SetVoiceGain += ClientSimPlayerManager.SetVoiceGain; VRCPlayerApi._SetVoiceDistanceNear += ClientSimPlayerManager.SetVoiceDistanceNear; VRCPlayerApi._SetVoiceDistanceFar += ClientSimPlayerManager.SetVoiceDistanceFar; VRCPlayerApi._SetVoiceVolumetricRadius += ClientSimPlayerManager.SetVoiceVolumetricRadius; VRCPlayerApi._SetVoiceLowpass += ClientSimPlayerManager.SetVoiceLowpass; VRCPlayerApi._GetVoiceGain += ClientSimPlayerManager.GetVoiceGain; VRCPlayerApi._GetVoiceDistanceNear += ClientSimPlayerManager.GetVoiceDistanceNear; VRCPlayerApi._GetVoiceDistanceFar += ClientSimPlayerManager.GetVoiceDistanceFar; VRCPlayerApi._GetVoiceVolumetricRadius += ClientSimPlayerManager.GetVoiceVolumetricRadius; VRCPlayerApi._GetVoiceLowpass += ClientSimPlayerManager.GetVoiceLowpass; VRCPlayerApi._GetCurrentLanguage += ClientSimPlayerManager.GetCurrentLanguage; VRCPlayerApi._GetAvailableLanguages += ClientSimPlayerManager.GetAvailableLanguages; VRC_SpatialAudioSource.Initialize += ClientSimSpatialAudioHelper.InitializeAudio; VRCAVProVideoPlayer.Initialize += ClientSimAVProVideoStub.InitializePlayer; InputManager._EnableObjectHighlight += highlightManager.EnableObjectHighlight; InputManager._GetLastUsedInputMethod += inputManager.GetLastUsedInputMethod; Store._sendProductEvent += ClientSimStoreManager.SendProductEvent; Store._listPurchases += ClientSimStoreManager.ListPurchases; Store._listAvailableProducts += ClientSimStoreManager.ListAvailableProducts; Store._doesPlayerOwnProduct += ClientSimStoreManager.DoesPlayerOwnProduct; Store._doesAnyPlayerOwnProduct += ClientSimStoreManager.DoesAnyPlayerOwnProduct; Store._getPlayersWhoOwnProduct += ClientSimStoreManager.GetPlayersWhoOwnProduct; Store._listProductOwners += ClientSimStoreManager.ListProductOwners; VRCQualitySettings.SetShadowDistanceInternal += SetShadowDistance; VRCQualitySettings.SetShadowDistanceInternalAll += SetShadowDistanceAll; VRCQualitySettings.ResetShadowDistanceInternal += ResetShadowDistance; VRCCameraSettings.OnCameraSettingsChanged += OnCameraSettingsChanged; NetworkCalling.GetAllQueuedEventsProxy += ClientSimNetworkCalling.GetAllQueuedEventsProxy; NetworkCalling.GetQueuedEventsProxy += ClientSimNetworkCalling.GetQueuedEventsProxy; NetworkCalling.SendCustomNetworkEventProxy += ClientSimNetworkCalling.SendCustomNetworkEventProxy; VRC.SDK3.Components.VRCOpenMenu._OpenAvatarListingDelegate = OpenAvatarListing; } private void RemoveSDKLinks() { UdonBehaviour.OnInit -= _udonManager.InitUdon; Networking._IsMaster -= _playerManager.IsLocalPlayerMaster; Networking._LocalPlayer -= _playerManager.LocalPlayer; Networking._GetOwner -= _playerManager.GetOwner; Networking._IsOwner -= _playerManager.IsOwner; Networking._SetOwner -= ClientSimPlayerManager.SetOwner; Networking._IsInstanceOwner -= _playerManager.IsInstanceOwner; Networking._IsObjectReady -= IsObjectReady; Networking._IsNetworkSettled -= IsNetworkReady; Networking._IsSuffering -= IsSuffering; Networking._GetUniqueName -= GetUniqueStringForObject; #if VRC_ENABLE_PLAYER_PERSISTENCE Networking._GetPlayerPersistence -= GetPlayerPersistence; Networking._GetPlayerDataStorageUsage -= GetPlayerDataUsage; Networking._GetPlayerObjectStorageUsage -= GetPlayerObjectsUsage; Networking._GetPlayerDataStorageLimit -= GetPlayerDataUsageLimit; Networking._GetPlayerObjectStorageLimit -= GetPlayerObjectsUsageLimit; Networking._FindComponentInPlayerObjects -= FindComponentInPlayerObjects; #endif #if VRC_ENABLE_PLAYER_PERSISTENCE || VRC_ENABLE_INSTANCE_PERSISTENCE Networking._RequestStorageUsageUpdate -= RequestStorageUsageUpdate; #endif VRCNetworkBehaviour.OnNetworkBehaviourAwake -= CallNetworkConfigure; VRCStation.Initialize -= ClientSimStationHelper.InitializeStations; VRCStation.useStationDelegate -= ClientSimStationHelper.UseStation; VRCStation.exitStationDelegate -= ClientSimStationHelper.ExitStation; VRCPlayerApi._UseAttachedStation -= ClientSimStationHelper.UseAttachedStation; // Player can be invalid if we weren't able to spawn if (_player) { VRC_UiShape.GetEventCamera -= _player.GetCameraProvider().GetCameraForObject; } VRC_Pickup.OnAwake -= ClientSimPickupHelper.InitializePickup; VRC_Pickup.ForceDrop -= ClientSimPickupHelper.ForceDrop; VRC_Pickup._GetCurrentPlayer -= ClientSimPickupHelper.GetCurrentPlayer; VRC_Pickup._GetPickupHand -= ClientSimPickupHelper.GetPickupHand; VRC_Pickup.OnDestroyed -= ClientSimPickupHelper.PickupDestroy; VRC_Pickup.HapticEvent -= ClientSimPickupHelper.PlayHapticForPickup; Components.VRCObjectPool.OnInit -= syncedObjectManager.InitializeObjectPool; Components.VRCObjectPool.OnReturn -= ClientSimObjectPoolHelper.OnReturn; Components.VRCObjectPool.OnSpawn -= ClientSimObjectPoolHelper.OnSpawn; Components.VRCObjectSync.OnAwake -= syncedObjectManager.InitializeObjectSync; Components.VRCObjectSync.FlagDiscontinuityHook -= ClientSimObjectSyncHelper.FlagDiscontinuityHook; Components.VRCObjectSync.RespawnHandler -= ClientSimObjectSyncHelper.RespawnObject; Components.VRCObjectSync.TeleportHandler -= ClientSimObjectSyncHelper.TeleportTo; Components.VRCObjectSync.SetGravityHook -= ClientSimObjectSyncHelper.SetUseGravity; Components.VRCObjectSync.SetKinematicHook -= ClientSimObjectSyncHelper.SetIsKinematic; VRCPlayerApi._GetPlayerId -= _playerManager.GetPlayerID; VRCPlayerApi._GetPlayerById -= _playerManager.GetPlayerByID; VRCPlayerApi._isMasterDelegate -= _playerManager.IsMaster; VRCPlayerApi._isSuspendedDelegate -= _playerManager.IsSuspended; VRCPlayerApi._isVRCPlusDelegate -= _playerManager.IsVRCPlus; VRCPlayerApi._TakeOwnership -= ClientSimPlayerManager.SetOwner; VRCPlayerApi._IsOwner -= _playerManager.IsOwner; VRCPlayerApi._isInstanceOwnerDelegate -= _playerManager.IsInstanceOwner; VRCPlayerApi._EnablePickups -= ClientSimPlayerManager.EnablePickups; VRCPlayerApi._Immobilize -= ClientSimPlayerManager.Immobilize; VRCPlayerApi._TeleportTo -= ClientSimPlayerManager.TeleportTo; VRCPlayerApi._TeleportToOrientation -= ClientSimPlayerManager.TeleportToOrientation; VRCPlayerApi._TeleportToOrientationLerp -= ClientSimPlayerManager.TeleportToOrientationLerp; VRCPlayerApi._PlayHapticEventInHand -= ClientSimPlayerManager.PlayHapticEventInHand; VRCPlayerApi._GetPlayerByGameObject -= ClientSimPlayerManager.GetPlayerByGameObject; VRCPlayerApi._GetPickupInHand -= ClientSimPlayerManager.GetPickupInHand; VRCPlayerApi._GetTrackingData -= ClientSimPlayerManager.GetTrackingData; VRCPlayerApi._GetBonePosition -= ClientSimPlayerManager.GetBonePosition; VRCPlayerApi._GetBoneRotation -= ClientSimPlayerManager.GetBoneRotation; VRCPlayerApi._ClearPlayerTags -= ClientSimPlayerManager.ClearPlayerTags; VRCPlayerApi._SetPlayerTag -= ClientSimPlayerManager.SetPlayerTag; VRCPlayerApi._GetPlayerTag -= ClientSimPlayerManager.GetPlayerTag; VRCPlayerApi._GetPlayersWithTag -= ClientSimPlayerManager.GetPlayersWithTag; VRCPlayerApi._SetSilencedToTagged -= ClientSimPlayerManager.SetSilencedToTagged; VRCPlayerApi._SetSilencedToUntagged -= ClientSimPlayerManager.SetSilencedToUntagged; VRCPlayerApi._ClearSilence -= ClientSimPlayerManager.ClearSilence; VRCPlayerApi._IsUserInVR -= ClientSimPlayerManager.IsUserInVR; VRCPlayerApi._GetRunSpeed -= ClientSimPlayerManager.GetRunSpeed; VRCPlayerApi._SetRunSpeed -= ClientSimPlayerManager.SetRunSpeed; VRCPlayerApi._GetWalkSpeed -= ClientSimPlayerManager.GetWalkSpeed; VRCPlayerApi._SetWalkSpeed -= ClientSimPlayerManager.SetWalkSpeed; VRCPlayerApi._GetJumpImpulse -= ClientSimPlayerManager.GetJumpImpulse; VRCPlayerApi._SetJumpImpulse -= ClientSimPlayerManager.SetJumpImpulse; VRCPlayerApi._GetStrafeSpeed -= ClientSimPlayerManager.GetStrafeSpeed; VRCPlayerApi._SetStrafeSpeed -= ClientSimPlayerManager.SetStrafeSpeed; VRCPlayerApi._GetVelocity -= ClientSimPlayerManager.GetVelocity; VRCPlayerApi._SetVelocity -= ClientSimPlayerManager.SetVelocity; VRCPlayerApi._GetPosition -= ClientSimPlayerManager.GetPosition; VRCPlayerApi._GetRotation -= ClientSimPlayerManager.GetRotation; VRCPlayerApi._GetGravityStrength -= ClientSimPlayerManager.GetGravityStrength; VRCPlayerApi._SetGravityStrength -= ClientSimPlayerManager.SetGravityStrength; VRCPlayerApi.IsGrounded -= ClientSimPlayerManager.IsGrounded; VRCPlayerApi._UseLegacyLocomotion -= ClientSimPlayerManager.UseLegacyLocomotion; VRCPlayerApi._Respawn -= ClientSimPlayerManager.Respawn; VRCPlayerApi._RespawnWithIndex -= ClientSimPlayerManager.RespawnWithIndex; VRCPlayerApi._GetManualAvatarScalingAllowed -= ClientSimPlayerManager.GetManualAvatarScalingAllowed; VRCPlayerApi._SetManualAvatarScalingAllowed -= ClientSimPlayerManager.SetManualAvatarScalingAllowed; VRCPlayerApi._GetAvatarEyeHeightMinimumAsMeters -= ClientSimPlayerManager.GetAvatarEyeHeightMinimumAsMeters; VRCPlayerApi._GetAvatarEyeHeightMaximumAsMeters -= ClientSimPlayerManager.GetAvatarEyeHeightMaximumAsMeters; VRCPlayerApi._SetAvatarEyeHeightMinimumByMeters -= ClientSimPlayerManager.SetAvatarEyeHeightMinimumByMeters; VRCPlayerApi._SetAvatarEyeHeightMaximumByMeters -= ClientSimPlayerManager.SetAvatarEyeHeightMaximumByMeters; VRCPlayerApi._GetAvatarEyeHeightAsMeters -= ClientSimPlayerManager.GetAvatarEyeHeightAsMeters; VRCPlayerApi._SetAvatarEyeHeightByMeters -= ClientSimPlayerManager.SetAvatarEyeHeightByMeters; VRCPlayerApi._SetAvatarEyeHeightByMultiplier -= ClientSimPlayerManager.SetAvatarEyeHeightByMultiplier; VRCPlayerApi._CombatSetup -= ClientSimCombatSystemHelper.CombatSetup; VRCPlayerApi._CombatSetMaxHitpoints -= ClientSimCombatSystemHelper.CombatSetMaxHitpoints; VRCPlayerApi._CombatGetCurrentHitpoints -= ClientSimCombatSystemHelper.CombatGetCurrentHitpoints; VRCPlayerApi._CombatSetRespawn -= ClientSimCombatSystemHelper.CombatSetRespawn; VRCPlayerApi._CombatSetDamageGraphic -= ClientSimCombatSystemHelper.CombatSetDamageGraphic; VRCPlayerApi._CombatGetDestructible -= ClientSimCombatSystemHelper.CombatGetDestructible; VRCPlayerApi._CombatSetCurrentHitpoints -= ClientSimCombatSystemHelper.CombatSetCurrentHitpoints; VRCPlayerApi._SetAvatarAudioVolumetricRadius -= ClientSimPlayerManager.SetAvatarAudioVolumetricRadius; VRCPlayerApi._SetAvatarAudioNearRadius -= ClientSimPlayerManager.SetAvatarAudioNearRadius; VRCPlayerApi._SetAvatarAudioFarRadius -= ClientSimPlayerManager.SetAvatarAudioFarRadius; VRCPlayerApi._SetAvatarAudioGain -= ClientSimPlayerManager.SetAvatarAudioGain; VRCPlayerApi._SetAvatarAudioForceSpatial -= ClientSimPlayerManager.SetAvatarAudioForceSpatial; VRCPlayerApi._SetAvatarAudioCustomCurve -= ClientSimPlayerManager.SetAvatarAudioCustomCurve; VRCPlayerApi._SetVoiceLowpass -= ClientSimPlayerManager.SetVoiceLowpass; VRCPlayerApi._SetVoiceVolumetricRadius -= ClientSimPlayerManager.SetVoiceVolumetricRadius; VRCPlayerApi._SetVoiceDistanceFar -= ClientSimPlayerManager.SetVoiceDistanceFar; VRCPlayerApi._SetVoiceDistanceNear -= ClientSimPlayerManager.SetVoiceDistanceNear; VRCPlayerApi._SetVoiceGain -= ClientSimPlayerManager.SetVoiceGain; VRC_SpatialAudioSource.Initialize -= ClientSimSpatialAudioHelper.InitializeAudio; VRCAVProVideoPlayer.Initialize -= ClientSimAVProVideoStub.InitializePlayer; InputManager._EnableObjectHighlight -= highlightManager.EnableObjectHighlight; Store._sendProductEvent -= ClientSimStoreManager.SendProductEvent; Store._listPurchases -= ClientSimStoreManager.ListPurchases; Store._listAvailableProducts -= ClientSimStoreManager.ListAvailableProducts; Store._doesPlayerOwnProduct -= ClientSimStoreManager.DoesPlayerOwnProduct; Store._doesAnyPlayerOwnProduct -= ClientSimStoreManager.DoesAnyPlayerOwnProduct; Store._getPlayersWhoOwnProduct -= ClientSimStoreManager.GetPlayersWhoOwnProduct; VRCQualitySettings.SetShadowDistanceInternal -= SetShadowDistance; VRCQualitySettings.SetShadowDistanceInternalAll -= SetShadowDistanceAll; VRCQualitySettings.ResetShadowDistanceInternal -= ResetShadowDistance; VRCCameraSettings.OnCameraSettingsChanged -= OnCameraSettingsChanged; NetworkCalling.GetAllQueuedEventsProxy -= ClientSimNetworkCalling.GetAllQueuedEventsProxy; NetworkCalling.GetQueuedEventsProxy -= ClientSimNetworkCalling.GetQueuedEventsProxy; NetworkCalling.SendCustomNetworkEventProxy -= ClientSimNetworkCalling.SendCustomNetworkEventProxy; VRC.SDK3.Components.VRCOpenMenu._OpenAvatarListingDelegate -= OpenAvatarListing; } #endregion #region Test Accessors internal ClientSimProxyObjects GetProxyObjects() { return _proxyObject; } internal ClientSimMenu GetMenu() { return menu; } internal IClientSimEventDispatcher GetEventDispatcher() { return _eventDispatcher; } internal IClientSimUdonEventSender GetUdonEventSender() { return _udonEventSender; } internal IClientSimBlacklistManager GetBlacklistManager() { return _blacklistManager; } internal IClientSimUdonManager GetUdonManager() { return _udonManager; } internal IClientSimSyncedObjectManager GetSyncedObjectManager() { return syncedObjectManager; } internal IClientSimPlayerManager GetPlayerManager() { return _playerManager; } #endregion } }