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.

380 lines
14KB

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