From 9b1e2548d1c2ba3f82d2356d25921efadb588118 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 14 Jun 2020 21:40:47 +0200 Subject: [PATCH 1/9] Attempts to create custom block types It can load certain assets (a Cube from a sample) but fails because of missing shaders My own Cube doesn't even get that far --- GamecraftModdingAPI/Blocks/CustomBlock.cs | 159 ++++++++++++++++++ .../Tests/GamecraftModdingAPIPluginTest.cs | 19 +++ 2 files changed, 178 insertions(+) create mode 100644 GamecraftModdingAPI/Blocks/CustomBlock.cs diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs new file mode 100644 index 0000000..9801e40 --- /dev/null +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text.Formatting; +using DataLoader; +using GamecraftModdingAPI.Utility; +using GPUInstancer; +using HarmonyLib; +using RobocraftX.Common; +using RobocraftX.Schedulers; +using Svelto.Tasks; +using Svelto.Tasks.ExtraLean; +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace GamecraftModdingAPI.Blocks +{ + public class CustomBlock + { + private static ushort nextID = 500; + public static void RegisterCustomBlock(string path) + { + var prefabData = new List(); + //category ID: + //0 - regular + //1 - joint + //2 - controller + uint prefabId = PrefabsID.FetchNewPrefabID(PrefabsID.GenerateDBID(0, nextID++)); + prefabData.Add(new PrefabData() + { + prefabName = path, + prefabId = prefabId + }); + var loadTask = Addressables.LoadAssetAsync(path); + AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab") + .Invoke(ECSGPUIResourceManager.Instance, new object[] {prefabId, loadTask.Result, 1}); + } + + [HarmonyPatch] + public static class Patch + { + /*public static IEnumerable Transpiler(IEnumerable instructions) + { + var list = new List(instructions); + try + { + *int index = -1; + CodeInstruction loadTask = null; + for (var i = 0; i < list.Count - 1; i++) + { + if (list[i].opcode == OpCodes.Ldfld + && ((string) list[i].operand).Contains("renderingWorld") + && list[i + 1].opcode == OpCodes.Brfalse_S) + index = i - 1; //It loads 'this' first + if (list[i].opcode == OpCodes.Ldflda + && ((string) list[i].operand).Contains("loadTask")) + loadTask = new CodeInstruction(list[i]); + }* + + var array = new[] + { + //Set Yield.It to be returned (current) + new CodeInstruction(OpCodes.Ldarg_0), // this + new CodeInstruction(OpCodes.Ldsfld, + typeof(Yield).GetField("It", BindingFlags.Public | BindingFlags.Static)), + new CodeInstruction(OpCodes.Stfld, "object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>2__current'"), + + //Set which yield return we're at (state) + new CodeInstruction(OpCodes.Ldarg_0), // this + new CodeInstruction(OpCodes.Ldc_I4_1), + //new CodeInstruction(OpCodes.Call, ((Action)AddInfo).Method) + }; + list.InsertRange(index, array); + * + IL_00ad: ldarg.0 // this + IL_00ae: ldsfld class [Svelto.Tasks]Svelto.Tasks.Yield [Svelto.Tasks]Svelto.Tasks.Yield::It + IL_00b3: stfld object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>2__current' + + IL_0072: ldarg.0 // this + IL_0073: ldnull + IL_0074: stfld object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>2__current' + IL_0079: ldarg.0 // this + IL_007a: ldc.i4.2 + IL_007b: stfld object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>1__state' + IL_0080: ldc.i4.1 + IL_0081: ret + * + yield break; + } + catch (Exception e) + { + Logging.LogWarning("Failed to inject AddInfo method for the debug display!\n" + e); + } + }*/ + + public static void Prefix(ref Dictionary blocks) + { + /*foreach (var block in blocks.Values.Cast()) + { + Console.WriteLine("Block info: " + block); + }*/ + + /*var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); + while (!res.IsDone) yield return Yield.It;*/ + + blocks.Add("modded_ConsoleBlock", new CubeListData + { + cubeType = CubeType.Block, + cubeCategory = CubeCategory.ConsoleBlock, + inventoryCategory = InventoryCategory.Logic, + ID = 500, + Path = "Assets/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) + SpriteName = "CTR_CommandBlock", + CubeNameKey = "strConsoleBlock", + SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, + GridScale = new[] {1, 1, 1}, + Mass = 1, + JointBreakAngle = 1 + }); + } + + public static MethodBase TargetMethod() + { //General block registration + return AccessTools.Method("RobocraftX.Blocks.BlocksCompositionRoot:RegisterPartPrefabs"); + } + } + + [HarmonyPatch] + public static class GOPatch + { + public static void Prefix(uint prefabID, GameObject gameObject) + { + Console.WriteLine("ID: " + prefabID + " - Name: " + gameObject.name); + if (gameObject.name == "Cube") + ECSGPUIResourceManager.Instance.RegisterRuntimePrefabs( + new[] {new PrefabData {prefabId = 500, prefabName = "Assets/Cube.prefab"}}, + new List {gameObject}, 1).Complete(); + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab", + new[] {typeof(uint), typeof(GameObject), typeof(uint)}); + } + } + + public static IEnumerator Prep() + { + Console.WriteLine("Loading custom catalog..."); + var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); + while (!res.IsDone) yield return Yield.It; + Console.WriteLine("Loaded custom catalog: " + res.Result.LocatorId); + Addressables.AddResourceLocator(res.Result); + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index dc07e1e..c4077d0 100644 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs @@ -13,6 +13,9 @@ using RobocraftX.Common; using RobocraftX.SimulationModeState; using RobocraftX.FrontEnd; using Unity.Mathematics; +using RobocraftX.Schedulers; +using Svelto.Tasks.ExtraLean; +using uREPL; using GamecraftModdingAPI.Commands; using GamecraftModdingAPI.Events; @@ -283,6 +286,22 @@ 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(); + /*JObject o1 = JObject.Parse(File.ReadAllText(@"Gamecraft_Data\StreamingAssets\aa\Windows\catalog.json")); + JObject o2 = JObject.Parse(File.ReadAllText(@"customCatalog.json")); + o1.Merge(o2, new JsonMergeSettings {MergeArrayHandling = MergeArrayHandling.Union}); + File.WriteAllText(@"Gamecraft_Data\StreamingAssets\aa\Windows\catalog.json", o1.ToString());*/ + CustomBlock.Prep().RunOn(ExtraLean.UIScheduler); } private string modsString; From c06ed340a255030be8480a7033c347a0c3f491bd Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 17 Sep 2020 23:08:26 +0200 Subject: [PATCH 2/9] Using the console block's material Progressed a lot --- GamecraftModdingAPI/Blocks/CustomBlock.cs | 46 +++++++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs index 9801e40..bc67fe9 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -13,6 +13,7 @@ using RobocraftX.Common; using RobocraftX.Schedulers; using Svelto.Tasks; using Svelto.Tasks.ExtraLean; +using Unity.Entities; using UnityEngine; using UnityEngine.AddressableAssets; @@ -112,7 +113,7 @@ namespace GamecraftModdingAPI.Blocks cubeCategory = CubeCategory.ConsoleBlock, inventoryCategory = InventoryCategory.Logic, ID = 500, - Path = "Assets/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) + Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) SpriteName = "CTR_CommandBlock", CubeNameKey = "strConsoleBlock", SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, @@ -131,19 +132,51 @@ namespace GamecraftModdingAPI.Blocks [HarmonyPatch] public static class GOPatch { + private static Material[] materials; public static void Prefix(uint prefabID, GameObject gameObject) { Console.WriteLine("ID: " + prefabID + " - Name: " + gameObject.name); if (gameObject.name == "Cube") + { + //Console.WriteLine("Length: " + gameObject.GetComponentsInChildren().Length); + if (materials != null) + gameObject.GetComponentsInChildren()[0].sharedMaterials = materials; ECSGPUIResourceManager.Instance.RegisterRuntimePrefabs( - new[] {new PrefabData {prefabId = 500, prefabName = "Assets/Cube.prefab"}}, - new List {gameObject}, 1).Complete(); + new[] {new PrefabData {prefabId = 500, prefabName = "Assets/Prefabs/Cube.prefab"}}, + new List {gameObject}).Complete(); + GPUInstancerAPI.AddInstancesToPrefabPrototypeAtRuntime(ECSGPUIResourceManager.Instance.prefabManager, + gameObject.GetComponent().prefabPrototype, new[] {gameObject}); + Console.WriteLine("Registered prefab to instancer"); + + var register = AccessTools.Method("RobocraftX.Common.ECSPhysicResourceManager:RegisterPrefab", + new[] {typeof(uint), typeof(GameObject), typeof(World), typeof(BlobAssetStore)}); + register.Invoke(ECSPhysicResourceManager.Instance, + new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2}); + Console.WriteLine("Registered prefab to physics"); + } + else if (gameObject.name == "CTR_CommandBlock") + materials = gameObject.GetComponentsInChildren()[0].sharedMaterials; } public static MethodBase TargetMethod() { return AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab", - new[] {typeof(uint), typeof(GameObject), typeof(uint)}); + new[] {typeof(uint), typeof(GameObject)}); + } + } + + [HarmonyPatch] + public static class MGPatch + { + internal static (World, BlobAssetStore) data; + public static void Prefix(World physicsWorld, BlobAssetStore blobStore) + { + data = (physicsWorld, blobStore); + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method("RobocraftX.CR.MainGame.MainGameCompositionRoot:Init"); } } @@ -154,6 +187,11 @@ namespace GamecraftModdingAPI.Blocks while (!res.IsDone) yield return Yield.It; Console.WriteLine("Loaded custom catalog: " + res.Result.LocatorId); Addressables.AddResourceLocator(res.Result); + /*Console.WriteLine("Loading Cube asset..."); + var loadTask = Addressables.LoadAssetAsync("Assets/Cube.prefab"); + while (!loadTask.IsDone) yield return Yield.It; + Console.WriteLine("Exception: "+loadTask.OperationException); + Console.WriteLine("Result: " + loadTask.Result.name);*/ } } } \ No newline at end of file From b0b496f22f40d0ff4a841cb77de2062c4072b58c Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 22 Oct 2020 02:34:59 +0200 Subject: [PATCH 3/9] Fix ConcurrentModificationException and some attempts --- GamecraftModdingAPI/Blocks/CustomBlock.cs | 108 ++++++++++++++---- .../GamecraftModdingAPI.csproj | 4 + 2 files changed, 91 insertions(+), 21 deletions(-) diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs index bc67fe9..b960101 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -9,13 +9,21 @@ using DataLoader; using GamecraftModdingAPI.Utility; using GPUInstancer; using HarmonyLib; +using RobocraftX.Blocks.Ghost; using RobocraftX.Common; using RobocraftX.Schedulers; +using Svelto.DataStructures; +using Svelto.ECS.Extensions.Unity; using Svelto.Tasks; using Svelto.Tasks.ExtraLean; using Unity.Entities; +using Unity.Entities.Conversion; +using Unity.Physics; using UnityEngine; using UnityEngine.AddressableAssets; +using BoxCollider = UnityEngine.BoxCollider; +using Collider = UnityEngine.Collider; +using Material = UnityEngine.Material; namespace GamecraftModdingAPI.Blocks { @@ -106,21 +114,6 @@ namespace GamecraftModdingAPI.Blocks /*var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); while (!res.IsDone) yield return Yield.It;*/ - - blocks.Add("modded_ConsoleBlock", new CubeListData - { - cubeType = CubeType.Block, - cubeCategory = CubeCategory.ConsoleBlock, - inventoryCategory = InventoryCategory.Logic, - ID = 500, - Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) - SpriteName = "CTR_CommandBlock", - CubeNameKey = "strConsoleBlock", - SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, - GridScale = new[] {1, 1, 1}, - Mass = 1, - JointBreakAngle = 1 - }); } public static MethodBase TargetMethod() @@ -130,7 +123,7 @@ namespace GamecraftModdingAPI.Blocks } [HarmonyPatch] - public static class GOPatch + public static class RendererPatch { private static Material[] materials; public static void Prefix(uint prefabID, GameObject gameObject) @@ -142,16 +135,30 @@ namespace GamecraftModdingAPI.Blocks if (materials != null) gameObject.GetComponentsInChildren()[0].sharedMaterials = materials; ECSGPUIResourceManager.Instance.RegisterRuntimePrefabs( - new[] {new PrefabData {prefabId = 500, prefabName = "Assets/Prefabs/Cube.prefab"}}, + new[] {new PrefabData {prefabId = prefabID, prefabName = "Assets/Prefabs/Cube.prefab"}}, new List {gameObject}).Complete(); GPUInstancerAPI.AddInstancesToPrefabPrototypeAtRuntime(ECSGPUIResourceManager.Instance.prefabManager, gameObject.GetComponent().prefabPrototype, new[] {gameObject}); Console.WriteLine("Registered prefab to instancer"); - var register = AccessTools.Method("RobocraftX.Common.ECSPhysicResourceManager:RegisterPrefab", + /*var register = AccessTools.Method("RobocraftX.Common.ECSPhysicResourceManager:RegisterPrefab", new[] {typeof(uint), typeof(GameObject), typeof(World), typeof(BlobAssetStore)}); register.Invoke(ECSPhysicResourceManager.Instance, - new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2}); + new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2});*/ + /*Console.WriteLine( + "Entity: " + ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID)); + Console.WriteLine("Prefab ID: " + PrefabsID.DBIDMAP[500]); + PhysicsCollider componentData = MGPatch.data.Item1.EntityManager.GetComponentData(ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID)); + Console.WriteLine("Collider valid: " + componentData.IsValid); + unsafe + { + Console.WriteLine("Collider type: " + componentData.ColliderPtr->Type); + CollisionFilter filter = componentData.Value.Value.Filter; + Console.WriteLine("Filter not empty: " + !filter.IsEmpty); + }*/ + //MGPatch.data.Item1.EntityManager.GetComponentData<>() + gameObject.AddComponent(); + gameObject.AddComponent(); Console.WriteLine("Registered prefab to physics"); } else if (gameObject.name == "CTR_CommandBlock") @@ -165,13 +172,72 @@ namespace GamecraftModdingAPI.Blocks } } + [HarmonyPatch] + public static class RMPatch + { + public static void Prefix(World physicsWorld, + GameObjectConversionSystem getExistingSystem, + FasterList gos, + List prefabData) + { + for (var index = 0; index < prefabData.Count; index++) + { + var data = prefabData[index]; + if (!data.prefabName.EndsWith("Cube.prefab")) continue; + //getExistingSystem.DeclareLinkedEntityGroup(gos[index]); + /*Entity entity = GameObjectConversionUtility.ConvertGameObjectHierarchy(gos[index], + GameObjectConversionSettings.FromWorld(physicsWorld, MGPatch.data.Item2));*/ + Console.WriteLine("Transform: " + gos[index].transform.childCount); + Entity primaryEntity = getExistingSystem.GetPrimaryEntity(gos[index]); + MultiListEnumerator entities = getExistingSystem.GetEntities(gos[index]); + Console.WriteLine("ID: " + data.prefabId + " - Name: " + data.prefabName); + Console.WriteLine("Primary entity: " + primaryEntity); + EntityManager entityManager = physicsWorld.EntityManager; + Console.WriteLine("Has collider: " + entityManager.HasComponent(primaryEntity)); + while (entities.MoveNext()) + { + Entity current = entities.Current; + Console.WriteLine("Entity " + current + " has collider: " + + entityManager.HasComponent(current)); + } + } + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method("RobocraftX.Common.ECSResourceManagerUtility:RelinkEntities", + new[] + { + typeof(World), + typeof(GameObjectConversionSystem), + typeof(FasterList), + typeof(List) + }); + } + } + [HarmonyPatch] public static class MGPatch { internal static (World, BlobAssetStore) data; - public static void Prefix(World physicsWorld, BlobAssetStore blobStore) + public static void Prefix(World physicsWorld, BlobAssetStore blobStore, IDataDB dataDB) { data = (physicsWorld, blobStore); + var blocks = dataDB.GetValues(); + blocks.Add("modded_ConsoleBlock", new CubeListData + { + cubeType = CubeType.Block, + cubeCategory = CubeCategory.ConsoleBlock, + inventoryCategory = InventoryCategory.Logic, + ID = 500, + Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) + SpriteName = "CTR_CommandBlock", + CubeNameKey = "strConsoleBlock", + SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, + GridScale = new[] {1, 1, 1}, + Mass = 1, + JointBreakAngle = 1 + }); } public static MethodBase TargetMethod() @@ -181,7 +247,7 @@ namespace GamecraftModdingAPI.Blocks } public static IEnumerator Prep() - { + { //TODO: Don't let the game load until this finishes Console.WriteLine("Loading custom catalog..."); var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); while (!res.IsDone) yield return Yield.It; diff --git a/GamecraftModdingAPI/GamecraftModdingAPI.csproj b/GamecraftModdingAPI/GamecraftModdingAPI.csproj index 8be57a7..ee28a68 100644 --- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj +++ b/GamecraftModdingAPI/GamecraftModdingAPI.csproj @@ -113,6 +113,10 @@ ..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.LightBlock.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.LightBlock.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.LogicBlock.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.LogicBlock.dll From 432d6bcf969cedd3db1ea1b3670af0cae20c24cf Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 12 Dec 2020 02:28:42 +0100 Subject: [PATCH 4/9] Use the same (physics) componentts and attempt to use custom material --- GamecraftModdingAPI/Blocks/CustomBlock.cs | 44 ++++++++++++++--------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs index b960101..7c5d6db 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -3,27 +3,22 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Reflection.Emit; -using System.Text.Formatting; using DataLoader; -using GamecraftModdingAPI.Utility; using GPUInstancer; using HarmonyLib; -using RobocraftX.Blocks.Ghost; +using RobocraftX.Blocks; using RobocraftX.Common; -using RobocraftX.Schedulers; +using RobocraftX.Rendering; using Svelto.DataStructures; -using Svelto.ECS.Extensions.Unity; using Svelto.Tasks; -using Svelto.Tasks.ExtraLean; using Unity.Entities; using Unity.Entities.Conversion; using Unity.Physics; using UnityEngine; using UnityEngine.AddressableAssets; using BoxCollider = UnityEngine.BoxCollider; -using Collider = UnityEngine.Collider; using Material = UnityEngine.Material; +using Object = UnityEngine.Object; namespace GamecraftModdingAPI.Blocks { @@ -122,7 +117,7 @@ namespace GamecraftModdingAPI.Blocks } } - [HarmonyPatch] + /*[HarmonyPatch] public static class RendererPatch { private static Material[] materials; @@ -134,9 +129,16 @@ namespace GamecraftModdingAPI.Blocks //Console.WriteLine("Length: " + gameObject.GetComponentsInChildren().Length); if (materials != null) gameObject.GetComponentsInChildren()[0].sharedMaterials = materials; - ECSGPUIResourceManager.Instance.RegisterRuntimePrefabs( + /*ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime( new[] {new PrefabData {prefabId = prefabID, prefabName = "Assets/Prefabs/Cube.prefab"}}, - new List {gameObject}).Complete(); + new List {gameObject});#1# + GameObject go = Object.Instantiate(gameObject); + go.SetActive(false); + AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:RegisterNewPrefabAtRuntime") + .Invoke(ECSGPUIResourceManager.Instance, + new object[] {(uint) ((int) prefabID * 2 + 1), gameObject, true}); + //ECSGPUIResourceManager.Instance.RegisterNewPrefabAtRuntime((uint) ((int)prefabID * 2 + 1), gameObject, true); //.isOcclusionCulling = false; + UnityEngine.Object.Destroy(gameObject); GPUInstancerAPI.AddInstancesToPrefabPrototypeAtRuntime(ECSGPUIResourceManager.Instance.prefabManager, gameObject.GetComponent().prefabPrototype, new[] {gameObject}); Console.WriteLine("Registered prefab to instancer"); @@ -144,7 +146,7 @@ namespace GamecraftModdingAPI.Blocks /*var register = AccessTools.Method("RobocraftX.Common.ECSPhysicResourceManager:RegisterPrefab", new[] {typeof(uint), typeof(GameObject), typeof(World), typeof(BlobAssetStore)}); register.Invoke(ECSPhysicResourceManager.Instance, - new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2});*/ + new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2});#1# /*Console.WriteLine( "Entity: " + ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID)); Console.WriteLine("Prefab ID: " + PrefabsID.DBIDMAP[500]); @@ -155,22 +157,23 @@ namespace GamecraftModdingAPI.Blocks Console.WriteLine("Collider type: " + componentData.ColliderPtr->Type); CollisionFilter filter = componentData.Value.Value.Filter; Console.WriteLine("Filter not empty: " + !filter.IsEmpty); - }*/ + }#1# //MGPatch.data.Item1.EntityManager.GetComponentData<>() gameObject.AddComponent(); gameObject.AddComponent(); Console.WriteLine("Registered prefab to physics"); + ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime(); } else if (gameObject.name == "CTR_CommandBlock") materials = gameObject.GetComponentsInChildren()[0].sharedMaterials; } public static MethodBase TargetMethod() - { + {RobocraftX.Common.ECSGPUIResourceManager.RegisterPrefab return AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab", new[] {typeof(uint), typeof(GameObject)}); } - } + }*/ [HarmonyPatch] public static class RMPatch @@ -180,6 +183,9 @@ namespace GamecraftModdingAPI.Blocks FasterList gos, List prefabData) { + Console.WriteLine("First game object data:\n" + gos[0].GetComponents() + .Select(c => c + " - " + c.name + " " + c.GetType()) + .Aggregate((a, b) => a + "\n" + b)); for (var index = 0; index < prefabData.Count; index++) { var data = prefabData[index]; @@ -200,12 +206,18 @@ namespace GamecraftModdingAPI.Blocks Console.WriteLine("Entity " + current + " has collider: " + entityManager.HasComponent(current)); } + + Console.WriteLine("Adding GPUI prefab"); + ((GPUInstancerPrefabManager) GPUInstancerManager.activeManagerList.Single(gim => + gim is GPUInstancerPrefabManager)) + .AddRuntimeRegisteredPrefab(gos[index].GetComponent()); + Console.WriteLine("Added GPUI prefab"); } } public static MethodBase TargetMethod() { - return AccessTools.Method("RobocraftX.Common.ECSResourceManagerUtility:RelinkEntities", + return AccessTools.Method("RobocraftX.Blocks.ECSResourceManagerUtility:RelinkEntities", new[] { typeof(World), From 5dfb01ef0b10e3e3823031f7ab5dfb22e1968e6b Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 12 Dec 2020 16:59:52 +0100 Subject: [PATCH 5/9] Use the console block's material again - IT WORKS It shows up in the inventory but crashes when selected --- GamecraftModdingAPI/Blocks/CustomBlock.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs index 7c5d6db..2cf6b72 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -100,7 +100,9 @@ namespace GamecraftModdingAPI.Blocks } }*/ - public static void Prefix(ref Dictionary blocks) + private static Material[] materials; + + public static void Prefix(List prefabData, IList prefabs) { /*foreach (var block in blocks.Values.Cast()) { @@ -109,11 +111,25 @@ namespace GamecraftModdingAPI.Blocks /*var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); while (!res.IsDone) yield return Yield.It;*/ + for (int i = 0; i < prefabData.Count; i++) + { + var gameObject = prefabs[i]; + switch (gameObject.name) + { + case "Cube": + gameObject.GetComponentsInChildren()[0].sharedMaterials = materials; + break; + case "CTR_CommandBlock": + materials = gameObject.GetComponentsInChildren()[0].sharedMaterials; + break; + } + } } public static MethodBase TargetMethod() { //General block registration - return AccessTools.Method("RobocraftX.Blocks.BlocksCompositionRoot:RegisterPartPrefabs"); + //return AccessTools.Method("RobocraftX.Blocks.BlocksCompositionRoot:RegisterPartPrefabs"); + return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs"); } } From 78f0ea01620cbb590588380a7e3c4bb8bf359eaf Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 12 Dec 2020 23:08:56 +0100 Subject: [PATCH 6/9] Use the intended method to create a CubeListData The block can be selected but not placed --- GamecraftModdingAPI/Blocks/CustomBlock.cs | 31 ++++++++++------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs index 2cf6b72..7a79345 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -111,9 +111,8 @@ namespace GamecraftModdingAPI.Blocks /*var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); while (!res.IsDone) yield return Yield.It;*/ - for (int i = 0; i < prefabData.Count; i++) + foreach (var gameObject in prefabs) { - var gameObject = prefabs[i]; switch (gameObject.name) { case "Cube": @@ -251,21 +250,19 @@ namespace GamecraftModdingAPI.Blocks public static void Prefix(World physicsWorld, BlobAssetStore blobStore, IDataDB dataDB) { data = (physicsWorld, blobStore); - var blocks = dataDB.GetValues(); - blocks.Add("modded_ConsoleBlock", new CubeListData - { - cubeType = CubeType.Block, - cubeCategory = CubeCategory.ConsoleBlock, - inventoryCategory = InventoryCategory.Logic, - ID = 500, - Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) - SpriteName = "CTR_CommandBlock", - CubeNameKey = "strConsoleBlock", - SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, - GridScale = new[] {1, 1, 1}, - Mass = 1, - JointBreakAngle = 1 - }); + //RobocraftX.CR.MachineEditing.UpdateSelectedGhostBlockEngine.UpdateGhostBlock + var cld = (CubeListData) ((DataImplementor) dataDB).CreateDataObject("500", typeof(CubeListData), null); + cld.cubeType = CubeType.Block; + cld.cubeCategory = CubeCategory.General; + cld.inventoryCategory = InventoryCategory.Shapes; + cld.ID = 500; + cld.Path = "Assets/Prefabs/Cube.prefab"; //Index out of range exception: Asset failed to load (wrong path) + cld.SpriteName = "CTR_CommandBlock"; + cld.CubeNameKey = "strConsoleBlock"; + cld.SelectableFaces = new[] {0, 1, 2, 3, 4, 5}; + cld.GridScale = new[] {1, 1, 1}; + cld.Mass = 1; + cld.JointBreakAngle = 1; } public static MethodBase TargetMethod() From 4e16f251ee6fdd74049a77a9d0f47827c0f4aa24 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 13 Dec 2020 20:21:46 +0100 Subject: [PATCH 7/9] Don't use the intended method to create a CubeListData It adds it with its ID as key but the ID hasn't been set at that point It works until the second simulation start now --- GamecraftModdingAPI/Blocks/CustomBlock.cs | 37 +++++++++++++++-------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/GamecraftModdingAPI/Blocks/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs index 7a79345..af8f374 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using DataLoader; +using GamecraftModdingAPI.Utility; using GPUInstancer; using HarmonyLib; using RobocraftX.Blocks; @@ -246,23 +247,33 @@ namespace GamecraftModdingAPI.Blocks [HarmonyPatch] public static class MGPatch { - internal static (World, BlobAssetStore) data; + internal static (World, BlobAssetStore) data; public static void Prefix(World physicsWorld, BlobAssetStore blobStore, IDataDB dataDB) { data = (physicsWorld, blobStore); //RobocraftX.CR.MachineEditing.UpdateSelectedGhostBlockEngine.UpdateGhostBlock - var cld = (CubeListData) ((DataImplementor) dataDB).CreateDataObject("500", typeof(CubeListData), null); - cld.cubeType = CubeType.Block; - cld.cubeCategory = CubeCategory.General; - cld.inventoryCategory = InventoryCategory.Shapes; - cld.ID = 500; - cld.Path = "Assets/Prefabs/Cube.prefab"; //Index out of range exception: Asset failed to load (wrong path) - cld.SpriteName = "CTR_CommandBlock"; - cld.CubeNameKey = "strConsoleBlock"; - cld.SelectableFaces = new[] {0, 1, 2, 3, 4, 5}; - cld.GridScale = new[] {1, 1, 1}; - cld.Mass = 1; - cld.JointBreakAngle = 1; + //var cld = (CubeListData) ((DataImplementor) dataDB).CreateDataObject("500", typeof(CubeListData), null); + var abd = dataDB.GetValue((int) BlockIDs.AluminiumCube); + var cld = new CubeListData + { + cubeType = CubeType.Block, + cubeCategory = CubeCategory.General, + inventoryCategory = InventoryCategory.Shapes, + ID = 500, + Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) + SpriteName = "CTR_CommandBlock", + CubeNameKey = "strConsoleBlock", + SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, + GridScale = new[] {1, 1, 1}, + Mass = 1, + JointBreakAngle = 1, + Material = abd.Material + }; + Console.WriteLine("Aluminium block data:\n" + abd); + Console.WriteLine("Material: " + abd.Material); + dataDB.GetValues().Add("500", cld); //The registration needs to happen after the ID has been set + dataDB.GetFasterValues().Add(500, cld); + //RobocraftX.ExplosionFragments.Engines.PlayFragmentExplodeEngine.PlayRigidBodyEffect } public static MethodBase TargetMethod() From a7f6a1623144cdfd6c78b6a9a5252a9a6268b7cb Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 17 Dec 2020 02:34:36 +0100 Subject: [PATCH 8/9] Update to Gamecraft 2020.12.16.14.19 and custom block stuff - Fixed the crash on second time start - Tweaked more stuff about the block Breaking changes coming from FMOD 2.0: - Audio[int index] changed to Audio[PARAMETER_ID index] - Audio.Parameters removed --- GamecraftModdingAPI/Blocks/BlockEngine.cs | 2 +- GamecraftModdingAPI/Blocks/CustomBlock.cs | 12 +++++++-- GamecraftModdingAPI/Blocks/DampedSpring.cs | 8 +++--- GamecraftModdingAPI/Blocks/Piston.cs | 4 +-- GamecraftModdingAPI/Blocks/Servo.cs | 4 +-- GamecraftModdingAPI/Blocks/TextBlock.cs | 6 +---- .../GamecraftModdingAPI.csproj | 22 +++++++++++----- GamecraftModdingAPI/Utility/Audio.cs | 26 ++++--------------- 8 files changed, 40 insertions(+), 44 deletions(-) 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 index af8f374..2f33035 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -20,6 +20,7 @@ using UnityEngine.AddressableAssets; using BoxCollider = UnityEngine.BoxCollider; using Material = UnityEngine.Material; using Object = UnityEngine.Object; +using ScalingPermission = DataLoader.ScalingPermission; namespace GamecraftModdingAPI.Blocks { @@ -266,8 +267,15 @@ namespace GamecraftModdingAPI.Blocks SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, GridScale = new[] {1, 1, 1}, Mass = 1, - JointBreakAngle = 1, - Material = abd.Material + Material = abd.Material, + scalingPermission = ScalingPermission.NonUniform, + SortIndex = 12, + DefaultColour = (byte) BlockColors.Lime, + Volume = 1f, + timeRunningCollision = TimeRunningCollision.Enabled, + IsIsolator = false, + EdgeConnectingFaces = new[] {0, 1, 2, 3, 4, 5}, + PointDataVolumeMultiplier = 1f }; Console.WriteLine("Aluminium block data:\n" + abd); Console.WriteLine("Material: " + abd.Material); 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/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 From 712ece86dbf4c462da1e5ba49f2e99713e19a009 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 17 Dec 2020 20:20:46 +0100 Subject: [PATCH 9/9] Add custom block registration functionality and a test --- GamecraftModdingAPI/Block.cs | 2 +- GamecraftModdingAPI/Blocks/CustomBlock.cs | 328 +++++------------- .../Blocks/CustomBlockAttribute.cs | 85 +++++ .../Tests/GamecraftModdingAPIPluginTest.cs | 33 +- 4 files changed, 204 insertions(+), 244 deletions(-) create mode 100644 GamecraftModdingAPI/Blocks/CustomBlockAttribute.cs 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/CustomBlock.cs b/GamecraftModdingAPI/Blocks/CustomBlock.cs index 2f33035..331552c 100644 --- a/GamecraftModdingAPI/Blocks/CustomBlock.cs +++ b/GamecraftModdingAPI/Blocks/CustomBlock.cs @@ -1,9 +1,11 @@ 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; @@ -11,8 +13,8 @@ using RobocraftX.Blocks; using RobocraftX.Common; using RobocraftX.Rendering; using Svelto.DataStructures; +using Svelto.ECS; using Svelto.Tasks; -using Unity.Entities; using Unity.Entities.Conversion; using Unity.Physics; using UnityEngine; @@ -24,264 +26,113 @@ using ScalingPermission = DataLoader.ScalingPermission; namespace GamecraftModdingAPI.Blocks { - public class CustomBlock + public class CustomBlock : Block { private static ushort nextID = 500; - public static void RegisterCustomBlock(string path) + /// + /// 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 { - var prefabData = new List(); - //category ID: - //0 - regular - //1 - joint - //2 - controller - uint prefabId = PrefabsID.FetchNewPrefabID(PrefabsID.GenerateDBID(0, nextID++)); - prefabData.Add(new PrefabData() - { - prefabName = path, - prefabId = prefabId - }); - var loadTask = Addressables.LoadAssetAsync(path); - AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab") - .Invoke(ECSGPUIResourceManager.Instance, new object[] {prefabId, loadTask.Result, 1}); + 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); } - [HarmonyPatch] - public static class Patch + public CustomBlock(EGID id) : base(id) { - /*public static IEnumerable Transpiler(IEnumerable instructions) - { - var list = new List(instructions); - try - { - *int index = -1; - CodeInstruction loadTask = null; - for (var i = 0; i < list.Count - 1; i++) - { - if (list[i].opcode == OpCodes.Ldfld - && ((string) list[i].operand).Contains("renderingWorld") - && list[i + 1].opcode == OpCodes.Brfalse_S) - index = i - 1; //It loads 'this' first - if (list[i].opcode == OpCodes.Ldflda - && ((string) list[i].operand).Contains("loadTask")) - loadTask = new CodeInstruction(list[i]); - }* + if (id.groupID != Group) + throw new BlockTypeException("The block is not a custom block! It has a group of " + id.groupID); + } - var array = new[] - { - //Set Yield.It to be returned (current) - new CodeInstruction(OpCodes.Ldarg_0), // this - new CodeInstruction(OpCodes.Ldsfld, - typeof(Yield).GetField("It", BindingFlags.Public | BindingFlags.Static)), - new CodeInstruction(OpCodes.Stfld, "object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>2__current'"), - - //Set which yield return we're at (state) - new CodeInstruction(OpCodes.Ldarg_0), // this - new CodeInstruction(OpCodes.Ldc_I4_1), - //new CodeInstruction(OpCodes.Call, ((Action)AddInfo).Method) - }; - list.InsertRange(index, array); - * - IL_00ad: ldarg.0 // this - IL_00ae: ldsfld class [Svelto.Tasks]Svelto.Tasks.Yield [Svelto.Tasks]Svelto.Tasks.Yield::It - IL_00b3: stfld object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>2__current' - - IL_0072: ldarg.0 // this - IL_0073: ldnull - IL_0074: stfld object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>2__current' - IL_0079: ldarg.0 // this - IL_007a: ldc.i4.2 - IL_007b: stfld object RobocraftX.Common.ECSResourceManagerUtility/'d__0'::'<>1__state' - IL_0080: ldc.i4.1 - IL_0081: ret - * - yield break; - } - catch (Exception e) - { - Logging.LogWarning("Failed to inject AddInfo method for the debug display!\n" + e); - } - }*/ + 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) { - /*foreach (var block in blocks.Values.Cast()) + for (var index = 0; index < prefabs.Count; index++) { - Console.WriteLine("Block info: " + block); - }*/ + if (prefabData[index].prefabName == "ConsoleBlock") + materials = prefabs[index].GetComponentsInChildren()[0].sharedMaterials; + } - /*var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); - while (!res.IsDone) yield return Yield.It;*/ - foreach (var gameObject in prefabs) + for (var index = 0; index < prefabs.Count; index++) { - switch (gameObject.name) - { - case "Cube": - gameObject.GetComponentsInChildren()[0].sharedMaterials = materials; - break; - case "CTR_CommandBlock": - materials = gameObject.GetComponentsInChildren()[0].sharedMaterials; - break; - } + 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.Blocks.BlocksCompositionRoot:RegisterPartPrefabs"); return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs"); } } - /*[HarmonyPatch] - public static class RendererPatch - { - private static Material[] materials; - public static void Prefix(uint prefabID, GameObject gameObject) - { - Console.WriteLine("ID: " + prefabID + " - Name: " + gameObject.name); - if (gameObject.name == "Cube") - { - //Console.WriteLine("Length: " + gameObject.GetComponentsInChildren().Length); - if (materials != null) - gameObject.GetComponentsInChildren()[0].sharedMaterials = materials; - /*ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime( - new[] {new PrefabData {prefabId = prefabID, prefabName = "Assets/Prefabs/Cube.prefab"}}, - new List {gameObject});#1# - GameObject go = Object.Instantiate(gameObject); - go.SetActive(false); - AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:RegisterNewPrefabAtRuntime") - .Invoke(ECSGPUIResourceManager.Instance, - new object[] {(uint) ((int) prefabID * 2 + 1), gameObject, true}); - //ECSGPUIResourceManager.Instance.RegisterNewPrefabAtRuntime((uint) ((int)prefabID * 2 + 1), gameObject, true); //.isOcclusionCulling = false; - UnityEngine.Object.Destroy(gameObject); - GPUInstancerAPI.AddInstancesToPrefabPrototypeAtRuntime(ECSGPUIResourceManager.Instance.prefabManager, - gameObject.GetComponent().prefabPrototype, new[] {gameObject}); - Console.WriteLine("Registered prefab to instancer"); - - /*var register = AccessTools.Method("RobocraftX.Common.ECSPhysicResourceManager:RegisterPrefab", - new[] {typeof(uint), typeof(GameObject), typeof(World), typeof(BlobAssetStore)}); - register.Invoke(ECSPhysicResourceManager.Instance, - new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2});#1# - /*Console.WriteLine( - "Entity: " + ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID)); - Console.WriteLine("Prefab ID: " + PrefabsID.DBIDMAP[500]); - PhysicsCollider componentData = MGPatch.data.Item1.EntityManager.GetComponentData(ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID)); - Console.WriteLine("Collider valid: " + componentData.IsValid); - unsafe - { - Console.WriteLine("Collider type: " + componentData.ColliderPtr->Type); - CollisionFilter filter = componentData.Value.Value.Filter; - Console.WriteLine("Filter not empty: " + !filter.IsEmpty); - }#1# - //MGPatch.data.Item1.EntityManager.GetComponentData<>() - gameObject.AddComponent(); - gameObject.AddComponent(); - Console.WriteLine("Registered prefab to physics"); - ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime(); - } - else if (gameObject.name == "CTR_CommandBlock") - materials = gameObject.GetComponentsInChildren()[0].sharedMaterials; - } - - public static MethodBase TargetMethod() - {RobocraftX.Common.ECSGPUIResourceManager.RegisterPrefab - return AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab", - new[] {typeof(uint), typeof(GameObject)}); - } - }*/ - [HarmonyPatch] - public static class RMPatch + public static class CubeRegistrationPatch { - public static void Prefix(World physicsWorld, - GameObjectConversionSystem getExistingSystem, - FasterList gos, - List prefabData) + public static void Prefix(IDataDB dataDB) { - Console.WriteLine("First game object data:\n" + gos[0].GetComponents() - .Select(c => c + " - " + c.name + " " + c.GetType()) - .Aggregate((a, b) => a + "\n" + b)); - for (var index = 0; index < prefabData.Count; index++) + //var abd = dataDB.GetValue((int) BlockIDs.AluminiumCube); + foreach (var (key, type) in _customBlocks) { - var data = prefabData[index]; - if (!data.prefabName.EndsWith("Cube.prefab")) continue; - //getExistingSystem.DeclareLinkedEntityGroup(gos[index]); - /*Entity entity = GameObjectConversionUtility.ConvertGameObjectHierarchy(gos[index], - GameObjectConversionSettings.FromWorld(physicsWorld, MGPatch.data.Item2));*/ - Console.WriteLine("Transform: " + gos[index].transform.childCount); - Entity primaryEntity = getExistingSystem.GetPrimaryEntity(gos[index]); - MultiListEnumerator entities = getExistingSystem.GetEntities(gos[index]); - Console.WriteLine("ID: " + data.prefabId + " - Name: " + data.prefabName); - Console.WriteLine("Primary entity: " + primaryEntity); - EntityManager entityManager = physicsWorld.EntityManager; - Console.WriteLine("Has collider: " + entityManager.HasComponent(primaryEntity)); - while (entities.MoveNext()) - { - Entity current = entities.Current; - Console.WriteLine("Entity " + current + " has collider: " + - entityManager.HasComponent(current)); - } - - Console.WriteLine("Adding GPUI prefab"); - ((GPUInstancerPrefabManager) GPUInstancerManager.activeManagerList.Single(gim => - gim is GPUInstancerPrefabManager)) - .AddRuntimeRegisteredPrefab(gos[index].GetComponent()); - Console.WriteLine("Added GPUI prefab"); + 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 } - } - public static MethodBase TargetMethod() - { - return AccessTools.Method("RobocraftX.Blocks.ECSResourceManagerUtility:RelinkEntities", - new[] - { - typeof(World), - typeof(GameObjectConversionSystem), - typeof(FasterList), - typeof(List) - }); - } - } - - [HarmonyPatch] - public static class MGPatch - { - internal static (World, BlobAssetStore) data; - public static void Prefix(World physicsWorld, BlobAssetStore blobStore, IDataDB dataDB) - { - data = (physicsWorld, blobStore); - //RobocraftX.CR.MachineEditing.UpdateSelectedGhostBlockEngine.UpdateGhostBlock - //var cld = (CubeListData) ((DataImplementor) dataDB).CreateDataObject("500", typeof(CubeListData), null); - var abd = dataDB.GetValue((int) BlockIDs.AluminiumCube); - var cld = new CubeListData - { - cubeType = CubeType.Block, - cubeCategory = CubeCategory.General, - inventoryCategory = InventoryCategory.Shapes, - ID = 500, - Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) - SpriteName = "CTR_CommandBlock", - CubeNameKey = "strConsoleBlock", - SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, - GridScale = new[] {1, 1, 1}, - Mass = 1, - Material = abd.Material, - scalingPermission = ScalingPermission.NonUniform, - SortIndex = 12, - DefaultColour = (byte) BlockColors.Lime, - Volume = 1f, - timeRunningCollision = TimeRunningCollision.Enabled, - IsIsolator = false, - EdgeConnectingFaces = new[] {0, 1, 2, 3, 4, 5}, - PointDataVolumeMultiplier = 1f - }; - Console.WriteLine("Aluminium block data:\n" + abd); - Console.WriteLine("Material: " + abd.Material); - dataDB.GetValues().Add("500", cld); //The registration needs to happen after the ID has been set - dataDB.GetFasterValues().Add(500, cld); - //RobocraftX.ExplosionFragments.Engines.PlayFragmentExplodeEngine.PlayRigidBodyEffect + _canRegister = false; } public static MethodBase TargetMethod() @@ -292,16 +143,15 @@ namespace GamecraftModdingAPI.Blocks public static IEnumerator Prep() { //TODO: Don't let the game load until this finishes - Console.WriteLine("Loading custom catalog..."); - var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); - while (!res.IsDone) yield return Yield.It; - Console.WriteLine("Loaded custom catalog: " + res.Result.LocatorId); - Addressables.AddResourceLocator(res.Result); - /*Console.WriteLine("Loading Cube asset..."); - var loadTask = Addressables.LoadAssetAsync("Assets/Cube.prefab"); - while (!loadTask.IsDone) yield return Yield.It; - Console.WriteLine("Exception: "+loadTask.OperationException); - Console.WriteLine("Result: " + loadTask.Result.name);*/ + 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/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index 268fdb2..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; @@ -291,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); @@ -361,11 +369,16 @@ namespace GamecraftModdingAPI.Tests Log.Output("Submitted: " + Window.selected.submittedCode); }) .Build(); - /*JObject o1 = JObject.Parse(File.ReadAllText(@"Gamecraft_Data\StreamingAssets\aa\Windows\catalog.json")); - JObject o2 = JObject.Parse(File.ReadAllText(@"customCatalog.json")); - o1.Merge(o2, new JsonMergeSettings {MergeArrayHandling = MergeArrayHandling.Union}); - File.WriteAllText(@"Gamecraft_Data\StreamingAssets\aa\Windows\catalog.json", o1.ToString());*/ 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(); @@ -429,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 }