A stable modding interface between Techblox and mods https://mod.exmods.org/
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

325 lines
12KB

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