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;

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 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;
            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 => DescribeGrid(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 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) item["connector"] = new JObject { ["status"] = connector.Status.ToString(), ["otherConnectorId"] = connector.OtherConnector == null ? null : Sid(connector.OtherConnector.EntityId), ["throwOut"] = connector.ThrowOut, ["collectAll"] = connector.CollectAll, ["pullStrength"] = connector.PullStrength };
            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;
            }
            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 } };
        }
    }
}