OliveRave/UnityProject/Assets/CyanEmu/Scripts/CyanEmuMain.cs

637 lines
25 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using VRC.SDKBase;
using Random = UnityEngine.Random;
namespace VRCPrefabs.CyanEmu
{
[AddComponentMenu("")]
public class CyanEmuMain : MonoBehaviour
{
private const string CYAN_EMU_GAMEOBJECT_NAME_ = "__CyanEmu";
private const string EDITOR_ONLY_TAG_ = "EditorOnly";
private static CyanEmuMain instance_;
private ICyanEmuSDKManager sdkManager_;
private CyanEmuSettings settings_;
private CyanEmuPlayerController playerController_;
private VRC_SceneDescriptor descriptor_;
private Transform proxyObjectParents_;
private bool shouldVerifySyncedObjectList_;
private readonly Queue<CyanEmuSyncedObjectHelper> toBeAddedSync_ = new Queue<CyanEmuSyncedObjectHelper>();
private readonly Queue<CyanEmuSyncedObjectHelper> toBeRemovedSync_ = new Queue<CyanEmuSyncedObjectHelper>();
private HashSet<CyanEmuSyncedObjectHelper> allSyncedObjects_ = new HashSet<CyanEmuSyncedObjectHelper>();
private int spawnedObjectCount_;
private bool networkReady_;
private int spawnOrder_ = 0;
// TODO save syncables
//private CyanEmuBufferManager bufferManager_;
// Dummy method to get the static initializer to be called early on.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void OnBeforeSceneLoad() { }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void OnAfterSceneLoad()
{
if (!CyanEmuEnabled())
{
return;
}
DestroyEditorOnly();
}
static CyanEmuMain()
{
if (!CyanEmuEnabled())
{
return;
}
LinkAPI();
CreateInstance();
}
private static bool CyanEmuEnabled()
{
return
CyanEmuSettings.Instance.enableCyanEmu &&
FindObjectOfType<PipelineSaver>() == null &&
Application.isPlaying;
}
private static void LinkAPI()
{
VRCStation.Initialize += CyanEmuStationHelper.InitializeStations;
VRCStation.useStationDelegate = CyanEmuStationHelper.UseStation;
VRCStation.exitStationDelegate = CyanEmuStationHelper.ExitStation;
VRC_UiShape.GetEventCamera = CyanEmuPlayerController.GetPlayerCamera;
VRC_Pickup.OnAwake = CyanEmuPickupHelper.InitializePickup;
VRC_Pickup.ForceDrop = CyanEmuPickupHelper.ForceDrop;
VRC_Pickup._GetCurrentPlayer = CyanEmuPickupHelper.GetCurrentPlayer;
VRC_Pickup._GetPickupHand = CyanEmuPickupHelper.GetPickupHand;
VRC_ObjectSpawn.Initialize = CyanEmuObjectSpawnHelper.InitializeSpawner;
#if UDON && VRC_SDK_VRCSDK3
VRC.Udon.UdonBehaviour.OnInit = CyanEmuUdonHelper.OnInit;
// This is no longer used as of SDK 2021.03.22.18.27
VRC.Udon.UdonBehaviour.SendCustomNetworkEventHook = CyanEmuUdonHelper.SendCustomNetworkEventHook;
// TODO
//VRC.Udon.UdonBehaviour.CheckValid
VRC.SDK3.Components.VRCObjectPool.OnInit = CyanEmuObjectPoolHelper.OnInit;
VRC.SDK3.Components.VRCObjectPool.OnReturn = CyanEmuObjectPoolHelper.OnReturn;
VRC.SDK3.Components.VRCObjectPool.OnSpawn = CyanEmuObjectPoolHelper.OnSpawn;
VRC.SDK3.Components.VRCObjectSync.FlagDiscontinuityHook = CyanEmuObjectSyncHelper.FlagDiscontinuityHook;
VRC.SDK3.Components.VRCObjectSync.OnAwake = CyanEmuObjectSyncHelper.InitializeObjectSync;
VRC.SDK3.Components.VRCObjectSync.RespawnHandler = CyanEmuObjectSyncHelper.RespawnObject;
VRC.SDK3.Components.VRCObjectSync.TeleportHandler = CyanEmuObjectSyncHelper.TeleportTo;
VRC.SDK3.Components.VRCObjectSync.SetGravityHook = CyanEmuObjectSyncHelper.SetUseGravity;
VRC.SDK3.Components.VRCObjectSync.SetKinematicHook = CyanEmuObjectSyncHelper.SetIsKinematic;
#endif
#if VRC_SDK_VRCSDK2
VRC_Trigger.InitializeTrigger = CyanEmuTriggerHelper.InitializeTrigger;
VRCSDK2.VRC_ObjectSync.Initialize += CyanEmuObjectSyncHelper.InitializeObjectSync;
VRCSDK2.VRC_ObjectSync.TeleportHandler += CyanEmuObjectSyncHelper.TeleportTo;
VRCSDK2.VRC_ObjectSync.RespawnHandler += CyanEmuObjectSyncHelper.RespawnObject;
VRCSDK2.VRC_ObjectSync.SetIsKinematic += CyanEmuObjectSyncHelper.SetIsKinematic;
VRCSDK2.VRC_ObjectSync.SetUseGravity += CyanEmuObjectSyncHelper.SetUseGravity;
VRCSDK2.VRC_ObjectSync.GetIsKinematic += CyanEmuObjectSyncHelper.GetIsKinematic;
VRCSDK2.VRC_ObjectSync.GetUseGravity += CyanEmuObjectSyncHelper.GetUseGravity;
VRCSDK2.VRC_PlayerMods.Initialize = CyanEmuPlayerModsHelper.InitializePlayerMods;
VRCSDK2.VRC_SyncAnimation.Initialize = CyanEmuSyncAnimationHelper.InitializationDelegate;
#endif
Networking._IsMaster = CyanEmuPlayerManager.IsLocalPlayerMaster;
Networking._LocalPlayer = CyanEmuPlayerManager.LocalPlayer;
Networking._GetOwner = CyanEmuPlayerManager.GetOwner;
Networking._IsOwner = CyanEmuPlayerManager.IsOwner;
Networking._SetOwner = CyanEmuPlayerManager.TakeOwnership;
Networking._GetUniqueName = VRC.Tools.GetGameObjectPath;
VRCPlayerApi._GetPlayerId = CyanEmuPlayerManager.GetPlayerID;
VRCPlayerApi._GetPlayerById = CyanEmuPlayerManager.GetPlayerByID;
VRCPlayerApi._isMasterDelegate = CyanEmuPlayerManager.IsMaster;
VRCPlayerApi.ClaimNetworkControl = CyanEmuPlayerManager.ClaimNetworkControl;
VRCPlayerApi._EnablePickups = CyanEmuPlayerManager.EnablePickups;
VRCPlayerApi._Immobilize = CyanEmuPlayerManager.Immobilize;
VRCPlayerApi._TeleportTo = CyanEmuPlayerManager.TeleportTo;
VRCPlayerApi._TeleportToOrientation = CyanEmuPlayerManager.TeleportToOrientation;
VRCPlayerApi._TeleportToOrientationLerp = CyanEmuPlayerManager.TeleportToOrientationLerp;
VRCPlayerApi._PlayHapticEventInHand = CyanEmuPlayerManager.PlayHapticEventInHand;
VRCPlayerApi._GetPlayerByGameObject = CyanEmuPlayerManager.GetPlayerByGameObject;
VRCPlayerApi._GetPickupInHand = CyanEmuPlayerManager.GetPickupInHand;
VRCPlayerApi._GetTrackingData = CyanEmuPlayerManager.GetTrackingData;
VRCPlayerApi._GetBoneTransform = CyanEmuPlayerManager.GetBoneTransform;
VRCPlayerApi._GetBonePosition = CyanEmuPlayerManager.GetBonePosition;
VRCPlayerApi._GetBoneRotation = CyanEmuPlayerManager.GetBoneRotation;
VRCPlayerApi._TakeOwnership = CyanEmuPlayerManager.TakeOwnership;
VRCPlayerApi._IsOwner = CyanEmuPlayerManager.IsOwner;
VRCPlayerApi._ClearPlayerTags = CyanEmuPlayerManager.ClearPlayerTag;
VRCPlayerApi._SetPlayerTag = CyanEmuPlayerManager.SetPlayerTag;
VRCPlayerApi._GetPlayerTag = CyanEmuPlayerManager.GetPlayerTag;
VRCPlayerApi._GetPlayersWithTag = CyanEmuPlayerManager.GetPlayersWithTag;
VRCPlayerApi._SetSilencedToTagged = CyanEmuPlayerManager.SetSilencedToTagged;
VRCPlayerApi._SetSilencedToUntagged = CyanEmuPlayerManager.SetSilencedToUntagged;
VRCPlayerApi._ClearSilence = CyanEmuPlayerManager.ClearSilence;
VRCPlayerApi._SetInvisibleToTagged = CyanEmuPlayerManager.SetInvisibleToTagged;
VRCPlayerApi._SetInvisibleToUntagged = CyanEmuPlayerManager.SetInvisibleToUntagged;
VRCPlayerApi._ClearInvisible = CyanEmuPlayerManager.ClearInvisible;
VRCPlayerApi._IsUserInVR = (VRCPlayerApi _) => false; // TODO one day...
VRCPlayerApi._GetRunSpeed = CyanEmuPlayerManager.GetRunSpeed;
VRCPlayerApi._SetRunSpeed = CyanEmuPlayerManager.SetRunSpeed;
VRCPlayerApi._GetWalkSpeed = CyanEmuPlayerManager.GetWalkSpeed;
VRCPlayerApi._SetWalkSpeed = CyanEmuPlayerManager.SetWalkSpeed;
VRCPlayerApi._GetJumpImpulse = CyanEmuPlayerManager.GetJumpImpulse;
VRCPlayerApi._SetJumpImpulse = CyanEmuPlayerManager.SetJumpImpulse;
VRCPlayerApi._GetStrafeSpeed = CyanEmuPlayerManager.GetStrafeSpeed;
VRCPlayerApi._SetStrafeSpeed = CyanEmuPlayerManager.SetStrafeSpeed;
VRCPlayerApi._GetVelocity = CyanEmuPlayerManager.GetVelocity;
VRCPlayerApi._SetVelocity = CyanEmuPlayerManager.SetVelocity;
VRCPlayerApi._GetPosition = CyanEmuPlayerManager.GetPosition;
VRCPlayerApi._GetRotation = CyanEmuPlayerManager.GetRotation;
VRCPlayerApi._GetGravityStrength = CyanEmuPlayerManager.GetGravityStrength;
VRCPlayerApi._SetGravityStrength = CyanEmuPlayerManager.SetGravityStrength;
VRCPlayerApi.IsGrounded = CyanEmuPlayerManager.IsGrounded;
VRCPlayerApi._UseAttachedStation = CyanEmuPlayerManager.UseAttachedStation;
VRCPlayerApi._UseLegacyLocomotion = CyanEmuPlayerManager.UseLegacyLocomotion;
VRCPlayerApi._CombatSetup = CyanEmuCombatSystemHelper.CombatSetup;
VRCPlayerApi._CombatSetMaxHitpoints = CyanEmuCombatSystemHelper.CombatSetMaxHitpoints;
VRCPlayerApi._CombatGetCurrentHitpoints = CyanEmuCombatSystemHelper.CombatGetCurrentHitpoints;
VRCPlayerApi._CombatSetRespawn = CyanEmuCombatSystemHelper.CombatSetRespawn;
VRCPlayerApi._CombatSetDamageGraphic = CyanEmuCombatSystemHelper.CombatSetDamageGraphic;
VRCPlayerApi._CombatGetDestructible = CyanEmuCombatSystemHelper.CombatGetDestructible;
VRCPlayerApi._CombatSetCurrentHitpoints = CyanEmuCombatSystemHelper.CombatSetCurrentHitpoints;
VRCPlayerApi._SetAvatarAudioVolumetricRadius = CyanEmuPlayerManager.SetAvatarAudioVolumetricRadius;
VRCPlayerApi._SetAvatarAudioNearRadius = CyanEmuPlayerManager.SetAvatarAudioNearRadius;
VRCPlayerApi._SetAvatarAudioFarRadius = CyanEmuPlayerManager.SetAvatarAudioFarRadius;
VRCPlayerApi._SetAvatarAudioGain = CyanEmuPlayerManager.SetAvatarAudioGain;
VRCPlayerApi._SetVoiceLowpass = CyanEmuPlayerManager.SetVoiceLowpass;
VRCPlayerApi._SetVoiceVolumetricRadius = CyanEmuPlayerManager.SetVoiceVolumetricRadius;
VRCPlayerApi._SetVoiceDistanceFar = CyanEmuPlayerManager.SetVoiceDistanceFar;
VRCPlayerApi._SetVoiceDistanceNear = CyanEmuPlayerManager.SetVoiceDistanceNear;
VRCPlayerApi._SetVoiceGain = CyanEmuPlayerManager.SetVoiceGain;
VRC_SpatialAudioSource.Initialize = CyanEmuSpatialAudioHelper.InitializeAudio;
// New methods added. Try not to break older sdks
// TODO figure out a better way...
// 2021-05-03
var isInstanceOwner = typeof(VRCPlayerApi).GetField("_isInstanceOwnerDelegate", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
if (isInstanceOwner != null) isInstanceOwner.SetValue(null, (Func<VRCPlayerApi, bool>)CyanEmuPlayerManager.IsInstanceOwner);
var isInstanceOwnerNetworking = typeof(Networking).GetField("_IsInstanceOwner", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
if (isInstanceOwnerNetworking != null) isInstanceOwnerNetworking.SetValue(null, (Func<bool>)CyanEmuPlayerManager.IsInstanceOwner);
}
private static void CreateInstance()
{
GameObject executor = new GameObject(CYAN_EMU_GAMEOBJECT_NAME_);
executor.tag = EDITOR_ONLY_TAG_;
instance_ = executor.AddComponent<CyanEmuMain>();
}
private static void DestroyEditorOnly()
{
if (!CyanEmuSettings.Instance.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.tag == EDITOR_ONLY_TAG_)
{
obj.Log("Deleting editor only object: " + VRC.Tools.GetGameObjectPath(obj));
DestroyImmediate(obj);
}
else
{
for (int child = 0; child < obj.transform.childCount; ++child)
{
queue.Enqueue(obj.transform.GetChild(child).gameObject);
}
}
}
}
public static bool HasInstance()
{
return instance_ != null;
}
public static bool IsNetworkReady()
{
return instance_.networkReady_;
}
private void Awake()
{
if (instance_ != null)
{
this.LogError("Already have an instance of CyanEmu!");
DestroyImmediate(gameObject);
return;
}
settings_ = CyanEmuSettings.Instance;
instance_ = this;
DontDestroyOnLoad(this);
proxyObjectParents_ = new GameObject(CYAN_EMU_GAMEOBJECT_NAME_ + "ProxyObjects").transform;
DontDestroyOnLoad(proxyObjectParents_);
CyanEmuInputModule.DisableOtherInputModules();
gameObject.AddComponent<CyanEmuBaseInput>();
gameObject.AddComponent<CyanEmuInputModule>();
descriptor_ = FindObjectOfType<VRC_SceneDescriptor>();
if (descriptor_ == null)
{
Debug.LogWarning("There is no VRC_SceneDescriptor in the scene.");
}
// SDK manager class
#if VRC_SDK_VRCSDK2
sdkManager_ = gameObject.AddComponent<CyanEmuTriggerExecutor>();
#endif
#if UDON
sdkManager_ = gameObject.AddComponent<CyanEmuUdonManager>();
#endif
StartCoroutine(OnNetworkReady());
}
private IEnumerator OnNetworkReady()
{
yield return new WaitForSeconds(0.5f);
this.Log("Sending OnNetworkReady");
sdkManager_.OnNetworkReady();
if (settings_.spawnPlayer)
{
// TODO add option to allow for spawning remote players first to have data on not being master
/*
for (int i = 0; i < x; ++i)
{
SpawnRemotePlayer();
}
*/
SpawnLocalPlayer();
}
networkReady_ = true;
yield return new WaitForSeconds(0.1f);
CyanEmuPlayerManager.OnNetworkReady();
}
public static Transform GetProxyObjectTransform()
{
return instance_.proxyObjectParents_;
}
public static void SpawnPlayer(bool local, string name = null)
{
if (local)
{
instance_?.SpawnLocalPlayer();
}
else
{
instance_?.SpawnRemotePlayer(name);
}
}
private void SpawnLocalPlayer()
{
if (descriptor_ == null)
{
Debug.LogError("Cannot spawn player if there is no world descriptor!");
return;
}
GameObject player = new GameObject("Local Player");
player.transform.parent = transform;
// Force move the player initially to the spawn point to prevent enter triggers at the origin
Transform spawn = GetSpawnPoint();
player.transform.position = spawn.position;
player.transform.rotation = Quaternion.Euler(0, spawn.rotation.eulerAngles.y, 0);
playerController_ = player.AddComponent<CyanEmuPlayerController>();
playerController_.Teleport(spawn, false);
CyanEmuPlayer playerObj = player.AddComponent<CyanEmuPlayer>();
VRCPlayerApi playerAPI = CyanEmuPlayerManager.CreateNewPlayer(true, player, settings_.customLocalPlayerName);
playerObj.SetPlayer(playerAPI);
player.name = $"[{playerAPI.playerId}] {player.name}";
}
private void SpawnRemotePlayer(string name = null)
{
if (descriptor_ == null)
{
Debug.LogError("Cannot spawn player if there is no world descriptor!");
return;
}
GameObject player = new GameObject("Remote Player");
player.transform.parent = transform;
player.layer = LayerMask.NameToLayer("Player");
// TODO do this better
Transform spawn = GetSpawnPoint(true);
player.transform.position = spawn.position;
player.transform.rotation = Quaternion.Euler(0, spawn.rotation.eulerAngles.y, 0);
GameObject playerVis = GameObject.CreatePrimitive(PrimitiveType.Sphere);
playerVis.layer = player.layer;
playerVis.transform.SetParent(player.transform, false);
CyanEmuPlayer playerObj = player.AddComponent<CyanEmuPlayer>();
VRCPlayerApi playerAPI = CyanEmuPlayerManager.CreateNewPlayer(false, player, name);
playerObj.SetPlayer(playerAPI);
player.name = $"[{playerAPI.playerId}] {player.name}";
Rigidbody rigidbody = player.AddComponent<Rigidbody>();
rigidbody.isKinematic = true;
}
public static Transform GetNextSpawnPoint()
{
if (instance_ != null)
{
return instance_.GetSpawnPoint();
}
return null;
}
private Transform GetSpawnPoint(bool remote = false)
{
if (descriptor_.spawns.Length == 0 || descriptor_.spawns[0] == null)
{
throw new Exception("[CyanEmuMain] Cannot spawn player when descriptor does not have a spawn set!");
}
// Remote players always restart the list, so for now, only first spawn
if (descriptor_.spawnOrder == VRC_SceneDescriptor.SpawnOrder.First ||
descriptor_.spawnOrder == VRC_SceneDescriptor.SpawnOrder.Demo ||
remote)
{
return descriptor_.spawns[0];
}
if (descriptor_.spawnOrder == VRC_SceneDescriptor.SpawnOrder.Random)
{
int spawn = Random.Range(0, descriptor_.spawns.Length);
return descriptor_.spawns[spawn];
}
if (descriptor_.spawnOrder == VRC_SceneDescriptor.SpawnOrder.Sequential)
{
Transform spawn = descriptor_.spawns[spawnOrder_];
spawnOrder_ = (spawnOrder_ + 1) % descriptor_.spawns.Length;
return spawn;
}
// Fallback to first spawn point
return descriptor_.spawns[0];
}
public static void RemovePlayer(VRCPlayerApi player)
{
CyanEmuPlayerManager.RemovePlayer(player);
Destroy(player.gameObject);
}
public static void PlayerJoined(VRCPlayerApi player)
{
instance_?.OnPlayerJoined(player);
}
private void OnPlayerJoined(VRCPlayerApi player)
{
sdkManager_.OnPlayerJoined(player);
}
public static void PlayerLeft(VRCPlayerApi player)
{
instance_?.OnPlayerLeft(player);
}
private void OnPlayerLeft(VRCPlayerApi player)
{
int masterID = CyanEmuPlayerManager.GetMasterID();
VRCPlayerApi masterPlayer = VRCPlayerApi.GetPlayerById(masterID);
foreach (CyanEmuSyncedObjectHelper sync in allSyncedObjects_)
{
if (sync == null)
{
continue;
}
GameObject syncObj = sync.gameObject;
if (Networking.GetOwner(syncObj)?.playerId == player.playerId)
{
Networking.SetOwner(masterPlayer, syncObj);
}
}
sdkManager_.OnPlayerLeft(player);
}
public static void PlayerRespawned(VRCPlayerApi player)
{
instance_?.OnPlayerRespawn(player);
}
private void OnPlayerRespawn(VRCPlayerApi player)
{
sdkManager_.OnPlayerRespawn(player);
}
public static GameObject SpawnObject(GameObject prefab)
{
return SpawnObject(prefab, Vector3.zero, Quaternion.identity);
}
public static GameObject SpawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
GameObject spawnedObject = Instantiate(prefab, position, rotation) as GameObject;
spawnedObject.name = prefab.name + " (Dynamic Clone " + instance_.spawnedObjectCount_ + ")";
spawnedObject.SetActive(true);
spawnedObject.AddComponent<CyanEmuSpawnHelper>();
++instance_.spawnedObjectCount_;
instance_.OnSpawnedObject(spawnedObject);
return spawnedObject;
}
private void OnSpawnedObject(GameObject spawnedObject)
{
sdkManager_.OnSpawnedObject(spawnedObject);
}
private void LateUpdate()
{
if (descriptor_ == null)
{
return;
}
ProcessAddedAndRemovedSyncedObjects();
ProcessSyncedObjectsBelowRespawn();
}
private void ProcessSyncedObjectsBelowRespawn()
{
if (playerController_ != null && playerController_.transform.position.y < descriptor_.RespawnHeightY)
{
playerController_.Teleport(descriptor_.spawns[0], false);
}
// TODO space this out so that there are only x number per frame instead of all every time?
List<GameObject> objsToDestroy = new List<GameObject>();
foreach (CyanEmuSyncedObjectHelper sync in allSyncedObjects_)
{
if (sync == null)
{
shouldVerifySyncedObjectList_ = true;
Debug.LogWarning("Null Synced Object!");
continue;
}
if (!sync.SyncPosition)
{
continue;
}
if (sync.transform.position.y < descriptor_.RespawnHeightY)
{
if (descriptor_.ObjectBehaviourAtRespawnHeight == VRC_SceneDescriptor.RespawnHeightBehaviour.Respawn)
{
sync.Respawn();
}
else
{
objsToDestroy.Add(sync.gameObject);
}
}
}
foreach (var obj in objsToDestroy)
{
Destroy(obj);
}
}
public static void AddSyncedObject(CyanEmuSyncedObjectHelper sync)
{
if (instance_ == null || sync == null)
{
return;
}
instance_.QueueAddSyncedObject(sync);
}
public static void RemoveSyncedObject(CyanEmuSyncedObjectHelper sync)
{
if (instance_ == null)
{
return;
}
instance_.QueueRemoveSyncedObject(sync);
}
private void QueueAddSyncedObject(CyanEmuSyncedObjectHelper syncedObject)
{
if (syncedObject == null)
{
return;
}
toBeAddedSync_.Enqueue(syncedObject);
}
private void QueueRemoveSyncedObject(CyanEmuSyncedObjectHelper syncedObject)
{
shouldVerifySyncedObjectList_ = true;
toBeRemovedSync_.Enqueue(syncedObject);
}
private void ProcessAddedAndRemovedSyncedObjects()
{
if (toBeAddedSync_.Count > 0)
{
foreach (var sync in toBeAddedSync_)
{
if (sync == null)
{
shouldVerifySyncedObjectList_ = true;
continue;
}
allSyncedObjects_.Add(sync);
}
toBeAddedSync_.Clear();
}
if (toBeRemovedSync_.Count > 0)
{
foreach (var udon in toBeRemovedSync_)
{
if (udon == null)
{
shouldVerifySyncedObjectList_ = true;
continue;
}
allSyncedObjects_.Remove(udon);
}
toBeRemovedSync_.Clear();
}
if (shouldVerifySyncedObjectList_)
{
HashSet<CyanEmuSyncedObjectHelper> allSyncs = new HashSet<CyanEmuSyncedObjectHelper>();
foreach (var sync in allSyncedObjects_)
{
if (sync == null)
{
continue;
}
allSyncs.Add(sync);
}
allSyncedObjects_ = allSyncs;
}
}
}
}