A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
10KB

  1. using System;
  2. using System.Diagnostics;
  3. using System.Reflection;
  4. using System.Text;
  5. using TechbloxModdingAPI.App;
  6. using HarmonyLib;
  7. using IllusionInjector;
  8. // test
  9. using RobocraftX.FrontEnd;
  10. using Unity.Mathematics;
  11. using UnityEngine;
  12. using RobocraftX.Common.Input;
  13. using TechbloxModdingAPI.Blocks;
  14. using TechbloxModdingAPI.Commands;
  15. using TechbloxModdingAPI.Input;
  16. using TechbloxModdingAPI.Interface.IMGUI;
  17. using TechbloxModdingAPI.Players;
  18. using TechbloxModdingAPI.Utility;
  19. namespace TechbloxModdingAPI.Tests
  20. {
  21. #if DEBUG
  22. // unused by design
  23. /// <summary>
  24. /// Modding API implemented as a standalone IPA Plugin.
  25. /// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself
  26. /// </summary>
  27. class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin
  28. {
  29. private static Harmony harmony { get; set; }
  30. public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
  31. public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
  32. public string HarmonyID { get; } = "org.git.exmods.modtainers.techbloxmoddingapi";
  33. public override void OnApplicationQuit()
  34. {
  35. Main.Shutdown();
  36. }
  37. public override void OnApplicationStart()
  38. {
  39. FileLog.Reset();
  40. Harmony.DEBUG = true;
  41. Main.Init();
  42. Logging.MetaDebugLog($"Version group id {(uint)ApiExclusiveGroups.versionGroup}");
  43. // disable background music
  44. Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers()));
  45. //AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :(
  46. //Utility.VersionTracking.Enable();//(very) unstable
  47. // debug/test handlers
  48. Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
  49. // debug/test commands
  50. if (Dependency.Hell("ExtraCommands"))
  51. {
  52. CommandBuilder.Builder()
  53. .Name("Exit")
  54. .Description("Close Techblox immediately, without any prompts")
  55. .Action(() => { UnityEngine.Application.Quit(); })
  56. .Build();
  57. CommandBuilder.Builder()
  58. .Name("SetFOV")
  59. .Description("Set the player camera's field of view")
  60. .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
  61. .Build();
  62. CommandBuilder.Builder()
  63. .Name("MoveLastBlock")
  64. .Description("Move the most-recently-placed block, and any connected blocks by the given offset")
  65. .Action((float x, float y, float z) =>
  66. {
  67. if (GameState.IsBuildMode())
  68. foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
  69. block.Position += new Unity.Mathematics.float3(x, y, z);
  70. else
  71. Logging.CommandLogError("Blocks can only be moved in Build mode!");
  72. }).Build();
  73. CommandBuilder.Builder()
  74. .Name("PlaceAluminium")
  75. .Description("Place a block of aluminium at the given coordinates")
  76. .Action((float x, float y, float z) =>
  77. {
  78. var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z));
  79. Logging.CommandLog("Block placed with type: " + block.Type);
  80. })
  81. .Build();
  82. CommandBuilder.Builder()
  83. .Name("PlaceAluminiumLots")
  84. .Description("Place a lot of blocks of aluminium at the given coordinates")
  85. .Action((float x, float y, float z) =>
  86. {
  87. Logging.CommandLog("Starting...");
  88. var sw = Stopwatch.StartNew();
  89. for (int i = 0; i < 100; i++)
  90. for (int j = 0; j < 100; j++)
  91. Block.PlaceNew(BlockIDs.Cube, new float3(x + i, y, z + j));
  92. sw.Stop();
  93. Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
  94. })
  95. .Build();
  96. Block b = null;
  97. CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up")
  98. .Action(() =>
  99. {
  100. if (b == null)
  101. {
  102. b = new Player(PlayerType.Local).GetBlockLookedAt();
  103. Logging.CommandLog("Block saved: " + b);
  104. }
  105. else
  106. Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0)));
  107. }).Build();
  108. CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
  109. .Action(() => { throw new Exception("Error Command always throws an error"); })
  110. .Build();
  111. CommandBuilder.Builder("ColorBlock",
  112. "Change color of the block looked at if there's any.")
  113. .Action<string>(str =>
  114. {
  115. if (!Enum.TryParse(str, out BlockColors color))
  116. {
  117. Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values.");
  118. var s = str.Split(' ');
  119. new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]),
  120. float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3]));
  121. return;
  122. }
  123. new Player(PlayerType.Local).GetBlockLookedAt().Color = color;
  124. Logging.CommandLog("Colored block to " + color);
  125. }).Build();
  126. CommandBuilder.Builder("MoveBlockByID", "Gets a block based on its object identifier and teleports it up.")
  127. .Action<char>(ch =>
  128. {
  129. foreach (var body in SimBody.GetFromObjectID(ch))
  130. {
  131. Logging.CommandLog("SimBody: " + body);
  132. body.Position += new float3(0, 10, 0);
  133. foreach (var bodyConnectedBody in body.GetConnectedBodies())
  134. {
  135. Logging.CommandLog("Moving " + bodyConnectedBody);
  136. bodyConnectedBody.Position += new float3(0, 10, 0);
  137. }
  138. }
  139. }).Build();
  140. CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.")
  141. .Action((float val, float max) =>
  142. {
  143. var body = new Player(PlayerType.Local).GetSimBodyLookedAt();
  144. if (body == null) return;
  145. body.CurrentHealth = val;
  146. body.InitialHealth = max;
  147. Logging.CommandLog("Health set to: " + val);
  148. }).Build();
  149. CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group")
  150. .Action((float x, float y, float z) =>
  151. {
  152. var pos = new float3(x, y, z);
  153. var group = BlockGroup.Create(new Block(BlockIDs.Cube, pos) {Color = BlockColors.Aqua});
  154. new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
  155. {Color = BlockColors.Blue, BlockGroup = group};
  156. new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
  157. {Color = BlockColors.Green, BlockGroup = group};
  158. new Block(BlockIDs.Cube, pos + new float3(1, 0, 0))
  159. {Color = BlockColors.Lime, BlockGroup = group};
  160. }).Build();
  161. CommandBuilder.Builder("placeCustomBlock", "Places a custom block, needs a custom catalog and assets.")
  162. .Action((float x, float y, float z) =>
  163. {
  164. Logging.CommandLog("Block placed: " +
  165. Block.PlaceNew((BlockIDs) 500, new float3(0, 0, 0)));
  166. }).Build();
  167. CommandBuilder.Builder("toggleTimeMode", "Enters or exits simulation.")
  168. .Action((float x, float y, float z) =>
  169. {
  170. Game.CurrentGame().ToggleTimeMode();
  171. }).Build();
  172. CommandBuilder.Builder("testColorBlock", "Tests coloring a block to default color")
  173. .Action(() => Player.LocalPlayer.GetBlockLookedAt().Color = BlockColors.Default).Build();
  174. CommandBuilder.Builder("testMaterialBlock", "Tests materialing a block to default material")
  175. .Action(() => Player.LocalPlayer.GetBlockLookedAt().Material = BlockMaterial.Default).Build();
  176. CommandBuilder.Builder("testGameName", "Tests changing the game name")
  177. .Action(() => Game.CurrentGame().Name = "Test").Build();
  178. CommandBuilder.Builder("makeBlockStatic", "Makes a block you look at static")
  179. .Action(() => Player.LocalPlayer.GetBlockLookedAt().Static = true).Build();
  180. Game.AddPersistentDebugInfo("InstalledMods", InstalledMods);
  181. Block.Placed += (sender, args) =>
  182. Logging.MetaDebugLog("Placed block " + args.Block);
  183. Block.Removed += (sender, args) =>
  184. Logging.MetaDebugLog("Removed block " + args.Block);
  185. }
  186. // dependency test
  187. if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0")))
  188. {
  189. Logging.LogWarning("You're in TechbloxScripting dependency hell");
  190. }
  191. else
  192. {
  193. Logging.Log("Compatible TechbloxScripting detected");
  194. }
  195. // Interface test
  196. /*Group uiGroup = new Group(new Rect(20, 20, 200, 500), "TechbloxModdingAPI_UITestGroup", true);
  197. var button = new Button("TEST");
  198. button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
  199. var button2 = new Button("TEST2");
  200. button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
  201. Text uiText = new Text("<Input!>", multiline: true);
  202. uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); };
  203. Label uiLabel = new Label("Label!");
  204. Image uiImg = new Image(name:"Behold this texture!");
  205. uiImg.Enabled = false;
  206. uiGroup.AddElement(button);
  207. uiGroup.AddElement(button2);
  208. uiGroup.AddElement(uiText);
  209. uiGroup.AddElement(uiLabel);
  210. uiGroup.AddElement(uiImg);*/
  211. /*Addressables.LoadAssetAsync<Texture2D>("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg")
  212. .Completed +=
  213. handle =>
  214. {
  215. uiImg.Texture = handle.Result;
  216. uiImg.Enabled = true;
  217. Logging.MetaDebugLog($"Got blue bg asset {handle.Result}");
  218. };*/
  219. /*((FasterList<GuiInputMap.GuiInputMapElement>)AccessTools.Property(typeof(GuiInputMap), "GuiInputsButtonDown").GetValue(null))
  220. .Add(new GuiInputMap.GuiInputMapElement(RewiredConsts.Action.ToggleCommandLine, GuiIn))*/
  221. #if TEST
  222. TestRoot.RunTests();
  223. #endif
  224. }
  225. private string modsString;
  226. private string InstalledMods()
  227. {
  228. if (modsString != null) return modsString;
  229. StringBuilder sb = new StringBuilder("Installed mods:");
  230. foreach (var plugin in PluginManager.Plugins)
  231. sb.Append("\n" + plugin.Name + " - " + plugin.Version);
  232. return modsString = sb.ToString();
  233. }
  234. public override void OnUpdate()
  235. {
  236. if (UnityEngine.Input.GetKeyDown(KeyCode.End))
  237. {
  238. Console.WriteLine("Pressed button to toggle console");
  239. FakeInput.CustomInput(new LocalInputEntityStruct {commandLineToggleInput = true});
  240. }
  241. }
  242. [HarmonyPatch]
  243. public class MinimumSpecsPatch
  244. {
  245. public static bool Prefix()
  246. {
  247. return false;
  248. }
  249. public static MethodInfo TargetMethod()
  250. {
  251. return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method;
  252. }
  253. }
  254. }
  255. #endif
  256. }