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

1161 lines
36 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using BlockSpace.Account;
using BlockSpace.Worlds;
using UnityEngine;
namespace BlockSpace.Voxels
{
[DisallowMultipleComponent]
public sealed class InventoryController : MonoBehaviour
{
private enum InventoryMode
{
Avatars = 0,
Creations = 1
}
private const float ButtonPressCooldown = 0.2f;
private const float ButtonShrinkDuration = 0.08f;
private const float ButtonSettleDuration = 0.12f;
private const float ButtonPressedScale = 0.84f;
private const float ButtonOvershootScale = 1.04f;
private const string ButtonThockResourcePath = "Thock";
private const float AvatarVoxelSizeScale = 0.5f;
private const float CreationPreviewMaxSize = 3f;
[SerializeField]
private VoxelGroupManager groupManager;
[SerializeField]
private VRVoxelBrush voxelBrush;
[SerializeField]
private string backendBaseUrl = "https://blockspacebackend-production.up.railway.app";
[Header("Buttons")]
[SerializeField]
private Collider avatarsButton;
[SerializeField]
private Collider creationsButton;
[SerializeField]
private Collider backButton;
[SerializeField]
private Collider nextButton;
[SerializeField]
private Collider saveButton;
[SerializeField]
private Collider loadButton;
[Header("Previews")]
[SerializeField]
private Transform creationPreviewRoot;
[SerializeField]
private Transform previewHead;
[SerializeField]
private Transform previewLeft;
[SerializeField]
private Transform previewRight;
[SerializeField]
private AudioClip buttonThockClip;
[SerializeField]
private bool debugLogs;
private readonly Dictionary<Transform, Vector3> buttonBaseScales = new Dictionary<Transform, Vector3>();
private readonly Dictionary<Transform, Coroutine> buttonAnimationCoroutines = new Dictionary<Transform, Coroutine>();
private readonly Dictionary<string, Transform> transformCache = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
private InventoryMode mode;
private readonly List<string> ids = new List<string>();
private int slotIndex;
private string equippedAvatarId = string.Empty;
private bool slotsDirty = true;
private float nextButtonPressTime;
private bool avatarsInsidePrev;
private bool creationsInsidePrev;
private bool backInsidePrev;
private bool nextInsidePrev;
private bool saveInsidePrev;
private bool loadInsidePrev;
private VoxelGroup previewCreationGroup;
private readonly List<VoxelGroup> previewCreationGroups = new List<VoxelGroup>();
private VoxelGroup previewHeadGroup;
private VoxelGroup previewLeftGroup;
private VoxelGroup previewRightGroup;
private bool warnedMissingAvatarPreviewRoots;
private bool warnedMissingCreationPreviewRoot;
private InventoryBackendClient client;
private int previewRequestToken;
public void Configure(VoxelGroupManager manager, VRVoxelBrush brush)
{
if (manager != null)
{
groupManager = manager;
}
if (brush != null)
{
voxelBrush = brush;
}
ResolveSceneReferences();
EnsureButtonAudioAssigned();
EnsurePreviewGroups();
client = new InventoryBackendClient(backendBaseUrl);
slotsDirty = true;
}
private void Awake()
{
ResolveSceneReferences();
EnsureButtonAudioAssigned();
EnsurePreviewGroups();
client = new InventoryBackendClient(backendBaseUrl);
}
private void OnDisable()
{
StopAllCoroutines();
ResetAllButtonScales();
}
private void Update()
{
ResolveSceneReferences();
EnsurePreviewGroups();
if (!(groupManager == null))
{
HandleButton(avatarsButton, ref avatarsInsidePrev, delegate
{
SwitchMode(InventoryMode.Avatars);
});
HandleButton(creationsButton, ref creationsInsidePrev, delegate
{
SwitchMode(InventoryMode.Creations);
});
HandleButton(backButton, ref backInsidePrev, PrevSlot);
HandleButton(nextButton, ref nextInsidePrev, NextSlot);
HandleButton(saveButton, ref saveInsidePrev, SaveCurrentSlot);
HandleButton(loadButton, ref loadInsidePrev, LoadCurrentSlot);
if (slotsDirty)
{
slotsDirty = false;
StartCoroutine(RefreshSlots());
}
}
}
private void SwitchMode(InventoryMode newMode)
{
if (mode != newMode)
{
mode = newMode;
slotIndex = 0;
if (mode == InventoryMode.Avatars)
{
ApplyEmptyCreationPreview();
}
else
{
ApplyEmptyAvatarPreview();
}
slotsDirty = true;
}
}
private void PrevSlot()
{
if (ids.Count <= 0)
{
slotIndex = 0;
return;
}
slotIndex = (slotIndex - 1 + ids.Count) % ids.Count;
UpdatePreviewsForSelectedSlot();
}
private void NextSlot()
{
if (ids.Count <= 0)
{
slotIndex = 0;
return;
}
slotIndex = (slotIndex + 1) % ids.Count;
UpdatePreviewsForSelectedSlot();
}
private void SaveCurrentSlot()
{
string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId();
if (string.IsNullOrEmpty(userFolderId) || client == null || (mode == InventoryMode.Creations && (groupManager == null || !groupManager.CreatorMode || groupManager.EditingLocked)))
{
return;
}
string orCreateSelectedId = GetOrCreateSelectedId();
if (!string.IsNullOrEmpty(orCreateSelectedId))
{
if (mode == InventoryMode.Avatars)
{
StartCoroutine(SaveAvatarSlot(userFolderId, orCreateSelectedId));
}
else
{
StartCoroutine(SaveCreationSlot(userFolderId, orCreateSelectedId));
}
}
}
private void LoadCurrentSlot()
{
string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId();
if (!string.IsNullOrEmpty(userFolderId) && client != null && (mode != InventoryMode.Creations || (!(groupManager == null) && groupManager.CreatorMode && !groupManager.EditingLocked)) && TryGetSelectedId(out var id) && !string.IsNullOrEmpty(id))
{
if (mode == InventoryMode.Avatars)
{
StartCoroutine(LoadAndEquipAvatar(userFolderId, id));
}
else
{
StartCoroutine(LoadCreationIntoWorld(userFolderId, id));
}
}
}
private IEnumerator RefreshSlots()
{
string playerId = BlockSpaceAccountPrefs.GetUserFolderId();
if (string.IsNullOrEmpty(playerId) || client == null)
{
ids.Clear();
yield break;
}
InventoryBackendClient.ListResponse response = null;
if (mode == InventoryMode.Avatars)
{
yield return client.ListAvatars(playerId, delegate(InventoryBackendClient.ListResponse r)
{
response = r;
});
}
else
{
yield return client.ListCreations(playerId, delegate(InventoryBackendClient.ListResponse r)
{
response = r;
});
}
ids.Clear();
if (response != null && response.ok && response.ids != null)
{
ids.AddRange(response.ids);
}
ids.Add(string.Empty);
equippedAvatarId = ((response != null) ? (response.equippedId ?? string.Empty) : string.Empty);
if (debugLogs)
{
Debug.Log($"InventoryController: mode={mode} playerIdLen={playerId.Length} ids={ids.Count} equipped={equippedAvatarId}");
}
slotIndex = Mathf.Clamp(slotIndex, 0, Mathf.Max(0, ids.Count - 1));
UpdatePreviewsForSelectedSlot();
if (mode != InventoryMode.Avatars || !BlockSpaceAccountPrefs.IsLinked)
{
yield break;
}
bool flag = false;
for (int num = 0; num < ids.Count; num++)
{
if (!string.IsNullOrEmpty(ids[num]))
{
flag = true;
break;
}
}
if (flag || !AvatarVoxelStorage.TryLoadLocal(out var localPayload) || !AvatarPayloadHasVoxels(localPayload))
{
yield break;
}
string newId = MakeNewId();
InventoryBackendClient.PutResponse put = null;
yield return client.UploadAvatar(playerId, newId, AvatarVoxelStorage.Serialize(localPayload), delegate(InventoryBackendClient.PutResponse r)
{
put = r;
});
if (put != null && put.ok && put.saved)
{
InventoryBackendClient.EquipResponse equip = null;
yield return client.EquipAvatar(playerId, newId, delegate(InventoryBackendClient.EquipResponse r)
{
equip = r;
});
PhotonVoxelAvatar localOwnedInstance = PhotonVoxelAvatar.LocalOwnedInstance;
if (localOwnedInstance != null)
{
localOwnedInstance.ApplyAndBroadcastAvatar(localPayload);
}
if (PlayerPrefs.HasKey("BlockSpace.Avatar.Payload"))
{
PlayerPrefs.DeleteKey("BlockSpace.Avatar.Payload");
PlayerPrefs.Save();
}
slotsDirty = true;
}
}
private static bool AvatarPayloadHasVoxels(AvatarVoxelPayload payload)
{
if (payload == null)
{
return false;
}
if (!GroupDataHasVoxels(payload.headBody) && !GroupDataHasVoxels(payload.left))
{
return GroupDataHasVoxels(payload.right);
}
return true;
}
private static bool AvatarPayloadHasAnyBase64(AvatarVoxelPayload payload)
{
if (payload == null)
{
return false;
}
if (string.IsNullOrEmpty(payload.headBody?.voxelsBase64) && string.IsNullOrEmpty(payload.left?.voxelsBase64))
{
return !string.IsNullOrEmpty(payload.right?.voxelsBase64);
}
return true;
}
private static int AvatarPayloadBase64TotalLength(AvatarVoxelPayload payload)
{
if (payload == null)
{
return 0;
}
return (payload.headBody?.voxelsBase64 ?? string.Empty).Length + (payload.left?.voxelsBase64 ?? string.Empty).Length + (payload.right?.voxelsBase64 ?? string.Empty).Length;
}
private static bool GroupDataHasVoxels(AvatarVoxelGroupData group)
{
if (group == null)
{
return false;
}
if (group.sizeX <= 0 || group.sizeY <= 0 || group.sizeZ <= 0)
{
return false;
}
return true;
}
private IEnumerator SaveAvatarSlot(string playerId, string avatarId)
{
AvatarCustomizerController avatarCustomizerController = UnityEngine.Object.FindObjectOfType<AvatarCustomizerController>();
PhotonVoxelAvatar runtime = PhotonVoxelAvatar.LocalOwnedInstance;
AvatarVoxelPayload editorPayload = null;
VoxelGroup headBody = null;
VoxelGroup left = null;
VoxelGroup right = null;
if (avatarCustomizerController != null && avatarCustomizerController.TryGetEditorGroups(out headBody, out left, out right))
{
editorPayload = AvatarVoxelStorage.CapturePayload(headBody, left, right);
}
AvatarVoxelPayload avatarVoxelPayload = ((runtime != null) ? runtime.CaptureRuntimePayload() : null);
AvatarVoxelPayload payload;
AvatarVoxelPayload localPayload = (AvatarVoxelStorage.TryLoadLocal(out payload) ? payload : null);
AvatarVoxelPayload payload2 = (AvatarPayloadHasAnyBase64(editorPayload) ? editorPayload : (AvatarPayloadHasAnyBase64(avatarVoxelPayload) ? avatarVoxelPayload : (AvatarPayloadHasAnyBase64(localPayload) ? localPayload : (AvatarPayloadHasVoxels(editorPayload) ? editorPayload : (AvatarPayloadHasVoxels(avatarVoxelPayload) ? avatarVoxelPayload : (AvatarPayloadHasVoxels(localPayload) ? localPayload : (editorPayload ?? avatarVoxelPayload ?? localPayload ?? AvatarVoxelStorage.CreateEmptyPayload())))))));
if (runtime != null && AvatarPayloadBase64TotalLength(payload2) <= 0 && !runtime.HasRuntimeGroups)
{
float waited = 0f;
while (waited < 1.5f && !runtime.HasRuntimeGroups)
{
waited += 0.1f;
yield return new WaitForSeconds(0.1f);
}
avatarVoxelPayload = runtime.CaptureRuntimePayload();
if (AvatarPayloadHasAnyBase64(avatarVoxelPayload))
{
payload2 = avatarVoxelPayload;
}
else if (AvatarPayloadHasAnyBase64(localPayload))
{
payload2 = localPayload;
}
}
string text = AvatarVoxelStorage.Serialize(payload2);
if (text == "{}" || text.Length <= 4 || AvatarPayloadBase64TotalLength(payload2) <= 0)
{
Debug.LogWarning($"InventoryController: refusing to upload empty avatar payload slot={avatarId} jsonLen={text.Length} base64Total={AvatarPayloadBase64TotalLength(payload2)}");
yield break;
}
if (debugLogs)
{
int num = ((headBody != null && headBody.voxels != null) ? headBody.voxels.Length : 0);
int num2 = ((left != null && left.voxels != null) ? left.voxels.Length : 0);
int num3 = ((right != null && right.voxels != null) ? right.voxels.Length : 0);
int num4 = Mathf.Max(0, payload2.headBody?.sizeX ?? 0) * Mathf.Max(0, payload2.headBody?.sizeY ?? 0) * Mathf.Max(0, payload2.headBody?.sizeZ ?? 0);
int num5 = Mathf.Max(0, payload2.left?.sizeX ?? 0) * Mathf.Max(0, payload2.left?.sizeY ?? 0) * Mathf.Max(0, payload2.left?.sizeZ ?? 0);
int num6 = Mathf.Max(0, payload2.right?.sizeX ?? 0) * Mathf.Max(0, payload2.right?.sizeY ?? 0) * Mathf.Max(0, payload2.right?.sizeZ ?? 0);
int length = (payload2.headBody?.voxelsBase64 ?? string.Empty).Length;
int num7 = AvatarPayloadBase64TotalLength(payload2);
string text2 = ((payload2 == editorPayload) ? "editor" : ((payload2 == avatarVoxelPayload) ? "runtime" : ((payload2 == localPayload) ? "local" : "other")));
Debug.Log($"InventoryController: saving avatar slot={avatarId} chosen={text2} editor(head={num},left={num2},right={num3}) payloadExpected(head={num4},left={num5},right={num6}) headB64Len={length} b64Total={num7} jsonLen={text.Length}");
}
AvatarVoxelStorage.SaveLocal(payload2);
InventoryBackendClient.PutResponse put = null;
yield return client.UploadAvatar(playerId, avatarId, text, delegate(InventoryBackendClient.PutResponse r)
{
put = r;
});
if (put != null && put.ok && put.saved)
{
slotsDirty = true;
if (debugLogs)
{
string roundTrip = string.Empty;
yield return client.DownloadAvatar(playerId, avatarId, delegate(string t)
{
roundTrip = t;
});
int num8 = ((roundTrip != null) ? Encoding.UTF8.GetByteCount(roundTrip) : 0);
int num9 = 0;
if (!string.IsNullOrEmpty(roundTrip) && AvatarVoxelStorage.TryDeserialize(roundTrip, out var payload3))
{
num9 = (payload3.headBody?.voxelsBase64 ?? string.Empty).Length;
}
Debug.Log($"InventoryController: avatar roundtrip bytes={num8} headB64Len={num9}");
}
}
else if (debugLogs)
{
Debug.LogWarning(string.Format("InventoryController: UploadAvatar failed ok={0} saved={1} err={2}", put != null && put.ok, put != null && put.saved, (put != null) ? put.error : "null"));
}
}
private IEnumerator LoadAndEquipAvatar(string playerId, string avatarId)
{
string json = string.Empty;
yield return client.DownloadAvatar(playerId, avatarId, delegate(string t)
{
json = t;
});
if (!string.IsNullOrWhiteSpace(json) && AvatarVoxelStorage.TryDeserialize(json, out var payload))
{
AvatarCustomizerController avatarCustomizerController = UnityEngine.Object.FindObjectOfType<AvatarCustomizerController>();
if (avatarCustomizerController != null && avatarCustomizerController.TryGetEditorGroups(out var headBody, out var left, out var right))
{
AvatarVoxelStorage.ApplyPayload(payload, headBody, left, right, groupManager, useLocalGridTransform: true);
}
PhotonVoxelAvatar localOwnedInstance = PhotonVoxelAvatar.LocalOwnedInstance;
if (localOwnedInstance != null)
{
localOwnedInstance.ApplyAndBroadcastAvatar(payload);
}
InventoryBackendClient.EquipResponse equip = null;
yield return client.EquipAvatar(playerId, avatarId, delegate(InventoryBackendClient.EquipResponse r)
{
equip = r;
});
slotsDirty = true;
}
}
private IEnumerator SaveCreationSlot(string playerId, string creationId)
{
VRGroupSelector instance = VRGroupSelector.Instance;
List<VoxelGroup> list = ((instance != null) ? instance.GetSelectedGroupsSnapshot() : new List<VoxelGroup>());
NodeGraphManager instance2 = NodeGraphManager.Instance;
List<Node> list2 = ((instance2 != null) ? instance2.GetSelectedNodesSnapshot() : new List<Node>());
bool num = list != null && list.Count > 0;
bool flag = list2 != null && list2.Count > 0;
if (!num && !flag)
{
yield break;
}
HashSet<int> hashSet = null;
if (flag)
{
hashSet = new HashSet<int>();
for (int i = 0; i < list2.Count; i++)
{
if (list2[i] != null)
{
hashSet.Add(list2[i].NodeId);
}
}
}
byte[] gzipBytes = WorldGroupsCodec.EncodeGzip(list, (instance2 != null) ? instance2.BuildWorldNodeRecords(hashSet) : null, (instance2 != null) ? instance2.BuildWorldWireRecords(hashSet, BuildGroupSaveIndexMap(list)) : null);
InventoryBackendClient.PutResponse put = null;
yield return client.UploadCreation(playerId, creationId, gzipBytes, delegate(InventoryBackendClient.PutResponse r)
{
put = r;
});
if (put != null && put.ok && put.saved)
{
slotsDirty = true;
}
}
private IEnumerator LoadCreationIntoWorld(string playerId, string creationId)
{
byte[] gzip = null;
yield return client.DownloadCreation(playerId, creationId, delegate(byte[] b)
{
gzip = b;
});
if (gzip == null)
{
gzip = Array.Empty<byte>();
}
if (!WorldGroupsCodec.TryDecodeGzip(gzip, out WorldGroupsCodec.WorldSaveData saveData) || groupManager == null || !TryGetSpawnGridPosition(out var gridPosition))
{
yield break;
}
List<WorldGroupsCodec.WorldGroupRecord> list = saveData.groupRecords ?? new List<WorldGroupsCodec.WorldGroupRecord>();
List<WorldGroupsCodec.WorldNodeRecord> list2 = saveData.nodeRecords ?? new List<WorldGroupsCodec.WorldNodeRecord>();
if (list.Count == 0 && list2.Count == 0)
{
yield break;
}
float voxelSize = groupManager.VoxelSize;
Vector3 vector = ((list.Count > 0) ? new Vector3((float)list[0].position.x * voxelSize, (float)list[0].position.y * voxelSize, (float)list[0].position.z * voxelSize) : list2[0].position);
for (int num = 1; num < list.Count; num++)
{
Vector3 rhs = new Vector3((float)list[num].position.x * voxelSize, (float)list[num].position.y * voxelSize, (float)list[num].position.z * voxelSize);
vector = Vector3.Min(vector, rhs);
}
for (int num2 = 0; num2 < list2.Count; num2++)
{
vector = Vector3.Min(vector, list2[num2].position);
}
Dictionary<int, int> dictionary = new Dictionary<int, int>();
for (int num3 = 0; num3 < list.Count; num3++)
{
WorldGroupsCodec.WorldGroupRecord worldGroupRecord = list[num3];
VoxelGroupManager.SavedGroupData data = new VoxelGroupManager.SavedGroupData
{
position = worldGroupRecord.position,
packedRotation = worldGroupRecord.packedRotation,
size = worldGroupRecord.size,
voxels = worldGroupRecord.voxels
};
Vector3 vector2 = new Vector3((float)worldGroupRecord.position.x * voxelSize, (float)worldGroupRecord.position.y * voxelSize, (float)worldGroupRecord.position.z * voxelSize);
Vector3Int vector3Int = new Vector3Int(Mathf.RoundToInt((vector2.x - vector.x) / voxelSize), Mathf.RoundToInt((vector2.y - vector.y) / voxelSize), Mathf.RoundToInt((vector2.z - vector.z) / voxelSize));
VoxelGroup voxelGroup = groupManager.SpawnSavedGroup(data, gridPosition + vector3Int);
if (voxelGroup != null)
{
dictionary[num3] = voxelGroup.id;
}
}
if (list2.Count > 0)
{
NodeGraphManager instance = NodeGraphManager.Instance;
if (instance != null)
{
instance.ApplySavedGraph(positionOffset: new Vector3((float)gridPosition.x * voxelSize, (float)gridPosition.y * voxelSize, (float)gridPosition.z * voxelSize) - vector, nodeRecords: list2, wireRecords: saveData.wireRecords, clearExisting: false, broadcast: true, savedGroupIndexToRuntimeId: dictionary);
}
}
}
private static Dictionary<int, int> BuildGroupSaveIndexMap(IList<VoxelGroup> groups)
{
Dictionary<int, int> dictionary = new Dictionary<int, int>();
if (groups == null)
{
return dictionary;
}
for (int i = 0; i < groups.Count; i++)
{
VoxelGroup voxelGroup = groups[i];
if (voxelGroup != null)
{
dictionary[voxelGroup.id] = i;
}
}
return dictionary;
}
private void UpdatePreviewsForSelectedSlot()
{
previewRequestToken++;
if (mode == InventoryMode.Avatars)
{
ApplyEmptyAvatarPreview();
StartCoroutine(UpdateAvatarPreviews());
}
else
{
ApplyEmptyCreationPreview();
StartCoroutine(UpdateCreationPreview());
}
}
private IEnumerator UpdateAvatarPreviews()
{
EnsurePreviewGroups();
int token = previewRequestToken;
string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId();
if (string.IsNullOrEmpty(userFolderId) || client == null)
{
yield break;
}
if (!TryGetSelectedId(out var id) || string.IsNullOrEmpty(id))
{
ApplyEmptyAvatarPreview();
yield break;
}
ApplyEmptyAvatarPreview();
string json = string.Empty;
yield return client.DownloadAvatar(userFolderId, id, delegate(string t)
{
json = t;
});
if (token != previewRequestToken)
{
yield break;
}
if (string.IsNullOrWhiteSpace(json) || !AvatarVoxelStorage.TryDeserialize(json, out var payload))
{
if (debugLogs)
{
string text = json ?? string.Empty;
if (text.Length > 120)
{
text = text.Substring(0, 120);
}
Debug.LogWarning($"InventoryController: avatar download/parse failed id={id} jsonLen={(json ?? string.Empty).Length} snippet={text}");
}
ApplyEmptyAvatarPreview();
yield break;
}
if (debugLogs)
{
int num = Mathf.Max(0, payload.headBody?.sizeX ?? 0) * Mathf.Max(0, payload.headBody?.sizeY ?? 0) * Mathf.Max(0, payload.headBody?.sizeZ ?? 0);
int num2 = Mathf.Max(0, payload.left?.sizeX ?? 0) * Mathf.Max(0, payload.left?.sizeY ?? 0) * Mathf.Max(0, payload.left?.sizeZ ?? 0);
int num3 = Mathf.Max(0, payload.right?.sizeX ?? 0) * Mathf.Max(0, payload.right?.sizeY ?? 0) * Mathf.Max(0, payload.right?.sizeZ ?? 0);
Debug.Log($"InventoryController: avatarId={id} headExpected={num} leftExpected={num2} rightExpected={num3} headB64Len={(payload.headBody?.voxelsBase64 ?? string.Empty).Length}");
}
ApplyAvatarPreviewPayload(payload);
}
private IEnumerator UpdateCreationPreview()
{
EnsurePreviewGroups();
int token = previewRequestToken;
string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId();
if (string.IsNullOrEmpty(userFolderId) || client == null)
{
yield break;
}
if (!TryGetSelectedId(out var id) || string.IsNullOrEmpty(id))
{
ApplyEmptyCreationPreview();
yield break;
}
ApplyEmptyCreationPreview();
byte[] gzip = null;
yield return client.DownloadCreation(userFolderId, id, delegate(byte[] b)
{
gzip = b;
});
if (token == previewRequestToken)
{
if (gzip == null)
{
gzip = Array.Empty<byte>();
}
if (!WorldGroupsCodec.TryDecodeGzip(gzip, out WorldGroupsCodec.WorldSaveData saveData) || saveData.groupRecords == null || saveData.groupRecords.Count == 0)
{
ApplyEmptyCreationPreview();
}
else
{
ApplyCreationPreviewRecords(saveData.groupRecords);
}
}
}
private void ApplyCreationPreviewRecords(List<WorldGroupsCodec.WorldGroupRecord> records)
{
if (creationPreviewRoot == null || groupManager == null)
{
return;
}
EnsurePreviewGroups();
Vector3Int vector3Int = records[0].position;
Vector3Int vector3Int2 = records[0].position + records[0].size;
for (int i = 1; i < records.Count; i++)
{
Vector3Int position = records[i].position;
Vector3Int rhs = records[i].position + records[i].size;
vector3Int = Vector3Int.Min(vector3Int, position);
vector3Int2 = Vector3Int.Max(vector3Int2, rhs);
}
while (previewCreationGroups.Count < records.Count)
{
int count = previewCreationGroups.Count;
GameObject obj = new GameObject($"CreationGroupPreview_{count}");
obj.transform.SetParent(creationPreviewRoot, worldPositionStays: false);
VoxelGroup voxelGroup = obj.AddComponent<VoxelGroup>();
voxelGroup.id = (ushort)(62500 + count);
voxelGroup.SetOwner(groupManager);
voxelGroup.SetUseLocalGridTransform(useLocalGrid: true);
voxelGroup.SetColliderGeneration(enabled: false);
previewCreationGroups.Add(voxelGroup);
}
for (int j = records.Count; j < previewCreationGroups.Count; j++)
{
if (previewCreationGroups[j] != null)
{
previewCreationGroups[j].OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false);
previewCreationGroups[j].SetVoxelSizeScale(1f, rebuild: false);
previewCreationGroups[j].RebuildMesh();
}
}
for (int k = 0; k < records.Count; k++)
{
WorldGroupsCodec.WorldGroupRecord worldGroupRecord = records[k];
VoxelGroup voxelGroup2 = previewCreationGroups[k];
if (!(voxelGroup2 == null))
{
Vector3Int gridPosition = worldGroupRecord.position - vector3Int;
voxelGroup2.SetUseLocalGridTransform(useLocalGrid: true);
voxelGroup2.SetColliderGeneration(enabled: false);
voxelGroup2.SetOwner(groupManager);
voxelGroup2.OverwriteData(gridPosition, worldGroupRecord.packedRotation, worldGroupRecord.size, worldGroupRecord.voxels, queueRebuild: false);
voxelGroup2.SetVoxelSizeScale(1f, rebuild: false);
voxelGroup2.RebuildMesh();
}
}
Vector3 vector = vector3Int2 - vector3Int;
float num = ((groupManager != null) ? groupManager.VoxelSize : 0.125f);
float num2 = Mathf.Max(vector.x, Mathf.Max(vector.y, vector.z)) * num;
float num3 = ((num2 > 0.0001f) ? Mathf.Min(1f, 3f / num2) : 1f);
creationPreviewRoot.localScale = Vector3.one * num3;
}
private void ApplyEmptyCreationPreview()
{
for (int i = 0; i < previewCreationGroups.Count; i++)
{
if (previewCreationGroups[i] != null)
{
previewCreationGroups[i].OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false);
previewCreationGroups[i].RebuildMesh();
}
}
if (creationPreviewRoot != null)
{
creationPreviewRoot.localScale = Vector3.one;
}
}
private void ApplyAvatarPreviewPayload(AvatarVoxelPayload payload)
{
if (!(groupManager == null))
{
EnsurePreviewGroups();
if (previewHeadGroup != null)
{
AvatarVoxelStorage.ApplyGroupData(previewHeadGroup, payload.headBody, groupManager, useLocalGridTransform: true);
}
if (previewLeftGroup != null)
{
AvatarVoxelStorage.ApplyGroupData(previewLeftGroup, payload.left, groupManager, useLocalGridTransform: true);
}
if (previewRightGroup != null)
{
AvatarVoxelStorage.ApplyGroupData(previewRightGroup, payload.right, groupManager, useLocalGridTransform: true);
}
if (previewHeadGroup != null)
{
previewHeadGroup.SetVoxelSizeScale(0.5f, rebuild: false);
previewHeadGroup.RebuildMesh();
}
if (previewLeftGroup != null)
{
previewLeftGroup.SetVoxelSizeScale(0.5f, rebuild: false);
previewLeftGroup.RebuildMesh();
}
if (previewRightGroup != null)
{
previewRightGroup.SetVoxelSizeScale(0.5f, rebuild: false);
previewRightGroup.RebuildMesh();
}
}
}
private void ApplyEmptyAvatarPreview()
{
EnsurePreviewGroups();
if (!(previewHeadGroup == null))
{
previewHeadGroup.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false);
previewLeftGroup.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false);
previewRightGroup.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false);
previewHeadGroup.RebuildMesh();
previewLeftGroup.RebuildMesh();
previewRightGroup.RebuildMesh();
}
}
private void ResolveSceneReferences()
{
if (groupManager == null)
{
groupManager = ((VoxelGroupManager.Instance != null) ? VoxelGroupManager.Instance : UnityEngine.Object.FindObjectOfType<VoxelGroupManager>());
}
if (voxelBrush == null)
{
voxelBrush = ((VRVoxelBrush.Instance != null) ? VRVoxelBrush.Instance : UnityEngine.Object.FindObjectOfType<VRVoxelBrush>());
}
if (avatarsButton == null)
{
avatarsButton = FindCollider("Avatars");
}
if (creationsButton == null)
{
creationsButton = FindCollider("Creations");
}
if (backButton == null)
{
backButton = FindCollider("Back");
}
if (nextButton == null)
{
nextButton = FindCollider("Next");
}
if (saveButton == null)
{
saveButton = FindCollider("Save");
}
if (loadButton == null)
{
loadButton = FindCollider("Load");
}
if (creationPreviewRoot == null)
{
creationPreviewRoot = FindTransform("Preview");
}
if (previewRight == null)
{
previewRight = FindTransform("PrevR");
}
if (previewHead == null)
{
previewHead = FindTransform("PrevH");
}
if (previewLeft == null)
{
previewLeft = FindTransform("PrevL");
}
}
private Collider FindCollider(string name)
{
Transform transform = base.transform.Find("UI/" + name);
if (transform == null)
{
GameObject gameObject = GameObject.Find(name);
transform = ((gameObject != null) ? gameObject.transform : null);
}
if (!(transform != null))
{
return null;
}
return transform.GetComponent<Collider>();
}
private Transform FindTransform(string name)
{
if (string.IsNullOrEmpty(name))
{
return null;
}
if (transformCache.TryGetValue(name, out var value) && value != null)
{
return value;
}
Transform transform = base.transform.Find(name);
if (transform != null)
{
transformCache[name] = transform;
return transform;
}
Transform[] componentsInChildren = GetComponentsInChildren<Transform>(includeInactive: true);
foreach (Transform transform2 in componentsInChildren)
{
if (transform2 != null && string.Equals(transform2.name, name, StringComparison.OrdinalIgnoreCase))
{
transformCache[name] = transform2;
return transform2;
}
}
GameObject gameObject = GameObject.Find(name);
if (gameObject != null)
{
transformCache[name] = gameObject.transform;
return gameObject.transform;
}
Transform[] array = UnityEngine.Object.FindObjectsOfType<Transform>(includeInactive: true);
foreach (Transform transform3 in array)
{
if (transform3 != null && string.Equals(transform3.name, name, StringComparison.OrdinalIgnoreCase))
{
transformCache[name] = transform3;
return transform3;
}
}
return null;
}
private void EnsureButtonAudioAssigned()
{
if (!(buttonThockClip != null))
{
buttonThockClip = Resources.Load<AudioClip>("Thock");
}
}
private void EnsurePreviewGroups()
{
if (!(groupManager == null))
{
if (creationPreviewRoot == null)
{
creationPreviewRoot = FindTransform("Preview");
}
if (previewRight == null)
{
previewRight = FindTransform("PrevR");
}
if (previewHead == null)
{
previewHead = FindTransform("PrevH");
}
if (previewLeft == null)
{
previewLeft = FindTransform("PrevL");
}
if (creationPreviewRoot != null && previewCreationGroups.Count == 0)
{
GameObject gameObject = new GameObject("CreationGroupPreview_0");
gameObject.transform.SetParent(creationPreviewRoot, worldPositionStays: false);
previewCreationGroup = gameObject.AddComponent<VoxelGroup>();
previewCreationGroup.id = 62500;
previewCreationGroup.SetOwner(groupManager);
previewCreationGroup.SetUseLocalGridTransform(useLocalGrid: true);
previewCreationGroup.SetColliderGeneration(enabled: false);
previewCreationGroups.Add(previewCreationGroup);
}
else if (creationPreviewRoot == null && !warnedMissingCreationPreviewRoot)
{
warnedMissingCreationPreviewRoot = true;
Debug.LogWarning("InventoryController: could not find Creation Preview root named 'Preview'. Creation previews will be hidden until it exists.");
}
previewHeadGroup = EnsurePreviewGroupOn(previewHead, previewHeadGroup, 62000);
previewLeftGroup = EnsurePreviewGroupOn(previewLeft, previewLeftGroup, 62001);
previewRightGroup = EnsurePreviewGroupOn(previewRight, previewRightGroup, 62002);
if (!warnedMissingAvatarPreviewRoots && (previewHead == null || previewLeft == null || previewRight == null))
{
warnedMissingAvatarPreviewRoots = true;
Debug.LogWarning("InventoryController: could not find one or more avatar preview roots ('PrevH', 'PrevL', 'PrevR'). Avatar previews will be hidden until they exist.");
}
}
}
private VoxelGroup EnsurePreviewGroupOn(Transform root, VoxelGroup existing, ushort id)
{
if (root == null || groupManager == null)
{
return existing;
}
VoxelGroup voxelGroup = ((existing != null) ? existing : root.GetComponent<VoxelGroup>());
if (voxelGroup == null)
{
voxelGroup = root.gameObject.AddComponent<VoxelGroup>();
}
voxelGroup.id = id;
voxelGroup.SetOwner(groupManager);
voxelGroup.SetUseLocalGridTransform(useLocalGrid: true);
voxelGroup.SetColliderGeneration(enabled: false);
voxelGroup.SetVoxelSizeScale(0.5f);
return voxelGroup;
}
private void HandleButton(Collider button, ref bool insidePrevious, Action action)
{
bool flag = IsHandInside(button, (voxelBrush != null) ? voxelBrush.LeftHandAnchor : null) || IsHandInside(button, (voxelBrush != null) ? voxelBrush.RightHandAnchor : null);
if (flag && !insidePrevious)
{
PlayButtonEnterAnimation(button);
if (Time.unscaledTime >= nextButtonPressTime)
{
nextButtonPressTime = Time.unscaledTime + 0.2f;
PlayButtonClickFeedback(button);
action?.Invoke();
}
}
insidePrevious = flag;
}
private static bool IsHandInside(Collider button, Transform hand)
{
if (button == null || hand == null)
{
return false;
}
return button.bounds.Contains(hand.position);
}
private void PlayButtonEnterAnimation(Collider button)
{
if (!(button == null))
{
Transform transform = button.transform;
if (!buttonBaseScales.TryGetValue(transform, out var value))
{
value = transform.localScale;
buttonBaseScales.Add(transform, value);
}
if (buttonAnimationCoroutines.TryGetValue(transform, out var value2) && value2 != null)
{
StopCoroutine(value2);
}
transform.localScale = value;
buttonAnimationCoroutines[transform] = StartCoroutine(AnimateButtonScale(transform, value));
}
}
private IEnumerator AnimateButtonScale(Transform buttonTransform, Vector3 baseScale)
{
if (!(buttonTransform == null))
{
float startTime = Time.unscaledTime;
while (Time.unscaledTime - startTime < 0.08f)
{
float t = Mathf.Clamp01((Time.unscaledTime - startTime) / 0.08f);
buttonTransform.localScale = Vector3.Lerp(baseScale, baseScale * 0.84f, t);
yield return null;
}
startTime = Time.unscaledTime;
while (Time.unscaledTime - startTime < 0.12f)
{
float num = Mathf.Clamp01((Time.unscaledTime - startTime) / 0.12f);
float num2 = Mathf.Sin(num * MathF.PI);
Vector3 a = Vector3.Lerp(baseScale * 0.84f, baseScale, num);
buttonTransform.localScale = Vector3.LerpUnclamped(a, baseScale * 1.04f, num2 * 0.15f);
yield return null;
}
buttonTransform.localScale = baseScale;
}
}
private void PlayButtonClickFeedback(Collider button)
{
if (!(buttonThockClip == null) && !(button == null))
{
AudioSource.PlayClipAtPoint(buttonThockClip, button.bounds.center, 1f);
}
}
private void ResetAllButtonScales()
{
foreach (KeyValuePair<Transform, Vector3> buttonBaseScale in buttonBaseScales)
{
if (buttonBaseScale.Key != null)
{
buttonBaseScale.Key.localScale = buttonBaseScale.Value;
}
}
}
private bool TryGetSelectedId(out string id)
{
id = string.Empty;
if (slotIndex < 0 || slotIndex >= ids.Count)
{
return false;
}
id = ids[slotIndex] ?? string.Empty;
return true;
}
private string GetOrCreateSelectedId()
{
if (!TryGetSelectedId(out var id))
{
return string.Empty;
}
if (!string.IsNullOrEmpty(id))
{
return id;
}
string text = MakeNewId();
ids[slotIndex] = text;
return text;
}
private static string MakeNewId()
{
return DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
}
private bool TryGetSpawnGridPosition(out Vector3Int gridPosition)
{
gridPosition = Vector3Int.zero;
VRVoxelBrush vRVoxelBrush = ((voxelBrush != null) ? voxelBrush : VRVoxelBrush.Instance);
if (vRVoxelBrush != null && vRVoxelBrush.TryGetSpawnGridPosition(out gridPosition))
{
return true;
}
float num = ((groupManager != null) ? groupManager.VoxelSize : 0.125f);
Vector3 vector = ((Camera.main != null) ? Camera.main.transform.position : base.transform.position) + ((Camera.main != null) ? Camera.main.transform.forward : base.transform.forward).normalized * 2f;
gridPosition = new Vector3Int(Mathf.RoundToInt(vector.x / num), Mathf.RoundToInt(vector.y / num), Mathf.RoundToInt(vector.z / num));
return true;
}
}
}