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
This commit is contained in:
Norbi Peti 2020-05-17 20:13:45 +02:00
parent ebea9da232
commit 1c5ce37fce
9 changed files with 206 additions and 39 deletions

0
Automation/gen_csproj.py Executable file → Normal file
View file

View file

@ -77,8 +77,11 @@ namespace GamecraftModdingAPI
/// </summary> /// </summary>
public float3 Position public float3 Position
{ {
get => MovementEngine.GetPosition(Id.entityID); get => Exists ? MovementEngine.GetPosition(Id.entityID) : float3.zero;
set => MovementEngine.MoveBlock(Id.entityID, value); set
{
if (Exists) MovementEngine.MoveBlock(Id.entityID, value);
}
} }
/// <summary> /// <summary>
@ -86,51 +89,41 @@ namespace GamecraftModdingAPI
/// </summary> /// </summary>
public float3 Rotation public float3 Rotation
{ {
get => RotationEngine.GetRotation(Id.entityID); get => Exists ? RotationEngine.GetRotation(Id.entityID) : float3.zero;
set => RotationEngine.RotateBlock(Id.entityID, value); set
{
if (Exists) RotationEngine.RotateBlock(Id.entityID, value);
}
} }
/// <summary> /// <summary>
/// The block's type (ID). Changing from or to a functional part may crash the game. /// The block's type (ID).
/// </summary> /// </summary>
public BlockIDs Type public BlockIDs Type => (BlockIDs) (BlockEngine.GetBlockInfo<DBEntityStruct>(Id)?.DBID ?? 0);
{
get => (BlockIDs) BlockEngine.GetBlockInfo<DBEntityStruct>(Id).DBID;
set
{
BlockEngine.GetBlockInfo<DBEntityStruct>(Id).DBID = (uint) value;
uint prefabId = PrefabsID.GetPrefabId((uint) value, 0);
BlockEngine.GetBlockInfo<GFXPrefabEntityStructGPUI>(Id).prefabID = prefabId;
BlockEngine.GetBlockInfo<PhysicsPrefabEntityStruct>(Id) = new PhysicsPrefabEntityStruct(prefabId);
}
}
public BlockColors Color public BlockColor Color
{ {
get => (BlockColors) (BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id).indexInPalette % 10); get
{
byte index = BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(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 set
{ {
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id); var def = new ColourParameterEntityStruct();
color.indexInPalette = (byte) (color.indexInPalette / 10 * 10 + value); ref var color = ref BlockEngine.GetBlockInfo(Id, ref def);
color.indexInPalette = (byte) (value.Color + value.Darkness * 10);
color.needsUpdate = true; color.needsUpdate = true;
} }
} }
public byte ColorDarkness public bool Exists => BlockEngine.BlockExists(Id);
{
get => (byte) (BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id).indexInPalette / 10);
set
{
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id);
color.indexInPalette = (byte) (10 * (byte) value + color.indexInPalette % 10);
color.needsUpdate = true;
}
}
/// <summary> /// <summary>
/// Returns an array of blocks that are connected to this one. /// Returns an array of blocks that are connected to this one.
/// </summary> /// </summary>
public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id.entityID); public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id);
/// <summary> /// <summary>
/// Removes this block. /// Removes this block.

View file

@ -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}";
}
}
/// <summary> /// <summary>
/// Preset block colours /// Preset block colours
/// </summary> /// </summary>

View file

@ -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<uint> cubeStack = new Stack<uint>(); Stack<uint> cubeStack = new Stack<uint>();
FasterList<uint> cubesToProcess = new FasterList<uint>(); FasterList<uint> cubesToProcess = new FasterList<uint>();
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]; var ret = new Block[cubesToProcess.count];
for (int i = 0; i < cubesToProcess.count; i++) for (int i = 0; i < cubesToProcess.count; i++)
ret[i] = new Block(cubesToProcess[i]); ret[i] = new Block(cubesToProcess[i]);
return ret; return ret;
} }
public ref T GetBlockInfo<T>(EGID blockID) where T : struct, IEntityComponent /// <summary>
/// Get a struct of a block. Can be used to set properties.
/// When only querying parameters, use the other overload for convenience.
/// </summary>
/// <param name="blockID"></param>
/// <param name="def"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public ref T GetBlockInfo<T>(EGID blockID, ref T def) where T : struct, IEntityComponent
{ {
if (entitiesDB.Exists<T>(blockID))
return ref entitiesDB.QueryEntity<T>(blockID); return ref entitiesDB.QueryEntity<T>(blockID);
return ref def;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="blockID">The block's EGID</param>
/// <typeparam name="T">The struct's type to get</typeparam>
/// <returns>A copy of the struct or null</returns>
public T? GetBlockInfo<T>(EGID blockID) where T : struct, IEntityComponent
{
if (entitiesDB.Exists<T>(blockID))
return entitiesDB.QueryEntity<T>(blockID);
return null;
}
public bool BlockExists(EGID id)
{
return entitiesDB.Exists<DBEntityStruct>(id);
} }
} }
} }

View file

@ -220,6 +220,11 @@ namespace GamecraftModdingAPI.Blocks
CentreHUD, CentreHUD,
ObjectiveHUD, ObjectiveHUD,
GameStatsHUD, //231 GameStatsHUD, //231
GameOverBlock,
MovementConstrainer = 246,
RotationConstrainer,
AdvancedMovementDampener,
AdvancedRotationDampener,
Mover = 250, Mover = 250,
Rotator, Rotator,
MovementDampener, MovementDampener,

View file

@ -72,6 +72,7 @@ namespace GamecraftModdingAPI
// init object-oriented classes // init object-oriented classes
Player.Init(); Player.Init();
Block.Init(); Block.Init();
DebugInterface.Init();
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized"); Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
} }

View file

@ -13,6 +13,7 @@ using GamecraftModdingAPI.Commands;
using GamecraftModdingAPI.Events; using GamecraftModdingAPI.Events;
using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Blocks; using GamecraftModdingAPI.Blocks;
using RobocraftX.FrontEnd;
namespace GamecraftModdingAPI.Tests namespace GamecraftModdingAPI.Tests
{ {
@ -174,9 +175,6 @@ namespace GamecraftModdingAPI.Tests
CommandBuilder.Builder("getBlock") CommandBuilder.Builder("getBlock")
.Action(() => uREPL.Log.Output(new Player(Players.PlayerType.Local).GetBlockLookedAt()+"")).Build(); .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.") CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
.Action(() => { throw new Exception("Error Command always throws an error"); }) .Action(() => { throw new Exception("Error Command always throws an error"); })
@ -246,5 +244,20 @@ namespace GamecraftModdingAPI.Tests
public void OnLevelWasLoaded(int level) { } public void OnLevelWasLoaded(int level) { }
public void OnUpdate() { } public void OnUpdate() { }
[HarmonyPatch]
public class MinimumSpecsPatch
{
public static bool Prefix(ref bool __result)
{
__result = true;
return false;
}
public static MethodInfo TargetMethod()
{
return ((Func<bool>)MinimumSpecsCheck.CheckRequirementsMet).Method;
}
}
} }
} }

View file

@ -0,0 +1,30 @@
using System;
using GamecraftModdingAPI.Blocks;
namespace GamecraftModdingAPI.Utility
{
public static class DebugInterface
{
private static DebugInterfaceEngine _engine = new DebugInterfaceEngine();
/// <summary>
/// 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.
/// </summary>
/// <param name="id">A global ID for the custom information</param>
/// <param name="contentGetter">A function that returns the current information</param>
public static void SetInfo(string id, Func<string> contentGetter) => _engine.SetInfo(id, contentGetter);
/// <summary>
/// Removes an information provided by a plugin.
/// </summary>
/// <param name="id">The ID of the custom information</param>
/// <returns></returns>
public static bool RemoveInfo(string id) => _engine.RemoveInfo(id);
public static void Init()
{
GameEngineManager.AddGameEngine(_engine);
}
}
}

View file

@ -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<string, Func<string>> _extraInfo=new Dictionary<string, Func<string>>();
public void Ready()
{
SetInfo("lookedAt", LookedAt);
}
public EntitiesDB entitiesDB { get; set; }
public void Dispose()
{
}
public void SetInfo(string id, Func<string> 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var list = new List<CodeInstruction>(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<StringBuffer>)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");
}
}
}
}