Иконка ресурса

FREE BackpackButton : 1.1.1 1.1.1

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Network;
using Newtonsoft.Json.Converters;
using UnityEngine;
using UnityEngine.UI;
using Time = UnityEngine.Time;

namespace Oxide.Plugins
{
    [Info("Backpack Button", "WhiteThunder", "1.1.1")]
    [Description("Adds a button which allows players to open their backpack, with multiple advanced features.")]
    internal class BackpackButton : CovalencePlugin
    {
        #region Fields

        private const string UsagePermission = "backpackbutton.use";

        private const int SaddleBagItemId = 1400460850;

        private Configuration _config;
        private SavedData _data;
        private BackpacksApi _backpacksApi;
        private readonly HashSet<ulong> _uiViewers = new HashSet<ulong>();
        private readonly UiUpdateManager _uiUpdateManager;

        [PluginReference]
        private readonly Plugin Backpacks;

        private static readonly VersionNumber RequiredBackpacksVersion = new VersionNumber(3, 11, 0);

        public BackpackButton()
        {
            _uiUpdateManager = new UiUpdateManager(this);
        }

        #endregion

        #region Hooks

        private void Init()
        {
            _config.Init();
            _data = SavedData.Load();

            permission.RegisterPermission(UsagePermission, this);

            AddCovalenceCommand(_config.Commands, nameof(BackpackButtonCommand));

            Unsubscribe(nameof(OnPlayerSleep));
            Unsubscribe(nameof(OnPlayerSleepEnded));
        }

        private void OnServerInitialized()
        {
            if (Backpacks == null)
            {
                LogError($"Backpacks is not loaded.Get it at https://umod.org.");
            }

            HandleBackpacksLoaded();

            timer.Every(900, _data.SaveIfChanged);

            Subscribe(nameof(OnPlayerSleep));
            Subscribe(nameof(OnPlayerSleepEnded));
        }

        private void Unload()
        {
            _uiUpdateManager.Unload();

            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyUiIfActive(player);
            }

            _data.SaveIfChanged();
        }

        private void OnPluginLoaded(Plugin plugin)
        {
            if (plugin.Name == nameof(Backpacks))
            {
                HandleBackpacksLoaded();
            }
        }

        private void OnPluginUnloaded(Plugin plugin)
        {
            if (plugin.Name == nameof(Backpacks))
            {
                _backpacksApi = null;

                foreach (var player in BasePlayer.activePlayerList)
                {
                    DestroyUiIfActive(player);
                }
            }
        }

        private void OnGroupPermissionGranted(string groupName, string perm)
        {
            if (!perm.Equals(UsagePermission))
                return;

            foreach (var player in BasePlayer.activePlayerList)
            {
                if (!permission.UserHasGroup(player.UserIDString, groupName))
                    continue;

                HandlePermissionChanged(player);
            }
        }

        private void OnGroupPermissionRevoked(string groupName, string perm)
        {
            OnGroupPermissionGranted(groupName, perm);
        }

        private void OnUserPermissionGranted(string userId, string perm)
        {
            if (!perm.Equals(UsagePermission))
                return;

            var player = BasePlayer.Find(userId);
            if (player != null)
            {
                HandlePermissionChanged(player);
            }
        }

        private void OnUserPermissionRevoked(string userId, string perm)
        {
            OnUserPermissionGranted(userId, perm);
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            CreateUiIfEnabled(player);
        }

        private void OnPlayerRespawned(BasePlayer player)
        {
            CreateUiIfEnabled(player);
        }

        private void OnPlayerSleepEnded(BasePlayer player)
        {
            CreateUiIfEnabled(player);
        }

        private void OnPlayerSleep(BasePlayer player)
        {
            DestroyUiIfActive(player);
        }

        // Handle player death while sleeping in a safe zone.private void OnEntityKill(BasePlayer player)
        {
            if (player.IsNpc)
                return;

            DestroyUiIfActive(player);
        }

        // Handle player death by normal means.private void OnEntityDeath(BasePlayer player, HitInfo info) => OnEntityKill(player);

        private void OnEntityMounted(ComputerStation station, BasePlayer player) => DestroyUiIfActive(player);

        private void OnEntityDismounted(ComputerStation station, BasePlayer player) => CreateUiIfEnabled(player);

        private void OnNpcConversationStart(NPCTalking npcTalking, BasePlayer player, ConversationData conversationData)
        {
            // This delay can be removed in the future if an OnNpcConversationStarted hook is created.NextTick(() =>
            {
                // Verify the conversation started, since another plugin may have blocked it.if (!npcTalking.conversingPlayers.Contains(player))
                    return;

                DestroyUiIfActive(player);
            });
        }

        private void OnNpcConversationEnded(NPCTalking npcTalking, BasePlayer player) => CreateUiIfEnabled(player);

        #endregion

        #region Dependencies

        private class BackpacksApi
        {
            public static BackpacksApi Parse(Dictionary<string, object> dict)
            {
                var backpacksApi = new BackpacksApi();

                GetOption(dict, "IsBackpackLoaded", out backpacksApi.IsBackpackLoaded);
                GetOption(dict, "GetBackpackCapacity", out backpacksApi.GetBackpackCapacity);
                GetOption(dict, "IsBackpackGathering", out backpacksApi.IsBackpackGathering);
                GetOption(dict, "IsBackpackRetrieving", out backpacksApi.IsBackpackRetrieving);
                GetOption(dict, "CountBackpackItems", out backpacksApi.CountBackpackItems);

                return backpacksApi;
            }

            public Func<BasePlayer, bool> IsBackpackLoaded;
            public Func<BasePlayer, int> GetBackpackCapacity;
            public Func<BasePlayer, bool> IsBackpackGathering;
            public Func<BasePlayer, bool> IsBackpackRetrieving;
            public Func<ulong, Dictionary<string, object>, int> CountBackpackItems;

            private static void GetOption<T>(Dictionary<string, object> dict, string key, out T result)
            {
                object value;
                result = dict.TryGetValue(key, out value) && value is T
                    ? (T)value
                    : default(T);
            }
        }

        #endregion

        #region Commands

        private void BackpackButtonCommand(IPlayer player, string command, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyPlayer(player, out basePlayer)
                || !VerifyHasPermission(player, UsagePermission)
                || _config.NumValidPositions == 0)
                return;

            var positionNameArg = args.FirstOrDefault();
            if (string.IsNullOrWhiteSpace(positionNameArg) || IsKeyBindArg(positionNameArg))
            {
                PrintCommandUsage(player, basePlayer, command);
                return;
            }

            var localizedToggleOption = GetMessage(player.Id, "Toggle");
            if (StringUtils.EqualsIgnoreCase(positionNameArg, localizedToggleOption))
            {
                var enabled = _data.GetEnabledPreference(basePlayer.userID) ?? _config.DefaultButtonPosition != null;
                _data.SetEnabledPreference(basePlayer.userID, !enabled);

                DestroyUiIfActive(basePlayer);
                CreateUiIfEnabled(basePlayer);
                return;
            }

            var localizedOffOption = GetMessage(player.Id, "Off");
            if (StringUtils.EqualsIgnoreCase(positionNameArg, localizedOffOption))
            {
                DestroyUiIfActive(basePlayer);

                if (_config.DefaultButtonPosition == null)
                {
                    _data.RemoveEnabledPreference(basePlayer.userID);
                }
                else
                {
                    _data.SetEnabledPreference(basePlayer.userID, false);
                }

                _data.RemovePositionPreference(basePlayer.userID);
                return;
            }

            var buttonPosition = GetMatchingButtonPosition(basePlayer, positionNameArg);
            if (buttonPosition == null)
            {
                PrintCommandUsage(player, basePlayer, command);
                return;
            }

            var defaultEnabled = _config.DefaultButtonPosition != null;
            var defaultPositionName = _config.DefaultButtonPosition?.Name;

            if (defaultEnabled)
            {
                _data.RemoveEnabledPreference(basePlayer.userID);
            }
            else
            {
                _data.SetEnabledPreference(basePlayer.userID, true);
            }

            if (defaultEnabled && StringUtils.EqualsIgnoreCase(buttonPosition.Name, defaultPositionName))
            {
                _data.RemovePositionPreference(basePlayer.userID);
            }
            else
            {
                _data.SetPositionPreference(basePlayer.userID, buttonPosition.Name);
            }

            CreateUiIfEnabled(basePlayer);
        }

        #endregion

        #region Helpers

        private static class StringUtils
        {
            public static bool EqualsIgnoreCase(string a, string b) =>
                string.Compare(a, b, StringComparison.OrdinalIgnoreCase) == 0;
        }

        private static void LogDebug(string message) => Interface.Oxide.LogDebug($"[Backpack Button] {message}");
        private static void LogInfo(string message) => Interface.Oxide.LogInfo($"[Backpack Button] {message}");
        private static void LogWarning(string message) => Interface.Oxide.LogWarning($"[Backpack Button] {message}");
        private static void LogError(string message) => Interface.Oxide.LogError($"[Backpack Button] {message}");

        private static bool IsKeyBindArg(string arg)
        {
            return arg == "True";
        }

        private static bool VerifyPlayer(IPlayer player, out BasePlayer basePlayer)
        {
            if (player.IsServer)
            {
                basePlayer = null;
                return false;
            }

            basePlayer = player.Object as BasePlayer;
            return true;
        }

        private bool VerifyBackpacksVersion()
        {
            if (Backpacks == null)
                return false;

            if (Backpacks.Version >= RequiredBackpacksVersion)
                return true;

            LogError($"Backpacks v{Backpacks.Version} is loaded, but this plugin requires Backpacks v{RequiredBackpacksVersion}+");
            return false;
        }

        private bool VerifyHasPermission(IPlayer player, string perm)
        {
            if (player.HasPermission(perm))
                return true;

            player.Reply(GetMessage(player, "No Permission"));
            return false;
        }

        private ButtonPosition GetPlayerButtonPosition(BasePlayer player)
        {
            var enabled = _data.GetEnabledPreference(player.userID) ?? _config.DefaultButtonPosition != null;
            if (!enabled)
                return null;

            var positionName = _data.GetPositionPreference(player.userID) ?? _config.DefaultButtonPosition?.Name;
            if (positionName == null)
                return null;

            return _config.GetButtonPosition(positionName);
        }

        private void PrintCommandUsage(IPlayer player, BasePlayer basePlayer, string command)
        {
            var currentButtonPosition = GetPlayerButtonPosition(basePlayer);
            var localizedButtonNameList = new List<string>();

            foreach (var buttonPosition in _config.ButtonPositions)
            {
                if (!buttonPosition.Enabled || buttonPosition.LangKey == null)
                    continue;

                var localizedButtonName = GetMessage(player.Id, buttonPosition.LangKey);
                localizedButtonNameList.Add(buttonPosition == currentButtonPosition
                    ? $"<color=#5bf>{localizedButtonName}</color>"
                    : localizedButtonName);
            }

            localizedButtonNameList.Add(GetMessage(player.Id, "Toggle"));

            var localizedOffName = GetMessage(player.Id, "Off");
            localizedButtonNameList.Add(currentButtonPosition == null
                ? $"<color=#5bf>{localizedOffName}</color>"
                : localizedOffName);

            if (player.LastCommand == CommandType.Chat)
            {
                command = $"/{command}";
            }

            var localizedUsageTemplate = GetMessage(player.Id, "Usage");
            player.Reply(string.Format(localizedUsageTemplate, command, string.Join(" | ", localizedButtonNameList)));
        }

        private void HandleBackpacksLoaded()
        {
            if (!VerifyBackpacksVersion())
                return;

            var apiDict = Backpacks.Call("API_GetApi") as Dictionary<string, object>;
            _backpacksApi = BackpacksApi.Parse(apiDict);
            if (_backpacksApi == null)
            {
                LogError("Failed to integrate with Backpacks Api.");
                return;
            }

            var hooks = new Dictionary<string, object>
            {
                ["OnBackpackLoaded"] = new Action<BasePlayer, int, int>((player, occupiedSlots, totalSlots) =>
                {
                    if (_uiViewers.Contains(player.userID))
                    {
                        ButtonUi.UpdateUi(this, player);
                    }
                    else
                    {
                        CreateUiIfEnabled(player);
                    }
                })
            };

            if (_config.FillBar.Enabled || _config.Slots.Enabled)
            {
                hooks["OnBackpackItemCountChanged"] = new Action<BasePlayer, int, int>((player, occupiedSlots, totalSlots) =>
                {
                    if (_uiViewers.Contains(player.userID))
                    {
                        _uiUpdateManager.ScheduleUpdate(player);
                    }
                });
            }

            if (_config.GatherMode.Enabled)
            {
                hooks["OnBackpackGatherChanged"] = new Action<BasePlayer, bool>((player, enabled) =>
                {
                    if (_uiViewers.Contains(player.userID))
                    {
                        _uiUpdateManager.ScheduleUpdate(player);
                    }
                });
            }

            if (_config.RetrieveMode.Enabled)
            {
                hooks["OnBackpackRetrieveChanged"] = new Action<BasePlayer, bool>((player, enabled) =>
                {
                    if (_uiViewers.Contains(player.userID))
                    {
                        _uiUpdateManager.ScheduleUpdate(player);
                    }
                });
            }

            Backpacks.Call("API_AddSubscriber", this, hooks);

            foreach (var player in BasePlayer.activePlayerList)
            {
                CreateUiIfEnabled(player);
            }
        }

        private bool ShouldDisplayUi(BasePlayer player, out ButtonPosition buttonPosition)
        {
            buttonPosition = null;

            if (_backpacksApi == null)
                return false;

            if (player == null || player.IsNpc || !player.IsAlive() || player.IsSleeping())
                return false;

            if (!permission.UserHasPermission(player.UserIDString, UsagePermission))
                return false;

            var enabled = _data.GetEnabledPreference(player.userID) ?? _config.DefaultButtonPosition != null;
            if (!enabled)
                return false;

            buttonPosition = GetPlayerButtonPosition(player);
            return buttonPosition != null;
        }

        private bool ShouldDisplayUi(BasePlayer player)
        {
            ButtonPosition buttonPosition;
            return ShouldDisplayUi(player, out buttonPosition);
        }

        private void CreateUiIfEnabled(BasePlayer player)
        {
            ButtonPosition buttonPosition;
            if (!ShouldDisplayUi(player, out buttonPosition))
                return;

            _uiViewers.Add(player.userID);
            ButtonUi.CreateUi(this, player, buttonPosition);
        }

        private void DestroyUiIfActive(BasePlayer player)
        {
            if (!_uiViewers.Remove(player.userID))
                return;

            ButtonUi.DestroyUi(player);
        }

        private void HandlePermissionChanged(BasePlayer player)
        {
            if (ShouldDisplayUi(player))
            {
                CreateUiIfEnabled(player);
            }
            else
            {
                DestroyUiIfActive(player);
            }
        }

        private ButtonPosition GetMatchingButtonPosition(BasePlayer player, string positionName)
        {
            foreach (var buttonPosition in _config.ButtonPositions)
            {
                if (!buttonPosition.Enabled || buttonPosition.LangKey == null)
                    continue;

                var localizedPositionName = GetMessage(player.UserIDString, buttonPosition.LangKey);
                if (StringUtils.EqualsIgnoreCase(positionName, localizedPositionName))
                    return buttonPosition;
            }

            return null;
        }

        #endregion

        #region UI Update Manager

        private class UiUpdateManager
        {
            private const float UpdateDelaySeconds = 0.5f;
            private const float ProcessFrequencySeconds = 0.1f;

            private BackpackButton _plugin;
            private Action _updateAction;
            private Dictionary<BasePlayer, float> _scheduledUpdates = new Dictionary<BasePlayer, float>();
            private List<BasePlayer> _playersProcessed = new List<BasePlayer>();

            public UiUpdateManager(BackpackButton plugin)
            {
                _plugin = plugin;
                _updateAction = ProcessUpdates;
            }

            public void ScheduleUpdate(BasePlayer player)
            {
                var currentlyRunning = _scheduledUpdates.Count > 0;
                _scheduledUpdates[player] = Time.time + UpdateDelaySeconds;

                if (!currentlyRunning)
                {
                    ServerMgr.Instance.InvokeRepeating(_updateAction, ProcessFrequencySeconds, ProcessFrequencySeconds);
                }
            }

            public void Unload()
            {
                ServerMgr.Instance.CancelInvoke(_updateAction);
                _scheduledUpdates.Clear();
            }

            private void ProcessUpdates()
            {
                _playersProcessed.Clear();

                var now = Time.time;

                foreach (var entry in _scheduledUpdates)
                {
                    if (entry.Value > now)
                        continue;

                    var player = entry.Key;
                    _playersProcessed.Add(player);

                    if (_plugin._uiViewers.Contains(player.userID))
                    {
                        ButtonUi.UpdateUi(_plugin, player);
                    }
                }

                foreach (var player in _playersProcessed)
                {
                    _scheduledUpdates.Remove(player);
                }

                _playersProcessed.Clear();

                if (_scheduledUpdates.Count == 0)
                {
                    ServerMgr.Instance.CancelInvoke(_updateAction);
                }
            }
        }

        #endregion

        #region String Cache

        private interface IStringCache
        {
            string Get<T>(T value);
            string Get<T>(T value, Func<T, string> createString);
            string Get(bool value);
        }

        private sealed class DefaultStringCache : IStringCache
        {
            public static readonly DefaultStringCache Instance = new DefaultStringCache();

            private static class StaticStringCache<T>
            {
                private static readonly Dictionary<T, string> _cacheByValue = new Dictionary<T, string>();

                public static string Get(T value)
                {
                    string str;
                    if (!_cacheByValue.TryGetValue(value, out str))
                    {
                        str = value.ToString();
                        _cacheByValue[value] = str;
                    }

                    return str;
                }
            }

            private static class StaticStringCacheWithFactory<T>
            {
                private static readonly Dictionary<Func<T, string>, Dictionary<T, string>> _cacheByDelegate =
                    new Dictionary<Func<T, string>, Dictionary<T, string>>();

                public static string Get(T value, Func<T, string> createString)
                {
                    if (createString.Target != null)
                        throw new InvalidOperationException($"{typeof(StaticStringCacheWithFactory<T>).Name} only accepts open delegates");

                    Dictionary<T, string> cache;
                    if (!_cacheByDelegate.TryGetValue(createString, out cache))
                    {
                        cache = new Dictionary<T, string>();
                        _cacheByDelegate[createString] = cache;
                    }

                    string str;
                    if (!cache.TryGetValue(value, out str))
                    {
                        str = createString(value);
                        cache[value] = str;
                    }

                    return str;
                }
            }

            private DefaultStringCache() {}

            public string Get<T>(T value)
            {
                return StaticStringCache<T>.Get(value);
            }

            public string Get(bool value)
            {
                return value ? "true" : "false";
            }

            public string Get<T>(T value, Func<T, string> createString)
            {
                return StaticStringCacheWithFactory<T>.Get(value, createString);
            }
        }

        #endregion

        #region UI Builder

        private interface IUiSerializable
        {
            void Serialize(IUiBuilder uiBuilder);
        }

        private interface IUiBuilder
        {
            IStringCache StringCache { get; }
            void Start();
            void End();
            void StartElement();
            void EndElement();
            void StartComponent();
            void EndComponent();
            void AddField<T>(string key, T value);
            void AddField(string key, string value);
            void AddXY(string key, float x, float y);
            void AddSerializable<T>(T serializable) where T : IUiSerializable;
            void AddComponents<T>(T components) where T : IUiComponentCollection;
            string ToJson();
            byte[] GetBytes();
            void AddUi(SendInfo sendInfo);
            void AddUi(BasePlayer player);
        }

        private class UiBuilder : IUiBuilder
        {
            private static NetWrite ClientRPCStart(BaseEntity entity, string funcName)
            {
                if (Net.sv.IsConnected() && entity.net != null)
                {
                    var write = Net.sv.StartWrite();
                    write.PacketID(Message.Type.RPCMessage);
                    write.EntityID(entity.net.ID);
                    write.UInt32(StringPool.Get(funcName));
                    return write;
                }
                return null;
            }

            public static readonly UiBuilder Default = new UiBuilder(65536);

            private enum State
            {
                Empty,
                ElementList,
                Element,
                ComponentList,
                Component,
                Complete
            }

            public int Length { get; private set; }

            private const char Delimiter = ',';
            private const char Quote = '"';
            private const char Colon = ':';
            private const char Space = ' ';
            private const char OpenBracket = '[';
            private const char CloseBracket = ']';
            private const char OpenCurlyBrace = '{';
            private const char CloseCurlyBrace = '}';

            private const int MinCapacity = 1024;
            private const int DefaultCapacity = 4096;

            public IStringCache StringCache { get; }
            private char[] _chars;
            private byte[] _bytes;
            private State _state;
            private bool _needsDelimiter;

            public UiBuilder(int capacity, IStringCache stringCache)
            {
                if (capacity < MinCapacity)
                    throw new InvalidOperationException($"Capacity must be at least {MinCapacity}");

                Resize(capacity);
                StringCache = stringCache;
            }

            public UiBuilder(int capacity = DefaultCapacity) : this(capacity, DefaultStringCache.Instance) {}

            public void Start()
            {
                Reset();
                StartArray();
                _state = State.ElementList;
            }

            public void End()
            {
                ValidateState(State.ElementList);
                EndArray();
                _state = State.Complete;
            }

            public void StartElement()
            {
                ValidateState(State.ElementList);
                StartObject();
                _state = State.Element;
            }

            public void EndElement()
            {
                ValidateState(State.Element);
                EndObject();
                _state = State.ElementList;
            }

            public void StartComponent()
            {
                ValidateState(State.ComponentList);
                StartObject();
                _state = State.Component;
            }

            public void EndComponent()
            {
                ValidateState(State.Component);
                EndObject();
                _state = State.ComponentList;
            }

            public void AddField<T>(string key, T value)
            {
                AddKey(key);
                Append(StringCache.Get(value));
                _needsDelimiter = true;
            }

            public void AddField(string key, string value)
            {
                if (value == null)
                    return;

                AddKey(key);
                Append(Quote);
                Append(value);
                Append(Quote);
                _needsDelimiter = true;
            }

            public void AddXY(string key, float x, float y)
            {
                AddKey(key);
                Append(Quote);
                Append(StringCache.Get(x));
                Append(Space);
                Append(StringCache.Get(y));
                Append(Quote);
                _needsDelimiter = true;
            }

            public void AddSerializable<T>(T serializable) where T : IUiSerializable
            {
                serializable.Serialize(this);
            }

            public void AddComponents<T>(T components) where T : IUiComponentCollection
            {
                ValidateState(State.Element);
                AddKey("components");
                StartArray();
                _state = State.ComponentList;
                components.Serialize(this);
                EndArray();
                _state = State.Element;
            }

            public string ToJson()
            {
                ValidateState(State.Complete);
                return new string(_chars, 0, Length);
            }

            public byte[] GetBytes()
            {
                ValidateState(State.Complete);
                var bytes = new byte[Length];
                Buffer.BlockCopy(_bytes, 0, bytes, 0, Length);
                return bytes;
            }

            public void AddUi(SendInfo sendInfo)
            {
                var write = ClientRPCStart(CommunityEntity.ServerInstance, "AddUI");
                if (write != null)
                {
                    var byteCount = Encoding.UTF8.GetBytes(_chars, 0, Length, _bytes, 0);
                    write.BytesWithSize(_bytes, byteCount);
                    write.Send(sendInfo);
                }
            }

            public void AddUi(BasePlayer player)
            {
                AddUi(new SendInfo(player.Connection));
            }

            private void ValidateState(State desiredState)
            {
                if (_state != desiredState)
                    throw new InvalidOperationException($"Expected state {desiredState} but found {_state}");
            }

            private void ValidateState(State desiredState, State alternateState)
            {
                if (_state != desiredState && _state != alternateState)
                    throw new InvalidOperationException($"Expected state {desiredState} or {alternateState} but found {_state}");
            }

            private void Resize(int length)
            {
                Array.Resize(ref _chars, length);
                Array.Resize(ref _bytes, length * 2);
            }

            private void ResizeIfApproachingLength()
            {
                if (Length + 1024 > _chars.Length)
                {
                    Resize(_chars.Length * 2);
                }
            }

            private void Append(char @char)
            {
                _chars[Length++] = @char;
            }

            private void Append(string str)
            {
                for (var i = 0; i < str.Length; i++)
                {
                    _chars[Length + i] = str[i];
                }

                Length += str.Length;
            }

            private void AddDelimiter()
            {
                Append(Delimiter);
            }

            private void AddDelimiterIfNeeded()
            {
                if (_needsDelimiter)
                {
                    AddDelimiter();
                }
            }

            private void StartObject()
            {
                AddDelimiterIfNeeded();
                Append(OpenCurlyBrace);
                _needsDelimiter = false;
            }

            private void EndObject()
            {
                Append(CloseCurlyBrace);
                _needsDelimiter = true;
            }

            private void StartArray()
            {
                Append(OpenBracket);
                _needsDelimiter = false;
            }

            private void EndArray()
            {
                Append(CloseBracket);
                _needsDelimiter = true;
            }

            private void AddKey(string key)
            {
                ValidateState(State.Element, State.Component);
                ResizeIfApproachingLength();
                AddDelimiterIfNeeded();
                Append(Quote);
                Append(key);
                Append(Quote);
                Append(Colon);
            }

            private void Reset()
            {
                Length = 0;
                _state = State.Empty;
                _needsDelimiter = false;
            }
        }

        #endregion

        #region UI Layout

        private struct UiRect
        {
            public string Anchor;
            public float XMin;
            public float XMax;
            public float YMin;
            public float YMax;
        }

        private static class Layout
        {
            [Flags]
            public enum Option
            {
                AnchorBottom = 1 << 0,
                AnchorRight = 1 << 1,
                Vertical = 1 << 2
            }

            public const string AnchorBottomLeft = "0 0";
            public const string AnchorBottomRight = "1 0";
            public const string AnchorTopLeft = "0 1";
            public const string AnchorTopRight = "1 1";

            public const string AnchorBottomCenter = "0.5 0";
            public const string AnchorTopCenter = "0.5 1";
            public const string AnchorCenterLeft = "0 0.5";
            public const string AnchorCenterRight = "1 0.5";

            public static string DetermineAnchor(Option options)
            {
                return options.HasFlag(Option.AnchorBottom)
                    ? options.HasFlag(Option.AnchorRight) ? AnchorBottomRight : AnchorBottomLeft
                    : options.HasFlag(Option.AnchorRight) ? AnchorTopRight : AnchorTopLeft;
            }
        }

        private interface ILayoutProvider {}

        private struct StatelessLayoutProvider : ILayoutProvider
        {
            public static UiRect GetRect(int index, Layout.Option options, Vector2 size, float spacing = 0, Vector2 offset = default(Vector2))
            {
                var xMin = !options.HasFlag(Layout.Option.Vertical)
                    ? offset.x + index * (spacing + size.x)
                    : offset.x;

                var xMax = xMin + size.x;

                var yMin = options.HasFlag(Layout.Option.Vertical)
                    ? offset.y + index * (spacing + size.y)
                    : offset.y;

                var yMax = yMin + size.y;

                if (options.HasFlag(Layout.Option.AnchorRight))
                {
                    var temp = xMin;
                    xMin = -xMax;
                    xMax = -temp;
                }

                if (!options.HasFlag(Layout.Option.AnchorBottom))
                {
                    var temp = yMin;
                    yMin = -yMax;
                    yMax = -temp;
                }

                return new UiRect
                {
                    Anchor = Layout.DetermineAnchor(options),
                    XMin = xMin,
                    XMax = xMax,
                    YMin = yMin,
                    YMax = yMax,
                };
            }

            public Layout.Option Options;
            public Vector2 Offset;
            public Vector2 Size;
            public float Spacing;

            public UiRect this[int index] => GetRect(index, Options, Size, Spacing, Offset);

            public static StatelessLayoutProvider operator +(StatelessLayoutProvider layoutProvider, Vector2 vector)
            {
                layoutProvider.Offset += vector;
                return layoutProvider;
            }

            public static StatelessLayoutProvider operator -(StatelessLayoutProvider layoutProvider, Vector2 vector)
            {
                layoutProvider.Offset -= vector;
                return layoutProvider;
            }
        }

        #endregion

        #region UI Components

        private interface IUiComponent : IUiSerializable {}

        private struct UiButtonComponent : IUiComponent
        {
            private const string Type = "UnityEngine.UI.Button";

            private const string DefaultCommand = null;
            private const string DefaultClose = null;
            private const string DefaultSprite = "Assets/Content/UI/UI.Background.Tile.psd";
            private const string DefaultMaterial = "Assets/Icons/IconMaterial.mat";
            private const string DefaultColor = "1 1 1 1";
            private const Image.Type DefaultImageType = Image.Type.Simple;
            private const float DefaultFadeIn = 0;

            public string Command;
            public string Close;
            public string Sprite;
            public string Material;
            public string Color;
            public Image.Type ImageType;
            public float FadeIn;

            public void Serialize(IUiBuilder builder)
            {
                if (Sprite == default(string))
                    Sprite = DefaultSprite;

                if (Material == default(string))
                    Material = DefaultMaterial;

                if (Color == default(string))
                    Color = DefaultColor;

                if (ImageType == default(Image.Type))
                    ImageType = DefaultImageType;

                builder.StartComponent();
                builder.AddField("type", Type);

                if (Command != DefaultCommand)
                    builder.AddField("command", Command);

                if (Close != DefaultClose)
                    builder.AddField("close", Close);

                if (Sprite != DefaultSprite)
                    builder.AddField("sprite", Sprite);

                if (Material != DefaultMaterial)
                    builder.AddField("material", Material);

                if (Color != DefaultColor)
                    builder.AddField("color", Color);

                if (ImageType != DefaultImageType)
                    builder.AddField("imagetype", builder.StringCache.Get(ImageType));

                if (FadeIn != DefaultFadeIn)
                    builder.AddField("fadeIn", FadeIn);

                builder.EndComponent();
            }
        }

        private struct UiImageComponent : IUiComponent
        {
            private const string Type = "UnityEngine.UI.Image";

            private const string DefaultSprite = "Assets/Content/UI/UI.Background.Tile.psd";
            private const string DefaultMaterial = "Assets/Icons/IconMaterial.mat";
            private const string DefaultColor = "1 1 1 1";
            private const Image.Type DefaultImageType = Image.Type.Simple;
            private const string DefaultPng = null;
            private const int DefaultItemId = 0;
            private const ulong DefaultSkinId = 0;
            private const float DefaultFadeIn = 0;

            public string Sprite;
            public string Material;
            public string Color;
            public Image.Type ImageType;
            public string Png;
            public int ItemId;
            public ulong SkinId;
            public float FadeIn;

            public void Serialize(IUiBuilder builder)
            {
                if (Sprite == default(string))
                    Sprite = DefaultSprite;

                if (Material == default(string))
                    Material = DefaultMaterial;

                if (Color == default(string))
                    Color = DefaultColor;

                if (ImageType == default(Image.Type))
                    ImageType = DefaultImageType;

                builder.StartComponent();
                builder.AddField("type", Type);

                if (Sprite != DefaultSprite)
                    builder.AddField("sprite", Sprite);

                if (Material != DefaultMaterial)
                    builder.AddField("material", Material);

                if (Color != DefaultColor)
                    builder.AddField("color", Color);

                if (ImageType != DefaultImageType)
                    builder.AddField("imagetype", builder.StringCache.Get(ImageType));

                if (Png != DefaultPng)
                    builder.AddField("png", Png);

                if (ItemId != DefaultItemId)
                    builder.AddField("itemid", ItemId);

                if (SkinId != DefaultSkinId)
                    builder.AddField("skinid", SkinId);

                if (FadeIn != DefaultFadeIn)
                    builder.AddField("fadeIn", FadeIn);

                builder.EndComponent();
            }
        }

        private struct UiRawImageComponent : IUiComponent
        {
            private const string Type = "UnityEngine.UI.RawImage";

            private const string DefaultSprite = "Assets/Icons/rust.png";
            private const string DefaultColor = "1 1 1 1";
            private const string DefaultMaterial = null;
            private const string DefaultUrl = null;
            private const string DefaultPng = null;
            private const float DefaultFadeIn = 0;

            public string Sprite;
            public string Color;
            public string Material;
            public string Url;
            public string Png;
            public float FadeIn;

            public void Serialize(IUiBuilder builder)
            {
                if (Sprite == default(string))
                    Sprite = DefaultSprite;

                if (Color == default(string))
                    Color = DefaultColor;

                builder.StartComponent();
                builder.AddField("type", Type);

                if (Sprite != DefaultSprite)
                    builder.AddField("sprite", Sprite);

                if (Color != DefaultColor)
                    builder.AddField("color", Color);

                if (Material != DefaultMaterial)
                    builder.AddField("material", Material);

                if (Url != DefaultUrl)
                    builder.AddField("url", Url);

                if (Png != DefaultPng)
                    builder.AddField("png", Png);

                if (FadeIn != DefaultFadeIn)
                    builder.AddField("fadeIn", FadeIn);

                builder.EndComponent();
            }
        }

        private struct UiRectTransformComponent : IUiComponent
        {
            private const string Type = "RectTransform";

            public const string DefaultAnchorMin = "0.0 0.0";
            public const string DefaultAnchorMax = "1.0 1.0";
            public const string DefaultOffsetMin = "0.0 0.0";
            public const string DefaultOffsetMax = "1.0 1.0";

            public string AnchorMin;
            public string AnchorMax;
            public string OffsetMin;
            public string OffsetMax;

            public void Serialize(IUiBuilder builder)
            {
                if (AnchorMin == default(string))
                    AnchorMin = DefaultAnchorMin;

                if (AnchorMax == default(string))
                    AnchorMax = DefaultAnchorMax;

                if (OffsetMin == default(string))
                    OffsetMin = DefaultOffsetMin;

                if (OffsetMax == default(string))
                    OffsetMax = DefaultOffsetMax;

                builder.StartComponent();
                builder.AddField("type", Type);

                if (AnchorMin != DefaultAnchorMin)
                    builder.AddField("anchormin", AnchorMin);

                if (AnchorMax != DefaultAnchorMax)
                    builder.AddField("anchormax", AnchorMax);

                if (OffsetMin != DefaultOffsetMin)
                    builder.AddField("offsetmin", OffsetMin);

                if (OffsetMax != DefaultOffsetMax)
                    builder.AddField("offsetmax", OffsetMax);

                builder.EndComponent();
            }
        }

        private struct UiTextComponent : IUiComponent
        {
            private const string Type = "UnityEngine.UI.Text";

            private const string DefaultText = "Text";
            private const int DefaultFontSize = 14;
            private const string DefaultFont = "RobotoCondensed-Bold.ttf";
            private const TextAnchor DefaultTextAlign = TextAnchor.UpperLeft;
            private const string DefaultColor = "1 1 1 1";
            private const VerticalWrapMode DefaultVerticalWrapMode = VerticalWrapMode.Truncate;
            private const float DefaultFadeIn = 0;

            public string Text;
            public int FontSize;
            public string Font;
            public TextAnchor TextAlign;
            public string Color;
            public VerticalWrapMode VerticalWrapMode;
            public float FadeIn;

            public void Serialize(IUiBuilder builder)
            {
                if (Text == default(string))
                    Text = DefaultText;

                if (FontSize == default(int))
                    FontSize = DefaultFontSize;

                if (Font == default(string))
                    Font = DefaultFont;

                if (TextAlign == default(TextAnchor))
                    TextAlign = DefaultTextAlign;

                if (Color == default(string))
                    Color = DefaultColor;

                if (VerticalWrapMode == default(VerticalWrapMode))
                    VerticalWrapMode = DefaultVerticalWrapMode;

                builder.StartComponent();
                builder.AddField("type", Type);

                if (Text != DefaultText)
                    builder.AddField("text", Text);

                if (FontSize != DefaultFontSize)
                    builder.AddField("fontSize", FontSize);

                if (Font != DefaultFont)
                    builder.AddField("font", Font);

                if (TextAlign != DefaultTextAlign)
                    builder.AddField("align", builder.StringCache.Get(TextAlign));

                if (Color != DefaultColor)
                    builder.AddField("color", Color);

                if (VerticalWrapMode != DefaultVerticalWrapMode)
                    builder.AddField("verticalOverflow", builder.StringCache.Get(VerticalWrapMode));

                if (FadeIn != DefaultFadeIn)
                    builder.AddField("fadeIn", FadeIn);

                builder.EndComponent();
            }
        }

        private struct UiOutlineComponent : IUiComponent
        {
            private const string Type = "UnityEngine.UI.Outline";

            private const string DefaultColor = "1 1 1 1";
            private const string DefaultDistance = "1.0 -1.0";
            private const bool DefaultUseGraphicAlpha = false;

            public string Color;
            public string Distance;
            public bool UseGraphicAlpha;

            public void Serialize(IUiBuilder builder)
            {
                if (Color == default(string))
                    Color = DefaultColor;

                if (Distance == default(string))
                    Distance = DefaultDistance;

                builder.StartComponent();
                builder.AddField("type", Type);

                if (Color != DefaultColor)
                    builder.AddField("color", Color);

                if (Distance != DefaultDistance)
                    builder.AddField("distance", Distance);

                if (UseGraphicAlpha != DefaultUseGraphicAlpha)
                    builder.AddField("useGraphicAlpha", UseGraphicAlpha);

                builder.EndComponent();
            }
        }

        // Custom component for handling positions.private struct UiRectComponent : IUiComponent
        {
            private const string Type = "RectTransform";

            public const string DefaultAnchorMin = "0.0 0.0";

            private const string DefaultAnchor = "0 0";

            public UiRect Rect;

            public UiRectComponent(UiRect rect)
            {
                Rect = rect;
            }

            public UiRectComponent(float x, float y, string anchor = DefaultAnchor)
            {
                Rect = new UiRect
                {
                    Anchor = anchor,
                    XMin = x,
                    XMax = x,
                    YMin = y,
                    YMax = y
                };
     
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху