Compare commits
88 commits
Author | SHA1 | Date | |
---|---|---|---|
5dff88d703 | |||
a8a451f8e4 | |||
67f32b8810 | |||
b3b1e9b9e7 | |||
e0cd7f6aec | |||
23439abde3 | |||
5e90c5ee26 | |||
5117b69500 | |||
f70b65e796 | |||
55344d1352 | |||
dfe1bfb504 | |||
a610623644 | |||
f9aa6ce2bb | |||
23abe47c72 | |||
c0ef8f1fae | |||
c4a9125ed3 | |||
3eecdf2cf5 | |||
2db7b607f0 | |||
7f63944a6e | |||
c6dae688fe | |||
7b2ac973d8 | |||
0ec47cd38b | |||
ddaa933e7d | |||
5fea7dc3b3 | |||
4684b33c69 | |||
d27bcee8d5 | |||
09d3c5e81c | |||
966fdd4c3a | |||
5602ef9268 | |||
93a0b2287a | |||
4ac8d53a2d | |||
f817becc6e | |||
2a1782cd82 | |||
5c1fe34f46 | |||
ef1b3de1a1 | |||
fef66c349d | |||
e3a7961be4 | |||
f53d0b63e7 | |||
619a5003cf | |||
6204b226d1 | |||
4bd636b8ed | |||
8a03277d84 | |||
aa947eaba1 | |||
63295f82c9 | |||
033ebdb86d | |||
2513040343 | |||
77d5e59ef6 | |||
9693341d7a | |||
c0eae77421 | |||
3351993936 | |||
49c3b60963 | |||
ece71c45a6 | |||
2a1676ce0f | |||
74d5a5c6b1 | |||
76faa69c74 | |||
52ccbe4dad | |||
0b2ffef0d3 | |||
99f077a917 | |||
4580ae3b66 | |||
f1376f5df6 | |||
2179ba6386 | |||
1a986056a1 | |||
|
d891f12701 | ||
|
1cb663b4d1 | ||
0bd348bd47 | |||
3929144171 | |||
92965404ce | |||
58cfba443e | |||
ee6a0e3af6 | |||
|
9e6edc19bd | ||
d581ec598a | |||
1e9d1c8f81 | |||
53bdd27166 | |||
2172364d26 | |||
|
50ebf4f0a6 | ||
|
167ea5388b | ||
47126d2d79 | |||
c5e9599c46 | |||
3f2139d592 | |||
|
9e47bbcd9a | ||
|
cda57afade | ||
|
4a9ceecc29 | ||
16521ab7eb | |||
cc4ed3e174 | |||
|
e0aa052305 | ||
d842df7681 | |||
3592c6f464 | |||
5bbb54c0c5 |
101 changed files with 8498 additions and 2552 deletions
|
@ -5,7 +5,7 @@ import re
|
|||
# this assumes a mostly semver-complient version number
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Increment GamecraftModdingAPI version")
|
||||
parser = argparse.ArgumentParser(description="Increment TechbloxModdingAPI version")
|
||||
parser.add_argument('version', metavar="VN", type=str, help="The version number to increment, or the index of the number (zero-indexed).")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -28,12 +28,12 @@ if __name__ == "__main__":
|
|||
old_version = ""
|
||||
new_version = ""
|
||||
|
||||
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile:
|
||||
print("Parsing GamecraftModdingAPI.csproj")
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
|
||||
print("Parsing TechbloxModdingAPI.csproj")
|
||||
fileStr = xmlFile.read()
|
||||
versionMatch = re.search(r"<Version>(.+)</Version>", fileStr)
|
||||
if versionMatch is None:
|
||||
print("Unable to find version number in GamecraftModdingAPI.csproj")
|
||||
print("Unable to find version number in TechbloxModdingAPI.csproj")
|
||||
exit(1)
|
||||
old_version = versionMatch.group(1)
|
||||
versionList = old_version.split(".")
|
||||
|
@ -53,7 +53,7 @@ if __name__ == "__main__":
|
|||
print(new_version)
|
||||
newFileContents = fileStr.replace("<Version>"+old_version+"</Version>", "<Version>"+new_version+"</Version>")
|
||||
|
||||
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "w") as xmlFile:
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
|
||||
print("Writing new version to project file")
|
||||
xmlFile.write(newFileContents)
|
||||
|
||||
|
|
|
@ -3,15 +3,24 @@
|
|||
import argparse
|
||||
from pathlib import Path, PurePath
|
||||
import re
|
||||
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):
|
||||
asmDir = Path(path)
|
||||
result = list()
|
||||
addedPath = ""
|
||||
if not asmDir.exists():
|
||||
addedPath = "../"
|
||||
asmDir = Path(addedPath + path)
|
||||
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"):
|
||||
result.append(str(child).replace("\\", "/"))
|
||||
if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child)) is None and str(child).lower().endswith(".dll"):
|
||||
childstr = str(child)
|
||||
childstr = os.path.relpath(childstr, addedPath).replace("\\", "/")
|
||||
result.append(childstr)
|
||||
result.sort(key=str.lower)
|
||||
result = [path + "/IllusionInjector.dll", path + "/IllusionPlugin.dll"] + result # Always put it on top
|
||||
return result
|
||||
|
||||
def buildReferencesXml(path):
|
||||
|
@ -32,7 +41,7 @@ if __name__ == "__main__":
|
|||
args = parser.parse_args()
|
||||
|
||||
print("Building Assembly references")
|
||||
asmXml = buildReferencesXml("../ref/TechbloxPreview_Data/Managed")
|
||||
asmXml = buildReferencesXml("../ref_TB/Techblox_Data/Managed")
|
||||
# print(asmXml)
|
||||
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
|
||||
|
@ -44,7 +53,7 @@ if __name__ == "__main__":
|
|||
if depsStart is None or depsEnd is None:
|
||||
print("Unable to find dependency XML comments, aborting!")
|
||||
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:
|
||||
print("Writing Assembly references")
|
||||
xmlFile.write(newFileStr)
|
||||
|
|
10
CodeGenerator/App.config
Normal file
10
CodeGenerator/App.config
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="mscorlib" publicKeyToken="b77a5c561934e089" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
158
CodeGenerator/BlockClassGenerator.cs
Normal file
158
CodeGenerator/BlockClassGenerator.cs
Normal file
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Gamecraft.Tweaks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
public class BlockClassGenerator
|
||||
{
|
||||
public void Generate(string name, string group = null, Dictionary<string, string> renames = null, params Type[] types)
|
||||
{
|
||||
if (group is null)
|
||||
{
|
||||
group = GetGroup(name) + "_BLOCK_GROUP";
|
||||
if (typeof(CommonExclusiveGroups).GetFields().All(field => field.Name != group))
|
||||
group = GetGroup(name) + "_BLOCK_BUILD_GROUP";
|
||||
}
|
||||
|
||||
if (!group.Contains('.'))
|
||||
group = "CommonExclusiveGroups." + group;
|
||||
|
||||
var codeUnit = new CodeCompileUnit();
|
||||
var ns = new CodeNamespace("TechbloxModdingAPI.Blocks");
|
||||
ns.Imports.Add(new CodeNamespaceImport("RobocraftX.Common"));
|
||||
ns.Imports.Add(new CodeNamespaceImport("Svelto.ECS"));
|
||||
var cl = new CodeTypeDeclaration(name);
|
||||
//cl.BaseTypes.Add(baseClass != null ? new CodeTypeReference(baseClass) : new CodeTypeReference("Block"));
|
||||
cl.BaseTypes.Add(new CodeTypeReference("SignalingBlock"));
|
||||
cl.Members.Add(new CodeConstructor
|
||||
{
|
||||
Parameters = {new CodeParameterDeclarationExpression("EGID", "egid")},
|
||||
Comments =
|
||||
{
|
||||
_start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
|
||||
},
|
||||
BaseConstructorArgs = {new CodeVariableReferenceExpression("egid")},
|
||||
Attributes = MemberAttributes.Public | MemberAttributes.Final
|
||||
});
|
||||
cl.Members.Add(new CodeConstructor
|
||||
{
|
||||
Parameters =
|
||||
{
|
||||
new CodeParameterDeclarationExpression(typeof(uint), "id")
|
||||
},
|
||||
Comments =
|
||||
{
|
||||
_start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
|
||||
},
|
||||
BaseConstructorArgs =
|
||||
{
|
||||
new CodeObjectCreateExpression("EGID", new CodeVariableReferenceExpression("id"),
|
||||
new CodeVariableReferenceExpression(group))
|
||||
},
|
||||
Attributes = MemberAttributes.Public | MemberAttributes.Final
|
||||
});
|
||||
foreach (var type in types)
|
||||
{
|
||||
GenerateProperties(cl, type, name, renames);
|
||||
}
|
||||
ns.Types.Add(cl);
|
||||
codeUnit.Namespaces.Add(ns);
|
||||
|
||||
var provider = CodeDomProvider.CreateProvider("CSharp");
|
||||
var path = $@"../../../../TechbloxModdingAPI/Blocks/{name}.cs";
|
||||
using (var sw = new StreamWriter(path))
|
||||
{
|
||||
provider.GenerateCodeFromCompileUnit(codeUnit, sw, new CodeGeneratorOptions {BracingStyle = "C"});
|
||||
}
|
||||
|
||||
File.WriteAllLines(path,
|
||||
File.ReadAllLines(path).SkipWhile(line => line.StartsWith("//") || line.Length == 0));
|
||||
}
|
||||
|
||||
private static string GetGroup(string name)
|
||||
{
|
||||
var ret = "";
|
||||
foreach (var ch in name)
|
||||
{
|
||||
if (char.IsUpper(ch) && ret.Length > 0)
|
||||
ret += "_" + ch;
|
||||
else
|
||||
ret += char.ToUpper(ch);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void GenerateProperties(CodeTypeDeclaration cl, Type type, string baseClass,
|
||||
Dictionary<string, string> renames)
|
||||
{
|
||||
if (!typeof(IEntityComponent).IsAssignableFrom(type))
|
||||
throw new ArgumentException("Type must be an entity component");
|
||||
bool reflection = type.IsNotPublic;
|
||||
var reflectedType = new CodeSnippetExpression($"HarmonyLib.AccessTools.TypeByName(\"{type.FullName}\")");
|
||||
foreach (var field in type.GetFields())
|
||||
{
|
||||
var attr = field.GetCustomAttribute<TweakableStatAttribute>();
|
||||
if (renames == null || !renames.TryGetValue(field.Name, out var propName))
|
||||
{
|
||||
propName = field.Name;
|
||||
if (attr != null)
|
||||
propName = attr.propertyName;
|
||||
}
|
||||
|
||||
propName = char.ToUpper(propName[0]) + propName.Substring(1);
|
||||
var getStruct = new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||
"GetBlockInfo", new CodeTypeReference(type)),
|
||||
new CodeThisReferenceExpression());
|
||||
CodeExpression structFieldReference = new CodeFieldReferenceExpression(getStruct, field.Name);
|
||||
CodeExpression reflectedGet = new CodeCastExpression(field.FieldType, new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||
"GetBlockInfo"),
|
||||
new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name)));
|
||||
CodeExpression reflectedSet = new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||
"SetBlockInfo"),
|
||||
new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name),
|
||||
new CodePropertySetValueReferenceExpression());
|
||||
cl.Members.Add(new CodeMemberProperty
|
||||
{
|
||||
Name = propName,
|
||||
HasGet = true,
|
||||
HasSet = true,
|
||||
GetStatements =
|
||||
{
|
||||
new CodeMethodReturnStatement(reflection ? reflectedGet : structFieldReference)
|
||||
},
|
||||
SetStatements =
|
||||
{
|
||||
reflection
|
||||
? (CodeStatement)new CodeExpressionStatement(reflectedSet)
|
||||
: new CodeAssignStatement(structFieldReference, new CodePropertySetValueReferenceExpression())
|
||||
},
|
||||
Type = new CodeTypeReference(field.FieldType),
|
||||
Attributes = MemberAttributes.Public | MemberAttributes.Final,
|
||||
Comments =
|
||||
{
|
||||
_start,
|
||||
new CodeCommentStatement($"Gets or sets the {baseClass}'s {propName} property." +
|
||||
$" {(attr != null ? "Tweakable stat." : "May not be saved.")}",
|
||||
true),
|
||||
_end
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly CodeCommentStatement _start = new CodeCommentStatement("<summary>", true);
|
||||
private static readonly CodeCommentStatement _end = new CodeCommentStatement("</summary>", true);
|
||||
}
|
||||
}
|
1592
CodeGenerator/CodeGenerator.csproj
Normal file
1592
CodeGenerator/CodeGenerator.csproj
Normal file
File diff suppressed because it is too large
Load diff
53
CodeGenerator/Program.cs
Normal file
53
CodeGenerator/Program.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.GroupTags;
|
||||
using RobocraftX.PilotSeat;
|
||||
using Svelto.ECS;
|
||||
using Techblox.EngineBlock;
|
||||
using Techblox.ServoBlocksServer;
|
||||
using Techblox.WheelRigBlock;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GenerateBlockClasses();
|
||||
}
|
||||
|
||||
private static void GenerateBlockClasses()
|
||||
{
|
||||
var bcg = new BlockClassGenerator();
|
||||
bcg.Generate("Engine", null, new Dictionary<string, string>
|
||||
{
|
||||
{ "engineOn", "On" }
|
||||
}, AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), // Simulation time properties
|
||||
typeof(EngineBlockTweakableComponent), typeof(EngineBlockReadonlyComponent));
|
||||
bcg.Generate("DampedSpring", "DAMPEDSPRING_BLOCK_GROUP", new Dictionary<string, string>
|
||||
{
|
||||
{"maxExtent", "MaxExtension"}
|
||||
},
|
||||
typeof(TweakableJointDampingComponent), typeof(DampedSpringReadOnlyStruct));
|
||||
bcg.Generate("LogicGate", "LOGIC_BLOCK_GROUP");
|
||||
bcg.Generate("Servo", types: typeof(ServoReadOnlyTweakableComponent), renames: new Dictionary<string, string>
|
||||
{
|
||||
{"minDeviation", "MinimumAngle"},
|
||||
{"maxDeviation", "MaximumAngle"},
|
||||
{"servoVelocity", "MaximumForce"}
|
||||
});
|
||||
bcg.Generate("WheelRig", "WHEELRIG_BLOCK_BUILD_GROUP", null,
|
||||
typeof(WheelRigTweakableStruct), typeof(WheelRigReadOnlyStruct),
|
||||
typeof(WheelRigSteerableTweakableStruct), typeof(WheelRigSteerableReadOnlyStruct));
|
||||
bcg.Generate("Seat", "RobocraftX.PilotSeat.SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP", null, typeof(SeatFollowCamComponent), typeof(SeatReadOnlySettingsComponent));
|
||||
bcg.Generate("Piston", null, new Dictionary<string, string>
|
||||
{
|
||||
{"pistonVelocity", "MaximumForce"}
|
||||
}, typeof(PistonReadOnlyStruct));
|
||||
bcg.Generate("Motor", null, null, typeof(MotorReadOnlyStruct));
|
||||
//bcg.Generate("ObjectID", "ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP", null, typeof(ObjectIDTweakableComponent));
|
||||
}
|
||||
}
|
||||
}
|
4
CodeGenerator/packages.config
Normal file
4
CodeGenerator/packages.config
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Lib.Harmony" version="2.2.0" targetFramework="net472" />
|
||||
</packages>
|
1574
MakeEverythingPublicInGame/MakeEverythingPublicInGame.csproj
Normal file
1574
MakeEverythingPublicInGame/MakeEverythingPublicInGame.csproj
Normal file
File diff suppressed because it is too large
Load diff
25
MakeEverythingPublicInGame/Program.cs
Normal file
25
MakeEverythingPublicInGame/Program.cs
Normal 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();
|
||||
}
|
|
@ -5,6 +5,10 @@ VisualStudioVersion = 16.0.29411.108
|
|||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechbloxModdingAPI", "TechbloxModdingAPI\TechbloxModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeEverythingPublicInGame", "MakeEverythingPublicInGame\MakeEverythingPublicInGame.csproj", "{391A3107-E5C6-4A04-9467-6D868AA9A8B4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -18,6 +22,18 @@ Global
|
|||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.ActiveCfg = Test|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.Build.0 = Test|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Release|Any CPU.ActiveCfg = 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.Build.0 = Debug|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Test|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Test|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
58
TechbloxModdingAPI/App/AntiAntiCheatPatch.cs
Normal file
58
TechbloxModdingAPI/App/AntiAntiCheatPatch.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using Svelto.Tasks;
|
||||
using Techblox.Anticheat.Client;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public static class AntiAntiCheatPatch
|
||||
{
|
||||
private delegate bool AntiAnticheatDelegate(ref object __result);
|
||||
|
||||
private delegate bool AntiAnticheatDelegateBool(ref bool __result);
|
||||
|
||||
private delegate bool AntiAnticheatDelegateTask(ref IEnumerator<TaskContract> __result);
|
||||
|
||||
public static void Init(Harmony harmony)
|
||||
{
|
||||
var type = AccessTools.TypeByName("Techblox.Services.Eos.Anticheat.Client.Services.AnticheatClientService");
|
||||
harmony.Patch(type.GetConstructors()[0], new HarmonyMethod(((Func<bool>) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method(type, "Shutdown"), new HarmonyMethod(((Func<bool>) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method(type, "StartProtectedSession"), new HarmonyMethod(((AntiAnticheatDelegate) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method(type, "StopProtectedSession"), new HarmonyMethod(((AntiAnticheatDelegateBool) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method("Techblox.Services.Eos.Anticheat.Client.EosGetPendingMessagesToSendServiceRequest:Execute"), new HarmonyMethod(((AntiAnticheatDelegateTask)AntiAntiCheatTask).Method));
|
||||
harmony.Patch(AccessTools.Method("Techblox.Anticheat.Client.Engines.ProcessEACViolationEngine:PollAnticheatStatus"), new HarmonyMethod(((AntiAnticheatDelegateTask)AntiAntiCheatTask).Method));
|
||||
harmony.Patch(AccessTools.Method(typeof(AnticheatClientCompositionRoot), "ClientComposeTimeRunning"), new HarmonyMethod(((Func<bool>)AntiAntiCheat).Method));
|
||||
}
|
||||
|
||||
private static bool AntiAntiCheat() => false;
|
||||
|
||||
private static bool AntiAntiCheat(ref object __result)
|
||||
{
|
||||
var targetType =
|
||||
AccessTools.TypeByName("Techblox.Services.Eos.Anticheat.Client.Services.StartProtectedSessionResult");
|
||||
var target = Activator.CreateInstance(targetType);
|
||||
targetType.GetField("Success").SetValue(target, true);
|
||||
__result = target;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AntiAntiCheat(ref bool __result)
|
||||
{
|
||||
__result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AntiAntiCheatTask(ref IEnumerator<TaskContract> __result)
|
||||
{
|
||||
IEnumerator<TaskContract> Func()
|
||||
{
|
||||
yield return Yield.It;
|
||||
}
|
||||
|
||||
__result = Func();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using RobocraftX.GUI;
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class AppEngine : IFactoryEngine
|
||||
{
|
||||
public event EventHandler<MenuEventArgs> EnterMenu;
|
||||
|
||||
public event EventHandler<MenuEventArgs> ExitMenu;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public string Name => "TechbloxModdingAPIAppEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInMenu = false;
|
||||
ExceptionUtil.InvokeEvent(ExitMenu, this, new MenuEventArgs { });
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInMenu = true;
|
||||
ExceptionUtil.InvokeEvent(EnterMenu, this, new MenuEventArgs { });
|
||||
}
|
||||
|
||||
// app functionality
|
||||
|
||||
public bool IsInMenu
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
|
||||
public Game[] GetMyGames()
|
||||
{
|
||||
EntityCollection<MyGameDataEntityStruct> mgsevs = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
var mgsevsB = mgsevs.ToBuffer().buffer;
|
||||
Game[] games = new Game[mgsevs.count];
|
||||
for (int i = 0; i < mgsevs.count; i++)
|
||||
{
|
||||
Utility.Logging.MetaDebugLog($"Found game named {mgsevsB[i].GameName}");
|
||||
games[i] = new Game(mgsevsB[i].ID);
|
||||
}
|
||||
return games;
|
||||
}
|
||||
}
|
||||
|
||||
public struct MenuEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -14,22 +14,19 @@ namespace TechbloxModdingAPI.App
|
|||
/// </summary>
|
||||
public class Client
|
||||
{
|
||||
// extensible engine
|
||||
protected static AppEngine appEngine = new AppEngine();
|
||||
|
||||
protected static Func<object> ErrorHandlerInstanceGetter;
|
||||
public static Client Instance { get; } = new Client();
|
||||
|
||||
protected static Func<object> ErrorHandlerInstanceGetter;
|
||||
|
||||
protected static Action<object, Error> EnqueueError;
|
||||
|
||||
protected static Action<object> HandleErrorClosed;
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// An event that fires whenever the main menu is loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> EnterMenu
|
||||
{
|
||||
add => appEngine.EnterMenu += ExceptionUtil.WrapHandler(value);
|
||||
remove => appEngine.EnterMenu -= value;
|
||||
add => Game.menuEngine.EnterMenu += value;
|
||||
remove => Game.menuEngine.EnterMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -37,8 +34,8 @@ namespace TechbloxModdingAPI.App
|
|||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> ExitMenu
|
||||
{
|
||||
add => appEngine.ExitMenu += ExceptionUtil.WrapHandler(value);
|
||||
remove => appEngine.ExitMenu -= value;
|
||||
add => Game.menuEngine.ExitMenu += value;
|
||||
remove => Game.menuEngine.ExitMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -69,8 +66,8 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
get
|
||||
{
|
||||
if (!appEngine.IsInMenu) return new Game[0];
|
||||
return appEngine.GetMyGames();
|
||||
if (!Game.menuEngine.IsInMenu) return Array.Empty<Game>();
|
||||
return Game.menuEngine.GetMyGames();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +77,7 @@ namespace TechbloxModdingAPI.App
|
|||
/// <value><c>true</c> if in menu; <c>false</c> when loading or in a game.</value>
|
||||
public bool InMenu
|
||||
{
|
||||
get => appEngine.IsInMenu;
|
||||
get => Game.menuEngine.IsInMenu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -96,14 +93,26 @@ namespace TechbloxModdingAPI.App
|
|||
EnqueueError(errorHandlerInstance, popup);
|
||||
}
|
||||
|
||||
// TODO
|
||||
/*public void CloseCurrentPrompt()
|
||||
public void CloseCurrentPrompt()
|
||||
{
|
||||
// RobocraftX.Services.ErrorHandler.Instance.HandlePopupClosed();
|
||||
// FIXME: this is a call that is also called when closing, not the actual closing action itself (so it doesn't work)
|
||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||
HandleErrorClosed(errorHandlerInstance);
|
||||
}*/
|
||||
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||
popup.Close();
|
||||
}
|
||||
|
||||
public void SelectFirstPromptButton()
|
||||
{
|
||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||
popup.FirstButton();
|
||||
}
|
||||
|
||||
public void SelectSecondPromptButton()
|
||||
{
|
||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||
popup.SecondButton();
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
|
@ -116,11 +125,6 @@ namespace TechbloxModdingAPI.App
|
|||
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
|
||||
.MakeGenericMethod(errorHandler, errorHandle)
|
||||
.Invoke(null, new object[0]);
|
||||
/*HandleErrorClosed = (Action<object>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenHandlePopupClosed")
|
||||
.MakeGenericMethod(errorHandler)
|
||||
.Invoke(null, new object[0]);*/
|
||||
// register engines
|
||||
MenuEngineManager.AddMenuEngine(appEngine);
|
||||
}
|
||||
|
||||
// Creating delegates once is faster than reflection every time
|
||||
|
@ -145,14 +149,23 @@ namespace TechbloxModdingAPI.App
|
|||
return enqueueCasted;
|
||||
}
|
||||
|
||||
private static Action<object> GenHandlePopupClosed<T>()
|
||||
private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
|
||||
|
||||
private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
|
||||
{
|
||||
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
|
||||
MethodInfo handlePopupClosed = AccessTools.Method(errorHandler, "HandlePopupClosed");
|
||||
Action<T> handleSimple =
|
||||
(Action<T>) Delegate.CreateDelegate(typeof(Action<T>), handlePopupClosed);
|
||||
Action<object> handleCasted = (object instance) => handleSimple((T) instance);
|
||||
return handleCasted;
|
||||
if (_errorPopup.Close != null)
|
||||
return _errorPopup;
|
||||
Type errorHandler = handler.GetType();
|
||||
FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
|
||||
var errorPopup = (ErrorPopup)field.GetValue(handler);
|
||||
MethodInfo info = AccessTools.Method(errorPopup.GetType(), "ClosePopup");
|
||||
var close = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
|
||||
info = AccessTools.Method(errorPopup.GetType(), "HandleFirstOption");
|
||||
var first = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
|
||||
info = AccessTools.Method(errorPopup.GetType(), "HandleSecondOption");
|
||||
var second = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
|
||||
_errorPopup = (close, first, second);
|
||||
return _errorPopup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,17 +42,25 @@ namespace TechbloxModdingAPI.App
|
|||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUp2()
|
||||
{
|
||||
Client c = new Client();
|
||||
c.PromptUser(popup2);
|
||||
//c.CloseCurrentPrompt();
|
||||
Client.Instance.PromptUser(popup2);
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUp1()
|
||||
{
|
||||
Client c = new Client();
|
||||
c.PromptUser(popup1);
|
||||
//c.CloseCurrentPrompt();
|
||||
Client.Instance.PromptUser(popup1);
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUpClose1()
|
||||
{
|
||||
Client.Instance.CloseCurrentPrompt();
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUpClose2()
|
||||
{
|
||||
Client.Instance.CloseCurrentPrompt();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
namespace TechbloxModdingAPI.App
|
||||
using System;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public enum CurrentGameMode
|
||||
{
|
||||
None,
|
||||
/// <summary>
|
||||
/// Building a game
|
||||
/// Building a world
|
||||
/// </summary>
|
||||
Build,
|
||||
/// <summary>
|
||||
/// Playing a game
|
||||
/// Playing on a map
|
||||
/// </summary>
|
||||
Play,
|
||||
/// <summary>
|
||||
/// Viewing a prefab
|
||||
/// Viewing a prefab (doesn't exist anymore)
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
View,
|
||||
/// <summary>
|
||||
/// Viewing a tutorial
|
||||
/// Viewing a tutorial (doesn't exist anymore)
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
Tutorial
|
||||
}
|
||||
}
|
|
@ -2,12 +2,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Techblox.GameSelection;
|
||||
|
||||
using TechbloxModdingAPI;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
@ -23,7 +21,7 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
// extensible engines
|
||||
protected static GameGameEngine gameEngine = new GameGameEngine();
|
||||
protected static GameMenuEngine menuEngine = new GameMenuEngine();
|
||||
protected internal static GameMenuEngine menuEngine = new GameMenuEngine();
|
||||
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
|
||||
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
|
||||
|
||||
|
@ -93,7 +91,7 @@ namespace TechbloxModdingAPI.App
|
|||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Simulate
|
||||
{
|
||||
add => buildSimEventEngine.SimulationMode += ExceptionUtil.WrapHandler(value);
|
||||
add => buildSimEventEngine.SimulationMode += value;
|
||||
remove => buildSimEventEngine.SimulationMode -= value;
|
||||
}
|
||||
|
||||
|
@ -103,7 +101,7 @@ namespace TechbloxModdingAPI.App
|
|||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Edit
|
||||
{
|
||||
add => buildSimEventEngine.BuildMode += ExceptionUtil.WrapHandler(value);
|
||||
add => buildSimEventEngine.BuildMode += value;
|
||||
remove => buildSimEventEngine.BuildMode -= value;
|
||||
}
|
||||
|
||||
|
@ -112,7 +110,7 @@ namespace TechbloxModdingAPI.App
|
|||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Enter
|
||||
{
|
||||
add => gameEngine.EnterGame += ExceptionUtil.WrapHandler(value);
|
||||
add => gameEngine.EnterGame += value;
|
||||
remove => gameEngine.EnterGame -= value;
|
||||
}
|
||||
|
||||
|
@ -122,7 +120,7 @@ namespace TechbloxModdingAPI.App
|
|||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Exit
|
||||
{
|
||||
add => gameEngine.ExitGame += ExceptionUtil.WrapHandler(value);
|
||||
add => gameEngine.ExitGame += value;
|
||||
remove => gameEngine.ExitGame -= value;
|
||||
}
|
||||
|
||||
|
@ -165,7 +163,7 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode) return menuEngine.GetGameInfo(EGID).GameName;
|
||||
return GameMode.SaveGameDetails.Name;
|
||||
return gameEngine.GetGameData().saveName;
|
||||
}
|
||||
|
||||
set
|
||||
|
@ -174,11 +172,7 @@ namespace TechbloxModdingAPI.App
|
|||
if (menuMode)
|
||||
{
|
||||
menuEngine.SetGameName(EGID, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMode.SaveGameDetails.Name = value;
|
||||
}
|
||||
} // Save details are directly saved from user input or not changed at all when in game
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,11 +195,7 @@ namespace TechbloxModdingAPI.App
|
|||
if (menuMode)
|
||||
{
|
||||
menuEngine.SetGameDescription(EGID, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No description exists in-game
|
||||
}
|
||||
} // No description exists in-game
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +209,7 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath;
|
||||
return GameMode.SaveGameDetails.Folder;
|
||||
return gameEngine.GetGameData().gameID;
|
||||
}
|
||||
|
||||
set
|
||||
|
@ -229,11 +219,6 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this likely breaks things
|
||||
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, value, GameMode.SaveGameDetails.WorkshopId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,28 +227,16 @@ namespace TechbloxModdingAPI.App
|
|||
/// In most cases this is invalid and returns 0, so this can be ignored.
|
||||
/// </summary>
|
||||
/// <value>The workshop identifier.</value>
|
||||
[Obsolete]
|
||||
public ulong WorkshopId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!VerifyMode()) return 0uL;
|
||||
if (menuMode) return 0uL; // MyGames don't have workshop IDs
|
||||
return GameMode.SaveGameDetails.WorkshopId;
|
||||
return 0uL; // Not supported anymore
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
VerifyMode();
|
||||
if (menuMode)
|
||||
{
|
||||
// MyGames don't have workshop IDs
|
||||
// menuEngine.GetGameInfo(EGID).GameName.Set(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this likely breaks things
|
||||
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, GameMode.SaveGameDetails.Folder, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,7 +316,7 @@ namespace TechbloxModdingAPI.App
|
|||
get
|
||||
{
|
||||
if (menuMode || !VerifyMode()) return CurrentGameMode.None;
|
||||
return (CurrentGameMode) GameMode.CurrentMode;
|
||||
return gameEngine.GetGameData().gameMode == GameMode.CreateWorld ? CurrentGameMode.Build : CurrentGameMode.Play;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,6 +446,15 @@ namespace TechbloxModdingAPI.App
|
|||
return blocks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable the screenshot taker for updating the game's screenshot. Breaks the pause menu in a new save.
|
||||
/// </summary>
|
||||
public void EnableScreenshotTaker()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
gameEngine.EnableScreenshotTaker();
|
||||
}
|
||||
|
||||
~Game()
|
||||
{
|
||||
foreach (string id in debugIds)
|
||||
|
|
|
@ -11,9 +11,9 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered
|
||||
{
|
||||
public event EventHandler<GameEventArgs> SimulationMode;
|
||||
public WrappedHandler<GameEventArgs> SimulationMode;
|
||||
|
||||
public event EventHandler<GameEventArgs> BuildMode;
|
||||
public WrappedHandler<GameEventArgs> BuildMode;
|
||||
|
||||
public string Name => "TechbloxModdingAPIBuildSimEventGameEngine";
|
||||
|
||||
|
@ -27,13 +27,13 @@ namespace TechbloxModdingAPI.App
|
|||
|
||||
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(SimulationMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(BuildMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" });
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Schedulers;
|
||||
|
@ -10,34 +9,61 @@ using Svelto.ECS;
|
|||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Lean;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common.Loading;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.ScreenshotTaker;
|
||||
using Techblox.Environment.Transition;
|
||||
using Techblox.GameSelection;
|
||||
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Input;
|
||||
using TechbloxModdingAPI.Players;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class GameGameEngine : IApiEngine
|
||||
public class GameGameEngine : IApiEngine, IReactOnAddAndRemove<LoadingActionEntityStruct>
|
||||
{
|
||||
public event EventHandler<GameEventArgs> EnterGame;
|
||||
public WrappedHandler<GameEventArgs> EnterGame;
|
||||
|
||||
public event EventHandler<GameEventArgs> ExitGame;
|
||||
public WrappedHandler<GameEventArgs> ExitGame;
|
||||
|
||||
public string Name => "TechbloxModdingAPIGameInfoMenuEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
private bool enteredGame;
|
||||
private bool loadingFinished;
|
||||
private bool playerJoined;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(ExitGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
if (GameReloadedPatch.IsReload)
|
||||
return; // Toggling time mode
|
||||
ExitGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
|
||||
IsInGame = false;
|
||||
loadingFinished = false;
|
||||
playerJoined = false;
|
||||
enteredGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(EnterGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
IsInGame = true;
|
||||
if (GameReloadedPatch.IsReload)
|
||||
return; // Toggling time mode
|
||||
enteredGame = true;
|
||||
Player.Joined += OnPlayerJoined;
|
||||
}
|
||||
|
||||
private void OnPlayerJoined(object sender, PlayerEventArgs args)
|
||||
{
|
||||
if (args.Player.Type != PlayerType.Local) return;
|
||||
playerJoined = true;
|
||||
Player.Joined -= OnPlayerJoined;
|
||||
CheckJoinEvent();
|
||||
}
|
||||
|
||||
// game functionality
|
||||
|
@ -52,7 +78,7 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
if (async)
|
||||
{
|
||||
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped);
|
||||
ExitCurrentGameAsync().RunOn(ClientLean.EveryFrameStepRunner_TimeRunningAndStopped);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -91,41 +117,79 @@ namespace TechbloxModdingAPI.App
|
|||
}
|
||||
|
||||
public void ToggleTimeMode()
|
||||
{
|
||||
if (TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB))
|
||||
FakeInput.ActionInput(toggleMode: true);
|
||||
else
|
||||
{
|
||||
IEnumerator<TaskContract> ReloadBuildModeTask()
|
||||
{
|
||||
SwitchAnimationUtil.Start(entitiesDB);
|
||||
while (SwitchAnimationUtil.IsFadeOutActive(entitiesDB))
|
||||
yield return (TaskContract)Yield.It;
|
||||
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
|
||||
AccessTools.Method(typeof(FullGameCompositionRoot), "ReloadGame")
|
||||
.Invoke(FullGameFields.Instance, new object[] { });
|
||||
}
|
||||
|
||||
ReloadBuildModeTask().RunOn(ClientLean.UIScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
||||
List<EGID> blockEGIDs = new List<EGID>();
|
||||
foreach (var ((_, ids, count), group) in allBlocks)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var id = new EGID(ids[i], group);
|
||||
uint dbid;
|
||||
if (filter == BlockIDs.Invalid)
|
||||
dbid = (uint)filter;
|
||||
else
|
||||
dbid = entitiesDB.QueryEntity<DBEntityStruct>(id).DBID;
|
||||
var ownership = entitiesDB.QueryEntity<BlockOwnershipComponent>(id).BlockOwnership;
|
||||
if ((ownership & BlockOwnership.User) != 0 && dbid == (ulong)filter)
|
||||
blockEGIDs.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
|
||||
public void EnableScreenshotTaker()
|
||||
{
|
||||
TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB);
|
||||
ref var local = ref entitiesDB.QueryEntity<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
|
||||
if (local.enabled)
|
||||
return;
|
||||
local.enabled = true;
|
||||
entitiesDB.PublishEntityChange<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
|
||||
}
|
||||
|
||||
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
||||
List<EGID> blockEGIDs = new List<EGID>();
|
||||
if (filter == BlockIDs.Invalid)
|
||||
{
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var buffer = blocks.ToBuffer().buffer;
|
||||
for (int i = 0; i < buffer.capacity; i++)
|
||||
blockEGIDs.Add(buffer[i].ID);
|
||||
}
|
||||
public GameSelectionComponent GetGameData()
|
||||
{
|
||||
return entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var array = blocks.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var block = array[index];
|
||||
uint dbid = entitiesDB.QueryEntity<DBEntityStruct>(block.ID).DBID;
|
||||
if (dbid == (ulong) filter)
|
||||
blockEGIDs.Add(block.ID);
|
||||
}
|
||||
}
|
||||
public void Add(ref LoadingActionEntityStruct entityComponent, EGID egid)
|
||||
{
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Remove(ref LoadingActionEntityStruct entityComponent, EGID egid)
|
||||
{ // Finished loading
|
||||
if (!enteredGame) return;
|
||||
enteredGame = false;
|
||||
loadingFinished = true;
|
||||
CheckJoinEvent();
|
||||
}
|
||||
|
||||
private void CheckJoinEvent()
|
||||
{
|
||||
if (!loadingFinished || !playerJoined) return;
|
||||
EnterGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
|
||||
IsInGame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.GUI;
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using RobocraftX.Multiplayer;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
using Svelto.DataStructures;
|
||||
using Techblox.GameSelection;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
|
@ -15,6 +17,9 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
public class GameMenuEngine : IFactoryEngine
|
||||
{
|
||||
public WrappedHandler<MenuEventArgs> EnterMenu;
|
||||
|
||||
public WrappedHandler<MenuEventArgs> ExitMenu;
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public string Name => "TechbloxModdingAPIGameInfoGameEngine";
|
||||
|
@ -23,23 +28,42 @@ namespace TechbloxModdingAPI.App
|
|||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public GameMenuEngine()
|
||||
{
|
||||
MenuEnteredEnginePatch.EnteredExitedMenu = () =>
|
||||
{
|
||||
if (IsInMenu)
|
||||
EnterMenu.Invoke(this, new MenuEventArgs { });
|
||||
else
|
||||
ExitMenu.Invoke(this, new MenuEventArgs { });
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInMenu = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInMenu = true;
|
||||
MenuEnteredEnginePatch.IsInMenu = true; // At first it uses ActivateMenu(), then GoToMenu() which is patched
|
||||
MenuEnteredEnginePatch.EnteredExitedMenu();
|
||||
}
|
||||
|
||||
// game functionality
|
||||
|
||||
public bool IsInMenu
|
||||
public bool IsInMenu => MenuEnteredEnginePatch.IsInMenu;
|
||||
|
||||
public Game[] GetMyGames()
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
var (mgsevs, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
Game[] games = new Game[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Utility.Logging.MetaDebugLog($"Found game named {mgsevs[i].GameName}");
|
||||
games[i] = new Game(mgsevs[i].ID);
|
||||
}
|
||||
return games;
|
||||
}
|
||||
|
||||
public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L)
|
||||
{
|
||||
|
@ -59,14 +83,13 @@ namespace TechbloxModdingAPI.App
|
|||
|
||||
public uint HighestID()
|
||||
{
|
||||
EntityCollection<MyGameDataEntityStruct> games = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
var gamesB = games.ToBuffer().buffer;
|
||||
var (games, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
uint max = 0;
|
||||
for (int i = 0; i < games.count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (gamesB[i].ID.entityID > max)
|
||||
if (games[i].ID.entityID > max)
|
||||
{
|
||||
max = gamesB[i].ID.entityID;
|
||||
max = games[i].ID.entityID;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
|
@ -76,15 +99,19 @@ namespace TechbloxModdingAPI.App
|
|||
{
|
||||
if (!ExistsGameInfo(id)) return false;
|
||||
ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id);
|
||||
return EnterGame(mgdes.GameName, mgdes.SavedGamePath);
|
||||
return EnterGame(mgdes.GameName, mgdes.FileId);
|
||||
}
|
||||
|
||||
public bool EnterGame(string gameName, string path, ulong workshopId = 0uL, bool autoEnterSim = false)
|
||||
public bool EnterGame(ECSString gameName, string fileId, bool autoEnterSim = false)
|
||||
{
|
||||
GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build;
|
||||
GameMode.SaveGameDetails = new SaveGameDetails(gameName, path, workshopId);
|
||||
// the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason
|
||||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[0]);
|
||||
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
|
||||
ref var selection = ref entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
|
||||
selection.userContentID.Set(fileId);
|
||||
selection.triggerStart = true;
|
||||
selection.saveType = SaveType.ExistingSave;
|
||||
selection.saveName = gameName;
|
||||
selection.gameMode = GameMode.PlayGame;
|
||||
selection.gameID.Set("GAMEID_Road_Track"); //TODO: Expose to the API
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -130,4 +157,41 @@ namespace TechbloxModdingAPI.App
|
|||
}
|
||||
|
||||
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }
|
||||
|
||||
[HarmonyPatch]
|
||||
static class MenuEnteredEnginePatch
|
||||
{
|
||||
internal static bool IsInMenu;
|
||||
internal static Action EnteredExitedMenu;
|
||||
public static void Postfix()
|
||||
{
|
||||
IsInMenu = true;
|
||||
EnteredExitedMenu();
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
static class MenuExitedEnginePatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
MenuEnteredEnginePatch.IsInMenu = false;
|
||||
MenuEnteredEnginePatch.EnteredExitedMenu();
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame");
|
||||
}
|
||||
}
|
||||
|
||||
public struct MenuEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@ using Svelto.ECS.EntityStructs;
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.Blocks;
|
||||
using Unity.Mathematics;
|
||||
using Gamecraft.Blocks.GUI;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX.PilotSeat;
|
||||
using RobocraftX.Rendering;
|
||||
using Techblox.BlockLabelsServer;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Blocks.Engines;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
|
@ -65,16 +67,22 @@ namespace TechbloxModdingAPI
|
|||
/// <returns>The block object or null if doesn't exist</returns>
|
||||
public static Block GetLastPlacedBlock()
|
||||
{
|
||||
EGID? egid = BlockEngine.FindBlockEGID(BlockIdentifiers.LatestBlockID);
|
||||
uint lastBlockID = CommonExclusiveGroups.blockIDGeneratorClient.Peek() - 1;
|
||||
EGID? egid = BlockEngine.FindBlockEGID(lastBlockID);
|
||||
return egid.HasValue ? New(egid.Value) : null;
|
||||
}
|
||||
|
||||
/*public static Block CreateGhostBlock()
|
||||
{
|
||||
return BlockGroup._engine.BuildGhostChild();
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires each time a block is placed.
|
||||
/// </summary>
|
||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
|
||||
{
|
||||
add => BlockEventsEngine.Placed += ExceptionUtil.WrapHandler(value);
|
||||
{ //TODO: Rename and add instance version in 3.0
|
||||
add => BlockEventsEngine.Placed += value;
|
||||
remove => BlockEventsEngine.Placed -= value;
|
||||
}
|
||||
|
||||
|
@ -83,7 +91,7 @@ namespace TechbloxModdingAPI
|
|||
/// </summary>
|
||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
|
||||
{
|
||||
add => BlockEventsEngine.Removed += ExceptionUtil.WrapHandler(value);
|
||||
add => BlockEventsEngine.Removed += value;
|
||||
remove => BlockEventsEngine.Removed -= value;
|
||||
}
|
||||
|
||||
|
@ -91,19 +99,42 @@ namespace TechbloxModdingAPI
|
|||
new Dictionary<ExclusiveBuildGroup, (Func<EGID, Block>, Type)>
|
||||
{
|
||||
{CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP, (id => new DampedSpring(id), typeof(DampedSpring))},
|
||||
{CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))}
|
||||
{CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))},
|
||||
{CommonExclusiveGroups.LOGIC_BLOCK_GROUP, (id => new LogicGate(id), typeof(LogicGate))},
|
||||
{CommonExclusiveGroups.PISTON_BLOCK_GROUP, (id => new Piston(id), typeof(Piston))},
|
||||
{CommonExclusiveGroups.SERVO_BLOCK_GROUP, (id => new Servo(id), typeof(Servo))},
|
||||
{CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))}
|
||||
};
|
||||
|
||||
internal static Block New(EGID egid)
|
||||
static Block()
|
||||
{
|
||||
return GroupToConstructor.ContainsKey(egid.groupID)
|
||||
? GroupToConstructor[egid.groupID].Constructor(egid)
|
||||
: new Block(egid);
|
||||
foreach (var group in SeatGroups.SEATS_BLOCK_GROUPS) // Adds driver and passenger seats, occupied and unoccupied
|
||||
GroupToConstructor.Add(group, (id => new Seat(id), typeof(Seat)));
|
||||
}
|
||||
|
||||
public Block(EGID id)
|
||||
/// <summary>
|
||||
/// Returns a correctly typed instance of this block. The instances are shared for a specific block.
|
||||
/// If an instance is no longer referenced a new instance is returned.
|
||||
/// </summary>
|
||||
/// <param name="egid">The EGID of the block</param>
|
||||
/// <param name="signaling">Whether the block is definitely a signaling block</param>
|
||||
/// <returns></returns>
|
||||
internal static Block New(EGID egid, bool signaling = false)
|
||||
{
|
||||
if (egid == default) return null;
|
||||
if (GroupToConstructor.ContainsKey(egid.groupID))
|
||||
{
|
||||
var (constructor, type) = GroupToConstructor[egid.groupID];
|
||||
return GetInstance(egid, constructor, type);
|
||||
}
|
||||
|
||||
return signaling
|
||||
? GetInstance(egid, e => new SignalingBlock(e))
|
||||
: GetInstance(egid, e => new Block(e));
|
||||
}
|
||||
|
||||
public Block(EGID id) : base(id)
|
||||
{
|
||||
Id = id;
|
||||
Type expectedType;
|
||||
if (GroupToConstructor.ContainsKey(id.groupID) &&
|
||||
!GetType().IsAssignableFrom(expectedType = GroupToConstructor[id.groupID].Type))
|
||||
|
@ -130,17 +161,18 @@ namespace TechbloxModdingAPI
|
|||
/// <param name="autoWire">Whether the block should be auto-wired (if functional)</param>
|
||||
/// <param name="player">The player who placed the block</param>
|
||||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null)
|
||||
: base(block =>
|
||||
{
|
||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode())
|
||||
throw new BlockException("Blocks can only be placed in build mode.");
|
||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
||||
block.InitData = initializer;
|
||||
Placed += ((Block)block).OnPlacedInit;
|
||||
return initializer.EGID;
|
||||
})
|
||||
{
|
||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode())
|
||||
throw new BlockException("Blocks can only be placed in build mode.");
|
||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
||||
Id = initializer.EGID;
|
||||
InitData = initializer;
|
||||
Placed += OnPlacedInit;
|
||||
}
|
||||
|
||||
public override EGID Id { get; }
|
||||
|
||||
private EGID copiedFrom;
|
||||
|
||||
/// <summary>
|
||||
|
@ -256,6 +288,7 @@ namespace TechbloxModdingAPI
|
|||
color.indexInPalette = value.Index;
|
||||
color.hasNetworkChange = true;
|
||||
color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); //Setting to 255 results in black
|
||||
BlockEngine.UpdateBlockColor(Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +325,10 @@ namespace TechbloxModdingAPI
|
|||
: throw new BlockTypeException("Unknown block type! Could not set default material.");
|
||||
if (!FullGameFields._dataDb.ContainsKey<MaterialPropertiesData>(val))
|
||||
throw new BlockException($"Block material {value} does not exist!");
|
||||
BlockEngine.GetBlockInfo<CubeMaterialStruct>(this).materialId = val;
|
||||
ref var comp = ref BlockEngine.GetBlockInfo<CubeMaterialStruct>(this);
|
||||
if (comp.materialId == val)
|
||||
return;
|
||||
comp.materialId = val;
|
||||
BlockEngine.UpdatePrefab(this, val, Flipped); //The default causes the screen to go black
|
||||
}
|
||||
}
|
||||
|
@ -304,11 +340,15 @@ namespace TechbloxModdingAPI
|
|||
[TestValue(null)]
|
||||
public string Label
|
||||
{
|
||||
get => BlockEngine.GetBlockInfoViewComponent<TextLabelEntityViewStruct>(this).textLabelComponent?.text;
|
||||
get
|
||||
{
|
||||
var opt = BlockEngine.GetBlockInfoOptional<LabelResourceIDComponent>(this);
|
||||
return opt ? FullGameFields._managers.blockLabelResourceManager.GetText(opt.Get().instanceID) : null;
|
||||
}
|
||||
set
|
||||
{
|
||||
var comp = BlockEngine.GetBlockInfoViewComponent<TextLabelEntityViewStruct>(this).textLabelComponent;
|
||||
if (comp != null) comp.text = value;
|
||||
var opt = BlockEngine.GetBlockInfoOptional<LabelResourceIDComponent>(this);
|
||||
if (opt) FullGameFields._managers.blockLabelResourceManager.SetText(opt.Get().instanceID, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,8 +366,12 @@ namespace TechbloxModdingAPI
|
|||
get
|
||||
{
|
||||
if (blockGroup != null) return blockGroup;
|
||||
if (!GameState.IsBuildMode()) return null; // Breaks in simulation
|
||||
var bgec = BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this);
|
||||
return blockGroup = bgec.currentBlockGroup == -1 ? null : new BlockGroup(bgec.currentBlockGroup, this);
|
||||
return blockGroup = bgec.currentBlockGroup == -1
|
||||
? null
|
||||
: GetInstance(new EGID((uint)bgec.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup),
|
||||
egid => new BlockGroup((int)egid.entityID, this));
|
||||
}
|
||||
set
|
||||
{
|
||||
|
@ -338,6 +382,8 @@ namespace TechbloxModdingAPI
|
|||
return;
|
||||
}
|
||||
blockGroup?.RemoveInternal(this);
|
||||
if (!InitData.Valid)
|
||||
return;
|
||||
BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this).currentBlockGroup = (int?) value?.Id.entityID ?? -1;
|
||||
value?.AddInternal(this);
|
||||
blockGroup = value;
|
||||
|
@ -349,8 +395,25 @@ namespace TechbloxModdingAPI
|
|||
/// </summary>
|
||||
public bool Static
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<OverrideStaticComponent>(this).staticIfUnconnected;
|
||||
set => BlockEngine.GetBlockInfo<OverrideStaticComponent>(this).staticIfUnconnected = value;
|
||||
get => BlockEngine.GetBlockInfo<BlockStaticComponent>(this).isStatic;
|
||||
set => BlockEngine.GetBlockInfo<BlockStaticComponent>(this).isStatic = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The mass of the block.
|
||||
/// </summary>
|
||||
public float Mass
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<MassStruct>(this).mass;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block complexity used for build rules. Determines the 'cost' of the block.
|
||||
/// </summary>
|
||||
public BlockComplexity Complexity
|
||||
{
|
||||
get => new(BlockEngine.GetBlockInfo<BlockComplexityComponent>(this));
|
||||
set => BlockEngine.GetBlockInfo<BlockComplexityComponent>(this) = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -378,9 +441,10 @@ namespace TechbloxModdingAPI
|
|||
public SimBody GetSimBody()
|
||||
{
|
||||
var st = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(this);
|
||||
return st.machineRigidBodyId != uint.MaxValue
|
||||
? new SimBody(st.machineRigidBodyId, st.clusterId)
|
||||
: null;
|
||||
/*return st.machineRigidBodyId != uint.MaxValue
|
||||
? new SimBody(st.machineRigidBodyId, st.clusterId) - TODO:
|
||||
: null;*/
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -404,7 +468,7 @@ namespace TechbloxModdingAPI
|
|||
if (e.ID != Id) return;
|
||||
Placed -= OnPlacedInit; //And we can reference it
|
||||
InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again
|
||||
if (copiedFrom != EGID.Empty)
|
||||
if (copiedFrom != default)
|
||||
BlockCloneEngine.CopyBlockStats(copiedFrom, Id);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -19,17 +19,16 @@ namespace TechbloxModdingAPI
|
|||
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
||||
{
|
||||
internal static BlueprintEngine _engine = new BlueprintEngine();
|
||||
public override EGID Id { get; }
|
||||
private readonly Block sourceBlock;
|
||||
private readonly List<Block> blocks;
|
||||
private float3 position, rotation;
|
||||
internal bool PosAndRotCalculated;
|
||||
|
||||
internal BlockGroup(int id, Block block)
|
||||
internal BlockGroup(int id, Block block) : base(new EGID((uint)id,
|
||||
BlockGroupExclusiveGroups.BlockGroupEntityGroup))
|
||||
{
|
||||
if (id == BlockGroupUtility.GROUP_UNASSIGNED)
|
||||
throw new BlockException("Cannot create a block group for blocks without a group!");
|
||||
Id = new EGID((uint) id, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
|
||||
sourceBlock = block;
|
||||
blocks = new List<Block>(GetBlocks());
|
||||
Block.Removed += OnBlockRemoved;
|
||||
|
|
17
TechbloxModdingAPI/Blocks/BlockComplexity.cs
Normal file
17
TechbloxModdingAPI/Blocks/BlockComplexity.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using RobocraftX.Blocks;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public record BlockComplexity(int Cpu, int Power)
|
||||
{
|
||||
public BlockComplexity(BlockComplexityComponent component) : this(component.cpu, component.power)
|
||||
{
|
||||
}
|
||||
|
||||
public int Cpu { get; } = Cpu;
|
||||
public int Power { get; } = Power;
|
||||
|
||||
public static implicit operator BlockComplexityComponent(BlockComplexity complexity) =>
|
||||
new() { cpu = complexity.Cpu, power = complexity.Power };
|
||||
}
|
||||
}
|
|
@ -25,38 +25,7 @@ namespace TechbloxModdingAPI.Blocks
|
|||
PlateQuarterPyramid,
|
||||
PlateTetrahedron,
|
||||
Sphere,
|
||||
Frame,
|
||||
FrameS1,
|
||||
FrameS2,
|
||||
FrameS3,
|
||||
FrameS4,
|
||||
FrameS5,
|
||||
FrameWedge,
|
||||
FrameWedgeS1,
|
||||
FrameWedgeS2,
|
||||
FrameWedgeS3,
|
||||
FrameWedgeS4,
|
||||
SideS0S1,
|
||||
SideS0S2,
|
||||
SideS0S3,
|
||||
SideS0S4,
|
||||
SideS0S5,
|
||||
SideS1S1,
|
||||
SideS1S2,
|
||||
SideS1S3,
|
||||
SideS1S4,
|
||||
SideS1S5,
|
||||
SideS2S1,
|
||||
SideS2S2,
|
||||
SideS2S3,
|
||||
SideS2S4,
|
||||
SideS2S5,
|
||||
WindscreenS1,
|
||||
WindscreenS2,
|
||||
WindscreenS3,
|
||||
WindscreenS4,
|
||||
WindscreenS5,
|
||||
CarWheelArch,
|
||||
CarWheelArch = 47,
|
||||
CarArchSmallFlare,
|
||||
CarArchFlare,
|
||||
CarArchExtrudedFlare,
|
||||
|
@ -73,8 +42,8 @@ namespace TechbloxModdingAPI.Blocks
|
|||
PlateTriangle = 130,
|
||||
PlateCircle,
|
||||
PlateQuarterCircle,
|
||||
PlateRWedge,
|
||||
PlateRTetrahedron,
|
||||
PlateRoundedWedge,
|
||||
PlateRoundedTetrahedron,
|
||||
Cone,
|
||||
ConeSegment,
|
||||
DoubleSliced,
|
||||
|
@ -106,7 +75,309 @@ namespace TechbloxModdingAPI.Blocks
|
|||
WideCylinderDiagonal,
|
||||
NarrowCylinderDiagonal,
|
||||
HeadlampTetrahedron,
|
||||
CarWheelWideProfile = 200,
|
||||
GoKartEngine,
|
||||
Screen5X2Y2Z,
|
||||
Screen5X2Y3Z,
|
||||
Screen5X2Y5Z,
|
||||
Screen9X2Y2Z,
|
||||
Screen9X3Y2Z,
|
||||
Screen9X2Y3Z,
|
||||
Screen9X3Y3Z,
|
||||
Screen9X2Y5Z,
|
||||
Screen9X3Y5Z,
|
||||
Screen11X3Y2Z,
|
||||
Screen11X3Y3Z,
|
||||
Screen11X3Y5Z,
|
||||
Window6X2Y2Z,
|
||||
Window6X3Y2Z,
|
||||
Window6X2Y2ZS1,
|
||||
Window6X3Y2ZS1,
|
||||
Window6X2Y2ZS2,
|
||||
Window6X3Y2ZS2,
|
||||
Window6X2Y2ZS4,
|
||||
Window6X3Y2ZS4,
|
||||
FrameSquare,
|
||||
FrameSkewedSquare,
|
||||
FrameTriangle,
|
||||
FrameSkewedTriangle,
|
||||
GlassFrameSquare,
|
||||
GlassFrameSkewedSquare,
|
||||
GlassFrameTriangle,
|
||||
GlassFrameSkewedTriangle,
|
||||
GlassPlate,
|
||||
GlassPlateTriangle,
|
||||
GoKartWheelRigNoSteering,
|
||||
GoKartWheelRigWithSteering,
|
||||
GoKartSeat,
|
||||
CarWheelWideProfile,
|
||||
CarWheel,
|
||||
GoKartWheelWideProfile,
|
||||
GoKartWheel,
|
||||
ANDLogicGate,
|
||||
ORLogicGate,
|
||||
NOTLogicGate,
|
||||
NANDLogicGate,
|
||||
NORLogicGate,
|
||||
XORLogicGate,
|
||||
XNORLogicGate,
|
||||
AdderMathBlock,
|
||||
SubtractorMathBlock,
|
||||
MultiplierMathBlock,
|
||||
DividerMathBlock,
|
||||
InverterMathBlock,
|
||||
AverageMathBlock,
|
||||
AbsoluteMathBlock,
|
||||
MinMathBlock,
|
||||
MaxMathBlock,
|
||||
SimpleConnector,
|
||||
Motor,
|
||||
AxleServo,
|
||||
HingeServo,
|
||||
Piston,
|
||||
Button,
|
||||
Switch,
|
||||
Dial,
|
||||
Lever,
|
||||
ThreeWaySwitch,
|
||||
EqualsMathBlock,
|
||||
LessThanMathBlock,
|
||||
LessThanOrEqualMathBlock,
|
||||
GreaterThanMathBlock,
|
||||
GreaterThanOrEqualMathBlock,
|
||||
HatchbackWheelRigNoSteering,
|
||||
HatchbackWheelRigWithSteering,
|
||||
HatchbackEngine,
|
||||
HatchbackWheel,
|
||||
HatchbackWheelArch,
|
||||
HatchbackArchSmallFlare,
|
||||
HatchbackArchFlare,
|
||||
CeilingStripLight,
|
||||
CardboardBox,
|
||||
BarrierRail,
|
||||
BarrierRailEnd,
|
||||
TruckWheel,
|
||||
HatchbackWheelWideProfile,
|
||||
TruckWheelRigWithSteering = 249,
|
||||
TruckWheelRigNoSteering,
|
||||
HatchbackDriverSeat,
|
||||
HatchbackPassengerSeat,
|
||||
FormulaEngine,
|
||||
SmallGrass,
|
||||
SmallGrassRoad,
|
||||
GrassBridge,
|
||||
SmallGrassTurn,
|
||||
MediumGrassTurn,
|
||||
LargeGrassTurn,
|
||||
ExtraLargeGrassTurn,
|
||||
TruckWheelDouble,
|
||||
TruckWheelArch,
|
||||
TruckArchSingleFlare,
|
||||
WoodenDoorWithWindow,
|
||||
TyreBarrierCorner,
|
||||
TyreBarrierEdge,
|
||||
TyreBarrierCenter,
|
||||
AppleTree,
|
||||
AppleForestTree,
|
||||
FormulaWheel,
|
||||
FormulaWheelRear,
|
||||
AppleSapling,
|
||||
GrassHill,
|
||||
GrassHillInnerCorner,
|
||||
GrassHillOuterCorner,
|
||||
GrassRoadHill,
|
||||
FormulaSeat,
|
||||
SmallDirt,
|
||||
SmallDirtRoad,
|
||||
SmallDirtTurn,
|
||||
MediumDirtTurn,
|
||||
LargeDirtTurn,
|
||||
ExtraLargeDirtTurn,
|
||||
SmallGrid,
|
||||
MonsterTruckWheel,
|
||||
SmallGrassGridStart,
|
||||
SmallGrassRumbleStripRoad,
|
||||
SmallGrassRumbleStripEndRoad,
|
||||
SmallGrassStartLine,
|
||||
MonsterTruckEngine,
|
||||
DirtHill,
|
||||
DirtHillInnerCorner,
|
||||
DirtHillOuterCorner,
|
||||
BuildingWindowEdge,
|
||||
BuildingWindowCorner,
|
||||
BuildingWindowStraight,
|
||||
BuildingWindowTJunction,
|
||||
BuildingWindowCross,
|
||||
BuildingWindowEdgeSill,
|
||||
BuildingWindowCornerSill,
|
||||
BuildingWindowTJunctionSill,
|
||||
Broadleaf,
|
||||
ForestBroadleaf,
|
||||
AzaleaBush,
|
||||
AzaleaFlowers1,
|
||||
AzaleaFlowers2,
|
||||
TreeStump1,
|
||||
TreeStump2,
|
||||
FieldJuniper,
|
||||
ForestJuniper,
|
||||
JuniperSapling,
|
||||
JuniperSeedling,
|
||||
FieldRedMaple,
|
||||
RedMapleForest1,
|
||||
RedMapleForest2,
|
||||
RedMapleSapling,
|
||||
FieldWhiteSpruce,
|
||||
ForestWhiteSpruce,
|
||||
WhiteSpruceSapling,
|
||||
GirderBase,
|
||||
GirderStraight,
|
||||
GirderDiagonal,
|
||||
GirderCorner,
|
||||
PostBase,
|
||||
PostStraight,
|
||||
PostLShape,
|
||||
PostTJunction,
|
||||
PostCross,
|
||||
PostCorner,
|
||||
PostDiagonal,
|
||||
DirtRock1,
|
||||
DirtRock2,
|
||||
DirtRock3,
|
||||
DirtRock4,
|
||||
DirtRoadHill,
|
||||
WoodenPalette,
|
||||
ElderberryBush,
|
||||
BarrelCactus,
|
||||
KnapweedFlower,
|
||||
MarigoldFlowers,
|
||||
TrampledBushyBluestep,
|
||||
RoughGrass,
|
||||
DogRose,
|
||||
WesternSwordFern,
|
||||
BackyardGrass,
|
||||
ThickGrass,
|
||||
FireExtinguisher,
|
||||
DirtLowRamp,
|
||||
DirtTabletopRamp,
|
||||
MonsterTruckWheelRigNoSteering,
|
||||
MonsterTruckWheelRigWithSteering,
|
||||
MeadowCloudyDayAtmosphere,
|
||||
BarrierRailDiagonal,
|
||||
DirtHighRamp,
|
||||
GrassRock1,
|
||||
GrassRock2,
|
||||
GrassRock3,
|
||||
GrassRock4,
|
||||
GreenFieldsSunnyDayAtmosphere,
|
||||
RedMountainsDawnAtmosphere,
|
||||
HighFantasySunriseAtmosphere,
|
||||
/// <summary>
|
||||
/// The grid block used by the world editor, named Small Grid like the other one
|
||||
/// </summary>
|
||||
SmallGridInWorldEditor,
|
||||
CityDoubleCrossing,
|
||||
CityDoubleCrossroads,
|
||||
CitySmallDoubleJunction,
|
||||
CityDoubleJunction,
|
||||
CityDoubleToSingleJunction,
|
||||
CitySmallDoubleRoad,
|
||||
CityDoubleRoad,
|
||||
CitySmallDoubleTurn,
|
||||
CityLargeDoubleTurn,
|
||||
CitySmallSingleTurn,
|
||||
CityLargeSingleTurn,
|
||||
CitySingleJunction,
|
||||
CitySingleRoad,
|
||||
SegoeUITextblock,
|
||||
GravtracTextblock,
|
||||
HauserTextblock,
|
||||
TechnopollasTextblock,
|
||||
CityDoubleHillRoad,
|
||||
DiagonalTrackTile,
|
||||
DiagonalTrackTile2,
|
||||
DiagonalTransitionTile,
|
||||
SplitLane,
|
||||
BitBlock,
|
||||
Timer,
|
||||
CityNightAtmosphere,
|
||||
FloodLight,
|
||||
SoccerBall,
|
||||
CircularWallLight,
|
||||
BlueSkyAtmos,
|
||||
DirtToGrassTransitionTile = 393,
|
||||
DirtToGrassTransitionInnerTile,
|
||||
DirtToGrassTransitionOuterTile,
|
||||
DirtToGrassTransitionHillTile,
|
||||
DirtToGrassTransitionRoadTile,
|
||||
DirtHill2 = 399,
|
||||
DirtHill3,
|
||||
DirtInnerCorner2 = 402,
|
||||
DirtInnerCorner3,
|
||||
DirtOuterCorner2 = 405,
|
||||
DirtOuterCorner3,
|
||||
CityTarmacEdgeInner,
|
||||
CityTarmacEdgeOuter,
|
||||
CityTarmacEdgeRoad,
|
||||
CityTarmac,
|
||||
SmallGrassQuarterTile,
|
||||
CityToRacetrackTransition,
|
||||
HUDTimer,
|
||||
CentreHUD,
|
||||
Checkpoint,
|
||||
ScoreboardHUD,
|
||||
GameplaySFX,
|
||||
SpawnPoint,
|
||||
AreaSensor,
|
||||
WorldResetter,
|
||||
SmallJet,
|
||||
MediumJet,
|
||||
LargeJet,
|
||||
DistanceSensor,
|
||||
Stabilizer,
|
||||
ObjectID,
|
||||
ScoreToTechpointConversion,
|
||||
TeamScore,
|
||||
ScorePickupBlock,
|
||||
SportyHatchbackDriverSeat,
|
||||
SportyHatchbackPassengerSeat,
|
||||
FlamingExhaust = 433,
|
||||
SmokingExhaust,
|
||||
StreetLamp,
|
||||
Vector7HatchbackWheel,
|
||||
Vector7HatchbackWheelWideProfile,
|
||||
Vector7SedanWheel,
|
||||
Vector7SedanWideProfile,
|
||||
Vector7FormulaWheel,
|
||||
Vector7FormulaWheelRear,
|
||||
Vector7MonsterTruckWheel,
|
||||
Vector7TruckWheel,
|
||||
Vector7TruckWheelDouble,
|
||||
BusSeat,
|
||||
XLJet,
|
||||
XXLJet,
|
||||
ElectricSedanEngine,
|
||||
HeadlampIndicator,
|
||||
HeadlampSrip,
|
||||
HeadlampStripEdge,
|
||||
ConstantBlock,
|
||||
CounterBlock,
|
||||
SmallGridHill,
|
||||
SmallGridHillInnerCorner,
|
||||
SmallGridHillOuterCorner,
|
||||
AimingAxleServo,
|
||||
AimingHingeServo,
|
||||
WeaponDisabler,
|
||||
Vector7SmallJet,
|
||||
Vector7MediumJet,
|
||||
Vector7LargeJet,
|
||||
Vector7XLJet,
|
||||
Vector7XXLJet,
|
||||
APCWheelRigNoSteering,
|
||||
APCWheelRigWithSteering,
|
||||
APCWheel,
|
||||
APCSeat,
|
||||
APCEngine,
|
||||
DamageScoreBlock,
|
||||
KillScoreBlock,
|
||||
Autocannon = 480
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
using Svelto.ECS;
|
||||
using RobocraftX.Common;
|
||||
|
||||
using HarmonyLib;
|
||||
using Svelto.DataStructures;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// ExclusiveGroups and IDs used with blocks
|
||||
/// </summary>
|
||||
public static class BlockIdentifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// Blocks placed by the player
|
||||
/// </summary>
|
||||
public static FasterReadOnlyList<ExclusiveGroupStruct> OWNED_BLOCKS { get { return CommonExclusiveGroups.REAL_BLOCKS_GROUPS_DON_T_USE_IN_NEW_CODE; } }
|
||||
|
||||
/// <summary>
|
||||
/// Extra parts used in functional blocks
|
||||
/// </summary>
|
||||
public static ExclusiveGroup FUNCTIONAL_BLOCK_PARTS { get { return CommonExclusiveGroups.FUNCTIONAL_BLOCK_PART_GROUP; } }
|
||||
|
||||
/// <summary>
|
||||
/// Blocks which are disabled in Simulation mode
|
||||
/// </summary>
|
||||
public static ExclusiveGroup SIM_BLOCKS_DISABLED { get { return CommonExclusiveGroups.DISABLED_JOINTS_IN_SIM_GROUP; } }
|
||||
|
||||
//public static ExclusiveGroup SPAWN_POINTS { get { return CommonExclusiveGroups.SPAWN_POINTS_GROUP; } }
|
||||
|
||||
//public static ExclusiveGroup SPAWN_POINTS_DISABLED { get { return CommonExclusiveGroups.SPAWN_POINTS_DISABLED_GROUP; } }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the most recently placed block
|
||||
/// </summary>
|
||||
public static uint LatestBlockID {
|
||||
get
|
||||
{ //Need the private field as the property increments itself
|
||||
return ((uint) AccessTools.Field(typeof(CommonExclusiveGroups), "_nextBlockEntityID").GetValue(null)) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,40 @@ namespace TechbloxModdingAPI.Blocks
|
|||
RigidSteel,
|
||||
CarbonFiber,
|
||||
Plastic,
|
||||
Wood = 6
|
||||
Wood = 6,
|
||||
RigidSteelPainted,
|
||||
RigidSteelRustedPaint,
|
||||
RigidSteelHeavyRust,
|
||||
SteelBodyworkMetallicPaint,
|
||||
SteelBodyworkRustedPaint,
|
||||
SteelBodyworkHeavyRust,
|
||||
WoodVarnishedDark,
|
||||
Chrome,
|
||||
FenceChainLink,
|
||||
ConcreteUnpainted,
|
||||
Grid9x9,
|
||||
CeramicTileFloor,
|
||||
PlasticBumpy,
|
||||
PlasticDustySmeared,
|
||||
AluminiumGarageDoor,
|
||||
SteelRigidScratched,
|
||||
AluminiumBrushedTinted,
|
||||
AluminiumSheetStained,
|
||||
ConcretePaintedGrooves,
|
||||
PlasticSpecklySatin,
|
||||
SteelBodyworkPaintedChipped,
|
||||
WoodPainted,
|
||||
WoodRoughGrungy,
|
||||
Boundary,
|
||||
Emissive,
|
||||
AircraftPanelingRivetedPainted,
|
||||
AircraftPanelingRivetedMetallic,
|
||||
SteelBodyworkPearlescent,
|
||||
SteelBodyworkRadWrap,
|
||||
SteelBodyworkGlitter,
|
||||
BouncyRubber,
|
||||
BouncyRubberTieDye,
|
||||
BrickPainted,
|
||||
FuturisticPanelingRivetedPainted,
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ using DataLoader;
|
|||
using Svelto.Tasks;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
|
@ -46,16 +45,20 @@ namespace TechbloxModdingAPI.Blocks
|
|||
"Block ID enum matches the known block types.");
|
||||
}
|
||||
|
||||
private static Block[] blocks; // Store placed blocks as some blocks are already present as the workshop and the game save
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestBlockIDs()
|
||||
{
|
||||
float3 pos = new float3();
|
||||
foreach (BlockIDs id in Enum.GetValues(typeof(BlockIDs)))
|
||||
var values = Enum.GetValues(typeof(BlockIDs));
|
||||
blocks = new Block[values.Length - 1]; // Minus the invalid ID
|
||||
int i = 0;
|
||||
foreach (BlockIDs id in values)
|
||||
{
|
||||
if (id == BlockIDs.Invalid) continue;
|
||||
try
|
||||
{
|
||||
Block.PlaceNew(id, pos);
|
||||
blocks[i++] = Block.PlaceNew(id, pos);
|
||||
pos += 0.2f;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -71,38 +74,55 @@ namespace TechbloxModdingAPI.Blocks
|
|||
[APITestCase(TestType.EditMode)]
|
||||
public static IEnumerator<TaskContract> TestBlockProperties()
|
||||
{ //Uses the result of the previous test case
|
||||
var blocks = Game.CurrentGame().GetBlocksInGame();
|
||||
yield return Yield.It;
|
||||
if (blocks is null)
|
||||
yield break;
|
||||
for (var index = 0; index < blocks.Length; index++)
|
||||
{
|
||||
if (index % 50 == 0) yield return Yield.It; //The material or flipped status can only be changed 130 times per submission
|
||||
var block = blocks[index];
|
||||
if (!block.Exists) continue;
|
||||
foreach (var property in block.GetType().GetProperties())
|
||||
{
|
||||
//Includes specialised block properties
|
||||
if (property.SetMethod == null) continue;
|
||||
var testValues = new (Type, object, Predicate<object>)[]
|
||||
|
||||
bool3 Float3Compare(float3 a, float3 b)
|
||||
{ // From Unity reference code
|
||||
return math.abs(b - a) < math.max(
|
||||
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||
float.Epsilon * 8
|
||||
);
|
||||
}
|
||||
bool4 Float4Compare(float4 a, float4 b)
|
||||
{ // From Unity reference code
|
||||
return math.abs(b - a) < math.max(
|
||||
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||
float.Epsilon * 8
|
||||
);
|
||||
}
|
||||
|
||||
var testValues = new (Type, object, Predicate<(object Value, object Default)>)[]
|
||||
{
|
||||
//(type, default value, predicate or null for equality)
|
||||
(typeof(long), 3, null),
|
||||
(typeof(int), 4, null),
|
||||
(typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon),
|
||||
(typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon),
|
||||
(typeof(bool), true, obj => (bool) obj),
|
||||
(typeof(string), "Test", obj => (string) obj == "Test"), //String equality check
|
||||
(typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)),
|
||||
(typeof(double), 5.2f, t => Math.Abs((double) t.Value - (double) t.Default) < float.Epsilon),
|
||||
(typeof(float), 5.2f, t => Math.Abs((float) t.Value - (float) t.Default) < float.Epsilon),
|
||||
(typeof(bool), true, t => (bool) t.Value),
|
||||
(typeof(string), "Test", t => (string) t.Value == "Test"), //String equality check
|
||||
(typeof(float3), (float3) 20, t => math.all(Float3Compare((float3)t.Value, (float3)t.Default))),
|
||||
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
||||
(typeof(float4), (float4) 5, obj => math.all((float4) obj - 5 < (float4) float.Epsilon))
|
||||
(typeof(float4), (float4) 5, t => math.all(Float4Compare((float4)t.Value, (float4)t.Default)))
|
||||
};
|
||||
var propType = property.PropertyType;
|
||||
if (!propType.IsValueType) continue;
|
||||
(object valueToUse, Predicate<object> predicateToUse) = (null, null);
|
||||
(object valueToUse, Predicate<(object Value, object Default)> predicateToUse) = (null, null);
|
||||
foreach (var (type, value, predicate) in testValues)
|
||||
{
|
||||
if (type.IsAssignableFrom(propType))
|
||||
{
|
||||
valueToUse = value;
|
||||
predicateToUse = predicate ?? (obj => Equals(obj, value));
|
||||
predicateToUse = predicate ?? (t => Equals(t.Value, t.Default));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +131,7 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
var values = propType.GetEnumValues();
|
||||
valueToUse = values.GetValue(values.Length / 2);
|
||||
predicateToUse = val => Equals(val, valueToUse);
|
||||
predicateToUse = t => Equals(t.Value, t.Default);
|
||||
}
|
||||
|
||||
if (valueToUse == null)
|
||||
|
@ -120,10 +140,26 @@ namespace TechbloxModdingAPI.Blocks
|
|||
yield break;
|
||||
}
|
||||
|
||||
property.SetValue(block, valueToUse);
|
||||
object got = property.GetValue(block);
|
||||
try
|
||||
{
|
||||
property.SetValue(block, valueToUse);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Assert.Fail($"Failed to set property {block.GetType().Name}.{property.Name} to {valueToUse}\n{e}");
|
||||
}
|
||||
object got;
|
||||
try
|
||||
{
|
||||
got = property.GetValue(block);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Assert.Fail($"Failed to get property {block.GetType().Name}.{property.Name}\n{e}");
|
||||
continue;
|
||||
}
|
||||
var attr = property.GetCustomAttribute<TestValueAttribute>();
|
||||
if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got)))
|
||||
if (!predicateToUse((got, valueToUse)) && (attr == null || !Equals(attr.PossibleValue, got)))
|
||||
{
|
||||
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
|
||||
yield break;
|
||||
|
@ -134,14 +170,33 @@ namespace TechbloxModdingAPI.Blocks
|
|||
Assert.Pass("Setting all possible properties of all registered API block types succeeded.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static IEnumerator<TaskContract> TestDefaultValue()
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{ //Tests shared defaults
|
||||
var block = Block.PlaceNew(BlockIDs.Cube, 1);
|
||||
while (!block.Exists)
|
||||
yield return Yield.It;
|
||||
block.Remove();
|
||||
while (block.Exists)
|
||||
yield return Yield.It;
|
||||
if(!Assert.Equal(block.Position, default,
|
||||
$"Block position default value {block.Position} is incorrect, should be 0.",
|
||||
$"Block position default value {block.Position} matches default."))
|
||||
yield break;
|
||||
block.Position = 4;
|
||||
}
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestDampedSpring()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.DampedSpring, Unity.Mathematics.float3.zero + 1);
|
||||
DampedSpring b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = (DampedSpring) newBlock; }, "Casting block to DampedSpring raised an exception: ", "Casting block to DampedSpring completed without issue.");
|
||||
if (!Assert.CloseTo(b.SpringFrequency, 30f, $"DampedSpring.SpringFrequency {b.SpringFrequency} does not equal default value, possibly because it failed silently.", "DampedSpring.SpringFrequency is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.Damping, 30f, $"DampedSpring.Damping {b.Damping} does not equal default value, possibly because it failed silently.", "DampedSpring.Damping is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.Stiffness, 1f, $"DampedSpring.Stiffness {b.Stiffness} does not equal default value, possibly because it failed silently.", "DampedSpring.Stiffness is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.Damping, 0.1f, $"DampedSpring.Damping {b.Damping} does not equal default value, possibly because it failed silently.", "DampedSpring.Damping is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.MaxExtension, 0.3f, $"DampedSpring.MaxExtension {b.MaxExtension} does not equal default value, possibly because it failed silently.", "DampedSpring.MaxExtension is close enough to default.")) return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,47 +1,71 @@
|
|||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public class DampedSpring : Block
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class DampedSpring : SignalingBlock
|
||||
{
|
||||
public DampedSpring(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public DampedSpring(uint id) : base(new EGID(id, CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The spring frequency.
|
||||
/// Constructs a(n) DampedSpring object representing an existing block.
|
||||
/// </summary>
|
||||
public float SpringFrequency
|
||||
public DampedSpring(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springFrequency;
|
||||
|
||||
set => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springFrequency = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The spring's maximum damping force.
|
||||
/// Constructs a(n) DampedSpring object representing an existing block.
|
||||
/// </summary>
|
||||
public DampedSpring(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DampedSpring's Stiffness property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Stiffness
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).stiffness;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).stiffness = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DampedSpring's Damping property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Damping
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springDamping;
|
||||
|
||||
set => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springDamping = value;
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).damping;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).damping = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The spring's maximum extension.
|
||||
/// Gets or sets the DampedSpring's MaxExtension property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaxExtension
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).maxExtent;
|
||||
|
||||
set => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).maxExtent = value;
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.DampedSpringReadOnlyStruct>(this).maxExtent;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.DampedSpringReadOnlyStruct>(this).maxExtent = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,382 @@
|
|||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Techblox.EngineBlock;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Engine : SignalingBlock
|
||||
{
|
||||
public Engine(EGID id) : base(id)
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Engine object representing an existing block.
|
||||
/// </summary>
|
||||
public Engine(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
public Engine(uint id) : base(new EGID(id, CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP))
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Engine object representing an existing block.
|
||||
/// </summary>
|
||||
public Engine(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*/// <summary> - TODO: Internal struct access
|
||||
/// Gets or sets the Engine's On property. May not be saved.
|
||||
/// </summary>
|
||||
public bool On
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).engineOn;
|
||||
set => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).engineOn = value;
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int TorqueDirection
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).torqueDirection;
|
||||
set => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).torqueDirection = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentGear property. May not be saved.
|
||||
/// </summary>
|
||||
public int CurrentGear
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).currentGear;
|
||||
set => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).currentGear = value;
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's GearChangeCountdown property. May not be saved.
|
||||
/// </summary>
|
||||
public float GearChangeCountdown
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentRpmAV property. May not be saved.
|
||||
/// </summary>
|
||||
public float CurrentRpmAV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentRpmLV property. May not be saved.
|
||||
/// </summary>
|
||||
public float CurrentRpmLV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TargetRpmAV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TargetRpmAV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TargetRpmLV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TargetRpmLV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentTorque property. May not be saved.
|
||||
/// </summary>
|
||||
public float CurrentTorque
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TotalWheelVelocityAV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TotalWheelVelocityAV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TotalWheelVelocityLV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TotalWheelVelocityLV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TotalWheelCount property. May not be saved.
|
||||
/// </summary>
|
||||
public int TotalWheelCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's LastGearUpInput property. May not be saved.
|
||||
/// </summary>
|
||||
public bool LastGearUpInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's LastGearDownInput property. May not be saved.
|
||||
/// </summary>
|
||||
public bool LastGearDownInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's ManualToAutoGearCoolOffCounter property. May not be saved.
|
||||
/// </summary>
|
||||
public float ManualToAutoGearCoolOffCounter
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's Load property. May not be saved.
|
||||
/// </summary>
|
||||
public float Load
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's Power property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Power
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).power;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).power = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's AutomaticGears property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool AutomaticGears
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).automaticGears;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).automaticGears = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's GearChangeTime property. May not be saved.
|
||||
/// </summary>
|
||||
public float GearChangeTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearChangeTime;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearChangeTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's MinRpm property. May not be saved.
|
||||
/// </summary>
|
||||
public float MinRpm
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).minRpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).minRpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's MaxRpm property. May not be saved.
|
||||
/// </summary>
|
||||
public float MaxRpm
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Engine's GearDownRpms property. May not be saved.
|
||||
/// </summary>
|
||||
public float[] GearDownRpms
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearDownRpms.ToManagedArray<float>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's GearUpRpm property. May not be saved.
|
||||
/// </summary>
|
||||
public float GearUpRpm
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearUpRpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearUpRpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's MaxRpmChange property. May not be saved.
|
||||
/// </summary>
|
||||
public float MaxRpmChange
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpmChange;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpmChange = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's ManualToAutoGearCoolOffTime property. May not be saved.
|
||||
/// </summary>
|
||||
public float ManualToAutoGearCoolOffTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,8 +48,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
pickedBlock.pickedBlockEntityID = sourceID;
|
||||
pickedBlock.placedBlockEntityID = targetID;
|
||||
pickedBlock.placedBlockTweaksMustCopy = true;
|
||||
if (entitiesDB.Exists<DBEntityStruct>(pickedBlock.pickedBlockEntityID)
|
||||
&& entitiesDB.Exists<DBEntityStruct>(pickedBlock.placedBlockEntityID))
|
||||
if (entitiesDB.Exists<BlockTagEntityStruct>(pickedBlock.pickedBlockEntityID)
|
||||
&& entitiesDB.Exists<BlockTagEntityStruct>(pickedBlock.placedBlockEntityID))
|
||||
{
|
||||
copyFromBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock});
|
||||
|
||||
|
@ -60,7 +60,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
copyToBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock});
|
||||
|
||||
ExclusiveGroupStruct group = WiresExclusiveGroups.WIRES_COPY_GROUP + playerID;
|
||||
ExclusiveGroupStruct group = BuildModeWiresGroups.WIRES_COPY_GROUP + playerID;
|
||||
copyWireToBlock.Invoke(Patch.createWireEngine, new object[] {group, pickedBlock.ID});
|
||||
|
||||
pickedBlock.placedBlockTweaksMustCopy = false;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
|
||||
using Gamecraft.ColourPalette;
|
||||
using Gamecraft.TimeRunning;
|
||||
using Gamecraft.Wires;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
|
@ -12,11 +13,16 @@ using RobocraftX.Rendering.GPUI;
|
|||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Svelto.ECS.Experimental;
|
||||
using Svelto.ECS.Hybrid;
|
||||
using Techblox.BuildingDrone;
|
||||
using Techblox.ObjectIDBlockServer;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
using PrefabsID = RobocraftX.Common.PrefabsID;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
|
@ -41,22 +47,21 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public Block[] GetConnectedBlocks(EGID blockID)
|
||||
{
|
||||
if (!BlockExists(blockID)) return new Block[0];
|
||||
if (!BlockExists(blockID)) return Array.Empty<Block>();
|
||||
Stack<EGID> cubeStack = new Stack<EGID>();
|
||||
FasterList<EGID> cubes = new FasterList<EGID>(10);
|
||||
var coll = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var (ecoll, _) in coll)
|
||||
foreach (var ((ecoll, count), _) in coll)
|
||||
{
|
||||
var ecollB = ecoll.ToBuffer();
|
||||
for(int i = 0; i < ecoll.count; i++)
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
ref var conn = ref ecollB.buffer[i];
|
||||
conn.isProcessed = false;
|
||||
ecoll[i].areConnectionsAssigned = false;
|
||||
}
|
||||
}
|
||||
|
||||
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
|
||||
(in GridConnectionsEntityStruct g) => { return false; });
|
||||
|
||||
//TODO: GetConnectedCubesUtility
|
||||
/*ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
|
||||
(in GridConnectionsEntityStruct _) => false);*/
|
||||
|
||||
var ret = new Block[cubes.count];
|
||||
for (int i = 0; i < cubes.count; i++)
|
||||
|
@ -67,7 +72,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
public float4 ConvertBlockColor(byte index) => index == byte.MaxValue
|
||||
? new float4(-1f, -1f, -1f, -1f)
|
||||
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
|
||||
CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
||||
ColourPaletteExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
||||
|
||||
public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
|
@ -76,6 +81,10 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public ref T GetBlockInfo<T>(Block block) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
#if DEBUG
|
||||
if (!typeof(BlockTagEntityStruct).IsAssignableFrom(typeof(T)) && block.Exists && block.InitData.Valid)
|
||||
throw new ArgumentException("The block exists but the init data has not been removed!");
|
||||
#endif
|
||||
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
||||
}
|
||||
|
||||
|
@ -89,13 +98,35 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
||||
}
|
||||
|
||||
internal object GetBlockInfo(Block block, Type type, string name)
|
||||
{
|
||||
var opt = AccessTools.Method(typeof(NativeApiExtensions), "QueryEntityOptional",
|
||||
new[] { typeof(EntitiesDB), typeof(EcsObjectBase), typeof(ExclusiveGroupStruct) }, new[] { type })
|
||||
.Invoke(null, new object[] { entitiesDB, block, null });
|
||||
var str = AccessTools.Property(opt.GetType(), "Value").GetValue(opt);
|
||||
return AccessTools.Field(str.GetType(), name).GetValue(str);
|
||||
}
|
||||
|
||||
internal void SetBlockInfo(Block block, Type type, string name, object value)
|
||||
{
|
||||
var opt = AccessTools.Method(typeof(BlockEngine), "GetBlockInfoOptional", generics: new[] { type })
|
||||
.Invoke(this, new object[] { block });
|
||||
var prop = AccessTools.Property(opt.GetType(), "Value");
|
||||
var str = prop.GetValue(opt);
|
||||
AccessTools.Field(str.GetType(), name).SetValue(str, value);
|
||||
prop.SetValue(opt, str);
|
||||
}
|
||||
|
||||
public void UpdateDisplayedBlock(EGID id)
|
||||
{
|
||||
if (!BlockExists(id)) return;
|
||||
var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
|
||||
var rot = entitiesDB.QueryEntity<RotationEntityStruct>(id);
|
||||
var scale = entitiesDB.QueryEntity<ScalingEntityStruct>(id);
|
||||
entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix = float4x4.TRS(pos.position, rot.rotation, scale.scale);
|
||||
var skew = entitiesDB.QueryEntity<SkewComponent>(id);
|
||||
entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix =
|
||||
math.mul(float4x4.TRS(pos.position, rot.rotation, scale.scale), skew.skewMatrix);
|
||||
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(id); // Signal a prefab change so it updates the render buffers
|
||||
}
|
||||
|
||||
internal void UpdatePrefab(Block block, byte material, bool flipped)
|
||||
|
@ -106,65 +137,73 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
: uint.MaxValue;
|
||||
if (prefabAssetID == uint.MaxValue)
|
||||
{
|
||||
if (entitiesDB.QueryEntityOptional<DBEntityStruct>(block)) //The block exists
|
||||
if (entitiesDB.QueryEntityOptional<BlockTagEntityStruct>(block)) //The block exists
|
||||
throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game
|
||||
return;
|
||||
}
|
||||
|
||||
uint prefabId =
|
||||
PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, material, 1, flipped);
|
||||
PrefabsID.GetOrAddPrefabID((ushort) prefabAssetID, material, 1, flipped);
|
||||
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
|
||||
if (block.Exists)
|
||||
entitiesDB.PublishEntityChange<GFXPrefabEntityStructGPUI>(block.Id);
|
||||
{
|
||||
entitiesDB.PublishEntityChangeDelayed<CubeMaterialStruct>(block.Id);
|
||||
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(block.Id);
|
||||
|
||||
ref BuildingActionComponent local =
|
||||
ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
|
||||
.GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
|
||||
local.buildAction = BuildAction.ChangeMaterial;
|
||||
local.targetPosition = block.Position;
|
||||
this.entitiesDB.PublishEntityChangeDelayed<BuildingActionComponent>(local.ID);
|
||||
}
|
||||
//Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
|
||||
/*Console.WriteLine("Materials:\n" + FullGameFields._dataDb.GetValues<MaterialPropertiesData>()
|
||||
.Select(kv => $"{kv.Key}: {((MaterialPropertiesData) kv.Value).Name}")
|
||||
.Aggregate((a, b) => a + "\n" + b));*/
|
||||
}
|
||||
|
||||
public void UpdateBlockColor(EGID id)
|
||||
{
|
||||
entitiesDB.PublishEntityChangeDelayed<ColourParameterEntityStruct>(id);
|
||||
}
|
||||
|
||||
public bool BlockExists(EGID blockID)
|
||||
{
|
||||
return entitiesDB.Exists<DBEntityStruct>(blockID);
|
||||
return entitiesDB.Exists<BlockTagEntityStruct>(blockID);
|
||||
}
|
||||
|
||||
public SimBody[] GetSimBodiesFromID(byte id)
|
||||
{
|
||||
var ret = new FasterList<SimBody>(4);
|
||||
var oide = entitiesDB.QueryEntities<ObjectIdEntityStruct>();
|
||||
var oids = entitiesDB.QueryEntitiesOptional<ObjectIdEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
|
||||
EGIDMapper<GridConnectionsEntityStruct>? connections = null;
|
||||
foreach (var ((oids, count), _) in oide)
|
||||
foreach (var oid in oids)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref ObjectIdEntityStruct oid = ref oids[i];
|
||||
if (oid.objectId != id) continue;
|
||||
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
|
||||
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.ID.groupID);
|
||||
var rid = connections.Value.Entity(oid.ID.entityID).machineRigidBodyId;
|
||||
foreach (var rb in ret)
|
||||
{
|
||||
if (rb.Id.entityID == rid)
|
||||
goto DUPLICATE; //Multiple Object Identifiers on one rigid body
|
||||
}
|
||||
|
||||
ret.Add(new SimBody(rid));
|
||||
DUPLICATE: ;
|
||||
if (oid.Get().objectId != id) continue;
|
||||
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
|
||||
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.EGID.groupID);
|
||||
//var rid = connections.Value.Entity(tag.ID.entityID).machineRigidBodyId;
|
||||
/*foreach (var rb in ret) - TODO
|
||||
{
|
||||
if (rb.Id.entityID == rid)
|
||||
goto DUPLICATE; //Multiple Object Identifiers on one rigid body
|
||||
}
|
||||
}
|
||||
|
||||
ret.Add(new SimBody(rid));
|
||||
DUPLICATE: ;*/
|
||||
}
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public SimBody[] GetConnectedSimBodies(uint id)
|
||||
{
|
||||
var joints = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP).ToBuffer();
|
||||
var (joints, count) = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP);
|
||||
var list = new FasterList<SimBody>(4);
|
||||
for (int i = 0; i < joints.count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref var joint = ref joints.buffer[i];
|
||||
ref var joint = ref joints[i];
|
||||
if (joint.isBroken) continue;
|
||||
if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB));
|
||||
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));
|
||||
/*if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); - TODO:
|
||||
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));*/
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
|
@ -174,14 +213,13 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
var bodies = new HashSet<uint>();
|
||||
foreach (var (coll, _) in groups)
|
||||
foreach (var ((coll, count), _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
if (conn.clusterId == cid)
|
||||
bodies.Add(conn.machineRigidBodyId);
|
||||
var conn = coll[index];
|
||||
/*if (conn.clusterId == cid) - TODO
|
||||
bodies.Add(conn.machineRigidBodyId);*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,10 +228,10 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public EGID? FindBlockEGID(uint id)
|
||||
{
|
||||
var groups = entitiesDB.FindGroups<DBEntityStruct>();
|
||||
var groups = entitiesDB.FindGroups<BlockTagEntityStruct>();
|
||||
foreach (ExclusiveGroupStruct group in groups)
|
||||
{
|
||||
if (entitiesDB.Exists<DBEntityStruct>(id, group))
|
||||
if (entitiesDB.Exists<BlockTagEntityStruct>(id, group))
|
||||
return new EGID(id, group);
|
||||
}
|
||||
|
||||
|
@ -203,15 +241,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
public Cluster GetCluster(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var (coll, _) in groups)
|
||||
foreach (var ((coll, count), _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
var conn = coll[index];
|
||||
//Static blocks don't have a cluster ID but the cluster destruction manager should have one
|
||||
if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue)
|
||||
return new Cluster(conn.clusterId);
|
||||
/*if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue) - TODO:
|
||||
return new Cluster(conn.clusterId);*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,27 +257,36 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public Block[] GetBodyBlocks(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
var groups = entitiesDB.FindGroups<GridConnectionsEntityStruct>();
|
||||
groups = new QueryGroups(groups).Except(CommonExclusiveGroups.DISABLED_JOINTS_IN_SIM_GROUP).Evaluate().result;
|
||||
var set = new HashSet<Block>();
|
||||
foreach (var (coll, _) in groups)
|
||||
foreach (var ((coll, tags, count), _) in entitiesDB.QueryEntities<GridConnectionsEntityStruct, BlockTagEntityStruct>(groups))
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
if (conn.machineRigidBodyId == sbid)
|
||||
set.Add(Block.New(conn.ID));
|
||||
var conn = coll[index];
|
||||
/*if (conn.machineRigidBodyId == sbid) - TODO
|
||||
set.Add(Block.New(tags[index].ID));*/
|
||||
}
|
||||
}
|
||||
|
||||
return set.ToArray();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public EntitiesDB GetEntitiesDB()
|
||||
public ObjectID[] GetObjectIDsFromID(byte id)
|
||||
{
|
||||
return entitiesDB;
|
||||
if (!entitiesDB.HasAny<ObjectIDTweakableComponent>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP))
|
||||
return Array.Empty<ObjectID>();
|
||||
|
||||
var ret = new FasterList<ObjectID>(4);
|
||||
var oids = entitiesDB.QueryEntitiesOptional<ObjectIDTweakableComponent>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
|
||||
foreach (var oid in oids)
|
||||
{
|
||||
if (oid.Get().objectIDToTrigger == id)
|
||||
ret.Add(new ObjectID(oid.EGID));
|
||||
}
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -10,8 +10,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
{
|
||||
public class BlockEventsEngine : IReactionaryEngine<BlockTagEntityStruct>
|
||||
{
|
||||
public event EventHandler<BlockPlacedRemovedEventArgs> Placed;
|
||||
public event EventHandler<BlockPlacedRemovedEventArgs> Removed;
|
||||
public WrappedHandler<BlockPlacedRemovedEventArgs> Placed;
|
||||
public WrappedHandler<BlockPlacedRemovedEventArgs> Removed;
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
|
@ -26,21 +26,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
public string Name { get; } = "TechbloxModdingAPIBlockEventsEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
private bool shouldAddRemove;
|
||||
public void Add(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||
{
|
||||
if (!(shouldAddRemove = !shouldAddRemove))
|
||||
return;
|
||||
ExceptionUtil.InvokeEvent(Placed, this,
|
||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
Placed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
}
|
||||
|
||||
public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||
{
|
||||
if (!(shouldAddRemove = !shouldAddRemove))
|
||||
return;
|
||||
ExceptionUtil.InvokeEvent(Removed, this,
|
||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
Removed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +42,6 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
public EGID ID;
|
||||
private Block block;
|
||||
|
||||
public Block Block => block ?? (block = Block.New(ID));
|
||||
public Block Block => block ??= Block.New(ID);
|
||||
}
|
||||
}
|
|
@ -1,20 +1,27 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Gamecraft.GUI.Blueprints;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||
using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations;
|
||||
using RobocraftX.Physics;
|
||||
using RobocraftX.Rendering;
|
||||
using RobocraftX.Rendering.GPUI;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.DataStructures;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Svelto.ECS.Native;
|
||||
using Svelto.ECS.Serialization;
|
||||
using Techblox.Blocks.Connections;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
@ -42,7 +49,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
private static readonly MethodInfo SerializeGhostBlueprint =
|
||||
AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities");
|
||||
|
||||
private static NativeEntityRemove nativeRemove;
|
||||
private static NativeEntityRemove nativeBlockRemove;
|
||||
private static NativeEntityRemove nativeConnectionRemove;
|
||||
private static MachineGraphConnectionEntityFactory connectionFactory;
|
||||
private static IEntityFunctions entityFunctions;
|
||||
private static ClipboardSerializationDataResourceManager clipboardManager;
|
||||
|
@ -82,8 +90,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public void RemoveBlockGroup(int id)
|
||||
{
|
||||
BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeRemove,
|
||||
connectionFactory, default).Complete();
|
||||
BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeBlockRemove,
|
||||
nativeConnectionRemove, connectionFactory, default).Complete();
|
||||
}
|
||||
|
||||
public int CreateBlockGroup(float3 position, quaternion rotation)
|
||||
|
@ -160,10 +168,15 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
private void BuildGhostBlueprint(ICollection<Block> blocks, float3 pos, quaternion rot, uint playerID)
|
||||
{
|
||||
GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions);
|
||||
var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerID,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
|
||||
if (!bssesopt)
|
||||
return;
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB,
|
||||
BuildGhostBlueprintFactory, false);
|
||||
BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference,
|
||||
FullGameFields._managers.blockLabelResourceManager);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,6 +260,77 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
{
|
||||
clipboardManager.DecrementRefCount(blueprintID);
|
||||
}
|
||||
|
||||
|
||||
//GhostChildUtility.BuildGhostChild
|
||||
public Block BuildGhostChild()
|
||||
{
|
||||
var sourceId = new EGID(Player.LocalPlayer.Id, GHOST_BLOCKS_ENABLED.Group);
|
||||
var positionEntityStruct = entitiesDB.QueryEntity<PositionEntityStruct>(sourceId);
|
||||
var rotationEntityStruct = entitiesDB.QueryEntity<RotationEntityStruct>(sourceId);
|
||||
var scalingEntityStruct = entitiesDB.QueryEntity<ScalingEntityStruct>(sourceId);
|
||||
var dbStruct = entitiesDB.QueryEntity<DBEntityStruct>(sourceId);
|
||||
var colliderStruct = entitiesDB.QueryEntity<ColliderAabb>(sourceId);
|
||||
var colorStruct = entitiesDB.QueryEntity<ColourParameterEntityStruct>(sourceId);
|
||||
uint ghostChildBlockId = CommonExclusiveGroups.GetNewGhostChildBlockID();
|
||||
var ghostEntityReference = GhostBlockUtils.GetGhostEntityReference(sourceId.entityID, entitiesDB);
|
||||
var entityInitializer = BuildGhostBlueprintFactory.Build(
|
||||
new EGID(ghostChildBlockId, BoxSelectExclusiveGroups.GhostChildEntitiesExclusiveGroup), /*dbStruct.DBID*/ (uint)BlockIDs.Cube,
|
||||
FullGameFields._managers.blockLabelResourceManager);
|
||||
entityInitializer.Init(dbStruct);
|
||||
entityInitializer.Init(new GFXPrefabEntityStructGPUI(
|
||||
PrefabsID.GetOrAddPrefabID((ushort)entityInitializer.Get<PrefabAssetIDComponent>().prefabAssetID,
|
||||
entitiesDB.QueryEntity<CubeMaterialStruct>(sourceId).materialId, 7,
|
||||
FlippedBlockUtils.IsFlipped(in scalingEntityStruct.scale)), true));
|
||||
entityInitializer.Init(entitiesDB.QueryEntity<CubeMaterialStruct>(sourceId));
|
||||
entityInitializer.Init(new GhostParentEntityStruct
|
||||
{
|
||||
ghostBlockParentEntityReference = ghostEntityReference,
|
||||
ownerMustSerializeOnAdd = false
|
||||
});
|
||||
entityInitializer.Init(colorStruct);
|
||||
entityInitializer.Init(colliderStruct);
|
||||
entityInitializer.Init(new RigidBodyEntityStruct
|
||||
{
|
||||
position = positionEntityStruct.position,
|
||||
rotation = rotationEntityStruct.rotation
|
||||
});
|
||||
entityInitializer.Init(new ScalingEntityStruct
|
||||
{
|
||||
scale = scalingEntityStruct.scale
|
||||
});
|
||||
entityInitializer.Init(new LocalTransformEntityStruct
|
||||
{
|
||||
position = positionEntityStruct.position,
|
||||
rotation = rotationEntityStruct.rotation
|
||||
});
|
||||
entityInitializer.Init(new RotationEntityStruct
|
||||
{
|
||||
rotation = rotationEntityStruct.rotation
|
||||
});
|
||||
entityInitializer.Init(new PositionEntityStruct
|
||||
{
|
||||
position = positionEntityStruct.position
|
||||
});
|
||||
entityInitializer.Init(new SkewComponent
|
||||
{
|
||||
skewMatrix = entitiesDB.QueryEntity<SkewComponent>(sourceId).skewMatrix
|
||||
});
|
||||
entityInitializer.Init(new BlockPlacementInfoStruct
|
||||
{
|
||||
placedByBuildingDrone = entitiesDB
|
||||
.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(Player.LocalPlayer.Id,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)).Get().buildingDroneReference
|
||||
});
|
||||
entityInitializer.Init(new GridRotationStruct
|
||||
{
|
||||
position = float3.zero,
|
||||
rotation = quaternion.identity
|
||||
});
|
||||
var block = Block.New(entityInitializer.EGID);
|
||||
block.InitData = entityInitializer;
|
||||
return block;
|
||||
}
|
||||
|
||||
public string Name { get; } = "TechbloxModdingAPIBlueprintGameEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
|
@ -257,7 +341,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
public static void Prefix(IEntityFunctions entityFunctions,
|
||||
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;
|
||||
BlueprintEngine.entityFunctions = entityFunctions;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.UECS;
|
||||
using RobocraftX.DOTS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
|
@ -7,6 +7,7 @@ using Unity.Transforms;
|
|||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
|
@ -40,7 +41,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<UECSPhysicsEntityStruct>(block);
|
||||
var phyStruct = this.entitiesDB.QueryEntityOptional<DOTSPhysicsEntityStruct>(block);
|
||||
// main (persistent) position
|
||||
posStruct.position = vector;
|
||||
// placement grid position
|
||||
|
@ -48,15 +49,15 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
// rendered position
|
||||
transStruct.position = vector;
|
||||
// collision position
|
||||
if (phyStruct.ID != EGID.Empty)
|
||||
if (phyStruct)
|
||||
{ //It exists
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity, new Translation
|
||||
{
|
||||
Value = posStruct.position
|
||||
});
|
||||
}
|
||||
|
||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).isProcessed = false;
|
||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
|
||||
return posStruct.position;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ using HarmonyLib;
|
|||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||
using RobocraftX.Rendering;
|
||||
using RobocraftX.Rendering.GPUI;
|
||||
using Svelto.ECS;
|
||||
|
@ -15,6 +16,7 @@ using Unity.Mathematics;
|
|||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
|
@ -53,11 +55,11 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
|
||||
DBEntityStruct dbEntity = new DBEntityStruct {DBID = block};
|
||||
|
||||
EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, block); //The ghost block index is only used for triggers
|
||||
EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.blockIDGeneratorClient.Next(), block); //The ghost block index is only used for triggers
|
||||
uint prefabAssetID = structInitializer.Has<PrefabAssetIDComponent>()
|
||||
? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID
|
||||
: throw new BlockException("Prefab asset ID not found!"); //Set by the game
|
||||
uint prefabId = PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, (byte) BlockMaterial.SteelBodywork, 1, false);
|
||||
uint prefabId = PrefabsID.GetOrAddPrefabID((ushort) prefabAssetID, (byte) BlockMaterial.SteelBodywork, 1, false);
|
||||
structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId));
|
||||
structInitializer.Init(dbEntity);
|
||||
structInitializer.Init(new PositionEntityStruct {position = position});
|
||||
|
@ -70,10 +72,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
});
|
||||
structInitializer.Init(new UniformBlockScaleEntityStruct {scaleFactor = 1});
|
||||
structInitializer.Get<CubeMaterialStruct>().materialId = (byte) BlockMaterial.SteelBodywork;
|
||||
var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerId,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
|
||||
if (!bssesopt)
|
||||
throw new BlockException("Invalid player ID specified for block placement");
|
||||
structInitializer.Init(new BlockPlacementInfoStruct
|
||||
{
|
||||
loadedFromDisk = false,
|
||||
placedBy = playerId,
|
||||
placedByBuildingDrone = bssesopt.Get().buildingDroneReference,
|
||||
triggerAutoWiring = autoWire && structInitializer.Has<BlockPortsStruct>()
|
||||
});
|
||||
|
||||
|
|
|
@ -1,45 +1,61 @@
|
|||
using System.Reflection;
|
||||
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.GroupTags;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Native;
|
||||
using Techblox.Blocks.Connections;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Allocator = Unity.Collections.Allocator;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class RemovalEngine : IApiEngine
|
||||
public class RemovalEngine : IApiEngine, IDeterministicTimeStopped
|
||||
{
|
||||
private static IEntityFunctions _entityFunctions;
|
||||
private static MachineGraphConnectionEntityFactory _connectionFactory;
|
||||
private NativeHashSet<ulong> removedConnections;
|
||||
|
||||
public bool RemoveBlock(EGID target)
|
||||
{
|
||||
if (!entitiesDB.Exists<MachineGraphConnectionsEntityStruct>(target))
|
||||
return false;
|
||||
var connections = entitiesDB.QueryEntity<MachineGraphConnectionsEntityStruct>(target);
|
||||
var groups = entitiesDB.FindGroups<MachineGraphConnectionsEntityStruct>();
|
||||
var connStructMapper =
|
||||
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(groups);
|
||||
for (int i = connections.connections.Count<MachineConnectionStruct>() - 1; i >= 0; i--)
|
||||
_connectionFactory.RemoveConnection(connections, i, connStructMapper);
|
||||
using var connStructMapper =
|
||||
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(GroupTag<BLOCK_TAG>.Groups,
|
||||
Svelto.Common.Allocator.Temp);
|
||||
if (entitiesDB.TryQueryNativeMappedEntities<MachineConnectionComponent>(
|
||||
ConnectionsExclusiveGroups.MACHINE_CONNECTION_GROUP, out var mapper))
|
||||
{
|
||||
BlockGroupUtility.RemoveBlockConnections(target, removedConnections, _connectionFactory,
|
||||
connStructMapper, mapper, entitiesDB.GetEntityReferenceMap(), _entityFunctions);
|
||||
}
|
||||
|
||||
_entityFunctions.RemoveEntity<BlockEntityDescriptor>(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
removedConnections = new(2000, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
removedConnections.Dispose();
|
||||
}
|
||||
|
||||
public string Name { get; } = "TechbloxModdingAPIRemovalGameEngine";
|
||||
public string Name => "TechbloxModdingAPIRemovalGameEngine";
|
||||
public string name => Name;
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
|
@ -59,5 +75,12 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine").GetConstructors()[0];
|
||||
}
|
||||
}
|
||||
|
||||
public JobHandle DeterministicStep(in float deltaTime, JobHandle inputDeps)
|
||||
{
|
||||
if (removedConnections.IsCreated)
|
||||
removedConnections.Clear();
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.UECS;
|
||||
using RobocraftX.DOTS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
|
@ -7,6 +7,7 @@ using UnityEngine;
|
|||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
|
@ -40,7 +41,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<UECSPhysicsEntityStruct>(block);
|
||||
var phyStruct = this.entitiesDB.QueryEntityOptional<DOTSPhysicsEntityStruct>(block);
|
||||
// main (persistent) rotation
|
||||
Quaternion newRotation = rotStruct.rotation;
|
||||
newRotation.eulerAngles = vector;
|
||||
|
@ -50,16 +51,18 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
// rendered rotation
|
||||
transStruct.rotation = newRotation;
|
||||
// collision rotation
|
||||
if (phyStruct.ID != EGID.Empty)
|
||||
if (phyStruct)
|
||||
{ //It exists
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity,
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity,
|
||||
new Unity.Transforms.Rotation
|
||||
{
|
||||
Value = rotStruct.rotation
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.UECS;
|
||||
using RobocraftX.DOTS;
|
||||
using Svelto.ECS;
|
||||
using Unity.Entities;
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
{
|
||||
public class ScalingEngine : IApiEngine
|
||||
{
|
||||
private static IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> physicsEngine;
|
||||
private static IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> physicsEngine;
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
|
@ -34,16 +34,16 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
if (_entityManager == default)
|
||||
_entityManager = FullGameFields._physicsWorld.EntityManager;
|
||||
//Assuming the block exists
|
||||
var entity = entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(egid).uecsEntity;
|
||||
var pes = new UECSPhysicsEntityCreationStruct();
|
||||
physicsEngine.Add(ref pes, egid); //Create new UECS entity
|
||||
var entity = entitiesDB.QueryEntity<DOTSPhysicsEntityStruct>(egid).dotsEntity;
|
||||
var pes = new DOTSPhysicsEntityCreationStruct();
|
||||
physicsEngine.Add(ref pes, egid); //Create new DOTS entity
|
||||
_entityManager.DestroyEntity(entity);
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
class PhysicsEnginePatch
|
||||
{
|
||||
static void Postfix(IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> __instance)
|
||||
static void Postfix(IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> __instance)
|
||||
{
|
||||
physicsEngine = __instance;
|
||||
Logging.MetaDebugLog("Physics engine injected.");
|
||||
|
@ -51,7 +51,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
static MethodBase TargetMethod(Harmony instance)
|
||||
{
|
||||
return AccessTools.Method("RobocraftX.StateSync.HandleUECSPhysicEntitiesWithPrefabCreationEngine" +
|
||||
return AccessTools.Method("RobocraftX.StateSync.HandleDOTSPhysicEntitiesWithPrefabCreationEngine" +
|
||||
":Ready");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using Svelto.ECS;
|
|||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
|
@ -41,19 +42,18 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
// implementations for block wiring
|
||||
|
||||
public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
|
||||
public (WireEntityStruct Wire, EGID ID) CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
|
||||
{
|
||||
EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup<WiresGroup>.Group);
|
||||
EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
|
||||
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
|
||||
wireInitializer.Init(new WireEntityStruct
|
||||
{
|
||||
sourceBlockEGID = startBlock,
|
||||
sourcePortUsage = startPort,
|
||||
destinationBlockEGID = endBlock,
|
||||
destinationPortUsage = endPort,
|
||||
ID = wireEGID
|
||||
destinationPortUsage = endPort
|
||||
});
|
||||
return wireInitializer.Get<WireEntityStruct>();
|
||||
return (wireInitializer.Get<WireEntityStruct>(), wireEGID);
|
||||
}
|
||||
|
||||
public ref WireEntityStruct GetWire(EGID wire)
|
||||
|
@ -77,8 +77,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
|
||||
{
|
||||
ExclusiveGroup group = input
|
||||
? NamedExclusiveGroup<InputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<OutputPortsGroup>.Group;
|
||||
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
||||
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
|
||||
EGID egid = new EGID(id, group);
|
||||
if (!entitiesDB.Exists<PortEntityStruct>(egid))
|
||||
|
@ -116,9 +116,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public bool SetSignal(uint signalID, float signal, bool input = true)
|
||||
{
|
||||
var array = GetSignalStruct(signalID, out uint index, input);
|
||||
var arrayB = array.ToBuffer();
|
||||
if (array.count > 0) arrayB.buffer[index].valueAsFloat = signal;
|
||||
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||
if (count > 0) array[index].valueAsFloat = signal;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -130,11 +129,10 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
|
||||
{
|
||||
var array = GetSignalStruct(signalID, out uint index, input);
|
||||
var arrayB = array.ToBuffer();
|
||||
if (array.count > 0)
|
||||
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||
if (count > 0)
|
||||
{
|
||||
ref var channelData = ref arrayB.buffer[index];
|
||||
ref var channelData = ref array[index];
|
||||
channelData.valueAsFloat += signal;
|
||||
if (clamp)
|
||||
{
|
||||
|
@ -162,9 +160,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public float GetSignal(uint signalID, bool input = true)
|
||||
{
|
||||
var array = GetSignalStruct(signalID, out uint index, input);
|
||||
var arrayB = array.ToBuffer();
|
||||
return array.count > 0 ? arrayB.buffer[index].valueAsFloat : 0f;
|
||||
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||
return count > 0 ? array[index].valueAsFloat : 0f;
|
||||
}
|
||||
|
||||
public uint[] GetSignalIDs(EGID blockID, bool input = true)
|
||||
|
@ -193,7 +190,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
EGID[] inputs = new EGID[ports.inputCount];
|
||||
for (uint i = 0; i < ports.inputCount; i++)
|
||||
{
|
||||
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<InputPortsGroup>.Group);
|
||||
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group);
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
@ -204,70 +201,55 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
EGID[] outputs = new EGID[ports.outputCount];
|
||||
for (uint i = 0; i < ports.outputCount; i++)
|
||||
{
|
||||
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<OutputPortsGroup>.Group);
|
||||
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group);
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists)
|
||||
|
||||
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(Block block, byte portUsage, bool output)
|
||||
{
|
||||
var ports = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
|
||||
exists = ports;
|
||||
return new EGID(ports.Get().firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
|
||||
return MatchBlockIOToPort(block.Id, portUsage, output);
|
||||
}
|
||||
|
||||
public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists)
|
||||
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(EGID block, byte portUsage, bool output)
|
||||
{
|
||||
if (!entitiesDB.Exists<BlockPortsStruct>(block))
|
||||
{
|
||||
exists = false;
|
||||
return default;
|
||||
}
|
||||
exists = true;
|
||||
var group = output
|
||||
? NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group;
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
||||
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
|
||||
}
|
||||
|
||||
public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
|
||||
{
|
||||
var ports = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
|
||||
exists = ports;
|
||||
return new EGID(ports.Get().firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
|
||||
}
|
||||
|
||||
public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists)
|
||||
{
|
||||
if (!entitiesDB.Exists<BlockPortsStruct>(block))
|
||||
{
|
||||
exists = false;
|
||||
if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper))
|
||||
return default;
|
||||
for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i)
|
||||
{
|
||||
uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i;
|
||||
if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) ||
|
||||
array[index].usage != portUsage) continue;
|
||||
return new OptionalRef<PortEntityStruct>(array, index, new EGID(entityID, group));
|
||||
}
|
||||
exists = true;
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
||||
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
|
||||
public OptionalRef<WireEntityStruct> MatchPortToWire(PortEntityStruct port, EGID blockID, out EGID wireID)
|
||||
{
|
||||
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
|
||||
var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group);
|
||||
var wiresB = wires.ToBuffer().buffer;
|
||||
for (uint i = 0; i < wires.count; i++)
|
||||
var (wires, ids, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
if ((wiresB[i].destinationPortUsage == port.usage && wiresB[i].destinationBlockEGID == blockID)
|
||||
|| (wiresB[i].sourcePortUsage == port.usage && wiresB[i].sourceBlockEGID == blockID))
|
||||
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|
||||
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
|
||||
{
|
||||
exists = true;
|
||||
return ref wiresB[i];
|
||||
wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group);
|
||||
return new OptionalRef<WireEntityStruct>(wires, i);
|
||||
}
|
||||
}
|
||||
exists = false;
|
||||
WireEntityStruct[] defRef = new WireEntityStruct[1];
|
||||
return ref defRef[0];
|
||||
|
||||
wireID = default;
|
||||
return default;
|
||||
}
|
||||
|
||||
public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, byte startPort = byte.MaxValue,
|
||||
byte endPort = byte.MaxValue)
|
||||
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue)
|
||||
{
|
||||
EGID[] startPorts;
|
||||
if (startPort == byte.MaxValue)
|
||||
|
@ -278,7 +260,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
else
|
||||
{
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
|
||||
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<OutputPortsGroup>.Group) };
|
||||
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
|
||||
}
|
||||
|
||||
EGID[] endPorts;
|
||||
|
@ -290,59 +272,49 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
else
|
||||
{
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
|
||||
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<InputPortsGroup>.Group) };
|
||||
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
|
||||
}
|
||||
|
||||
EntityCollection<WireEntityStruct> wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group);
|
||||
var wiresB = wires.ToBuffer().buffer;
|
||||
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
|
||||
{
|
||||
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
|
||||
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
|
||||
{
|
||||
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
|
||||
for (int w = 0; w < wires.count; w++)
|
||||
foreach (var wireOpt in entitiesDB.QueryEntitiesOptional<WireEntityStruct>(
|
||||
NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group))
|
||||
{
|
||||
if ((wiresB[w].destinationPortUsage == endPES.usage && wiresB[w].destinationBlockEGID == endBlock)
|
||||
&& (wiresB[w].sourcePortUsage == startPES.usage && wiresB[w].sourceBlockEGID == startBlock))
|
||||
var wire = wireOpt.Get();
|
||||
if ((wire.destinationPortUsage == endPES.usage && wire.destinationBlockEGID == endBlock)
|
||||
&& (wire.sourcePortUsage == startPES.usage && wire.sourceBlockEGID == startBlock))
|
||||
{
|
||||
exists = true;
|
||||
return ref wiresB[w];
|
||||
return wireOpt.EGID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exists = false;
|
||||
WireEntityStruct[] defRef = new WireEntityStruct[1];
|
||||
return ref defRef[0];
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists)
|
||||
{
|
||||
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
|
||||
var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
|
||||
var channelsB = channels.ToBuffer();
|
||||
if (port.firstChannelIndexCachedInSim < channels.count)
|
||||
{
|
||||
exists = true;
|
||||
return ref channelsB.buffer[port.firstChannelIndexCachedInSim];
|
||||
}
|
||||
exists = false;
|
||||
ChannelDataStruct[] defRef = new ChannelDataStruct[1];
|
||||
return ref defRef[0];
|
||||
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
|
||||
{
|
||||
var port = GetPort(portID);
|
||||
var (channels, count) = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
||||
return port.firstChannelIndexCachedInSim < count
|
||||
? new OptionalRef<ChannelDataStruct>(channels, port.firstChannelIndexCachedInSim)
|
||||
: default;
|
||||
}
|
||||
|
||||
public EGID[] GetElectricBlocks()
|
||||
{
|
||||
var res = new FasterList<EGID>();
|
||||
foreach (var (coll, _) in entitiesDB.QueryEntities<BlockPortsStruct>())
|
||||
foreach (var ((coll, ids, count), _) in entitiesDB.QueryEntities<BlockPortsStruct>())
|
||||
{
|
||||
var collB = coll.ToBuffer();
|
||||
for (int i = 0; i < coll.count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref BlockPortsStruct s = ref collB.buffer[i];
|
||||
res.Add(s.ID);
|
||||
ref BlockPortsStruct s = ref coll[i];
|
||||
//res.Add(s.ID); - TODO: Would need to search for the groups for each block
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,55 +323,30 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
|||
|
||||
public EGID[] WiredToInput(EGID block, byte port)
|
||||
{
|
||||
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
|
||||
(WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block);
|
||||
EGID[] result = new EGID[wireEntityStructs.Length];
|
||||
for (uint i = 0; i < wireEntityStructs.Length; i++)
|
||||
{
|
||||
result[i] = wireEntityStructs[i].ID;
|
||||
}
|
||||
|
||||
return result;
|
||||
return entitiesDB
|
||||
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
||||
.ToArray(wire => wire.ID,
|
||||
wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block);
|
||||
}
|
||||
|
||||
public EGID[] WiredToOutput(EGID block, byte port)
|
||||
{
|
||||
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
|
||||
(WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block);
|
||||
EGID[] result = new EGID[wireEntityStructs.Length];
|
||||
for (uint i = 0; i < wireEntityStructs.Length; i++)
|
||||
{
|
||||
result[i] = wireEntityStructs[i].ID;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private T[] Search<T>(ExclusiveGroup group, Func<T, bool> isMatch) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
FasterList<T> results = new FasterList<T>();
|
||||
EntityCollection<T> components = entitiesDB.QueryEntities<T>(group);
|
||||
var componentsB = components.ToBuffer();
|
||||
for (uint i = 0; i < components.count; i++)
|
||||
{
|
||||
if (isMatch(componentsB.buffer[i]))
|
||||
{
|
||||
results.Add(componentsB.buffer[i]);
|
||||
}
|
||||
}
|
||||
return results.ToArray();
|
||||
return entitiesDB
|
||||
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
||||
.ToArray(wire => wire.ID,
|
||||
wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block);
|
||||
}
|
||||
|
||||
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
|
||||
{
|
||||
ExclusiveGroup group = input
|
||||
? NamedExclusiveGroup<InputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<OutputPortsGroup>.Group;
|
||||
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
||||
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
||||
{
|
||||
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex;
|
||||
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).firstChannelIndexCachedInSim;
|
||||
var channelData =
|
||||
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
|
||||
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
||||
return channelData;
|
||||
}
|
||||
|
||||
|
|
26
TechbloxModdingAPI/Blocks/LogicGate.cs
Normal file
26
TechbloxModdingAPI/Blocks/LogicGate.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class LogicGate : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) LogicGate object representing an existing block.
|
||||
/// </summary>
|
||||
public LogicGate(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) LogicGate object representing an existing block.
|
||||
/// </summary>
|
||||
public LogicGate(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.LOGIC_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
71
TechbloxModdingAPI/Blocks/Motor.cs
Normal file
71
TechbloxModdingAPI/Blocks/Motor.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Motor : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Motor object representing an existing block.
|
||||
/// </summary>
|
||||
public Motor(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Motor object representing an existing block.
|
||||
/// </summary>
|
||||
public Motor(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.MOTOR_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Motor's TopSpeed property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float TopSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Motor's Torque property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Torque
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxForce;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxForce = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Motor's Reverse property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool Reverse
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).reverse;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).reverse = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
50
TechbloxModdingAPI/Blocks/ObjectID.cs
Normal file
50
TechbloxModdingAPI/Blocks/ObjectID.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using Techblox.ObjectIDBlockServer;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class ObjectID : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) ObjectID object representing an existing block.
|
||||
/// </summary>
|
||||
public ObjectID(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) ObjectID object representing an existing block.
|
||||
/// </summary>
|
||||
public ObjectID(uint id) :
|
||||
base(new EGID(id, ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ObjectID's Identifier property. Tweakable stat.
|
||||
/// </summary>
|
||||
public char Identifier
|
||||
{
|
||||
get => (char) (BlockEngine.GetBlockInfo<ObjectIDTweakableComponent>(this).objectIDToTrigger + 'A');
|
||||
set
|
||||
{
|
||||
if(value is < 'A' or > 'Z')
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "ObjectIdentifier must be set to a letter between A and Z.");
|
||||
BlockEngine.GetBlockInfo<ObjectIDTweakableComponent>(this).objectIDToTrigger = (byte) (value - 'A');
|
||||
Label = value + ""; //The label isn't updated automatically
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the identifier blocks with the given ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID to look for</param>
|
||||
/// <returns>An array that may be empty</returns>
|
||||
public static ObjectID[] GetByID(char id) => BlockEngine.GetObjectIDsFromID((byte) (id - 'A'));
|
||||
}
|
||||
}
|
71
TechbloxModdingAPI/Blocks/Piston.cs
Normal file
71
TechbloxModdingAPI/Blocks/Piston.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Piston : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Piston object representing an existing block.
|
||||
/// </summary>
|
||||
public Piston(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Piston object representing an existing block.
|
||||
/// </summary>
|
||||
public Piston(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.PISTON_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Piston's MaximumForce property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaximumForce
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).pistonVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).pistonVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Piston's MaxExtension property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaxExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).maxDeviation;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).maxDeviation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Piston's InputIsExtension property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool InputIsExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).hasProportionalInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).hasProportionalInput = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
TechbloxModdingAPI/Blocks/Seat.cs
Normal file
56
TechbloxModdingAPI/Blocks/Seat.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Seat : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Seat object representing an existing block.
|
||||
/// </summary>
|
||||
public Seat(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Seat object representing an existing block.
|
||||
/// </summary>
|
||||
public Seat(uint id) :
|
||||
base(new EGID(id, RobocraftX.PilotSeat.SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Seat's FollowCam property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool FollowCam
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatFollowCamComponent>(this).followCam;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatFollowCamComponent>(this).followCam = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Seat's CharacterColliderHeight property. May not be saved.
|
||||
/// </summary>
|
||||
public float CharacterColliderHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatReadOnlySettingsComponent>(this).characterColliderHeight;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatReadOnlySettingsComponent>(this).characterColliderHeight = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
146
TechbloxModdingAPI/Blocks/Servo.cs
Normal file
146
TechbloxModdingAPI/Blocks/Servo.cs
Normal file
|
@ -0,0 +1,146 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Servo : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Servo object representing an existing block.
|
||||
/// </summary>
|
||||
public Servo(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Servo object representing an existing block.
|
||||
/// </summary>
|
||||
public Servo(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.SERVO_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's MaximumForce property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaximumForce
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).servoVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).servoVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's MinimumAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MinimumAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).minDeviation;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).minDeviation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's MaximumAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaximumAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).maxDeviation;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).maxDeviation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's Reverse property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool Reverse
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).reverse;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).reverse = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's InputIsAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool InputIsAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).hasProportionalInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).hasProportionalInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's DirectionVector property. May not be saved.
|
||||
/// </summary>
|
||||
public Unity.Mathematics.float3 DirectionVector
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).directionVector;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).directionVector = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's RotationAxis property. May not be saved.
|
||||
/// </summary>
|
||||
public Unity.Mathematics.float3 RotationAxis
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).rotationAxis;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).rotationAxis = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's ForceAxis property. May not be saved.
|
||||
/// </summary>
|
||||
public Unity.Mathematics.float3 ForceAxis
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).forceAxis;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).forceAxis = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// <returns>The connected wire.</returns>
|
||||
/// <param name="portId">Port identifier.</param>
|
||||
/// <param name="connected">Whether the port has a wire connected to it.</param>
|
||||
protected ref WireEntityStruct GetConnectedWire(EGID portId, out bool connected)
|
||||
protected OptionalRef<WireEntityStruct> GetConnectedWire(PortEntityStruct port, out EGID egid)
|
||||
{
|
||||
return ref SignalEngine.MatchPortToWire(portId, Id, out connected);
|
||||
return SignalEngine.MatchPortToWire(port, Id, out egid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -56,10 +56,9 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// </summary>
|
||||
/// <returns>The channel data.</returns>
|
||||
/// <param name="portId">Port identifier.</param>
|
||||
/// <param name="exists">Whether the channel actually exists.</param>
|
||||
protected ref ChannelDataStruct GetChannelData(EGID portId, out bool exists)
|
||||
protected OptionalRef<ChannelDataStruct> GetChannelData(EGID portId)
|
||||
{
|
||||
return ref SignalEngine.GetChannelDataStruct(portId, out exists);
|
||||
return SignalEngine.GetChannelDataStruct(portId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
137
TechbloxModdingAPI/Blocks/WheelRig.cs
Normal file
137
TechbloxModdingAPI/Blocks/WheelRig.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
using TechbloxModdingAPI.Tests;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class WheelRig : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) WheelRig object representing an existing block.
|
||||
/// </summary>
|
||||
public WheelRig(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) WheelRig object representing an existing block.
|
||||
/// </summary>
|
||||
public WheelRig(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's BrakingStrength property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float BrakingStrength
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).brakingStrength;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).brakingStrength = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's FlipDirection property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool FlipDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).flipDirection;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).flipDirection = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's MaxVelocity property. May not be saved.
|
||||
/// </summary>
|
||||
public float MaxVelocity
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigReadOnlyStruct>(this).maxVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigReadOnlyStruct>(this).maxVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's SteerAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
[TestValue(0f)] // Can be 0 for no steer variant
|
||||
public float SteerAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).steerAngle;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).steerAngle = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's FlipSteering property. Tweakable stat.
|
||||
/// </summary>
|
||||
[TestValue(false)]
|
||||
public bool FlipSteering
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).flipSteering;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).flipSteering = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's VelocityForMinAngle property. May not be saved.
|
||||
/// </summary>
|
||||
[TestValue(0f)]
|
||||
public float VelocityForMinAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).velocityForMinAngle;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).velocityForMinAngle = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's MinSteerAngleFactor property. May not be saved.
|
||||
/// </summary>
|
||||
[TestValue(0f)]
|
||||
public float MinSteerAngleFactor
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).minSteerAngleFactor;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).minSteerAngleFactor = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,11 @@ using Svelto.ECS;
|
|||
using Svelto.ECS.Experimental;
|
||||
|
||||
using TechbloxModdingAPI.Blocks.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public class Wire
|
||||
public class Wire : EcsObjectBase
|
||||
{
|
||||
internal static SignalEngine signalEngine;
|
||||
|
||||
|
@ -30,8 +31,8 @@ namespace TechbloxModdingAPI.Blocks
|
|||
|
||||
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
|
||||
{
|
||||
WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
|
||||
return new Wire(wire, start, end);
|
||||
var (wire, id) = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
|
||||
return new Wire(wire, start, end, id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -43,14 +44,12 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
|
||||
public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
|
||||
{
|
||||
EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
|
||||
if (!exists) return null;
|
||||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists);
|
||||
if (exists)
|
||||
{
|
||||
return new Wire(Block.New(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort);
|
||||
}
|
||||
return null;
|
||||
var port = signalEngine.MatchBlockIOToPort(end, endPort, false);
|
||||
if (!port) return null;
|
||||
var wire = signalEngine.MatchPortToWire(port, end.Id, out var egid);
|
||||
return wire
|
||||
? new Wire(wire.Get().sourceBlockEGID, end.Id, wire.Get().sourcePortUsage, endPort, egid, false)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -62,63 +61,51 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
|
||||
public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
|
||||
{
|
||||
EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
|
||||
if (!exists) return null;
|
||||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists);
|
||||
if (exists)
|
||||
{
|
||||
return new Wire(start, Block.New(wire.destinationBlockEGID), startPort, wire.destinationPortUsage);
|
||||
}
|
||||
return null;
|
||||
var port = signalEngine.MatchBlockIOToPort(start, startPort, true);
|
||||
if (!port) return null;
|
||||
var wire = signalEngine.MatchPortToWire(port, start.Id, out var egid);
|
||||
return wire
|
||||
? new Wire(start.Id, wire.Get().destinationBlockEGID, startPort, wire.Get().destinationPortUsage, egid, false)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a wire object from an existing connection.
|
||||
/// Construct a wire object froam n existing connection.
|
||||
/// </summary>
|
||||
/// <param name="start">Starting block ID.</param>
|
||||
/// <param name="end">Ending block ID.</param>
|
||||
/// <param name="startPort">Starting port number, or guess if omitted.</param>
|
||||
/// <param name="endPort">Ending port number, or guess if omitted.</param>
|
||||
/// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
|
||||
public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue)
|
||||
public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) : base(ecs =>
|
||||
{
|
||||
startBlockEGID = start.Id;
|
||||
endBlockEGID = end.Id;
|
||||
var th = (Wire)ecs;
|
||||
th.startBlockEGID = start.Id;
|
||||
th.endBlockEGID = end.Id;
|
||||
bool flipped = false;
|
||||
// find block ports
|
||||
WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort);
|
||||
if (exists)
|
||||
EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
|
||||
if (wire == default)
|
||||
{
|
||||
wireEGID = wire.ID;
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(end, wire.destinationPortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(start, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
inputToOutput = false;
|
||||
endPort = wire.destinationPortUsage;
|
||||
startPort = wire.sourcePortUsage;
|
||||
// flip I/O around and try again
|
||||
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort);
|
||||
flipped = true;
|
||||
// NB: start and end are handled exactly as they're received as params.
|
||||
// This makes wire traversal easier, but makes logic in this class a bit more complex
|
||||
}
|
||||
|
||||
if (wire != default)
|
||||
{
|
||||
th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
|
||||
}
|
||||
else
|
||||
{
|
||||
// flip I/O around and try again
|
||||
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort);
|
||||
if (exists)
|
||||
{
|
||||
wireEGID = wire.ID;
|
||||
endPortEGID = signalEngine.MatchBlockOutputToPort(end, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockInputToPort(start, wire.destinationPortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
inputToOutput = true; // end is actually the source
|
||||
// NB: start and end are handled exactly as they're received as params.
|
||||
// This makes wire traversal easier, but makes logic in this class a bit more complex
|
||||
endPort = wire.sourcePortUsage;
|
||||
startPort = wire.destinationPortUsage;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WireInvalidException("Wire not found");
|
||||
}
|
||||
throw new WireInvalidException("Wire not found");
|
||||
}
|
||||
|
||||
return th.wireEGID;
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -131,25 +118,25 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// <param name="wire">The wire ID.</param>
|
||||
/// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
|
||||
public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
|
||||
: this(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
|
||||
{
|
||||
this.startBlockEGID = start.Id;
|
||||
this.endBlockEGID = end.Id;
|
||||
}
|
||||
|
||||
private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire)
|
||||
{
|
||||
Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
|
||||
}
|
||||
|
||||
private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
|
||||
{
|
||||
this.startBlockEGID = startBlock;
|
||||
this.endBlockEGID = endBlock;
|
||||
this.inputToOutput = inputToOutput;
|
||||
this.wireEGID = wire;
|
||||
if (inputToOutput)
|
||||
{
|
||||
endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
}
|
||||
endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).EGID;
|
||||
if (endPortEGID == default) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).EGID;
|
||||
if (startPortEGID == default) throw new WireInvalidException("Wire start port not found");
|
||||
this.startPort = startPort;
|
||||
this.endPort = endPort;
|
||||
}
|
||||
|
@ -158,41 +145,16 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// Construct a wire object from an existing wire connection.
|
||||
/// </summary>
|
||||
/// <param name="wireEgid">The wire ID.</param>
|
||||
public Wire(EGID wireEgid)
|
||||
public Wire(EGID wireEgid) : base(wireEgid)
|
||||
{
|
||||
this.wireEGID = wireEgid;
|
||||
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
|
||||
this.startBlockEGID = wire.sourceBlockEGID;
|
||||
this.endBlockEGID = wire.destinationBlockEGID;
|
||||
this.inputToOutput = false;
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
this.endPort = wire.destinationPortUsage;
|
||||
this.startPort = wire.sourcePortUsage;
|
||||
Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
|
||||
wireEgid, false);
|
||||
}
|
||||
|
||||
internal Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest)
|
||||
private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest, EGID wireEgid)
|
||||
: this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wireEgid, false)
|
||||
{
|
||||
this.wireEGID = wire.ID;
|
||||
this.startBlockEGID = wire.sourceBlockEGID;
|
||||
this.endBlockEGID = wire.destinationBlockEGID;
|
||||
inputToOutput = false;
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(dest, wire.destinationPortUsage, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(src, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
this.endPort = wire.destinationPortUsage;
|
||||
this.startPort = wire.sourcePortUsage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's in-game id.
|
||||
/// </summary>
|
||||
public EGID Id
|
||||
{
|
||||
get => wireEGID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -202,16 +164,12 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return 0f;
|
||||
return cds.valueAsFloat;
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsFloat = value;
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,16 +180,12 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return "";
|
||||
return cds.valueAsEcsString;
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsEcsString.Set(value);
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,16 +196,12 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return default;
|
||||
return cds.valueAsEcsString;
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsEcsString = value;
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,16 +213,12 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return uint.MaxValue;
|
||||
return cds.valueAsID;
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID;
|
||||
}
|
||||
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsID = value;
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +227,7 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// </summary>
|
||||
public SignalingBlock Start
|
||||
{
|
||||
get => new SignalingBlock(startBlockEGID);
|
||||
get => (SignalingBlock)Block.New(startBlockEGID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -291,13 +237,21 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
get => startPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The display name of the start port.
|
||||
/// </summary>
|
||||
public string StartPortName
|
||||
{
|
||||
get => signalEngine.GetPort(startPortEGID).portNameLocalised;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block at the end of the wire.
|
||||
/// </summary>
|
||||
public SignalingBlock End
|
||||
{
|
||||
get => new SignalingBlock(endBlockEGID);
|
||||
get => (SignalingBlock)Block.New(endBlockEGID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -308,6 +262,14 @@ namespace TechbloxModdingAPI.Blocks
|
|||
get => endPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The display name of the end port.
|
||||
/// </summary>
|
||||
public string EndPortName
|
||||
{
|
||||
get => signalEngine.GetPort(endPortEGID).portNameLocalised;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of the wire object where the direction of the wire is guaranteed to be from a block output to a block input.
|
||||
/// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
|
||||
|
@ -315,7 +277,7 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// <returns>A copy of the wire object.</returns>
|
||||
public Wire OutputToInputCopy()
|
||||
{
|
||||
return new Wire(wireEGID);
|
||||
return GetInstance(wireEGID, egid => new Wire(egid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -329,15 +291,11 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
inputToOutput = false;
|
||||
// swap inputs and outputs
|
||||
EGID temp = endBlockEGID;
|
||||
endBlockEGID = startBlockEGID;
|
||||
startBlockEGID = temp;
|
||||
temp = endPortEGID;
|
||||
(endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
|
||||
var tempPort = endPortEGID;
|
||||
endPortEGID = startPortEGID;
|
||||
startPortEGID = temp;
|
||||
byte tempPortNumber = endPort;
|
||||
endPort = startPort;
|
||||
startPort = tempPortNumber;
|
||||
startPortEGID = tempPort;
|
||||
(endPort, startPort) = (startPort, endPort);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,7 +303,7 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
if (signalEngine.Exists<WireEntityStruct>(wireEGID))
|
||||
{
|
||||
return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {Start.PortName(StartPort, inputToOutput)}) -> ({End.Type}::{EndPort} aka {End.PortName(EndPort, !inputToOutput)})";
|
||||
return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {(StartPort != byte.MaxValue ? Start.PortName(StartPort, inputToOutput) : "")}) -> ({End.Type}::{EndPort} aka {(EndPort != byte.MaxValue ? End.PortName(EndPort, !inputToOutput) : "")})";
|
||||
}
|
||||
return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})";
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using Gamecraft.Damage;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS;
|
||||
using Techblox.TimeRunning.Clusters;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
|
@ -10,35 +9,37 @@ namespace TechbloxModdingAPI
|
|||
/// </summary>
|
||||
public class Cluster : EcsObjectBase
|
||||
{
|
||||
public override EGID Id { get; }
|
||||
|
||||
public Cluster(EGID id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
||||
public Cluster(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public float InitialHealth
|
||||
public Cluster(uint id) : this(new EGID(id, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
||||
{
|
||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth;
|
||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth = value;
|
||||
}
|
||||
|
||||
public float InitialHealth //TODO
|
||||
{
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
public float CurrentHealth
|
||||
{
|
||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth;
|
||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth = value;
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
public float HealthMultiplier
|
||||
{
|
||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier;
|
||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier = value;
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The mass of the cluster.
|
||||
/// </summary>
|
||||
public float Mass => Block.BlockEngine.GetBlockInfo<ClusterMassComponent>(this).mass;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the simulation-time rigid bodies for the chunks in this cluster.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using HarmonyLib;
|
||||
using Svelto.ECS;
|
||||
using RobocraftX.CR.MainGame;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.StateSync;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.CR.MainGame.MainGameCompositionRoot.DeterministicCompose<T>()
|
||||
/// Initializes existing and custom commands
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
static class CommandPatch
|
||||
{
|
||||
public static void Postfix(Action reloadGame, MultiplayerInitParameters multiplayerParameters,
|
||||
StateSyncRegistrationHelper stateSyncReg)
|
||||
{
|
||||
/*CommandLineCompositionRoot.Compose(contextHolder, stateSyncReg.enginesRoot, reloadGame, multiplayerParameters,
|
||||
stateSyncReg); - uREPL C# compilation not supported anymore */
|
||||
var enginesRoot = stateSyncReg.enginesRoot;
|
||||
var entityFunctions = enginesRoot.GenerateEntityFunctions();
|
||||
var entityFactory = enginesRoot.GenerateEntityFactory();
|
||||
var entitySerializer = enginesRoot.GenerateEntitySerializer();
|
||||
Logging.MetaDebugLog("Adding existing command engines");
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteSetGravityCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteSetPhysicsPrecisionCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteSetPhysicsFrequencyCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName(
|
||||
"RobocraftX.GUI.CommandLine.ExecuteClearAllPartsCommandEngine"),
|
||||
entityFunctions));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteHelpCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName(
|
||||
"RobocraftX.GUI.CommandLine.ExecuteSetLinearRestingThresholdCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName(
|
||||
"RobocraftX.GUI.CommandLine.ExecuteSetAngularRestingThresholdCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteEnableVisualProfilerCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteSetNetworkJitterFramesEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteSetSendConnectedEntitiesCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteSetMaxSimFramesEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.SetDebugDisplayExtraInfoCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.SetNetSyncBandwidthLimitCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ThrowExceptionCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.SetPriorityCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.TeleportCharacterCommandEngine"),
|
||||
entityFactory));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ChangeTextBlockTextCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.SetCharacterRunSpeedCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.SetCameraZoomDistanceCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.EditLightingSettingsCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.EditSkySettingsCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.EditFogSettingsCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.TeleportCharacterImplementationEngine"),
|
||||
entityFunctions));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteConnectToServerCommandEngine"),
|
||||
entityFunctions, entitySerializer, reloadGame, multiplayerParameters));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.SetInputBroadcastCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ExecuteSetJointInertiaTensorCommandEngine")));
|
||||
enginesRoot.AddEngine(
|
||||
(IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.ChangeTeamCommandEngine")));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.DamageCharacterCommandEngine"), entityFactory));
|
||||
enginesRoot.AddEngine((IEngine) Activator.CreateInstance(
|
||||
AccessTools.TypeByName("RobocraftX.GUI.CommandLine.DisableCharacterDamageCommandEngine")));
|
||||
Logging.MetaDebugLog("Existing command engines added");
|
||||
|
||||
CommandManager.RegisterEngines(enginesRoot);
|
||||
}
|
||||
|
||||
public static MethodInfo TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose")
|
||||
.MakeGenericMethod(typeof(object));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,6 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using uREPL;
|
||||
using RobocraftX.CommandLine.Custom;
|
||||
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -17,9 +14,7 @@ namespace TechbloxModdingAPI.Commands
|
|||
{
|
||||
public static void Register(string name, Action action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Register(string name, Action<object> action, string desc, bool noConsole = false)
|
||||
|
@ -39,50 +34,42 @@ namespace TechbloxModdingAPI.Commands
|
|||
|
||||
public static void Register<Param0>(string name, Action<Param0> action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register<Param0>(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register<Param0>(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Register<Param0, Param1>(string name, Action<Param0, Param1> action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register<Param0, Param1>(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register<Param0, Param1>(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Register<Param0, Param1, Param2>(string name, Action<Param0, Param1, Param2> action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register<Param0, Param1, Param2>(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register<Param0, Param1, Param2>(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Unregister(string name, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Unregister(name);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Unregister(name);
|
||||
CustomCommands.Unregister(name);
|
||||
}
|
||||
|
||||
public static void Call(string name)
|
||||
{
|
||||
RuntimeCommands.Call(name);
|
||||
CustomCommands.Call(name);
|
||||
}
|
||||
|
||||
public static void Call<Param0>(string name, Param0 param0)
|
||||
{
|
||||
RuntimeCommands.Call<Param0>(name, param0);
|
||||
CustomCommands.Call(name, param0);
|
||||
}
|
||||
|
||||
public static void Call<Param0, Param1>(string name, Param0 param0, Param1 param1)
|
||||
{
|
||||
RuntimeCommands.Call<Param0, Param1>(name, param0, param1);
|
||||
CustomCommands.Call(name, param0, param1);
|
||||
}
|
||||
|
||||
public static void Call<Param0, Param1, Param2>(string name, Param0 param0, Param1 param1, Param2 param2)
|
||||
{
|
||||
RuntimeCommands.Call<Param0, Param1, Param2>(name, param0, param1, param2);
|
||||
CustomCommands.Call(name, param0, param1, param2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
57
TechbloxModdingAPI/Commands/CustomCommands.cs
Normal file
57
TechbloxModdingAPI/Commands/CustomCommands.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
internal static class CustomCommands
|
||||
{
|
||||
public struct CommandData
|
||||
{
|
||||
public string Name;
|
||||
public string Description;
|
||||
public Delegate Action;
|
||||
}
|
||||
|
||||
private static Dictionary<string, CommandData> _commands = new Dictionary<string, CommandData>();
|
||||
public static void Register(string name, Delegate action, string desc)
|
||||
{
|
||||
_commands.Add(name, new CommandData
|
||||
{
|
||||
Name = name,
|
||||
Description = desc,
|
||||
Action = action
|
||||
});
|
||||
}
|
||||
|
||||
public static void Call(string name, params object[] args)
|
||||
{
|
||||
if (_commands.TryGetValue(name, out var command))
|
||||
{
|
||||
var paramz = command.Action.Method.GetParameters();
|
||||
if (paramz.Length > args.Length)
|
||||
throw new CommandParameterMissingException(
|
||||
$"This command requires {paramz.Length} arguments, {args.Length} given");
|
||||
for (var index = 0; index < paramz.Length; index++)
|
||||
{
|
||||
args[index] = Convert.ChangeType(args[index], paramz[index].ParameterType);
|
||||
}
|
||||
|
||||
command.Action.DynamicInvoke(args);
|
||||
}
|
||||
else
|
||||
throw new CommandNotFoundException($"Command {name} does not exist!");
|
||||
}
|
||||
|
||||
public static void Unregister(string name)
|
||||
{
|
||||
_commands.Remove(name);
|
||||
}
|
||||
|
||||
public static bool Exists(string name) => _commands.ContainsKey(name);
|
||||
|
||||
public static ReadOnlyDictionary<string, CommandData> GetAllCommandData() =>
|
||||
new ReadOnlyDictionary<string, CommandData>(_commands);
|
||||
}
|
||||
}
|
|
@ -1,39 +1,37 @@
|
|||
using System.Linq;
|
||||
|
||||
using uREPL;
|
||||
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
public static class ExistingCommands
|
||||
{
|
||||
public static void Call(string commandName)
|
||||
{
|
||||
RuntimeCommands.Call(commandName);
|
||||
CustomCommands.Call(commandName);
|
||||
}
|
||||
|
||||
public static void Call<Arg0>(string commandName, Arg0 arg0)
|
||||
{
|
||||
RuntimeCommands.Call<Arg0>(commandName, arg0);
|
||||
CustomCommands.Call(commandName, arg0);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1>(string commandName, Arg0 arg0, Arg1 arg1)
|
||||
{
|
||||
RuntimeCommands.Call<Arg0, Arg1>(commandName, arg0, arg1);
|
||||
CustomCommands.Call(commandName, arg0, arg1);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1, Arg2>(string commandName, Arg0 arg0, Arg1 arg1, Arg2 arg2)
|
||||
{
|
||||
RuntimeCommands.Call<Arg0, Arg1, Arg2>(commandName, arg0, arg1, arg2);
|
||||
CustomCommands.Call(commandName, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
public static bool Exists(string commandName)
|
||||
{
|
||||
return RuntimeCommands.HasRegistered(commandName);
|
||||
return CustomCommands.Exists(commandName);
|
||||
}
|
||||
|
||||
public static (string Name, string Description)[] GetCommandNamesAndDescriptions()
|
||||
{
|
||||
return RuntimeCommands.table.Values.Select(command => (command.name, command.description)).ToArray();
|
||||
return CustomCommands.GetAllCommandData().Values.Select(command => (command.Name, command.Description)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,81 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Internal;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
public abstract class EcsObjectBase
|
||||
{
|
||||
public abstract EGID Id { get; } //Abstract to support the 'place' Block constructor
|
||||
public EGID Id { get; }
|
||||
|
||||
private static readonly Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>> _instances =
|
||||
new Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>>();
|
||||
|
||||
private static readonly WeakDictionary<EGID, EcsObjectBase> _noInstance =
|
||||
new WeakDictionary<EGID, EcsObjectBase>();
|
||||
|
||||
internal static WeakDictionary<EGID, EcsObjectBase> GetInstances(Type type)
|
||||
{
|
||||
return _instances.TryGetValue(type, out var dict) ? dict : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cached instance if there's an actively used instance of the object already.
|
||||
/// Objects still get garbage collected and then they will be removed from the cache.
|
||||
/// </summary>
|
||||
/// <param name="egid">The EGID of the entity</param>
|
||||
/// <param name="constructor">The constructor to construct the object</param>
|
||||
/// <typeparam name="T">The object type</typeparam>
|
||||
/// <returns></returns>
|
||||
internal static T GetInstance<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase
|
||||
{
|
||||
var instances = GetInstances(type ?? typeof(T));
|
||||
if (instances == null || !instances.TryGetValue(egid, out var instance))
|
||||
return constructor(egid); // It will be added by the constructor
|
||||
return (T)instance;
|
||||
}
|
||||
|
||||
protected EcsObjectBase(EGID id)
|
||||
{
|
||||
if (!_instances.TryGetValue(GetType(), out var dict))
|
||||
{
|
||||
dict = new WeakDictionary<EGID, EcsObjectBase>();
|
||||
_instances.Add(GetType(), dict);
|
||||
}
|
||||
if (!dict.ContainsKey(id)) // Multiple instances may be created
|
||||
dict.Add(id, this);
|
||||
Id = id;
|
||||
}
|
||||
|
||||
protected EcsObjectBase(Func<EcsObjectBase, EGID> initializer)
|
||||
{
|
||||
if (!_instances.TryGetValue(GetType(), out var dict))
|
||||
{
|
||||
dict = new WeakDictionary<EGID, EcsObjectBase>();
|
||||
_instances.Add(GetType(), dict);
|
||||
}
|
||||
|
||||
var id = initializer(this);
|
||||
if (!dict.ContainsKey(id)) // Multiple instances may be created
|
||||
dict.Add(id, this);
|
||||
else
|
||||
{
|
||||
Logging.MetaDebugLog($"An object of this type and ID is already stored: {GetType()} - {id}");
|
||||
Logging.MetaDebugLog(this);
|
||||
Logging.MetaDebugLog(dict[id]);
|
||||
}
|
||||
|
||||
Id = id;
|
||||
}
|
||||
|
||||
#region ECS initializer stuff
|
||||
|
||||
protected internal EcsInitData InitData;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Holds information needed to construct a component initializer
|
||||
/// </summary>
|
||||
|
@ -22,7 +85,7 @@ namespace TechbloxModdingAPI
|
|||
private EntityReference reference;
|
||||
|
||||
public static implicit operator EcsInitData(EntityInitializer initializer) => new EcsInitData
|
||||
{group = GetInitGroup(initializer), reference = initializer.reference};
|
||||
{ group = GetInitGroup(initializer), reference = initializer.reference };
|
||||
|
||||
public EntityInitializer Initializer(EGID id) => new EntityInitializer(id, group, reference);
|
||||
public bool Valid => group != null;
|
||||
|
@ -56,8 +119,10 @@ namespace TechbloxModdingAPI
|
|||
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType);
|
||||
|
||||
var lambda =
|
||||
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam});
|
||||
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam });
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -5,25 +5,68 @@ using RobocraftX.CR.MainGame;
|
|||
using RobocraftX.FrontEnd;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Schedulers;
|
||||
using TechbloxModdingAPI.Commands;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Engines
|
||||
{
|
||||
[HarmonyPatch]
|
||||
class GameLoadedEnginePatch
|
||||
static class GameLoadedTimeStoppedEnginePatch
|
||||
{
|
||||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
||||
{
|
||||
// register all game engines, including deterministic
|
||||
GameEngineManager.RegisterEngines(stateSyncReg);
|
||||
// register command engines
|
||||
/*CommandLineCompositionRoot.Compose(contextHolder, stateSyncReg.enginesRoot, reloadGame, multiplayerParameters,
|
||||
stateSyncReg); - uREPL C# compilation not supported anymore */
|
||||
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose").MakeGenericMethod(typeof(object));
|
||||
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicTimeStoppedCompose").MakeGenericMethod(typeof(object));
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
static class GameLoadedTimeRunningEnginePatch
|
||||
{
|
||||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
||||
{
|
||||
GameEngineManager.RegisterEngines(stateSyncReg);
|
||||
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicTimeRunningCompose").MakeGenericMethod(typeof(object));
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
static class GameReloadedPatch
|
||||
{
|
||||
internal static bool IsReload;
|
||||
public static void Prefix() => IsReload = true;
|
||||
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "ReloadGame");
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
static class GameSwitchedToPatch
|
||||
{
|
||||
public static void Prefix() => GameReloadedPatch.IsReload = false;
|
||||
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame");
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
static class MenuSwitchedToPatch
|
||||
{
|
||||
public static void Prefix() => GameReloadedPatch.IsReload = false;
|
||||
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu");
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
class MenuLoadedEnginePatch
|
||||
{
|
||||
|
|
9
TechbloxModdingAPI/Engines/IFunEngine.cs
Normal file
9
TechbloxModdingAPI/Engines/IFunEngine.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using Svelto.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Engines
|
||||
{
|
||||
public interface IFunEngine : IApiEngine
|
||||
{
|
||||
public IEntityFunctions Functions { set; }
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ namespace TechbloxModdingAPI.Engines
|
|||
/// <summary>
|
||||
/// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines.
|
||||
/// </summary>
|
||||
public interface IReactionaryEngine<T> : IApiEngine, IReactOnAddAndRemove<T>, IReactOnAddAndRemove where T : unmanaged, IEntityComponent
|
||||
public interface IReactionaryEngine<T> : IApiEngine, IReactOnAddAndRemove<T> where T : unmanaged, IEntityComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
using System;
|
||||
using RobocraftX.Common.Input;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Common.Input;
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Input
|
||||
{
|
||||
public static class FakeInput
|
||||
{
|
||||
private static readonly FakeInputEngine inputEngine = new FakeInputEngine();
|
||||
internal static readonly FakeInputEngine inputEngine = new FakeInputEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Customize the local input.
|
||||
/// </summary>
|
||||
/// <param name="input">The custom input.</param>
|
||||
public static void CustomInput(LocalInputEntityStruct input)
|
||||
public static void CustomInput(LocalCosmeticInputEntityComponent input)
|
||||
{
|
||||
inputEngine.SendCustomInput(input);
|
||||
}
|
||||
|
@ -34,7 +32,7 @@ namespace TechbloxModdingAPI.Input
|
|||
inputEngine.SendCustomPlayerInput(input, playerID);
|
||||
}
|
||||
|
||||
public static LocalInputEntityStruct GetInput()
|
||||
public static LocalCosmeticInputEntityComponent GetInput()
|
||||
{
|
||||
return inputEngine.GetInput();
|
||||
}
|
||||
|
@ -48,31 +46,23 @@ namespace TechbloxModdingAPI.Input
|
|||
return inputEngine.GetPlayerInput(playerID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fake a GUI input.
|
||||
/// <summary>
|
||||
/// Fake a GUI input.
|
||||
/// Omit any parameter you do not want to affect.
|
||||
/// Parameters that end with "?" don't do anything... yet.
|
||||
/// </summary>
|
||||
/// <param name="playerID">The player. Omit this to use the local player.</param>
|
||||
/// <param name="hotbar">Select the hotbar slot by number.</param>
|
||||
/// <param name="commandLine">Toggle the command line?</param>
|
||||
/// <param name="escape">Open escape menu?</param>
|
||||
/// <param name="enter">Page return?</param>
|
||||
/// <param name="debug">Toggle debug display?</param>
|
||||
/// <param name="next">Select next?</param>
|
||||
/// <param name="previous">Select previous?</param>
|
||||
/// <param name="tab">Tab?</param>
|
||||
/// <param name="colour">Toggle to hotbar colour mode?</param>
|
||||
/// <param name="hotbarPage">Select the hotbar page by number?</param>
|
||||
/// <param name="quickSave">Quicksave?</param>
|
||||
/// <param name="paste">Paste?</param>
|
||||
public static void GuiInput(uint playerID = uint.MaxValue, int hotbar = -1, bool escape = false, bool enter = false, bool debug = false, bool next = false, bool previous = false, bool tab = false, int hotbarPage = -1, bool quickSave = false, bool paste = false)
|
||||
/// </summary>
|
||||
/// <param name="hotbar">Select the hotbar slot by number.</param>
|
||||
/// <param name="commandLine">Toggle the command line?</param>
|
||||
/// <param name="escape">Open escape menu?</param>
|
||||
/// <param name="enter">Page return?</param>
|
||||
/// <param name="debug">Toggle debug display?</param>
|
||||
/// <param name="colour">Toggle to hotbar colour mode?</param>
|
||||
/// <param name="hotbarPage">Select the hotbar page by number?</param>
|
||||
/// <param name="quickSave">Quicksave?</param>
|
||||
/// <param name="paste">Paste?</param>
|
||||
public static void GuiInput(int hotbar = -1, bool escape = false, bool enter = false, bool debug = false, int hotbarPage = -1, bool quickSave = false, bool paste = false)
|
||||
{
|
||||
if (playerID == uint.MaxValue)
|
||||
{
|
||||
playerID = inputEngine.GetLocalPlayerID();
|
||||
}
|
||||
ref LocalInputEntityStruct currentInput = ref inputEngine.GetInputRef();
|
||||
ref LocalCosmeticInputEntityComponent currentInput = ref inputEngine.GetInputRef();
|
||||
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
|
||||
// set inputs
|
||||
switch(hotbar)
|
||||
|
@ -92,9 +82,6 @@ namespace TechbloxModdingAPI.Input
|
|||
if (escape) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Escape;
|
||||
if (enter) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Return;
|
||||
if (debug) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleDebugDisplay;
|
||||
if (next) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectNext;
|
||||
if (previous) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectPrev;
|
||||
if (tab) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Tab;
|
||||
switch (hotbarPage)
|
||||
{
|
||||
case 1: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage1; break;
|
||||
|
@ -107,47 +94,46 @@ namespace TechbloxModdingAPI.Input
|
|||
case 8: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage8; break;
|
||||
case 9: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage9; break;
|
||||
case 10: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage10; break;
|
||||
default: break;
|
||||
}
|
||||
//RewiredConsts.Action
|
||||
if (quickSave) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.QuickSave;
|
||||
if (paste) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.PasteSelection;
|
||||
if (paste) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectLastCopiedBlueprint;
|
||||
}
|
||||
|
||||
public static void ActionInput(uint playerID = uint.MaxValue, bool toggleMode = false, bool forward = false, bool backward = false, bool up = false, bool down = false, bool left = false, bool right = false, bool sprint = false, bool toggleFly = false, bool alt = false, bool primary = false, bool secondary = false, bool tertiary = false, bool primaryHeld = false, bool secondaryHeld = false, bool toggleUnitGrid = false, bool ctrl = false, bool toggleColourMode = false, bool scaleBlockUp = false, bool scaleBlockDown = false, bool rotateBlockClockwise = false, bool rotateBlockCounterclockwise = false, bool cutSelection = false, bool copySelection = false, bool deleteSelection = false)
|
||||
{
|
||||
if (playerID == uint.MaxValue)
|
||||
{
|
||||
playerID = inputEngine.GetLocalPlayerID();
|
||||
}
|
||||
ref LocalPlayerInputEntityStruct currentInput = ref inputEngine.GetPlayerInputRef(playerID);
|
||||
{ // TODO: We can only alter our own inputs clientside
|
||||
ref var currentInput = ref inputEngine._localInputCache;
|
||||
|
||||
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
|
||||
// set inputs
|
||||
if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningMode;
|
||||
if (forward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Forward;
|
||||
if (backward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Backward;
|
||||
if (up) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Up;
|
||||
if (down) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Down;
|
||||
if (left) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Left;
|
||||
if (right) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Right;
|
||||
if (sprint) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Sprint;
|
||||
//if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode;
|
||||
//if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction;
|
||||
if (primary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryAction;
|
||||
if (secondary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryAction;
|
||||
if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction;
|
||||
if (primaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld;
|
||||
if (secondaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld;
|
||||
//if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid;
|
||||
if (ctrl) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CtrlAction;
|
||||
if (toggleColourMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleColourMode;
|
||||
if (scaleBlockUp) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp;
|
||||
if (scaleBlockDown) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown;
|
||||
if (rotateBlockClockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise;
|
||||
if (rotateBlockCounterclockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise;
|
||||
if (cutSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CutSelection;
|
||||
if (copySelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CopySelection;
|
||||
if (deleteSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.DeleteSelection;
|
||||
if (toggleMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningModePlay; //TODO: Test, play
|
||||
if (forward) currentInput |= RobocraftX.Common.Input.ActionInput.Forward;
|
||||
if (backward) currentInput |= RobocraftX.Common.Input.ActionInput.Backward;
|
||||
if (up) currentInput |= RobocraftX.Common.Input.ActionInput.Up;
|
||||
if (down) currentInput |= RobocraftX.Common.Input.ActionInput.Down;
|
||||
if (left) currentInput |= RobocraftX.Common.Input.ActionInput.Left;
|
||||
if (right) currentInput |= RobocraftX.Common.Input.ActionInput.Right;
|
||||
if (sprint) currentInput |= RobocraftX.Common.Input.ActionInput.Sprint;
|
||||
//if (toggleFly) currentInput |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode;
|
||||
//if (alt) currentInput |= RobocraftX.Common.Input.ActionInput.AltAction;
|
||||
if (primary) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionClick;
|
||||
if (secondary) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionClick;
|
||||
if (tertiary) currentInput |= RobocraftX.Common.Input.ActionInput.TertiaryAction;
|
||||
if (primaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld;
|
||||
if (secondaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld;
|
||||
//if (toggleUnitGrid) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid;
|
||||
if (ctrl) currentInput |= RobocraftX.Common.Input.ActionInput.CtrlAction;
|
||||
if (toggleColourMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleColourMode;
|
||||
if (scaleBlockUp) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp;
|
||||
if (scaleBlockDown) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown;
|
||||
if (rotateBlockClockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise;
|
||||
if (rotateBlockCounterclockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise;
|
||||
if (cutSelection) currentInput |= RobocraftX.Common.Input.ActionInput.CutSelection;
|
||||
if (copySelection) currentInput |= RobocraftX.Common.Input.ActionInput.CopySelection;
|
||||
if (deleteSelection) currentInput |= RobocraftX.Common.Input.ActionInput.DeleteSelection;
|
||||
|
||||
if(Game.CurrentGame().IsTimeStopped)
|
||||
inputEngine.HandleCustomInput(); // Only gets called when online, so calling it here as well
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace TechbloxModdingAPI.Input
|
|||
|
||||
public bool IsReady = false;
|
||||
|
||||
internal ActionInput _localInputCache;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsReady = false;
|
||||
|
@ -30,12 +32,12 @@ namespace TechbloxModdingAPI.Input
|
|||
IsReady = true;
|
||||
}
|
||||
|
||||
public bool SendCustomInput(LocalInputEntityStruct input)
|
||||
public bool SendCustomInput(LocalCosmeticInputEntityComponent input)
|
||||
{
|
||||
EGID egid = CommonExclusiveGroups.GameStateEGID;
|
||||
if (entitiesDB.Exists<LocalInputEntityStruct>(egid))
|
||||
if (entitiesDB.Exists<LocalCosmeticInputEntityComponent>(egid))
|
||||
{
|
||||
ref LocalInputEntityStruct ies = ref entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
|
||||
ref LocalCosmeticInputEntityComponent ies = ref entitiesDB.QueryEntity<LocalCosmeticInputEntityComponent>(egid);
|
||||
ies = input;
|
||||
return true;
|
||||
}
|
||||
|
@ -54,14 +56,14 @@ namespace TechbloxModdingAPI.Input
|
|||
else return false;
|
||||
}
|
||||
|
||||
public LocalInputEntityStruct GetInput()
|
||||
public LocalCosmeticInputEntityComponent GetInput()
|
||||
{
|
||||
EGID egid = CommonExclusiveGroups.GameStateEGID;
|
||||
if (entitiesDB.Exists<LocalInputEntityStruct>(egid))
|
||||
if (entitiesDB.Exists<LocalCosmeticInputEntityComponent>(egid))
|
||||
{
|
||||
return entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
|
||||
return entitiesDB.QueryEntity<LocalCosmeticInputEntityComponent>(egid);
|
||||
}
|
||||
else return default(LocalInputEntityStruct);
|
||||
else return default(LocalCosmeticInputEntityComponent);
|
||||
}
|
||||
|
||||
public LocalPlayerInputEntityStruct GetPlayerInput(uint playerID, bool remote = false)
|
||||
|
@ -74,10 +76,10 @@ namespace TechbloxModdingAPI.Input
|
|||
else return default;
|
||||
}
|
||||
|
||||
public ref LocalInputEntityStruct GetInputRef()
|
||||
public ref LocalCosmeticInputEntityComponent GetInputRef()
|
||||
{
|
||||
EGID egid = CommonExclusiveGroups.GameStateEGID;
|
||||
return ref entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
|
||||
return ref entitiesDB.QueryEntity<LocalCosmeticInputEntityComponent>(egid);
|
||||
}
|
||||
|
||||
public ref LocalPlayerInputEntityStruct GetPlayerInputRef(uint playerID, bool remote = false)
|
||||
|
@ -86,6 +88,14 @@ namespace TechbloxModdingAPI.Input
|
|||
return ref entitiesDB.QueryEntity<LocalPlayerInputEntityStruct>(egid);
|
||||
}
|
||||
|
||||
internal void HandleCustomInput()
|
||||
{
|
||||
if (!LocalPlayerIDUtility.DoesLocalPlayerExist(entitiesDB))
|
||||
return;
|
||||
GetPlayerInputRef(GetLocalPlayerID()).actionMask |= _localInputCache;
|
||||
_localInputCache = default;
|
||||
}
|
||||
|
||||
public uint GetLocalPlayerID()
|
||||
{
|
||||
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB);
|
||||
|
|
19
TechbloxModdingAPI/Input/FakeInputPatch.cs
Normal file
19
TechbloxModdingAPI/Input/FakeInputPatch.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace TechbloxModdingAPI.Input
|
||||
{
|
||||
[HarmonyPatch]
|
||||
public static class FakeInputPatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
FakeInput.inputEngine.HandleCustomInput(); // This gets called right before the input is sent to the server
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method("RobocraftX.Multiplayer.Input.DeterministicInputRecorderEngine:RecordDeterministicInput");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// </summary>
|
||||
public event EventHandler<bool> OnClick;
|
||||
|
||||
public void OnGUI()
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (automaticLayout)
|
||||
{
|
||||
|
@ -42,34 +42,15 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The button's unique name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display the button.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new button with automatic layout.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to display on the button.</param>
|
||||
/// <param name="name">The button's name.</param>
|
||||
public Button(string text, string name = null)
|
||||
public Button(string text, string name = null) : base(text, name)
|
||||
{
|
||||
automaticLayout = true;
|
||||
this.text = text;
|
||||
if (name == null)
|
||||
{
|
||||
this.Name = typeof(Button).FullName + "::" + text;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
IMGUIManager.AddElement(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -83,10 +64,5 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
automaticLayout = false;
|
||||
this.Box = box;
|
||||
}
|
||||
|
||||
~Button()
|
||||
{
|
||||
IMGUIManager.RemoveElement(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,12 +20,12 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// </summary>
|
||||
public Rect Box { get; set; }
|
||||
|
||||
public void OnGUI()
|
||||
public override void OnGUI()
|
||||
{
|
||||
/*if (Constants.Default == null) return;
|
||||
if (Constants.Default.box == null) return;*/
|
||||
GUIStyle guiStyle = Constants.Default.box;
|
||||
UIElement[] elems = elements.ToArrayFast(out uint count);
|
||||
UIElement[] elems = elements.ToArrayFast(out int count);
|
||||
if (automaticLayout)
|
||||
{
|
||||
GUILayout.BeginArea(Box, guiStyle);
|
||||
|
@ -60,16 +60,6 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
GUI.EndGroup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The group's unique name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display the group and everything in it.
|
||||
/// </summary>
|
||||
public bool Enabled { set; get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of elements in the group.
|
||||
|
@ -85,19 +75,10 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// <param name="box">The rectangular area to use in the window.</param>
|
||||
/// <param name="name">Name of the group.</param>
|
||||
/// <param name="automaticLayout">Whether to use automatic UI layout.</param>
|
||||
public Group(Rect box, string name = null, bool automaticLayout = false)
|
||||
public Group(Rect box, string name = null, bool automaticLayout = false) : base(box.ToString().Replace(" ", ""), name)
|
||||
{
|
||||
Box = box;
|
||||
if (name == null)
|
||||
{
|
||||
this.Name = typeof(Group).FullName + "::" + box.ToString().Replace(" ", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
this.automaticLayout = automaticLayout;
|
||||
IMGUIManager.AddElement(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -132,7 +113,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
{
|
||||
if (index < 0 || index >= elements.count) return false;
|
||||
IMGUIManager.AddElement(elements[index]); // re-add to global manager
|
||||
elements.RemoveAt(index);
|
||||
elements.RemoveAt((uint) index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -143,7 +124,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// <returns>The element's index, or -1 if not found.</returns>
|
||||
public int IndexOf(UIElement element)
|
||||
{
|
||||
UIElement[] elems = elements.ToArrayFast(out uint count);
|
||||
UIElement[] elems = elements.ToArrayFast(out int count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (elems[i].Name == element.Name)
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using Rewired.Internal;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.ExtraLean;
|
||||
using Svelto.Tasks.ExtraLean.Unity;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TechbloxModdingAPI.Interface.IMGUI
|
||||
|
@ -20,9 +15,9 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// </summary>
|
||||
public static class IMGUIManager
|
||||
{
|
||||
internal static OnGuiRunner ImguiScheduler = new OnGuiRunner("TechbloxModdingAPI_IMGUIScheduler");
|
||||
|
||||
private static Dictionary<string, UIElement> _activeElements = new Dictionary<string,UIElement>();
|
||||
internal static OnGuiRunner ImguiScheduler = new("TechbloxModdingAPI_IMGUIScheduler");
|
||||
|
||||
private static readonly WeakDictionary<string, UIElement> _activeElements = new();
|
||||
|
||||
/// <summary>
|
||||
/// Add an UIElement instance to be managed by IMGUIManager.
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// </summary>
|
||||
public Rect Box { get; set; } = Rect.zero;
|
||||
|
||||
public void OnGUI()
|
||||
public override void OnGUI()
|
||||
{
|
||||
//if (Texture == null) return;
|
||||
if (automaticLayout)
|
||||
|
@ -32,43 +32,16 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
GUI.Label(Box, Texture, Constants.Default.label);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The image element's unique name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display the image and everything in it.
|
||||
/// </summary>
|
||||
public bool Enabled { set; get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Interface.IMGUI.Image"/> class with automatic layout.
|
||||
/// </summary>
|
||||
/// <param name="texture">Image to display.</param>
|
||||
/// <param name="name">The element's name.</param>
|
||||
public Image(Texture texture = null, string name = null)
|
||||
public Image(Texture texture = null, string name = null) : base(texture == null ? "" : $"{texture.name}({texture.width}x{texture.height})", name)
|
||||
{
|
||||
automaticLayout = true;
|
||||
Texture = texture;
|
||||
if (name == null)
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
this.Name = typeof(Image).FullName + "::" + texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Name = typeof(Image).FullName + "::" + texture.name + "(" + texture.width + "x" + texture.height + ")";
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
IMGUIManager.AddElement(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// </summary>
|
||||
public Rect Box { get; set; } = Rect.zero;
|
||||
|
||||
public void OnGUI()
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (automaticLayout)
|
||||
{
|
||||
|
@ -32,34 +32,15 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The label's unique name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display the label.
|
||||
/// </summary>
|
||||
public bool Enabled { set; get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Interface.IMGUI.Label"/> class with automatic layout.
|
||||
/// </summary>
|
||||
/// <param name="initialText">Initial string to display on the label.</param>
|
||||
/// <param name="name">The element's name.</param>
|
||||
public Label(string initialText = null, string name = null)
|
||||
public Label(string initialText = null, string name = null) : base(initialText, name)
|
||||
{
|
||||
automaticLayout = true;
|
||||
Text = initialText;
|
||||
if (name == null)
|
||||
{
|
||||
this.Name = typeof(Label).FullName + "::" + initialText;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
IMGUIManager.AddElement(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// </summary>
|
||||
public event EventHandler<string> OnEdit;
|
||||
|
||||
public void OnGUI()
|
||||
public override void OnGUI()
|
||||
{
|
||||
string editedText = null;
|
||||
if (automaticLayout)
|
||||
|
@ -61,36 +61,17 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text field's unique name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display the text field.
|
||||
/// </summary>
|
||||
public bool Enabled { set; get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the text input field with automatic layout.
|
||||
/// </summary>
|
||||
/// <param name="initialText">Initial text in the input field.</param>
|
||||
/// <param name="name">The text field's name.</param>
|
||||
/// <param name="multiline">Allow multiple lines?</param>
|
||||
public Text(string initialText = null, string name = null, bool multiline = false)
|
||||
public Text(string initialText = null, string name = null, bool multiline = false) : base(initialText, name)
|
||||
{
|
||||
this.Multiline = multiline;
|
||||
automaticLayout = true;
|
||||
text = initialText ?? "";
|
||||
if (name == null)
|
||||
{
|
||||
this.Name = typeof(Text).FullName + "::" + text;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
IMGUIManager.AddElement(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -6,23 +6,34 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
|||
/// GUI Element like a text field, button or picture.
|
||||
/// This interface is used to wrap many elements from Unity's IMGUI system.
|
||||
/// </summary>
|
||||
public interface UIElement
|
||||
public abstract class UIElement
|
||||
{
|
||||
protected UIElement(string text, string name)
|
||||
{
|
||||
Name = name ?? GetType().FullName + "::" + text;
|
||||
IMGUIManager.AddElement(this);
|
||||
}
|
||||
|
||||
~UIElement()
|
||||
{
|
||||
IMGUIManager.RemoveElement(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GUI operations to perform in the OnGUI scope.
|
||||
/// This is basically equivalent to a MonoBehaviour's OnGUI method.
|
||||
/// </summary>
|
||||
void OnGUI();
|
||||
public abstract void OnGUI();
|
||||
|
||||
/// <summary>
|
||||
/// The element's name.
|
||||
/// This should be unique for every instance of the class.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display the UI element or not.
|
||||
/// </summary>
|
||||
bool Enabled { get; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
}
|
||||
}
|
|
@ -3,13 +3,11 @@ using System.Reflection;
|
|||
using HarmonyLib;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Schedulers;
|
||||
using RobocraftX.Services;
|
||||
using Svelto.Context;
|
||||
using Svelto.Tasks.ExtraLean;
|
||||
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Events;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
|
@ -51,34 +49,42 @@ namespace TechbloxModdingAPI
|
|||
harmony.PatchAll(currentAssembly);
|
||||
}
|
||||
catch (Exception e)
|
||||
{ //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet
|
||||
Logging.Log(e.ToString());
|
||||
Logging.LogWarning("Failed to patch Techblox. Attempting to patch to display error...");
|
||||
harmony.Patch(AccessTools.Method(typeof(FullGameCompositionRoot), "OnContextInitialized")
|
||||
.MakeGenericMethod(typeof(UnityContext<FullGameCompositionRoot>)),
|
||||
new HarmonyMethod(((Action) OnPatchError).Method)); //Can't use lambdas here :(
|
||||
{
|
||||
HandleError(e, "Failed to patch Techblox. Attempting to patch to display error...", OnPatchError);
|
||||
return;
|
||||
}
|
||||
|
||||
// init utility
|
||||
Logging.MetaDebugLog($"Initializing Utility");
|
||||
Utility.GameState.Init();
|
||||
// init block implementors
|
||||
Logging.MetaDebugLog($"Initializing Blocks");
|
||||
// init input
|
||||
Input.FakeInput.Init();
|
||||
// init object-oriented classes
|
||||
Player.Init();
|
||||
Block.Init();
|
||||
BlockGroup.Init();
|
||||
Wire.Init();
|
||||
Logging.MetaDebugLog($"Initializing Client");
|
||||
Client.Init();
|
||||
Game.Init();
|
||||
// init UI
|
||||
Interface.IMGUI.Constants.Init();
|
||||
Interface.IMGUI.IMGUIManager.Init();
|
||||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
|
||||
try
|
||||
{
|
||||
// init utility
|
||||
Logging.MetaDebugLog($"Initializing Utility");
|
||||
Utility.GameState.Init();
|
||||
// init block implementors
|
||||
Logging.MetaDebugLog($"Initializing Blocks");
|
||||
// init input
|
||||
Input.FakeInput.Init();
|
||||
// init object-oriented classes
|
||||
Player.Init();
|
||||
Block.Init();
|
||||
BlockGroup.Init();
|
||||
Wire.Init();
|
||||
// init client
|
||||
Logging.MetaDebugLog($"Initializing Client");
|
||||
Client.Init();
|
||||
Game.Init();
|
||||
// init UI
|
||||
Logging.MetaDebugLog($"Initializing UI");
|
||||
Interface.IMGUI.Constants.Init();
|
||||
Interface.IMGUI.IMGUIManager.Init();
|
||||
// init anti-anticheat
|
||||
Logging.MetaDebugLog("Initializing anti-anticheat");
|
||||
AntiAntiCheatPatch.Init(harmony);
|
||||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
HandleError(e, "Failed to initialize the API! Attempting to patch to display error...", OnInitError);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -98,7 +104,7 @@ namespace TechbloxModdingAPI
|
|||
}
|
||||
Scheduler.Dispose();
|
||||
var currentAssembly = Assembly.GetExecutingAssembly();
|
||||
harmony.UnpatchAll(currentAssembly.GetName().Name);
|
||||
harmony.UnpatchSelf();
|
||||
harmony = null;
|
||||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
|
||||
}
|
||||
|
@ -109,5 +115,26 @@ namespace TechbloxModdingAPI
|
|||
ErrorBuilder.DisplayMustQuitError("Failed to patch Techblox!\n" +
|
||||
"Make sure you're using the latest version of TechbloxModdingAPI or disable mods if the API isn't released yet.");
|
||||
}
|
||||
|
||||
private static void OnInitError()
|
||||
{
|
||||
ErrorBuilder.DisplayMustQuitError("Failed to initialize the modding API!\n" +
|
||||
"Make sure you're using the latest version. If you are, please report the error.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles an init error. Logs the exception, a log message, and allows displaying an error in-game.
|
||||
/// </summary>
|
||||
/// <param name="e">The exception</param>
|
||||
/// <param name="logMsg">The log message</param>
|
||||
/// <param name="onInit">The action to run when the game is ready to display error messages</param>
|
||||
private static void HandleError(Exception e, string logMsg, Action onInit)
|
||||
{ //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet
|
||||
Logging.Log(e.ToString());
|
||||
Logging.LogWarning(logMsg);
|
||||
harmony.Patch(AccessTools.Method(typeof(FullGameCompositionRoot), "OnContextInitialized")
|
||||
.MakeGenericMethod(typeof(UnityContext<FullGameCompositionRoot>)),
|
||||
new HarmonyMethod(onInit.Method)); //Can't use lambdas here :(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ using TechbloxModdingAPI.Utility;
|
|||
|
||||
namespace TechbloxModdingAPI.Persistence
|
||||
{
|
||||
[HarmonyPatch]
|
||||
//[HarmonyPatch] - TODO
|
||||
class DeserializeFromDiskEntitiesEnginePatch
|
||||
{
|
||||
internal static EntitiesDB entitiesDB = null;
|
||||
|
@ -26,7 +26,7 @@ namespace TechbloxModdingAPI.Persistence
|
|||
SerializerManager.RegisterSerializers(SaveAndLoadCompositionRootPatch.currentEnginesRoot);
|
||||
uint originalPos = ____serializationData.dataPos;
|
||||
Logging.MetaDebugLog($"dataPos: {originalPos}");
|
||||
BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out uint count), ____serializationData.dataPos);
|
||||
BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out int count), ____serializationData.dataPos);
|
||||
byte[] frameBuffer = new byte[frameStart.Length];
|
||||
Logging.MetaDebugLog($"serial data count: {____serializationData.data.count} capacity: {____serializationData.data.capacity}");
|
||||
int i = 0;
|
||||
|
|
|
@ -4,17 +4,18 @@ using RobocraftX.SaveAndLoad;
|
|||
using Svelto.ECS;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX.StateSync;
|
||||
|
||||
namespace TechbloxModdingAPI.Persistence
|
||||
{
|
||||
[HarmonyPatch(typeof(SaveAndLoadCompositionRoot), "Compose")]
|
||||
[HarmonyPatch(typeof(SaveAndLoadCompositionRoot), "ClientComposeTimeStopped")]
|
||||
class SaveAndLoadCompositionRootPatch
|
||||
{
|
||||
public static EnginesRoot currentEnginesRoot;
|
||||
|
||||
public static void Prefix(EnginesRoot enginesRoot)
|
||||
public static void Prefix(StateSyncRegistrationHelper stateSyncHelper)
|
||||
{
|
||||
currentEnginesRoot = enginesRoot;
|
||||
currentEnginesRoot = stateSyncHelper.enginesRoot;
|
||||
//SerializerManager.RegisterSerializers(enginesRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ namespace TechbloxModdingAPI.Persistence
|
|||
Logging.MetaDebugLog("Skipping component serialization: no serializers registered!");
|
||||
return;
|
||||
}
|
||||
serializationData.data.ExpandBy((uint)frameStart.Length);
|
||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint buffLen), serializationData.dataPos);
|
||||
serializationData.data.IncreaseCapacityBy((uint)frameStart.Length);
|
||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int buffLen), serializationData.dataPos);
|
||||
uint originalPos = serializationData.dataPos;
|
||||
Logging.MetaDebugLog($"dataPos: {originalPos}");
|
||||
// Add frame start so it's easier to find TechbloxModdingAPI-serialized components
|
||||
|
@ -35,14 +35,14 @@ namespace TechbloxModdingAPI.Persistence
|
|||
bbw.Write(frameStart[i]);
|
||||
}
|
||||
Logging.MetaDebugLog($"dataPos (after frame start): {bbw.Position}");
|
||||
serializationData.data.ExpandBy(4u);
|
||||
serializationData.data.IncreaseCapacityBy(4u);
|
||||
bbw.Write((uint)SerializerManager.GetSerializersCount());
|
||||
string[] serializerKeys = SerializerManager.GetSerializerNames();
|
||||
for (uint c = 0; c < serializerKeys.Length; c++)
|
||||
{
|
||||
Logging.MetaDebugLog($"dataPos (loop start): {bbw.Position}");
|
||||
// write component info
|
||||
serializationData.data.ExpandBy(4u + (uint)serializerKeys[c].Length);
|
||||
serializationData.data.IncreaseCapacityBy(4u + (uint)serializerKeys[c].Length);
|
||||
bbw.Write((uint)serializerKeys[c].Length);
|
||||
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(serializerKeys[c]);
|
||||
|
@ -51,7 +51,7 @@ namespace TechbloxModdingAPI.Persistence
|
|||
bbw.Write(nameBytes[i]);
|
||||
}
|
||||
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
||||
serializationData.data.ExpandBy(4u);
|
||||
serializationData.data.IncreaseCapacityBy(4u);
|
||||
serializationData.dataPos = bbw.Position + 4u;
|
||||
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
||||
Logging.MetaDebugLog($"dataPos (appears to be): {serializationData.dataPos}");
|
||||
|
@ -73,8 +73,8 @@ namespace TechbloxModdingAPI.Persistence
|
|||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return typeof(SaveGameEngine).GetMethod("SerializeGameToBuffer");
|
||||
{
|
||||
return AccessTools.TypeByName("RobocraftX.SaveAndLoad.SaveGameEngine").GetMethod("SerializeGameToBuffer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace TechbloxModdingAPI.Persistence
|
|||
|
||||
public bool Deserialize(ref ISerializationData serializationData, IEntitySerialization entitySerializer)
|
||||
{
|
||||
BinaryBufferReader bbr = new BinaryBufferReader(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos);
|
||||
BinaryBufferReader bbr = new BinaryBufferReader(serializationData.data.ToArrayFast(out int count), serializationData.dataPos);
|
||||
uint entityCount = bbr.ReadUint();
|
||||
serializationData.dataPos = bbr.Position;
|
||||
for (uint i = 0; i < entityCount; i++)
|
||||
|
@ -46,8 +46,8 @@ namespace TechbloxModdingAPI.Persistence
|
|||
|
||||
public bool Serialize(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer)
|
||||
{
|
||||
serializationData.data.ExpandBy(4u);
|
||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos);
|
||||
serializationData.data.IncreaseCapacityBy(4u);
|
||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int count), serializationData.dataPos);
|
||||
EGID[] toSerialize = getEntitiesToSerialize(entitiesDB);
|
||||
bbw.Write((uint)toSerialize.Length);
|
||||
serializationData.dataPos = bbw.Position;
|
||||
|
|
52
TechbloxModdingAPI/Player.Events.cs
Normal file
52
TechbloxModdingAPI/Player.Events.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
public partial class Player
|
||||
{
|
||||
internal WrappedHandler<PlayerSeatEventArgs> seatEntered;
|
||||
public event EventHandler<PlayerSeatEventArgs> SeatEntered
|
||||
{
|
||||
add => seatEntered += value;
|
||||
remove => seatEntered -= value;
|
||||
}
|
||||
|
||||
internal WrappedHandler<PlayerSeatEventArgs> seatExited;
|
||||
public event EventHandler<PlayerSeatEventArgs> SeatExited
|
||||
{
|
||||
add => seatExited += value;
|
||||
remove => seatExited -= value;
|
||||
}
|
||||
|
||||
internal static WrappedHandler<PlayerEventArgs> joined;
|
||||
|
||||
public static event EventHandler<PlayerEventArgs> Joined
|
||||
{
|
||||
add => joined += value;
|
||||
remove => joined -= value;
|
||||
}
|
||||
|
||||
internal static WrappedHandler<PlayerEventArgs> left;
|
||||
|
||||
public static event EventHandler<PlayerEventArgs> Left
|
||||
{
|
||||
add => left += value;
|
||||
remove => left -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PlayerSeatEventArgs
|
||||
{
|
||||
public EGID SeatId;
|
||||
public Seat Seat => (Seat)Block.New(SeatId);
|
||||
}
|
||||
|
||||
public struct PlayerEventArgs
|
||||
{
|
||||
public EGID PlayerId;
|
||||
public Player Player => Player.GetInstance(PlayerId.entityID);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
using System;
|
||||
using Gamecraft.Wires;
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Character.Movement;
|
||||
using Unity.Mathematics;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Common.Players;
|
||||
using RobocraftX.GUI.Wires;
|
||||
using RobocraftX.Physics;
|
||||
using Svelto.ECS;
|
||||
using Techblox.BuildingDrone;
|
||||
using Techblox.Camera;
|
||||
using Techblox.FlyCam;
|
||||
using Techblox.Character;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Players;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
@ -18,10 +21,11 @@ namespace TechbloxModdingAPI
|
|||
/// <summary>
|
||||
/// An in-game player character. Any Leo you see is a player.
|
||||
/// </summary>
|
||||
public class Player : IEquatable<Player>, IEquatable<EGID>
|
||||
public partial class Player : EcsObjectBase, IEquatable<Player>, IEquatable<EGID>
|
||||
{
|
||||
// static functionality
|
||||
private static PlayerEngine playerEngine = new PlayerEngine();
|
||||
private static readonly PlayerEngine playerEngine = new PlayerEngine();
|
||||
private static readonly PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine();
|
||||
private static Player localPlayer;
|
||||
|
||||
/// <summary>
|
||||
|
@ -61,23 +65,32 @@ namespace TechbloxModdingAPI
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current player belonging to this client.
|
||||
/// Returns the current player belonging to this client. It will be different after entering/leaving simulation.
|
||||
/// May return null if the local player doesn't exist.
|
||||
/// </summary>
|
||||
public static Player LocalPlayer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer())
|
||||
localPlayer = new Player(PlayerType.Local);
|
||||
var playerId = playerEngine.GetLocalPlayer();
|
||||
if (playerId == uint.MaxValue) return null;
|
||||
if (localPlayer == null || localPlayer.Id != playerId)
|
||||
localPlayer = GetInstance(playerId);
|
||||
return localPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Player GetInstance(uint id)
|
||||
{
|
||||
return EcsObjectBase.GetInstance(new EGID(id, CharacterExclusiveGroups.OnFootGroup),
|
||||
e => new Player(e.entityID));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The player's unique identifier.</param>
|
||||
public Player(uint id)
|
||||
public Player(uint id) : base(new EGID(id, CharacterExclusiveGroups.OnFootGroup))
|
||||
{
|
||||
this.Id = id;
|
||||
if (!Exists(id))
|
||||
|
@ -91,22 +104,32 @@ namespace TechbloxModdingAPI
|
|||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
|
||||
public Player(PlayerType player)
|
||||
public Player(PlayerType player) : base(ecs =>
|
||||
{
|
||||
switch (player)
|
||||
{
|
||||
case PlayerType.Local:
|
||||
this.Id = playerEngine.GetLocalPlayer();
|
||||
break;
|
||||
case PlayerType.Remote:
|
||||
this.Id = playerEngine.GetRemotePlayer();
|
||||
break;
|
||||
}
|
||||
if (this.Id == uint.MaxValue)
|
||||
{
|
||||
throw new PlayerNotFoundException($"No player of {player} type exists");
|
||||
}
|
||||
this.Type = player;
|
||||
uint id;
|
||||
switch (player)
|
||||
{
|
||||
case PlayerType.Local:
|
||||
id = playerEngine.GetLocalPlayer();
|
||||
break;
|
||||
case PlayerType.Remote:
|
||||
id = playerEngine.GetRemotePlayer();
|
||||
break;
|
||||
default:
|
||||
id = uint.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (id == uint.MaxValue)
|
||||
{
|
||||
throw new PlayerNotFoundException($"No player of {player} type exists");
|
||||
}
|
||||
|
||||
return new EGID(id, CharacterExclusiveGroups.OnFootGroup);
|
||||
})
|
||||
{
|
||||
this.Type = player;
|
||||
Id = base.Id.entityID;
|
||||
}
|
||||
|
||||
// object fields & properties
|
||||
|
@ -122,7 +145,7 @@ namespace TechbloxModdingAPI
|
|||
/// The player's unique identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public uint Id { get; }
|
||||
public new uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The player's current position.
|
||||
|
@ -141,10 +164,10 @@ namespace TechbloxModdingAPI
|
|||
public float3 Rotation
|
||||
{
|
||||
get => ((Quaternion) (GameState.IsBuildMode()
|
||||
? playerEngine.GetCameraStruct<CharacterCameraEntityStruct>(Id).Get().rotation
|
||||
? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation
|
||||
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation)).eulerAngles;
|
||||
set => _ = GameState.IsBuildMode()
|
||||
? playerEngine.GetCameraStruct<CharacterCameraEntityStruct>(Id).Get().rotation = quaternion.Euler(value)
|
||||
? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation = quaternion.Euler(value)
|
||||
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation = quaternion.Euler(value);
|
||||
}
|
||||
|
||||
|
@ -172,10 +195,8 @@ namespace TechbloxModdingAPI
|
|||
/// The player's mass.
|
||||
/// </summary>
|
||||
/// <value>The mass.</value>
|
||||
public float Mass =>
|
||||
1f / playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().physicsMass.InverseMass;
|
||||
|
||||
private float _ping = -1f;
|
||||
[Obsolete] // We cannot get it clientside or something
|
||||
public float Mass => 0;
|
||||
|
||||
/// <summary>
|
||||
/// The player's latest network ping time.
|
||||
|
@ -185,12 +206,7 @@ namespace TechbloxModdingAPI
|
|||
{
|
||||
get
|
||||
{
|
||||
var opt = playerEngine.GetPlayerStruct<PlayerNetworkStatsEntityStruct>(Id, Type);
|
||||
if (opt)
|
||||
{
|
||||
_ping = opt.Get().lastPingTimeSinceLevelLoad ?? _ping;
|
||||
}
|
||||
return _ping;
|
||||
return playerEngine.GetPing() / 1000f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,15 +214,16 @@ namespace TechbloxModdingAPI
|
|||
/// The player's initial health when entering Simulation (aka Time Running) mode.
|
||||
/// </summary>
|
||||
/// <value>The initial health.</value>
|
||||
[Obsolete("We can no longer get initial health, returns max health.")]
|
||||
public float InitialHealth
|
||||
{
|
||||
get
|
||||
{
|
||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
|
||||
return opt ? opt.Get().initialHealth : -1f;
|
||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id);
|
||||
return opt ? opt.Get().maxHealth : -1f;
|
||||
}
|
||||
|
||||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().initialHealth = value;
|
||||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().maxHealth = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -217,30 +234,25 @@ namespace TechbloxModdingAPI
|
|||
{
|
||||
get
|
||||
{
|
||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
|
||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id);
|
||||
return opt ? opt.Get().currentHealth : -1f;
|
||||
}
|
||||
|
||||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().currentHealth = value;
|
||||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().currentHealth = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
|
||||
[Obsolete("Players are probably always damageable")]
|
||||
public bool Damageable
|
||||
{
|
||||
get
|
||||
{
|
||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
|
||||
return opt.Get().canTakeDamageStat;
|
||||
}
|
||||
get => true;
|
||||
|
||||
// ReSharper disable once ValueParameterNotUsed
|
||||
set
|
||||
{
|
||||
ref var healthStruct = ref playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get();
|
||||
healthStruct.canTakeDamage = value;
|
||||
healthStruct.canTakeDamageStat = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,30 +260,26 @@ namespace TechbloxModdingAPI
|
|||
/// The player's lives when initially entering Simulation (aka Time Running) mode.
|
||||
/// </summary>
|
||||
/// <value>The initial lives.</value>
|
||||
[Obsolete("The player has infinite lives")]
|
||||
public uint InitialLives
|
||||
{
|
||||
get
|
||||
{
|
||||
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
|
||||
return opt ? opt.Get().initialLives : uint.MaxValue;
|
||||
}
|
||||
get => uint.MaxValue;
|
||||
|
||||
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().initialLives = value;
|
||||
// ReSharper disable once ValueParameterNotUsed
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's current lives in Simulation (aka Time Running) mode.
|
||||
/// </summary>
|
||||
/// <value>The current lives.</value>
|
||||
[Obsolete("The player has infinite lives")]
|
||||
public uint CurrentLives
|
||||
{
|
||||
get
|
||||
{
|
||||
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
|
||||
return opt ? opt.Get().currentLives : uint.MaxValue;
|
||||
}
|
||||
get => uint.MaxValue;
|
||||
|
||||
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().currentLives = value;
|
||||
// ReSharper disable once ValueParameterNotUsed
|
||||
set { }
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
|
@ -302,7 +310,7 @@ namespace TechbloxModdingAPI
|
|||
get
|
||||
{
|
||||
var optstruct = playerEngine.GetCharacterStruct<EquippedPartStruct>(Id);
|
||||
return optstruct ? (BlockIDs) optstruct.Get().SelectedDBPartID : BlockIDs.Invalid;
|
||||
return optstruct ? (BlockIDs) optstruct.Get().selectedDBPartID : BlockIDs.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,8 +356,20 @@ namespace TechbloxModdingAPI
|
|||
/// <summary>
|
||||
/// The player's mode in time stopped mode, determining what they place.
|
||||
/// </summary>
|
||||
public PlayerBuildingMode BuildingMode => (PlayerBuildingMode) playerEngine
|
||||
.GetCharacterStruct<PlayerInputTimeStoppedContextStruct>(Id).Get().timeStoppedContext;
|
||||
public PlayerBuildingMode BuildingMode => (PlayerBuildingMode)Math.Log((double)playerEngine
|
||||
.GetCharacterStruct<TimeStoppedModeComponent>(Id).Get().timeStoppedContext, 2); // It's a bit field in game now
|
||||
|
||||
public PlayerState State =>
|
||||
playerEngine.GetCharacterStruct<CharacterTagEntityStruct>(Id).Get().ID.groupID switch
|
||||
{
|
||||
var group when group == CharacterExclusiveGroups.MachineSpawningGroup => PlayerState.HoldingMachine,
|
||||
var group when group == CharacterExclusiveGroups.OnFootGroup => PlayerState.OnFoot,
|
||||
var group when group == CharacterExclusiveGroups.InPilotSeatGroup => PlayerState.InSeat,
|
||||
var group when group == CharacterExclusiveGroups.DyingOnFootGroup => PlayerState.OnFoot,
|
||||
var group when group == CharacterExclusiveGroups.DyingInPilotSeatGroup => PlayerState.InSeat,
|
||||
var group when group == CharacterExclusiveGroups.DeadGroup => PlayerState.OnFoot,
|
||||
_ => throw new ArgumentOutOfRangeException("", "Unknown player state")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player is sprinting.
|
||||
|
@ -357,10 +377,10 @@ namespace TechbloxModdingAPI
|
|||
public bool Sprinting
|
||||
{
|
||||
get => GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementComponent>(Id).Get().sprinting
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting;
|
||||
set => _ = GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementComponent>(Id).Get().sprinting = value
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting = value
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting = value;
|
||||
}
|
||||
|
||||
|
@ -370,10 +390,10 @@ namespace TechbloxModdingAPI
|
|||
public float SpeedSetting
|
||||
{
|
||||
get => GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speed
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed;
|
||||
set => _ = GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speed = value
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed = value
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed = value;
|
||||
}
|
||||
|
||||
|
@ -383,10 +403,10 @@ namespace TechbloxModdingAPI
|
|||
public float SpeedSprintMultiplierSetting
|
||||
{
|
||||
get => GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speedSprintMultiplier
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier;
|
||||
set => _ = GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speedSprintMultiplier = value
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier = value
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier = value;
|
||||
}
|
||||
|
||||
|
@ -396,10 +416,10 @@ namespace TechbloxModdingAPI
|
|||
public float AccelerationSetting
|
||||
{
|
||||
get => GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().acceleration
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration;
|
||||
set => _ = GameState.IsBuildMode()
|
||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().acceleration = value
|
||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration = value
|
||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration = value;
|
||||
}
|
||||
|
||||
|
@ -422,6 +442,39 @@ namespace TechbloxModdingAPI
|
|||
}
|
||||
playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enter the given seat.
|
||||
/// </summary>
|
||||
/// <param name="seat">The seat to enter.</param>
|
||||
public void EnterSeat(Seat seat)
|
||||
{
|
||||
playerEngine.EnterSeat(Id, seat.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exit the seat the player is currently in.
|
||||
/// </summary>
|
||||
public void ExitSeat()
|
||||
{
|
||||
playerEngine.ExitSeat(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn the machine the player is holding in time running mode.
|
||||
/// </summary>
|
||||
public bool SpawnMachine()
|
||||
{
|
||||
return playerEngine.SpawnMachine(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Despawn the player's machine in time running mode and place it in their hand.
|
||||
/// </summary>
|
||||
public bool DespawnMachine()
|
||||
{
|
||||
return playerEngine.DespawnMachine(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the block the player is currently looking at in build mode.
|
||||
|
@ -431,9 +484,10 @@ namespace TechbloxModdingAPI
|
|||
public Block GetBlockLookedAt(float maxDistance = -1f)
|
||||
{
|
||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||
return egid != EGID.Empty && egid.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||
return egid != default && egid.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||
&& egid.groupID != WiresGUIExclusiveGroups.WireGroup
|
||||
? Block.New(egid)
|
||||
: null;
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -444,8 +498,22 @@ namespace TechbloxModdingAPI
|
|||
public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
|
||||
{
|
||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||
return egid != EGID.Empty && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||
? new SimBody(egid)
|
||||
return egid != default && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||
? EcsObjectBase.GetInstance(egid, e => new SimBody(e))
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the wire the player is currently looking at in build mode.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
|
||||
/// <returns>The wire or null if not found</returns>
|
||||
public Wire GetWireLookedAt(float maxDistance = -1f)
|
||||
{
|
||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||
return egid != default && egid.groupID == WiresGUIExclusiveGroups.WireGroup
|
||||
? EcsObjectBase.GetInstance(new EGID(egid.entityID, BuildModeWiresGroups.WiresGroup.Group),
|
||||
e => new Wire(e))
|
||||
: null;
|
||||
}
|
||||
|
||||
|
@ -458,6 +526,15 @@ namespace TechbloxModdingAPI
|
|||
return playerEngine.GetSelectedBlocks(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ghost block that shows the block to be placed in build mode.
|
||||
/// </summary>
|
||||
/// <returns>A block instance or null if not found</returns>
|
||||
public Block GetGhostBlock()
|
||||
{
|
||||
return playerEngine.GetGhostBlock(Id);
|
||||
}
|
||||
|
||||
public bool Equals(Player other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
|
@ -485,11 +562,17 @@ namespace TechbloxModdingAPI
|
|||
return (int) Id;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Type)}: {Type}, {nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(Mass)}: {Mass}";
|
||||
}
|
||||
|
||||
// internal methods
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
Utility.GameEngineManager.AddGameEngine(playerEngine);
|
||||
Utility.GameEngineManager.AddGameEngine(playerEventsEngine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
ColourMode,
|
||||
ConfigMode,
|
||||
BlueprintMode,
|
||||
MaterialMode
|
||||
MaterialMode,
|
||||
LandscapeMode
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using RobocraftX.Character;
|
||||
|
@ -10,22 +8,24 @@ using RobocraftX.Common.Input;
|
|||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||
using RobocraftX.Physics;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using Gamecraft.GUI.HUDFeedbackBlocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using Svelto.ECS;
|
||||
using Techblox.Camera;
|
||||
using Techblox.FlyCam;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Physics;
|
||||
using UnityEngine;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS.DataStructures;
|
||||
using Techblox.BuildingDrone;
|
||||
using Techblox.Character;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Input;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Players
|
||||
{
|
||||
internal class PlayerEngine : IApiEngine, IFactoryEngine
|
||||
internal class PlayerEngine : IFunEngine
|
||||
{
|
||||
public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine";
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace TechbloxModdingAPI.Players
|
|||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
public IEntityFunctions Functions { get; set; }
|
||||
|
||||
private bool isReady = false;
|
||||
|
||||
|
@ -50,10 +50,10 @@ namespace TechbloxModdingAPI.Players
|
|||
public uint GetLocalPlayer()
|
||||
{
|
||||
if (!isReady) return uint.MaxValue;
|
||||
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToBuffer();
|
||||
if (localPlayers.count > 0)
|
||||
var (localPlayers, count) = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
|
||||
if (count > 0)
|
||||
{
|
||||
return localPlayers.buffer[0].ID.entityID;
|
||||
return localPlayers[0].ID.entityID;
|
||||
}
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
@ -61,10 +61,10 @@ namespace TechbloxModdingAPI.Players
|
|||
public uint GetRemotePlayer()
|
||||
{
|
||||
if (!isReady) return uint.MaxValue;
|
||||
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToBuffer();
|
||||
if (localPlayers.count > 0)
|
||||
var (localPlayers, count) = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
||||
if (count > 0)
|
||||
{
|
||||
return localPlayers.buffer[0].ID.entityID;
|
||||
return localPlayers[0].ID.entityID;
|
||||
}
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
@ -107,26 +107,16 @@ namespace TechbloxModdingAPI.Players
|
|||
return false;
|
||||
if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
|
||||
{
|
||||
EGID egid = new EGID(playerId, group);
|
||||
entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
|
||||
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
|
||||
ExitSeat(playerId);
|
||||
}
|
||||
rbesOpt.Get().position = location;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetGameOverScreen(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref HudActivatedBlocksEntityStruct habes = ref entitiesDB.QueryEntity<HudActivatedBlocksEntityStruct>(HUDFeedbackBlocksGUIExclusiveGroups.GameOverHudEgid);
|
||||
NativeDynamicArrayCast<EGID> nativeDynamicArrayCast = new NativeDynamicArrayCast<EGID>(habes.activatedBlocksOrdered);
|
||||
return nativeDynamicArrayCast.count > 0;
|
||||
}
|
||||
|
||||
public bool IsDead(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return true;
|
||||
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
|
||||
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadGroup);
|
||||
}
|
||||
|
||||
// reusable methods
|
||||
|
@ -140,8 +130,8 @@ namespace TechbloxModdingAPI.Players
|
|||
{
|
||||
group = default;
|
||||
if (GameState.IsBuildMode())
|
||||
return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, Techblox.FlyCam.FlyCam.Group));
|
||||
|
||||
return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, LocalBuildingDrone.BuildGroup));
|
||||
|
||||
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
||||
for (int i = 0; i < characterGroups.count; i++)
|
||||
{
|
||||
|
@ -167,34 +157,35 @@ namespace TechbloxModdingAPI.Players
|
|||
|
||||
public OptionalRef<T> GetCameraStruct<T>(uint playerId) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, CameraExclusiveGroups.CameraGroup));
|
||||
return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup));
|
||||
}
|
||||
|
||||
public EGID GetThingLookedAt(uint playerId, float maxDistance = -1f)
|
||||
{
|
||||
var opt = GetCameraStruct<CharacterCameraRayCastEntityStruct>(playerId);
|
||||
if (!opt) return EGID.Empty;
|
||||
CharacterCameraRayCastEntityStruct rayCast = opt;
|
||||
var opt = GetCameraStruct<PhysicCameraRayCastEntityStruct>(playerId);
|
||||
if (!opt) return default;
|
||||
PhysicCameraRayCastEntityStruct rayCast = opt;
|
||||
EGID physicCameraEgid = new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup);
|
||||
float distance = maxDistance < 0
|
||||
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast,
|
||||
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, physicCameraEgid,
|
||||
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
|
||||
: maxDistance;
|
||||
if (rayCast.hit && rayCast.distance <= distance)
|
||||
return rayCast.hitEgid; //May be EGID.Empty
|
||||
return rayCast.hitEgid; //May be EGID.Empty (default)
|
||||
|
||||
return EGID.Empty;
|
||||
return default;
|
||||
}
|
||||
|
||||
public unsafe Block[] GetSelectedBlocks(uint playerid)
|
||||
{
|
||||
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
|
||||
return new Block[0];
|
||||
return Array.Empty<Block>();
|
||||
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||
if (!state.active) return new Block[0];
|
||||
if (!state.active) return Array.Empty<Block>();
|
||||
var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
|
||||
var ret = new Block[blocks.count];
|
||||
for (int j = 0; j < blocks.count; j++)
|
||||
|
@ -205,5 +196,79 @@ namespace TechbloxModdingAPI.Players
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void EnterSeat(uint playerId, EGID seatId)
|
||||
{
|
||||
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
|
||||
return;
|
||||
/*PilotSeatGroupUtils.SwapTagTo<OCCUPIED_TAG>(Functions, seatId);
|
||||
var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(playerId, out var group);
|
||||
if (!opt) return; - TODO: This is server code and mods run in client code atm. We can only send inputs even in singleplayer as it is.
|
||||
ref CharacterPilotSeatEntityStruct charSeat = ref opt.Get();
|
||||
var charId = new EGID(playerId, group);
|
||||
charSeat.pilotSeatEntity = entitiesDB.GetEntityReference(seatId);
|
||||
charSeat.entryPositionOffset =
|
||||
entitiesDB.QueryEntity<PositionEntityStruct>(charId).position -
|
||||
entitiesDB.QueryEntity<PositionEntityStruct>(seatId).position;
|
||||
ref var seat = ref entitiesDB.QueryEntity<PilotSeatEntityStruct>(seatId);
|
||||
seat.occupyingCharacter = entitiesDB.GetEntityReference(charId);
|
||||
charSeat.followCam = entitiesDB.QueryEntity<SeatFollowCamComponent>(seatId).followCam;
|
||||
Functions.SwapEntityGroup<CharacterEntityDescriptor>(charId, CharacterExclusiveGroups.InPilotSeatGroup);*/
|
||||
}
|
||||
|
||||
public void ExitSeat(uint playerId)
|
||||
{
|
||||
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
|
||||
return;
|
||||
/*EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup);
|
||||
var opt = entitiesDB.QueryEntityOptional<CharacterPilotSeatEntityStruct>(egid);
|
||||
if (!opt) return;
|
||||
opt.Get().instantExit = true;
|
||||
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);*/
|
||||
}
|
||||
|
||||
public bool SpawnMachine(uint playerId)
|
||||
{
|
||||
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
|
||||
return false;
|
||||
EGID egid = new EGID(playerId, CharacterExclusiveGroups.MachineSpawningGroup);
|
||||
if (!entitiesDB.Exists<CharacterTagEntityStruct>(egid))
|
||||
return false;
|
||||
if (entitiesDB.QueryEntity<CharacterMachineSpawningValidityComponent>(egid).isMachinePlacementInvalid)
|
||||
{
|
||||
Logging.MetaDebugLog("Machine placement invalid");
|
||||
return false;
|
||||
}
|
||||
//Functions.SwapEntityGroup<CharacterEntityDescriptor>(egid, CharacterExclusiveGroups.OnFootGroup);
|
||||
FakeInput.ActionInput(playerId, primary: true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DespawnMachine(uint playerId)
|
||||
{
|
||||
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
|
||||
return false;
|
||||
GetCharacterStruct<CharacterTagEntityStruct>(playerId, out var group);
|
||||
if (group.isInvalid)
|
||||
return false;
|
||||
EGID egid = new EGID(playerId, group);
|
||||
if (!entitiesDB.Exists<CharacterTagEntityStruct>(egid))
|
||||
return false;
|
||||
Functions.SwapEntityGroup<CharacterEntityDescriptor>(egid, CharacterExclusiveGroups.MachineSpawningGroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
public uint GetPing()
|
||||
{
|
||||
return entitiesDB
|
||||
.QueryUniqueEntity<NetworkStatsEntityStruct>(MultiplayerExclusiveGroups.MultiplayerStateGroup)
|
||||
.networkStats.PingMs;
|
||||
}
|
||||
|
||||
public Block GetGhostBlock(uint playerId)
|
||||
{
|
||||
var egid = new EGID(playerId, GHOST_BLOCKS_ENABLED.Group);
|
||||
return entitiesDB.Exists<DBEntityStruct>(egid) ? Block.New(egid) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
45
TechbloxModdingAPI/Players/PlayerEventsEngine.cs
Normal file
45
TechbloxModdingAPI/Players/PlayerEventsEngine.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Character.Movement;
|
||||
using RobocraftX.Common.Input;
|
||||
using Svelto.ECS;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
|
||||
namespace TechbloxModdingAPI.Players
|
||||
{
|
||||
public class PlayerEventsEngine : IApiEngine, IReactOnSwap<CharacterPilotSeatEntityStruct>, IReactOnAddAndRemove<PlayerIDStruct>
|
||||
{
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name => "TechbloxModdingAPIPlayerEventsEngine";
|
||||
public bool isRemovable => false;
|
||||
|
||||
public void MovedTo(ref CharacterPilotSeatEntityStruct entityComponent, ExclusiveGroupStruct previousGroup, EGID egid)
|
||||
{
|
||||
entitiesDB.TryGetEGID(entityComponent.pilotSeatEntity, out var seatId); //TODO: Can't get EGID
|
||||
var player = Player.GetInstance(egid.entityID);
|
||||
if (previousGroup == CharacterExclusiveGroups.InPilotSeatGroup)
|
||||
player.seatExited.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId});
|
||||
else if (egid.groupID == CharacterExclusiveGroups.InPilotSeatGroup)
|
||||
player.seatEntered.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId });
|
||||
}
|
||||
|
||||
public void Add(ref PlayerIDStruct entityComponent, EGID egid)
|
||||
{
|
||||
Player.joined.Invoke(this, new PlayerEventArgs { PlayerId = egid });
|
||||
}
|
||||
|
||||
public void Remove(ref PlayerIDStruct entityComponent, EGID egid)
|
||||
{
|
||||
Player.left.Invoke(this, new PlayerEventArgs { PlayerId = egid });
|
||||
}
|
||||
}
|
||||
}
|
9
TechbloxModdingAPI/Players/PlayerState.cs
Normal file
9
TechbloxModdingAPI/Players/PlayerState.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace TechbloxModdingAPI.Players
|
||||
{
|
||||
public enum PlayerState
|
||||
{
|
||||
HoldingMachine,
|
||||
OnFoot,
|
||||
InSeat
|
||||
}
|
||||
}
|
|
@ -1,9 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Enumerators;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI;
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Players
|
||||
{
|
||||
|
@ -24,12 +29,62 @@ namespace TechbloxModdingAPI.Players
|
|||
[APITestCase(TestType.EditMode)]
|
||||
public static void PositionTest()
|
||||
{
|
||||
Player p = new Player(PlayerType.Local);
|
||||
Player p = Player.LocalPlayer;
|
||||
if (!Assert.Errorless(() => { p.Teleport(0, 0, 0, relative: false); }, "Player.Teleport(origin) errored: ", "Player teleported to origin successfully.")) return;
|
||||
if (!Assert.CloseTo(p.Position, float3.zero, "Player is not close to origin despite being teleported there.", "Player.Position is at origin.")) return;
|
||||
if (!Assert.Errorless(() => { p.Position = float3.zero + 1; }, "Player.Position = origin+1 errored: ", "Player moved to origin+1.")) return;
|
||||
Assert.CloseTo(p.Position, float3.zero + 1, "Player is not close to origin+1 despite being teleported there.", "Player.Position is at origin+1.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Game)]
|
||||
public static void SeatEventTestBuild()
|
||||
{
|
||||
Block.PlaceNew(BlockIDs.DriverSeat, Player.LocalPlayer.Position);
|
||||
}
|
||||
|
||||
[APITestCase(TestType.SimulationMode)]
|
||||
public static IEnumerator<TaskContract> SeatEventTestSim()
|
||||
{
|
||||
Player.LocalPlayer.SeatEntered += Assert.CallsBack<PlayerSeatEventArgs>("SeatEntered");
|
||||
Player.LocalPlayer.SeatExited += Assert.CallsBack<PlayerSeatEventArgs>("SeatExited");
|
||||
Assert.Equal(Player.LocalPlayer.SpawnMachine(), true, "Failed to spawn the player's machine.", "Successfully spawned the player's machine.");
|
||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||
var seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat);
|
||||
int c = 0;
|
||||
while (seats.Length == 0 && c < 10)
|
||||
{
|
||||
Logging.MetaLog("Waiting for a seat to be spawned...");
|
||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||
Logging.MetaLog("Spawn machine: " + Player.LocalPlayer.SpawnMachine());
|
||||
seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat);
|
||||
c++;
|
||||
}
|
||||
|
||||
if (seats.Length == 0)
|
||||
{
|
||||
Assert.Fail("No driver seat found!");
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (seats[0] is Seat seat)
|
||||
{
|
||||
Assert.Errorless(() => Player.LocalPlayer.EnterSeat(seat), "Failed to enter seat.",
|
||||
"Entered seat successfully.");
|
||||
while (Player.LocalPlayer.State != PlayerState.InSeat)
|
||||
{
|
||||
bool cont = false;
|
||||
Client.Instance.PromptUser(new SingleChoicePrompt("Testing", $"Enter the seat at {seat.Position} pls", "OK", () => cont = true));
|
||||
while (!cont)
|
||||
yield return Yield.It;
|
||||
yield return new WaitForSecondsEnumerator(5f).Continue();
|
||||
}
|
||||
}
|
||||
else
|
||||
Assert.Fail("Found a seat that is not a seat!");
|
||||
yield return new WaitForSecondsEnumerator(5).Continue();
|
||||
Assert.Errorless(() => Player.LocalPlayer.ExitSeat(), "Failed to exit seat.",
|
||||
"Exited seat successfully.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void InvalidStateTest()
|
||||
|
|
|
@ -6,6 +6,7 @@ using UnityEngine;
|
|||
using Gamecraft.Damage;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Physics;
|
||||
using Techblox.TimeRunning.Clusters;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
|
@ -14,20 +15,20 @@ namespace TechbloxModdingAPI
|
|||
/// </summary>
|
||||
public class SimBody : EcsObjectBase, IEquatable<SimBody>, IEquatable<EGID>
|
||||
{
|
||||
public override EGID Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist.
|
||||
/// Get the SimBody from a Block if possible for good performance here.
|
||||
/// </summary>
|
||||
public Cluster Cluster => cluster ?? (cluster = clusterId == uint.MaxValue ? Block.BlockEngine.GetCluster(Id.entityID) : new Cluster(clusterId));
|
||||
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
|
||||
: GetInstance(new EGID(clusterId, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP),
|
||||
egid => new Cluster(egid)); // Otherwise get the cluster from the ID
|
||||
|
||||
private Cluster cluster;
|
||||
private readonly uint clusterId = uint.MaxValue;
|
||||
|
||||
public SimBody(EGID id)
|
||||
public SimBody(EGID id) : base(id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public SimBody(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_BODIES_GROUP))
|
||||
|
@ -73,15 +74,16 @@ namespace TechbloxModdingAPI
|
|||
}
|
||||
}
|
||||
|
||||
[Obsolete] //Cannot get mass even from UECS
|
||||
public float Mass
|
||||
{
|
||||
get => math.rcp(GetStruct().physicsMass.InverseMass);
|
||||
get => 0f;
|
||||
//set => GetStruct().physicsMass.InverseMass = math.rcp(value);
|
||||
}
|
||||
|
||||
public float3 CenterOfMass
|
||||
{
|
||||
get => GetStruct().physicsMass.CenterOfMass;
|
||||
get => 0f; //TODO
|
||||
//set => GetStruct().physicsMass.CenterOfMass = value;
|
||||
}
|
||||
|
||||
|
@ -92,20 +94,20 @@ namespace TechbloxModdingAPI
|
|||
|
||||
public float InitialHealth
|
||||
{
|
||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth;
|
||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth = value;
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
public float CurrentHealth
|
||||
{
|
||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth;
|
||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth = value;
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
public float HealthMultiplier
|
||||
{
|
||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier;
|
||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier = value;
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
17
TechbloxModdingAPI/Tasks/OnGuiRunner.cs
Normal file
17
TechbloxModdingAPI/Tasks/OnGuiRunner.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System.Collections;
|
||||
using RobocraftX.Schedulers;
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.ExtraLean;
|
||||
using Svelto.Tasks.Unity.Internal;
|
||||
|
||||
namespace TechbloxModdingAPI.Tasks
|
||||
{
|
||||
public class OnGuiRunner : SteppableRunner<ExtraLeanSveltoTask<IEnumerator>>
|
||||
{
|
||||
public OnGuiRunner(string name, uint runningOrder = 0)
|
||||
: base(name)
|
||||
{
|
||||
UnityCoroutineRunner.StartOnGuiCoroutine(this._processEnumerator, runningOrder);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ namespace TechbloxModdingAPI.Tasks
|
|||
{
|
||||
get
|
||||
{
|
||||
return RobocraftX.Schedulers.Lean.UIScheduler;
|
||||
return RobocraftX.Schedulers.ClientLean.UIScheduler;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace TechbloxModdingAPI.Tasks
|
|||
{
|
||||
get
|
||||
{
|
||||
return RobocraftX.Schedulers.ExtraLean.UIScheduler;
|
||||
return RobocraftX.Schedulers.ClientExtraLean.UIScheduler;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,289 +1,84 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using TechbloxModdingAPI.App;
|
||||
using BepInEx;
|
||||
using BepInEx.Bootstrap;
|
||||
using HarmonyLib;
|
||||
using IllusionInjector;
|
||||
// test
|
||||
using RobocraftX.FrontEnd;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using RobocraftX.Common.Input;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Commands;
|
||||
using TechbloxModdingAPI.Input;
|
||||
using TechbloxModdingAPI.Interface.IMGUI;
|
||||
using TechbloxModdingAPI.Players;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Tests
|
||||
{
|
||||
#if DEBUG
|
||||
// unused by design
|
||||
/// <summary>
|
||||
/// Modding API implemented as a standalone IPA Plugin.
|
||||
/// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself
|
||||
/// </summary>
|
||||
class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin
|
||||
#if DEBUG // The API should be loaded by other plugins, but it can be used by itself for testing
|
||||
[BepInPlugin("org.exmods.TechbloxModdingAPIPluginTest", PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
|
||||
[BepInProcess("Techblox.exe")]
|
||||
public class TechbloxModdingAPIPluginTest : BaseUnityPlugin
|
||||
{
|
||||
private void Awake()
|
||||
{
|
||||
Main.Init();
|
||||
Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
|
||||
Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler");
|
||||
Game.Enter += (s, a) =>
|
||||
{
|
||||
Player.LocalPlayer.SeatEntered += (sender, args) =>
|
||||
Console.WriteLine($"Player {Player.LocalPlayer} entered seat {args.Seat}");
|
||||
Player.LocalPlayer.SeatExited += (sender, args) =>
|
||||
Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}");
|
||||
};
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("Exit")
|
||||
.Description("Close Techblox immediately, without any prompts")
|
||||
.Action(() => { UnityEngine.Application.Quit(); })
|
||||
.Build();
|
||||
|
||||
private static Harmony harmony { get; set; }
|
||||
CommandBuilder.Builder()
|
||||
.Name("SetFOV")
|
||||
.Description("Set the player camera's field of view")
|
||||
.Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
|
||||
.Build();
|
||||
|
||||
Game.AddPersistentDebugInfo("InstalledMods", InstalledMods);
|
||||
|
||||
// Plugin startup logic
|
||||
Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!");
|
||||
|
||||
#if TEST
|
||||
TestRoot.RunTests();
|
||||
#endif
|
||||
}
|
||||
|
||||
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()
|
||||
private void OnDestroy()
|
||||
{
|
||||
Main.Shutdown();
|
||||
}
|
||||
|
||||
public override void OnApplicationStart()
|
||||
{
|
||||
FileLog.Reset();
|
||||
Harmony.DEBUG = true;
|
||||
Main.Init();
|
||||
Logging.MetaDebugLog($"Version group id {(uint)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!");
|
||||
|
||||
// debug/test commands
|
||||
if (Dependency.Hell("ExtraCommands"))
|
||||
{
|
||||
CommandBuilder.Builder()
|
||||
.Name("Exit")
|
||||
.Description("Close Techblox immediately, without any prompts")
|
||||
.Action(() => { UnityEngine.Application.Quit(); })
|
||||
.Build();
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("SetFOV")
|
||||
.Description("Set the player camera's field of view")
|
||||
.Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
|
||||
.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);
|
||||
Block.Placed += (sender, args) =>
|
||||
Logging.MetaDebugLog("Placed block " + args.Block);
|
||||
Block.Removed += (sender, args) =>
|
||||
Logging.MetaDebugLog("Removed block " + args.Block);
|
||||
}
|
||||
|
||||
// dependency test
|
||||
if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0")))
|
||||
{
|
||||
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))*/
|
||||
#if TEST
|
||||
TestRoot.RunTests();
|
||||
#endif
|
||||
}
|
||||
|
||||
private string modsString;
|
||||
private string modsString;
|
||||
private string InstalledMods()
|
||||
{
|
||||
if (modsString != null) return modsString;
|
||||
StringBuilder sb = new StringBuilder("Installed mods:");
|
||||
foreach (var plugin in PluginManager.Plugins)
|
||||
sb.Append("\n" + plugin.Name + " - " + plugin.Version);
|
||||
return modsString = sb.ToString();
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
if (UnityEngine.Input.GetKeyDown(KeyCode.End))
|
||||
{
|
||||
Console.WriteLine("Pressed button to toggle console");
|
||||
FakeInput.CustomInput(new LocalInputEntityStruct {commandLineToggleInput = true});
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public class MinimumSpecsPatch
|
||||
{
|
||||
public static bool Prefix()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static MethodInfo TargetMethod()
|
||||
{
|
||||
return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method;
|
||||
}
|
||||
if (modsString != null) return modsString;
|
||||
StringBuilder sb = new StringBuilder("Installed mods:");
|
||||
foreach (var (_, plugin) in Chainloader.PluginInfos)
|
||||
sb.Append("\n" + plugin.Metadata.Name + " - " + plugin.Metadata.Version);
|
||||
return modsString = sb.ToString();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[HarmonyPatch]
|
||||
public class MinimumSpecsPatch
|
||||
{
|
||||
public static bool Prefix(ref bool __result)
|
||||
{
|
||||
__result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static MethodInfo TargetMethod()
|
||||
{
|
||||
return ((Func<bool>) MinimumSpecsCheck.CheckRequirementsMet).Method;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ using System.Linq; // welcome to the dark side
|
|||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Lean;
|
||||
using Svelto.Tasks.Enumerators;
|
||||
using Svelto.Tasks.Lean.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
@ -64,7 +66,7 @@ namespace TechbloxModdingAPI.Tests
|
|||
_testsCountPassed = 0;
|
||||
_testsCountFailed = 0;
|
||||
// flow control
|
||||
Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.Lean.EveryFrameStepRunner_TimeRunningAndStopped); };
|
||||
Game.Enter += (sender, args) => { GameTests().RunOn(new UpdateMonoRunner("TechbloxModdingAPITestRunner")); };
|
||||
Game.Exit += (s, a) => state = "ReturningFromGame";
|
||||
Client.EnterMenu += (sender, args) =>
|
||||
{
|
||||
|
@ -129,7 +131,7 @@ namespace TechbloxModdingAPI.Tests
|
|||
|
||||
private static IEnumerator<TaskContract> GoToGameTests()
|
||||
{
|
||||
Client app = new Client();
|
||||
Client app = Client.Instance;
|
||||
int oldLength = 0;
|
||||
while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
|
||||
{
|
||||
|
@ -137,7 +139,15 @@ namespace TechbloxModdingAPI.Tests
|
|||
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||
}
|
||||
yield return Yield.It;
|
||||
app.MyGames[0].EnterGame();
|
||||
try
|
||||
{
|
||||
app.MyGames[0].EnterGame();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Failed to go to game tests");
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
/*Game newGame = Game.NewGame();
|
||||
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync
|
||||
newGame.EnterGame();*/
|
||||
|
@ -157,6 +167,7 @@ namespace TechbloxModdingAPI.Tests
|
|||
};
|
||||
for (var index = 0; index < testTypesToRun.Length; index++)
|
||||
{
|
||||
Logging.MetaLog($"Running test type {testTypesToRun[index]}");
|
||||
foreach (Type t in testTypes)
|
||||
{
|
||||
foreach (MethodBase m in t.GetMethods())
|
||||
|
@ -189,16 +200,26 @@ namespace TechbloxModdingAPI.Tests
|
|||
cont = false;
|
||||
}
|
||||
|
||||
yield return Yield.It;
|
||||
if (cont)
|
||||
yield return enumerator.Current;
|
||||
} while (cont);
|
||||
}
|
||||
|
||||
yield return Yield.It;
|
||||
yield return new WaitForSecondsEnumerator(1f).Continue();
|
||||
}
|
||||
}
|
||||
|
||||
if (index + 1 < testTypesToRun.Length) //Don't toggle on the last test
|
||||
{
|
||||
bool running = currentGame.IsTimeRunning;
|
||||
currentGame.ToggleTimeMode();
|
||||
while (running ? !currentGame.IsTimeStopped : !currentGame.IsTimeRunning)
|
||||
{
|
||||
Logging.MetaLog($"Waiting for time to {(running?"stop":"start")}...");
|
||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||
}
|
||||
}
|
||||
|
||||
yield return new WaitForSecondsEnumerator(5).Continue();
|
||||
}
|
||||
// exit game
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
|
||||
using IllusionInjector;
|
||||
using IllusionPlugin;
|
||||
using BepInEx.Bootstrap;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility
|
||||
{
|
||||
|
@ -15,11 +13,11 @@ namespace TechbloxModdingAPI.Utility
|
|||
/// </summary>
|
||||
/// <returns>The plugin.</returns>
|
||||
/// <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;
|
||||
}
|
||||
|
@ -35,45 +33,15 @@ namespace TechbloxModdingAPI.Utility
|
|||
/// <param name="name">The plugin's name.</param>
|
||||
public static Version GetPluginVersion(string name)
|
||||
{
|
||||
IPlugin plugin = GetPlugin(name);
|
||||
var plugin = GetPlugin(name);
|
||||
if (plugin != null) {
|
||||
try
|
||||
{
|
||||
return new Version(plugin.Version);
|
||||
} catch (Exception e) when (
|
||||
e is ArgumentException
|
||||
|| e is ArgumentNullException
|
||||
|| e is ArgumentOutOfRangeException
|
||||
|| e is FormatException
|
||||
|| e is OverflowException) {}
|
||||
return plugin.Metadata.Version;
|
||||
} catch (Exception e) when (e is ArgumentException or FormatException or OverflowException) {}
|
||||
return plugin.GetType().Assembly.GetName().Version;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
75
TechbloxModdingAPI/Utility/ECS/ManagedApiExtensions.cs
Normal file
75
TechbloxModdingAPI/Utility/ECS/ManagedApiExtensions.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using Svelto.ECS;
|
||||
using Svelto.ECS.Hybrid;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility.ECS
|
||||
{
|
||||
public static class ManagedApiExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns an optional that contains the result if succeeded.
|
||||
/// <b>This overload does not take initializer data into account.</b>
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB</param>
|
||||
/// <param name="egid">The EGID to query</param>
|
||||
/// <typeparam name="T">The component type to query</typeparam>
|
||||
/// <returns>An optional that contains the result on success or is empty if not found</returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EGID egid)
|
||||
where T : struct, IEntityViewComponent
|
||||
{
|
||||
return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array)
|
||||
? new OptionalRef<T>(array, index)
|
||||
: new OptionalRef<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result in an optional reference.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||
/// <param name="obj">The ECS object to query</param>
|
||||
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||
/// <typeparam name="T">The component to query</typeparam>
|
||||
/// <returns>A reference to the component or a dummy value</returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj,
|
||||
ExclusiveGroupStruct group = default)
|
||||
where T : struct, IEntityViewComponent
|
||||
{
|
||||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||
return opt ? opt : new OptionalRef<T>(obj, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||
/// <param name="obj">The ECS object to query</param>
|
||||
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||
/// <typeparam name="T">The component to query</typeparam>
|
||||
/// <returns>A reference to the component or a dummy value</returns>
|
||||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj,
|
||||
ExclusiveGroupStruct group = default)
|
||||
where T : struct, IEntityViewComponent
|
||||
{
|
||||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||
if (opt) return ref opt.Get();
|
||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd<T>();
|
||||
return ref opt.Get(); //Default value
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query entities as OptionalRefs. The elements always exist, it's just a nice way to encapsulate the data.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB"></param>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="select"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TR"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static RefCollection<T> QueryEntitiesOptional<T>(this EntitiesDB entitiesDB, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent
|
||||
{
|
||||
var (buffer, ids, count) = entitiesDB.QueryEntities<T>(group);
|
||||
return new RefCollection<T>(count, buffer, ids, group);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility.ECS
|
||||
{
|
||||
public static partial class NativeApiExtensions
|
||||
{
|
||||
[SuppressMessage("ReSharper", "StaticMemberInGenericType")]
|
||||
private static class EntitiesDBHelper<T> where T : unmanaged, IEntityComponent
|
||||
{ // Each type gets a new set of fields here (that's what the ReSharper warning is about too)
|
||||
public static readonly Lazy<MethodInfo> EntityStream =
|
||||
new(() => AccessTools.PropertyGetter(typeof(EntitiesDB), "_entityStream"));
|
||||
|
||||
public static readonly Lazy<FieldInfo> Streams = new(() =>
|
||||
AccessTools.Field(EntityStream.Value.ReturnType, "_streams"));
|
||||
|
||||
public static readonly Lazy<FieldInfo> Consumers = new(() =>
|
||||
AccessTools.Field(typeof(EntityStream<T>), "_consumers"));
|
||||
|
||||
public static readonly Lazy<MethodInfo> TryGetValue =
|
||||
new(AccessTools.Method(Streams.Value.FieldType, "TryGetValue"));
|
||||
|
||||
public static readonly Lazy<FieldInfo> RingBuffer =
|
||||
new(() => AccessTools.Field(typeof(Consumer<T>), "_ringBuffer"));
|
||||
}
|
||||
|
||||
private static EntityStream<T> GetEntityStream<T>(this EntitiesDB entitiesDB) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
// EntitiesStreams (internal)
|
||||
var entitiesStreams = EntitiesDBHelper<T>.EntityStream.Value.Invoke(entitiesDB, Array.Empty<object>());
|
||||
// FasterDictionary<RefWrapperType, ITypeSafeStream> (interface is internal)
|
||||
var streams = EntitiesDBHelper<T>.Streams.Value.GetValue(entitiesStreams);
|
||||
|
||||
var parameters = new object[] { TypeRefWrapper<T>.wrapper, null };
|
||||
var success = EntitiesDBHelper<T>.TryGetValue.Value.Invoke(streams, parameters);
|
||||
if (!(bool)success)
|
||||
return null; // There is no entity stream for this type
|
||||
return (EntityStream<T>)parameters[1];
|
||||
}
|
||||
|
||||
private static ThreadSafeFasterList<Consumer<T>> GetConsumers<T>(this EntityStream<T> stream) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return (ThreadSafeFasterList<Consumer<T>>)EntitiesDBHelper<T>.Consumers.Value.GetValue(stream);
|
||||
}
|
||||
|
||||
private static RingBuffer<(T, EGID)> GetRingBuffer<T>(this Consumer<T> consumer) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return (RingBuffer<(T, EGID)>)EntitiesDBHelper<T>.RingBuffer.Value.GetValue(consumer);
|
||||
}
|
||||
}
|
||||
}
|
129
TechbloxModdingAPI/Utility/ECS/NativeApiExtensions.cs
Normal file
129
TechbloxModdingAPI/Utility/ECS/NativeApiExtensions.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Lean;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility.ECS
|
||||
{
|
||||
public static partial class NativeApiExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns an optional that contains the result if succeeded.
|
||||
/// <b>This overload does not take initializer data into account.</b>
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB</param>
|
||||
/// <param name="egid">The EGID to query</param>
|
||||
/// <typeparam name="T">The component type to query</typeparam>
|
||||
/// <returns>An optional that contains the result on success or is empty if not found</returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EGID egid)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array)
|
||||
? new OptionalRef<T>(array, index)
|
||||
: new OptionalRef<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result in an optional reference.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||
/// <param name="obj">The ECS object to query</param>
|
||||
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||
/// <typeparam name="T">The component to query</typeparam>
|
||||
/// <returns>A reference to the component or a dummy value</returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj,
|
||||
ExclusiveGroupStruct group = default)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||
return opt ? opt : new OptionalRef<T>(obj, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB to query from</param>
|
||||
/// <param name="obj">The ECS object to query</param>
|
||||
/// <param name="group">The group of the entity if the object can have multiple</param>
|
||||
/// <typeparam name="T">The component to query</typeparam>
|
||||
/// <returns>A reference to the component or a dummy value</returns>
|
||||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj,
|
||||
ExclusiveGroupStruct group = default)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||
if (opt) return ref opt.Get();
|
||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd<T>();
|
||||
/*if (!obj.InitData.Valid) return ref opt.Get(); //Default value
|
||||
var init = obj.InitData.Initializer(id);
|
||||
// Do not create the component if missing, as that can trigger Add() listeners that, in some cases, may be
|
||||
// invalid if (ab)using the classes in an unusual way - TODO: Check entity descriptor or something
|
||||
if (init.Has<T>()) return ref init.Get<T>();*/
|
||||
return ref opt.Get(); //Default value
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary.
|
||||
/// It will only publish in the next frame.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB to publish to</param>
|
||||
/// <param name="id">The ECS object that got changed</param>
|
||||
/// <typeparam name="T">The component that changed</typeparam>
|
||||
public static void PublishEntityChangeDelayed<T>(this EntitiesDB entitiesDB, EGID id)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
PublishChanges<T>(entitiesDB, id).RunOn(Scheduler.leanRunner);
|
||||
}
|
||||
|
||||
private static IEnumerator<TaskContract> PublishChanges<T>(EntitiesDB entitiesDB, EGID id)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
yield return Yield.It;
|
||||
var entityStream = entitiesDB.GetEntityStream<T>();
|
||||
if (entityStream is null)
|
||||
yield break; // There is no entity stream for this type
|
||||
var consumers = entityStream.GetConsumers();
|
||||
if (consumers == null)
|
||||
{
|
||||
Console.WriteLine("Consumers is null");
|
||||
yield break;
|
||||
}
|
||||
|
||||
bool waitForConsumers;
|
||||
do
|
||||
{
|
||||
waitForConsumers = false;
|
||||
for (int i = 0; i < consumers.count; i++)
|
||||
{
|
||||
var buffer = consumers[i].GetRingBuffer();
|
||||
if (buffer.Count + 1 <= buffer.Capacity) continue;
|
||||
waitForConsumers = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (waitForConsumers) yield return Yield.It;
|
||||
} while (waitForConsumers);
|
||||
entitiesDB.PublishEntityChange<T>(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query entities as OptionalRefs. The elements always exist, it's just a nice way to encapsulate the data.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB"></param>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="select"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TR"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static RefCollection<T> QueryEntitiesOptional<T>(this EntitiesDB entitiesDB, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
var (buffer, ids, count) = entitiesDB.QueryEntities<T>(group);
|
||||
return new RefCollection<T>(count, buffer, ids, group);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
using System;
|
||||
using TechbloxModdingAPI.Events;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility
|
||||
{
|
||||
public static class ExceptionUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Invokes an event with a null-check.
|
||||
/// </summary>
|
||||
/// <param name="handler">The event to emit, can be null</param>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="args">Event arguments</param>
|
||||
/// <typeparam name="T">Type of the event arguments</typeparam>
|
||||
public static void InvokeEvent<T>(EventHandler<T> handler, object sender, T args)
|
||||
{
|
||||
handler?.Invoke(sender, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wraps the event handler in a try-catch block to avoid propagating exceptions.
|
||||
/// </summary>
|
||||
/// <param name="handler">The handler to wrap (not null)</param>
|
||||
/// <typeparam name="T">Type of the event arguments</typeparam>
|
||||
/// <returns>The wrapped handler</returns>
|
||||
public static EventHandler<T> WrapHandler<T>(EventHandler<T> handler)
|
||||
{
|
||||
return (sender, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
handler(sender, e);
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
EventRuntimeException wrappedException =
|
||||
new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception", e1);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using DataLoader;
|
||||
using DataLoader;
|
||||
using HarmonyLib;
|
||||
using RobocraftX;
|
||||
using RobocraftX.Common.Utilities;
|
||||
using RobocraftX.CR.MainGame;
|
||||
using RobocraftX.GUI;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.Rendering;
|
||||
using Svelto.Context;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Schedulers;
|
||||
using Svelto.ECS.GUI;
|
||||
using Techblox.GameSelection;
|
||||
using UnityEngine;
|
||||
using Unity.Entities;
|
||||
using Unity.Physics.Systems;
|
||||
|
@ -72,14 +66,6 @@ namespace TechbloxModdingAPI.Utility
|
|||
}
|
||||
}
|
||||
|
||||
public static SimpleEntitiesSubmissionScheduler _mainGameSubmissionScheduler
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SimpleEntitiesSubmissionScheduler)fgcr?.Field("_sub").Field("_mainGameSubmissionScheduler").GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static BuildPhysicsWorld _physicsWorldSystem
|
||||
{
|
||||
get
|
||||
|
@ -112,14 +98,6 @@ namespace TechbloxModdingAPI.Utility
|
|||
}
|
||||
}
|
||||
|
||||
public static PhysicsUtility _physicsUtility
|
||||
{
|
||||
get
|
||||
{
|
||||
return (PhysicsUtility)fgcr?.Field("_physicsUtility").GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
/*public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler
|
||||
{
|
||||
get
|
||||
|
@ -144,11 +122,11 @@ namespace TechbloxModdingAPI.Utility
|
|||
}
|
||||
}
|
||||
|
||||
public static ECSGameObjectResourceManager _eCsGameObjectResourceManager
|
||||
public static ECSMainGameResourceManagers _managers
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ECSGameObjectResourceManager)fgcr?.Field("_eCsGameObjectResourceManager").GetValue();
|
||||
return (ECSMainGameResourceManagers)fgcr?.Field("_gameManagers").GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,6 +146,22 @@ namespace TechbloxModdingAPI.Utility
|
|||
}
|
||||
}
|
||||
|
||||
public static SveltoGUI _frontEndGUI
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SveltoGUI)fgcr?.Field("_frontEndGUI").GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static GameSelectionData _gameSelectionData
|
||||
{
|
||||
get
|
||||
{
|
||||
return (GameSelectionData)fgcr?.Field("_gameSelectionData").GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static Traverse fgcr;
|
||||
|
||||
public static void Init(FullGameCompositionRoot instance)
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
|
@ -26,10 +23,10 @@ namespace TechbloxModdingAPI.Utility
|
|||
{
|
||||
Logging.MetaDebugLog($"Registering Game IApiEngine {engine.Name}");
|
||||
_lastEngineRoot.AddEngine(engine);
|
||||
if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType()))
|
||||
{
|
||||
((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
}
|
||||
if (engine is IFactoryEngine factoryEngine)
|
||||
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
if (engine is IFunEngine funEngine)
|
||||
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +63,7 @@ namespace TechbloxModdingAPI.Utility
|
|||
var enginesRoot = helper.enginesRoot;
|
||||
_lastEngineRoot = enginesRoot;
|
||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
||||
foreach (var key in _gameEngines.Keys)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}");
|
||||
|
@ -75,6 +73,8 @@ namespace TechbloxModdingAPI.Utility
|
|||
enginesRoot.AddEngine(_gameEngines[key]);
|
||||
if (_gameEngines[key] is IFactoryEngine factEngine)
|
||||
factEngine.Factory = factory;
|
||||
if (_gameEngines[key] is IFunEngine funEngine)
|
||||
funEngine.Functions = functions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ namespace TechbloxModdingAPI.Utility
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CommandLog(string msg)
|
||||
{
|
||||
uREPL.Log.Output(msg);
|
||||
Log(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -179,7 +179,7 @@ namespace TechbloxModdingAPI.Utility
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CommandLogError(string msg)
|
||||
{
|
||||
uREPL.Log.Error(msg);
|
||||
LogError(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -195,7 +195,7 @@ namespace TechbloxModdingAPI.Utility
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CommandLogWarning(string msg)
|
||||
{
|
||||
uREPL.Log.Warn(msg);
|
||||
LogWarning(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
using Svelto.ECS;
|
||||
using Svelto.ECS.Hybrid;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility
|
||||
{
|
||||
public static class ManagedApiExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns an optional that contains the result if succeeded.
|
||||
/// <b>This overload does not take initializer data into account.</b>
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB</param>
|
||||
/// <param name="egid">The EGID to query</param>
|
||||
/// <typeparam name="T">The component type to query</typeparam>
|
||||
/// <returns>An optional that contains the result on success or is empty if not found</returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EGID egid)
|
||||
where T : struct, IEntityViewComponent
|
||||
{
|
||||
return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array)
|
||||
? new OptionalRef<T>(array, index)
|
||||
: new OptionalRef<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
||||
where T : struct, IEntityViewComponent
|
||||
{
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
||||
return opt ? opt : new OptionalRef<T>(obj, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
||||
where T : struct, IEntityViewComponent
|
||||
{
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
||||
if (opt) return ref opt.Get();
|
||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(obj.Id).GetOrCreate<T>();
|
||||
return ref opt.Get(); //Default value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,10 +26,10 @@ namespace TechbloxModdingAPI.Utility
|
|||
{
|
||||
Logging.MetaDebugLog($"Registering Menu IApiEngine {engine.Name}");
|
||||
_lastEngineRoot.AddEngine(engine);
|
||||
if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType()))
|
||||
{
|
||||
((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
}
|
||||
if (engine is IFactoryEngine factoryEngine)
|
||||
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
if (engine is IFunEngine funEngine)
|
||||
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,14 +65,13 @@ namespace TechbloxModdingAPI.Utility
|
|||
{
|
||||
_lastEngineRoot = enginesRoot;
|
||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
||||
foreach (var key in _menuEngines.Keys)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}");
|
||||
enginesRoot.AddEngine(_menuEngines[key]);
|
||||
if (_menuEngines[key] is IFactoryEngine factEngine)
|
||||
{
|
||||
factEngine.Factory = factory;
|
||||
}
|
||||
if (_menuEngines[key] is IFactoryEngine factEngine) factEngine.Factory = factory;
|
||||
if(_menuEngines[key] is IFunEngine funEngine) funEngine.Functions = functions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
using Svelto.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility
|
||||
{
|
||||
public static class NativeApiExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns an optional that contains the result if succeeded.
|
||||
/// <b>This overload does not take initializer data into account.</b>
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB">The entities DB</param>
|
||||
/// <param name="egid">The EGID to query</param>
|
||||
/// <typeparam name="T">The component type to query</typeparam>
|
||||
/// <returns>An optional that contains the result on success or is empty if not found</returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EGID egid)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array)
|
||||
? new OptionalRef<T>(array, index)
|
||||
: new OptionalRef<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
||||
return opt ? opt : new OptionalRef<T>(obj, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to query an entity and returns the result or a dummy value that can be modified.
|
||||
/// </summary>
|
||||
/// <param name="entitiesDB"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj)
|
||||
where T : unmanaged, IEntityComponent
|
||||
{
|
||||
var opt = QueryEntityOptional<T>(entitiesDB, obj.Id);
|
||||
if (opt) return ref opt.Get();
|
||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(obj.Id).GetOrCreate<T>();
|
||||
return ref opt.Get(); //Default value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility
|
||||
{
|
||||
public ref struct OptionalRef<T> where T : struct, IEntityComponent
|
||||
public ref struct OptionalRef<T> where T : struct, IBaseEntityComponent
|
||||
{
|
||||
private readonly EGID entityId;
|
||||
private readonly State state;
|
||||
private readonly uint index;
|
||||
private NB<T> array;
|
||||
|
@ -14,19 +14,22 @@ namespace TechbloxModdingAPI.Utility
|
|||
private readonly EntityInitializer initializer;
|
||||
//The possible fields are: (index && (array || managedArray)) || initializer
|
||||
|
||||
public OptionalRef(NB<T> array, uint index)
|
||||
public OptionalRef(NB<T> array, uint index, EGID entityId = default)
|
||||
{
|
||||
state = State.Native;
|
||||
this.array = array;
|
||||
this.index = index;
|
||||
this.entityId = entityId;
|
||||
initializer = default;
|
||||
managedArray = default;
|
||||
}
|
||||
|
||||
public OptionalRef(MB<T> array, uint index)
|
||||
public OptionalRef(MB<T> array, uint index, EGID entityId = default)
|
||||
{
|
||||
state = State.Managed;
|
||||
managedArray = array;
|
||||
this.index = index;
|
||||
this.entityId = entityId;
|
||||
initializer = default;
|
||||
this.array = default;
|
||||
}
|
||||
|
@ -36,8 +39,9 @@ namespace TechbloxModdingAPI.Utility
|
|||
/// </summary>
|
||||
/// <param name="obj">The object with the initializer</param>
|
||||
/// <param name="unmanaged">Whether the struct is unmanaged</param>
|
||||
public OptionalRef(EcsObjectBase obj, bool unmanaged)
|
||||
public OptionalRef(EcsObjectBase obj, bool unmanaged, EGID entityId = default)
|
||||
{
|
||||
this.entityId = entityId;
|
||||
if (obj.InitData.Valid)
|
||||
{
|
||||
initializer = obj.InitData.Initializer(obj.Id);
|
||||
|
@ -50,6 +54,7 @@ namespace TechbloxModdingAPI.Utility
|
|||
}
|
||||
array = default;
|
||||
index = default;
|
||||
managedArray = default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -58,22 +63,40 @@ namespace TechbloxModdingAPI.Utility
|
|||
/// <returns>The value or the default value</returns>
|
||||
public ref T Get()
|
||||
{
|
||||
CompRefCache.Default = default; //The default value can be changed by mods
|
||||
if (state == State.Empty) return ref CompRefCache.Default;
|
||||
if ((state & State.Initializer) != State.Empty) return ref initializer.GetOrCreate<T>();
|
||||
if ((state & State.Initializer) != State.Empty) return ref initializer.GetOrAdd<T>();
|
||||
if ((state & State.Native) != State.Empty) return ref array[index];
|
||||
return ref managedArray[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A non-by-ref view that allows getting and setting the value.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get => Get();
|
||||
set => Get() = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the entity this component belongs to or default if it doesn't exist.
|
||||
/// </summary>
|
||||
public EGID EGID => entityId;
|
||||
|
||||
public bool Exists => state != State.Empty;
|
||||
public T? Nullable() => this ? Get() : default;
|
||||
|
||||
public static implicit operator T(OptionalRef<T> opt) => opt.Get();
|
||||
|
||||
public static implicit operator bool(OptionalRef<T> opt) => opt.state != State.Empty;
|
||||
|
||||
public static implicit operator EGID(OptionalRef<T> opt) => opt.entityId;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of a struct T that can be referenced.
|
||||
/// </summary>
|
||||
internal struct CompRefCache
|
||||
private struct CompRefCache
|
||||
{
|
||||
public static T Default;
|
||||
}
|
||||
|
|
94
TechbloxModdingAPI/Utility/RefCollection.cs
Normal file
94
TechbloxModdingAPI/Utility/RefCollection.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
105
TechbloxModdingAPI/Utility/WeakDictionary.cs
Normal file
105
TechbloxModdingAPI/Utility/WeakDictionary.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility
|
||||
{
|
||||
public class WeakDictionary<TKey, TValue> : IDictionary<TKey, TValue> where TValue : class
|
||||
{
|
||||
private readonly Dictionary<TKey, WeakReference<TValue>> _dictionary = new();
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
using var enumerator = _dictionary.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
if (enumerator.Current.Value.TryGetTarget(out var value))
|
||||
yield return new KeyValuePair<TKey, TValue>(enumerator.Current.Key, value);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
|
||||
public void Clear() => _dictionary.Clear();
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item) =>
|
||||
TryGetValue(item.Key, out var value) && item.Value == value;
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) =>
|
||||
throw new System.NotImplementedException();
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item) => Contains(item) && Remove(item.Key);
|
||||
public int Count => _dictionary.Count;
|
||||
public bool IsReadOnly => false;
|
||||
public bool ContainsKey(TKey key) => TryGetValue(key, out _);
|
||||
public void Add(TKey key, TValue value) => _dictionary.Add(key, new WeakReference<TValue>(value));
|
||||
public bool Remove(TKey key) => _dictionary.Remove(key);
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
value = null;
|
||||
bool ret = _dictionary.TryGetValue(key, out var reference) && reference.TryGetTarget(out value);
|
||||
if (!ret) _dictionary.Remove(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get => TryGetValue(key, out var value)
|
||||
? value
|
||||
: throw new KeyNotFoundException($"Key {key} not found in WeakDictionary.");
|
||||
set => _dictionary[key] = new WeakReference<TValue>(value);
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => new KeyCollection(this);
|
||||
public ICollection<TValue> Values => new ValueCollection(this);
|
||||
|
||||
public class KeyCollection : ICollection<TKey>, IReadOnlyCollection<TKey>
|
||||
{
|
||||
private readonly WeakDictionary<TKey, TValue> _dictionary;
|
||||
internal KeyCollection(WeakDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
_dictionary = dictionary;
|
||||
}
|
||||
|
||||
public IEnumerator<TKey> GetEnumerator()
|
||||
{
|
||||
using var enumerator = _dictionary.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
yield return enumerator.Current.Key;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public void Add(TKey item) => throw new NotSupportedException("The key collection is read only.");
|
||||
public void Clear() => throw new NotSupportedException("The key collection is read only.");
|
||||
public bool Contains(TKey item) => _dictionary.ContainsKey(item);
|
||||
public void CopyTo(TKey[] array, int arrayIndex) => throw new NotImplementedException();
|
||||
public bool Remove(TKey item) => throw new NotSupportedException("The key collection is read only.");
|
||||
public int Count => _dictionary.Count;
|
||||
public bool IsReadOnly => true;
|
||||
}
|
||||
|
||||
public class ValueCollection : ICollection<TValue>, IReadOnlyCollection<TValue>
|
||||
{
|
||||
private readonly WeakDictionary<TKey, TValue> _dictionary;
|
||||
internal ValueCollection(WeakDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
_dictionary = dictionary;
|
||||
}
|
||||
|
||||
public IEnumerator<TValue> GetEnumerator()
|
||||
{
|
||||
using var enumerator = _dictionary.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
yield return enumerator.Current.Value;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public void Add(TValue item) => throw new NotSupportedException("The value collection is read only.");
|
||||
public void Clear() => throw new NotSupportedException("The value collection is read only.");
|
||||
public bool Contains(TValue item) => _dictionary.Any(kv => kv.Value == item);
|
||||
public void CopyTo(TValue[] array, int arrayIndex) => throw new NotImplementedException();
|
||||
public bool Remove(TValue item) => throw new NotSupportedException("The value collection is read only.");
|
||||
public int Count => _dictionary.Count;
|
||||
public bool IsReadOnly => true;
|
||||
}
|
||||
}
|
||||
}
|
59
TechbloxModdingAPI/Utility/WrappedHandler.cs
Normal file
59
TechbloxModdingAPI/Utility/WrappedHandler.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TechbloxModdingAPI.Events;
|
||||
|
||||
namespace TechbloxModdingAPI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps the event handler in a try-catch block to avoid propagating exceptions.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The event arguments type</typeparam>
|
||||
public struct WrappedHandler<T>
|
||||
{
|
||||
private EventHandler<T> eventHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Store wrappers so we can unregister them properly
|
||||
/// </summary>
|
||||
private static Dictionary<EventHandler<T>, EventHandler<T>> wrappers =
|
||||
new Dictionary<EventHandler<T>, EventHandler<T>>();
|
||||
|
||||
public static WrappedHandler<T> operator +(WrappedHandler<T> original, EventHandler<T> added)
|
||||
{
|
||||
EventHandler<T> wrapped = (sender, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
added(sender, e);
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
EventRuntimeException wrappedException =
|
||||
new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception",
|
||||
e1);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
}
|
||||
};
|
||||
if (wrappers.ContainsKey(added))
|
||||
{
|
||||
original.eventHandler -= wrapped;
|
||||
wrappers.Remove(added);
|
||||
}
|
||||
|
||||
wrappers.Add(added, wrapped);
|
||||
return new WrappedHandler<T> { eventHandler = original.eventHandler + wrapped };
|
||||
}
|
||||
|
||||
public static WrappedHandler<T> operator -(WrappedHandler<T> original, EventHandler<T> removed)
|
||||
{
|
||||
if (!wrappers.TryGetValue(removed, out var wrapped)) return original;
|
||||
wrappers.Remove(removed);
|
||||
return new WrappedHandler<T> { eventHandler = original.eventHandler - wrapped };
|
||||
}
|
||||
|
||||
public void Invoke(object sender, T args)
|
||||
{
|
||||
eventHandler?.Invoke(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue