213 lines
7.6 KiB
C#
213 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using VRC.SDK3.Components;
|
|
using VRC.SDKBase;
|
|
|
|
namespace VRC.SDK3.ClientSim
|
|
{
|
|
/// <summary>
|
|
/// System that keeps track of all synced objects. Handles changing ownership when players leave
|
|
/// and also checks for if position synced objects fall below the respawn height.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Listens to Events:
|
|
/// - ClientSimOnPlayerLeftEvent
|
|
/// </remarks>
|
|
[AddComponentMenu("")]
|
|
public class ClientSimSyncedObjectManager : ClientSimBehaviour, IClientSimSyncedObjectManager, IDisposable
|
|
{
|
|
// Collection of all synced objects initialized in the scene.
|
|
private readonly ClientSimObjectCollection<IClientSimSyncable> _syncedObjects =
|
|
new ClientSimObjectCollection<IClientSimSyncable>();
|
|
|
|
// Collection of all position synced objects initialized in the scene.
|
|
private readonly ClientSimObjectCollection<IClientSimPositionSyncable> _positionSyncedObjects =
|
|
new ClientSimObjectCollection<IClientSimPositionSyncable>();
|
|
|
|
// Collection of all synced playerObjects initialized in the scene.
|
|
private readonly ClientSimObjectCollection<IClientSimSyncable> _playerObjects =
|
|
new ClientSimObjectCollection<IClientSimSyncable>();
|
|
|
|
// TODO add generic system for saving synced data and restoring it to simulate late joining into a world.
|
|
// TODO add system to manage udon synced data
|
|
|
|
private IClientSimEventDispatcher _eventDispatcher;
|
|
private IClientSimSceneManager _sceneManager;
|
|
private IClientSimPlayerManager _playerManager;
|
|
|
|
public void Initialize(
|
|
IClientSimEventDispatcher eventDispatcher,
|
|
IClientSimSceneManager sceneManager,
|
|
IClientSimPlayerManager playerManager)
|
|
{
|
|
_eventDispatcher = eventDispatcher;
|
|
_sceneManager = sceneManager;
|
|
_playerManager = playerManager;
|
|
|
|
_eventDispatcher.Subscribe<ClientSimOnPlayerLeftEvent>(OnPlayerLeft);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
Dispose();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_eventDispatcher?.Unsubscribe<ClientSimOnPlayerLeftEvent>(OnPlayerLeft);
|
|
}
|
|
|
|
public void InitializeObjectSync(VRCObjectSync sync)
|
|
{
|
|
// Only allow one object sync helper per object.
|
|
if (sync.TryGetComponent(out ClientSimObjectSyncHelper syncHelper))
|
|
{
|
|
syncHelper.Initialize(sync,this);
|
|
}
|
|
else
|
|
{
|
|
ClientSimObjectSyncHelper helper = sync.gameObject.AddComponent<ClientSimObjectSyncHelper>();
|
|
helper.Initialize(sync,this);
|
|
}
|
|
}
|
|
|
|
public void InitializeObjectPool(VRCObjectPool objectPool)
|
|
{
|
|
objectPool.gameObject.AddComponent<ClientSimObjectPoolHelper>().Initialize(objectPool, this);
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
ProcessPositionSyncedObjects();
|
|
}
|
|
|
|
private void ProcessPositionSyncedObjects()
|
|
{
|
|
_positionSyncedObjects.ProcessAddedAndRemovedObjects();
|
|
|
|
// 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 (IClientSimPositionSyncable sync in _positionSyncedObjects.GetObjects())
|
|
{
|
|
if (sync == null)
|
|
{
|
|
_positionSyncedObjects.ShouldVerifyObjects();
|
|
continue;
|
|
}
|
|
|
|
if (!sync.SyncPosition)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// VRChatBug: The following method will enforce users to use VRCObjectSync's methods for setting
|
|
// useGravity and isKinematic, but the current SDK does not support the hook to allow this.
|
|
// Verify Sync properties, eg check if useGravity and isKinematic is properly set.
|
|
// sync.UpdatePositionSync();
|
|
|
|
// Verify if the object is below respawn.
|
|
Transform syncTransform = sync.GetTransform();
|
|
if (syncTransform.position.y < _sceneManager.GetRespawnHeight())
|
|
{
|
|
if (_sceneManager.ShouldObjectsDestroyAtRespawnHeight())
|
|
{
|
|
objsToDestroy.Add(syncTransform.gameObject);
|
|
}
|
|
else
|
|
{
|
|
sync.Respawn();
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var obj in objsToDestroy)
|
|
{
|
|
Destroy(obj);
|
|
}
|
|
}
|
|
|
|
#region ClientSim Events
|
|
|
|
// Handle updating object ownership for all objects the leaving player previously owned.
|
|
private void OnPlayerLeft(ClientSimOnPlayerLeftEvent leftEvent)
|
|
{
|
|
VRCPlayerApi leftPlayer = leftEvent.player;
|
|
int leftPlayerId = leftPlayer.playerId;
|
|
VRCPlayerApi masterPlayer = _playerManager.GetMaster();
|
|
if (masterPlayer == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_syncedObjects.ProcessAddedAndRemovedObjects();
|
|
foreach (IClientSimSyncable sync in _syncedObjects.GetObjects())
|
|
{
|
|
if (sync == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (sync is Component syncComp)
|
|
{
|
|
if(syncComp == null) continue;
|
|
|
|
GameObject syncObj = syncComp.gameObject;
|
|
if (Networking.GetOwner(syncObj)?.playerId == leftPlayerId)
|
|
{
|
|
Networking.SetOwner(masterPlayer, syncObj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sync.GetOwner() == leftPlayerId)
|
|
{
|
|
sync.SetOwner(masterPlayer.playerId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IClientSimSyncedObjectManager
|
|
|
|
public void AddSyncedObject(IClientSimSyncable sync)
|
|
{
|
|
_syncedObjects.AddObject(sync);
|
|
_syncedObjects.ProcessAddedAndRemovedObjects();
|
|
|
|
if (sync is IClientSimPositionSyncable posSync && posSync.SyncPosition)
|
|
{
|
|
_positionSyncedObjects.AddObject(posSync);
|
|
_positionSyncedObjects.ProcessAddedAndRemovedObjects();
|
|
}
|
|
|
|
if((sync as MonoBehaviour).GetComponentInParent<VRCPlayerObject>() != null)
|
|
{
|
|
_playerObjects.AddObject(sync);
|
|
_playerObjects.ProcessAddedAndRemovedObjects();
|
|
}
|
|
}
|
|
|
|
public void RemoveSyncedObject(IClientSimSyncable sync)
|
|
{
|
|
_syncedObjects.RemoveObject(sync);
|
|
_syncedObjects.ProcessAddedAndRemovedObjects();
|
|
|
|
if (sync is IClientSimPositionSyncable posSync && posSync.SyncPosition)
|
|
{
|
|
_positionSyncedObjects.RemoveObject(posSync);
|
|
_positionSyncedObjects.ProcessAddedAndRemovedObjects();
|
|
}
|
|
|
|
if((sync as MonoBehaviour).GetComponentInParent<VRCPlayerObject>() != null)
|
|
{
|
|
_playerObjects.RemoveObject(sync);
|
|
_playerObjects.ProcessAddedAndRemovedObjects();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |