264 lines
7.6 KiB
C#
264 lines
7.6 KiB
C#
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<AvatarVoxelPayload>(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;
|
|
}
|
|
}
|
|
}
|
|
}
|