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.Linq;
using Newtonsoft.Json.Linq;
using Sandbox.Game.Entities;
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame;
using VRageMath;
using Terminal = Sandbox.ModAPI.Ingame.IMyTerminalBlock;
using SlimBlock = Sandbox.Game.Entities.Cube.MySlimBlock;
using ShipConnector = Sandbox.Game.Entities.Cube.MyShipConnector;

namespace XFE.SeAgent.Plugin.Game
{
    public sealed partial class GameDebugApi
    {
        private JObject ListGrids(JObject args)
        {
            var grids = MyEntities.GetEntities().OfType<MyCubeGrid>().Where(g => !g.Closed).OrderBy(g => g.EntityId).ToList();
            string name = (string)args["name"];
            if (!string.IsNullOrEmpty(name)) grids = grids.Where(g => (g.DisplayName ?? "").IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
            int offset = Math.Max(0, (int?)args["offset"] ?? 0);
            var items = new JArray(grids.Skip(offset).Take(Limit(args)).Select(DescribeGrid));
            return new JObject { ["grids"] = items, ["total"] = grids.Count, ["offset"] = offset, ["truncated"] = offset + items.Count < grids.Count };
        }

        private static JObject DescribeGrid(MyCubeGrid grid)
        {
            var result = new JObject { ["entityId"] = Sid(grid.EntityId), ["name"] = grid.DisplayName, ["isStatic"] = grid.IsStatic,
                ["gridSize"] = grid.GridSize, ["blockCount"] = grid.GetBlocks().Count, ["pose"] = Pose(grid.WorldMatrix), ["bounds"] = Bounds(grid.PositionComp.WorldAABB) };
            if (grid.Physics != null)
            {
                result["linearVelocity"] = Vec(grid.Physics.LinearVelocity);
                result["angularVelocity"] = Vec(grid.Physics.AngularVelocity);
                result["speed"] = grid.Physics.LinearVelocity.Length();
            }
            return result;
        }

        private static JObject DescribeGridWithIntegrity(MyCubeGrid grid)
        {
            var result = DescribeGrid(grid);
            // Include armor as well as terminal blocks. Bound work on unusually large grids;
            // ordinary grids.list queries do not perform this scan.
            const int maximumBlocks = 20000;
            int inspected = 0, damaged = 0, deformed = 0, incomplete = 0, affected = 0;
            double integrity = 0, buildIntegrity = 0, maxIntegrity = 0, currentDamage = 0, pendingDamage = 0;
            var damageLocations = new JArray();
            foreach (var block in grid.GetBlocks())
            {
                if (inspected >= maximumBlocks) break;
                inspected++;
                integrity += block.Integrity; buildIntegrity += block.BuildIntegrity; maxIntegrity += block.MaxIntegrity;
                currentDamage += block.CurrentDamage; pendingDamage += block.AccumulatedDamage;
                bool isDamaged = block.CurrentDamage > 0 || block.AccumulatedDamage > 0;
                if (isDamaged) damaged++;
                if (block.HasDeformation) deformed++;
                if (block.BuildIntegrity < block.MaxIntegrity) incomplete++;
                if (isDamaged || block.HasDeformation) affected++;
                if ((isDamaged || block.HasDeformation) && damageLocations.Count < 32)
                {
                    var detail = DescribeIntegrity(block);
                    detail["positionInGrid"] = Vec(block.Position);
                    damageLocations.Add(detail);
                }
            }
            result["integrity"] = new JObject { ["inspectedBlockCount"] = inspected,
                ["truncated"] = inspected < grid.GetBlocks().Count, ["current"] = integrity,
                ["build"] = buildIntegrity, ["maximum"] = maxIntegrity, ["currentDamage"] = currentDamage,
                ["accumulatedDamage"] = pendingDamage, ["damagedBlockCount"] = damaged,
                ["deformedBlockCount"] = deformed, ["incompleteBlockCount"] = incomplete,
                ["damageLocations"] = damageLocations, ["damageLocationsTruncated"] = affected > damageLocations.Count };
            return result;
        }

        private static JObject DescribeIntegrity(SlimBlock block)
        {
            return new JObject { ["current"] = block.Integrity, ["build"] = block.BuildIntegrity,
                ["maximum"] = block.MaxIntegrity, ["currentDamage"] = block.CurrentDamage,
                ["accumulatedDamage"] = block.AccumulatedDamage, ["hasDeformation"] = block.HasDeformation };
        }

        private IEnumerable<Terminal> SelectBlocks(JObject args)
        {
            if (args["entityIds"] is JArray ids)
            {
                if (ids.Count > 256) throw new ArgumentException("entityIds accepts at most 256 blocks.");
                return ids.Select(id => Block(Id(new JObject { ["id"] = id.DeepClone() }, "id"))).Distinct().ToList();
            }
            IEnumerable<MyCubeGrid> grids = args["gridId"] != null ? new[] { Grid(Id(args, "gridId")) } : MyEntities.GetEntities().OfType<MyCubeGrid>();
            IEnumerable<Terminal> blocks = grids.Where(g => !g.Closed).SelectMany(g => g.GetFatBlocks().OfType<Terminal>()).Where(b => !b.Closed);
            string name = (string)args["name"], type = (string)args["type"];
            if (!string.IsNullOrEmpty(name)) blocks = blocks.Where(b => (b.CustomName ?? "").IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0);
            if (!string.IsNullOrEmpty(type)) blocks = blocks.Where(b => b.BlockDefinition.TypeIdString.IndexOf(type, StringComparison.OrdinalIgnoreCase) >= 0 || b.BlockDefinition.SubtypeName.IndexOf(type, StringComparison.OrdinalIgnoreCase) >= 0);
            return blocks.OrderBy(b => b.EntityId);
        }

        private JObject ListBlocks(JObject args)
        {
            var blocks = SelectBlocks(args).ToList();
            int offset = Math.Max(0, (int?)args["offset"] ?? 0);
            var array = new JArray(blocks.Skip(offset).Take(Limit(args)).Select(b => DescribeBlock(b, false)));
            return new JObject { ["blocks"] = array, ["total"] = blocks.Count, ["offset"] = offset, ["truncated"] = offset + array.Count < blocks.Count };
        }

        private static JObject DescribeBlock(Terminal block, bool details)
        {
            var result = new JObject { ["entityId"] = Sid(block.EntityId), ["gridId"] = Sid(block.CubeGrid.EntityId), ["name"] = block.CustomName,
                ["type"] = block.BlockDefinition.TypeIdString, ["subtype"] = block.BlockDefinition.SubtypeName, ["functional"] = block.IsFunctional,
                ["working"] = block.IsWorking, ["ownerId"] = Sid(block.OwnerId), ["positionInGrid"] = Vec(block.Position), ["pose"] = Pose(block.WorldMatrix),
                ["bounds"] = Bounds(block.WorldAABB), ["inventoryCount"] = block.InventoryCount };
            if (block is IMyFunctionalBlock functional) result["enabled"] = functional.Enabled;
            var slim = (block.CubeGrid as MyCubeGrid)?.GetCubeBlock(block.Position);
            if (slim != null) result["integrity"] = DescribeIntegrity(slim);
            if (details) { result["customData"] = block.CustomData ?? ""; result["detailedInfo"] = Clip(block.DetailedInfo, 32768); result["customInfo"] = Clip(block.CustomInfo, 32768); }
            return result;
        }

        private JObject Telemetry(JObject args)
        {
            int limit = Limit(args, 256, 1024);
            var selected = SelectBlocks(args).ToList();
            var blocks = selected.Take(limit).ToList();
            var result = new JArray();
            foreach (var block in blocks)
            {
                var item = DescribeBlock(block, false);
                try { AddTelemetry(item, block, (bool?)args["includeInventoryItems"] ?? true, (bool?)args["includeScreens"] ?? true); }
                catch (Exception error) { item["telemetryError"] = error.GetType().Name + ": " + error.Message; }
                result.Add(item);
            }
            var grids = blocks.Select(b => b.CubeGrid.EntityId).Distinct().Select(id => DescribeGridWithIntegrity(Grid(id)));
            return new JObject { ["utc"] = DateTime.UtcNow, ["frame"] = RequireWorld().GameplayFrameCounter, ["grids"] = new JArray(grids), ["blocks"] = result, ["total"] = selected.Count, ["truncated"] = selected.Count > limit };
        }

        private static void AddTelemetry(JObject item, Terminal block, bool includeInventoryItems, bool includeScreens)
        {
            if (block is IMyConveyorSorter sorter) item["sorter"] = DescribeSorter(sorter);
            if (block is IMyBatteryBlock battery) item["battery"] = new JObject { ["storedMWh"] = battery.CurrentStoredPower, ["maxStoredMWh"] = battery.MaxStoredPower, ["inputMW"] = battery.CurrentInput, ["outputMW"] = battery.CurrentOutput, ["chargeMode"] = battery.ChargeMode.ToString(), ["charging"] = battery.IsCharging };
            if (block is IMyThrust thrust) item["thrust"] = new JObject { ["currentN"] = thrust.CurrentThrust, ["maximumN"] = thrust.MaxThrust, ["maxEffectiveN"] = thrust.MaxEffectiveThrust, ["overrideN"] = thrust.ThrustOverride, ["overrideRatio"] = thrust.ThrustOverridePercentage, ["gridDirection"] = Vec(thrust.GridThrustDirection), ["forceDirection"] = Vec(thrust.WorldMatrix.Backward) };
            if (block is IMyGyro gyro) item["gyro"] = new JObject { ["override"] = gyro.GyroOverride, ["power"] = gyro.GyroPower, ["yaw"] = gyro.Yaw, ["pitch"] = gyro.Pitch, ["roll"] = gyro.Roll };
            if (block is IMyShipConnector connector)
            {
                var status = connector.Status;
                var value = new JObject { ["status"] = status.ToString(),
                    ["connected"] = status == MyShipConnectorStatus.Connected,
                    ["connectable"] = status == MyShipConnectorStatus.Connectable,
                    ["otherConnectorId"] = connector.OtherConnector == null ? null : Sid(connector.OtherConnector.EntityId),
                    ["throwOut"] = connector.ThrowOut, ["collectAll"] = connector.CollectAll, ["pullStrength"] = connector.PullStrength };
                item["connector"] = value;
                if (block is ShipConnector actual)
                {
                    var point = actual.ConstraintPositionWorld();
                    value["constraintPosition"] = Vec(point);
                    value["inConstraint"] = actual.InConstraint;
                    value["magnetized"] = actual.InConstraint && status != MyShipConnectorStatus.Connected;
                    value["isSmallConnector"] = actual.IsSmallConnector;
                    value["tradingEnabled"] = actual.TradingEnabled.Value;
                    value["protectedFromLockingByTrading"] = actual.IsProtectedFromLockingByTrading();
                    var other = actual.Other;
                    value["constraintOtherConnectorId"] = other == null ? null : Sid(other.EntityId);
                    if (other != null)
                    {
                        var otherPoint = other.ConstraintPositionWorld();
                        value["otherConstraintPosition"] = Vec(otherPoint);
                        value["constraintDistance"] = Vector3D.Distance(point, otherPoint);
                    }
                }
            }
            if (block is IMyCameraBlock camera) item["camera"] = new JObject { ["enabledRaycast"] = camera.EnableRaycast, ["availableScanRange"] = camera.AvailableScanRange, ["coneLimitDegrees"] = camera.RaycastConeLimit, ["distanceLimit"] = camera.RaycastDistanceLimit };
            if (block is IMyGasTank tank) item["gasTank"] = new JObject { ["capacity"] = tank.Capacity, ["filledRatio"] = tank.FilledRatio, ["stockpile"] = tank.Stockpile };
            if (block is IMyShipController controller)
            {
                var velocities = controller.GetShipVelocities(); var mass = controller.CalculateShipMass();
                item["flight"] = new JObject { ["linearVelocity"] = Vec(velocities.LinearVelocity), ["angularVelocity"] = Vec(velocities.AngularVelocity),
                    ["speed"] = controller.GetShipSpeed(), ["naturalGravity"] = Vec(controller.GetNaturalGravity()), ["artificialGravity"] = Vec(controller.GetArtificialGravity()),
                    ["totalMassKg"] = mass.TotalMass, ["physicalMassKg"] = mass.PhysicalMass, ["baseMassKg"] = mass.BaseMass,
                    ["centerOfMass"] = Vec(controller.CenterOfMass), ["dampeners"] = controller.DampenersOverride, ["underControl"] = controller.IsUnderControl,
                    ["controlThrusters"] = controller.ControlThrusters, ["moveIndicator"] = Vec(controller.MoveIndicator), ["rotationIndicator"] = new JObject { ["x"] = controller.RotationIndicator.X, ["y"] = controller.RotationIndicator.Y }, ["rollIndicator"] = controller.RollIndicator };
            }
            if (block is Sandbox.ModAPI.IMyProgrammableBlock pb)
            {
                var instance = PbField(pb, "m_instance") as Sandbox.ModAPI.IMyGridProgram;
                item["programmableBlock"] = new JObject { ["sha256"] = Sha(pb.ProgramData), ["compileErrors"] = pb.HasCompileErrors, ["hasInstance"] = instance != null,
                    ["runtime"] = instance == null ? null : Runtime(instance.Runtime), ["echo"] = Clip(PbField(pb, "m_echoOutput")?.ToString(), 8192), ["terminationReason"] = PbField(pb, "m_terminationReason")?.ToString() };
            }
            if (block.HasInventory)
            {
                var inventories = new JArray();
                for (int i = 0; i < Math.Min(block.InventoryCount, 16); i++)
                {
                    var inventory = block.GetInventory(i);
                    var inv = new JObject { ["index"] = i, ["massKg"] = (double)inventory.CurrentMass, ["volumeM3"] = (double)inventory.CurrentVolume, ["maxVolumeM3"] = (double)inventory.MaxVolume, ["itemCount"] = inventory.ItemCount };
                    if (includeInventoryItems)
                    {
                        var items = new List<MyInventoryItem>(); inventory.GetItems(items, null);
                        inv["items"] = new JArray(items.Take(128).Select(value => new JObject { ["itemId"] = value.ItemId, ["type"] = value.Type.TypeId, ["subtype"] = value.Type.SubtypeId, ["amount"] = (double)value.Amount }));
                        inv["truncated"] = items.Count > 128;
                    }
                    inventories.Add(inv);
                }
                item["inventories"] = inventories;
                item["cargoInventory"] = block is IMyCargoContainer || block is IMyShipDrill || block is IMyShipConnector || block is IMyConveyorSorter;
            }
            if (includeScreens)
            {
                var screens = new JArray();
                if (block is IMyTextSurfaceProvider provider) for (int i = 0; i < Math.Min(provider.SurfaceCount, 16); i++) screens.Add(Surface(provider.GetSurface(i), i));
                else if (block is IMyTextSurface surface) screens.Add(Surface(surface, 0));
                if (screens.Count > 0) item["screens"] = screens;
            }
        }

        private static JObject Surface(IMyTextSurface surface, int index)
        {
            return new JObject { ["index"] = index, ["name"] = surface.Name, ["displayName"] = surface.DisplayName, ["contentType"] = surface.ContentType.ToString(), ["text"] = Clip(surface.GetText(), 8192), ["script"] = surface.Script, ["surfaceSize"] = new JObject { ["x"] = surface.SurfaceSize.X, ["y"] = surface.SurfaceSize.Y } };
        }
    }
}