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.

CustomBlock.cs 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Reflection;
  6. using HarmonyLib;
  7. using DataLoader;
  8. using RobocraftX.Blocks;
  9. using RobocraftX.Rendering;
  10. using Svelto.ECS;
  11. using Svelto.Tasks;
  12. using UnityEngine;
  13. using UnityEngine.AddressableAssets;
  14. using Material = UnityEngine.Material;
  15. using GamecraftModdingAPI.Utility;
  16. namespace GamecraftModdingAPI.Blocks
  17. {
  18. public class CustomBlock : Block
  19. {
  20. private static ushort nextID = 500;
  21. /// <summary>
  22. /// Key: Prefab path
  23. /// </summary>
  24. private static Dictionary<string, Type> _customBlocks = new Dictionary<string, Type>();
  25. private static bool _canRegister = true;
  26. /// <summary>
  27. /// Register a custom block type. Call it as soon as possible (in OnApplicationStart()).<br />
  28. /// You need a Unity project with Addressables and Havok installed and need a prefab added as an addressable asset.
  29. /// Build the addressables and the project and copy the catalog.json from StreamingAssets, you'll need to reference this file.
  30. /// Also copy the asset files from the subfolder to the same path in the game.
  31. /// </summary>
  32. /// <typeparam name="T">The custom block type</typeparam>
  33. public static void RegisterCustomBlock<T>() where T : CustomBlock
  34. {
  35. if (!_canRegister)
  36. throw new InvalidOperationException(
  37. "It's too late to register custom blocks. Register it before the game starts loading.");
  38. var type = typeof(T);
  39. var attr = type.GetCustomAttribute<CustomBlockAttribute>();
  40. if (attr == null)
  41. throw new ArgumentException("The custom block type is missing the CustomBlock annotation");
  42. string typeName = type.FullName ??
  43. throw new ArgumentException("The given block type doesn't have a concrete full name.");
  44. if (!File.Exists(attr.Catalog))
  45. throw new FileNotFoundException("The specified catalog cannot be found for " + typeName);
  46. _customBlocks.Add(attr.AssetPath, type);
  47. Logging.MetaDebugLog("Registered custom block type " + typeName);
  48. }
  49. public CustomBlock(EGID id) : base(id)
  50. {
  51. /*if (id.groupID != Group)
  52. throw new BlockTypeException("The block is not a custom block! It has a group of " + id.groupID);*/
  53. }
  54. public CustomBlock(uint id) : base(id)
  55. {
  56. }
  57. //public static ExclusiveGroup Group { get; } = new ExclusiveGroup("Custom block");
  58. [HarmonyPatch]
  59. public static class MaterialCopyPatch
  60. {
  61. private static Material[] materials;
  62. public static void Prefix(List<PrefabData> prefabData, IList<GameObject> prefabs)
  63. {
  64. for (var index = 0; index < prefabs.Count; index++)
  65. {
  66. if (prefabData[index].prefabName == "ConsoleBlock")
  67. materials = prefabs[index].GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials;
  68. }
  69. for (var index = 0; index < prefabs.Count; index++)
  70. {
  71. if (_customBlocks.ContainsKey(prefabData[index].prefabName)) //This is a custom block
  72. prefabs[index].GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials = materials;
  73. }
  74. }
  75. public static MethodBase TargetMethod()
  76. { //General block registration
  77. return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs");
  78. }
  79. }
  80. [HarmonyPatch]
  81. public static class CubeRegistrationPatch
  82. {
  83. public static void Prefix(IDataDB dataDB)
  84. {
  85. //var abd = dataDB.GetValue<CubeListData>((int) BlockIDs.AluminiumCube);
  86. foreach (var (key, type) in _customBlocks)
  87. {
  88. var attr = type.GetCustomAttribute<CustomBlockAttribute>();
  89. var cld = new CubeListData
  90. { //"Assets/Prefabs/Cube.prefab" - "CTR_CommandBlock" - "strConsoleBlock"
  91. cubeType = attr.Type,
  92. //cubeCategory = (CubeCategory) 1000,
  93. cubeCategory = attr.Category,
  94. inventoryCategory = attr.InventoryCategory,
  95. ID = nextID++,
  96. Path = attr.AssetPath, //Index out of range exception: Asset failed to load (wrong path)
  97. SpriteName = attr.SpriteName,
  98. CubeNameKey = attr.NameKey,
  99. CubeDescriptionKey = attr.DescKey,
  100. SelectableFaces = new[] {0, 1, 2, 3, 4, 5},
  101. GridScale = new[] {5, 5, 5},
  102. Mass = attr.Mass,
  103. Material = attr.Material,
  104. scalingPermission = attr.ScalingPermission,
  105. SortIndex = attr.SortIndex,
  106. DefaultColour = attr.DefaultColor.Index,
  107. Volume = attr.Volume,
  108. EdgeConnectingFaces = new[] {0, 1, 2, 3, 4, 5},
  109. PointDataVolumeMultiplier = 1f
  110. };
  111. dataDB.GetValues<CubeListData>().Add(cld.ID.ToString(), cld); //The registration needs to happen after the ID has been set
  112. dataDB.GetFasterValues<CubeListData>().Add(cld.ID, cld); //So can't use the builtin method to create a CubeListData
  113. }
  114. _canRegister = false;
  115. }
  116. public static MethodBase TargetMethod()
  117. {
  118. return AccessTools.Method("RobocraftX.CR.MainGame.MainGameCompositionRoot:Init");
  119. }
  120. }
  121. /*[HarmonyPatch] - The block has no collision even in simulation if using a custom category
  122. private static class FactorySetupPatch
  123. {
  124. public static void Prefix(BlockEntityFactory __instance)
  125. {
  126. var builders = (Dictionary<CubeCategory, IBlockBuilder>)
  127. AccessTools.Field(__instance.GetType(), "_blockBuilders").GetValue(__instance);
  128. builders.Add((CubeCategory) 1000, new BlockBuilder<StandardBlockEntityDescriptor>(Group));
  129. }
  130. public static MethodBase TargetMethod()
  131. {
  132. return AccessTools.Method(typeof(BlockEntityFactory), "ParseDataDB");
  133. }
  134. }*/
  135. internal static IEnumerator Prepare()
  136. { //Should be pretty quick
  137. foreach (var type in _customBlocks.Values)
  138. {
  139. var attr = type.GetCustomAttribute<CustomBlockAttribute>();
  140. Logging.Log("Loading custom block catalog " + attr.Catalog);
  141. var res = Addressables.LoadContentCatalogAsync(attr.Catalog);
  142. while (!res.IsDone) yield return Yield.It;
  143. Logging.Log("Loaded custom block catalog: " + res.Result.LocatorId);
  144. Addressables.AddResourceLocator(res.Result);
  145. }
  146. }
  147. /*internal static void OnBlockFactoryObtained(BlockEntityFactory factory)
  148. {
  149. var builders = (Dictionary<CubeCategory, IBlockBuilder>)
  150. AccessTools.Field(factory.GetType(), "_blockBuilders").GetValue(factory);
  151. builders.Add((CubeCategory) 1000, new BlockBuilder<StandardBlockEntityDescriptor>(Group));
  152. }*/
  153. }
  154. }