TechbloxModdingAPI/GamecraftModdingAPI/Blocks/CustomBlock.cs

157 lines
6.7 KiB
C#
Raw Normal View History

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using DataLoader;
using GamecraftModdingAPI.App;
using GamecraftModdingAPI.Utility;
using GPUInstancer;
using HarmonyLib;
using RobocraftX.Blocks;
using RobocraftX.Common;
using RobocraftX.Rendering;
using Svelto.DataStructures;
using Svelto.ECS;
using Svelto.Tasks;
using Unity.Entities.Conversion;
using Unity.Physics;
using UnityEngine;
using UnityEngine.AddressableAssets;
using BoxCollider = UnityEngine.BoxCollider;
using Material = UnityEngine.Material;
using Object = UnityEngine.Object;
using ScalingPermission = DataLoader.ScalingPermission;
namespace GamecraftModdingAPI.Blocks
{
public class CustomBlock : Block
{
private static ushort nextID = 500;
/// <summary>
/// Key: Prefab path
/// </summary>
private static Dictionary<string, Type> _customBlocks = new Dictionary<string, Type>();
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);
}
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) : this(new EGID(id, Group))
{
}
public static ExclusiveGroup Group { get; } = new ExclusiveGroup("Custom block");
[HarmonyPatch]
public static class Patch
{
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.AluminiumCube);
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 = 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},
Mass = attr.Mass,
Material = attr.Material,
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
}
_canRegister = false;
}
public static MethodBase TargetMethod()
{
return AccessTools.Method("RobocraftX.CR.MainGame.MainGameCompositionRoot:Init");
}
}
public static IEnumerator Prep()
{ //TODO: Don't let the game load until this finishes
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);
}
}
}
}