|
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Reflection;
- using HarmonyLib;
-
- using DataLoader;
- using RobocraftX.Common;
- using RobocraftX.Rendering;
- using RobocraftX.Schedulers;
- using Svelto.ECS;
- using Svelto.ECS.Experimental;
- using Svelto.Tasks;
- using Svelto.Tasks.ExtraLean;
- using UnityEngine;
- using UnityEngine.AddressableAssets;
- using Material = UnityEngine.Material;
-
- using GamecraftModdingAPI.Utility;
- using ServiceLayer;
-
- namespace GamecraftModdingAPI.Blocks
- {
- /// <summary>
- /// Experimental support for adding custom blocks to the game.
- /// </summary>
- public class CustomBlock : Block
- {
- private static ushort nextID = 500;
- /// <summary>
- /// Key: Prefab path
- /// </summary>
- private static readonly Dictionary<string, Type> CustomBlocks = new Dictionary<string, Type>();
- //private static readonly CustomBlockEngine Engine = new CustomBlockEngine();
- private static readonly List<(ushort id, Action<CubeListData> action)> BlockChangeActions =
- new List<(ushort, Action<CubeListData>)>();
-
- private static bool _canRegister = true;
-
- /// <summary>
- /// Register a custom block type. Call it as soon as possible (in OnApplicationStart()).<br />
- /// 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.
- /// </summary>
- /// <typeparam name="T">The custom block type</typeparam>
- public static void RegisterCustomBlock<T>() where T : CustomBlock
- {
- 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<CustomBlockAttribute>();
- 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);
- }
-
- /// <summary>
- /// A low-level method for changing any property of an existing block. Use with caution.
- /// </summary>
- /// <param name="id">The block ID</param>
- /// <param name="modifier">An action that modifies a property of the block</param>
- public static void ChangeExistingBlock(ushort id, Action<CubeListData> modifier)
- {
- BlockChangeActions.Add((id, modifier));
- }
-
- public CustomBlock(EGID id) : base(id)
- {
- /*if (id.groupID != Group)
- throw new BlockTypeException("The block is not a custom block! It has a group of " + id.groupID);*/
- }
-
- public CustomBlock(uint id) : base(id)
- {
- }
-
- //public static ExclusiveGroup Group { get; } = new ExclusiveGroup("Custom block");
-
- //[HarmonyPatch] - TODO
- public static class MaterialCopyPatch
- {
- private static Material[] materials;
-
- public static void Prefix(List<PrefabData> prefabData, IList<GameObject> prefabs)
- {
- for (var index = 0; index < prefabs.Count; index++)
- {
- if (prefabData[index].prefabName == "ConsoleBlock")
- materials = prefabs[index].GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials;
- }
-
- for (var index = 0; index < prefabs.Count; index++)
- {
- if (CustomBlocks.ContainsKey(prefabData[index].prefabName)) //This is a custom block
- prefabs[index].GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials = materials;
- }
- }
-
- public static MethodBase TargetMethod()
- { //General block registration
- return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs");
- }
- }
-
- [HarmonyPatch]
- public static class CubeRegistrationPatch
- {
- public static void Prefix(IDataDB dataDB)
- {
- //var abd = dataDB.GetValue<CubeListData>((int) BlockIDs.Cube);
- foreach (var (key, type) in CustomBlocks)
- {
- var attr = type.GetCustomAttribute<CustomBlockAttribute>();
- var cld = new CubeListData
- { //"Assets/Prefabs/Cube.prefab" - "CTR_CommandBlock" - "strConsoleBlock"
- cubeType = attr.Type,
- //cubeCategory = (CubeCategory) 1000,
- 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},
- DefaultMaterialID = 0, //TODO: Material API
- 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<CubeListData>().Add(cld.ID.ToString(), cld); //The registration needs to happen after the ID has been set
- dataDB.GetFasterValues<CubeListData>().Add(cld.ID, cld); //So can't use the builtin method to create a CubeListData
- //Engine.RegisterBlock((ushort) cld.ID, key); - TODO
- }
-
- foreach (var (id, action) in BlockChangeActions)
- action(dataDB.GetValue<CubeListData>(id));
-
- /*foreach (var (key, value) in dataDB.GetValues<CubeListData>())
- {
- var data = (CubeListData) value;
- Console.WriteLine($"ID: {key} - Name: {data.CubeNameKey}: {LocalizationService.Localize(data.CubeNameKey)}");
- }*/
-
- _canRegister = false;
- }
-
- public static MethodBase TargetMethod()
- {
- return AccessTools.Method("RobocraftX.CR.MainGame.MainGameCompositionRoot:Init");
- }
- }
-
- /*[HarmonyPatch] - The block has no collision even in simulation if using a custom category
- private static class FactorySetupPatch
- {
- public static void Prefix(BlockEntityFactory __instance)
- {
- var builders = (Dictionary<CubeCategory, IBlockBuilder>)
- AccessTools.Field(__instance.GetType(), "_blockBuilders").GetValue(__instance);
- builders.Add((CubeCategory) 1000, new BlockBuilder<StandardBlockEntityDescriptor>(Group));
- }
-
- public static MethodBase TargetMethod()
- {
- return AccessTools.Method(typeof(BlockEntityFactory), "ParseDataDB");
- }
- }*/
-
- private static IEnumerator Prepare()
- { //Should be pretty quick
- foreach (var type in CustomBlocks.Values)
- {
- var attr = type.GetCustomAttribute<CustomBlockAttribute>();
- 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);
- }
- }
-
- internal new static void Init()
- {
- Prepare().RunOn(ExtraLean.UIScheduler);
- //GameEngineManager.AddGameEngine(Engine); - TODO: Fix serialization and implement block ID update
- }
-
- /*internal static void OnBlockFactoryObtained(BlockEntityFactory factory)
- {
- var builders = (Dictionary<CubeCategory, IBlockBuilder>)
- AccessTools.Field(factory.GetType(), "_blockBuilders").GetValue(factory);
- builders.Add((CubeCategory) 1000, new BlockBuilder<StandardBlockEntityDescriptor>(Group));
- }*/
-
- internal struct DataStruct : IEntityComponent
- {
- public ECSString Name;
- public ushort ID;
- }
- }
- }
|