From 1c5ce37fcece606505ce5e62c5786a00b8c70d21 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 17 May 2020 20:13:45 +0200 Subject: [PATCH] Add debug interface API and improve block API Added API for adding more information on the debug display (not object-oriented yet) Removed the setter for block type to ensure stability Made the block API return defaults if the block no longer exists Added property to check if the block exists Made a struct for the block's color property Added missing block IDs --- Automation/gen_csproj.py | 0 GamecraftModdingAPI/Block.cs | 55 ++++++------ .../Blocks/{BlockColors.cs => BlockColor.cs} | 13 ++- GamecraftModdingAPI/Blocks/BlockEngine.cs | 38 ++++++++- GamecraftModdingAPI/Blocks/BlockIDs.cs | 5 ++ GamecraftModdingAPI/Main.cs | 1 + .../Tests/GamecraftModdingAPIPluginTest.cs | 19 ++++- GamecraftModdingAPI/Utility/DebugInterface.cs | 30 +++++++ .../Utility/DebugInterfaceEngine.cs | 84 +++++++++++++++++++ 9 files changed, 206 insertions(+), 39 deletions(-) mode change 100755 => 100644 Automation/gen_csproj.py rename GamecraftModdingAPI/Blocks/{BlockColors.cs => BlockColor.cs} (50%) create mode 100644 GamecraftModdingAPI/Utility/DebugInterface.cs create mode 100644 GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs diff --git a/Automation/gen_csproj.py b/Automation/gen_csproj.py old mode 100755 new mode 100644 diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs index 336215e..db220dc 100644 --- a/GamecraftModdingAPI/Block.cs +++ b/GamecraftModdingAPI/Block.cs @@ -77,8 +77,11 @@ namespace GamecraftModdingAPI /// public float3 Position { - get => MovementEngine.GetPosition(Id.entityID); - set => MovementEngine.MoveBlock(Id.entityID, value); + get => Exists ? MovementEngine.GetPosition(Id.entityID) : float3.zero; + set + { + if (Exists) MovementEngine.MoveBlock(Id.entityID, value); + } } /// @@ -86,51 +89,41 @@ namespace GamecraftModdingAPI /// public float3 Rotation { - get => RotationEngine.GetRotation(Id.entityID); - set => RotationEngine.RotateBlock(Id.entityID, value); + get => Exists ? RotationEngine.GetRotation(Id.entityID) : float3.zero; + set + { + if (Exists) RotationEngine.RotateBlock(Id.entityID, value); + } } /// - /// The block's type (ID). Changing from or to a functional part may crash the game. + /// The block's type (ID). /// - public BlockIDs Type - { - get => (BlockIDs) BlockEngine.GetBlockInfo(Id).DBID; - set - { - BlockEngine.GetBlockInfo(Id).DBID = (uint) value; - uint prefabId = PrefabsID.GetPrefabId((uint) value, 0); - BlockEngine.GetBlockInfo(Id).prefabID = prefabId; - BlockEngine.GetBlockInfo(Id) = new PhysicsPrefabEntityStruct(prefabId); - } - } + public BlockIDs Type => (BlockIDs) (BlockEngine.GetBlockInfo(Id)?.DBID ?? 0); - public BlockColors Color + public BlockColor Color { - get => (BlockColors) (BlockEngine.GetBlockInfo(Id).indexInPalette % 10); + get + { + byte index = BlockEngine.GetBlockInfo(Id)?.indexInPalette ?? byte.MaxValue; + if (index == byte.MaxValue) return new BlockColor {Color = BlockColors.Default}; + return new BlockColor {Color = (BlockColors) (index % 10), Darkness = (byte) (index / 10)}; + } set { - ref var color = ref BlockEngine.GetBlockInfo(Id); - color.indexInPalette = (byte) (color.indexInPalette / 10 * 10 + value); + var def = new ColourParameterEntityStruct(); + ref var color = ref BlockEngine.GetBlockInfo(Id, ref def); + color.indexInPalette = (byte) (value.Color + value.Darkness * 10); color.needsUpdate = true; } } - public byte ColorDarkness - { - get => (byte) (BlockEngine.GetBlockInfo(Id).indexInPalette / 10); - set - { - ref var color = ref BlockEngine.GetBlockInfo(Id); - color.indexInPalette = (byte) (10 * (byte) value + color.indexInPalette % 10); - color.needsUpdate = true; - } - } + public bool Exists => BlockEngine.BlockExists(Id); /// /// Returns an array of blocks that are connected to this one. /// - public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id.entityID); + public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id); /// /// Removes this block. diff --git a/GamecraftModdingAPI/Blocks/BlockColors.cs b/GamecraftModdingAPI/Blocks/BlockColor.cs similarity index 50% rename from GamecraftModdingAPI/Blocks/BlockColors.cs rename to GamecraftModdingAPI/Blocks/BlockColor.cs index 146bcbf..fc87eb7 100644 --- a/GamecraftModdingAPI/Blocks/BlockColors.cs +++ b/GamecraftModdingAPI/Blocks/BlockColor.cs @@ -1,5 +1,16 @@ -namespace GamecraftModdingAPI.Blocks +namespace GamecraftModdingAPI.Blocks { + public struct BlockColor + { + public BlockColors Color; + public byte Darkness; + + public override string ToString() + { + return $"{nameof(Color)}: {Color}, {nameof(Darkness)}: {Darkness}"; + } + } + /// /// Preset block colours /// diff --git a/GamecraftModdingAPI/Blocks/BlockEngine.cs b/GamecraftModdingAPI/Blocks/BlockEngine.cs index a851643..f678501 100644 --- a/GamecraftModdingAPI/Blocks/BlockEngine.cs +++ b/GamecraftModdingAPI/Blocks/BlockEngine.cs @@ -25,20 +25,50 @@ namespace GamecraftModdingAPI.Blocks { } - public Block[] GetConnectedBlocks(uint blockID) + public Block[] GetConnectedBlocks(EGID blockID) { + if (!BlockExists(blockID)) return new Block[0]; Stack cubeStack = new Stack(); FasterList cubesToProcess = new FasterList(); - ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubesToProcess, (in GridConnectionsEntityStruct g) => { return false; }); + ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID.entityID, cubeStack, cubesToProcess, (in GridConnectionsEntityStruct g) => { return false; }); var ret = new Block[cubesToProcess.count]; for (int i = 0; i < cubesToProcess.count; i++) ret[i] = new Block(cubesToProcess[i]); return ret; } - public ref T GetBlockInfo(EGID blockID) where T : struct, IEntityComponent + /// + /// Get a struct of a block. Can be used to set properties. + /// When only querying parameters, use the other overload for convenience. + /// + /// + /// + /// + /// + public ref T GetBlockInfo(EGID blockID, ref T def) where T : struct, IEntityComponent { - return ref entitiesDB.QueryEntity(blockID); + if (entitiesDB.Exists(blockID)) + return ref entitiesDB.QueryEntity(blockID); + return ref def; + } + + /// + /// Get a struct of a block. Can only be used to retrieve information. + /// Use the overload with a default parameter to get the struct by reference to set values. + /// + /// The block's EGID + /// The struct's type to get + /// A copy of the struct or null + public T? GetBlockInfo(EGID blockID) where T : struct, IEntityComponent + { + if (entitiesDB.Exists(blockID)) + return entitiesDB.QueryEntity(blockID); + return null; + } + + public bool BlockExists(EGID id) + { + return entitiesDB.Exists(id); } } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockIDs.cs b/GamecraftModdingAPI/Blocks/BlockIDs.cs index 1a7a77a..bba2583 100644 --- a/GamecraftModdingAPI/Blocks/BlockIDs.cs +++ b/GamecraftModdingAPI/Blocks/BlockIDs.cs @@ -220,6 +220,11 @@ namespace GamecraftModdingAPI.Blocks CentreHUD, ObjectiveHUD, GameStatsHUD, //231 + GameOverBlock, + MovementConstrainer = 246, + RotationConstrainer, + AdvancedMovementDampener, + AdvancedRotationDampener, Mover = 250, Rotator, MovementDampener, diff --git a/GamecraftModdingAPI/Main.cs b/GamecraftModdingAPI/Main.cs index e8ab256..3b15de8 100644 --- a/GamecraftModdingAPI/Main.cs +++ b/GamecraftModdingAPI/Main.cs @@ -72,6 +72,7 @@ namespace GamecraftModdingAPI // init object-oriented classes Player.Init(); Block.Init(); + DebugInterface.Init(); Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized"); } diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index 307b3f6..2e97df7 100644 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs @@ -13,6 +13,7 @@ using GamecraftModdingAPI.Commands; using GamecraftModdingAPI.Events; using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Blocks; +using RobocraftX.FrontEnd; namespace GamecraftModdingAPI.Tests { @@ -174,9 +175,6 @@ namespace GamecraftModdingAPI.Tests CommandBuilder.Builder("getBlock") .Action(() => uREPL.Log.Output(new Player(Players.PlayerType.Local).GetBlockLookedAt()+"")).Build(); - CommandBuilder.Builder("changeToAluminium") - .Action(() => new Player(Players.PlayerType.Local).GetBlockLookedAt().Type = BlockIDs.AluminiumCube) - .Build(); CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.") .Action(() => { throw new Exception("Error Command always throws an error"); }) @@ -246,5 +244,20 @@ namespace GamecraftModdingAPI.Tests public void OnLevelWasLoaded(int level) { } public void OnUpdate() { } + + [HarmonyPatch] + public class MinimumSpecsPatch + { + public static bool Prefix(ref bool __result) + { + __result = true; + return false; + } + + public static MethodInfo TargetMethod() + { + return ((Func)MinimumSpecsCheck.CheckRequirementsMet).Method; + } + } } } diff --git a/GamecraftModdingAPI/Utility/DebugInterface.cs b/GamecraftModdingAPI/Utility/DebugInterface.cs new file mode 100644 index 0000000..1d5499e --- /dev/null +++ b/GamecraftModdingAPI/Utility/DebugInterface.cs @@ -0,0 +1,30 @@ +using System; +using GamecraftModdingAPI.Blocks; + +namespace GamecraftModdingAPI.Utility +{ + public static class DebugInterface + { + private static DebugInterfaceEngine _engine = new DebugInterfaceEngine(); + + /// + /// Saves the extra information to be displayed on the debug view. + /// The provided getter function is called each time the view updates so make sure it returns quickly. + /// + /// A global ID for the custom information + /// A function that returns the current information + public static void SetInfo(string id, Func contentGetter) => _engine.SetInfo(id, contentGetter); + + /// + /// Removes an information provided by a plugin. + /// + /// The ID of the custom information + /// + public static bool RemoveInfo(string id) => _engine.RemoveInfo(id); + + public static void Init() + { + GameEngineManager.AddGameEngine(_engine); + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs b/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs new file mode 100644 index 0000000..2d470bb --- /dev/null +++ b/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text.Formatting; +using GamecraftModdingAPI.Blocks; +using GamecraftModdingAPI.Engines; +using GamecraftModdingAPI.Players; +using HarmonyLib; +using RobocraftX.GUI.Debug; +using Svelto.ECS; +using Svelto.ECS.Experimental; + +namespace GamecraftModdingAPI.Utility +{ + public class DebugInterfaceEngine : IApiEngine + { + private static Dictionary> _extraInfo=new Dictionary>(); + public void Ready() + { + SetInfo("lookedAt", LookedAt); + } + + public EntitiesDB entitiesDB { get; set; } + + public void Dispose() + { + } + + public void SetInfo(string id, Func contentGetter) => _extraInfo[id] = contentGetter; + public bool RemoveInfo(string id) => _extraInfo.Remove(id); + + private Player player; + private string LookedAt() + { + if (player == null) + player = new Player(PlayerType.Local); + Block block = player.GetBlockLookedAt(); + if (block == null) return "Block: none"; + return "Block: " + block.Type + "\nColor: " + block.Color + "\n" + "At: " + block.Position; + } + + public string Name { get; } = "GamecraftModdingAPIDebugInterfaceGameEngine"; + public bool isRemovable { get; } = true; + + [HarmonyPatch] + private class Patch + { + public static IEnumerable Transpiler(IEnumerable instructions) + { + var list = new List(instructions); + try + { + //Before setting the text from the StringBuffer + int index = list.FindLastIndex(inst => inst.opcode == OpCodes.Ldfld); + var array = new CodeInstruction[] + { + new CodeInstruction(OpCodes.Ldloc_0), //StringBuffer + new CodeInstruction(OpCodes.Call, ((Action)AddInfo).Method) + }; + list.InsertRange(index, array); + } + catch (Exception e) + { + Console.WriteLine(e); + } + + return list; + } + + public static void AddInfo(StringBuffer sb) + { + foreach (var info in _extraInfo.Values) + sb.Append(info() + "\n"); + } + + public static MethodInfo TargetMethod() + { + return AccessTools.Method("RobocraftX.GUI.Debug.DebugDisplayEngine:UpdateDisplay"); + } + } + } +} \ No newline at end of file