XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFE.SpaceEngineers.AgentBridge

【SpaceEngineer】AI调试插件

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json.Linq;
using Sandbox.Definitions;
using Sandbox.ModAPI.Ingame;
using VRage;
using VRage.Game;
using VRage.Game.ModAPI.Ingame;
using Terminal = Sandbox.ModAPI.Ingame.IMyTerminalBlock;

namespace XFE.SeAgent.Plugin.Game
{
    public sealed partial class GameDebugApi
    {
        private JObject InventoryRoute(JObject args)
        {
            InventoryParameters(args, "sourceId", "targetId", "sourceInventoryIndex", "targetInventoryIndex", "itemType");
            var sourceBlock = Block(InventoryEntityId(args, "sourceId"));
            var targetBlock = Block(InventoryEntityId(args, "targetId"));
            int sourceIndex = InventoryIndex(args, "sourceInventoryIndex");
            int targetIndex = InventoryIndex(args, "targetInventoryIndex");
            var source = InventoryAt(sourceBlock, sourceIndex);
            var target = InventoryAt(targetBlock, targetIndex);
            var definition = InventoryDefinition(args["itemType"] ?? new JValue("MyObjectBuilder_Ore/Stone"), false);
            var itemType = MyItemType.Parse(definition.ToString());
            return new JObject
            {
                ["source"] = InventorySummary(sourceBlock, source, sourceIndex),
                ["target"] = InventorySummary(targetBlock, target, targetIndex),
                ["itemType"] = definition.ToString(), ["amountTested"] = 1,
                ["isConnectedTo"] = source.IsConnectedTo(target),
                ["canTransferItemTo"] = source.CanTransferItemTo(target, itemType),
                ["targetCanAddOne"] = target.CanItemsBeAdded((MyFixedPoint)1, itemType)
            };
        }

        private JObject SetSorterFilters(JObject args)
        {
            InventoryParameters(args, "entityId", "mode", "items");
            long entityId = InventoryEntityId(args, "entityId");
            string modeText = Text(args, "mode");
            MyConveyorSorterMode mode;
            if (modeText == "Whitelist") mode = MyConveyorSorterMode.Whitelist;
            else if (modeText == "Blacklist") mode = MyConveyorSorterMode.Blacklist;
            else throw new ArgumentException("mode must be Whitelist or Blacklist.");
            var items = args["items"] as JArray;
            if (items == null || items.Count > 128) throw new ArgumentException("items must be an array of at most 128 filters; an empty array explicitly clears the filters.");
            var filters = new List<MyInventoryItemFilter>();
            var keys = new HashSet<string>(StringComparer.Ordinal);
            foreach (var token in items)
            {
                var item = token as JObject ?? throw new ArgumentException("Each filter must be an object with itemId and optional allSubTypes.");
                InventoryParameters(item, "itemId", "allSubTypes");
                bool allSubTypes = false;
                if (item["allSubTypes"] != null)
                {
                    if (item["allSubTypes"].Type != JTokenType.Boolean) throw new ArgumentException("allSubTypes must be a JSON boolean.");
                    allSubTypes = (bool)item["allSubTypes"];
                }
                var definition = InventoryDefinition(item["itemId"], allSubTypes);
                var filter = new MyInventoryItemFilter(definition, allSubTypes);
                if (!keys.Add(FilterKey(filter))) throw new ArgumentException("Duplicate sorter filter.");
                filters.Add(filter);
            }
            var sorter = Block(entityId) as IMyConveyorSorter ?? throw new ArgumentException("Entity is not a conveyor sorter.");
            var previous = DescribeSorter(sorter);
            // All inputs have been validated before this sole hardware mutation. SetFilter
            // replaces only mode/list; DrainAll, Enabled, names and other sorters are untouched.
            sorter.SetFilter(mode, filters);
            var actual = new List<MyInventoryItemFilter>();
            sorter.GetFilterList(actual);
            bool matches = sorter.Mode == mode && actual.Count == filters.Count && actual.All(value => keys.Contains(FilterKey(value)));
            _log("Replace sorter filters on " + Sid(entityId) + ": " + modeText + ", " + filters.Count + " entries; readback matches=" + matches);
            return new JObject { ["entityId"] = Sid(entityId), ["submitted"] = true,
                ["readbackMatchesRequest"] = matches, ["previous"] = previous, ["sorter"] = DescribeSorter(sorter, actual) };
        }

        private static JObject DescribeSorter(IMyConveyorSorter sorter, List<MyInventoryItemFilter> filters = null)
        {
            if (filters == null) { filters = new List<MyInventoryItemFilter>(); sorter.GetFilterList(filters); }
            return new JObject { ["mode"] = sorter.Mode.ToString(), ["drainAll"] = sorter.DrainAll,
                ["items"] = new JArray(filters.Take(128).Select(value => new JObject {
                    ["itemId"] = value.ItemId.ToString(), ["allSubTypes"] = value.AllSubTypes })),
                ["total"] = filters.Count, ["truncated"] = filters.Count > 128 };
        }

        private static string FilterKey(MyInventoryItemFilter filter)
        {
            return filter.AllSubTypes ? filter.ItemId.TypeId.ToString() + "/*" : filter.ItemId.ToString();
        }

        private static MyDefinitionId InventoryDefinition(JToken token, bool allSubTypes)
        {
            if (token == null || token.Type != JTokenType.String) throw new ArgumentException("itemId/itemType must be a string.");
            string text = (string)token;
            if (text.Length == 0 || text.Length > 256 || text.Any(char.IsControl) || text != text.Trim())
                throw new ArgumentException("itemId/itemType must contain 1..256 characters without surrounding whitespace or control characters.");
            int slash = text.IndexOf('/');
            if (slash < 0 && allSubTypes) { text += "/"; slash = text.Length - 1; }
            if (slash <= 0 || text.IndexOf('/', slash + 1) >= 0 || !text.StartsWith("MyObjectBuilder_", StringComparison.Ordinal))
                throw new ArgumentException("Use MyObjectBuilder_Type/Subtype; an allSubTypes filter may use MyObjectBuilder_Type alone.");
            string subtype = text.Substring(slash + 1);
            if (allSubTypes ? subtype.Length > 0 && subtype != "(null)" : subtype.Length == 0 || subtype == "(null)")
                throw new ArgumentException("allSubTypes requires an empty subtype; a specific item requires a nonempty subtype.");
            if (text.Substring(0, slash).Any(c => !(char.IsLetterOrDigit(c) || c == '_')) || subtype != subtype.Trim())
                throw new ArgumentException("Invalid item definition syntax.");
            MyDefinitionId definition;
            if (!MyDefinitionId.TryParse(text, out definition) || !typeof(MyObjectBuilder_PhysicalObject).IsAssignableFrom((Type)definition.TypeId))
                throw new ArgumentException("Item type is not a registered physical inventory item type.");
            if (!allSubTypes && MyDefinitionManager.Static.TryGetPhysicalItemDefinition(definition) == null)
                throw new ArgumentException("Item definition does not exist in the current world.");
            return definition;
        }

        private static long InventoryEntityId(JObject args, string key)
        {
            var token = args[key];
            if (token == null || (token.Type != JTokenType.String && token.Type != JTokenType.Integer))
                throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
            string text = (string)token;
            int firstDigit = text.StartsWith("-", StringComparison.Ordinal) ? 1 : 0;
            if (text.Length <= firstDigit || text.Length > 20 || text.Skip(firstDigit).Any(c => c < '0' || c > '9'))
                throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
            long id;
            if (!long.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out id) || id == 0)
                throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
            return id;
        }

        private static int InventoryIndex(JObject args, string key)
        {
            var token = args[key];
            if (token == null) return 0;
            int index;
            if (token.Type != JTokenType.Integer || !int.TryParse((string)token, NumberStyles.None, CultureInfo.InvariantCulture, out index) || index < 0 || index > 15)
                throw new ArgumentException(key + " must be a JSON integer from 0 to 15.");
            return index;
        }

        private static IMyInventory InventoryAt(Terminal block, int index)
        {
            if (!block.HasInventory || index >= block.InventoryCount) throw new ArgumentException("Inventory index does not exist on block " + Sid(block.EntityId) + ".");
            return block.GetInventory(index) ?? throw new ArgumentException("Block inventory is unavailable.");
        }

        private static JObject InventorySummary(Terminal block, IMyInventory inventory, int index)
        {
            return new JObject { ["entityId"] = Sid(block.EntityId), ["gridId"] = Sid(block.CubeGrid.EntityId), ["index"] = index,
                ["massKg"] = (double)inventory.CurrentMass, ["volumeM3"] = (double)inventory.CurrentVolume,
                ["maxVolumeM3"] = (double)inventory.MaxVolume, ["freeVolumeM3"] = Math.Max(0, (double)inventory.MaxVolume - (double)inventory.CurrentVolume),
                ["isFull"] = inventory.IsFull, ["canPutItems"] = inventory.CanPutItems, ["itemCount"] = inventory.ItemCount };
        }

        private static void InventoryParameters(JObject args, params string[] names)
        {
            foreach (var property in args.Properties())
                if (!names.Contains(property.Name, StringComparer.Ordinal)) throw new ArgumentException("Unknown parameter: " + property.Name);
        }
    }
}