A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
14KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using DataLoader;
  7. using GPUInstancer;
  8. using HarmonyLib;
  9. using RobocraftX.Blocks;
  10. using RobocraftX.Common;
  11. using RobocraftX.Rendering;
  12. using Svelto.DataStructures;
  13. using Svelto.Tasks;
  14. using Unity.Entities;
  15. using Unity.Entities.Conversion;
  16. using Unity.Physics;
  17. using UnityEngine;
  18. using UnityEngine.AddressableAssets;
  19. using BoxCollider = UnityEngine.BoxCollider;
  20. using Material = UnityEngine.Material;
  21. using Object = UnityEngine.Object;
  22. namespace GamecraftModdingAPI.Blocks
  23. {
  24. public class CustomBlock
  25. {
  26. private static ushort nextID = 500;
  27. public static void RegisterCustomBlock(string path)
  28. {
  29. var prefabData = new List<PrefabData>();
  30. //category ID:
  31. //0 - regular
  32. //1 - joint
  33. //2 - controller
  34. uint prefabId = PrefabsID.FetchNewPrefabID(PrefabsID.GenerateDBID(0, nextID++));
  35. prefabData.Add(new PrefabData()
  36. {
  37. prefabName = path,
  38. prefabId = prefabId
  39. });
  40. var loadTask = Addressables.LoadAssetAsync<GameObject>(path);
  41. AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab")
  42. .Invoke(ECSGPUIResourceManager.Instance, new object[] {prefabId, loadTask.Result, 1});
  43. }
  44. [HarmonyPatch]
  45. public static class Patch
  46. {
  47. /*public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
  48. {
  49. var list = new List<CodeInstruction>(instructions);
  50. try
  51. {
  52. *int index = -1;
  53. CodeInstruction loadTask = null;
  54. for (var i = 0; i < list.Count - 1; i++)
  55. {
  56. if (list[i].opcode == OpCodes.Ldfld
  57. && ((string) list[i].operand).Contains("renderingWorld")
  58. && list[i + 1].opcode == OpCodes.Brfalse_S)
  59. index = i - 1; //It loads 'this' first
  60. if (list[i].opcode == OpCodes.Ldflda
  61. && ((string) list[i].operand).Contains("loadTask"))
  62. loadTask = new CodeInstruction(list[i]);
  63. }*
  64. var array = new[]
  65. {
  66. //Set Yield.It to be returned (current)
  67. new CodeInstruction(OpCodes.Ldarg_0), // this
  68. new CodeInstruction(OpCodes.Ldsfld,
  69. typeof(Yield).GetField("It", BindingFlags.Public | BindingFlags.Static)),
  70. new CodeInstruction(OpCodes.Stfld, "object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>2__current'"),
  71. //Set which yield return we're at (state)
  72. new CodeInstruction(OpCodes.Ldarg_0), // this
  73. new CodeInstruction(OpCodes.Ldc_I4_1),
  74. //new CodeInstruction(OpCodes.Call, ((Action<StringBuffer>)AddInfo).Method)
  75. };
  76. list.InsertRange(index, array);
  77. *
  78. IL_00ad: ldarg.0 // this
  79. IL_00ae: ldsfld class [Svelto.Tasks]Svelto.Tasks.Yield [Svelto.Tasks]Svelto.Tasks.Yield::It
  80. IL_00b3: stfld object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>2__current'
  81. IL_0072: ldarg.0 // this
  82. IL_0073: ldnull
  83. IL_0074: stfld object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>2__current'
  84. IL_0079: ldarg.0 // this
  85. IL_007a: ldc.i4.2
  86. IL_007b: stfld object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>1__state'
  87. IL_0080: ldc.i4.1
  88. IL_0081: ret
  89. *
  90. yield break;
  91. }
  92. catch (Exception e)
  93. {
  94. Logging.LogWarning("Failed to inject AddInfo method for the debug display!\n" + e);
  95. }
  96. }*/
  97. private static Material[] materials;
  98. public static void Prefix(List<PrefabData> prefabData, IList<GameObject> prefabs)
  99. {
  100. /*foreach (var block in blocks.Values.Cast<CubeListData>())
  101. {
  102. Console.WriteLine("Block info: " + block);
  103. }*/
  104. /*var res = Addressables.LoadContentCatalogAsync("customCatalog.json");
  105. while (!res.IsDone) yield return Yield.It;*/
  106. foreach (var gameObject in prefabs)
  107. {
  108. switch (gameObject.name)
  109. {
  110. case "Cube":
  111. gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials = materials;
  112. break;
  113. case "CTR_CommandBlock":
  114. materials = gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials;
  115. break;
  116. }
  117. }
  118. }
  119. public static MethodBase TargetMethod()
  120. { //General block registration
  121. //return AccessTools.Method("RobocraftX.Blocks.BlocksCompositionRoot:RegisterPartPrefabs");
  122. return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs");
  123. }
  124. }
  125. /*[HarmonyPatch]
  126. public static class RendererPatch
  127. {
  128. private static Material[] materials;
  129. public static void Prefix(uint prefabID, GameObject gameObject)
  130. {
  131. Console.WriteLine("ID: " + prefabID + " - Name: " + gameObject.name);
  132. if (gameObject.name == "Cube")
  133. {
  134. //Console.WriteLine("Length: " + gameObject.GetComponentsInChildren<MeshRenderer>().Length);
  135. if (materials != null)
  136. gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials = materials;
  137. /*ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime(
  138. new[] {new PrefabData {prefabId = prefabID, prefabName = "Assets/Prefabs/Cube.prefab"}},
  139. new List<GameObject> {gameObject});#1#
  140. GameObject go = Object.Instantiate(gameObject);
  141. go.SetActive(false);
  142. AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:RegisterNewPrefabAtRuntime")
  143. .Invoke(ECSGPUIResourceManager.Instance,
  144. new object[] {(uint) ((int) prefabID * 2 + 1), gameObject, true});
  145. //ECSGPUIResourceManager.Instance.RegisterNewPrefabAtRuntime((uint) ((int)prefabID * 2 + 1), gameObject, true); //.isOcclusionCulling = false;
  146. UnityEngine.Object.Destroy(gameObject);
  147. GPUInstancerAPI.AddInstancesToPrefabPrototypeAtRuntime(ECSGPUIResourceManager.Instance.prefabManager,
  148. gameObject.GetComponent<GPUInstancerPrefab>().prefabPrototype, new[] {gameObject});
  149. Console.WriteLine("Registered prefab to instancer");
  150. /*var register = AccessTools.Method("RobocraftX.Common.ECSPhysicResourceManager:RegisterPrefab",
  151. new[] {typeof(uint), typeof(GameObject), typeof(World), typeof(BlobAssetStore)});
  152. register.Invoke(ECSPhysicResourceManager.Instance,
  153. new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2});#1#
  154. /*Console.WriteLine(
  155. "Entity: " + ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID));
  156. Console.WriteLine("Prefab ID: " + PrefabsID.DBIDMAP[500]);
  157. PhysicsCollider componentData = MGPatch.data.Item1.EntityManager.GetComponentData<PhysicsCollider>(ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID));
  158. Console.WriteLine("Collider valid: " + componentData.IsValid);
  159. unsafe
  160. {
  161. Console.WriteLine("Collider type: " + componentData.ColliderPtr->Type);
  162. CollisionFilter filter = componentData.Value.Value.Filter;
  163. Console.WriteLine("Filter not empty: " + !filter.IsEmpty);
  164. }#1#
  165. //MGPatch.data.Item1.EntityManager.GetComponentData<>()
  166. gameObject.AddComponent<BoxCollider>();
  167. gameObject.AddComponent<Transform>();
  168. Console.WriteLine("Registered prefab to physics");
  169. ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime();
  170. }
  171. else if (gameObject.name == "CTR_CommandBlock")
  172. materials = gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials;
  173. }
  174. public static MethodBase TargetMethod()
  175. {RobocraftX.Common.ECSGPUIResourceManager.RegisterPrefab
  176. return AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab",
  177. new[] {typeof(uint), typeof(GameObject)});
  178. }
  179. }*/
  180. [HarmonyPatch]
  181. public static class RMPatch
  182. {
  183. public static void Prefix(World physicsWorld,
  184. GameObjectConversionSystem getExistingSystem,
  185. FasterList<GameObject> gos,
  186. List<PrefabData> prefabData)
  187. {
  188. Console.WriteLine("First game object data:\n" + gos[0].GetComponents<Component>()
  189. .Select(c => c + " - " + c.name + " " + c.GetType())
  190. .Aggregate((a, b) => a + "\n" + b));
  191. for (var index = 0; index < prefabData.Count; index++)
  192. {
  193. var data = prefabData[index];
  194. if (!data.prefabName.EndsWith("Cube.prefab")) continue;
  195. //getExistingSystem.DeclareLinkedEntityGroup(gos[index]);
  196. /*Entity entity = GameObjectConversionUtility.ConvertGameObjectHierarchy(gos[index],
  197. GameObjectConversionSettings.FromWorld(physicsWorld, MGPatch.data.Item2));*/
  198. Console.WriteLine("Transform: " + gos[index].transform.childCount);
  199. Entity primaryEntity = getExistingSystem.GetPrimaryEntity(gos[index]);
  200. MultiListEnumerator<Entity> entities = getExistingSystem.GetEntities(gos[index]);
  201. Console.WriteLine("ID: " + data.prefabId + " - Name: " + data.prefabName);
  202. Console.WriteLine("Primary entity: " + primaryEntity);
  203. EntityManager entityManager = physicsWorld.EntityManager;
  204. Console.WriteLine("Has collider: " + entityManager.HasComponent<PhysicsCollider>(primaryEntity));
  205. while (entities.MoveNext())
  206. {
  207. Entity current = entities.Current;
  208. Console.WriteLine("Entity " + current + " has collider: " +
  209. entityManager.HasComponent<PhysicsCollider>(current));
  210. }
  211. Console.WriteLine("Adding GPUI prefab");
  212. ((GPUInstancerPrefabManager) GPUInstancerManager.activeManagerList.Single(gim =>
  213. gim is GPUInstancerPrefabManager))
  214. .AddRuntimeRegisteredPrefab(gos[index].GetComponent<GPUInstancerPrefab>());
  215. Console.WriteLine("Added GPUI prefab");
  216. }
  217. }
  218. public static MethodBase TargetMethod()
  219. {
  220. return AccessTools.Method("RobocraftX.Blocks.ECSResourceManagerUtility:RelinkEntities",
  221. new[]
  222. {
  223. typeof(World),
  224. typeof(GameObjectConversionSystem),
  225. typeof(FasterList<GameObject>),
  226. typeof(List<PrefabData>)
  227. });
  228. }
  229. }
  230. [HarmonyPatch]
  231. public static class MGPatch
  232. {
  233. internal static (World, BlobAssetStore) data;
  234. public static void Prefix(World physicsWorld, BlobAssetStore blobStore, IDataDB dataDB)
  235. {
  236. data = (physicsWorld, blobStore);
  237. //RobocraftX.CR.MachineEditing.UpdateSelectedGhostBlockEngine.UpdateGhostBlock
  238. var cld = (CubeListData) ((DataImplementor) dataDB).CreateDataObject("500", typeof(CubeListData), null);
  239. cld.cubeType = CubeType.Block;
  240. cld.cubeCategory = CubeCategory.General;
  241. cld.inventoryCategory = InventoryCategory.Shapes;
  242. cld.ID = 500;
  243. cld.Path = "Assets/Prefabs/Cube.prefab"; //Index out of range exception: Asset failed to load (wrong path)
  244. cld.SpriteName = "CTR_CommandBlock";
  245. cld.CubeNameKey = "strConsoleBlock";
  246. cld.SelectableFaces = new[] {0, 1, 2, 3, 4, 5};
  247. cld.GridScale = new[] {1, 1, 1};
  248. cld.Mass = 1;
  249. cld.JointBreakAngle = 1;
  250. }
  251. public static MethodBase TargetMethod()
  252. {
  253. return AccessTools.Method("RobocraftX.CR.MainGame.MainGameCompositionRoot:Init");
  254. }
  255. }
  256. public static IEnumerator Prep()
  257. { //TODO: Don't let the game load until this finishes
  258. Console.WriteLine("Loading custom catalog...");
  259. var res = Addressables.LoadContentCatalogAsync("customCatalog.json");
  260. while (!res.IsDone) yield return Yield.It;
  261. Console.WriteLine("Loaded custom catalog: " + res.Result.LocatorId);
  262. Addressables.AddResourceLocator(res.Result);
  263. /*Console.WriteLine("Loading Cube asset...");
  264. var loadTask = Addressables.LoadAssetAsync<GameObject>("Assets/Cube.prefab");
  265. while (!loadTask.IsDone) yield return Yield.It;
  266. Console.WriteLine("Exception: "+loadTask.OperationException);
  267. Console.WriteLine("Result: " + loadTask.Result.name);*/
  268. }
  269. }
  270. }