diff --git a/README.md b/README.md index b1cd5f1..2bb598f 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,11 @@ - [x] invoke command (with direct response for optional delay) - [x] send message (custom color author channel) - [ ] grids - - [ ] get all (grouped by physical factor) - - [ ] get by entity id (basic info + counts of each terminal block subtype) - - [ ] toggle power state by entity id - - [ ] delete by entity id - - [ ] delete the entire group by entity id of one grid + - [x] get all + - [x] get grids in group by id + - [x] get by entity id (basic info) + - [x] toggle power state by entity id + - [x] delete by entity id + - [x] delete the entire group by entity id of one grid - [ ] get block list - [ ] filtering: all, terminal, functional (is enabled, is working), type, subtype diff --git a/TorchRemote.Models/Responses/EntityWorldData.cs b/TorchRemote.Models/Responses/EntityWorldData.cs new file mode 100644 index 0000000..84740bf --- /dev/null +++ b/TorchRemote.Models/Responses/EntityWorldData.cs @@ -0,0 +1,5 @@ +using TorchRemote.Models.Shared; + +namespace TorchRemote.Models.Responses; + +public record EntityWorldData(Vector3 Position, Vector3 Forward, Vector3 Up, Vector3 LinearVelocity, Vector3 AngularVelocity); \ No newline at end of file diff --git a/TorchRemote.Models/Responses/GridInfo.cs b/TorchRemote.Models/Responses/GridInfo.cs new file mode 100644 index 0000000..fc37dbd --- /dev/null +++ b/TorchRemote.Models/Responses/GridInfo.cs @@ -0,0 +1,9 @@ +namespace TorchRemote.Models.Responses; + +public record GridInfo(long Id, + string Name, + EntityWorldData WorldData, + long? BiggestOwner, + ICollection? Owners, + int BlockCount, + int Pcu); \ No newline at end of file diff --git a/TorchRemote.Models/Shared/Vector3.cs b/TorchRemote.Models/Shared/Vector3.cs new file mode 100644 index 0000000..ccc455a --- /dev/null +++ b/TorchRemote.Models/Shared/Vector3.cs @@ -0,0 +1,6 @@ +namespace TorchRemote.Models.Shared; + +public record Vector3(float X, float Y, float Z) +{ + public static Vector3 Zero => new Vector3(0, 0, 0); +} \ No newline at end of file diff --git a/TorchRemote.Models/TorchRemote.Models.csproj b/TorchRemote.Models/TorchRemote.Models.csproj index 7706a08..0c03f48 100644 --- a/TorchRemote.Models/TorchRemote.Models.csproj +++ b/TorchRemote.Models/TorchRemote.Models.csproj @@ -14,6 +14,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/TorchRemote.Models/Trash/IsExternalInit.cs b/TorchRemote.Models/Trash/IsExternalInit.cs deleted file mode 100644 index 1b4fd00..0000000 --- a/TorchRemote.Models/Trash/IsExternalInit.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("TorchRemote.Plugin")] -// ReSharper disable once CheckNamespace -namespace System.Runtime.CompilerServices; -internal class IsExternalInit { } \ No newline at end of file diff --git a/TorchRemote.Plugin/Controllers/GridsController.cs b/TorchRemote.Plugin/Controllers/GridsController.cs new file mode 100644 index 0000000..deab025 --- /dev/null +++ b/TorchRemote.Plugin/Controllers/GridsController.cs @@ -0,0 +1,121 @@ +using System.Net; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; +using Sandbox.Game.Entities; +using Torch.API; +using TorchRemote.Models.Responses; +using TorchRemote.Plugin.Utils; +using VRageMath; +using Vector3 = TorchRemote.Models.Shared.Vector3; + +namespace TorchRemote.Plugin.Controllers; + +public class GridsController : WebApiController +{ + private const string RootPath = "/grids"; + + [Route(HttpVerbs.Get, RootPath)] + public Task> GetAsync() + { + if (Statics.Torch.GameState is not TorchGameState.Loaded) + throw new HttpException(HttpStatusCode.ServiceUnavailable); + + return Statics.Torch.InvokeAsync>(() => + { + return MyCubeGridGroups.Static.Physical.Groups.SelectMany(b => b.Nodes.Select(n => n.NodeData.EntityId)) + .ToArray(); + }); + } + + [Route(HttpVerbs.Get, $"{RootPath}/{{id}}")] + public Task GetAsync(long id) + { + if (Statics.Torch.GameState is not TorchGameState.Loaded) + throw new HttpException(HttpStatusCode.ServiceUnavailable); + + return Statics.Torch.InvokeAsync(() => + { + if (!MyEntities.TryGetEntityById(id, out MyCubeGrid grid)) + throw HttpException.NotFound("Grid with given id does not exist", id); + + return GetInfo(grid); + }); + } + + [Route(HttpVerbs.Post, $"{RootPath}/{{id}}/power")] + public Task PostPowerAsync(long id, [QueryField] bool powered) + { + if (Statics.Torch.GameState is not TorchGameState.Loaded) + throw new HttpException(HttpStatusCode.ServiceUnavailable); + + return Statics.Torch.InvokeAsync(() => + { + if (!MyEntities.TryGetEntityById(id, out MyCubeGrid grid)) + throw HttpException.NotFound("Grid with given id does not exist", id); + + if (grid.IsPowered != powered) + grid.SwitchPower(); + }); + } + + [Route(HttpVerbs.Delete, $"{RootPath}/{{id}}")] + public Task DeleteAsync(long id) + { + if (Statics.Torch.GameState is not TorchGameState.Loaded) + throw new HttpException(HttpStatusCode.ServiceUnavailable); + + return Statics.Torch.InvokeAsync(() => + { + if (!MyEntities.TryGetEntityById(id, out MyCubeGrid grid)) + throw HttpException.NotFound("Grid with given id does not exist", id); + + grid.Close(); + }); + } + + [Route(HttpVerbs.Delete, $"{RootPath}/{{id}}/group")] + public Task DeleteGroupAsync(long id) + { + if (Statics.Torch.GameState is not TorchGameState.Loaded) + throw new HttpException(HttpStatusCode.ServiceUnavailable); + + return Statics.Torch.InvokeAsync(() => + { + if (!MyEntities.TryGetEntityById(id, out MyCubeGrid grid)) + throw HttpException.NotFound("Grid with given id does not exist", id); + + foreach (var node in MyCubeGridGroups.Static.Physical.GetGroup(grid).Nodes.ToArray()) + { + node.NodeData.Close(); + } + }); + } + + [Route(HttpVerbs.Get, $"{RootPath}/{{id}}/group")] + public Task> GetGroupAsync(long id) + { + if (Statics.Torch.GameState is not TorchGameState.Loaded) + throw new HttpException(HttpStatusCode.ServiceUnavailable); + + return Statics.Torch.InvokeAsync>(() => + { + if (!MyEntities.TryGetEntityById(id, out MyCubeGrid grid)) + throw HttpException.NotFound("Grid with given id does not exist", id); + + return MyCubeGridGroups.Static.Physical.GetGroup(grid).Nodes.Select(n => GetInfo(n.NodeData)).ToArray(); + }); + } + + private static GridInfo GetInfo(MyCubeGrid grid) + { + var matrixD = grid.PositionComp?.WorldMatrixRef ?? MatrixD.Zero; + return new GridInfo(grid.EntityId, grid.DisplayName, + new(matrixD.Translation.ToNumerics(), matrixD.Forward.ToNumerics(), + matrixD.Up.ToNumerics(), grid.Physics?.LinearVelocity.ToNumerics() ?? Vector3.Zero, + grid.Physics?.AngularVelocity.ToNumerics() ?? Vector3.Zero), + grid.BigOwners.Count > 0 ? grid.BigOwners[0] : null, + grid.BigOwners.Count + grid.SmallOwners.Count > 0 ? grid.BigOwners.Concat(grid.SmallOwners).ToArray() : null, + grid.BlocksCount, grid.BlocksPCU); + } +} \ No newline at end of file diff --git a/TorchRemote.Plugin/Managers/ApiServerManager.cs b/TorchRemote.Plugin/Managers/ApiServerManager.cs index b73968e..0adecc5 100644 --- a/TorchRemote.Plugin/Managers/ApiServerManager.cs +++ b/TorchRemote.Plugin/Managers/ApiServerManager.cs @@ -65,7 +65,8 @@ public class ApiServerManager : Manager .WithController() .WithController() .WithController() - .WithController()) + .WithController() + .WithController()) .WithModule(new LogsModule("/api/live/logs", true)) .WithModule(chatModule); } diff --git a/TorchRemote.Plugin/TorchRemote.Plugin.csproj b/TorchRemote.Plugin/TorchRemote.Plugin.csproj index 9dc6dbd..80fce4a 100644 --- a/TorchRemote.Plugin/TorchRemote.Plugin.csproj +++ b/TorchRemote.Plugin/TorchRemote.Plugin.csproj @@ -25,6 +25,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/TorchRemote.Plugin/Utils/Extensions.cs b/TorchRemote.Plugin/Utils/Extensions.cs index 253d845..a66ddb9 100644 --- a/TorchRemote.Plugin/Utils/Extensions.cs +++ b/TorchRemote.Plugin/Utils/Extensions.cs @@ -5,6 +5,7 @@ using Sandbox.Engine.Networking; using Sandbox.Game; using Sandbox.Game.World; using TorchRemote.Models.Shared; + namespace TorchRemote.Plugin.Utils; public static class Extensions @@ -26,4 +27,10 @@ public static class Extensions using var md5 = MD5.Create(); return new(md5.ComputeHash(Encoding.UTF8.GetBytes(s))); } + + public static Vector3 ToNumerics(this VRageMath.Vector3D vector3D) => + new((float)vector3D.X, (float)vector3D.Y, (float)vector3D.Z); + + public static Vector3 ToNumerics(this VRageMath.Vector3 vector3) => + new(vector3.X, vector3.Y, vector3.Z); } diff --git a/TorchRemote.Plugin/manifest.xml b/TorchRemote.Plugin/manifest.xml index 30f2e41..bd87216 100644 --- a/TorchRemote.Plugin/manifest.xml +++ b/TorchRemote.Plugin/manifest.xml @@ -2,5 +2,5 @@ Torch Remote 284017F3-9682-4841-A544-EB04DB8CB9BA - v1.0.7 + v1.0.8 \ No newline at end of file