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.

263 lines
13KB

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