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.

299 lines
15KB

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