Add custom block registration functionality and a test
This commit is contained in:
parent
a7f6a16231
commit
712ece86db
4 changed files with 204 additions and 244 deletions
|
@ -213,7 +213,7 @@ namespace GamecraftModdingAPI
|
||||||
throw new BlockTypeException("The block has the wrong group! The type is " + GetType() +
|
throw new BlockTypeException("The block has the wrong group! The type is " + GetType() +
|
||||||
" while the group is " + id.groupID);
|
" while the group is " + id.groupID);
|
||||||
}
|
}
|
||||||
else if (type != typeof(Block))
|
else if (type != typeof(Block) && !typeof(CustomBlock).IsAssignableFrom(type))
|
||||||
Logging.LogWarning($"Unknown block type! Add {type} to the dictionary.");
|
Logging.LogWarning($"Unknown block type! Add {type} to the dictionary.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using DataLoader;
|
using DataLoader;
|
||||||
|
using GamecraftModdingAPI.App;
|
||||||
using GamecraftModdingAPI.Utility;
|
using GamecraftModdingAPI.Utility;
|
||||||
using GPUInstancer;
|
using GPUInstancer;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
@ -11,8 +13,8 @@ using RobocraftX.Blocks;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.Rendering;
|
using RobocraftX.Rendering;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.ECS;
|
||||||
using Svelto.Tasks;
|
using Svelto.Tasks;
|
||||||
using Unity.Entities;
|
|
||||||
using Unity.Entities.Conversion;
|
using Unity.Entities.Conversion;
|
||||||
using Unity.Physics;
|
using Unity.Physics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
@ -24,264 +26,113 @@ using ScalingPermission = DataLoader.ScalingPermission;
|
||||||
|
|
||||||
namespace GamecraftModdingAPI.Blocks
|
namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class CustomBlock
|
public class CustomBlock : Block
|
||||||
{
|
{
|
||||||
private static ushort nextID = 500;
|
private static ushort nextID = 500;
|
||||||
public static void RegisterCustomBlock(string path)
|
/// <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
|
||||||
{
|
{
|
||||||
var prefabData = new List<PrefabData>();
|
if (!_canRegister)
|
||||||
//category ID:
|
throw new InvalidOperationException(
|
||||||
//0 - regular
|
"It's too late to register custom blocks. Register it before the game starts loading.");
|
||||||
//1 - joint
|
var type = typeof(T);
|
||||||
//2 - controller
|
var attr = type.GetCustomAttribute<CustomBlockAttribute>();
|
||||||
uint prefabId = PrefabsID.FetchNewPrefabID(PrefabsID.GenerateDBID(0, nextID++));
|
if (attr == null)
|
||||||
prefabData.Add(new PrefabData()
|
throw new ArgumentException("The custom block type is missing the CustomBlock annotation");
|
||||||
{
|
string typeName = type.FullName ??
|
||||||
prefabName = path,
|
throw new ArgumentException("The given block type doesn't have a concrete full name.");
|
||||||
prefabId = prefabId
|
if (!File.Exists(attr.Catalog))
|
||||||
});
|
throw new FileNotFoundException("The specified catalog cannot be found for " + typeName);
|
||||||
var loadTask = Addressables.LoadAssetAsync<GameObject>(path);
|
_customBlocks.Add(attr.AssetPath, type);
|
||||||
AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab")
|
Logging.MetaDebugLog("Registered custom block type " + typeName);
|
||||||
.Invoke(ECSGPUIResourceManager.Instance, new object[] {prefabId, loadTask.Result, 1});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]
|
[HarmonyPatch]
|
||||||
public static class Patch
|
public static class Patch
|
||||||
{
|
{
|
||||||
/*public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
|
||||||
{
|
|
||||||
var list = new List<CodeInstruction>(instructions);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
*int index = -1;
|
|
||||||
CodeInstruction loadTask = null;
|
|
||||||
for (var i = 0; i < list.Count - 1; i++)
|
|
||||||
{
|
|
||||||
if (list[i].opcode == OpCodes.Ldfld
|
|
||||||
&& ((string) list[i].operand).Contains("renderingWorld")
|
|
||||||
&& list[i + 1].opcode == OpCodes.Brfalse_S)
|
|
||||||
index = i - 1; //It loads 'this' first
|
|
||||||
if (list[i].opcode == OpCodes.Ldflda
|
|
||||||
&& ((string) list[i].operand).Contains("loadTask"))
|
|
||||||
loadTask = new CodeInstruction(list[i]);
|
|
||||||
}*
|
|
||||||
|
|
||||||
var array = new[]
|
|
||||||
{
|
|
||||||
//Set Yield.It to be returned (current)
|
|
||||||
new CodeInstruction(OpCodes.Ldarg_0), // this
|
|
||||||
new CodeInstruction(OpCodes.Ldsfld,
|
|
||||||
typeof(Yield).GetField("It", BindingFlags.Public | BindingFlags.Static)),
|
|
||||||
new CodeInstruction(OpCodes.Stfld, "object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>2__current'"),
|
|
||||||
|
|
||||||
//Set which yield return we're at (state)
|
|
||||||
new CodeInstruction(OpCodes.Ldarg_0), // this
|
|
||||||
new CodeInstruction(OpCodes.Ldc_I4_1),
|
|
||||||
//new CodeInstruction(OpCodes.Call, ((Action<StringBuffer>)AddInfo).Method)
|
|
||||||
};
|
|
||||||
list.InsertRange(index, array);
|
|
||||||
*
|
|
||||||
IL_00ad: ldarg.0 // this
|
|
||||||
IL_00ae: ldsfld class [Svelto.Tasks]Svelto.Tasks.Yield [Svelto.Tasks]Svelto.Tasks.Yield::It
|
|
||||||
IL_00b3: stfld object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>2__current'
|
|
||||||
|
|
||||||
IL_0072: ldarg.0 // this
|
|
||||||
IL_0073: ldnull
|
|
||||||
IL_0074: stfld object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>2__current'
|
|
||||||
IL_0079: ldarg.0 // this
|
|
||||||
IL_007a: ldc.i4.2
|
|
||||||
IL_007b: stfld object RobocraftX.Common.ECSResourceManagerUtility/'<RegisterPrefabs>d__0'::'<>1__state'
|
|
||||||
IL_0080: ldc.i4.1
|
|
||||||
IL_0081: ret
|
|
||||||
*
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logging.LogWarning("Failed to inject AddInfo method for the debug display!\n" + e);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private static Material[] materials;
|
private static Material[] materials;
|
||||||
|
|
||||||
public static void Prefix(List<PrefabData> prefabData, IList<GameObject> prefabs)
|
public static void Prefix(List<PrefabData> prefabData, IList<GameObject> prefabs)
|
||||||
{
|
{
|
||||||
/*foreach (var block in blocks.Values.Cast<CubeListData>())
|
for (var index = 0; index < prefabs.Count; index++)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Block info: " + block);
|
if (prefabData[index].prefabName == "ConsoleBlock")
|
||||||
}*/
|
materials = prefabs[index].GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials;
|
||||||
|
|
||||||
/*var res = Addressables.LoadContentCatalogAsync("customCatalog.json");
|
|
||||||
while (!res.IsDone) yield return Yield.It;*/
|
|
||||||
foreach (var gameObject in prefabs)
|
|
||||||
{
|
|
||||||
switch (gameObject.name)
|
|
||||||
{
|
|
||||||
case "Cube":
|
|
||||||
gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials = materials;
|
|
||||||
break;
|
|
||||||
case "CTR_CommandBlock":
|
|
||||||
materials = gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
public static MethodBase TargetMethod()
|
||||||
{ //General block registration
|
{ //General block registration
|
||||||
//return AccessTools.Method("RobocraftX.Blocks.BlocksCompositionRoot:RegisterPartPrefabs");
|
|
||||||
return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs");
|
return AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:InitPreRegisteredPrefabs");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[HarmonyPatch]
|
|
||||||
public static class RendererPatch
|
|
||||||
{
|
|
||||||
private static Material[] materials;
|
|
||||||
public static void Prefix(uint prefabID, GameObject gameObject)
|
|
||||||
{
|
|
||||||
Console.WriteLine("ID: " + prefabID + " - Name: " + gameObject.name);
|
|
||||||
if (gameObject.name == "Cube")
|
|
||||||
{
|
|
||||||
//Console.WriteLine("Length: " + gameObject.GetComponentsInChildren<MeshRenderer>().Length);
|
|
||||||
if (materials != null)
|
|
||||||
gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials = materials;
|
|
||||||
/*ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime(
|
|
||||||
new[] {new PrefabData {prefabId = prefabID, prefabName = "Assets/Prefabs/Cube.prefab"}},
|
|
||||||
new List<GameObject> {gameObject});#1#
|
|
||||||
GameObject go = Object.Instantiate(gameObject);
|
|
||||||
go.SetActive(false);
|
|
||||||
AccessTools.Method("RobocraftX.Rendering.ECSGPUIResourceManager:RegisterNewPrefabAtRuntime")
|
|
||||||
.Invoke(ECSGPUIResourceManager.Instance,
|
|
||||||
new object[] {(uint) ((int) prefabID * 2 + 1), gameObject, true});
|
|
||||||
//ECSGPUIResourceManager.Instance.RegisterNewPrefabAtRuntime((uint) ((int)prefabID * 2 + 1), gameObject, true); //.isOcclusionCulling = false;
|
|
||||||
UnityEngine.Object.Destroy(gameObject);
|
|
||||||
GPUInstancerAPI.AddInstancesToPrefabPrototypeAtRuntime(ECSGPUIResourceManager.Instance.prefabManager,
|
|
||||||
gameObject.GetComponent<GPUInstancerPrefab>().prefabPrototype, new[] {gameObject});
|
|
||||||
Console.WriteLine("Registered prefab to instancer");
|
|
||||||
|
|
||||||
/*var register = AccessTools.Method("RobocraftX.Common.ECSPhysicResourceManager:RegisterPrefab",
|
|
||||||
new[] {typeof(uint), typeof(GameObject), typeof(World), typeof(BlobAssetStore)});
|
|
||||||
register.Invoke(ECSPhysicResourceManager.Instance,
|
|
||||||
new object[] {prefabID, gameObject, MGPatch.data.Item1, MGPatch.data.Item2});#1#
|
|
||||||
/*Console.WriteLine(
|
|
||||||
"Entity: " + ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID));
|
|
||||||
Console.WriteLine("Prefab ID: " + PrefabsID.DBIDMAP[500]);
|
|
||||||
PhysicsCollider componentData = MGPatch.data.Item1.EntityManager.GetComponentData<PhysicsCollider>(ECSPhysicResourceManager.Instance.GetUECSPhysicEntityPrefab(prefabID));
|
|
||||||
Console.WriteLine("Collider valid: " + componentData.IsValid);
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
Console.WriteLine("Collider type: " + componentData.ColliderPtr->Type);
|
|
||||||
CollisionFilter filter = componentData.Value.Value.Filter;
|
|
||||||
Console.WriteLine("Filter not empty: " + !filter.IsEmpty);
|
|
||||||
}#1#
|
|
||||||
//MGPatch.data.Item1.EntityManager.GetComponentData<>()
|
|
||||||
gameObject.AddComponent<BoxCollider>();
|
|
||||||
gameObject.AddComponent<Transform>();
|
|
||||||
Console.WriteLine("Registered prefab to physics");
|
|
||||||
ECSGPUIResourceManager.Instance.RegisterGhostsPrefabsAtRuntime();
|
|
||||||
}
|
|
||||||
else if (gameObject.name == "CTR_CommandBlock")
|
|
||||||
materials = gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MethodBase TargetMethod()
|
|
||||||
{RobocraftX.Common.ECSGPUIResourceManager.RegisterPrefab
|
|
||||||
return AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab",
|
|
||||||
new[] {typeof(uint), typeof(GameObject)});
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
public static class RMPatch
|
public static class CubeRegistrationPatch
|
||||||
{
|
{
|
||||||
public static void Prefix(World physicsWorld,
|
public static void Prefix(IDataDB dataDB)
|
||||||
GameObjectConversionSystem getExistingSystem,
|
|
||||||
FasterList<GameObject> gos,
|
|
||||||
List<PrefabData> prefabData)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("First game object data:\n" + gos[0].GetComponents<Component>()
|
//var abd = dataDB.GetValue<CubeListData>((int) BlockIDs.AluminiumCube);
|
||||||
.Select(c => c + " - " + c.name + " " + c.GetType())
|
foreach (var (key, type) in _customBlocks)
|
||||||
.Aggregate((a, b) => a + "\n" + b));
|
|
||||||
for (var index = 0; index < prefabData.Count; index++)
|
|
||||||
{
|
{
|
||||||
var data = prefabData[index];
|
var attr = type.GetCustomAttribute<CustomBlockAttribute>();
|
||||||
if (!data.prefabName.EndsWith("Cube.prefab")) continue;
|
|
||||||
//getExistingSystem.DeclareLinkedEntityGroup(gos[index]);
|
|
||||||
/*Entity entity = GameObjectConversionUtility.ConvertGameObjectHierarchy(gos[index],
|
|
||||||
GameObjectConversionSettings.FromWorld(physicsWorld, MGPatch.data.Item2));*/
|
|
||||||
Console.WriteLine("Transform: " + gos[index].transform.childCount);
|
|
||||||
Entity primaryEntity = getExistingSystem.GetPrimaryEntity(gos[index]);
|
|
||||||
MultiListEnumerator<Entity> entities = getExistingSystem.GetEntities(gos[index]);
|
|
||||||
Console.WriteLine("ID: " + data.prefabId + " - Name: " + data.prefabName);
|
|
||||||
Console.WriteLine("Primary entity: " + primaryEntity);
|
|
||||||
EntityManager entityManager = physicsWorld.EntityManager;
|
|
||||||
Console.WriteLine("Has collider: " + entityManager.HasComponent<PhysicsCollider>(primaryEntity));
|
|
||||||
while (entities.MoveNext())
|
|
||||||
{
|
|
||||||
Entity current = entities.Current;
|
|
||||||
Console.WriteLine("Entity " + current + " has collider: " +
|
|
||||||
entityManager.HasComponent<PhysicsCollider>(current));
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Adding GPUI prefab");
|
|
||||||
((GPUInstancerPrefabManager) GPUInstancerManager.activeManagerList.Single(gim =>
|
|
||||||
gim is GPUInstancerPrefabManager))
|
|
||||||
.AddRuntimeRegisteredPrefab(gos[index].GetComponent<GPUInstancerPrefab>());
|
|
||||||
Console.WriteLine("Added GPUI prefab");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MethodBase TargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method("RobocraftX.Blocks.ECSResourceManagerUtility:RelinkEntities",
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
typeof(World),
|
|
||||||
typeof(GameObjectConversionSystem),
|
|
||||||
typeof(FasterList<GameObject>),
|
|
||||||
typeof(List<PrefabData>)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch]
|
|
||||||
public static class MGPatch
|
|
||||||
{
|
|
||||||
internal static (World, BlobAssetStore) data;
|
|
||||||
public static void Prefix(World physicsWorld, BlobAssetStore blobStore, IDataDB dataDB)
|
|
||||||
{
|
|
||||||
data = (physicsWorld, blobStore);
|
|
||||||
//RobocraftX.CR.MachineEditing.UpdateSelectedGhostBlockEngine.UpdateGhostBlock
|
|
||||||
//var cld = (CubeListData) ((DataImplementor) dataDB).CreateDataObject("500", typeof(CubeListData), null);
|
|
||||||
var abd = dataDB.GetValue<CubeListData>((int) BlockIDs.AluminiumCube);
|
|
||||||
var cld = new CubeListData
|
var cld = new CubeListData
|
||||||
{
|
{ //"Assets/Prefabs/Cube.prefab" - "CTR_CommandBlock" - "strConsoleBlock"
|
||||||
cubeType = CubeType.Block,
|
cubeType = attr.Type,
|
||||||
cubeCategory = CubeCategory.General,
|
cubeCategory = attr.Category,
|
||||||
inventoryCategory = InventoryCategory.Shapes,
|
inventoryCategory = attr.InventoryCategory,
|
||||||
ID = 500,
|
ID = nextID++,
|
||||||
Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path)
|
Path = attr.AssetPath, //Index out of range exception: Asset failed to load (wrong path)
|
||||||
SpriteName = "CTR_CommandBlock",
|
SpriteName = attr.SpriteName,
|
||||||
CubeNameKey = "strConsoleBlock",
|
CubeNameKey = attr.NameKey,
|
||||||
|
CubeDescriptionKey = attr.DescKey,
|
||||||
SelectableFaces = new[] {0, 1, 2, 3, 4, 5},
|
SelectableFaces = new[] {0, 1, 2, 3, 4, 5},
|
||||||
GridScale = new[] {1, 1, 1},
|
GridScale = new[] {5, 5, 5},
|
||||||
Mass = 1,
|
Mass = attr.Mass,
|
||||||
Material = abd.Material,
|
Material = attr.Material,
|
||||||
scalingPermission = ScalingPermission.NonUniform,
|
scalingPermission = attr.ScalingPermission,
|
||||||
SortIndex = 12,
|
SortIndex = attr.SortIndex,
|
||||||
DefaultColour = (byte) BlockColors.Lime,
|
DefaultColour = attr.DefaultColor.Index,
|
||||||
Volume = 1f,
|
Volume = attr.Volume,
|
||||||
timeRunningCollision = TimeRunningCollision.Enabled,
|
|
||||||
IsIsolator = false,
|
|
||||||
EdgeConnectingFaces = new[] {0, 1, 2, 3, 4, 5},
|
EdgeConnectingFaces = new[] {0, 1, 2, 3, 4, 5},
|
||||||
PointDataVolumeMultiplier = 1f
|
PointDataVolumeMultiplier = 1f
|
||||||
};
|
};
|
||||||
Console.WriteLine("Aluminium block data:\n" + abd);
|
dataDB.GetValues<CubeListData>().Add(cld.ID.ToString(), cld); //The registration needs to happen after the ID has been set
|
||||||
Console.WriteLine("Material: " + abd.Material);
|
dataDB.GetFasterValues<CubeListData>().Add(cld.ID, cld); //So can't use the builtin method to create a CubeListData
|
||||||
dataDB.GetValues<CubeListData>().Add("500", cld); //The registration needs to happen after the ID has been set
|
}
|
||||||
dataDB.GetFasterValues<CubeListData>().Add(500, cld);
|
|
||||||
//RobocraftX.ExplosionFragments.Engines.PlayFragmentExplodeEngine.PlayRigidBodyEffect
|
_canRegister = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodBase TargetMethod()
|
public static MethodBase TargetMethod()
|
||||||
|
@ -292,16 +143,15 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
|
|
||||||
public static IEnumerator Prep()
|
public static IEnumerator Prep()
|
||||||
{ //TODO: Don't let the game load until this finishes
|
{ //TODO: Don't let the game load until this finishes
|
||||||
Console.WriteLine("Loading custom catalog...");
|
foreach (var type in _customBlocks.Values)
|
||||||
var res = Addressables.LoadContentCatalogAsync("customCatalog.json");
|
{
|
||||||
|
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;
|
while (!res.IsDone) yield return Yield.It;
|
||||||
Console.WriteLine("Loaded custom catalog: " + res.Result.LocatorId);
|
Logging.Log("Loaded custom block catalog: " + res.Result.LocatorId);
|
||||||
Addressables.AddResourceLocator(res.Result);
|
Addressables.AddResourceLocator(res.Result);
|
||||||
/*Console.WriteLine("Loading Cube asset...");
|
}
|
||||||
var loadTask = Addressables.LoadAssetAsync<GameObject>("Assets/Cube.prefab");
|
|
||||||
while (!loadTask.IsDone) yield return Yield.It;
|
|
||||||
Console.WriteLine("Exception: "+loadTask.OperationException);
|
|
||||||
Console.WriteLine("Result: " + loadTask.Result.name);*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
85
GamecraftModdingAPI/Blocks/CustomBlockAttribute.cs
Normal file
85
GamecraftModdingAPI/Blocks/CustomBlockAttribute.cs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
using System;
|
||||||
|
using DataLoader;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Blocks
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class CustomBlockAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Custom block attribute necessary for configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="catalog">File path to the catalog.json that holds asset references for the custom block</param>
|
||||||
|
/// <param name="assetPath">The path/address to the block's prefab specified in Unity</param>
|
||||||
|
/// <param name="nameKey">The translation key for the block's name</param>
|
||||||
|
/// <param name="spriteName">The path to the inventory sprite for the block, console block by default</param>
|
||||||
|
/// <param name="descKey">The translation key for the block's description</param>
|
||||||
|
public CustomBlockAttribute(string catalog, string assetPath, string nameKey,
|
||||||
|
string spriteName = "CTR_CommandBlock", string descKey = "")
|
||||||
|
{
|
||||||
|
Catalog = catalog;
|
||||||
|
AssetPath = assetPath;
|
||||||
|
SpriteName = spriteName;
|
||||||
|
NameKey = nameKey;
|
||||||
|
DescKey = descKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The location of the catalog.json file used to find assets for this block.
|
||||||
|
/// </summary>
|
||||||
|
public string Catalog { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The asset path/address for the block's prefab.
|
||||||
|
/// </summary>
|
||||||
|
public string AssetPath { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the sprite used in the inventory.
|
||||||
|
/// </summary>
|
||||||
|
public string SpriteName { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The translation key for the block's name.
|
||||||
|
/// </summary>
|
||||||
|
public string NameKey { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The translation key for the block's description.
|
||||||
|
/// </summary>
|
||||||
|
public string DescKey { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The block's type - block, joint, light.
|
||||||
|
/// </summary>
|
||||||
|
public CubeType Type { get; set; } = CubeType.Block;
|
||||||
|
/// <summary>
|
||||||
|
/// The block's category, so it's treated as a pre-existing functional block.
|
||||||
|
/// </summary>
|
||||||
|
public CubeCategory Category { get; set; } = CubeCategory.General;
|
||||||
|
/// <summary>
|
||||||
|
/// The block's inventory category.
|
||||||
|
/// </summary>
|
||||||
|
public InventoryCategory InventoryCategory { get; set; } = InventoryCategory.Shapes;
|
||||||
|
/// <summary>
|
||||||
|
/// The block's mass.
|
||||||
|
/// </summary>
|
||||||
|
public float Mass { get; set; } = 1f;
|
||||||
|
/// <summary>
|
||||||
|
/// The key of the material properties this block should use.
|
||||||
|
/// </summary>
|
||||||
|
public string Material { get; set; } = "Aluminium";
|
||||||
|
/// <summary>
|
||||||
|
/// The scaling permission determining what scaling is allowed on this block.
|
||||||
|
/// </summary>
|
||||||
|
public ScalingPermission ScalingPermission { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The sort index in the inventory.
|
||||||
|
/// </summary>
|
||||||
|
public int SortIndex { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The default color of the block when placed.
|
||||||
|
/// </summary>
|
||||||
|
public BlockColor DefaultColor { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The volume of the block.
|
||||||
|
/// </summary>
|
||||||
|
public float Volume { get; set; } = 1f;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
@ -291,6 +292,13 @@ namespace GamecraftModdingAPI.Tests
|
||||||
.BlockGroup = group;
|
.BlockGroup = group;
|
||||||
}).Build();
|
}).Build();
|
||||||
|
|
||||||
|
CommandBuilder.Builder("placeCustomBlock", "Places a custom block, needs a custom catalog and assets.")
|
||||||
|
.Action((float x, float y, float z) =>
|
||||||
|
{
|
||||||
|
Logging.CommandLog("Block placed: " +
|
||||||
|
Block.PlaceNew<TestBlock>((BlockIDs) 500, new float3(0, 0, 0)));
|
||||||
|
}).Build();
|
||||||
|
|
||||||
GameClient.SetDebugInfo("InstalledMods", InstalledMods);
|
GameClient.SetDebugInfo("InstalledMods", InstalledMods);
|
||||||
Block.Placed += (sender, args) =>
|
Block.Placed += (sender, args) =>
|
||||||
Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID);
|
Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID);
|
||||||
|
@ -361,11 +369,16 @@ namespace GamecraftModdingAPI.Tests
|
||||||
Log.Output("Submitted: " + Window.selected.submittedCode);
|
Log.Output("Submitted: " + Window.selected.submittedCode);
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
/*JObject o1 = JObject.Parse(File.ReadAllText(@"Gamecraft_Data\StreamingAssets\aa\Windows\catalog.json"));
|
|
||||||
JObject o2 = JObject.Parse(File.ReadAllText(@"customCatalog.json"));
|
|
||||||
o1.Merge(o2, new JsonMergeSettings {MergeArrayHandling = MergeArrayHandling.Union});
|
|
||||||
File.WriteAllText(@"Gamecraft_Data\StreamingAssets\aa\Windows\catalog.json", o1.ToString());*/
|
|
||||||
CustomBlock.Prep().RunOn(ExtraLean.UIScheduler);
|
CustomBlock.Prep().RunOn(ExtraLean.UIScheduler);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CustomBlock.RegisterCustomBlock<TestBlock>();
|
||||||
|
Logging.MetaDebugLog("Registered test custom block");
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
Logging.MetaDebugLog("Test custom block catalog not found");
|
||||||
|
}
|
||||||
|
|
||||||
#if TEST
|
#if TEST
|
||||||
TestRoot.RunTests();
|
TestRoot.RunTests();
|
||||||
|
@ -429,6 +442,18 @@ namespace GamecraftModdingAPI.Tests
|
||||||
return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method;
|
return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CustomBlock("customCatalog.json", "Assets/Prefabs/Cube.prefab", "strAluminiumCube", SortIndex = 12)]
|
||||||
|
public class TestBlock : CustomBlock
|
||||||
|
{
|
||||||
|
public TestBlock(EGID id) : base(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestBlock(uint id) : base(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue