Files
BlockSpace-v1.5.0b/Assets/Scripts/Assembly-CSharp/BlockSpace/Voxels/PhotonVoxelAvatar.cs
T
2026-06-01 17:19:55 +02:00

415 lines
11 KiB
C#

using System.Collections;
using BlockSpace.Account;
using BlockSpace.Worlds;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
namespace BlockSpace.Voxels
{
[DisallowMultipleComponent]
public sealed class PhotonVoxelAvatar : MonoBehaviourPunCallbacks
{
private const string HeadAnchorName = "Avatar Group - Head";
private const string LeftAnchorName = "Avatar Group - Left";
private const string RightAnchorName = "Avatar Group - Right";
private const float AvatarVoxelSizeScale = 0.5f;
private const string InventoryBackendBaseUrl = "https://blockspacebackend-production.up.railway.app";
[SerializeField]
private Transform headTransform;
[SerializeField]
private Transform leftHandTransform;
[SerializeField]
private Transform rightHandTransform;
[SerializeField]
private Transform headBodyAnchor;
[SerializeField]
private Transform leftAvatarAnchor;
[SerializeField]
private Transform rightAvatarAnchor;
private VoxelGroupManager groupManager;
private VoxelGroup headBodyGroup;
private VoxelGroup leftGroup;
private VoxelGroup rightGroup;
private bool runtimeGroupsReady;
private bool localAvatarLoaded;
private bool roomSyncScheduled;
private string pendingAvatarPayloadJson;
private Coroutine roomSyncCoroutine;
private Coroutine localAvatarLoadCoroutine;
public static PhotonVoxelAvatar LocalOwnedInstance { get; private set; }
public bool HasRuntimeGroups => runtimeGroupsReady;
public void Configure(Transform head, Transform leftHand, Transform rightHand)
{
if (head != null)
{
headTransform = head;
}
if (leftHand != null)
{
leftHandTransform = leftHand;
}
if (rightHand != null)
{
rightHandTransform = rightHand;
}
ResolveAnchors();
TryInitializeRuntimeGroups();
}
public void ApplyAndBroadcastAvatar(AvatarVoxelPayload payload)
{
ApplyAvatarPayload(payload, saveLocally: true, PhotonNetwork.InRoom);
}
public void ApplyAvatarPayload(AvatarVoxelPayload payload, bool saveLocally, bool broadcastToOthers)
{
if (!TryInitializeRuntimeGroups())
{
pendingAvatarPayloadJson = AvatarVoxelStorage.Serialize(payload);
if (saveLocally)
{
AvatarVoxelStorage.SaveLocal(payload);
}
return;
}
AvatarVoxelPayload payload2 = payload ?? AvatarVoxelStorage.CreateEmptyPayload();
AvatarVoxelStorage.ApplyPayload(payload2, headBodyGroup, leftGroup, rightGroup, groupManager, useLocalGridTransform: true);
if (base.photonView.IsMine)
{
localAvatarLoaded = true;
}
if (saveLocally && base.photonView.IsMine)
{
AvatarVoxelStorage.SaveLocal(payload2);
}
if (broadcastToOthers && base.photonView.IsMine && PhotonNetwork.InRoom)
{
SendAvatarDataToOthers(payload2);
}
}
public AvatarVoxelPayload CaptureRuntimePayload()
{
return AvatarVoxelStorage.CapturePayload(headBodyGroup, leftGroup, rightGroup);
}
private void Awake()
{
if (base.photonView.IsMine)
{
LocalOwnedInstance = this;
}
ResolveAnchors();
}
private new void OnEnable()
{
if (base.photonView.IsMine)
{
LocalOwnedInstance = this;
}
roomSyncScheduled = false;
TryInitializeRuntimeGroups();
WorldLoadingScreenController.LoadingFinished += OnWorldLoadingFinished;
TryScheduleRoomSync();
}
private new void OnDisable()
{
WorldLoadingScreenController.LoadingFinished -= OnWorldLoadingFinished;
if (roomSyncCoroutine != null)
{
StopCoroutine(roomSyncCoroutine);
roomSyncCoroutine = null;
}
if (localAvatarLoadCoroutine != null)
{
StopCoroutine(localAvatarLoadCoroutine);
localAvatarLoadCoroutine = null;
}
}
private void OnDestroy()
{
if (LocalOwnedInstance == this)
{
LocalOwnedInstance = null;
}
}
private void Update()
{
if (!runtimeGroupsReady)
{
TryInitializeRuntimeGroups();
}
if (runtimeGroupsReady && !string.IsNullOrEmpty(pendingAvatarPayloadJson))
{
ApplyPendingAvatarPayload();
}
if (base.photonView.IsMine && runtimeGroupsReady && !localAvatarLoaded && !IsWaitingForLoadingScreen())
{
ApplyLocalAvatarAfterLoading();
}
if (base.photonView.IsMine)
{
TryScheduleRoomSync();
}
}
public override void OnJoinedRoom()
{
roomSyncScheduled = false;
TryScheduleRoomSync();
}
public override void OnLeftRoom()
{
roomSyncScheduled = false;
}
[PunRPC]
private void RPC_SendAvatarData(string payloadJson)
{
if (TryInitializeRuntimeGroups())
{
ApplySerializedAvatarPayload(payloadJson);
}
else
{
pendingAvatarPayloadJson = payloadJson;
}
}
[PunRPC]
private void RPC_RequestAvatarData(int requestingActorNumber)
{
if (!PhotonNetwork.InRoom)
{
return;
}
PhotonVoxelAvatar localOwnedInstance = LocalOwnedInstance;
if (!(localOwnedInstance == null) && localOwnedInstance.photonView.IsMine)
{
Player player = PhotonNetwork.CurrentRoom?.GetPlayer(requestingActorNumber);
if (player != null && player != PhotonNetwork.LocalPlayer)
{
localOwnedInstance.SendAvatarDataToPlayer(player, localOwnedInstance.CaptureRuntimePayload());
}
}
}
private bool TryInitializeRuntimeGroups()
{
if (runtimeGroupsReady)
{
return true;
}
if (groupManager == null)
{
groupManager = ((VoxelGroupManager.Instance != null) ? VoxelGroupManager.Instance : Object.FindObjectOfType<VoxelGroupManager>());
}
ResolveAnchors();
if (groupManager == null || headBodyAnchor == null || leftAvatarAnchor == null || rightAvatarAnchor == null)
{
return false;
}
headBodyGroup = PrepareRuntimeGroup(headBodyAnchor.gameObject, headBodyGroup, GetGroupIdBase());
leftGroup = PrepareRuntimeGroup(leftAvatarAnchor.gameObject, leftGroup, (ushort)(GetGroupIdBase() + 1));
rightGroup = PrepareRuntimeGroup(rightAvatarAnchor.gameObject, rightGroup, (ushort)(GetGroupIdBase() + 2));
runtimeGroupsReady = headBodyGroup != null && leftGroup != null && rightGroup != null;
return runtimeGroupsReady;
}
private VoxelGroup PrepareRuntimeGroup(GameObject groupObject, VoxelGroup existingGroup, ushort groupId)
{
if (groupObject == null || groupManager == null)
{
return null;
}
VoxelGroup voxelGroup = ((existingGroup != null) ? existingGroup : groupObject.GetComponent<VoxelGroup>());
if (voxelGroup == null)
{
voxelGroup = groupObject.AddComponent<VoxelGroup>();
}
AvatarVoxelStorage.ApplyGroupData(voxelGroup, AvatarVoxelStorage.CaptureGroup(voxelGroup), groupManager, useLocalGridTransform: true);
voxelGroup.SetVoxelSizeScale(0.5f);
voxelGroup.id = groupId;
return voxelGroup;
}
private void ResolveAnchors()
{
if (headBodyAnchor == null && headTransform != null)
{
headBodyAnchor = headTransform.Find("Avatar Group - Head");
}
if (leftAvatarAnchor == null && leftHandTransform != null)
{
leftAvatarAnchor = leftHandTransform.Find("Avatar Group - Left");
}
if (rightAvatarAnchor == null && rightHandTransform != null)
{
rightAvatarAnchor = rightHandTransform.Find("Avatar Group - Right");
}
}
private void TryScheduleRoomSync()
{
if (base.photonView.IsMine && PhotonNetwork.InRoom && runtimeGroupsReady && localAvatarLoaded && !roomSyncScheduled && base.photonView.ViewID != 0)
{
roomSyncScheduled = true;
if (roomSyncCoroutine != null)
{
StopCoroutine(roomSyncCoroutine);
}
roomSyncCoroutine = StartCoroutine(SendRoomAvatarSync());
}
}
private void OnWorldLoadingFinished()
{
if (base.photonView.IsMine)
{
TryInitializeRuntimeGroups();
if (runtimeGroupsReady)
{
ApplyLocalAvatarAfterLoading();
}
}
}
private bool IsWaitingForLoadingScreen()
{
WorldLoadingScreenController instance = WorldLoadingScreenController.Instance;
if (instance != null)
{
return instance.IsLoading;
}
return false;
}
private void ApplyLocalAvatarAfterLoading()
{
if (localAvatarLoadCoroutine == null)
{
localAvatarLoadCoroutine = StartCoroutine(ApplyLocalAvatarAfterLoadingRoutine());
}
}
private IEnumerator ApplyLocalAvatarAfterLoadingRoutine()
{
AvatarVoxelPayload payload = null;
bool saveLoadedPayload = false;
string playerId = BlockSpaceAccountPrefs.GetUserFolderId();
if (!string.IsNullOrEmpty(playerId))
{
InventoryBackendClient client = new InventoryBackendClient("https://blockspacebackend-production.up.railway.app");
InventoryBackendClient.ListResponse listResponse = null;
yield return client.ListAvatars(playerId, delegate(InventoryBackendClient.ListResponse response)
{
listResponse = response;
});
string text = ((listResponse != null) ? listResponse.equippedId : string.Empty);
if (!string.IsNullOrEmpty(text))
{
string json = string.Empty;
yield return client.DownloadAvatar(playerId, text, delegate(string text2)
{
json = text2;
});
if (!string.IsNullOrWhiteSpace(json) && AvatarVoxelStorage.TryDeserialize(json, out var payload2))
{
payload = payload2;
saveLoadedPayload = true;
}
}
}
if (payload == null)
{
payload = AvatarVoxelStorage.LoadLocalOrDefault();
}
ApplyAvatarPayload(payload, saveLoadedPayload, broadcastToOthers: false);
localAvatarLoaded = true;
localAvatarLoadCoroutine = null;
TryScheduleRoomSync();
}
private IEnumerator SendRoomAvatarSync()
{
yield return null;
if (!base.photonView.IsMine || !PhotonNetwork.InRoom)
{
roomSyncCoroutine = null;
yield break;
}
AvatarVoxelPayload payload = CaptureRuntimePayload();
SendAvatarDataToOthers(payload);
base.photonView.RPC("RPC_RequestAvatarData", RpcTarget.Others, PhotonNetwork.LocalPlayer.ActorNumber);
roomSyncCoroutine = null;
}
private void SendAvatarDataToOthers(AvatarVoxelPayload payload)
{
if (PhotonNetwork.InRoom && base.photonView.ViewID != 0)
{
base.photonView.RPC("RPC_SendAvatarData", RpcTarget.Others, AvatarVoxelStorage.Serialize(payload));
}
}
private void SendAvatarDataToPlayer(Player targetPlayer, AvatarVoxelPayload payload)
{
if (targetPlayer != null && PhotonNetwork.InRoom && base.photonView.ViewID != 0)
{
base.photonView.RPC("RPC_SendAvatarData", targetPlayer, AvatarVoxelStorage.Serialize(payload));
}
}
private void ApplySerializedAvatarPayload(string payloadJson)
{
if (!AvatarVoxelStorage.TryDeserialize(payloadJson, out var payload))
{
payload = AvatarVoxelStorage.CreateEmptyPayload();
}
ApplyAvatarPayload(payload, saveLocally: false, broadcastToOthers: false);
}
private void ApplyPendingAvatarPayload()
{
string payloadJson = pendingAvatarPayloadJson;
pendingAvatarPayloadJson = null;
ApplySerializedAvatarPayload(payloadJson);
}
private ushort GetGroupIdBase()
{
int num = ((base.photonView != null && base.photonView.Owner != null) ? base.photonView.Owner.ActorNumber : 0);
return (ushort)Mathf.Clamp(60000 + num * 8, 60000, 65520);
}
}
}