Compare commits

...

11 commits

Author SHA1 Message Date
5dff88d703
Switch from IPA to BepInEx
- Removed a bunch of test code
- Preparing for 3.0
2023-08-22 00:02:26 +02:00
a8a451f8e4
Merge TB update feature branch 2023-03-30 01:22:10 +02:00
67f32b8810
Improved and fixed publish queue detection and block test
- Made the PublishEntityChangesDelayed() method use the internals of Svelto.ECS to determine if it should wait
-- I had this code for a while but it used too much reflection to my liking
-- Now I made the reflection code nicer and a bit safer
- Fixed float comparisons: last time I didn't actually used abs() for float3 but I found this even better method
2023-03-30 01:17:31 +02:00
b3b1e9b9e7
Update reference paths to allow for RC2 dev as well 2022-10-18 20:19:41 +02:00
e0cd7f6aec
Fix assembly editing and add more of it
- It breaks the game atm, not sure why exactly but it's probably not a good thing
2022-10-05 01:53:54 +02:00
23439abde3
Add new blocks and materials, make every type public in the game, fix entity publish
- Probably should've committed more
- Added new block IDs and a material (also fixed material names)
- Added missing player states
- Added a class to make every type public in the game's code, so we don't need to worry about internal components
- We don't need to worry about anticheat so it should be fine - will need to be made into its own exe though
- Fixed delayed entity publishing and set the limits based on the consumers (just 30 almost everywhere)
2022-10-04 01:47:09 +02:00
5e90c5ee26
Fix all compiler issues and add Count property and smart ToArray() function to RefCollection
- Collections can be converted into arrays using a mapper and a predicate function
2022-10-02 01:34:51 +02:00
5117b69500
Fix RefCollection and start using it to query multiple users
- I overcomplicated in the beginning
- It doesn't shorten the code that much but it provides a stable interface and it's easier to use so I guess it's nice
2022-09-29 01:26:51 +02:00
f70b65e796
Start updating to Techblox 2022.08.11.09.42 and start work on RefCollection
What have I got myself into
2022-09-29 00:29:12 +02:00
55344d1352
Start updating to Techblox 2022.05.25.11.05
Resolved compiler errors
Mostly by removing erroring code
2022-06-01 16:54:17 +02:00
dfe1bfb504
Begin updating to Techblox 2022.04.28.14.02
Updated project generator script to always order assemblies (it didn't do that for me on Linux) and to fix minor issues
2022-04-29 02:07:46 +02:00
38 changed files with 3920 additions and 2276 deletions

View file

@ -5,7 +5,7 @@ from pathlib import Path, PurePath
import re import re
import os import os
DLL_EXCLUSIONS_REGEX = r"(System|Microsoft|Mono|IronPython|DiscordRPC)\." DLL_EXCLUSIONS_REGEX = r"(System|Microsoft|Mono|IronPython|DiscordRPC|IllusionInjector|IllusionPlugin|netstandard)\."
def getAssemblyReferences(path): def getAssemblyReferences(path):
asmDir = Path(path) asmDir = Path(path)
@ -15,10 +15,12 @@ def getAssemblyReferences(path):
addedPath = "../" addedPath = "../"
asmDir = Path(addedPath + path) asmDir = Path(addedPath + path)
for child in asmDir.iterdir(): for child in asmDir.iterdir():
if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child), re.I) is None and str(child).lower().endswith(".dll"): if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child)) is None and str(child).lower().endswith(".dll"):
childstr = str(child) childstr = str(child)
childstr = os.path.relpath(childstr, addedPath).replace("\\", "/") childstr = os.path.relpath(childstr, addedPath).replace("\\", "/")
result.append(childstr) result.append(childstr)
result.sort(key=str.lower)
result = [path + "/IllusionInjector.dll", path + "/IllusionPlugin.dll"] + result # Always put it on top
return result return result
def buildReferencesXml(path): def buildReferencesXml(path):
@ -39,7 +41,7 @@ if __name__ == "__main__":
args = parser.parse_args() args = parser.parse_args()
print("Building Assembly references") print("Building Assembly references")
asmXml = buildReferencesXml("../ref/Techblox_Data/Managed") asmXml = buildReferencesXml("../ref_TB/Techblox_Data/Managed")
# print(asmXml) # print(asmXml)
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile: with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
@ -51,7 +53,7 @@ if __name__ == "__main__":
if depsStart is None or depsEnd is None: if depsStart is None or depsEnd is None:
print("Unable to find dependency XML comments, aborting!") print("Unable to find dependency XML comments, aborting!")
exit(1) exit(1)
newFileStr = fileStr[:depsStart.start()] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:] newFileStr = fileStr[:depsStart.start() - 1] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile: with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
print("Writing Assembly references") print("Writing Assembly references")
xmlFile.write(newFileStr) xmlFile.write(newFileStr)

View file

@ -67,7 +67,7 @@ namespace CodeGenerator
codeUnit.Namespaces.Add(ns); codeUnit.Namespaces.Add(ns);
var provider = CodeDomProvider.CreateProvider("CSharp"); var provider = CodeDomProvider.CreateProvider("CSharp");
var path = $@"..\..\..\..\TechbloxModdingAPI\Blocks\{name}.cs"; var path = $@"../../../../TechbloxModdingAPI/Blocks/{name}.cs";
using (var sw = new StreamWriter(path)) using (var sw = new StreamWriter(path))
{ {
provider.GenerateCodeFromCompileUnit(codeUnit, sw, new CodeGeneratorOptions {BracingStyle = "C"}); provider.GenerateCodeFromCompileUnit(codeUnit, sw, new CodeGeneratorOptions {BracingStyle = "C"});

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using HarmonyLib; using HarmonyLib;
using RobocraftX.Blocks; using RobocraftX.Blocks;
using RobocraftX.Common;
using RobocraftX.GroupTags;
using RobocraftX.PilotSeat; using RobocraftX.PilotSeat;
using Svelto.ECS;
using Techblox.EngineBlock; using Techblox.EngineBlock;
using Techblox.ObjectIDBlockServer; using Techblox.ServoBlocksServer;
using Techblox.WheelRigBlock; using Techblox.WheelRigBlock;
namespace CodeGenerator namespace CodeGenerator
@ -11,6 +14,11 @@ namespace CodeGenerator
internal class Program internal class Program
{ {
public static void Main(string[] args) public static void Main(string[] args)
{
GenerateBlockClasses();
}
private static void GenerateBlockClasses()
{ {
var bcg = new BlockClassGenerator(); var bcg = new BlockClassGenerator();
bcg.Generate("Engine", null, new Dictionary<string, string> bcg.Generate("Engine", null, new Dictionary<string, string>
@ -24,7 +32,7 @@ namespace CodeGenerator
}, },
typeof(TweakableJointDampingComponent), typeof(DampedSpringReadOnlyStruct)); typeof(TweakableJointDampingComponent), typeof(DampedSpringReadOnlyStruct));
bcg.Generate("LogicGate", "LOGIC_BLOCK_GROUP"); bcg.Generate("LogicGate", "LOGIC_BLOCK_GROUP");
bcg.Generate("Servo", types: typeof(ServoReadOnlyStruct), renames: new Dictionary<string, string> bcg.Generate("Servo", types: typeof(ServoReadOnlyTweakableComponent), renames: new Dictionary<string, string>
{ {
{"minDeviation", "MinimumAngle"}, {"minDeviation", "MinimumAngle"},
{"maxDeviation", "MaximumAngle"}, {"maxDeviation", "MaximumAngle"},
@ -39,7 +47,7 @@ namespace CodeGenerator
{"pistonVelocity", "MaximumForce"} {"pistonVelocity", "MaximumForce"}
}, typeof(PistonReadOnlyStruct)); }, typeof(PistonReadOnlyStruct));
bcg.Generate("Motor", null, null, typeof(MotorReadOnlyStruct)); bcg.Generate("Motor", null, null, typeof(MotorReadOnlyStruct));
bcg.Generate("ObjectID", "ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP", null, typeof(ObjectIDTweakableComponent)); //bcg.Generate("ObjectID", "ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP", null, typeof(ObjectIDTweakableComponent));
} }
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -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();
}

View file

@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechbloxModdingAPI", "Techb
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeEverythingPublicInGame", "MakeEverythingPublicInGame\MakeEverythingPublicInGame.csproj", "{391A3107-E5C6-4A04-9467-6D868AA9A8B4}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Debug|Any CPU
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Test|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -114,11 +114,6 @@ namespace TechbloxModdingAPI.App
popup.SecondButton(); popup.SecondButton();
} }
internal void CloseBetaPopup()
{
Game.menuEngine.CloseBetaPopup();
}
internal static void Init() internal static void Init()
{ {
// this would have been so much simpler if this didn't involve a bunch of internal fields & classes // this would have been so much simpler if this didn't involve a bunch of internal fields & classes

View file

@ -140,18 +140,19 @@ namespace TechbloxModdingAPI.App
{ {
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>(); var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
List<EGID> blockEGIDs = new List<EGID>(); 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++) for (int i = 0; i < count; i++)
{ {
var id = new EGID(ids[i], group);
uint dbid; uint dbid;
if (filter == BlockIDs.Invalid) if (filter == BlockIDs.Invalid)
dbid = (uint)filter; dbid = (uint)filter;
else else
dbid = entitiesDB.QueryEntity<DBEntityStruct>(buffer[i].ID).DBID; dbid = entitiesDB.QueryEntity<DBEntityStruct>(id).DBID;
var ownership = entitiesDB.QueryEntity<BlockOwnershipComponent>(buffer[i].ID).BlockOwnership; var ownership = entitiesDB.QueryEntity<BlockOwnershipComponent>(id).BlockOwnership;
if ((ownership & BlockOwnership.User) != 0 && dbid == (ulong)filter) if ((ownership & BlockOwnership.User) != 0 && dbid == (ulong)filter)
blockEGIDs.Add(buffer[i].ID); blockEGIDs.Add(id);
} }
} }

View file

@ -154,16 +154,6 @@ namespace TechbloxModdingAPI.App
{ {
return ref entitiesDB.QueryEntity<T>(id); 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> { } internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }

View file

@ -441,9 +441,10 @@ namespace TechbloxModdingAPI
public SimBody GetSimBody() public SimBody GetSimBody()
{ {
var st = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(this); var st = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(this);
return st.machineRigidBodyId != uint.MaxValue /*return st.machineRigidBodyId != uint.MaxValue
? new SimBody(st.machineRigidBodyId, st.clusterId) ? new SimBody(st.machineRigidBodyId, st.clusterId) - TODO:
: null; : null;*/
return null;
} }
/// <summary> /// <summary>

View file

@ -302,6 +302,7 @@ namespace TechbloxModdingAPI.Blocks
FloodLight, FloodLight,
SoccerBall, SoccerBall,
CircularWallLight, CircularWallLight,
BlueSkyAtmos,
DirtToGrassTransitionTile = 393, DirtToGrassTransitionTile = 393,
DirtToGrassTransitionInnerTile, DirtToGrassTransitionInnerTile,
DirtToGrassTransitionOuterTile, DirtToGrassTransitionOuterTile,
@ -333,13 +334,50 @@ namespace TechbloxModdingAPI.Blocks
DistanceSensor, DistanceSensor,
Stabilizer, Stabilizer,
ObjectID, ObjectID,
TeamScore = 428, ScoreToTechpointConversion,
TeamScore,
ScorePickupBlock, ScorePickupBlock,
StreetLamp = 435, SportyHatchbackDriverSeat,
ConstantBlock = 452, SportyHatchbackPassengerSeat,
FlamingExhaust = 433,
SmokingExhaust,
StreetLamp,
Vector7HatchbackWheel,
Vector7HatchbackWheelWideProfile,
Vector7SedanWheel,
Vector7SedanWideProfile,
Vector7FormulaWheel,
Vector7FormulaWheelRear,
Vector7MonsterTruckWheel,
Vector7TruckWheel,
Vector7TruckWheelDouble,
BusSeat,
XLJet,
XXLJet,
ElectricSedanEngine,
HeadlampIndicator,
HeadlampSrip,
HeadlampStripEdge,
ConstantBlock,
CounterBlock, CounterBlock,
SmallGridHill, SmallGridHill,
SmallGridHillInnerCorner, SmallGridHillInnerCorner,
SmallGridHillOuterCorner SmallGridHillOuterCorner,
AimingAxleServo,
AimingHingeServo,
WeaponDisabler,
Vector7SmallJet,
Vector7MediumJet,
Vector7LargeJet,
Vector7XLJet,
Vector7XXLJet,
APCWheelRigNoSteering,
APCWheelRigWithSteering,
APCWheel,
APCSeat,
APCEngine,
DamageScoreBlock,
KillScoreBlock,
Autocannon = 480
} }
} }

View file

@ -32,6 +32,15 @@ namespace TechbloxModdingAPI.Blocks
WoodPainted, WoodPainted,
WoodRoughGrungy, WoodRoughGrungy,
Boundary, Boundary,
Emissive Emissive,
AircraftPanelingRivetedPainted,
AircraftPanelingRivetedMetallic,
SteelBodyworkPearlescent,
SteelBodyworkRadWrap,
SteelBodyworkGlitter,
BouncyRubber,
BouncyRubberTieDye,
BrickPainted,
FuturisticPanelingRivetedPainted,
} }
} }

View file

@ -5,7 +5,6 @@ using System.Reflection;
using DataLoader; using DataLoader;
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.Enumerators;
using Unity.Mathematics; using Unity.Mathematics;
using TechbloxModdingAPI.Tests; using TechbloxModdingAPI.Tests;
@ -80,35 +79,50 @@ namespace TechbloxModdingAPI.Blocks
yield break; yield break;
for (var index = 0; index < blocks.Length; index++) 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]; var block = blocks[index];
if (!block.Exists) continue; if (!block.Exists) continue;
foreach (var property in block.GetType().GetProperties()) foreach (var property in block.GetType().GetProperties())
{ {
//Includes specialised block properties //Includes specialised block properties
if (property.SetMethod == null) continue; 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) //(type, default value, predicate or null for equality)
(typeof(long), 3, null), (typeof(long), 3, null),
(typeof(int), 4, null), (typeof(int), 4, null),
(typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon), (typeof(double), 5.2f, t => Math.Abs((double) t.Value - (double) t.Default) < float.Epsilon),
(typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon), (typeof(float), 5.2f, t => Math.Abs((float) t.Value - (float) t.Default) < float.Epsilon),
(typeof(bool), true, obj => (bool) obj), (typeof(bool), true, t => (bool) t.Value),
(typeof(string), "Test", obj => (string) obj == "Test"), //String equality check (typeof(string), "Test", t => (string) t.Value == "Test"), //String equality check
(typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)), (typeof(float3), (float3) 20, t => math.all(Float3Compare((float3)t.Value, (float3)t.Default))),
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null), (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; var propType = property.PropertyType;
if (!propType.IsValueType) continue; 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) foreach (var (type, value, predicate) in testValues)
{ {
if (type.IsAssignableFrom(propType)) if (type.IsAssignableFrom(propType))
{ {
valueToUse = value; valueToUse = value;
predicateToUse = predicate ?? (obj => Equals(obj, value)); predicateToUse = predicate ?? (t => Equals(t.Value, t.Default));
break; break;
} }
} }
@ -117,7 +131,7 @@ namespace TechbloxModdingAPI.Blocks
{ {
var values = propType.GetEnumValues(); var values = propType.GetEnumValues();
valueToUse = values.GetValue(values.Length / 2); valueToUse = values.GetValue(values.Length / 2);
predicateToUse = val => Equals(val, valueToUse); predicateToUse = t => Equals(t.Value, t.Default);
} }
if (valueToUse == null) if (valueToUse == null)
@ -145,7 +159,7 @@ namespace TechbloxModdingAPI.Blocks
continue; continue;
} }
var attr = property.GetCustomAttribute<TestValueAttribute>(); 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}."); Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
yield break; yield break;

View file

@ -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. /// Gets or sets the Engine's On property. May not be saved.
/// </summary> /// </summary>
public bool On public bool On
{ {
get get
{ {
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "engineOn"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn;
} }
set 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 get
{ {
return ((int)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentGear"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "gearChangeCountdown"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmAV"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmLV"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmAV"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmLV"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentTorque"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityAV"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityLV"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV;
} }
set 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 get
{ {
return ((int)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelCount"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount;
} }
set 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 get
{ {
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearUpInput"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput;
} }
set 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 get
{ {
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearDownInput"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "manualToAutoGearCoolOffCounter"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter;
} }
set 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 get
{ {
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "load"))); return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load;
} }
set 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; BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
} }
} }*/
} }
} }

View file

@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using HarmonyLib; using HarmonyLib;
using Gamecraft.ColourPalette; using Gamecraft.ColourPalette;
using Gamecraft.TimeRunning;
using Gamecraft.Wires; using Gamecraft.Wires;
using RobocraftX.Blocks; using RobocraftX.Blocks;
using RobocraftX.Common; using RobocraftX.Common;
@ -23,6 +21,8 @@ using Unity.Mathematics;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
using PrefabsID = RobocraftX.Common.PrefabsID;
namespace TechbloxModdingAPI.Blocks.Engines namespace TechbloxModdingAPI.Blocks.Engines
{ {
@ -55,12 +55,13 @@ namespace TechbloxModdingAPI.Blocks.Engines
{ {
for(int i = 0; i < count; i++) for(int i = 0; i < count; i++)
{ {
ecoll[i].isProcessed = false; ecoll[i].areConnectionsAssigned = false;
} }
} }
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes, //TODO: GetConnectedCubesUtility
(in GridConnectionsEntityStruct _) => false); /*ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
(in GridConnectionsEntityStruct _) => false);*/
var ret = new Block[cubes.count]; var ret = new Block[cubes.count];
for (int i = 0; i < cubes.count; i++) for (int i = 0; i < cubes.count; i++)
@ -71,7 +72,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public float4 ConvertBlockColor(byte index) => index == byte.MaxValue public float4 ConvertBlockColor(byte index) => index == byte.MaxValue
? new float4(-1f, -1f, -1f, -1f) ? new float4(-1f, -1f, -1f, -1f)
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index, : 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 public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent
{ {
@ -161,7 +162,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public void UpdateBlockColor(EGID id) public void UpdateBlockColor(EGID id)
{ {
entitiesDB.PublishEntityChange<ColourParameterEntityStruct>(id); entitiesDB.PublishEntityChangeDelayed<ColourParameterEntityStruct>(id);
} }
public bool BlockExists(EGID blockID) public bool BlockExists(EGID blockID)
@ -172,23 +173,22 @@ namespace TechbloxModdingAPI.Blocks.Engines
public SimBody[] GetSimBodiesFromID(byte id) public SimBody[] GetSimBodiesFromID(byte id)
{ {
var ret = new FasterList<SimBody>(4); 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; EGIDMapper<GridConnectionsEntityStruct>? connections = null;
for (int i = 0; i < count; i++) foreach (var oid in oids)
{ {
if (oids[i].objectId != id) continue; if (oid.Get().objectId != id) continue;
var tag = tags[i];
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise 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; //var rid = connections.Value.Entity(tag.ID.entityID).machineRigidBodyId;
foreach (var rb in ret) /*foreach (var rb in ret) - TODO
{ {
if (rb.Id.entityID == rid) if (rb.Id.entityID == rid)
goto DUPLICATE; //Multiple Object Identifiers on one rigid body goto DUPLICATE; //Multiple Object Identifiers on one rigid body
} }
ret.Add(new SimBody(rid)); ret.Add(new SimBody(rid));
DUPLICATE: ; DUPLICATE: ;*/
} }
return ret.ToArray(); return ret.ToArray();
@ -202,8 +202,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
{ {
ref var joint = ref joints[i]; ref var joint = ref joints[i];
if (joint.isBroken) continue; if (joint.isBroken) continue;
if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); /*if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); - TODO:
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA)); else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));*/
} }
return list.ToArray(); return list.ToArray();
@ -218,8 +218,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
for (var index = 0; index < count; index++) for (var index = 0; index < count; index++)
{ {
var conn = coll[index]; var conn = coll[index];
if (conn.clusterId == cid) /*if (conn.clusterId == cid) - TODO
bodies.Add(conn.machineRigidBodyId); bodies.Add(conn.machineRigidBodyId);*/
} }
} }
@ -247,8 +247,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
{ {
var conn = coll[index]; var conn = coll[index];
//Static blocks don't have a cluster ID but the cluster destruction manager should have one //Static blocks don't have a cluster ID but the cluster destruction manager should have one
if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue) /*if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue) - TODO:
return new Cluster(conn.clusterId); return new Cluster(conn.clusterId);*/
} }
} }
@ -265,8 +265,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
for (var index = 0; index < count; index++) for (var index = 0; index < count; index++)
{ {
var conn = coll[index]; var conn = coll[index];
if (conn.machineRigidBodyId == sbid) /*if (conn.machineRigidBodyId == sbid) - TODO
set.Add(Block.New(tags[index].ID)); set.Add(Block.New(tags[index].ID));*/
} }
} }
@ -279,11 +279,11 @@ namespace TechbloxModdingAPI.Blocks.Engines
return Array.Empty<ObjectID>(); return Array.Empty<ObjectID>();
var ret = new FasterList<ObjectID>(4); var ret = new FasterList<ObjectID>(4);
var (oids, tags, count) = entitiesDB.QueryEntities<ObjectIDTweakableComponent, BlockTagEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP); var oids = entitiesDB.QueryEntitiesOptional<ObjectIDTweakableComponent>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
for (var index = 0; index < count; index++) foreach (var oid in oids)
{ {
if (oids[index].objectIDToTrigger == id) if (oid.Get().objectIDToTrigger == id)
ret.Add(new ObjectID(tags[index].ID)); ret.Add(new ObjectID(oid.EGID));
} }
return ret.ToArray(); return ret.ToArray();

View file

@ -18,9 +18,10 @@ using Svelto.ECS.DataStructures;
using Svelto.ECS.EntityStructs; using Svelto.ECS.EntityStructs;
using Svelto.ECS.Native; using Svelto.ECS.Native;
using Svelto.ECS.Serialization; using Svelto.ECS.Serialization;
using Techblox.Blocks; using Techblox.Blocks.Connections;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
using Unity.Collections; using Unity.Collections;
using Unity.Mathematics; using Unity.Mathematics;
using UnityEngine; using UnityEngine;
@ -48,7 +49,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
private static readonly MethodInfo SerializeGhostBlueprint = private static readonly MethodInfo SerializeGhostBlueprint =
AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities"); AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities");
private static NativeEntityRemove nativeRemove; private static NativeEntityRemove nativeBlockRemove;
private static NativeEntityRemove nativeConnectionRemove;
private static MachineGraphConnectionEntityFactory connectionFactory; private static MachineGraphConnectionEntityFactory connectionFactory;
private static IEntityFunctions entityFunctions; private static IEntityFunctions entityFunctions;
private static ClipboardSerializationDataResourceManager clipboardManager; private static ClipboardSerializationDataResourceManager clipboardManager;
@ -88,8 +90,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public void RemoveBlockGroup(int id) public void RemoveBlockGroup(int id)
{ {
BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeRemove, BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeBlockRemove,
connectionFactory, default).Complete(); nativeConnectionRemove, connectionFactory, default).Complete();
} }
public int CreateBlockGroup(float3 position, quaternion rotation) public int CreateBlockGroup(float3 position, quaternion rotation)
@ -339,7 +341,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public static void Prefix(IEntityFunctions entityFunctions, public static void Prefix(IEntityFunctions entityFunctions,
MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory) MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
{ {
nativeRemove = entityFunctions.ToNativeRemove<BlockEntityDescriptor>("GCAPI" + nameof(BlueprintEngine)); nativeBlockRemove = entityFunctions.ToNativeRemove<BlockEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
nativeConnectionRemove = entityFunctions.ToNativeRemove<MachineConnectionEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
connectionFactory = machineGraphConnectionEntityFactory; connectionFactory = machineGraphConnectionEntityFactory;
BlueprintEngine.entityFunctions = entityFunctions; BlueprintEngine.entityFunctions = entityFunctions;
} }

View file

@ -7,6 +7,7 @@ using Unity.Transforms;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
namespace TechbloxModdingAPI.Blocks.Engines namespace TechbloxModdingAPI.Blocks.Engines
{ {
@ -56,7 +57,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
}); });
} }
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).isProcessed = false; entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
return posStruct.position; return posStruct.position;
} }

View file

@ -16,6 +16,7 @@ using Unity.Mathematics;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
namespace TechbloxModdingAPI.Blocks.Engines namespace TechbloxModdingAPI.Blocks.Engines
{ {

View file

@ -1,47 +1,61 @@
using System.Reflection; using System.Reflection;
using Gamecraft.Blocks.BlockGroups;
using HarmonyLib; using HarmonyLib;
using RobocraftX.Blocks; using RobocraftX.Blocks;
using RobocraftX.Common; using RobocraftX.Common;
using Svelto.Common; using RobocraftX.GroupTags;
using RobocraftX.StateSync;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Native; using Svelto.ECS.Native;
using Techblox.Blocks.Connections;
using Unity.Collections;
using Unity.Jobs;
using Allocator = Unity.Collections.Allocator;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Blocks.Engines namespace TechbloxModdingAPI.Blocks.Engines
{ {
public class RemovalEngine : IApiEngine public class RemovalEngine : IApiEngine, IDeterministicTimeStopped
{ {
private static IEntityFunctions _entityFunctions; private static IEntityFunctions _entityFunctions;
private static MachineGraphConnectionEntityFactory _connectionFactory; private static MachineGraphConnectionEntityFactory _connectionFactory;
private NativeHashSet<ulong> removedConnections;
public bool RemoveBlock(EGID target) public bool RemoveBlock(EGID target)
{ {
if (!entitiesDB.Exists<MachineGraphConnectionsEntityStruct>(target)) if (!entitiesDB.Exists<MachineGraphConnectionsEntityStruct>(target))
return false; return false;
var connections = entitiesDB.QueryEntity<MachineGraphConnectionsEntityStruct>(target); using var connStructMapper =
var groups = entitiesDB.FindGroups<MachineGraphConnectionsEntityStruct>(); entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(GroupTag<BLOCK_TAG>.Groups,
using var connStructMapper = //The allocator needs to be persistent because that's what is used in the Dispose() method Svelto.Common.Allocator.Temp);
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(groups, Allocator.Persistent); if (entitiesDB.TryQueryNativeMappedEntities<MachineConnectionComponent>(
for (int i = connections.connections.Count<MachineConnectionStruct>() - 1; i >= 0; i--) ConnectionsExclusiveGroups.MACHINE_CONNECTION_GROUP, out var mapper))
_connectionFactory.RemoveConnection(connections, i, connStructMapper); {
BlockGroupUtility.RemoveBlockConnections(target, removedConnections, _connectionFactory,
connStructMapper, mapper, entitiesDB.GetEntityReferenceMap(), _entityFunctions);
}
_entityFunctions.RemoveEntity<BlockEntityDescriptor>(target); _entityFunctions.RemoveEntity<BlockEntityDescriptor>(target);
return true; return true;
} }
public void Ready() public void Ready()
{ {
removedConnections = new(2000, Allocator.Persistent);
} }
public EntitiesDB entitiesDB { get; set; } public EntitiesDB entitiesDB { get; set; }
public void Dispose() public void Dispose()
{ {
removedConnections.Dispose();
} }
public string Name { get; } = "TechbloxModdingAPIRemovalGameEngine"; public string Name => "TechbloxModdingAPIRemovalGameEngine";
public string name => Name;
public bool isRemovable => false; public bool isRemovable => false;
@ -61,5 +75,12 @@ namespace TechbloxModdingAPI.Blocks.Engines
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine").GetConstructors()[0]; return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine").GetConstructors()[0];
} }
} }
public JobHandle DeterministicStep(in float deltaTime, JobHandle inputDeps)
{
if (removedConnections.IsCreated)
removedConnections.Clear();
return default;
}
} }
} }

View file

@ -7,6 +7,7 @@ using UnityEngine;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
namespace TechbloxModdingAPI.Blocks.Engines namespace TechbloxModdingAPI.Blocks.Engines
{ {
@ -59,7 +60,9 @@ namespace TechbloxModdingAPI.Blocks.Engines
}); });
} }
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).isProcessed = false; // TODO: Connections probably need to be assigned (maybe)
// They are assigned during machine processing anyway
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
return ((Quaternion)rotStruct.rotation).eulerAngles; return ((Quaternion)rotStruct.rotation).eulerAngles;
} }

View file

@ -6,6 +6,7 @@ using Svelto.ECS;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
namespace TechbloxModdingAPI.Blocks.Engines namespace TechbloxModdingAPI.Blocks.Engines
{ {
@ -41,7 +42,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
// implementations for block wiring // 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); EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID); EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
@ -50,10 +51,9 @@ namespace TechbloxModdingAPI.Blocks.Engines
sourceBlockEGID = startBlock, sourceBlockEGID = startBlock,
sourcePortUsage = startPort, sourcePortUsage = startPort,
destinationBlockEGID = endBlock, destinationBlockEGID = endBlock,
destinationPortUsage = endPort, destinationPortUsage = endPort
ID = wireEGID
}); });
return wireInitializer.Get<WireEntityStruct>(); return (wireInitializer.Get<WireEntityStruct>(), wireEGID);
} }
public ref WireEntityStruct GetWire(EGID wire) public ref WireEntityStruct GetWire(EGID wire)
@ -226,27 +226,27 @@ namespace TechbloxModdingAPI.Blocks.Engines
uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i; uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i;
if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) || if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) ||
array[index].usage != portUsage) continue; array[index].usage != portUsage) continue;
return new OptionalRef<PortEntityStruct>(array, index); return new OptionalRef<PortEntityStruct>(array, index, new EGID(entityID, group));
} }
return default; 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++) for (uint i = 0; i < count; i++)
{ {
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID) if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID)) || (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
{ {
exists = true; wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group);
return ref wires[i]; return new OptionalRef<WireEntityStruct>(wires, i);
} }
} }
exists = false;
WireEntityStruct[] defRef = new WireEntityStruct[1]; wireID = default;
return ref defRef[0]; return default;
} }
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue) 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) }; 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++) for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
{ {
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]); PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++) for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
{ {
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[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) var wire = wireOpt.Get();
&& (wires[w].sourcePortUsage == startPES.usage && wires[w].sourceBlockEGID == startBlock)) 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() public EGID[] GetElectricBlocks()
{ {
var res = new FasterList<EGID>(); 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++) for (int i = 0; i < count; i++)
{ {
ref BlockPortsStruct s = ref coll[i]; ref BlockPortsStruct s = ref coll[i];
res.Add(s.ID); //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) public EGID[] WiredToInput(EGID block, byte port)
{ {
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group, return entitiesDB
(WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block); .QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
EGID[] result = new EGID[wireEntityStructs.Length]; .ToArray(wire => wire.ID,
for (uint i = 0; i < wireEntityStructs.Length; i++) wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block);
{
result[i] = wireEntityStructs[i].ID;
}
return result;
} }
public EGID[] WiredToOutput(EGID block, byte port) public EGID[] WiredToOutput(EGID block, byte port)
{ {
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group, return entitiesDB
(WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block); .QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
EGID[] result = new EGID[wireEntityStructs.Length]; .ToArray(wire => wire.ID,
for (uint i = 0; i < wireEntityStructs.Length; i++) wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block);
{
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();
} }
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true) private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
@ -367,7 +344,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group; : NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
if (entitiesDB.Exists<PortEntityStruct>(signalID, group)) if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
{ {
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex; index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).firstChannelIndexCachedInSim;
var channelData = var channelData =
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group); entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
return channelData; return channelData;

View file

@ -30,11 +30,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).servoVelocity; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).servoVelocity;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).servoVelocity = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).servoVelocity = value;
} }
} }
@ -45,11 +45,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).minDeviation; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).minDeviation;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).minDeviation = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).minDeviation = value;
} }
} }
@ -60,11 +60,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).maxDeviation; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).maxDeviation;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).maxDeviation = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).maxDeviation = value;
} }
} }
@ -75,11 +75,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).reverse; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).reverse;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).reverse = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).reverse = value;
} }
} }
@ -90,11 +90,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).hasProportionalInput; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).hasProportionalInput;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).hasProportionalInput = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).hasProportionalInput = value;
} }
} }
@ -105,11 +105,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).directionVector; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).directionVector;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).directionVector = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).directionVector = value;
} }
} }
@ -120,11 +120,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).rotationAxis; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).rotationAxis;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).rotationAxis = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).rotationAxis = value;
} }
} }
@ -135,11 +135,11 @@ namespace TechbloxModdingAPI.Blocks
{ {
get get
{ {
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).forceAxis; return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).forceAxis;
} }
set set
{ {
BlockEngine.GetBlockInfo<RobocraftX.Blocks.ServoReadOnlyStruct>(this).forceAxis = value; BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).forceAxis = value;
} }
} }
} }

View file

@ -46,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks
/// <returns>The connected wire.</returns> /// <returns>The connected wire.</returns>
/// <param name="portId">Port identifier.</param> /// <param name="portId">Port identifier.</param>
/// <param name="connected">Whether the port has a wire connected to it.</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> /// <summary>

View file

@ -5,6 +5,7 @@ using Svelto.ECS;
using Svelto.ECS.Experimental; using Svelto.ECS.Experimental;
using TechbloxModdingAPI.Blocks.Engines; using TechbloxModdingAPI.Blocks.Engines;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Blocks namespace TechbloxModdingAPI.Blocks
{ {
@ -30,8 +31,8 @@ namespace TechbloxModdingAPI.Blocks
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort) public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
{ {
WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort); var (wire, id) = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
return new Wire(wire, start, end); return new Wire(wire, start, end, id);
} }
/// <summary> /// <summary>
@ -45,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks
{ {
var port = signalEngine.MatchBlockIOToPort(end, endPort, false); var port = signalEngine.MatchBlockIOToPort(end, endPort, false);
if (!port) return null; if (!port) return null;
WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out var exists); var wire = signalEngine.MatchPortToWire(port, end.Id, out var egid);
return exists return wire
? new Wire(wire.sourceBlockEGID, end.Id, wire.sourcePortUsage, endPort, wire.ID, false) ? new Wire(wire.Get().sourceBlockEGID, end.Id, wire.Get().sourcePortUsage, endPort, egid, false)
: null; : null;
} }
@ -62,9 +63,9 @@ namespace TechbloxModdingAPI.Blocks
{ {
var port = signalEngine.MatchBlockIOToPort(start, startPort, true); var port = signalEngine.MatchBlockIOToPort(start, startPort, true);
if (!port) return null; if (!port) return null;
WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out var exists); var wire = signalEngine.MatchPortToWire(port, start.Id, out var egid);
return exists return wire
? new Wire(start.Id, wire.destinationBlockEGID, startPort, wire.destinationPortUsage, wire.ID, false) ? new Wire(start.Id, wire.Get().destinationBlockEGID, startPort, wire.Get().destinationPortUsage, egid, false)
: null; : null;
} }
@ -132,9 +133,9 @@ namespace TechbloxModdingAPI.Blocks
this.endBlockEGID = endBlock; this.endBlockEGID = endBlock;
this.inputToOutput = inputToOutput; this.inputToOutput = inputToOutput;
this.wireEGID = wire; 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"); 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"); if (startPortEGID == default) throw new WireInvalidException("Wire start port not found");
this.startPort = startPort; this.startPort = startPort;
this.endPort = endPort; this.endPort = endPort;
@ -151,8 +152,8 @@ namespace TechbloxModdingAPI.Blocks
wireEgid, false); wireEgid, false);
} }
private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest) private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest, EGID wireEgid)
: this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wire.ID, false) : this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wireEgid, false)
{ {
} }

View file

@ -1,7 +1,5 @@
using Gamecraft.Damage; using Svelto.ECS;
using RobocraftX.Common; using Techblox.TimeRunning.Clusters;
using Svelto.ECS;
using Techblox.Physics;
namespace TechbloxModdingAPI namespace TechbloxModdingAPI
{ {
@ -15,26 +13,26 @@ namespace TechbloxModdingAPI
{ {
} }
public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP)) public Cluster(uint id) : this(new EGID(id, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
{ {
} }
public float InitialHealth public float InitialHealth //TODO
{ {
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth; get => 0f;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth = value; set { }
} }
public float CurrentHealth public float CurrentHealth
{ {
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth; get => 0f;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth = value; set { }
} }
public float HealthMultiplier public float HealthMultiplier
{ {
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier; get => 0f;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier = value; set { }
} }
/// <summary> /// <summary>

View file

@ -104,7 +104,7 @@ namespace TechbloxModdingAPI
} }
Scheduler.Dispose(); Scheduler.Dispose();
var currentAssembly = Assembly.GetExecutingAssembly(); var currentAssembly = Assembly.GetExecutingAssembly();
harmony.UnpatchAll(currentAssembly.GetName().Name); harmony.UnpatchSelf();
harmony = null; harmony = null;
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown"); Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
} }

View file

@ -1,6 +1,5 @@
using System; using System;
using Gamecraft.Wires; using Gamecraft.Wires;
using RobocraftX.Blocks.Ghost;
using RobocraftX.Character; using RobocraftX.Character;
using RobocraftX.Character.Movement; using RobocraftX.Character.Movement;
using Unity.Mathematics; using Unity.Mathematics;
@ -11,6 +10,7 @@ using RobocraftX.Physics;
using Svelto.ECS; using Svelto.ECS;
using Techblox.BuildingDrone; using Techblox.BuildingDrone;
using Techblox.Camera; using Techblox.Camera;
using Techblox.Character;
using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Players; using TechbloxModdingAPI.Players;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
@ -214,15 +214,16 @@ namespace TechbloxModdingAPI
/// The player's initial health when entering Simulation (aka Time Running) mode. /// The player's initial health when entering Simulation (aka Time Running) mode.
/// </summary> /// </summary>
/// <value>The initial health.</value> /// <value>The initial health.</value>
[Obsolete("We can no longer get initial health, returns max health.")]
public float InitialHealth public float InitialHealth
{ {
get get
{ {
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id); var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id);
return opt ? opt.Get().initialHealth : -1f; return opt ? opt.Get().maxHealth : -1f;
} }
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().initialHealth = value; set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().maxHealth = value;
} }
/// <summary> /// <summary>
@ -233,30 +234,25 @@ namespace TechbloxModdingAPI
{ {
get get
{ {
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id); var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id);
return opt ? opt.Get().currentHealth : -1f; return opt ? opt.Get().currentHealth : -1f;
} }
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().currentHealth = value; set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().currentHealth = value;
} }
/// <summary> /// <summary>
/// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable. /// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable.
/// </summary> /// </summary>
/// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value> /// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
[Obsolete("Players are probably always damageable")]
public bool Damageable public bool Damageable
{ {
get get => true;
{
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
return opt.Get().canTakeDamageStat;
}
// ReSharper disable once ValueParameterNotUsed
set 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. /// The player's lives when initially entering Simulation (aka Time Running) mode.
/// </summary> /// </summary>
/// <value>The initial lives.</value> /// <value>The initial lives.</value>
[Obsolete("The player has infinite lives")]
public uint InitialLives public uint InitialLives
{ {
get get => uint.MaxValue;
{
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
return opt ? opt.Get().initialLives : uint.MaxValue;
}
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().initialLives = value; // ReSharper disable once ValueParameterNotUsed
set { }
} }
/// <summary> /// <summary>
/// The player's current lives in Simulation (aka Time Running) mode. /// The player's current lives in Simulation (aka Time Running) mode.
/// </summary> /// </summary>
/// <value>The current lives.</value> /// <value>The current lives.</value>
[Obsolete("The player has infinite lives")]
public uint CurrentLives public uint CurrentLives
{ {
get get => uint.MaxValue;
{
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
return opt ? opt.Get().currentLives : uint.MaxValue;
}
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().currentLives = value; // ReSharper disable once ValueParameterNotUsed
set { }
} }
/*/// <summary> /*/// <summary>
@ -373,6 +365,9 @@ namespace TechbloxModdingAPI
var group when group == CharacterExclusiveGroups.MachineSpawningGroup => PlayerState.HoldingMachine, var group when group == CharacterExclusiveGroups.MachineSpawningGroup => PlayerState.HoldingMachine,
var group when group == CharacterExclusiveGroups.OnFootGroup => PlayerState.OnFoot, var group when group == CharacterExclusiveGroups.OnFootGroup => PlayerState.OnFoot,
var group when group == CharacterExclusiveGroups.InPilotSeatGroup => PlayerState.InSeat, 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") _ => throw new ArgumentOutOfRangeException("", "Unknown player state")
}; };

View file

@ -21,6 +21,7 @@ using Techblox.Character;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Input; using TechbloxModdingAPI.Input;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
namespace TechbloxModdingAPI.Players namespace TechbloxModdingAPI.Players
{ {
@ -115,7 +116,7 @@ namespace TechbloxModdingAPI.Players
public bool IsDead(uint playerId) public bool IsDead(uint playerId)
{ {
if (entitiesDB == null) return true; if (entitiesDB == null) return true;
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters); return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadGroup);
} }
// reusable methods // reusable methods
@ -164,8 +165,9 @@ namespace TechbloxModdingAPI.Players
var opt = GetCameraStruct<PhysicCameraRayCastEntityStruct>(playerId); var opt = GetCameraStruct<PhysicCameraRayCastEntityStruct>(playerId);
if (!opt) return default; if (!opt) return default;
PhysicCameraRayCastEntityStruct rayCast = opt; PhysicCameraRayCastEntityStruct rayCast = opt;
EGID physicCameraEgid = new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup);
float distance = maxDistance < 0 float distance = maxDistance < 0
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, physicCameraEgid,
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize) GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
: maxDistance; : maxDistance;
if (rayCast.hit && rayCast.distance <= distance) if (rayCast.hit && rayCast.distance <= distance)

View file

@ -6,6 +6,7 @@ using UnityEngine;
using Gamecraft.Damage; using Gamecraft.Damage;
using RobocraftX.Common; using RobocraftX.Common;
using RobocraftX.Physics; using RobocraftX.Physics;
using Techblox.TimeRunning.Clusters;
namespace TechbloxModdingAPI namespace TechbloxModdingAPI
{ {
@ -20,7 +21,7 @@ namespace TechbloxModdingAPI
/// </summary> /// </summary>
public Cluster Cluster => cluster ??= clusterId == uint.MaxValue // Return cluster or if it's null then set it public Cluster Cluster => cluster ??= clusterId == uint.MaxValue // Return cluster or if it's null then set it
? Block.BlockEngine.GetCluster(Id.entityID) // If we don't have a clusterId set then get it from the game ? Block.BlockEngine.GetCluster(Id.entityID) // If we don't have a clusterId set then get it from the game
: GetInstance(new EGID(clusterId, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP), : GetInstance(new EGID(clusterId, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP),
egid => new Cluster(egid)); // Otherwise get the cluster from the ID egid => new Cluster(egid)); // Otherwise get the cluster from the ID
private Cluster cluster; private Cluster cluster;
@ -82,7 +83,7 @@ namespace TechbloxModdingAPI
public float3 CenterOfMass public float3 CenterOfMass
{ {
get => Block.BlockEngine.GetBlockInfo<MassEntityStruct>(this).centreOfMass; get => 0f; //TODO
//set => GetStruct().physicsMass.CenterOfMass = value; //set => GetStruct().physicsMass.CenterOfMass = value;
} }
@ -93,20 +94,20 @@ namespace TechbloxModdingAPI
public float InitialHealth public float InitialHealth
{ {
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth; get => 0f;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth = value; set { }
} }
public float CurrentHealth public float CurrentHealth
{ {
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth; get => 0f;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth = value; set { }
} }
public float HealthMultiplier public float HealthMultiplier
{ {
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier; get => 0f;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier = value; set { }
} }
/// <summary> /// <summary>

File diff suppressed because it is too large Load diff

View file

@ -1,64 +1,23 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using BepInEx;
using DataLoader; using BepInEx.Bootstrap;
using TechbloxModdingAPI.App;
using HarmonyLib; using HarmonyLib;
using IllusionInjector;
// test
using RobocraftX.FrontEnd; using RobocraftX.FrontEnd;
using ServiceLayer; using TechbloxModdingAPI.App;
using Unity.Mathematics;
using UnityEngine;
using Svelto.Tasks;
using Svelto.Tasks.Lean;
using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Commands; using TechbloxModdingAPI.Commands;
using TechbloxModdingAPI.Players;
using TechbloxModdingAPI.Tasks;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Tests namespace TechbloxModdingAPI.Tests
{ {
#if DEBUG #if DEBUG // The API should be loaded by other plugins, but it can be used by itself for testing
// unused by design [BepInPlugin("org.exmods.TechbloxModdingAPIPluginTest", PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
/// <summary> [BepInProcess("Techblox.exe")]
/// Modding API implemented as a standalone IPA Plugin. public class TechbloxModdingAPIPluginTest : BaseUnityPlugin
/// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself
/// </summary>
class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin
{ {
private void Awake()
private static Harmony harmony { get; set; }
public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
public string HarmonyID { get; } = "org.git.exmods.modtainers.techbloxmoddingapi";
public override void OnApplicationQuit()
{ {
Main.Shutdown();
}
public override void OnApplicationStart()
{
FileLog.Reset();
Harmony.DEBUG = true;
Main.Init(); Main.Init();
Logging.MetaDebugLog($"Version group id {ApiExclusiveGroups.versionGroup}");
// disable background music
Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers()));
//AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :(
//Utility.VersionTracking.Enable();//(very) unstable
// debug/test handlers
Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!"); Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler"); Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler");
Game.Enter += (s, a) => Game.Enter += (s, a) =>
@ -69,9 +28,6 @@ namespace TechbloxModdingAPI.Tests
Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}"); Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}");
}; };
// debug/test commands
if (Dependency.Hell("ExtraCommands"))
{
CommandBuilder.Builder() CommandBuilder.Builder()
.Name("Exit") .Name("Exit")
.Description("Close Techblox immediately, without any prompts") .Description("Close Techblox immediately, without any prompts")
@ -84,310 +40,31 @@ namespace TechbloxModdingAPI.Tests
.Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; }) .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
.Build(); .Build();
CommandBuilder.Builder()
.Name("MoveLastBlock")
.Description("Move the most-recently-placed block, and any connected blocks by the given offset")
.Action((float x, float y, float z) =>
{
if (GameState.IsBuildMode())
foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
block.Position += new Unity.Mathematics.float3(x, y, z);
else
Logging.CommandLogError("Blocks can only be moved in Build mode!");
}).Build();
CommandBuilder.Builder()
.Name("PlaceAluminium")
.Description("Place a block of aluminium at the given coordinates")
.Action((float x, float y, float z) =>
{
var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z));
Logging.CommandLog("Block placed with type: " + block.Type);
})
.Build();
CommandBuilder.Builder()
.Name("PlaceAluminiumLots")
.Description("Place a lot of blocks of aluminium at the given coordinates")
.Action((float x, float y, float z) =>
{
Logging.CommandLog("Starting...");
var sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
for (int j = 0; j < 100; j++)
Block.PlaceNew(BlockIDs.Cube, new float3(x + i, y, z + j));
sw.Stop();
Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
})
.Build();
Block b = null;
CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up")
.Action(() =>
{
if (b == null)
{
b = new Player(PlayerType.Local).GetBlockLookedAt();
Logging.CommandLog("Block saved: " + b);
}
else
Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0)));
}).Build();
CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
.Action(() => { throw new Exception("Error Command always throws an error"); })
.Build();
CommandBuilder.Builder("ColorBlock",
"Change color of the block looked at if there's any.")
.Action<string>(str =>
{
if (!Enum.TryParse(str, out BlockColors color))
{
Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values.");
var s = str.Split(' ');
new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]),
float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3]));
return;
}
new Player(PlayerType.Local).GetBlockLookedAt().Color = color;
Logging.CommandLog("Colored block to " + color);
}).Build();
CommandBuilder.Builder("MoveBlockByID", "Gets a block based on its object identifier and teleports it up.")
.Action<char>(ch =>
{
foreach (var body in SimBody.GetFromObjectID(ch))
{
Logging.CommandLog("SimBody: " + body);
body.Position += new float3(0, 10, 0);
foreach (var bodyConnectedBody in body.GetConnectedBodies())
{
Logging.CommandLog("Moving " + bodyConnectedBody);
bodyConnectedBody.Position += new float3(0, 10, 0);
}
}
}).Build();
CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.")
.Action((float val, float max) =>
{
var body = new Player(PlayerType.Local).GetSimBodyLookedAt();
if (body == null) return;
body.CurrentHealth = val;
body.InitialHealth = max;
Logging.CommandLog("Health set to: " + val);
}).Build();
CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group")
.Action((float x, float y, float z) =>
{
var pos = new float3(x, y, z);
var group = BlockGroup.Create(new Block(BlockIDs.Cube, pos) {Color = BlockColors.Aqua});
new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
{Color = BlockColors.Blue, BlockGroup = group};
new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
{Color = BlockColors.Green, BlockGroup = group};
new Block(BlockIDs.Cube, pos + new float3(1, 0, 0))
{Color = BlockColors.Lime, BlockGroup = group};
}).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((BlockIDs) 500, new float3(0, 0, 0)));
}).Build();
CommandBuilder.Builder("toggleTimeMode", "Enters or exits simulation.")
.Action((float x, float y, float z) =>
{
Game.CurrentGame().ToggleTimeMode();
}).Build();
CommandBuilder.Builder("testColorBlock", "Tests coloring a block to default color")
.Action(() => Player.LocalPlayer.GetBlockLookedAt().Color = BlockColors.Default).Build();
CommandBuilder.Builder("testMaterialBlock", "Tests materialing a block to default material")
.Action(() => Player.LocalPlayer.GetBlockLookedAt().Material = BlockMaterial.Default).Build();
CommandBuilder.Builder("testGameName", "Tests changing the game name")
.Action(() => Game.CurrentGame().Name = "Test").Build();
CommandBuilder.Builder("makeBlockStatic", "Makes a block you look at static")
.Action(() => Player.LocalPlayer.GetBlockLookedAt().Static = true).Build();
Game.AddPersistentDebugInfo("InstalledMods", InstalledMods); Game.AddPersistentDebugInfo("InstalledMods", InstalledMods);
/*Block.Placed += (sender, args) =>
Logging.MetaDebugLog("Placed block " + args.Block);
Block.Removed += (sender, args) =>
Logging.MetaDebugLog("Removed block " + args.Block);*/
}
// dependency test // Plugin startup logic
if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0"))) Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!");
{
Logging.LogWarning("You're in TechbloxScripting dependency hell");
}
else
{
Logging.Log("Compatible TechbloxScripting detected");
}
// Interface test
/*Group uiGroup = new Group(new Rect(20, 20, 200, 500), "TechbloxModdingAPI_UITestGroup", true);
var button = new Button("TEST");
button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
var button2 = new Button("TEST2");
button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
Text uiText = new Text("<Input!>", multiline: true);
uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); };
Label uiLabel = new Label("Label!");
Image uiImg = new Image(name:"Behold this texture!");
uiImg.Enabled = false;
uiGroup.AddElement(button);
uiGroup.AddElement(button2);
uiGroup.AddElement(uiText);
uiGroup.AddElement(uiLabel);
uiGroup.AddElement(uiImg);*/
/*Addressables.LoadAssetAsync<Texture2D>("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg")
.Completed +=
handle =>
{
uiImg.Texture = handle.Result;
uiImg.Enabled = true;
Logging.MetaDebugLog($"Got blue bg asset {handle.Result}");
};*/
/*((FasterList<GuiInputMap.GuiInputMapElement>)AccessTools.Property(typeof(GuiInputMap), "GuiInputsButtonDown").GetValue(null))
.Add(new GuiInputMap.GuiInputMapElement(RewiredConsts.Action.ToggleCommandLine, GuiIn))*/
/*Game.Enter += (sender, e) =>
{
ushort lastKey = ushort.MaxValue;
foreach (var kv in FullGameFields._dataDb.GetValues<CubeListData>()
.OrderBy(kv=>ushort.Parse(kv.Key)))
{
var data = (CubeListData) kv.Value;
ushort currentKey = ushort.Parse(kv.Key);
var toReplace = new Dictionary<string, string>
{
{"Scalable", ""}, {"Qtr", "Quarter"}, {"RNeg", "Rounded Negative"},
{"Neg", "Negative"}, {"Tetra", "Tetrahedron"},
{"RWedge", "Rounded Wedge"}, {"RTetra", "Rounded Tetrahedron"}
};
string name = LocalizationService.Localize(data.CubeNameKey).Split(' ').Select(str =>
str.Length > 0 ? char.ToUpper(str[0]) + str.Substring(1) : str).Aggregate((a, b) => a + b)
.Replace("-", "");
foreach (var rkv in toReplace)
{
name = Regex.Replace(name, rkv.Key + "([A-Z]|$)", rkv.Value + "$1");
}
Console.WriteLine($"{name}{(currentKey != lastKey + 1 ? $" = {currentKey}" : "")},");
lastKey = currentKey;
}
};
Game.Enter += (sender, e) =>
{
ushort lastKey = ushort.MaxValue;
Console.WriteLine("Materials:\n" + FullGameFields._dataDb.GetValues<MaterialPropertiesData>()
.OrderBy(kv => ushort.Parse(kv.Key))
.Select(kv =>
{
ushort currentKey = ushort.Parse(kv.Key);
string result = $"{((MaterialPropertiesData)kv.Value).Name}{(currentKey != lastKey + 1 ? $" = {kv.Key}" : "")},";
lastKey = currentKey;
return result;
})
.Aggregate((a, b) => a + "\n" + b));
};*/
CommandBuilder.Builder("takeScreenshot", "Enables the screenshot taker")
.Action(() =>
{
Game.CurrentGame().EnableScreenshotTaker();
}).Build();
CommandBuilder.Builder("testPositionDefault", "Tests the Block.Position property's default value.")
.Action(() =>
{
IEnumerator<TaskContract> Loop()
{
for (int i = 0; i < 2; i++)
{
Console.WriteLine("A");
var block = Block.PlaceNew(BlockIDs.Cube, 1);
Console.WriteLine("B");
while (!block.Exists)
yield return Yield.It;
Console.WriteLine("C");
block.Remove();
Console.WriteLine("D");
while (block.Exists)
yield return Yield.It;
Console.WriteLine("E - Pos: " + block.Position);
block.Position = 4;
Console.WriteLine("F - Pos: " + block.Position);
}
}
Loop().RunOn(Scheduler.leanRunner);
}).Build();
CommandBuilder.Builder("importAssetBundle")
.Action(() =>
{
Logging.CommandLog("Importing asset bundle...");
var ab = AssetBundle.LoadFromFile(
@"filepath");
Logging.CommandLog("Imported asset bundle: " + ab);
var assets = ab.LoadAllAssets();
Logging.CommandLog("Loaded " + assets.Length + " assets");
foreach (var asset in assets)
{
Logging.CommandLog(asset);
}
}).Build();
bool shouldTestGhostBlock = false;
CommandBuilder.Builder("testGhostBlock")
.Action(() =>
{
if (shouldTestGhostBlock)
{
shouldTestGhostBlock = false;
Logging.CommandLog("Test disabled");
}
shouldTestGhostBlock = true;
Scheduler.Schedule(new Repeatable(() =>
{
var ghostBlock = Player.LocalPlayer.GetGhostBlock();
if (ghostBlock == null) return;
ghostBlock.Position = Player.LocalPlayer.Position + 2;
ghostBlock.Color = new BlockColor(BlockColors.Lime);
}, () => shouldTestGhostBlock));
Logging.CommandLog("Test enabled");
}).Build();
Client.EnterMenu += (sender, args) => Scheduler.Schedule(new Once(() => Client.Instance.CloseBetaPopup()));
Game.Enter += (sender, args) =>
Console.WriteLine(
$"Current game selection data: {FullGameFields._gameSelectionData.gameMode} - {FullGameFields._gameSelectionData.saveType}");
#if TEST #if TEST
TestRoot.RunTests(); TestRoot.RunTests();
#endif #endif
} }
private void OnDestroy()
{
Main.Shutdown();
}
private string modsString; private string modsString;
private string InstalledMods() private string InstalledMods()
{ {
if (modsString != null) return modsString; if (modsString != null) return modsString;
StringBuilder sb = new StringBuilder("Installed mods:"); StringBuilder sb = new StringBuilder("Installed mods:");
foreach (var plugin in PluginManager.Plugins) foreach (var (_, plugin) in Chainloader.PluginInfos)
sb.Append("\n" + plugin.Name + " - " + plugin.Version); sb.Append("\n" + plugin.Metadata.Name + " - " + plugin.Metadata.Version);
return modsString = sb.ToString(); return modsString = sb.ToString();
} }
}
[HarmonyPatch] [HarmonyPatch]
public class MinimumSpecsPatch public class MinimumSpecsPatch
@ -403,6 +80,5 @@ namespace TechbloxModdingAPI.Tests
return ((Func<bool>) MinimumSpecsCheck.CheckRequirementsMet).Method; return ((Func<bool>) MinimumSpecsCheck.CheckRequirementsMet).Method;
} }
} }
}
#endif #endif
} }

View file

@ -1,7 +1,5 @@
using System; using System;
using BepInEx.Bootstrap;
using IllusionInjector;
using IllusionPlugin;
namespace TechbloxModdingAPI.Utility namespace TechbloxModdingAPI.Utility
{ {
@ -15,11 +13,11 @@ namespace TechbloxModdingAPI.Utility
/// </summary> /// </summary>
/// <returns>The plugin.</returns> /// <returns>The plugin.</returns>
/// <param name="name">The plugin's name.</param> /// <param name="name">The plugin's name.</param>
public static IPlugin GetPlugin(string name) public static BepInEx.PluginInfo GetPlugin(string name)
{ {
foreach(IPlugin plugin in PluginManager.Plugins) foreach(var plugin in Chainloader.PluginInfos.Values)
{ {
if (plugin.Name == name) if (plugin.Metadata.Name == name)
{ {
return plugin; return plugin;
} }
@ -35,45 +33,15 @@ namespace TechbloxModdingAPI.Utility
/// <param name="name">The plugin's name.</param> /// <param name="name">The plugin's name.</param>
public static Version GetPluginVersion(string name) public static Version GetPluginVersion(string name)
{ {
IPlugin plugin = GetPlugin(name); var plugin = GetPlugin(name);
if (plugin != null) { if (plugin != null) {
try try
{ {
return new Version(plugin.Version); return plugin.Metadata.Version;
} catch (Exception e) when ( } catch (Exception e) when (e is ArgumentException or FormatException or OverflowException) {}
e is ArgumentException
|| e is ArgumentNullException
|| e is ArgumentOutOfRangeException
|| e is FormatException
|| e is OverflowException) {}
return plugin.GetType().Assembly.GetName().Version; return plugin.GetType().Assembly.GetName().Version;
} }
return null; return null;
} }
// (I'm leaving the auto-generated version)
// <summary>
// Hell the specified name and version.
// </summary>
// <returns>The hell.</returns>
// <param name="name">Name.</param>
// <param name="version">Version.</param>
/// <summary>
/// Detect if you're in dependency hell with respect to the plugin.
/// ie Check if the plugin doesn't exist or is out of date.
/// When version is null, this only checks if the plugin exists.
/// The version is retrieved using GetPluginVersion(string name).
/// </summary>
/// <returns>Are you in dependency hell?</returns>
/// <param name="name">The plugin's name'</param>
/// <param name="version">The target version.</param>
public static bool Hell(string name, Version version = null)
{
Version pluginVersion = GetPluginVersion(name);
if (version == null) {
return pluginVersion == null;
}
return (pluginVersion == null || pluginVersion < version);
}
} }
} }

View file

@ -1,7 +1,7 @@
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Hybrid; using Svelto.ECS.Hybrid;
namespace TechbloxModdingAPI.Utility namespace TechbloxModdingAPI.Utility.ECS
{ {
public static class ManagedApiExtensions public static class ManagedApiExtensions
{ {
@ -29,7 +29,8 @@ namespace TechbloxModdingAPI.Utility
/// <param name="group">The group of the entity if the object can have multiple</param> /// <param name="group">The group of the entity if the object can have multiple</param>
/// <typeparam name="T">The component to query</typeparam> /// <typeparam name="T">The component to query</typeparam>
/// <returns>A reference to the component or a dummy value</returns> /// <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 where T : struct, IEntityViewComponent
{ {
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); 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> /// <param name="group">The group of the entity if the object can have multiple</param>
/// <typeparam name="T">The component to query</typeparam> /// <typeparam name="T">The component to query</typeparam>
/// <returns>A reference to the component or a dummy value</returns> /// <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 where T : struct, IEntityViewComponent
{ {
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); 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>(); if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd<T>();
return ref opt.Get(); //Default value 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);
}
} }
} }

View file

@ -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);
}
}
}

View file

@ -1,13 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS; using Svelto.ECS;
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.Lean; using Svelto.Tasks.Lean;
using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Tasks;
namespace TechbloxModdingAPI.Utility namespace TechbloxModdingAPI.Utility.ECS
{ {
public static class NativeApiExtensions public static partial class NativeApiExtensions
{ {
/// <summary> /// <summary>
/// Attempts to query an entity and returns an optional that contains the result if succeeded. /// Attempts to query an entity and returns an optional that contains the result if succeeded.
@ -33,7 +34,8 @@ namespace TechbloxModdingAPI.Utility
/// <param name="group">The group of the entity if the object can have multiple</param> /// <param name="group">The group of the entity if the object can have multiple</param>
/// <typeparam name="T">The component to query</typeparam> /// <typeparam name="T">The component to query</typeparam>
/// <returns>A reference to the component or a dummy value</returns> /// <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 where T : unmanaged, IEntityComponent
{ {
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); 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> /// <param name="group">The group of the entity if the object can have multiple</param>
/// <typeparam name="T">The component to query</typeparam> /// <typeparam name="T">The component to query</typeparam>
/// <returns>A reference to the component or a dummy value</returns> /// <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 where T : unmanaged, IEntityComponent
{ {
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); 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 return ref opt.Get(); //Default value
} }
private static readonly Dictionary<Type, (int PublishedCount, HashSet<EGID> Changes)> ChangesToPublish = new();
/// <summary> /// <summary>
/// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary. /// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary.
/// It will only publish in the next frame. /// It will only publish in the next frame.
/// </summary> /// </summary>
/// <param name="entitiesDB">The entities DB to publish to</param> /// <param name="entitiesDB">The entities DB to publish to</param>
/// <param name="id">The ECS object that got changed</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> /// <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 where T : unmanaged, IEntityComponent
{ //TODO: Doesn't seem to help {
if(!ChangesToPublish.ContainsKey(typeof(T))) PublishChanges<T>(entitiesDB, id).RunOn(Scheduler.leanRunner);
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);
} }
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 where T : unmanaged, IEntityComponent
{ {
yield return Yield.It; yield return Yield.It;
while (ChangesToPublish[typeof(T)].PublishedCount >= limit) var entityStream = entitiesDB.GetEntityStream<T>();
yield return Yield.It; 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); entitiesDB.PublishEntityChange<T>(id);
var (count, changes) = ChangesToPublish[typeof(T)]; }
changes.Remove(id);
ChangesToPublish[typeof(T)] = (count + 1, changes); /// <summary>
yield return Yield.It; /// Query entities as OptionalRefs. The elements always exist, it's just a nice way to encapsulate the data.
ChangesToPublish[typeof(T)] = (0, changes); /// </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);
} }
} }
} }

View file

@ -4,8 +4,9 @@ using Svelto.ECS;
namespace TechbloxModdingAPI.Utility 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 State state;
private readonly uint index; private readonly uint index;
private NB<T> array; private NB<T> array;
@ -13,19 +14,22 @@ namespace TechbloxModdingAPI.Utility
private readonly EntityInitializer initializer; private readonly EntityInitializer initializer;
//The possible fields are: (index && (array || managedArray)) || 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; state = State.Native;
this.array = array; this.array = array;
this.index = index; this.index = index;
this.entityId = entityId;
initializer = default; initializer = default;
managedArray = default;
} }
public OptionalRef(MB<T> array, uint index) public OptionalRef(MB<T> array, uint index, EGID entityId = default)
{ {
state = State.Managed; state = State.Managed;
managedArray = array; managedArray = array;
this.index = index; this.index = index;
this.entityId = entityId;
initializer = default; initializer = default;
this.array = default; this.array = default;
} }
@ -35,8 +39,9 @@ namespace TechbloxModdingAPI.Utility
/// </summary> /// </summary>
/// <param name="obj">The object with the initializer</param> /// <param name="obj">The object with the initializer</param>
/// <param name="unmanaged">Whether the struct is unmanaged</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) if (obj.InitData.Valid)
{ {
initializer = obj.InitData.Initializer(obj.Id); initializer = obj.InitData.Initializer(obj.Id);
@ -49,6 +54,7 @@ namespace TechbloxModdingAPI.Utility
} }
array = default; array = default;
index = default; index = default;
managedArray = default;
} }
/// <summary> /// <summary>
@ -73,6 +79,11 @@ namespace TechbloxModdingAPI.Utility
set => Get() = value; 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 bool Exists => state != State.Empty;
public T? Nullable() => this ? Get() : default; 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 bool(OptionalRef<T> opt) => opt.state != State.Empty;
public static implicit operator EGID(OptionalRef<T> opt) => opt.entityId;
/// <summary> /// <summary>
/// Creates an instance of a struct T that can be referenced. /// Creates an instance of a struct T that can be referenced.
/// </summary> /// </summary>

View file

@ -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;
}
}
}
}