using System; using BlockSpace.Voxels; using UnityEngine; [Serializable] public struct BSValue { private const double WholeNumberEpsilon = 0.0001; public BSValueKind Kind; public double Number; public string Text; public bool Bool; public int PlayerId; public int GroupId; public bool IsInitialized { get { if (Kind == BSValueKind.None && !(Math.Abs(Number) > 0.0001) && string.IsNullOrEmpty(Text) && PlayerId == 0) { return GroupId != 0; } return true; } } public static BSValue None => new BSValue { Kind = BSValueKind.None }; public static BSValue FromBool(bool value) { return new BSValue { Kind = BSValueKind.Bool, Bool = value }; } public static BSValue FromNumber(double value) { return new BSValue { Kind = BSValueKind.Number, Number = NormalizeNumber(value) }; } public static BSValue FromText(string value) { if (string.IsNullOrWhiteSpace(value) || string.Equals(value, "None", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "Null", StringComparison.OrdinalIgnoreCase)) { return None; } if (string.Equals(value, "True", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "False", StringComparison.OrdinalIgnoreCase)) { return FromBool(string.Equals(value, "True", StringComparison.OrdinalIgnoreCase)); } if (double.TryParse(value, out var result)) { return FromNumber(result); } return new BSValue { Kind = BSValueKind.Text, Text = value }; } public static BSValue FromPlayer(int playerId) { return new BSValue { Kind = BSValueKind.Player, PlayerId = playerId }; } public static BSValue FromGroup(int groupId) { return new BSValue { Kind = BSValueKind.Group, GroupId = groupId }; } public BSValue Normalize() { switch (Kind) { case BSValueKind.Number: Number = NormalizeNumber(Number); break; case BSValueKind.Text: if (Text == null) { Text = string.Empty; } break; } return this; } public bool IsWholeNumber() { return Math.Abs(Number - Math.Round(Number)) <= 0.0001; } public double AsNumber() { switch (Kind) { case BSValueKind.Number: return NormalizeNumber(Number); case BSValueKind.Bool: if (!Bool) { return 0.0; } return 1.0; case BSValueKind.Text: { if (double.TryParse(Text, out var result)) { return NormalizeNumber(result); } return EncodeTextAsNumber(Text); } case BSValueKind.Player: return PlayerId; case BSValueKind.Group: return GroupId; default: return 0.0; } } public bool AsBool() { return Kind switch { BSValueKind.Bool => Bool, BSValueKind.None => false, BSValueKind.Number => Math.Abs(AsNumber()) > 0.0001, BSValueKind.Text => !string.IsNullOrEmpty(AsText()), BSValueKind.Player => AsPlayerId() > 0, BSValueKind.Group => AsGroupId() > 0, _ => false, }; } public string AsText() { switch (Kind) { case BSValueKind.Number: if (!IsWholeNumber()) { return NormalizeNumber(Number).ToString("0.#######"); } return Math.Round(Number).ToString(); case BSValueKind.Bool: if (!Bool) { return "False"; } return "True"; case BSValueKind.Text: return Text ?? string.Empty; case BSValueKind.Player: return PlayerId.ToString(); case BSValueKind.Group: return GroupId.ToString(); default: return string.Empty; } } public int AsPlayerId() { switch (Kind) { case BSValueKind.Player: return PlayerId; case BSValueKind.Number: return Mathf.RoundToInt((float)NormalizeNumber(Number)); case BSValueKind.Group: return GroupId; case BSValueKind.Text: { if (int.TryParse(Text, out var result)) { return result; } return Mathf.RoundToInt((float)EncodeTextAsNumber(Text)); } default: return 0; } } public int AsGroupId() { switch (Kind) { case BSValueKind.Group: return GroupId; case BSValueKind.Number: return Mathf.RoundToInt((float)NormalizeNumber(Number)); case BSValueKind.Player: return PlayerId; case BSValueKind.Text: { if (int.TryParse(Text, out var result)) { return result; } return Mathf.RoundToInt((float)EncodeTextAsNumber(Text)); } default: return 0; } } public bool TryResolveGroup(out VoxelGroup group) { return TryResolveGroup(VoxelGroupManager.Instance, out group); } public bool TryResolveGroup(VoxelGroupManager manager, out VoxelGroup group) { group = null; if (manager == null) { return false; } int num = AsGroupId(); if (num > 0 && num <= 65535 && manager.TryGetGroup((ushort)num, out group)) { return group != null; } return false; } public BSValue ConvertTo(BSValueKind targetKind) { return targetKind switch { BSValueKind.Bool => FromBool(AsBool()), BSValueKind.Number => FromNumber(AsNumber()), BSValueKind.Text => new BSValue { Kind = BSValueKind.Text, Text = AsText() }, BSValueKind.Player => FromPlayer(AsPlayerId()), BSValueKind.Group => FromGroup(AsGroupId()), _ => None, }; } public override string ToString() { return Kind switch { BSValueKind.Number => AsText(), BSValueKind.Text => AsText(), BSValueKind.Bool => AsText(), BSValueKind.Player => $"Player({AsPlayerId()})", BSValueKind.Group => $"Group({AsGroupId()})", _ => "None", }; } private static double NormalizeNumber(double value) { double num = Math.Round(value); if (!(Math.Abs(value - num) <= 0.0001)) { return value; } return num; } private static double EncodeTextAsNumber(string value) { if (string.IsNullOrEmpty(value)) { return 0.0; } uint num = 2166136261u; for (int i = 0; i < value.Length; i++) { num ^= value[i]; num *= 16777619; } return num; } }