using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Text; using TechbloxModdingAPI.App; using HarmonyLib; using IllusionInjector; // test using RobocraftX.FrontEnd; using Unity.Mathematics; using UnityEngine; using RobocraftX.Common.Input; using Svelto.Tasks; using Svelto.Tasks.Lean; using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Commands; using TechbloxModdingAPI.Input; using TechbloxModdingAPI.Players; using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.Tests { #if DEBUG // unused by design /// /// Modding API implemented as a standalone IPA Plugin. /// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself /// class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin { private static Harmony harmony { get; set; } public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name; public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString(); public string HarmonyID { get; } = "org.git.exmods.modtainers.techbloxmoddingapi"; public override void OnApplicationQuit() { Main.Shutdown(); } public override void OnApplicationStart() { FileLog.Reset(); Harmony.DEBUG = true; Main.Init(); Logging.MetaDebugLog($"Version group id {ApiExclusiveGroups.versionGroup}"); // disable background music Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers())); //AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :( //Utility.VersionTracking.Enable();//(very) unstable // debug/test handlers Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!"); Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler"); Game.Enter += (s, a) => { Player.LocalPlayer.SeatEntered += (sender, args) => Console.WriteLine($"Player {Player.LocalPlayer} entered seat {args.Seat}"); Player.LocalPlayer.SeatExited += (sender, args) => Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}"); }; // debug/test commands if (Dependency.Hell("ExtraCommands")) { CommandBuilder.Builder() .Name("Exit") .Description("Close Techblox immediately, without any prompts") .Action(() => { UnityEngine.Application.Quit(); }) .Build(); CommandBuilder.Builder() .Name("SetFOV") .Description("Set the player camera's field of view") .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; }) .Build(); CommandBuilder.Builder() .Name("MoveLastBlock") .Description("Move the most-recently-placed block, and any connected blocks by the given offset") .Action((float x, float y, float z) => { if (GameState.IsBuildMode()) foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes()) block.Position += new Unity.Mathematics.float3(x, y, z); else Logging.CommandLogError("Blocks can only be moved in Build mode!"); }).Build(); CommandBuilder.Builder() .Name("PlaceAluminium") .Description("Place a block of aluminium at the given coordinates") .Action((float x, float y, float z) => { var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z), force: true); Logging.CommandLog("Block placed with type: " + block.Type); }) .Build(); CommandBuilder.Builder() .Name("PlaceAluminiumLots") .Description("Place a lot of blocks of aluminium at the given coordinates") .Action((float x, float y, float z) => { Logging.CommandLog("Starting..."); var sw = Stopwatch.StartNew(); for (int i = 0; i < 100; i++) for (int j = 0; j < 100; j++) Block.PlaceNew(BlockIDs.Cube, new float3(x + i, y, z + j)); sw.Stop(); Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms"); }) .Build(); Block b = null; CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up") .Action(() => { if (b == null) { b = new Player(PlayerType.Local).GetBlockLookedAt(); Logging.CommandLog("Block saved: " + b); } else Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0))); }).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"); }) .Build(); CommandBuilder.Builder("ColorBlock", "Change color of the block looked at if there's any.") .Action(str => { if (!Enum.TryParse(str, out BlockColors color)) { Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values."); var s = str.Split(' '); new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]), float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3])); return; } new Player(PlayerType.Local).GetBlockLookedAt().Color = color; Logging.CommandLog("Colored block to " + color); }).Build(); CommandBuilder.Builder("MoveBlockByID", "Gets a block based on its object identifier and teleports it up.") .Action(ch => { foreach (var body in SimBody.GetFromObjectID(ch)) { Logging.CommandLog("SimBody: " + body); body.Position += new float3(0, 10, 0); foreach (var bodyConnectedBody in body.GetConnectedBodies()) { Logging.CommandLog("Moving " + bodyConnectedBody); bodyConnectedBody.Position += new float3(0, 10, 0); } } }).Build(); CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.") .Action((float val, float max) => { var body = new Player(PlayerType.Local).GetSimBodyLookedAt(); if (body == null) return; body.CurrentHealth = val; body.InitialHealth = max; Logging.CommandLog("Health set to: " + val); }).Build(); CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group") .Action((float x, float y, float z) => { var pos = new float3(x, y, z); var group = BlockGroup.Create(new Block(BlockIDs.Cube, pos) {Color = BlockColors.Aqua}); new Block(BlockIDs.Cube, pos += new float3(1, 0, 0)) {Color = BlockColors.Blue, BlockGroup = group}; new Block(BlockIDs.Cube, pos += new float3(1, 0, 0)) {Color = BlockColors.Green, BlockGroup = group}; new Block(BlockIDs.Cube, pos + new float3(1, 0, 0)) {Color = BlockColors.Lime, BlockGroup = group}; }).Build(); CommandBuilder.Builder("placeCustomBlock", "Places a custom block, needs a custom catalog and assets.") .Action((float x, float y, float z) => { Logging.CommandLog("Block placed: " + Block.PlaceNew((BlockIDs) 500, new float3(0, 0, 0))); }).Build(); CommandBuilder.Builder("toggleTimeMode", "Enters or exits simulation.") .Action((float x, float y, float z) => { Game.CurrentGame().ToggleTimeMode(); }).Build(); CommandBuilder.Builder("testColorBlock", "Tests coloring a block to default color") .Action(() => Player.LocalPlayer.GetBlockLookedAt().Color = BlockColors.Default).Build(); CommandBuilder.Builder("testMaterialBlock", "Tests materialing a block to default material") .Action(() => Player.LocalPlayer.GetBlockLookedAt().Material = BlockMaterial.Default).Build(); CommandBuilder.Builder("testGameName", "Tests changing the game name") .Action(() => Game.CurrentGame().Name = "Test").Build(); CommandBuilder.Builder("makeBlockStatic", "Makes a block you look at static") .Action(() => Player.LocalPlayer.GetBlockLookedAt().Static = true).Build(); Game.AddPersistentDebugInfo("InstalledMods", InstalledMods); /*Block.Placed += (sender, args) => Logging.MetaDebugLog("Placed block " + args.Block); Block.Removed += (sender, args) => Logging.MetaDebugLog("Removed block " + args.Block);*/ } // dependency test if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0"))) { Logging.LogWarning("You're in TechbloxScripting dependency hell"); } else { Logging.Log("Compatible TechbloxScripting detected"); } // Interface test /*Group uiGroup = new Group(new Rect(20, 20, 200, 500), "TechbloxModdingAPI_UITestGroup", true); var button = new Button("TEST"); button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");}; var button2 = new Button("TEST2"); button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");}; Text uiText = new Text("", multiline: true); uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); }; Label uiLabel = new Label("Label!"); Image uiImg = new Image(name:"Behold this texture!"); uiImg.Enabled = false; uiGroup.AddElement(button); uiGroup.AddElement(button2); uiGroup.AddElement(uiText); uiGroup.AddElement(uiLabel); uiGroup.AddElement(uiImg);*/ /*Addressables.LoadAssetAsync("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg") .Completed += handle => { uiImg.Texture = handle.Result; uiImg.Enabled = true; Logging.MetaDebugLog($"Got blue bg asset {handle.Result}"); };*/ /*((FasterList)AccessTools.Property(typeof(GuiInputMap), "GuiInputsButtonDown").GetValue(null)) .Add(new GuiInputMap.GuiInputMapElement(RewiredConsts.Action.ToggleCommandLine, GuiIn))*/ /*Game.Enter += (sender, e) => { ushort lastKey = ushort.MaxValue; foreach (var kv in FullGameFields._dataDb.GetValues() .OrderBy(kv=>ushort.Parse(kv.Key))) { var data = (CubeListData) kv.Value; ushort currentKey = ushort.Parse(kv.Key); var toReplace = new Dictionary { {"Scalable", ""}, {"Qtr", "Quarter"}, {"RNeg", "Rounded Negative"}, {"Neg", "Negative"}, {"Tetra", "Tetrahedron"}, {"RWedge", "Rounded Wedge"}, {"RTetra", "Rounded Tetrahedron"} }; string name = LocalizationService.Localize(data.CubeNameKey).Split(' ').Select(str => str.Length > 0 ? char.ToUpper(str[0]) + str.Substring(1) : str).Aggregate((a, b) => a + b) .Replace("-", ""); foreach (var rkv in toReplace) { name = Regex.Replace(name, rkv.Key + "([A-Z]|$)", rkv.Value + "$1"); } Console.WriteLine($"{name}{(currentKey != lastKey + 1 ? $" = {currentKey}" : "")},"); lastKey = currentKey; } };*/ /*Game.Enter += (sender, e) => { ushort lastKey = ushort.MaxValue; Console.WriteLine("Materials:\n" + FullGameFields._dataDb.GetValues() .OrderBy(kv => ushort.Parse(kv.Key)) .Select(kv => { ushort currentKey = ushort.Parse(kv.Key); string result = $"{((MaterialPropertiesData)kv.Value).Name}{(currentKey != lastKey + 1 ? $" = {kv.Key}" : "")},"; lastKey = currentKey; return result; }) .Aggregate((a, b) => a + "\n" + b)); };*/ CommandBuilder.Builder("takeScreenshot", "Enables the screenshot taker") .Action(() => { Game.CurrentGame().EnableScreenshotTaker(); }).Build(); CommandBuilder.Builder("testPositionDefault", "Tests the Block.Position property's default value.") .Action(() => { IEnumerator Loop() { for (int i = 0; i < 2; i++) { Console.WriteLine("A"); var block = Block.PlaceNew(BlockIDs.Cube, 1); Console.WriteLine("B"); while (!block.Exists) yield return Yield.It; Console.WriteLine("C"); block.Remove(); Console.WriteLine("D"); while (block.Exists) yield return Yield.It; Console.WriteLine("E - Pos: " + block.Position); block.Position = 4; Console.WriteLine("F - Pos: " + block.Position); } } Loop().RunOn(Scheduler.leanRunner); }).Build(); CommandBuilder.Builder("importAssetBundle") .Action(() => { Logging.CommandLog("Importing asset bundle..."); var ab = AssetBundle.LoadFromFile( @"filepath"); Logging.CommandLog("Imported asset bundle: " + ab); var assets = ab.LoadAllAssets(); Logging.CommandLog("Loaded " + assets.Length + " assets"); foreach (var asset in assets) { Logging.CommandLog(asset); } }).Build(); #if TEST TestRoot.RunTests(); #endif } private string modsString; private string InstalledMods() { if (modsString != null) return modsString; StringBuilder sb = new StringBuilder("Installed mods:"); foreach (var plugin in PluginManager.Plugins) sb.Append("\n" + plugin.Name + " - " + plugin.Version); return modsString = sb.ToString(); } [HarmonyPatch] public class MinimumSpecsPatch { public static bool Prefix(ref bool __result) { __result = true; return false; } public static MethodInfo TargetMethod() { return ((Func) MinimumSpecsCheck.CheckRequirementsMet).Method; } } } #endif }