diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs
index 524847d..4aa0a30 100644
--- a/GamecraftModdingAPI/Block.cs
+++ b/GamecraftModdingAPI/Block.cs
@@ -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/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs
new file mode 100644
index 0000000..331552c
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using DataLoader;
+using GamecraftModdingAPI.App;
+using GamecraftModdingAPI.Utility;
+using GPUInstancer;
+using HarmonyLib;
+using RobocraftX.Blocks;
+using RobocraftX.Common;
+using RobocraftX.Rendering;
+using Svelto.DataStructures;
+using Svelto.ECS;
+using Svelto.Tasks;
+using Unity.Entities.Conversion;
+using Unity.Physics;
+using UnityEngine;
+using UnityEngine.AddressableAssets;
+using BoxCollider = UnityEngine.BoxCollider;
+using Material = UnityEngine.Material;
+using Object = UnityEngine.Object;
+using ScalingPermission = DataLoader.ScalingPermission;
+
+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) : this(new EGID(id, Group))
+ {
+ }
+
+ public static ExclusiveGroup Group { get; } = new ExclusiveGroup("Custom block");
+
+ [HarmonyPatch]
+ public static class Patch
+ {
+ 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 = 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");
+ }
+ }
+
+ public static IEnumerator Prep()
+ { //TODO: Don't let the game load until this finishes
+ 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);
+ }
+ }
+ }
+}
\ 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..51da25e 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,10 @@ 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);
}
}
}
\ 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 c76ebd9..6324b9a 100644
--- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj
+++ b/GamecraftModdingAPI/GamecraftModdingAPI.csproj
@@ -78,9 +78,9 @@
..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll
..\..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.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\FullGame.dll
@@ -266,6 +266,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
@@ -286,6 +290,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
@@ -510,6 +518,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
@@ -554,10 +566,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/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs
index 81a814c..02c7d2c 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;
@@ -288,6 +292,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);
@@ -348,6 +359,27 @@ namespace GamecraftModdingAPI.Tests
Logging.Log("Compatible GamecraftScripting detected");
}
+ 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();
+ CustomBlock.Prep().RunOn(ExtraLean.UIScheduler);
+ try
+ {
+ CustomBlock.RegisterCustomBlock();
+ Logging.MetaDebugLog("Registered test custom block");
+ }
+ catch (FileNotFoundException)
+ {
+ Logging.MetaDebugLog("Test custom block catalog not found");
+ }
+
#if TEST
TestRoot.RunTests();
#endif
@@ -410,6 +442,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