@@ -1,7 +1,10 @@ | |||
using System.Collections.Generic; | |||
using HarmonyLib; | |||
using RobocraftX.Blocks; | |||
using RobocraftX.Common; | |||
using RobocraftX.GroupTags; | |||
using RobocraftX.PilotSeat; | |||
using Svelto.ECS; | |||
using Techblox.EngineBlock; | |||
using Techblox.ServoBlocksServer; | |||
using Techblox.WheelRigBlock; | |||
@@ -11,6 +14,11 @@ namespace CodeGenerator | |||
internal class Program | |||
{ | |||
public static void Main(string[] args) | |||
{ | |||
GenerateBlockClasses(); | |||
} | |||
private static void GenerateBlockClasses() | |||
{ | |||
var bcg = new BlockClassGenerator(); | |||
bcg.Generate("Engine", null, new Dictionary<string, string> | |||
@@ -0,0 +1,25 @@ | |||
using System.Text.RegularExpressions; | |||
using Mono.Cecil; | |||
Console.WriteLine("Starting assembly editing..."); | |||
var fileRegex = | |||
new Regex(".*(Techblox|Gamecraft|RobocraftX|FullGame|RobocraftECS|DataLoader|RCX|GameState|Svelto\\.ECS)[^/]*(\\.dll)"); | |||
foreach (var file in Directory.EnumerateFiles(@"../../../../../ref/Techblox_Data/Managed")) | |||
{ | |||
if (!fileRegex.IsMatch(file)) continue; | |||
Console.WriteLine(file); | |||
ProcessAssembly(file); | |||
} | |||
void ProcessAssembly(string path) | |||
{ | |||
using var mod = ModuleDefinition.ReadModule(path, new(ReadingMode.Immediate) { ReadWrite = true }); | |||
foreach (var typeDefinition in mod.Types) | |||
{ | |||
typeDefinition.IsPublic = true; | |||
foreach (var method in typeDefinition.Methods) method.IsPublic = true; | |||
foreach (var field in typeDefinition.Fields) field.IsPublic = true; | |||
} | |||
mod.Write(); | |||
} |
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechbloxModdingAPI", "Techb | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeEverythingPublicInGame", "MakeEverythingPublicInGame\MakeEverythingPublicInGame.csproj", "{391A3107-E5C6-4A04-9467-6D868AA9A8B4}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|Any CPU = Debug|Any CPU | |||
@@ -26,6 +28,12 @@ Global | |||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Test|Any CPU.ActiveCfg = Debug|Any CPU | |||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Test|Any CPU.Build.0 = Debug|Any CPU | |||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Test|Any CPU.ActiveCfg = Debug|Any CPU | |||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Test|Any CPU.Build.0 = Debug|Any CPU | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
@@ -114,11 +114,6 @@ namespace TechbloxModdingAPI.App | |||
popup.SecondButton(); | |||
} | |||
internal void CloseBetaPopup() | |||
{ | |||
Game.menuEngine.CloseBetaPopup(); | |||
} | |||
internal static void Init() | |||
{ | |||
// this would have been so much simpler if this didn't involve a bunch of internal fields & classes | |||
@@ -140,18 +140,19 @@ namespace TechbloxModdingAPI.App | |||
{ | |||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>(); | |||
List<EGID> blockEGIDs = new List<EGID>(); | |||
foreach (var ((buffer, count), _) in allBlocks) | |||
foreach (var ((_, ids, count), group) in allBlocks) | |||
{ | |||
for (int i = 0; i < count; i++) | |||
{ | |||
var id = new EGID(ids[i], group); | |||
uint dbid; | |||
if (filter == BlockIDs.Invalid) | |||
dbid = (uint)filter; | |||
else | |||
dbid = entitiesDB.QueryEntity<DBEntityStruct>(buffer[i].ID).DBID; | |||
var ownership = entitiesDB.QueryEntity<BlockOwnershipComponent>(buffer[i].ID).BlockOwnership; | |||
dbid = entitiesDB.QueryEntity<DBEntityStruct>(id).DBID; | |||
var ownership = entitiesDB.QueryEntity<BlockOwnershipComponent>(id).BlockOwnership; | |||
if ((ownership & BlockOwnership.User) != 0 && dbid == (ulong)filter) | |||
blockEGIDs.Add(buffer[i].ID); | |||
blockEGIDs.Add(id); | |||
} | |||
} | |||
@@ -154,16 +154,6 @@ namespace TechbloxModdingAPI.App | |||
{ | |||
return ref entitiesDB.QueryEntity<T>(id); | |||
} | |||
internal void CloseBetaPopup() | |||
{ | |||
var (buffer, count) = entitiesDB.QueryEntities<TogglePanelButtonEntityViewStruct>(ExclusiveGroup.Search("BetaPopup")); | |||
for (int index = 0; index < count; ++index) | |||
{ | |||
entitiesDB.QueryEntity<GUIEntityViewStruct>(buffer[index].TogglePanelButtonComponent.targetPanel) | |||
.guiRoot.enabled = false; | |||
} | |||
} | |||
} | |||
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { } | |||
@@ -302,6 +302,7 @@ namespace TechbloxModdingAPI.Blocks | |||
FloodLight, | |||
SoccerBall, | |||
CircularWallLight, | |||
BlueSkyAtmos, | |||
DirtToGrassTransitionTile = 393, | |||
DirtToGrassTransitionInnerTile, | |||
DirtToGrassTransitionOuterTile, | |||
@@ -362,10 +363,21 @@ namespace TechbloxModdingAPI.Blocks | |||
SmallGridHill, | |||
SmallGridHillInnerCorner, | |||
SmallGridHillOuterCorner, | |||
Vector7SmallJet = 460, | |||
AimingAxleServo, | |||
AimingHingeServo, | |||
WeaponDisabler, | |||
Vector7SmallJet, | |||
Vector7MediumJet, | |||
Vector7LargeJet, | |||
Vector7XLJet, | |||
Vector7XXLJet | |||
Vector7XXLJet, | |||
APCWheelRigNoSteering, | |||
APCWheelRigWithSteering, | |||
APCWheel, | |||
APCSeat, | |||
APCEngine, | |||
DamageScoreBlock, | |||
KillScoreBlock, | |||
Autocannon = 480 | |||
} | |||
} |
@@ -33,13 +33,14 @@ namespace TechbloxModdingAPI.Blocks | |||
WoodRoughGrungy, | |||
Boundary, | |||
Emissive, | |||
AircraftPaneling_Riveted_Painted, | |||
AircraftPaneling_Riveted_Metallic, | |||
Steel_Bodywork_Pearlescent, | |||
Steel_Bodywork_RadWrap, | |||
Steel_Bodywork_Glitter, | |||
AircraftPanelingRivetedPainted, | |||
AircraftPanelingRivetedMetallic, | |||
SteelBodyworkPearlescent, | |||
SteelBodyworkRadWrap, | |||
SteelBodyworkGlitter, | |||
BouncyRubber, | |||
BouncyRubber_TieDye, | |||
FuturisticPaneling_Riveted_Painted = 40 | |||
BouncyRubberTieDye, | |||
BrickPainted, | |||
FuturisticPanelingRivetedPainted, | |||
} | |||
} |
@@ -5,7 +5,6 @@ using System.Reflection; | |||
using DataLoader; | |||
using Svelto.Tasks; | |||
using Svelto.Tasks.Enumerators; | |||
using Unity.Mathematics; | |||
using TechbloxModdingAPI.Tests; | |||
@@ -80,35 +79,50 @@ namespace TechbloxModdingAPI.Blocks | |||
yield break; | |||
for (var index = 0; index < blocks.Length; index++) | |||
{ | |||
if (index % 10 == 0) yield return new WaitForSecondsEnumerator(1f).Continue(); //The material or flipped status can only be changed 130 times per submission | |||
var block = blocks[index]; | |||
if (!block.Exists) continue; | |||
foreach (var property in block.GetType().GetProperties()) | |||
{ | |||
//Includes specialised block properties | |||
if (property.SetMethod == null) continue; | |||
var testValues = new (Type, object, Predicate<object>)[] | |||
bool3 Float3Compare(float3 a, float3 b) | |||
{ // From Unity reference code | |||
return math.abs(b - a) < math.max( | |||
0.000001f * math.max(math.abs(a), math.abs(b)), | |||
float.Epsilon * 8 | |||
); | |||
} | |||
bool4 Float4Compare(float4 a, float4 b) | |||
{ // From Unity reference code | |||
return math.abs(b - a) < math.max( | |||
0.000001f * math.max(math.abs(a), math.abs(b)), | |||
float.Epsilon * 8 | |||
); | |||
} | |||
var testValues = new (Type, object, Predicate<(object Value, object Default)>)[] | |||
{ | |||
//(type, default value, predicate or null for equality) | |||
(typeof(long), 3, null), | |||
(typeof(int), 4, null), | |||
(typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon), | |||
(typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon), | |||
(typeof(bool), true, obj => (bool) obj), | |||
(typeof(string), "Test", obj => (string) obj == "Test"), //String equality check | |||
(typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)), | |||
(typeof(double), 5.2f, t => Math.Abs((double) t.Value - (double) t.Default) < float.Epsilon), | |||
(typeof(float), 5.2f, t => Math.Abs((float) t.Value - (float) t.Default) < float.Epsilon), | |||
(typeof(bool), true, t => (bool) t.Value), | |||
(typeof(string), "Test", t => (string) t.Value == "Test"), //String equality check | |||
(typeof(float3), (float3) 20, t => math.all(Float3Compare((float3)t.Value, (float3)t.Default))), | |||
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null), | |||
(typeof(float4), (float4) 5, obj => math.all((float4) obj - 5 < (float4) float.Epsilon)) | |||
(typeof(float4), (float4) 5, t => math.all(Float4Compare((float4)t.Value, (float4)t.Default))) | |||
}; | |||
var propType = property.PropertyType; | |||
if (!propType.IsValueType) continue; | |||
(object valueToUse, Predicate<object> predicateToUse) = (null, null); | |||
(object valueToUse, Predicate<(object Value, object Default)> predicateToUse) = (null, null); | |||
foreach (var (type, value, predicate) in testValues) | |||
{ | |||
if (type.IsAssignableFrom(propType)) | |||
{ | |||
valueToUse = value; | |||
predicateToUse = predicate ?? (obj => Equals(obj, value)); | |||
predicateToUse = predicate ?? (t => Equals(t.Value, t.Default)); | |||
break; | |||
} | |||
} | |||
@@ -117,7 +131,7 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
var values = propType.GetEnumValues(); | |||
valueToUse = values.GetValue(values.Length / 2); | |||
predicateToUse = val => Equals(val, valueToUse); | |||
predicateToUse = t => Equals(t.Value, t.Default); | |||
} | |||
if (valueToUse == null) | |||
@@ -145,7 +159,7 @@ namespace TechbloxModdingAPI.Blocks | |||
continue; | |||
} | |||
var attr = property.GetCustomAttribute<TestValueAttribute>(); | |||
if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got))) | |||
if (!predicateToUse((got, valueToUse)) && (attr == null || !Equals(attr.PossibleValue, got))) | |||
{ | |||
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}."); | |||
yield break; | |||
@@ -23,18 +23,18 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
} | |||
/// <summary> | |||
/*/// <summary> - TODO: Internal struct access | |||
/// Gets or sets the Engine's On property. May not be saved. | |||
/// </summary> | |||
public bool On | |||
{ | |||
get | |||
{ | |||
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "engineOn"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "engineOn", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn = value; | |||
} | |||
} | |||
@@ -45,11 +45,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((int)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentGear"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentGear", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear = value; | |||
} | |||
} | |||
@@ -60,11 +60,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "gearChangeCountdown"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "gearChangeCountdown", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown = value; | |||
} | |||
} | |||
@@ -75,11 +75,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmAV"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmAV", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV = value; | |||
} | |||
} | |||
@@ -90,11 +90,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmLV"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmLV", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV = value; | |||
} | |||
} | |||
@@ -105,11 +105,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmAV"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmAV", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV = value; | |||
} | |||
} | |||
@@ -120,11 +120,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmLV"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmLV", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV = value; | |||
} | |||
} | |||
@@ -135,11 +135,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentTorque"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentTorque", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque = value; | |||
} | |||
} | |||
@@ -150,11 +150,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityAV"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityAV", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV = value; | |||
} | |||
} | |||
@@ -165,11 +165,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityLV"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityLV", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV = value; | |||
} | |||
} | |||
@@ -180,11 +180,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((int)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelCount"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelCount", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount = value; | |||
} | |||
} | |||
@@ -195,11 +195,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearUpInput"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearUpInput", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput = value; | |||
} | |||
} | |||
@@ -210,11 +210,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearDownInput"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearDownInput", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput = value; | |||
} | |||
} | |||
@@ -225,11 +225,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "manualToAutoGearCoolOffCounter"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "manualToAutoGearCoolOffCounter", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter = value; | |||
} | |||
} | |||
@@ -240,11 +240,11 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
get | |||
{ | |||
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "load"))); | |||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load; | |||
} | |||
set | |||
{ | |||
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "load", value); | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load = value; | |||
} | |||
} | |||
@@ -377,6 +377,6 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value; | |||
} | |||
} | |||
}*/ | |||
} | |||
} |
@@ -21,6 +21,8 @@ using Unity.Mathematics; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
using TechbloxModdingAPI.Utility.ECS; | |||
using PrefabsID = RobocraftX.Common.PrefabsID; | |||
namespace TechbloxModdingAPI.Blocks.Engines | |||
{ | |||
@@ -70,7 +72,7 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
public float4 ConvertBlockColor(byte index) => index == byte.MaxValue | |||
? new float4(-1f, -1f, -1f, -1f) | |||
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index, | |||
CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour; | |||
ColourPaletteExclusiveGroups.COLOUR_PALETTE_GROUP).Colour; | |||
public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent | |||
{ | |||
@@ -160,7 +162,7 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
public void UpdateBlockColor(EGID id) | |||
{ | |||
entitiesDB.PublishEntityChange<ColourParameterEntityStruct>(id); | |||
entitiesDB.PublishEntityChangeDelayed<ColourParameterEntityStruct>(id); | |||
} | |||
public bool BlockExists(EGID blockID) | |||
@@ -171,14 +173,13 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
public SimBody[] GetSimBodiesFromID(byte id) | |||
{ | |||
var ret = new FasterList<SimBody>(4); | |||
var (oids, tags, count) = entitiesDB.QueryEntities<ObjectIdEntityStruct, BlockTagEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP); | |||
var oids = entitiesDB.QueryEntitiesOptional<ObjectIdEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP); | |||
EGIDMapper<GridConnectionsEntityStruct>? connections = null; | |||
for (int i = 0; i < count; i++) | |||
foreach (var oid in oids) | |||
{ | |||
if (oids[i].objectId != id) continue; | |||
var tag = tags[i]; | |||
if (oid.Get().objectId != id) continue; | |||
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise | |||
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(tag.ID.groupID); | |||
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.EGID.groupID); | |||
//var rid = connections.Value.Entity(tag.ID.entityID).machineRigidBodyId; | |||
/*foreach (var rb in ret) - TODO | |||
{ | |||
@@ -278,11 +279,11 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
return Array.Empty<ObjectID>(); | |||
var ret = new FasterList<ObjectID>(4); | |||
var (oids, tags, count) = entitiesDB.QueryEntities<ObjectIDTweakableComponent, BlockTagEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP); | |||
for (var index = 0; index < count; index++) | |||
var oids = entitiesDB.QueryEntitiesOptional<ObjectIDTweakableComponent>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP); | |||
foreach (var oid in oids) | |||
{ | |||
if (oids[index].objectIDToTrigger == id) | |||
ret.Add(new ObjectID(tags[index].ID)); | |||
if (oid.Get().objectIDToTrigger == id) | |||
ret.Add(new ObjectID(oid.EGID)); | |||
} | |||
return ret.ToArray(); | |||
@@ -21,6 +21,7 @@ using Svelto.ECS.Serialization; | |||
using Techblox.Blocks.Connections; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
using TechbloxModdingAPI.Utility.ECS; | |||
using Unity.Collections; | |||
using Unity.Mathematics; | |||
using UnityEngine; | |||
@@ -7,6 +7,7 @@ using Unity.Transforms; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
using TechbloxModdingAPI.Utility.ECS; | |||
namespace TechbloxModdingAPI.Blocks.Engines | |||
{ | |||
@@ -16,6 +16,7 @@ using Unity.Mathematics; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
using TechbloxModdingAPI.Utility.ECS; | |||
namespace TechbloxModdingAPI.Blocks.Engines | |||
{ | |||
@@ -22,7 +22,7 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
{ | |||
private static IEntityFunctions _entityFunctions; | |||
private static MachineGraphConnectionEntityFactory _connectionFactory; | |||
private NativeHashSet<ulong> removedConnections = new(2000, Allocator.Persistent); | |||
private NativeHashSet<ulong> removedConnections; | |||
public bool RemoveBlock(EGID target) | |||
{ | |||
@@ -44,6 +44,7 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
public void Ready() | |||
{ | |||
removedConnections = new(2000, Allocator.Persistent); | |||
} | |||
public EntitiesDB entitiesDB { get; set; } | |||
@@ -77,7 +78,8 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
public JobHandle DeterministicStep(in float deltaTime, JobHandle inputDeps) | |||
{ | |||
removedConnections.Clear(); | |||
if (removedConnections.IsCreated) | |||
removedConnections.Clear(); | |||
return default; | |||
} | |||
} |
@@ -7,6 +7,7 @@ using UnityEngine; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
using TechbloxModdingAPI.Utility.ECS; | |||
namespace TechbloxModdingAPI.Blocks.Engines | |||
{ | |||
@@ -6,6 +6,7 @@ using Svelto.ECS; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
using TechbloxModdingAPI.Utility.ECS; | |||
namespace TechbloxModdingAPI.Blocks.Engines | |||
{ | |||
@@ -41,7 +42,7 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
// implementations for block wiring | |||
public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort) | |||
public (WireEntityStruct Wire, EGID ID) CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort) | |||
{ | |||
EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group); | |||
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID); | |||
@@ -50,10 +51,9 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
sourceBlockEGID = startBlock, | |||
sourcePortUsage = startPort, | |||
destinationBlockEGID = endBlock, | |||
destinationPortUsage = endPort, | |||
ID = wireEGID | |||
destinationPortUsage = endPort | |||
}); | |||
return wireInitializer.Get<WireEntityStruct>(); | |||
return (wireInitializer.Get<WireEntityStruct>(), wireEGID); | |||
} | |||
public ref WireEntityStruct GetWire(EGID wire) | |||
@@ -226,27 +226,27 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i; | |||
if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) || | |||
array[index].usage != portUsage) continue; | |||
return new OptionalRef<PortEntityStruct>(array, index); | |||
return new OptionalRef<PortEntityStruct>(array, index, new EGID(entityID, group)); | |||
} | |||
return default; | |||
} | |||
public ref WireEntityStruct MatchPortToWire(PortEntityStruct port, EGID blockID, out bool exists) | |||
public OptionalRef<WireEntityStruct> MatchPortToWire(PortEntityStruct port, EGID blockID, out EGID wireID) | |||
{ | |||
var (wires, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group); | |||
var (wires, ids, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group); | |||
for (uint i = 0; i < count; i++) | |||
{ | |||
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID) | |||
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID)) | |||
{ | |||
exists = true; | |||
return ref wires[i]; | |||
wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group); | |||
return new OptionalRef<WireEntityStruct>(wires, i); | |||
} | |||
} | |||
exists = false; | |||
WireEntityStruct[] defRef = new WireEntityStruct[1]; | |||
return ref defRef[0]; | |||
wireID = default; | |||
return default; | |||
} | |||
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue) | |||
@@ -275,19 +275,20 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) }; | |||
} | |||
var (wires, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group); | |||
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++) | |||
{ | |||
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]); | |||
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++) | |||
{ | |||
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]); | |||
for (int w = 0; w < count; w++) | |||
foreach (var wireOpt in entitiesDB.QueryEntitiesOptional<WireEntityStruct>( | |||
NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)) | |||
{ | |||
if ((wires[w].destinationPortUsage == endPES.usage && wires[w].destinationBlockEGID == endBlock) | |||
&& (wires[w].sourcePortUsage == startPES.usage && wires[w].sourceBlockEGID == startBlock)) | |||
var wire = wireOpt.Get(); | |||
if ((wire.destinationPortUsage == endPES.usage && wire.destinationBlockEGID == endBlock) | |||
&& (wire.sourcePortUsage == startPES.usage && wire.sourceBlockEGID == startBlock)) | |||
{ | |||
return wires[w].ID; | |||
return wireOpt.EGID; | |||
} | |||
} | |||
} | |||
@@ -308,12 +309,12 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
public EGID[] GetElectricBlocks() | |||
{ | |||
var res = new FasterList<EGID>(); | |||
foreach (var ((coll, count), _) in entitiesDB.QueryEntities<BlockPortsStruct>()) | |||
foreach (var ((coll, ids, count), _) in entitiesDB.QueryEntities<BlockPortsStruct>()) | |||
{ | |||
for (int i = 0; i < count; i++) | |||
{ | |||
ref BlockPortsStruct s = ref coll[i]; | |||
//res.Add(s.ID); - TODO | |||
//res.Add(s.ID); - TODO: Would need to search for the groups for each block | |||
} | |||
} | |||
@@ -322,42 +323,18 @@ namespace TechbloxModdingAPI.Blocks.Engines | |||
public EGID[] WiredToInput(EGID block, byte port) | |||
{ | |||
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group, | |||
(WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block); | |||
EGID[] result = new EGID[wireEntityStructs.Length]; | |||
for (uint i = 0; i < wireEntityStructs.Length; i++) | |||
{ | |||
result[i] = wireEntityStructs[i].ID; | |||
} | |||
return result; | |||
return entitiesDB | |||
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group) | |||
.ToArray(wire => wire.ID, | |||
wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block); | |||
} | |||
public EGID[] WiredToOutput(EGID block, byte port) | |||
{ | |||
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group, | |||
(WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block); | |||
EGID[] result = new EGID[wireEntityStructs.Length]; | |||
for (uint i = 0; i < wireEntityStructs.Length; i++) | |||
{ | |||
result[i] = wireEntityStructs[i].ID; | |||
} | |||
return result; | |||
} | |||
private T[] Search<T>(ExclusiveGroup group, Func<T, bool> isMatch) where T : unmanaged, IEntityComponent | |||
{ | |||
FasterList<T> results = new FasterList<T>(); | |||
var (components, count) = entitiesDB.QueryEntities<T>(group); | |||
for (uint i = 0; i < count; i++) | |||
{ | |||
if (isMatch(components[i])) | |||
{ | |||
results.Add(components[i]); | |||
} | |||
} | |||
return results.ToArray(); | |||
return entitiesDB | |||
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group) | |||
.ToArray(wire => wire.ID, | |||
wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block); | |||
} | |||
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true) | |||
@@ -46,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks | |||
/// <returns>The connected wire.</returns> | |||
/// <param name="portId">Port identifier.</param> | |||
/// <param name="connected">Whether the port has a wire connected to it.</param> | |||
protected ref WireEntityStruct GetConnectedWire(PortEntityStruct port, out bool connected) | |||
protected OptionalRef<WireEntityStruct> GetConnectedWire(PortEntityStruct port, out EGID egid) | |||
{ | |||
return ref SignalEngine.MatchPortToWire(port, Id, out connected); | |||
return SignalEngine.MatchPortToWire(port, Id, out egid); | |||
} | |||
/// <summary> | |||
@@ -5,6 +5,7 @@ using Svelto.ECS; | |||
using Svelto.ECS.Experimental; | |||
using TechbloxModdingAPI.Blocks.Engines; | |||
using TechbloxModdingAPI.Utility; | |||
namespace TechbloxModdingAPI.Blocks | |||
{ | |||
@@ -30,8 +31,8 @@ namespace TechbloxModdingAPI.Blocks | |||
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort) | |||
{ | |||
WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort); | |||
return new Wire(wire, start, end); | |||
var (wire, id) = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort); | |||
return new Wire(wire, start, end, id); | |||
} | |||
/// <summary> | |||
@@ -45,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
var port = signalEngine.MatchBlockIOToPort(end, endPort, false); | |||
if (!port) return null; | |||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out var exists); | |||
return exists | |||
? new Wire(wire.sourceBlockEGID, end.Id, wire.sourcePortUsage, endPort, wire.ID, false) | |||
var wire = signalEngine.MatchPortToWire(port, end.Id, out var egid); | |||
return wire | |||
? new Wire(wire.Get().sourceBlockEGID, end.Id, wire.Get().sourcePortUsage, endPort, egid, false) | |||
: null; | |||
} | |||
@@ -62,9 +63,9 @@ namespace TechbloxModdingAPI.Blocks | |||
{ | |||
var port = signalEngine.MatchBlockIOToPort(start, startPort, true); | |||
if (!port) return null; | |||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out var exists); | |||
return exists | |||
? new Wire(start.Id, wire.destinationBlockEGID, startPort, wire.destinationPortUsage, wire.ID, false) | |||
var wire = signalEngine.MatchPortToWire(port, start.Id, out var egid); | |||
return wire | |||
? new Wire(start.Id, wire.Get().destinationBlockEGID, startPort, wire.Get().destinationPortUsage, egid, false) | |||
: null; | |||
} | |||
@@ -132,9 +133,9 @@ namespace TechbloxModdingAPI.Blocks | |||
this.endBlockEGID = endBlock; | |||
this.inputToOutput = inputToOutput; | |||
this.wireEGID = wire; | |||
endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).Nullable()?.ID ?? default; | |||
endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).EGID; | |||
if (endPortEGID == default) throw new WireInvalidException("Wire end port not found"); | |||
startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).Nullable()?.ID ?? default; | |||
startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).EGID; | |||
if (startPortEGID == default) throw new WireInvalidException("Wire start port not found"); | |||
this.startPort = startPort; | |||
this.endPort = endPort; | |||
@@ -151,8 +152,8 @@ namespace TechbloxModdingAPI.Blocks | |||
wireEgid, false); | |||
} | |||
private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest) | |||
: this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wire.ID, false) | |||
private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest, EGID wireEgid) | |||
: this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wireEgid, false) | |||
{ | |||
} | |||
@@ -1,6 +1,5 @@ | |||
using System; | |||
using Gamecraft.Wires; | |||
using RobocraftX.Blocks.Ghost; | |||
using RobocraftX.Character; | |||
using RobocraftX.Character.Movement; | |||
using Unity.Mathematics; | |||
@@ -11,6 +10,7 @@ using RobocraftX.Physics; | |||
using Svelto.ECS; | |||
using Techblox.BuildingDrone; | |||
using Techblox.Camera; | |||
using Techblox.Character; | |||
using TechbloxModdingAPI.Blocks; | |||
using TechbloxModdingAPI.Players; | |||
using TechbloxModdingAPI.Utility; | |||
@@ -214,15 +214,16 @@ namespace TechbloxModdingAPI | |||
/// The player's initial health when entering Simulation (aka Time Running) mode. | |||
/// </summary> | |||
/// <value>The initial health.</value> | |||
[Obsolete("We can no longer get initial health, returns max health.")] | |||
public float InitialHealth | |||
{ | |||
get | |||
{ | |||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id); | |||
return opt ? opt.Get().initialHealth : -1f; | |||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id); | |||
return opt ? opt.Get().maxHealth : -1f; | |||
} | |||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().initialHealth = value; | |||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().maxHealth = value; | |||
} | |||
/// <summary> | |||
@@ -233,30 +234,25 @@ namespace TechbloxModdingAPI | |||
{ | |||
get | |||
{ | |||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id); | |||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id); | |||
return opt ? opt.Get().currentHealth : -1f; | |||
} | |||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().currentHealth = value; | |||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().currentHealth = value; | |||
} | |||
/// <summary> | |||
/// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable. | |||
/// </summary> | |||
/// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value> | |||
[Obsolete("Players are probably always damageable")] | |||
public bool Damageable | |||
{ | |||
get | |||
{ | |||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id); | |||
return opt.Get().canTakeDamageStat; | |||
} | |||
get => true; | |||
// ReSharper disable once ValueParameterNotUsed | |||
set | |||
{ | |||
ref var healthStruct = ref playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get(); | |||
healthStruct.canTakeDamage = value; | |||
healthStruct.canTakeDamageStat = value; | |||
} | |||
} | |||
@@ -264,30 +260,26 @@ namespace TechbloxModdingAPI | |||
/// The player's lives when initially entering Simulation (aka Time Running) mode. | |||
/// </summary> | |||
/// <value>The initial lives.</value> | |||
[Obsolete("The player has infinite lives")] | |||
public uint InitialLives | |||
{ | |||
get | |||
{ | |||
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id); | |||
return opt ? opt.Get().initialLives : uint.MaxValue; | |||
} | |||
get => uint.MaxValue; | |||
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().initialLives = value; | |||
// ReSharper disable once ValueParameterNotUsed | |||
set { } | |||
} | |||
/// <summary> | |||
/// The player's current lives in Simulation (aka Time Running) mode. | |||
/// </summary> | |||
/// <value>The current lives.</value> | |||
[Obsolete("The player has infinite lives")] | |||
public uint CurrentLives | |||
{ | |||
get | |||
{ | |||
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id); | |||
return opt ? opt.Get().currentLives : uint.MaxValue; | |||
} | |||
get => uint.MaxValue; | |||
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().currentLives = value; | |||
// ReSharper disable once ValueParameterNotUsed | |||
set { } | |||
} | |||
/*/// <summary> | |||
@@ -373,6 +365,9 @@ namespace TechbloxModdingAPI | |||
var group when group == CharacterExclusiveGroups.MachineSpawningGroup => PlayerState.HoldingMachine, | |||
var group when group == CharacterExclusiveGroups.OnFootGroup => PlayerState.OnFoot, | |||
var group when group == CharacterExclusiveGroups.InPilotSeatGroup => PlayerState.InSeat, | |||
var group when group == CharacterExclusiveGroups.DyingOnFootGroup => PlayerState.OnFoot, | |||
var group when group == CharacterExclusiveGroups.DyingInPilotSeatGroup => PlayerState.InSeat, | |||
var group when group == CharacterExclusiveGroups.DeadGroup => PlayerState.OnFoot, | |||
_ => throw new ArgumentOutOfRangeException("", "Unknown player state") | |||
}; | |||
@@ -21,6 +21,7 @@ using Techblox.Character; | |||
using TechbloxModdingAPI.Engines; | |||
using TechbloxModdingAPI.Input; | |||
using TechbloxModdingAPI.Utility; | |||
using TechbloxModdingAPI.Utility.ECS; | |||
namespace TechbloxModdingAPI.Players | |||
{ | |||
@@ -115,7 +116,7 @@ namespace TechbloxModdingAPI.Players | |||
public bool IsDead(uint playerId) | |||
{ | |||
if (entitiesDB == null) return true; | |||
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters); | |||
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadGroup); | |||
} | |||
// reusable methods | |||
@@ -164,8 +165,9 @@ namespace TechbloxModdingAPI.Players | |||
var opt = GetCameraStruct<PhysicCameraRayCastEntityStruct>(playerId); | |||
if (!opt) return default; | |||
PhysicCameraRayCastEntityStruct rayCast = opt; | |||
EGID physicCameraEgid = new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup); | |||
float distance = maxDistance < 0 | |||
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, | |||
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, physicCameraEgid, | |||
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize) | |||
: maxDistance; | |||
if (rayCast.hit && rayCast.distance <= distance) | |||
@@ -368,8 +368,6 @@ namespace TechbloxModdingAPI.Tests | |||
}, () => shouldTestGhostBlock)); | |||
Logging.CommandLog("Test enabled"); | |||
}).Build(); | |||
Client.EnterMenu += (sender, args) => Scheduler.Schedule(new Once(() => Client.Instance.CloseBetaPopup())); | |||
Game.Enter += (sender, args) => | |||
Console.WriteLine( | |||
@@ -1,7 +1,7 @@ | |||
using Svelto.ECS; | |||
using Svelto.ECS.Hybrid; | |||
namespace TechbloxModdingAPI.Utility | |||
namespace TechbloxModdingAPI.Utility.ECS | |||
{ | |||
public static class ManagedApiExtensions | |||
{ | |||
@@ -18,7 +18,7 @@ namespace TechbloxModdingAPI.Utility | |||
{ | |||
return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array) | |||
? new OptionalRef<T>(array, index) | |||
: new OptionalRef<T>(); | |||
: new OptionalRef<T>(); | |||
} | |||
/// <summary> | |||
@@ -29,7 +29,8 @@ namespace TechbloxModdingAPI.Utility | |||
/// <param name="group">The group of the entity if the object can have multiple</param> | |||
/// <typeparam name="T">The component to query</typeparam> | |||
/// <returns>A reference to the component or a dummy value</returns> | |||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) | |||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, | |||
ExclusiveGroupStruct group = default) | |||
where T : struct, IEntityViewComponent | |||
{ | |||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); | |||
@@ -45,7 +46,8 @@ namespace TechbloxModdingAPI.Utility | |||
/// <param name="group">The group of the entity if the object can have multiple</param> | |||
/// <typeparam name="T">The component to query</typeparam> | |||
/// <returns>A reference to the component or a dummy value</returns> | |||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) | |||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, | |||
ExclusiveGroupStruct group = default) | |||
where T : struct, IEntityViewComponent | |||
{ | |||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); | |||
@@ -54,5 +56,20 @@ namespace TechbloxModdingAPI.Utility | |||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd<T>(); | |||
return ref opt.Get(); //Default value | |||
} | |||
/// <summary> | |||
/// Query entities as OptionalRefs. The elements always exist, it's just a nice way to encapsulate the data. | |||
/// </summary> | |||
/// <param name="entitiesDB"></param> | |||
/// <param name="group"></param> | |||
/// <param name="select"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <typeparam name="TR"></typeparam> | |||
/// <returns></returns> | |||
public static RefCollection<T> QueryEntitiesOptional<T>(this EntitiesDB entitiesDB, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent | |||
{ | |||
var (buffer, ids, count) = entitiesDB.QueryEntities<T>(group); | |||
return new RefCollection<T>(count, buffer, ids, group); | |||
} | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
using System; | |||
using System.Diagnostics.CodeAnalysis; | |||
using System.Linq; | |||
using System.Reflection; | |||
using HarmonyLib; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS; | |||
namespace TechbloxModdingAPI.Utility.ECS | |||
{ | |||
public static partial class NativeApiExtensions | |||
{ | |||
[SuppressMessage("ReSharper", "StaticMemberInGenericType")] | |||
private static class EntitiesDBHelper<T> where T : unmanaged, IEntityComponent | |||
{ // Each type gets a new set of fields here (that's what the ReSharper warning is about too) | |||
public static readonly Lazy<MethodInfo> EntityStream = | |||
new(() => AccessTools.PropertyGetter(typeof(EntitiesDB), "_entityStream")); | |||
public static readonly Lazy<FieldInfo> Streams = new(() => | |||
AccessTools.Field(EntityStream.Value.ReturnType, "_streams")); | |||
public static readonly Lazy<FieldInfo> Consumers = new(() => | |||
AccessTools.Field(typeof(EntityStream<T>), "_consumers")); | |||
public static readonly Lazy<MethodInfo> TryGetValue = | |||
new(AccessTools.Method(Streams.Value.FieldType, "TryGetValue")); | |||
public static readonly Lazy<FieldInfo> RingBuffer = | |||
new(() => AccessTools.Field(typeof(Consumer<T>), "_ringBuffer")); | |||
} | |||
private static EntityStream<T> GetEntityStream<T>(this EntitiesDB entitiesDB) where T : unmanaged, IEntityComponent | |||
{ | |||
// EntitiesStreams (internal) | |||
var entitiesStreams = EntitiesDBHelper<T>.EntityStream.Value.Invoke(entitiesDB, Array.Empty<object>()); | |||
// FasterDictionary<RefWrapperType, ITypeSafeStream> (interface is internal) | |||
var streams = EntitiesDBHelper<T>.Streams.Value.GetValue(entitiesStreams); | |||
var parameters = new object[] { TypeRefWrapper<T>.wrapper, null }; | |||
var success = EntitiesDBHelper<T>.TryGetValue.Value.Invoke(streams, parameters); | |||
if (!(bool)success) | |||
return null; // There is no entity stream for this type | |||
return (EntityStream<T>)parameters[1]; | |||
} | |||
private static ThreadSafeFasterList<Consumer<T>> GetConsumers<T>(this EntityStream<T> stream) where T : unmanaged, IEntityComponent | |||
{ | |||
return (ThreadSafeFasterList<Consumer<T>>)EntitiesDBHelper<T>.Consumers.Value.GetValue(stream); | |||
} | |||
private static RingBuffer<(T, EGID)> GetRingBuffer<T>(this Consumer<T> consumer) where T : unmanaged, IEntityComponent | |||
{ | |||
return (RingBuffer<(T, EGID)>)EntitiesDBHelper<T>.RingBuffer.Value.GetValue(consumer); | |||
} | |||
} | |||
} |
@@ -1,13 +1,14 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS; | |||
using Svelto.Tasks; | |||
using Svelto.Tasks.Lean; | |||
using TechbloxModdingAPI.Tasks; | |||
namespace TechbloxModdingAPI.Utility | |||
namespace TechbloxModdingAPI.Utility.ECS | |||
{ | |||
public static class NativeApiExtensions | |||
public static partial class NativeApiExtensions | |||
{ | |||
/// <summary> | |||
/// Attempts to query an entity and returns an optional that contains the result if succeeded. | |||
@@ -22,7 +23,7 @@ namespace TechbloxModdingAPI.Utility | |||
{ | |||
return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array) | |||
? new OptionalRef<T>(array, index) | |||
: new OptionalRef<T>(); | |||
: new OptionalRef<T>(); | |||
} | |||
/// <summary> | |||
@@ -33,7 +34,8 @@ namespace TechbloxModdingAPI.Utility | |||
/// <param name="group">The group of the entity if the object can have multiple</param> | |||
/// <typeparam name="T">The component to query</typeparam> | |||
/// <returns>A reference to the component or a dummy value</returns> | |||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) | |||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, | |||
ExclusiveGroupStruct group = default) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); | |||
@@ -49,7 +51,8 @@ namespace TechbloxModdingAPI.Utility | |||
/// <param name="group">The group of the entity if the object can have multiple</param> | |||
/// <typeparam name="T">The component to query</typeparam> | |||
/// <returns>A reference to the component or a dummy value</returns> | |||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) | |||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, | |||
ExclusiveGroupStruct group = default) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); | |||
@@ -64,39 +67,63 @@ namespace TechbloxModdingAPI.Utility | |||
return ref opt.Get(); //Default value | |||
} | |||
private static readonly Dictionary<Type, (int PublishedCount, HashSet<EGID> Changes)> ChangesToPublish = new(); | |||
/// <summary> | |||
/// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary. | |||
/// It will only publish in the next frame. | |||
/// </summary> | |||
/// <param name="entitiesDB">The entities DB to publish to</param> | |||
/// <param name="id">The ECS object that got changed</param> | |||
/// <param name="limit">Limits how many changes to publish - should be no more than the consumers' capacity that process this component</param> | |||
/// <typeparam name="T">The component that changed</typeparam> | |||
public static void PublishEntityChangeDelayed<T>(this EntitiesDB entitiesDB, EGID id, int limit = 80) | |||
public static void PublishEntityChangeDelayed<T>(this EntitiesDB entitiesDB, EGID id) | |||
where T : unmanaged, IEntityComponent | |||
{ //TODO: Doesn't seem to help | |||
if(!ChangesToPublish.ContainsKey(typeof(T))) | |||
ChangesToPublish.Add(typeof(T), (0, new HashSet<EGID>())); | |||
var changes = ChangesToPublish[typeof(T)].Changes; | |||
if (changes.Contains(id)) return; | |||
changes.Add(id); | |||
PublishChanges<T>(entitiesDB, id, limit).RunOn(Scheduler.leanRunner); | |||
{ | |||
PublishChanges<T>(entitiesDB, id).RunOn(Scheduler.leanRunner); | |||
} | |||
private static IEnumerator<TaskContract> PublishChanges<T>(EntitiesDB entitiesDB, EGID id, int limit) | |||
private static IEnumerator<TaskContract> PublishChanges<T>(EntitiesDB entitiesDB, EGID id) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
yield return Yield.It; | |||
while (ChangesToPublish[typeof(T)].PublishedCount >= limit) | |||
yield return Yield.It; | |||
var entityStream = entitiesDB.GetEntityStream<T>(); | |||
if (entityStream is null) | |||
yield break; // There is no entity stream for this type | |||
var consumers = entityStream.GetConsumers(); | |||
if (consumers == null) | |||
{ | |||
Console.WriteLine("Consumers is null"); | |||
yield break; | |||
} | |||
bool waitForConsumers; | |||
do | |||
{ | |||
waitForConsumers = false; | |||
for (int i = 0; i < consumers.count; i++) | |||
{ | |||
var buffer = consumers[i].GetRingBuffer(); | |||
if (buffer.Count + 1 <= buffer.Capacity) continue; | |||
waitForConsumers = true; | |||
break; | |||
} | |||
if (waitForConsumers) yield return Yield.It; | |||
} while (waitForConsumers); | |||
entitiesDB.PublishEntityChange<T>(id); | |||
var (count, changes) = ChangesToPublish[typeof(T)]; | |||
changes.Remove(id); | |||
ChangesToPublish[typeof(T)] = (count + 1, changes); | |||
yield return Yield.It; | |||
ChangesToPublish[typeof(T)] = (0, changes); | |||
} | |||
/// <summary> | |||
/// Query entities as OptionalRefs. The elements always exist, it's just a nice way to encapsulate the data. | |||
/// </summary> | |||
/// <param name="entitiesDB"></param> | |||
/// <param name="group"></param> | |||
/// <param name="select"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <typeparam name="TR"></typeparam> | |||
/// <returns></returns> | |||
public static RefCollection<T> QueryEntitiesOptional<T>(this EntitiesDB entitiesDB, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent | |||
{ | |||
var (buffer, ids, count) = entitiesDB.QueryEntities<T>(group); | |||
return new RefCollection<T>(count, buffer, ids, group); | |||
} | |||
} | |||
} |
@@ -4,8 +4,9 @@ using Svelto.ECS; | |||
namespace TechbloxModdingAPI.Utility | |||
{ | |||
public ref struct OptionalRef<T> where T : struct, IEntityComponent | |||
public ref struct OptionalRef<T> where T : struct, IBaseEntityComponent | |||
{ | |||
private readonly EGID entityId; | |||
private readonly State state; | |||
private readonly uint index; | |||
private NB<T> array; | |||
@@ -13,19 +14,22 @@ namespace TechbloxModdingAPI.Utility | |||
private readonly EntityInitializer initializer; | |||
//The possible fields are: (index && (array || managedArray)) || initializer | |||
public OptionalRef(NB<T> array, uint index) | |||
public OptionalRef(NB<T> array, uint index, EGID entityId = default) | |||
{ | |||
state = State.Native; | |||
this.array = array; | |||
this.index = index; | |||
this.entityId = entityId; | |||
initializer = default; | |||
managedArray = default; | |||
} | |||
public OptionalRef(MB<T> array, uint index) | |||
public OptionalRef(MB<T> array, uint index, EGID entityId = default) | |||
{ | |||
state = State.Managed; | |||
managedArray = array; | |||
this.index = index; | |||
this.entityId = entityId; | |||
initializer = default; | |||
this.array = default; | |||
} | |||
@@ -35,8 +39,9 @@ namespace TechbloxModdingAPI.Utility | |||
/// </summary> | |||
/// <param name="obj">The object with the initializer</param> | |||
/// <param name="unmanaged">Whether the struct is unmanaged</param> | |||
public OptionalRef(EcsObjectBase obj, bool unmanaged) | |||
public OptionalRef(EcsObjectBase obj, bool unmanaged, EGID entityId = default) | |||
{ | |||
this.entityId = entityId; | |||
if (obj.InitData.Valid) | |||
{ | |||
initializer = obj.InitData.Initializer(obj.Id); | |||
@@ -49,6 +54,7 @@ namespace TechbloxModdingAPI.Utility | |||
} | |||
array = default; | |||
index = default; | |||
managedArray = default; | |||
} | |||
/// <summary> | |||
@@ -73,6 +79,11 @@ namespace TechbloxModdingAPI.Utility | |||
set => Get() = value; | |||
} | |||
/// <summary> | |||
/// The ID of the entity this component belongs to or default if it doesn't exist. | |||
/// </summary> | |||
public EGID EGID => entityId; | |||
public bool Exists => state != State.Empty; | |||
public T? Nullable() => this ? Get() : default; | |||
@@ -80,6 +91,8 @@ namespace TechbloxModdingAPI.Utility | |||
public static implicit operator bool(OptionalRef<T> opt) => opt.state != State.Empty; | |||
public static implicit operator EGID(OptionalRef<T> opt) => opt.entityId; | |||
/// <summary> | |||
/// Creates an instance of a struct T that can be referenced. | |||
/// </summary> | |||
@@ -0,0 +1,94 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
namespace TechbloxModdingAPI.Utility | |||
{ | |||
public readonly ref struct RefCollection<T> where T : struct, IBaseEntityComponent | |||
{ | |||
private readonly bool managed; | |||
private readonly int count; | |||
private readonly NB<T> nativeArray; | |||
private readonly MB<T> managedArray; | |||
private readonly NativeEntityIDs nativeIDs; | |||
private readonly ManagedEntityIDs managedIDs; | |||
private readonly ExclusiveGroupStruct group; | |||
public RefCollection(int count, MB<T> managedArray, ManagedEntityIDs managedIDs, ExclusiveGroupStruct group) | |||
{ | |||
this.count = count; | |||
this.managedArray = managedArray; | |||
this.managedIDs = managedIDs; | |||
this.group = group; | |||
managed = true; | |||
nativeArray = default; | |||
nativeIDs = default; | |||
} | |||
public RefCollection(int count, NB<T> nativeArray, NativeEntityIDs nativeIDs, ExclusiveGroupStruct group) | |||
{ | |||
this.count = count; | |||
this.nativeArray = nativeArray; | |||
this.nativeIDs = nativeIDs; | |||
this.group = group; | |||
managed = false; | |||
managedArray = default; | |||
managedIDs = default; | |||
} | |||
public Enumerator GetEnumerator() => new(this); | |||
/// <summary> | |||
/// The amount of items in the collection. | |||
/// </summary> | |||
public int Count => count; | |||
public T[] ToArray() => ToArray(a => a.Component); | |||
public TA[] ToArray<TA>(Func<(T Component, EGID ID), TA> transformFunction, Predicate<(T Component, EGID ID)> predicateFunction = null) | |||
{ | |||
var result = new TA[Count]; | |||
int i = 0; | |||
foreach (var opt in this) | |||
{ | |||
if (predicateFunction != null && !predicateFunction((opt.Get(), opt.EGID))) continue; | |||
result[i] = transformFunction((opt.Get(), opt.EGID)); | |||
i++; | |||
} | |||
return result; | |||
} | |||
public ref struct Enumerator | |||
{ | |||
private RefCollection<T> coll; | |||
private int index; | |||
public Enumerator(RefCollection<T> collection) | |||
{ | |||
index = -1; | |||
coll = collection; | |||
} | |||
public OptionalRef<T> Current | |||
{ | |||
get | |||
{ | |||
if (coll.count <= index && index >= 0) return default; | |||
if (coll.managed) | |||
return new OptionalRef<T>(coll.managedArray, (uint)index, | |||
new EGID(coll.managedIDs[index], coll.group)); | |||
return new OptionalRef<T>(coll.nativeArray, (uint)index, | |||
new EGID(coll.nativeIDs[index], coll.group)); | |||
} | |||
} | |||
public bool MoveNext() | |||
{ | |||
return ++index < coll.count; | |||
} | |||
} | |||
} | |||
} |