diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs index 524847d..513ad5e 100644 --- a/GamecraftModdingAPI/Block.cs +++ b/GamecraftModdingAPI/Block.cs @@ -171,7 +171,7 @@ namespace GamecraftModdingAPI egid = new EGID(id, group.Value); if (typeToGroup.TryGetValue(type, out var gr) && gr.All(egs => egs != group.Value)) //If this subclass has a specific group, then use that - so Block should still work - throw new BlockTypeException($"Incompatible block type! Type {type.Name} belongs to group {gr.Select(g => g.ToString()).Aggregate((a, b) => a + ", " + b)} instead of {group.Value}"); + throw new BlockTypeException($"Incompatible block type! Type {type.Name} belongs to group {gr.Select(g => ((uint)g).ToString()).Aggregate((a, b) => a + ", " + b)} instead of {(uint)group.Value}"); } if (initializers.TryGetValue(type, out var func)) @@ -213,7 +213,7 @@ namespace GamecraftModdingAPI throw new BlockTypeException("The block has the wrong group! The type is " + GetType() + " while the group is " + id.groupID); } - else if (type != typeof(Block)) + else if (type != typeof(Block) && !typeof(CustomBlock).IsAssignableFrom(type)) Logging.LogWarning($"Unknown block type! Add {type} to the dictionary."); } diff --git a/GamecraftModdingAPI/Blocks/BlockEngine.cs b/GamecraftModdingAPI/Blocks/BlockEngine.cs index a365d54..53a7254 100644 --- a/GamecraftModdingAPI/Blocks/BlockEngine.cs +++ b/GamecraftModdingAPI/Blocks/BlockEngine.cs @@ -227,7 +227,7 @@ namespace GamecraftModdingAPI.Blocks for (int i = 0; i < joints.count; i++) { ref var joint = ref joints.buffer[i]; - if (joint.jointState == JointState.Broken) continue; + if (joint.isBroken) continue; if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA)); } diff --git a/GamecraftModdingAPI/Blocks/BlockIDs.cs b/GamecraftModdingAPI/Blocks/BlockIDs.cs index 9591ae2..ff08b51 100644 --- a/GamecraftModdingAPI/Blocks/BlockIDs.cs +++ b/GamecraftModdingAPI/Blocks/BlockIDs.cs @@ -71,8 +71,9 @@ namespace GamecraftModdingAPI.Blocks GlassConeSegment, GlassCylinder, GlassSphere, - Lever, //63 - two IDs skipped - PlayerSpawn = 66, //Crashes without special handling + Lever, //63 + WoodenSlatsDoor = 65, + PlayerSpawn, //Crashes without special handling SmallSpawn, MediumSpawn, LargeSpawn, @@ -86,10 +87,17 @@ namespace GamecraftModdingAPI.Blocks DampedSpring, ServoPiston, StepperPiston, - PneumaticPiston, + PneumaticPiston, //80 PneumaticHinge, - PneumaticAxle, //82 - PilotSeat = 90, //Might crash + PneumaticAxle, + WindowedDoor, + Bench, + Chair, + Stool, + DampedHingeSpring, + PlainGlassDoor, + PlainWoodenDoor, + PilotSeat, //Might crash PassengerSeat, PilotControls, GrassCube, @@ -148,8 +156,9 @@ namespace GamecraftModdingAPI.Blocks WoodCylinder, WoodHemisphere, WoodSphere, - BrickCube, //149 - BrickSlicedCube = 151, + BrickCube, + DampedAxleSpring, //150 + BrickSlicedCube, BrickSlope, BrickCorner, ConcreteCube, @@ -355,6 +364,7 @@ namespace GamecraftModdingAPI.Blocks HexNetCylinder = 797, HexNetHemisphere, HexNetSphere, - HexNetTubeCorner //800 + HexNetTubeCorner, //800 + CenterOfMassBlock = 1346 } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockTests.cs b/GamecraftModdingAPI/Blocks/BlockTests.cs index 5447f6c..dbcd39e 100644 --- a/GamecraftModdingAPI/Blocks/BlockTests.cs +++ b/GamecraftModdingAPI/Blocks/BlockTests.cs @@ -1,6 +1,7 @@ using System; using Gamecraft.Wires; +using Unity.Mathematics; using GamecraftModdingAPI; using GamecraftModdingAPI.Tests; @@ -60,19 +61,31 @@ namespace GamecraftModdingAPI.Blocks Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized Piston is not null.")) return; if (!Assert.CloseTo(b.MaximumExtension, 1.01f, $"Piston.MaximumExtension {b.MaximumExtension} does not equal default value, possibly because it failed silently.", "Piston.MaximumExtension is close enough to default.")) return; - if (!Assert.CloseTo(b.MaximumForce, 750f, $"Piston.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Piston.MaximumForce is close enough to default.")) return; + if (!Assert.CloseTo(b.MaximumForce, 1.0f, $"Piston.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Piston.MaximumForce is close enough to default.")) return; } - [APITestCase(TestType.EditMode)] + [APITestCase(TestType.EditMode)] public static void TestServo() { - Block newBlock = Block.PlaceNew(BlockIDs.ServoAxle, Unity.Mathematics.float3.zero + 1); - Servo b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler - Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized Servo is not null.")) return; - if (!Assert.CloseTo(b.MaximumAngle, 180f, $"Servo.MaximumAngle {b.MaximumAngle} does not equal default value, possibly because it failed silently.", "Servo.MaximumAngle is close enough to default.")) return; + Block newBlock = Block.PlaceNew(BlockIDs.ServoAxle, Unity.Mathematics.float3.zero + 1); + Servo b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler + Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); + if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized Servo is not null.")) return; + if (!Assert.CloseTo(b.MaximumAngle, 180f, $"Servo.MaximumAngle {b.MaximumAngle} does not equal default value, possibly because it failed silently.", "Servo.MaximumAngle is close enough to default.")) return; if (!Assert.CloseTo(b.MinimumAngle, -180f, $"Servo.MinimumAngle {b.MinimumAngle} does not equal default value, possibly because it failed silently.", "Servo.MinimumAngle is close enough to default.")) return; - if (!Assert.CloseTo(b.MaximumForce, 750f, $"Servo.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Servo.MaximumForce is close enough to default.")) return; + if (!Assert.CloseTo(b.MaximumForce, 60f, $"Servo.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Servo.MaximumForce is close enough to default.")) return; + } + + [APITestCase(TestType.EditMode)] + public static void TestDampedSpring() + { + Block newBlock = Block.PlaceNew(BlockIDs.DampedSpring, Unity.Mathematics.float3.zero + 1); + DampedSpring b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler + Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); + if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized DampedSpring is not null.")) return; + if (!Assert.CloseTo(b.Stiffness, 1.0f, $"DampedSpring.Stiffness {b.Stiffness} does not equal default value, possibly because it failed silently.", "DampedSpring.Stiffness is close enough to default.")) return; + if (!Assert.CloseTo(b.Damping, 0.1f, $"DampedSpring.Damping {b.Damping} does not equal default value, possibly because it failed silently.", "DampedSpring.Damping is close enough to default.")) return; + if (!Assert.CloseTo(b.MaxExtension, 0.3f, $"DampedSpring.MaxExtension {b.MaxExtension} does not equal default value, possibly because it failed silently.", "DampedSpring.MaxExtension is close enough to default.")) return; } [APITestCase(TestType.Game)] @@ -119,6 +132,13 @@ namespace GamecraftModdingAPI.Blocks if (!Assert.Errorless(() => { newWire = b.Connect(0, target, 0);})) return; if (!Assert.NotNull(newWire, "SignalingBlock.Connect(...) returned null, possible because it failed silently.", "SignalingBlock.Connect(...) returned a non-null value.")) return; } + + [APITestCase(TestType.EditMode)] + public static void TestSpecialiseError() + { + Block newBlock = Block.PlaceNew(BlockIDs.Bench, new float3(1, 1, 1)); + if (Assert.Errorful(() => newBlock.Specialise(), "Block.Specialise() was expected to error on a bench block.", "Block.Specialise() errored as expected for a bench block.")) return; + } } #endif } diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs new file mode 100644 index 0000000..15cfb15 --- /dev/null +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using HarmonyLib; + +using DataLoader; +using RobocraftX.Blocks; +using RobocraftX.Rendering; +using Svelto.ECS; +using Svelto.Tasks; +using UnityEngine; +using UnityEngine.AddressableAssets; +using Material = UnityEngine.Material; + +using GamecraftModdingAPI.Utility; + +namespace GamecraftModdingAPI.Blocks +{ + public class CustomBlock : Block + { + private static ushort nextID = 500; + /// + /// Key: Prefab path + /// + private static Dictionary _customBlocks = new Dictionary(); + + private static bool _canRegister = true; + + /// + /// Register a custom block type. Call it as soon as possible (in OnApplicationStart()).
+ /// You need a Unity project with Addressables and Havok installed and need a prefab added as an addressable asset. + /// Build the addressables and the project and copy the catalog.json from StreamingAssets, you'll need to reference this file. + /// Also copy the asset files from the subfolder to the same path in the game. + ///
+ /// The custom block type + public static void RegisterCustomBlock() where T : CustomBlock + { + if (!_canRegister) + throw new InvalidOperationException( + "It's too late to register custom blocks. Register it before the game starts loading."); + var type = typeof(T); + var attr = type.GetCustomAttribute(); + if (attr == null) + throw new ArgumentException("The custom block type is missing the CustomBlock annotation"); + string typeName = type.FullName ?? + throw new ArgumentException("The given block type doesn't have a concrete full name."); + if (!File.Exists(attr.Catalog)) + throw new FileNotFoundException("The specified catalog cannot be found for " + typeName); + _customBlocks.Add(attr.AssetPath, type); + Logging.MetaDebugLog("Registered custom block type " + typeName); + } + + public CustomBlock(EGID id) : base(id) + { + /*if (id.groupID != Group) + throw new BlockTypeException("The block is not a custom block! It has a group of " + id.groupID);*/ + } + + public CustomBlock(uint id) : base(id) + { + } + + //public static ExclusiveGroup Group { get; } = new ExclusiveGroup("Custom block"); + + [HarmonyPatch] + public static class MaterialCopyPatch + { + private static Material[] materials; + + public static void Prefix(List prefabData, IList prefabs) + { + for (var index = 0; index < prefabs.Count; index++) + { + if (prefabData[index].prefabName == "ConsoleBlock") + materials = prefabs[index].GetComponentsInChildren()[0].sharedMaterials; + } + + for (var index = 0; index < prefabs.Count; index++) + { + if (_customBlocks.ContainsKey(prefabData[index].prefabName)) //This is a custom block + prefabs[index].GetComponentsInChildren()[0].sharedMaterials = materials; + } + } + + public static MethodBase TargetMethod() + { //General block registration + return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs"); + } + } + + [HarmonyPatch] + public static class CubeRegistrationPatch + { + public static void Prefix(IDataDB dataDB) + { + //var abd = dataDB.GetValue((int) BlockIDs.AluminiumCube); + foreach (var (key, type) in _customBlocks) + { + var attr = type.GetCustomAttribute(); + var cld = new CubeListData + { //"Assets/Prefabs/Cube.prefab" - "CTR_CommandBlock" - "strConsoleBlock" + cubeType = attr.Type, + //cubeCategory = (CubeCategory) 1000, + cubeCategory = attr.Category, + inventoryCategory = attr.InventoryCategory, + ID = nextID++, + Path = attr.AssetPath, //Index out of range exception: Asset failed to load (wrong path) + SpriteName = attr.SpriteName, + CubeNameKey = attr.NameKey, + CubeDescriptionKey = attr.DescKey, + SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, + GridScale = new[] {5, 5, 5}, + Mass = attr.Mass, + Material = attr.Material, + scalingPermission = attr.ScalingPermission, + SortIndex = attr.SortIndex, + DefaultColour = attr.DefaultColor.Index, + Volume = attr.Volume, + EdgeConnectingFaces = new[] {0, 1, 2, 3, 4, 5}, + PointDataVolumeMultiplier = 1f + }; + dataDB.GetValues().Add(cld.ID.ToString(), cld); //The registration needs to happen after the ID has been set + dataDB.GetFasterValues().Add(cld.ID, cld); //So can't use the builtin method to create a CubeListData + } + + _canRegister = false; + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method("RobocraftX.CR.MainGame.MainGameCompositionRoot:Init"); + } + } + + /*[HarmonyPatch] - The block has no collision even in simulation if using a custom category + private static class FactorySetupPatch + { + public static void Prefix(BlockEntityFactory __instance) + { + var builders = (Dictionary) + AccessTools.Field(__instance.GetType(), "_blockBuilders").GetValue(__instance); + builders.Add((CubeCategory) 1000, new BlockBuilder(Group)); + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method(typeof(BlockEntityFactory), "ParseDataDB"); + } + }*/ + + internal static IEnumerator Prepare() + { //Should be pretty quick + foreach (var type in _customBlocks.Values) + { + var attr = type.GetCustomAttribute(); + Logging.Log("Loading custom block catalog " + attr.Catalog); + var res = Addressables.LoadContentCatalogAsync(attr.Catalog); + while (!res.IsDone) yield return Yield.It; + Logging.Log("Loaded custom block catalog: " + res.Result.LocatorId); + Addressables.AddResourceLocator(res.Result); + } + } + + /*internal static void OnBlockFactoryObtained(BlockEntityFactory factory) + { + var builders = (Dictionary) + AccessTools.Field(factory.GetType(), "_blockBuilders").GetValue(factory); + builders.Add((CubeCategory) 1000, new BlockBuilder(Group)); + }*/ + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/CustomBlockAttribute.cs b/GamecraftModdingAPI/Blocks/CustomBlockAttribute.cs new file mode 100644 index 0000000..7c7669f --- /dev/null +++ b/GamecraftModdingAPI/Blocks/CustomBlockAttribute.cs @@ -0,0 +1,85 @@ +using System; +using DataLoader; + +namespace GamecraftModdingAPI.Blocks +{ + [AttributeUsage(AttributeTargets.Class)] + public class CustomBlockAttribute : Attribute + { + /// + /// Custom block attribute necessary for configuration. + /// + /// File path to the catalog.json that holds asset references for the custom block + /// The path/address to the block's prefab specified in Unity + /// The translation key for the block's name + /// The path to the inventory sprite for the block, console block by default + /// The translation key for the block's description + public CustomBlockAttribute(string catalog, string assetPath, string nameKey, + string spriteName = "CTR_CommandBlock", string descKey = "") + { + Catalog = catalog; + AssetPath = assetPath; + SpriteName = spriteName; + NameKey = nameKey; + DescKey = descKey; + } + + /// + /// The location of the catalog.json file used to find assets for this block. + /// + public string Catalog { get; } + /// + /// The asset path/address for the block's prefab. + /// + public string AssetPath { get; } + /// + /// The name of the sprite used in the inventory. + /// + public string SpriteName { get; } + /// + /// The translation key for the block's name. + /// + public string NameKey { get; } + /// + /// The translation key for the block's description. + /// + public string DescKey { get; } + + /// + /// The block's type - block, joint, light. + /// + public CubeType Type { get; set; } = CubeType.Block; + /// + /// The block's category, so it's treated as a pre-existing functional block. + /// + public CubeCategory Category { get; set; } = CubeCategory.General; + /// + /// The block's inventory category. + /// + public InventoryCategory InventoryCategory { get; set; } = InventoryCategory.Shapes; + /// + /// The block's mass. + /// + public float Mass { get; set; } = 1f; + /// + /// The key of the material properties this block should use. + /// + public string Material { get; set; } = "Aluminium"; + /// + /// The scaling permission determining what scaling is allowed on this block. + /// + public ScalingPermission ScalingPermission { get; set; } + /// + /// The sort index in the inventory. + /// + public int SortIndex { get; set; } + /// + /// The default color of the block when placed. + /// + public BlockColor DefaultColor { get; set; } + /// + /// The volume of the block. + /// + public float Volume { get; set; } = 1f; + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/DampedSpring.cs b/GamecraftModdingAPI/Blocks/DampedSpring.cs index 5a5a936..20d28f2 100644 --- a/GamecraftModdingAPI/Blocks/DampedSpring.cs +++ b/GamecraftModdingAPI/Blocks/DampedSpring.cs @@ -19,10 +19,10 @@ namespace GamecraftModdingAPI.Blocks /// public float MaxForce { - get => BlockEngine.GetBlockInfo(this, (DampedSpringReadOnlyStruct dsrs) => dsrs.maxForce); + get => BlockEngine.GetBlockInfo(this, (DampedSpringReadOnlyStruct dsrs) => dsrs.springFrequency); set => BlockEngine.SetBlockInfo(this, - (ref DampedSpringReadOnlyStruct dsrs, float val) => dsrs.maxForce = val, value); + (ref DampedSpringReadOnlyStruct dsrs, float val) => dsrs.springFrequency = val, value); } /// @@ -39,10 +39,21 @@ namespace GamecraftModdingAPI.Blocks /// public float Damping { - get => BlockEngine.GetBlockInfo(this, (LinearJointForcesReadOnlyStruct ljf) => ljf.dampingForceMagnitude); + get => BlockEngine.GetBlockInfo(this, (DampedSpringReadOnlyStruct ljf) => ljf.springDamping); set => BlockEngine.SetBlockInfo(this, - (ref LinearJointForcesReadOnlyStruct ljf, float val) => ljf.dampingForceMagnitude = val, value); + (ref DampedSpringReadOnlyStruct ljf, float val) => ljf.springDamping = val, value); + } + + /// + /// The spring's maximum extension. + /// + public float MaxExtension + { + get => BlockEngine.GetBlockInfo(this, (DampedSpringReadOnlyStruct ljf) => ljf.maxExtent); + + set => BlockEngine.SetBlockInfo(this, + (ref DampedSpringReadOnlyStruct ljf, float val) => ljf.maxExtent = val, value); } } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/Piston.cs b/GamecraftModdingAPI/Blocks/Piston.cs index 04b3aeb..d05ac20 100644 --- a/GamecraftModdingAPI/Blocks/Piston.cs +++ b/GamecraftModdingAPI/Blocks/Piston.cs @@ -40,11 +40,11 @@ namespace GamecraftModdingAPI.Blocks /// public float MaximumForce { - get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxForce); + get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.pistonVelocity); set { - BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxForce = val, value); + BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.pistonVelocity = val, value); } } } diff --git a/GamecraftModdingAPI/Blocks/Servo.cs b/GamecraftModdingAPI/Blocks/Servo.cs index 606a48a..a5dceec 100644 --- a/GamecraftModdingAPI/Blocks/Servo.cs +++ b/GamecraftModdingAPI/Blocks/Servo.cs @@ -52,11 +52,11 @@ namespace GamecraftModdingAPI.Blocks /// public float MaximumForce { - get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxForce); + get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.servoVelocity); set { - BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxForce = val, value); + BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.servoVelocity = val, value); } } diff --git a/GamecraftModdingAPI/Blocks/TextBlock.cs b/GamecraftModdingAPI/Blocks/TextBlock.cs index d7d620a..3d46611 100644 --- a/GamecraftModdingAPI/Blocks/TextBlock.cs +++ b/GamecraftModdingAPI/Blocks/TextBlock.cs @@ -35,10 +35,8 @@ namespace GamecraftModdingAPI.Blocks BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) => { tbds.textCurrent.Set(val); - tbds.textStored.Set(val); + tbds.textStored.Set(val, true); }, value); - BlockEngine.SetBlockInfo(this, - (ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockStringContent.Set(val), value); } } @@ -54,8 +52,6 @@ namespace GamecraftModdingAPI.Blocks if (value == null) value = ""; BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) => tbds.textBlockID.Set(val), value); - BlockEngine.SetBlockInfo(this, - (ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockID.Set(val), value); } } } diff --git a/GamecraftModdingAPI/GamecraftModdingAPI.csproj b/GamecraftModdingAPI/GamecraftModdingAPI.csproj index 3cdfa73..8b6404c 100644 --- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj +++ b/GamecraftModdingAPI/GamecraftModdingAPI.csproj @@ -79,9 +79,13 @@ ..\ref\Gamecraft_Data\Managed\DDNA.dll ..\..\ref\Gamecraft_Data\Managed\DDNA.dll - - ..\ref\Gamecraft_Data\Managed\FMOD.dll - ..\..\ref\Gamecraft_Data\Managed\FMOD.dll + + ..\ref\Gamecraft_Data\Managed\FMODUnity.dll + ..\..\ref\Gamecraft_Data\Managed\FMODUnity.dll + + + ..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll + ..\..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll ..\ref\Gamecraft_Data\Managed\FullGame.dll @@ -267,6 +271,10 @@ ..\ref\Gamecraft_Data\Managed\Gamecraft.Music.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Music.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.NetStrings.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.NetStrings.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.PerformanceWarnings.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.PerformanceWarnings.dll @@ -287,6 +295,10 @@ ..\ref\Gamecraft_Data\Managed\Gamecraft.Projectiles.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Projectiles.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.Serialization.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Serialization.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.dll @@ -475,6 +487,10 @@ ..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll + + ..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.Serializers.dll + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.Serializers.dll + ..\ref\Gamecraft_Data\Managed\RobocraftX.MultiplayerInput.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.MultiplayerInput.dll @@ -519,10 +535,6 @@ ..\ref\Gamecraft_Data\Managed\RobocraftX.SaveGameDialog.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.SaveGameDialog.dll - - ..\ref\Gamecraft_Data\Managed\RobocraftX.Serializers.dll - ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Serializers.dll - ..\ref\Gamecraft_Data\Managed\RobocraftX.Services.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Services.dll diff --git a/GamecraftModdingAPI/Main.cs b/GamecraftModdingAPI/Main.cs index d957a46..66e923e 100644 --- a/GamecraftModdingAPI/Main.cs +++ b/GamecraftModdingAPI/Main.cs @@ -1,12 +1,14 @@ using System; using System.Reflection; -using GamecraftModdingAPI.Blocks; using HarmonyLib; using RobocraftX; +using RobocraftX.Schedulers; using RobocraftX.Services; using Svelto.Context; +using Svelto.Tasks.ExtraLean; +using GamecraftModdingAPI.Blocks; using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Events; using GamecraftModdingAPI.Tasks; @@ -89,6 +91,7 @@ namespace GamecraftModdingAPI AsyncUtils.Init(); GamecraftModdingAPI.App.Client.Init(); GamecraftModdingAPI.App.Game.Init(); + CustomBlock.Prepare().RunOn(ExtraLean.UIScheduler); // init UI Interface.IMGUI.Constants.Init(); Interface.IMGUI.IMGUIManager.Init(); diff --git a/GamecraftModdingAPI/Tests/Assert.cs b/GamecraftModdingAPI/Tests/Assert.cs index 78f0597..edc1392 100644 --- a/GamecraftModdingAPI/Tests/Assert.cs +++ b/GamecraftModdingAPI/Tests/Assert.cs @@ -119,6 +119,25 @@ namespace GamecraftModdingAPI.Tests return true; } + public static bool Errorful(Action tryThis, string err = null, string success = null) where T : Exception + { + if (err == null) err = $"{tryThis} was expected to error but completed without errors."; + if (success == null) success = $"{tryThis} completed with an expected error."; + try + { + tryThis(); + } + catch (T e) + { + TestRoot.TestsPassed = true; + Log(PASS + success + " " + e.GetType().Name + ": " + e.Message); + return true; + } + Log(FAIL + err); + TestRoot.TestsPassed = false; + return false; + } + public static bool CloseTo(float a, float b, string err = null, string success = null, float delta = float.Epsilon) { if (err == null) err = $"{a} is not within {delta} of {b}."; diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index e3abdca..dd96226 100644 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -17,6 +18,9 @@ using RobocraftX.SimulationModeState; using RobocraftX.FrontEnd; using Unity.Mathematics; using UnityEngine; +using RobocraftX.Schedulers; +using Svelto.Tasks.ExtraLean; +using uREPL; using GamecraftModdingAPI.Commands; using GamecraftModdingAPI.Events; @@ -296,6 +300,13 @@ namespace GamecraftModdingAPI.Tests .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(); + GameClient.SetDebugInfo("InstalledMods", InstalledMods); Block.Placed += (sender, args) => Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID); @@ -356,7 +367,7 @@ namespace GamecraftModdingAPI.Tests Logging.Log("Compatible GamecraftScripting detected"); } // Interface test - /*Interface.IMGUI.Group uiGroup = new Group(new Rect(20, 20, 200, 500), "GamecraftModdingAPI_UITestGroup", true); + Interface.IMGUI.Group uiGroup = new Group(new Rect(20, 20, 200, 500), "GamecraftModdingAPI_UITestGroup", true); Interface.IMGUI.Button button = new Button("TEST"); button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");}; Interface.IMGUI.Button button2 = new Button("TEST2"); @@ -380,7 +391,27 @@ namespace GamecraftModdingAPI.Tests uiImg.Enabled = true; Logging.MetaDebugLog($"Got blue bg asset {handle.Result}"); }; - */ + + + CommandBuilder.Builder("enableCompletions") + .Action(() => + { + var p = Window.selected.main.parameters; + p.useCommandCompletion = true; + p.useMonoCompletion = true; + p.useGlobalClassCompletion = true; + Log.Output("Submitted: " + Window.selected.submittedCode); + }) + .Build(); + try + { + CustomBlock.RegisterCustomBlock(); + Logging.MetaDebugLog("Registered test custom block"); + } + catch (FileNotFoundException) + { + Logging.MetaDebugLog("Test custom block catalog not found"); + } #if TEST TestRoot.RunTests(); #endif @@ -443,6 +474,18 @@ namespace GamecraftModdingAPI.Tests return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method; } } + + [CustomBlock("customCatalog.json", "Assets/Prefabs/Cube.prefab", "strAluminiumCube", SortIndex = 12)] + public class TestBlock : CustomBlock + { + public TestBlock(EGID id) : base(id) + { + } + + public TestBlock(uint id) : base(id) + { + } + } } #endif } diff --git a/GamecraftModdingAPI/Utility/Audio.cs b/GamecraftModdingAPI/Utility/Audio.cs index 9d227a8..b2566f2 100644 --- a/GamecraftModdingAPI/Utility/Audio.cs +++ b/GamecraftModdingAPI/Utility/Audio.cs @@ -43,40 +43,24 @@ namespace GamecraftModdingAPI.Utility { get { - sound.getParameterValue(key, out float val, out float finalVal); + sound.getParameterByName(key, out float val, out float finalVal); return val; } - set => sound.setParameterValue(key, value); + set => sound.setParameterByName(key, value); } - public float this[int index] + public float this[PARAMETER_ID index] { get { - sound.getParameterValueByIndex(index, out float val, out float finalVal); + sound.getParameterByID(index, out float val, out float finalVal); return val; } - set => sound.setParameterValueByIndex(index, value); + set => sound.setParameterByID(index, value); } - public string[] Parameters - { - get - { - sound.getParameterCount(out int count); - string[] parameters = new string[count]; - for (int i = 0; i < count; i++) - { - sound.getParameterByIndex(i, out ParameterInstance param); - param.getDescription(out PARAMETER_DESCRIPTION desc); - parameters[i] = desc.name; - } - return parameters; - } - } - public float3 Position { get