using System; using System.IO; using BlockSpace.Account; using UnityEngine; namespace BlockSpace.Voxels { public static class AvatarVoxelStorage { public const string PlayerPrefsKey = "BlockSpace.Avatar.Payload"; private const string AvatarPayloadFileName = "avatar_payload.json"; private static string GetAvatarPayloadPath() { string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId(); return Path.Combine(Application.persistentDataPath, "BlockSpace", "Users", userFolderId, "avatar_payload.json"); } private static string GetAvatarPayloadPathForUser(string userId) { return Path.Combine(Application.persistentDataPath, "BlockSpace", "Users", userId, "avatar_payload.json"); } private static void TryMigrateLocalAvatarToLinked() { if (!BlockSpaceAccountPrefs.IsLinked) { return; } string linkedPlayerId = BlockSpaceAccountPrefs.GetLinkedPlayerId(); if (string.IsNullOrEmpty(linkedPlayerId)) { return; } string avatarPayloadPathForUser = GetAvatarPayloadPathForUser(linkedPlayerId); if (File.Exists(avatarPayloadPathForUser)) { return; } string orCreateLocalUserId = BlockSpaceAccountPrefs.GetOrCreateLocalUserId(); if (string.IsNullOrEmpty(orCreateLocalUserId)) { return; } string avatarPayloadPathForUser2 = GetAvatarPayloadPathForUser(orCreateLocalUserId); if (!File.Exists(avatarPayloadPathForUser2)) { return; } try { string directoryName = Path.GetDirectoryName(avatarPayloadPathForUser); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } File.Copy(avatarPayloadPathForUser2, avatarPayloadPathForUser, overwrite: true); } catch { } } public static AvatarVoxelPayload CreateEmptyPayload() { return new AvatarVoxelPayload(); } public static AvatarVoxelPayload CapturePayload(VoxelGroup headBodyGroup, VoxelGroup leftGroup, VoxelGroup rightGroup) { return new AvatarVoxelPayload { headBody = CaptureGroup(headBodyGroup), left = CaptureGroup(leftGroup), right = CaptureGroup(rightGroup) }; } public static AvatarVoxelGroupData CaptureGroup(VoxelGroup group) { if (group == null) { return new AvatarVoxelGroupData(); } return new AvatarVoxelGroupData { positionX = group.position.x, positionY = group.position.y, positionZ = group.position.z, packedRotation = group.packedRotation, sizeX = group.size.x, sizeY = group.size.y, sizeZ = group.size.z, voxelsBase64 = ((group.voxels != null && group.voxels.Length != 0) ? Convert.ToBase64String(group.voxels) : string.Empty) }; } public static void ApplyPayload(AvatarVoxelPayload payload, VoxelGroup headBodyGroup, VoxelGroup leftGroup, VoxelGroup rightGroup, VoxelGroupManager manager, bool useLocalGridTransform) { AvatarVoxelPayload avatarVoxelPayload = payload ?? CreateEmptyPayload(); ApplyGroupData(headBodyGroup, avatarVoxelPayload.headBody, manager, useLocalGridTransform); ApplyGroupData(leftGroup, avatarVoxelPayload.left, manager, useLocalGridTransform); ApplyGroupData(rightGroup, avatarVoxelPayload.right, manager, useLocalGridTransform); } public static void ApplyGroupData(VoxelGroup group, AvatarVoxelGroupData data, VoxelGroupManager manager, bool useLocalGridTransform) { if (!(group == null) && !(manager == null)) { AvatarVoxelGroupData avatarVoxelGroupData = data ?? new AvatarVoxelGroupData(); Vector3Int vector3Int = new Vector3Int(Mathf.Max(0, avatarVoxelGroupData.sizeX), Mathf.Max(0, avatarVoxelGroupData.sizeY), Mathf.Max(0, avatarVoxelGroupData.sizeZ)); byte[] array = DecodeVoxelBytes(avatarVoxelGroupData, vector3Int); int num = ((vector3Int.x > 0 && vector3Int.y > 0 && vector3Int.z > 0) ? (vector3Int.x * vector3Int.y * vector3Int.z) : 0); if (num > 0 && array.Length != num) { vector3Int = Vector3Int.zero; array = new byte[0]; } group.SetUseLocalGridTransform(useLocalGridTransform); group.SetColliderGeneration(enabled: false); group.SetOwner(manager); group.OverwriteData(new Vector3Int(avatarVoxelGroupData.positionX, avatarVoxelGroupData.positionY, avatarVoxelGroupData.positionZ), (byte)Mathf.Clamp(avatarVoxelGroupData.packedRotation, 0, 255), vector3Int, array, queueRebuild: false); group.SetGreedyRenderMeshing(enabled: true); group.RebuildMesh(); group.RebuildCollider(); DisableCollider(group); } } public static void SaveLocal(AvatarVoxelPayload payload) { try { string avatarPayloadPath = GetAvatarPayloadPath(); string directoryName = Path.GetDirectoryName(avatarPayloadPath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllText(avatarPayloadPath, Serialize(payload)); if (PlayerPrefs.HasKey("BlockSpace.Avatar.Payload")) { PlayerPrefs.DeleteKey("BlockSpace.Avatar.Payload"); PlayerPrefs.Save(); } } catch (Exception ex) { Debug.LogWarning("Failed to save avatar payload to disk, falling back to PlayerPrefs: " + ex.Message); PlayerPrefs.SetString("BlockSpace.Avatar.Payload", Serialize(payload)); PlayerPrefs.Save(); } } public static AvatarVoxelPayload LoadLocalOrDefault() { if (!TryLoadLocal(out var payload)) { return CreateEmptyPayload(); } return payload; } public static bool TryLoadLocal(out AvatarVoxelPayload payload) { payload = CreateEmptyPayload(); try { TryMigrateLocalAvatarToLinked(); string avatarPayloadPath = GetAvatarPayloadPath(); if (File.Exists(avatarPayloadPath)) { return TryDeserialize(File.ReadAllText(avatarPayloadPath), out payload); } } catch (Exception ex) { Debug.LogWarning("Failed to load avatar payload from disk: " + ex.Message); } if (!PlayerPrefs.HasKey("BlockSpace.Avatar.Payload")) { return false; } bool num = TryDeserialize(PlayerPrefs.GetString("BlockSpace.Avatar.Payload", string.Empty), out payload); if (num) { SaveLocal(payload); } return num; } public static string Serialize(AvatarVoxelPayload payload) { return JsonUtility.ToJson(payload ?? CreateEmptyPayload()); } public static bool TryDeserialize(string json, out AvatarVoxelPayload payload) { payload = CreateEmptyPayload(); if (string.IsNullOrWhiteSpace(json)) { return false; } try { AvatarVoxelPayload avatarVoxelPayload = JsonUtility.FromJson(json); if (avatarVoxelPayload == null) { return false; } if (avatarVoxelPayload.headBody == null) { avatarVoxelPayload.headBody = new AvatarVoxelGroupData(); } if (avatarVoxelPayload.left == null) { avatarVoxelPayload.left = new AvatarVoxelGroupData(); } if (avatarVoxelPayload.right == null) { avatarVoxelPayload.right = new AvatarVoxelGroupData(); } payload = avatarVoxelPayload; return true; } catch (Exception ex) { Debug.LogWarning("Failed to deserialize avatar payload: " + ex.Message); return false; } } private static byte[] DecodeVoxelBytes(AvatarVoxelGroupData data, Vector3Int size) { int num = ((size.x > 0 && size.y > 0 && size.z > 0) ? (size.x * size.y * size.z) : 0); if (num <= 0 || string.IsNullOrEmpty(data.voxelsBase64)) { return new byte[0]; } try { byte[] array = Convert.FromBase64String(data.voxelsBase64); if (array.Length == num) { return array; } } catch (FormatException) { } return new byte[0]; } private static void DisableCollider(VoxelGroup group) { if (!(group.meshCollider == null)) { group.meshCollider.enabled = false; } } } }