|
|
@@ -1,9 +1,11 @@ |
|
|
|
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; |
|
|
@@ -11,8 +13,8 @@ using RobocraftX.Blocks; |
|
|
|
using RobocraftX.Common; |
|
|
|
using RobocraftX.Rendering; |
|
|
|
using Svelto.DataStructures; |
|
|
|
using Svelto.ECS; |
|
|
|
using Svelto.Tasks; |
|
|
|
using Unity.Entities; |
|
|
|
using Unity.Entities.Conversion; |
|
|
|
using Unity.Physics; |
|
|
|
using UnityEngine; |
|
|
@@ -24,264 +26,113 @@ using ScalingPermission = DataLoader.ScalingPermission; |
|
|
|
|
|
|
|
namespace GamecraftModdingAPI.Blocks |
|
|
|
{ |
|
|
|
public class CustomBlock |
|
|
|
public class CustomBlock : Block |
|
|
|
{ |
|
|
|
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>(); |
|
|
|
//category ID: |
|
|
|
//0 - regular |
|
|
|
//1 - joint |
|
|
|
//2 - controller |
|
|
|
uint prefabId = PrefabsID.FetchNewPrefabID(PrefabsID.GenerateDBID(0, nextID++)); |
|
|
|
prefabData.Add(new PrefabData() |
|
|
|
{ |
|
|
|
prefabName = path, |
|
|
|
prefabId = prefabId |
|
|
|
}); |
|
|
|
var loadTask = Addressables.LoadAssetAsync<GameObject>(path); |
|
|
|
AccessTools.Method("RobocraftX.Common.ECSGPUIResourceManager:RegisterPrefab") |
|
|
|
.Invoke(ECSGPUIResourceManager.Instance, new object[] {prefabId, loadTask.Result, 1}); |
|
|
|
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); |
|
|
|
} |
|
|
|
|
|
|
|
[HarmonyPatch] |
|
|
|
public static class Patch |
|
|
|
public CustomBlock(EGID id) : base(id) |
|
|
|
{ |
|
|
|
/*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]); |
|
|
|
}* |
|
|
|
if (id.groupID != Group) |
|
|
|
throw new BlockTypeException("The block is not a custom block! It has a group of " + id.groupID); |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
} |
|
|
|
}*/ |
|
|
|
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) |
|
|
|
{ |
|
|
|
/*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) |
|
|
|
for (var index = 0; index < prefabs.Count; index++) |
|
|
|
{ |
|
|
|
switch (gameObject.name) |
|
|
|
{ |
|
|
|
case "Cube": |
|
|
|
gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials = materials; |
|
|
|
break; |
|
|
|
case "CTR_CommandBlock": |
|
|
|
materials = gameObject.GetComponentsInChildren<MeshRenderer>()[0].sharedMaterials; |
|
|
|
break; |
|
|
|
} |
|
|
|
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.Blocks.BlocksCompositionRoot:RegisterPartPrefabs"); |
|
|
|
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] |
|
|
|
public static class RMPatch |
|
|
|
public static class CubeRegistrationPatch |
|
|
|
{ |
|
|
|
public static void Prefix(World physicsWorld, |
|
|
|
GameObjectConversionSystem getExistingSystem, |
|
|
|
FasterList<GameObject> gos, |
|
|
|
List<PrefabData> prefabData) |
|
|
|
public static void Prefix(IDataDB dataDB) |
|
|
|
{ |
|
|
|
Console.WriteLine("First game object data:\n" + gos[0].GetComponents<Component>() |
|
|
|
.Select(c => c + " - " + c.name + " " + c.GetType()) |
|
|
|
.Aggregate((a, b) => a + "\n" + b)); |
|
|
|
for (var index = 0; index < prefabData.Count; index++) |
|
|
|
//var abd = dataDB.GetValue<CubeListData>((int) BlockIDs.AluminiumCube); |
|
|
|
foreach (var (key, type) in _customBlocks) |
|
|
|
{ |
|
|
|
var data = prefabData[index]; |
|
|
|
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"); |
|
|
|
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 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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 |
|
|
|
{ |
|
|
|
cubeType = CubeType.Block, |
|
|
|
cubeCategory = CubeCategory.General, |
|
|
|
inventoryCategory = InventoryCategory.Shapes, |
|
|
|
ID = 500, |
|
|
|
Path = "Assets/Prefabs/Cube.prefab", //Index out of range exception: Asset failed to load (wrong path) |
|
|
|
SpriteName = "CTR_CommandBlock", |
|
|
|
CubeNameKey = "strConsoleBlock", |
|
|
|
SelectableFaces = new[] {0, 1, 2, 3, 4, 5}, |
|
|
|
GridScale = new[] {1, 1, 1}, |
|
|
|
Mass = 1, |
|
|
|
Material = abd.Material, |
|
|
|
scalingPermission = ScalingPermission.NonUniform, |
|
|
|
SortIndex = 12, |
|
|
|
DefaultColour = (byte) BlockColors.Lime, |
|
|
|
Volume = 1f, |
|
|
|
timeRunningCollision = TimeRunningCollision.Enabled, |
|
|
|
IsIsolator = false, |
|
|
|
EdgeConnectingFaces = new[] {0, 1, 2, 3, 4, 5}, |
|
|
|
PointDataVolumeMultiplier = 1f |
|
|
|
}; |
|
|
|
Console.WriteLine("Aluminium block data:\n" + abd); |
|
|
|
Console.WriteLine("Material: " + abd.Material); |
|
|
|
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() |
|
|
@@ -292,16 +143,15 @@ namespace GamecraftModdingAPI.Blocks |
|
|
|
|
|
|
|
public static IEnumerator Prep() |
|
|
|
{ //TODO: Don't let the game load until this finishes |
|
|
|
Console.WriteLine("Loading custom catalog..."); |
|
|
|
var res = Addressables.LoadContentCatalogAsync("customCatalog.json"); |
|
|
|
while (!res.IsDone) yield return Yield.It; |
|
|
|
Console.WriteLine("Loaded custom catalog: " + res.Result.LocatorId); |
|
|
|
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);*/ |
|
|
|
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); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |