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().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 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 grids = args["gridId"] != null ? new[] { Grid(Id(args, "gridId")) } : MyEntities.GetEntities().OfType(); IEnumerable blocks = grids.Where(g => !g.Closed).SelectMany(g => g.GetFatBlocks().OfType()).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(); 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 } }; } } }