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
|
# this assumes a mostly semver-complient version number
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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).")
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -28,12 +28,12 @@ if __name__ == "__main__":
|
||||||
old_version = ""
|
old_version = ""
|
||||||
new_version = ""
|
new_version = ""
|
||||||
|
|
||||||
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile:
|
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
|
||||||
print("Parsing GamecraftModdingAPI.csproj")
|
print("Parsing TechbloxModdingAPI.csproj")
|
||||||
fileStr = xmlFile.read()
|
fileStr = xmlFile.read()
|
||||||
versionMatch = re.search(r"<Version>(.+)</Version>", fileStr)
|
versionMatch = re.search(r"<Version>(.+)</Version>", fileStr)
|
||||||
if versionMatch is None:
|
if versionMatch is None:
|
||||||
print("Unable to find version number in GamecraftModdingAPI.csproj")
|
print("Unable to find version number in TechbloxModdingAPI.csproj")
|
||||||
exit(1)
|
exit(1)
|
||||||
old_version = versionMatch.group(1)
|
old_version = versionMatch.group(1)
|
||||||
versionList = old_version.split(".")
|
versionList = old_version.split(".")
|
||||||
|
@ -53,7 +53,7 @@ if __name__ == "__main__":
|
||||||
print(new_version)
|
print(new_version)
|
||||||
newFileContents = fileStr.replace("<Version>"+old_version+"</Version>", "<Version>"+new_version+"</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")
|
print("Writing new version to project file")
|
||||||
xmlFile.write(newFileContents)
|
xmlFile.write(newFileContents)
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,24 @@
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
import re
|
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):
|
def getAssemblyReferences(path):
|
||||||
asmDir = Path(path)
|
asmDir = Path(path)
|
||||||
result = list()
|
result = list()
|
||||||
|
addedPath = ""
|
||||||
|
if not asmDir.exists():
|
||||||
|
addedPath = "../"
|
||||||
|
asmDir = Path(addedPath + path)
|
||||||
for child in asmDir.iterdir():
|
for child in asmDir.iterdir():
|
||||||
if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child), re.I) is None and str(child).lower().endswith(".dll"):
|
if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child)) is None and str(child).lower().endswith(".dll"):
|
||||||
result.append(str(child).replace("\\", "/"))
|
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
|
return result
|
||||||
|
|
||||||
def buildReferencesXml(path):
|
def buildReferencesXml(path):
|
||||||
|
@ -32,7 +41,7 @@ if __name__ == "__main__":
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
print("Building Assembly references")
|
print("Building Assembly references")
|
||||||
asmXml = buildReferencesXml("../ref/TechbloxPreview_Data/Managed")
|
asmXml = buildReferencesXml("../ref_TB/Techblox_Data/Managed")
|
||||||
# print(asmXml)
|
# print(asmXml)
|
||||||
|
|
||||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
|
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
|
||||||
|
@ -44,7 +53,7 @@ if __name__ == "__main__":
|
||||||
if depsStart is None or depsEnd is None:
|
if depsStart is None or depsEnd is None:
|
||||||
print("Unable to find dependency XML comments, aborting!")
|
print("Unable to find dependency XML comments, aborting!")
|
||||||
exit(1)
|
exit(1)
|
||||||
newFileStr = fileStr[:depsStart.start()] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
|
newFileStr = fileStr[:depsStart.start() - 1] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
|
||||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
|
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
|
||||||
print("Writing Assembly references")
|
print("Writing Assembly references")
|
||||||
xmlFile.write(newFileStr)
|
xmlFile.write(newFileStr)
|
||||||
|
|
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
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechbloxModdingAPI", "TechbloxModdingAPI\TechbloxModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechbloxModdingAPI", "TechbloxModdingAPI\TechbloxModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}"
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Test|Any CPU
|
||||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
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>
|
/// </summary>
|
||||||
public class Client
|
public class Client
|
||||||
{
|
{
|
||||||
// extensible engine
|
public static Client Instance { get; } = new Client();
|
||||||
protected static AppEngine appEngine = new AppEngine();
|
|
||||||
|
|
||||||
protected static Func<object> ErrorHandlerInstanceGetter;
|
protected static Func<object> ErrorHandlerInstanceGetter;
|
||||||
|
|
||||||
protected static Action<object, Error> EnqueueError;
|
protected static Action<object, Error> EnqueueError;
|
||||||
|
|
||||||
protected static Action<object> HandleErrorClosed;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An event that fires whenever the main menu is loaded.
|
/// An event that fires whenever the main menu is loaded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<MenuEventArgs> EnterMenu
|
public static event EventHandler<MenuEventArgs> EnterMenu
|
||||||
{
|
{
|
||||||
add => appEngine.EnterMenu += ExceptionUtil.WrapHandler(value);
|
add => Game.menuEngine.EnterMenu += value;
|
||||||
remove => appEngine.EnterMenu -= value;
|
remove => Game.menuEngine.EnterMenu -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -37,8 +34,8 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<MenuEventArgs> ExitMenu
|
public static event EventHandler<MenuEventArgs> ExitMenu
|
||||||
{
|
{
|
||||||
add => appEngine.ExitMenu += ExceptionUtil.WrapHandler(value);
|
add => Game.menuEngine.ExitMenu += value;
|
||||||
remove => appEngine.ExitMenu -= value;
|
remove => Game.menuEngine.ExitMenu -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -69,8 +66,8 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!appEngine.IsInMenu) return new Game[0];
|
if (!Game.menuEngine.IsInMenu) return Array.Empty<Game>();
|
||||||
return appEngine.GetMyGames();
|
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>
|
/// <value><c>true</c> if in menu; <c>false</c> when loading or in a game.</value>
|
||||||
public bool InMenu
|
public bool InMenu
|
||||||
{
|
{
|
||||||
get => appEngine.IsInMenu;
|
get => Game.menuEngine.IsInMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -96,14 +93,26 @@ namespace TechbloxModdingAPI.App
|
||||||
EnqueueError(errorHandlerInstance, popup);
|
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();
|
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()
|
internal static void Init()
|
||||||
{
|
{
|
||||||
|
@ -116,11 +125,6 @@ namespace TechbloxModdingAPI.App
|
||||||
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
|
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
|
||||||
.MakeGenericMethod(errorHandler, errorHandle)
|
.MakeGenericMethod(errorHandler, errorHandle)
|
||||||
.Invoke(null, new object[0]);
|
.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
|
// Creating delegates once is faster than reflection every time
|
||||||
|
@ -145,14 +149,23 @@ namespace TechbloxModdingAPI.App
|
||||||
return enqueueCasted;
|
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");
|
if (_errorPopup.Close != null)
|
||||||
MethodInfo handlePopupClosed = AccessTools.Method(errorHandler, "HandlePopupClosed");
|
return _errorPopup;
|
||||||
Action<T> handleSimple =
|
Type errorHandler = handler.GetType();
|
||||||
(Action<T>) Delegate.CreateDelegate(typeof(Action<T>), handlePopupClosed);
|
FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
|
||||||
Action<object> handleCasted = (object instance) => handleSimple((T) instance);
|
var errorPopup = (ErrorPopup)field.GetValue(handler);
|
||||||
return handleCasted;
|
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)]
|
[APITestCase(TestType.Menu)]
|
||||||
public static void TestPopUp2()
|
public static void TestPopUp2()
|
||||||
{
|
{
|
||||||
Client c = new Client();
|
Client.Instance.PromptUser(popup2);
|
||||||
c.PromptUser(popup2);
|
|
||||||
//c.CloseCurrentPrompt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[APITestCase(TestType.Menu)]
|
[APITestCase(TestType.Menu)]
|
||||||
public static void TestPopUp1()
|
public static void TestPopUp1()
|
||||||
{
|
{
|
||||||
Client c = new Client();
|
Client.Instance.PromptUser(popup1);
|
||||||
c.PromptUser(popup1);
|
}
|
||||||
//c.CloseCurrentPrompt();
|
|
||||||
|
[APITestCase(TestType.Menu)]
|
||||||
|
public static void TestPopUpClose1()
|
||||||
|
{
|
||||||
|
Client.Instance.CloseCurrentPrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
[APITestCase(TestType.Menu)]
|
||||||
|
public static void TestPopUpClose2()
|
||||||
|
{
|
||||||
|
Client.Instance.CloseCurrentPrompt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,23 +1,27 @@
|
||||||
namespace TechbloxModdingAPI.App
|
using System;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
public enum CurrentGameMode
|
public enum CurrentGameMode
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Building a game
|
/// Building a world
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Build,
|
Build,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Playing a game
|
/// Playing on a map
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Play,
|
Play,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Viewing a prefab
|
/// Viewing a prefab (doesn't exist anymore)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete]
|
||||||
View,
|
View,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Viewing a tutorial
|
/// Viewing a tutorial (doesn't exist anymore)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete]
|
||||||
Tutorial
|
Tutorial
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
using RobocraftX.Common;
|
|
||||||
using RobocraftX.GUI.MyGamesScreen;
|
using RobocraftX.GUI.MyGamesScreen;
|
||||||
using RobocraftX.StateSync;
|
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using Techblox.GameSelection;
|
||||||
|
|
||||||
using TechbloxModdingAPI;
|
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
@ -23,7 +21,7 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
// extensible engines
|
// extensible engines
|
||||||
protected static GameGameEngine gameEngine = new GameGameEngine();
|
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 DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
|
||||||
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
|
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
|
||||||
|
|
||||||
|
@ -93,7 +91,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Simulate
|
public static event EventHandler<GameEventArgs> Simulate
|
||||||
{
|
{
|
||||||
add => buildSimEventEngine.SimulationMode += ExceptionUtil.WrapHandler(value);
|
add => buildSimEventEngine.SimulationMode += value;
|
||||||
remove => buildSimEventEngine.SimulationMode -= value;
|
remove => buildSimEventEngine.SimulationMode -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +101,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Edit
|
public static event EventHandler<GameEventArgs> Edit
|
||||||
{
|
{
|
||||||
add => buildSimEventEngine.BuildMode += ExceptionUtil.WrapHandler(value);
|
add => buildSimEventEngine.BuildMode += value;
|
||||||
remove => buildSimEventEngine.BuildMode -= value;
|
remove => buildSimEventEngine.BuildMode -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +110,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Enter
|
public static event EventHandler<GameEventArgs> Enter
|
||||||
{
|
{
|
||||||
add => gameEngine.EnterGame += ExceptionUtil.WrapHandler(value);
|
add => gameEngine.EnterGame += value;
|
||||||
remove => gameEngine.EnterGame -= value;
|
remove => gameEngine.EnterGame -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +120,7 @@ namespace TechbloxModdingAPI.App
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<GameEventArgs> Exit
|
public static event EventHandler<GameEventArgs> Exit
|
||||||
{
|
{
|
||||||
add => gameEngine.ExitGame += ExceptionUtil.WrapHandler(value);
|
add => gameEngine.ExitGame += value;
|
||||||
remove => gameEngine.ExitGame -= value;
|
remove => gameEngine.ExitGame -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +163,7 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
if (!VerifyMode()) return null;
|
if (!VerifyMode()) return null;
|
||||||
if (menuMode) return menuEngine.GetGameInfo(EGID).GameName;
|
if (menuMode) return menuEngine.GetGameInfo(EGID).GameName;
|
||||||
return GameMode.SaveGameDetails.Name;
|
return gameEngine.GetGameData().saveName;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
|
@ -174,11 +172,7 @@ namespace TechbloxModdingAPI.App
|
||||||
if (menuMode)
|
if (menuMode)
|
||||||
{
|
{
|
||||||
menuEngine.SetGameName(EGID, value);
|
menuEngine.SetGameName(EGID, value);
|
||||||
}
|
} // Save details are directly saved from user input or not changed at all when in game
|
||||||
else
|
|
||||||
{
|
|
||||||
GameMode.SaveGameDetails.Name = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,11 +195,7 @@ namespace TechbloxModdingAPI.App
|
||||||
if (menuMode)
|
if (menuMode)
|
||||||
{
|
{
|
||||||
menuEngine.SetGameDescription(EGID, value);
|
menuEngine.SetGameDescription(EGID, value);
|
||||||
}
|
} // No description exists in-game
|
||||||
else
|
|
||||||
{
|
|
||||||
// No description exists in-game
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +209,7 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
if (!VerifyMode()) return null;
|
if (!VerifyMode()) return null;
|
||||||
if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath;
|
if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath;
|
||||||
return GameMode.SaveGameDetails.Folder;
|
return gameEngine.GetGameData().gameID;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
|
@ -229,11 +219,6 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value);
|
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.
|
/// In most cases this is invalid and returns 0, so this can be ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The workshop identifier.</value>
|
/// <value>The workshop identifier.</value>
|
||||||
|
[Obsolete]
|
||||||
public ulong WorkshopId
|
public ulong WorkshopId
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!VerifyMode()) return 0uL;
|
return 0uL; // Not supported anymore
|
||||||
if (menuMode) return 0uL; // MyGames don't have workshop IDs
|
|
||||||
return GameMode.SaveGameDetails.WorkshopId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
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
|
get
|
||||||
{
|
{
|
||||||
if (menuMode || !VerifyMode()) return CurrentGameMode.None;
|
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;
|
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()
|
~Game()
|
||||||
{
|
{
|
||||||
foreach (string id in debugIds)
|
foreach (string id in debugIds)
|
||||||
|
|
|
@ -11,9 +11,9 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered
|
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";
|
public string Name => "TechbloxModdingAPIBuildSimEventGameEngine";
|
||||||
|
|
||||||
|
@ -27,13 +27,13 @@ namespace TechbloxModdingAPI.App
|
||||||
|
|
||||||
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
|
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;
|
return inputDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JobHandle OnInitializeTimeStoppedMode(JobHandle 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;
|
return inputDeps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
|
using HarmonyLib;
|
||||||
using RobocraftX;
|
using RobocraftX;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.Schedulers;
|
using RobocraftX.Schedulers;
|
||||||
|
@ -10,17 +9,25 @@ using Svelto.ECS;
|
||||||
using Svelto.Tasks;
|
using Svelto.Tasks;
|
||||||
using Svelto.Tasks.Lean;
|
using Svelto.Tasks.Lean;
|
||||||
using RobocraftX.Blocks;
|
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.Blocks;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
|
using TechbloxModdingAPI.Input;
|
||||||
|
using TechbloxModdingAPI.Players;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.App
|
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 string Name => "TechbloxModdingAPIGameInfoMenuEngine";
|
||||||
|
|
||||||
|
@ -28,16 +35,35 @@ namespace TechbloxModdingAPI.App
|
||||||
|
|
||||||
public EntitiesDB entitiesDB { set; private get; }
|
public EntitiesDB entitiesDB { set; private get; }
|
||||||
|
|
||||||
|
private bool enteredGame;
|
||||||
|
private bool loadingFinished;
|
||||||
|
private bool playerJoined;
|
||||||
|
|
||||||
public void Dispose()
|
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;
|
IsInGame = false;
|
||||||
|
loadingFinished = false;
|
||||||
|
playerJoined = false;
|
||||||
|
enteredGame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
ExceptionUtil.InvokeEvent(EnterGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
if (GameReloadedPatch.IsReload)
|
||||||
IsInGame = true;
|
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
|
// game functionality
|
||||||
|
@ -52,7 +78,7 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
if (async)
|
if (async)
|
||||||
{
|
{
|
||||||
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped);
|
ExitCurrentGameAsync().RunOn(ClientLean.EveryFrameStepRunner_TimeRunningAndStopped);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -92,40 +118,78 @@ namespace TechbloxModdingAPI.App
|
||||||
|
|
||||||
public void ToggleTimeMode()
|
public void ToggleTimeMode()
|
||||||
{
|
{
|
||||||
TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB);
|
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)
|
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||||
{
|
{
|
||||||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
||||||
List<EGID> blockEGIDs = new List<EGID>();
|
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)
|
if (filter == BlockIDs.Invalid)
|
||||||
{
|
dbid = (uint)filter;
|
||||||
foreach (var (blocks, _) in allBlocks)
|
|
||||||
{
|
|
||||||
var buffer = blocks.ToBuffer().buffer;
|
|
||||||
for (int i = 0; i < buffer.capacity; i++)
|
|
||||||
blockEGIDs.Add(buffer[i].ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return blockEGIDs.ToArray();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
dbid = entitiesDB.QueryEntity<DBEntityStruct>(id).DBID;
|
||||||
foreach (var (blocks, _) in allBlocks)
|
var ownership = entitiesDB.QueryEntity<BlockOwnershipComponent>(id).BlockOwnership;
|
||||||
{
|
if ((ownership & BlockOwnership.User) != 0 && dbid == (ulong)filter)
|
||||||
var array = blocks.ToBuffer().buffer;
|
blockEGIDs.Add(id);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockEGIDs.ToArray();
|
return blockEGIDs.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnableScreenshotTaker()
|
||||||
|
{
|
||||||
|
ref var local = ref entitiesDB.QueryEntity<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
|
||||||
|
if (local.enabled)
|
||||||
|
return;
|
||||||
|
local.enabled = true;
|
||||||
|
entitiesDB.PublishEntityChange<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameSelectionComponent GetGameData()
|
||||||
|
{
|
||||||
|
return entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(ref LoadingActionEntityStruct entityComponent, EGID egid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
using System.Reflection;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
|
||||||
using RobocraftX;
|
using RobocraftX;
|
||||||
using RobocraftX.Common;
|
|
||||||
using RobocraftX.GUI;
|
using RobocraftX.GUI;
|
||||||
using RobocraftX.GUI.MyGamesScreen;
|
using RobocraftX.GUI.MyGamesScreen;
|
||||||
|
using RobocraftX.Multiplayer;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Experimental;
|
using Svelto.ECS.Experimental;
|
||||||
using Svelto.DataStructures;
|
using Techblox.GameSelection;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
@ -15,6 +17,9 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
public class GameMenuEngine : IFactoryEngine
|
public class GameMenuEngine : IFactoryEngine
|
||||||
{
|
{
|
||||||
|
public WrappedHandler<MenuEventArgs> EnterMenu;
|
||||||
|
|
||||||
|
public WrappedHandler<MenuEventArgs> ExitMenu;
|
||||||
public IEntityFactory Factory { set; private get; }
|
public IEntityFactory Factory { set; private get; }
|
||||||
|
|
||||||
public string Name => "TechbloxModdingAPIGameInfoGameEngine";
|
public string Name => "TechbloxModdingAPIGameInfoGameEngine";
|
||||||
|
@ -23,23 +28,42 @@ namespace TechbloxModdingAPI.App
|
||||||
|
|
||||||
public EntitiesDB entitiesDB { set; private get; }
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
IsInMenu = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
IsInMenu = true;
|
MenuEnteredEnginePatch.IsInMenu = true; // At first it uses ActivateMenu(), then GoToMenu() which is patched
|
||||||
|
MenuEnteredEnginePatch.EnteredExitedMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
// game functionality
|
// game functionality
|
||||||
|
|
||||||
public bool IsInMenu
|
public bool IsInMenu => MenuEnteredEnginePatch.IsInMenu;
|
||||||
|
|
||||||
|
public Game[] GetMyGames()
|
||||||
{
|
{
|
||||||
get;
|
var (mgsevs, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||||
private set;
|
Game[] games = new Game[count];
|
||||||
} = false;
|
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)
|
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()
|
public uint HighestID()
|
||||||
{
|
{
|
||||||
EntityCollection<MyGameDataEntityStruct> games = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
var (games, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||||
var gamesB = games.ToBuffer().buffer;
|
|
||||||
uint max = 0;
|
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;
|
return max;
|
||||||
|
@ -76,15 +99,19 @@ namespace TechbloxModdingAPI.App
|
||||||
{
|
{
|
||||||
if (!ExistsGameInfo(id)) return false;
|
if (!ExistsGameInfo(id)) return false;
|
||||||
ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id);
|
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;
|
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
|
||||||
GameMode.SaveGameDetails = new SaveGameDetails(gameName, path, workshopId);
|
ref var selection = ref entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
|
||||||
// the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason
|
selection.userContentID.Set(fileId);
|
||||||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[0]);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,4 +157,41 @@ namespace TechbloxModdingAPI.App
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }
|
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.Common;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using Gamecraft.Blocks.GUI;
|
using HarmonyLib;
|
||||||
|
using RobocraftX.PilotSeat;
|
||||||
|
using RobocraftX.Rendering;
|
||||||
|
using Techblox.BlockLabelsServer;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Blocks.Engines;
|
using TechbloxModdingAPI.Blocks.Engines;
|
||||||
using TechbloxModdingAPI.Tests;
|
using TechbloxModdingAPI.Tests;
|
||||||
|
@ -65,16 +67,22 @@ namespace TechbloxModdingAPI
|
||||||
/// <returns>The block object or null if doesn't exist</returns>
|
/// <returns>The block object or null if doesn't exist</returns>
|
||||||
public static Block GetLastPlacedBlock()
|
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;
|
return egid.HasValue ? New(egid.Value) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*public static Block CreateGhostBlock()
|
||||||
|
{
|
||||||
|
return BlockGroup._engine.BuildGhostChild();
|
||||||
|
}*/
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An event that fires each time a block is placed.
|
/// An event that fires each time a block is placed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
|
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
|
||||||
{
|
{ //TODO: Rename and add instance version in 3.0
|
||||||
add => BlockEventsEngine.Placed += ExceptionUtil.WrapHandler(value);
|
add => BlockEventsEngine.Placed += value;
|
||||||
remove => BlockEventsEngine.Placed -= value;
|
remove => BlockEventsEngine.Placed -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +91,7 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
|
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
|
||||||
{
|
{
|
||||||
add => BlockEventsEngine.Removed += ExceptionUtil.WrapHandler(value);
|
add => BlockEventsEngine.Removed += value;
|
||||||
remove => BlockEventsEngine.Removed -= value;
|
remove => BlockEventsEngine.Removed -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,19 +99,42 @@ namespace TechbloxModdingAPI
|
||||||
new Dictionary<ExclusiveBuildGroup, (Func<EGID, Block>, Type)>
|
new Dictionary<ExclusiveBuildGroup, (Func<EGID, Block>, Type)>
|
||||||
{
|
{
|
||||||
{CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP, (id => new DampedSpring(id), typeof(DampedSpring))},
|
{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)
|
foreach (var group in SeatGroups.SEATS_BLOCK_GROUPS) // Adds driver and passenger seats, occupied and unoccupied
|
||||||
? GroupToConstructor[egid.groupID].Constructor(egid)
|
GroupToConstructor.Add(group, (id => new Seat(id), typeof(Seat)));
|
||||||
: new Block(egid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
Type expectedType;
|
||||||
if (GroupToConstructor.ContainsKey(id.groupID) &&
|
if (GroupToConstructor.ContainsKey(id.groupID) &&
|
||||||
!GetType().IsAssignableFrom(expectedType = GroupToConstructor[id.groupID].Type))
|
!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="autoWire">Whether the block should be auto-wired (if functional)</param>
|
||||||
/// <param name="player">The player who placed the block</param>
|
/// <param name="player">The player who placed the block</param>
|
||||||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null)
|
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null)
|
||||||
|
: base(block =>
|
||||||
{
|
{
|
||||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode())
|
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode())
|
||||||
throw new BlockException("Blocks can only be placed in build mode.");
|
throw new BlockException("Blocks can only be placed in build mode.");
|
||||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
||||||
Id = initializer.EGID;
|
block.InitData = initializer;
|
||||||
InitData = initializer;
|
Placed += ((Block)block).OnPlacedInit;
|
||||||
Placed += OnPlacedInit;
|
return initializer.EGID;
|
||||||
|
})
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override EGID Id { get; }
|
|
||||||
|
|
||||||
private EGID copiedFrom;
|
private EGID copiedFrom;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -256,6 +288,7 @@ namespace TechbloxModdingAPI
|
||||||
color.indexInPalette = value.Index;
|
color.indexInPalette = value.Index;
|
||||||
color.hasNetworkChange = true;
|
color.hasNetworkChange = true;
|
||||||
color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); //Setting to 255 results in black
|
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.");
|
: throw new BlockTypeException("Unknown block type! Could not set default material.");
|
||||||
if (!FullGameFields._dataDb.ContainsKey<MaterialPropertiesData>(val))
|
if (!FullGameFields._dataDb.ContainsKey<MaterialPropertiesData>(val))
|
||||||
throw new BlockException($"Block material {value} does not exist!");
|
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
|
BlockEngine.UpdatePrefab(this, val, Flipped); //The default causes the screen to go black
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,11 +340,15 @@ namespace TechbloxModdingAPI
|
||||||
[TestValue(null)]
|
[TestValue(null)]
|
||||||
public string Label
|
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
|
set
|
||||||
{
|
{
|
||||||
var comp = BlockEngine.GetBlockInfoViewComponent<TextLabelEntityViewStruct>(this).textLabelComponent;
|
var opt = BlockEngine.GetBlockInfoOptional<LabelResourceIDComponent>(this);
|
||||||
if (comp != null) comp.text = value;
|
if (opt) FullGameFields._managers.blockLabelResourceManager.SetText(opt.Get().instanceID, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,8 +366,12 @@ namespace TechbloxModdingAPI
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (blockGroup != null) return blockGroup;
|
if (blockGroup != null) return blockGroup;
|
||||||
|
if (!GameState.IsBuildMode()) return null; // Breaks in simulation
|
||||||
var bgec = BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this);
|
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
|
set
|
||||||
{
|
{
|
||||||
|
@ -338,6 +382,8 @@ namespace TechbloxModdingAPI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
blockGroup?.RemoveInternal(this);
|
blockGroup?.RemoveInternal(this);
|
||||||
|
if (!InitData.Valid)
|
||||||
|
return;
|
||||||
BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this).currentBlockGroup = (int?) value?.Id.entityID ?? -1;
|
BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this).currentBlockGroup = (int?) value?.Id.entityID ?? -1;
|
||||||
value?.AddInternal(this);
|
value?.AddInternal(this);
|
||||||
blockGroup = value;
|
blockGroup = value;
|
||||||
|
@ -349,8 +395,25 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Static
|
public bool Static
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<OverrideStaticComponent>(this).staticIfUnconnected;
|
get => BlockEngine.GetBlockInfo<BlockStaticComponent>(this).isStatic;
|
||||||
set => BlockEngine.GetBlockInfo<OverrideStaticComponent>(this).staticIfUnconnected = value;
|
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>
|
/// <summary>
|
||||||
|
@ -378,9 +441,10 @@ namespace TechbloxModdingAPI
|
||||||
public SimBody GetSimBody()
|
public SimBody GetSimBody()
|
||||||
{
|
{
|
||||||
var st = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(this);
|
var st = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(this);
|
||||||
return st.machineRigidBodyId != uint.MaxValue
|
/*return st.machineRigidBodyId != uint.MaxValue
|
||||||
? new SimBody(st.machineRigidBodyId, st.clusterId)
|
? new SimBody(st.machineRigidBodyId, st.clusterId) - TODO:
|
||||||
: null;
|
: null;*/
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -404,7 +468,7 @@ namespace TechbloxModdingAPI
|
||||||
if (e.ID != Id) return;
|
if (e.ID != Id) return;
|
||||||
Placed -= OnPlacedInit; //And we can reference it
|
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
|
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);
|
BlockCloneEngine.CopyBlockStats(copiedFrom, Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -19,17 +19,16 @@ namespace TechbloxModdingAPI
|
||||||
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
||||||
{
|
{
|
||||||
internal static BlueprintEngine _engine = new BlueprintEngine();
|
internal static BlueprintEngine _engine = new BlueprintEngine();
|
||||||
public override EGID Id { get; }
|
|
||||||
private readonly Block sourceBlock;
|
private readonly Block sourceBlock;
|
||||||
private readonly List<Block> blocks;
|
private readonly List<Block> blocks;
|
||||||
private float3 position, rotation;
|
private float3 position, rotation;
|
||||||
internal bool PosAndRotCalculated;
|
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)
|
if (id == BlockGroupUtility.GROUP_UNASSIGNED)
|
||||||
throw new BlockException("Cannot create a block group for blocks without a group!");
|
throw new BlockException("Cannot create a block group for blocks without a group!");
|
||||||
Id = new EGID((uint) id, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
|
|
||||||
sourceBlock = block;
|
sourceBlock = block;
|
||||||
blocks = new List<Block>(GetBlocks());
|
blocks = new List<Block>(GetBlocks());
|
||||||
Block.Removed += OnBlockRemoved;
|
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,
|
PlateQuarterPyramid,
|
||||||
PlateTetrahedron,
|
PlateTetrahedron,
|
||||||
Sphere,
|
Sphere,
|
||||||
Frame,
|
CarWheelArch = 47,
|
||||||
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,
|
|
||||||
CarArchSmallFlare,
|
CarArchSmallFlare,
|
||||||
CarArchFlare,
|
CarArchFlare,
|
||||||
CarArchExtrudedFlare,
|
CarArchExtrudedFlare,
|
||||||
|
@ -73,8 +42,8 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
PlateTriangle = 130,
|
PlateTriangle = 130,
|
||||||
PlateCircle,
|
PlateCircle,
|
||||||
PlateQuarterCircle,
|
PlateQuarterCircle,
|
||||||
PlateRWedge,
|
PlateRoundedWedge,
|
||||||
PlateRTetrahedron,
|
PlateRoundedTetrahedron,
|
||||||
Cone,
|
Cone,
|
||||||
ConeSegment,
|
ConeSegment,
|
||||||
DoubleSliced,
|
DoubleSliced,
|
||||||
|
@ -106,7 +75,309 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
WideCylinderDiagonal,
|
WideCylinderDiagonal,
|
||||||
NarrowCylinderDiagonal,
|
NarrowCylinderDiagonal,
|
||||||
HeadlampTetrahedron,
|
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,
|
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,
|
RigidSteel,
|
||||||
CarbonFiber,
|
CarbonFiber,
|
||||||
Plastic,
|
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 Svelto.Tasks;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.App;
|
|
||||||
using TechbloxModdingAPI.Tests;
|
using TechbloxModdingAPI.Tests;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
@ -46,16 +45,20 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
"Block ID enum matches the known block types.");
|
"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)]
|
[APITestCase(TestType.EditMode)]
|
||||||
public static void TestBlockIDs()
|
public static void TestBlockIDs()
|
||||||
{
|
{
|
||||||
float3 pos = new float3();
|
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;
|
if (id == BlockIDs.Invalid) continue;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Block.PlaceNew(id, pos);
|
blocks[i++] = Block.PlaceNew(id, pos);
|
||||||
pos += 0.2f;
|
pos += 0.2f;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -71,38 +74,55 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
[APITestCase(TestType.EditMode)]
|
[APITestCase(TestType.EditMode)]
|
||||||
public static IEnumerator<TaskContract> TestBlockProperties()
|
public static IEnumerator<TaskContract> TestBlockProperties()
|
||||||
{ //Uses the result of the previous test case
|
{ //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++)
|
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];
|
var block = blocks[index];
|
||||||
if (!block.Exists) continue;
|
if (!block.Exists) continue;
|
||||||
foreach (var property in block.GetType().GetProperties())
|
foreach (var property in block.GetType().GetProperties())
|
||||||
{
|
{
|
||||||
//Includes specialised block properties
|
//Includes specialised block properties
|
||||||
if (property.SetMethod == null) continue;
|
if (property.SetMethod == null) continue;
|
||||||
var testValues = new (Type, object, Predicate<object>)[]
|
|
||||||
|
bool3 Float3Compare(float3 a, float3 b)
|
||||||
|
{ // From Unity reference code
|
||||||
|
return math.abs(b - a) < math.max(
|
||||||
|
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||||
|
float.Epsilon * 8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
bool4 Float4Compare(float4 a, float4 b)
|
||||||
|
{ // From Unity reference code
|
||||||
|
return math.abs(b - a) < math.max(
|
||||||
|
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||||
|
float.Epsilon * 8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var testValues = new (Type, object, Predicate<(object Value, object Default)>)[]
|
||||||
{
|
{
|
||||||
//(type, default value, predicate or null for equality)
|
//(type, default value, predicate or null for equality)
|
||||||
(typeof(long), 3, null),
|
(typeof(long), 3, null),
|
||||||
(typeof(int), 4, null),
|
(typeof(int), 4, null),
|
||||||
(typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon),
|
(typeof(double), 5.2f, t => Math.Abs((double) t.Value - (double) t.Default) < float.Epsilon),
|
||||||
(typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon),
|
(typeof(float), 5.2f, t => Math.Abs((float) t.Value - (float) t.Default) < float.Epsilon),
|
||||||
(typeof(bool), true, obj => (bool) obj),
|
(typeof(bool), true, t => (bool) t.Value),
|
||||||
(typeof(string), "Test", obj => (string) obj == "Test"), //String equality check
|
(typeof(string), "Test", t => (string) t.Value == "Test"), //String equality check
|
||||||
(typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)),
|
(typeof(float3), (float3) 20, t => math.all(Float3Compare((float3)t.Value, (float3)t.Default))),
|
||||||
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
||||||
(typeof(float4), (float4) 5, obj => math.all((float4) obj - 5 < (float4) float.Epsilon))
|
(typeof(float4), (float4) 5, t => math.all(Float4Compare((float4)t.Value, (float4)t.Default)))
|
||||||
};
|
};
|
||||||
var propType = property.PropertyType;
|
var propType = property.PropertyType;
|
||||||
if (!propType.IsValueType) continue;
|
if (!propType.IsValueType) continue;
|
||||||
(object valueToUse, Predicate<object> predicateToUse) = (null, null);
|
(object valueToUse, Predicate<(object Value, object Default)> predicateToUse) = (null, null);
|
||||||
foreach (var (type, value, predicate) in testValues)
|
foreach (var (type, value, predicate) in testValues)
|
||||||
{
|
{
|
||||||
if (type.IsAssignableFrom(propType))
|
if (type.IsAssignableFrom(propType))
|
||||||
{
|
{
|
||||||
valueToUse = value;
|
valueToUse = value;
|
||||||
predicateToUse = predicate ?? (obj => Equals(obj, value));
|
predicateToUse = predicate ?? (t => Equals(t.Value, t.Default));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +131,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
var values = propType.GetEnumValues();
|
var values = propType.GetEnumValues();
|
||||||
valueToUse = values.GetValue(values.Length / 2);
|
valueToUse = values.GetValue(values.Length / 2);
|
||||||
predicateToUse = val => Equals(val, valueToUse);
|
predicateToUse = t => Equals(t.Value, t.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueToUse == null)
|
if (valueToUse == null)
|
||||||
|
@ -120,10 +140,26 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
property.SetValue(block, valueToUse);
|
property.SetValue(block, valueToUse);
|
||||||
object got = property.GetValue(block);
|
}
|
||||||
|
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>();
|
var attr = property.GetCustomAttribute<TestValueAttribute>();
|
||||||
if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got)))
|
if (!predicateToUse((got, valueToUse)) && (attr == null || !Equals(attr.PossibleValue, got)))
|
||||||
{
|
{
|
||||||
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
|
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
|
||||||
yield break;
|
yield break;
|
||||||
|
@ -134,14 +170,33 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
Assert.Pass("Setting all possible properties of all registered API block types succeeded.");
|
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)]
|
[APITestCase(TestType.EditMode)]
|
||||||
public static void TestDampedSpring()
|
public static void TestDampedSpring()
|
||||||
{
|
{
|
||||||
Block newBlock = Block.PlaceNew(BlockIDs.DampedSpring, Unity.Mathematics.float3.zero + 1);
|
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
|
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.");
|
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.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, 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.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;
|
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;
|
namespace TechbloxModdingAPI.Blocks
|
||||||
|
{
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks
|
|
||||||
{
|
|
||||||
public class DampedSpring : Block
|
|
||||||
{
|
|
||||||
public DampedSpring(EGID id) : base(id)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public DampedSpring(uint id) : base(new EGID(id, CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP))
|
public class DampedSpring : SignalingBlock
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The spring frequency.
|
/// Constructs a(n) DampedSpring object representing an existing block.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float SpringFrequency
|
public DampedSpring(EGID egid) :
|
||||||
|
base(egid)
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springFrequency;
|
|
||||||
|
|
||||||
set => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springFrequency = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public float Damping
|
public float Damping
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springDamping;
|
get
|
||||||
|
{
|
||||||
set => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).springDamping = value;
|
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).damping;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).damping = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The spring's maximum extension.
|
/// Gets or sets the DampedSpring's MaxExtension property. Tweakable stat.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float MaxExtension
|
public float MaxExtension
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).maxExtent;
|
get
|
||||||
|
{
|
||||||
set => BlockEngine.GetBlockInfo<DampedSpringReadOnlyStruct>(this).maxExtent = value;
|
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
|
namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
|
using RobocraftX.Common;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
|
||||||
public class Engine : SignalingBlock
|
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
|
public bool On
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).engineOn;
|
get
|
||||||
set => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).engineOn = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int TorqueDirection
|
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).torqueDirection;
|
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn;
|
||||||
set => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).torqueDirection = value;
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Engine's CurrentGear property. May not be saved.
|
||||||
|
/// </summary>
|
||||||
public int CurrentGear
|
public int CurrentGear
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).currentGear;
|
get
|
||||||
set => BlockEngine.GetBlockInfo<EngineBlockComponent>(this).currentGear = value;
|
{
|
||||||
|
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.pickedBlockEntityID = sourceID;
|
||||||
pickedBlock.placedBlockEntityID = targetID;
|
pickedBlock.placedBlockEntityID = targetID;
|
||||||
pickedBlock.placedBlockTweaksMustCopy = true;
|
pickedBlock.placedBlockTweaksMustCopy = true;
|
||||||
if (entitiesDB.Exists<DBEntityStruct>(pickedBlock.pickedBlockEntityID)
|
if (entitiesDB.Exists<BlockTagEntityStruct>(pickedBlock.pickedBlockEntityID)
|
||||||
&& entitiesDB.Exists<DBEntityStruct>(pickedBlock.placedBlockEntityID))
|
&& entitiesDB.Exists<BlockTagEntityStruct>(pickedBlock.placedBlockEntityID))
|
||||||
{
|
{
|
||||||
copyFromBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock});
|
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});
|
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});
|
copyWireToBlock.Invoke(Patch.createWireEngine, new object[] {group, pickedBlock.ID});
|
||||||
|
|
||||||
pickedBlock.placedBlockTweaksMustCopy = false;
|
pickedBlock.placedBlockTweaksMustCopy = false;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
using Gamecraft.ColourPalette;
|
using Gamecraft.ColourPalette;
|
||||||
using Gamecraft.TimeRunning;
|
|
||||||
using Gamecraft.Wires;
|
using Gamecraft.Wires;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
|
@ -12,11 +13,16 @@ using RobocraftX.Rendering.GPUI;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.EntityStructs;
|
using Svelto.ECS.EntityStructs;
|
||||||
|
using Svelto.ECS.Experimental;
|
||||||
using Svelto.ECS.Hybrid;
|
using Svelto.ECS.Hybrid;
|
||||||
|
using Techblox.BuildingDrone;
|
||||||
|
using Techblox.ObjectIDBlockServer;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
using PrefabsID = RobocraftX.Common.PrefabsID;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
@ -41,22 +47,21 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public Block[] GetConnectedBlocks(EGID blockID)
|
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>();
|
Stack<EGID> cubeStack = new Stack<EGID>();
|
||||||
FasterList<EGID> cubes = new FasterList<EGID>(10);
|
FasterList<EGID> cubes = new FasterList<EGID>(10);
|
||||||
var coll = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
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 < count; i++)
|
||||||
for(int i = 0; i < ecoll.count; i++)
|
|
||||||
{
|
{
|
||||||
ref var conn = ref ecollB.buffer[i];
|
ecoll[i].areConnectionsAssigned = false;
|
||||||
conn.isProcessed = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
|
//TODO: GetConnectedCubesUtility
|
||||||
(in GridConnectionsEntityStruct g) => { return false; });
|
/*ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
|
||||||
|
(in GridConnectionsEntityStruct _) => false);*/
|
||||||
|
|
||||||
var ret = new Block[cubes.count];
|
var ret = new Block[cubes.count];
|
||||||
for (int i = 0; i < cubes.count; i++)
|
for (int i = 0; i < cubes.count; i++)
|
||||||
|
@ -67,7 +72,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
public float4 ConvertBlockColor(byte index) => index == byte.MaxValue
|
public float4 ConvertBlockColor(byte index) => index == byte.MaxValue
|
||||||
? new float4(-1f, -1f, -1f, -1f)
|
? new float4(-1f, -1f, -1f, -1f)
|
||||||
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
|
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
|
||||||
CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
ColourPaletteExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
||||||
|
|
||||||
public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent
|
public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent
|
||||||
{
|
{
|
||||||
|
@ -76,6 +81,10 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public ref T GetBlockInfo<T>(Block block) where T : unmanaged, IEntityComponent
|
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);
|
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,13 +98,35 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
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)
|
public void UpdateDisplayedBlock(EGID id)
|
||||||
{
|
{
|
||||||
if (!BlockExists(id)) return;
|
if (!BlockExists(id)) return;
|
||||||
var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
|
var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
|
||||||
var rot = entitiesDB.QueryEntity<RotationEntityStruct>(id);
|
var rot = entitiesDB.QueryEntity<RotationEntityStruct>(id);
|
||||||
var scale = entitiesDB.QueryEntity<ScalingEntityStruct>(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)
|
internal void UpdatePrefab(Block block, byte material, bool flipped)
|
||||||
|
@ -106,50 +137,58 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
: uint.MaxValue;
|
: uint.MaxValue;
|
||||||
if (prefabAssetID == 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
|
throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint prefabId =
|
uint prefabId =
|
||||||
PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, material, 1, flipped);
|
PrefabsID.GetOrAddPrefabID((ushort) prefabAssetID, material, 1, flipped);
|
||||||
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
|
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
|
||||||
if (block.Exists)
|
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
|
//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)
|
public bool BlockExists(EGID blockID)
|
||||||
{
|
{
|
||||||
return entitiesDB.Exists<DBEntityStruct>(blockID);
|
return entitiesDB.Exists<BlockTagEntityStruct>(blockID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimBody[] GetSimBodiesFromID(byte id)
|
public SimBody[] GetSimBodiesFromID(byte id)
|
||||||
{
|
{
|
||||||
var ret = new FasterList<SimBody>(4);
|
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;
|
EGIDMapper<GridConnectionsEntityStruct>? connections = null;
|
||||||
foreach (var ((oids, count), _) in oide)
|
foreach (var oid in oids)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
if (oid.Get().objectId != id) continue;
|
||||||
{
|
|
||||||
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
|
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
|
||||||
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.ID.groupID);
|
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.EGID.groupID);
|
||||||
var rid = connections.Value.Entity(oid.ID.entityID).machineRigidBodyId;
|
//var rid = connections.Value.Entity(tag.ID.entityID).machineRigidBodyId;
|
||||||
foreach (var rb in ret)
|
/*foreach (var rb in ret) - TODO
|
||||||
{
|
{
|
||||||
if (rb.Id.entityID == rid)
|
if (rb.Id.entityID == rid)
|
||||||
goto DUPLICATE; //Multiple Object Identifiers on one rigid body
|
goto DUPLICATE; //Multiple Object Identifiers on one rigid body
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Add(new SimBody(rid));
|
ret.Add(new SimBody(rid));
|
||||||
DUPLICATE: ;
|
DUPLICATE: ;*/
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret.ToArray();
|
return ret.ToArray();
|
||||||
|
@ -157,14 +196,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public SimBody[] GetConnectedSimBodies(uint id)
|
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);
|
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.isBroken) continue;
|
||||||
if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB));
|
/*if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); - TODO:
|
||||||
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));
|
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));*/
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.ToArray();
|
return list.ToArray();
|
||||||
|
@ -174,14 +213,13 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||||
var bodies = new HashSet<uint>();
|
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 < count; index++)
|
||||||
for (var index = 0; index < array.capacity; index++)
|
|
||||||
{
|
{
|
||||||
var conn = array[index];
|
var conn = coll[index];
|
||||||
if (conn.clusterId == cid)
|
/*if (conn.clusterId == cid) - TODO
|
||||||
bodies.Add(conn.machineRigidBodyId);
|
bodies.Add(conn.machineRigidBodyId);*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,10 +228,10 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public EGID? FindBlockEGID(uint id)
|
public EGID? FindBlockEGID(uint id)
|
||||||
{
|
{
|
||||||
var groups = entitiesDB.FindGroups<DBEntityStruct>();
|
var groups = entitiesDB.FindGroups<BlockTagEntityStruct>();
|
||||||
foreach (ExclusiveGroupStruct group in groups)
|
foreach (ExclusiveGroupStruct group in groups)
|
||||||
{
|
{
|
||||||
if (entitiesDB.Exists<DBEntityStruct>(id, group))
|
if (entitiesDB.Exists<BlockTagEntityStruct>(id, group))
|
||||||
return new EGID(id, group);
|
return new EGID(id, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,15 +241,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
public Cluster GetCluster(uint sbid)
|
public Cluster GetCluster(uint sbid)
|
||||||
{
|
{
|
||||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
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 < count; index++)
|
||||||
for (var index = 0; index < array.capacity; 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
|
//Static blocks don't have a cluster ID but the cluster destruction manager should have one
|
||||||
if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue)
|
/*if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue) - TODO:
|
||||||
return new Cluster(conn.clusterId);
|
return new Cluster(conn.clusterId);*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,27 +257,36 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public Block[] GetBodyBlocks(uint sbid)
|
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>();
|
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 < count; index++)
|
||||||
for (var index = 0; index < array.capacity; index++)
|
|
||||||
{
|
{
|
||||||
var conn = array[index];
|
var conn = coll[index];
|
||||||
if (conn.machineRigidBodyId == sbid)
|
/*if (conn.machineRigidBodyId == sbid) - TODO
|
||||||
set.Add(Block.New(conn.ID));
|
set.Add(Block.New(tags[index].ID));*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return set.ToArray();
|
return set.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
public ObjectID[] GetObjectIDsFromID(byte id)
|
||||||
public EntitiesDB GetEntitiesDB()
|
|
||||||
{
|
{
|
||||||
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 class BlockEventsEngine : IReactionaryEngine<BlockTagEntityStruct>
|
||||||
{
|
{
|
||||||
public event EventHandler<BlockPlacedRemovedEventArgs> Placed;
|
public WrappedHandler<BlockPlacedRemovedEventArgs> Placed;
|
||||||
public event EventHandler<BlockPlacedRemovedEventArgs> Removed;
|
public WrappedHandler<BlockPlacedRemovedEventArgs> Removed;
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
|
@ -26,21 +26,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
public string Name { get; } = "TechbloxModdingAPIBlockEventsEngine";
|
public string Name { get; } = "TechbloxModdingAPIBlockEventsEngine";
|
||||||
public bool isRemovable { get; } = false;
|
public bool isRemovable { get; } = false;
|
||||||
|
|
||||||
private bool shouldAddRemove;
|
|
||||||
public void Add(ref BlockTagEntityStruct entityComponent, EGID egid)
|
public void Add(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||||
{
|
{
|
||||||
if (!(shouldAddRemove = !shouldAddRemove))
|
Placed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||||
return;
|
|
||||||
ExceptionUtil.InvokeEvent(Placed, this,
|
|
||||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid)
|
public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||||
{
|
{
|
||||||
if (!(shouldAddRemove = !shouldAddRemove))
|
Removed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||||
return;
|
|
||||||
ExceptionUtil.InvokeEvent(Removed, this,
|
|
||||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +42,6 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
public EGID ID;
|
public EGID ID;
|
||||||
private Block block;
|
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.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Gamecraft.Blocks.BlockGroups;
|
using Gamecraft.Blocks.BlockGroups;
|
||||||
using Gamecraft.GUI.Blueprints;
|
using Gamecraft.GUI.Blueprints;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
|
using RobocraftX.Blocks.Ghost;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||||
using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations;
|
using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations;
|
||||||
|
using RobocraftX.Physics;
|
||||||
|
using RobocraftX.Rendering;
|
||||||
|
using RobocraftX.Rendering.GPUI;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.DataStructures;
|
using Svelto.ECS.DataStructures;
|
||||||
using Svelto.ECS.EntityStructs;
|
using Svelto.ECS.EntityStructs;
|
||||||
|
using Svelto.ECS.Native;
|
||||||
using Svelto.ECS.Serialization;
|
using Svelto.ECS.Serialization;
|
||||||
|
using Techblox.Blocks.Connections;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
@ -42,7 +49,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
private static readonly MethodInfo SerializeGhostBlueprint =
|
private static readonly MethodInfo SerializeGhostBlueprint =
|
||||||
AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities");
|
AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities");
|
||||||
|
|
||||||
private static NativeEntityRemove nativeRemove;
|
private static NativeEntityRemove nativeBlockRemove;
|
||||||
|
private static NativeEntityRemove nativeConnectionRemove;
|
||||||
private static MachineGraphConnectionEntityFactory connectionFactory;
|
private static MachineGraphConnectionEntityFactory connectionFactory;
|
||||||
private static IEntityFunctions entityFunctions;
|
private static IEntityFunctions entityFunctions;
|
||||||
private static ClipboardSerializationDataResourceManager clipboardManager;
|
private static ClipboardSerializationDataResourceManager clipboardManager;
|
||||||
|
@ -82,8 +90,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public void RemoveBlockGroup(int id)
|
public void RemoveBlockGroup(int id)
|
||||||
{
|
{
|
||||||
BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeRemove,
|
BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeBlockRemove,
|
||||||
connectionFactory, default).Complete();
|
nativeConnectionRemove, connectionFactory, default).Complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CreateBlockGroup(float3 position, quaternion rotation)
|
public int CreateBlockGroup(float3 position, quaternion rotation)
|
||||||
|
@ -160,10 +168,15 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
private void BuildGhostBlueprint(ICollection<Block> blocks, float3 pos, quaternion rot, uint playerID)
|
private void BuildGhostBlueprint(ICollection<Block> blocks, float3 pos, quaternion rot, uint playerID)
|
||||||
{
|
{
|
||||||
GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions);
|
GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions);
|
||||||
|
var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerID,
|
||||||
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
|
||||||
|
if (!bssesopt)
|
||||||
|
return;
|
||||||
foreach (var block in blocks)
|
foreach (var block in blocks)
|
||||||
{
|
{
|
||||||
GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB,
|
GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB,
|
||||||
BuildGhostBlueprintFactory, false);
|
BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference,
|
||||||
|
FullGameFields._managers.blockLabelResourceManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +261,77 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
clipboardManager.DecrementRefCount(blueprintID);
|
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 string Name { get; } = "TechbloxModdingAPIBlueprintGameEngine";
|
||||||
public bool isRemovable { get; } = false;
|
public bool isRemovable { get; } = false;
|
||||||
|
|
||||||
|
@ -257,7 +341,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
public static void Prefix(IEntityFunctions entityFunctions,
|
public static void Prefix(IEntityFunctions entityFunctions,
|
||||||
MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
|
MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
|
||||||
{
|
{
|
||||||
nativeRemove = entityFunctions.ToNativeRemove<BlockEntityDescriptor>("GCAPI" + nameof(BlueprintEngine));
|
nativeBlockRemove = entityFunctions.ToNativeRemove<BlockEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
|
||||||
|
nativeConnectionRemove = entityFunctions.ToNativeRemove<MachineConnectionEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
|
||||||
connectionFactory = machineGraphConnectionEntityFactory;
|
connectionFactory = machineGraphConnectionEntityFactory;
|
||||||
BlueprintEngine.entityFunctions = entityFunctions;
|
BlueprintEngine.entityFunctions = entityFunctions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.UECS;
|
using RobocraftX.DOTS;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.EntityStructs;
|
using Svelto.ECS.EntityStructs;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
@ -7,6 +7,7 @@ using Unity.Transforms;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
@ -40,7 +41,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block);
|
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block);
|
||||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
||||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(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
|
// main (persistent) position
|
||||||
posStruct.position = vector;
|
posStruct.position = vector;
|
||||||
// placement grid position
|
// placement grid position
|
||||||
|
@ -48,15 +49,15 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
// rendered position
|
// rendered position
|
||||||
transStruct.position = vector;
|
transStruct.position = vector;
|
||||||
// collision position
|
// collision position
|
||||||
if (phyStruct.ID != EGID.Empty)
|
if (phyStruct)
|
||||||
{ //It exists
|
{ //It exists
|
||||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation
|
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity, new Translation
|
||||||
{
|
{
|
||||||
Value = posStruct.position
|
Value = posStruct.position
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).isProcessed = false;
|
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
|
||||||
return posStruct.position;
|
return posStruct.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ using HarmonyLib;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
using RobocraftX.Character;
|
using RobocraftX.Character;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
|
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||||
using RobocraftX.Rendering;
|
using RobocraftX.Rendering;
|
||||||
using RobocraftX.Rendering.GPUI;
|
using RobocraftX.Rendering.GPUI;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
@ -15,6 +16,7 @@ using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
@ -53,11 +55,11 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
|
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
|
||||||
DBEntityStruct dbEntity = new DBEntityStruct {DBID = block};
|
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>()
|
uint prefabAssetID = structInitializer.Has<PrefabAssetIDComponent>()
|
||||||
? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID
|
? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID
|
||||||
: throw new BlockException("Prefab asset ID not found!"); //Set by the game
|
: 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(new GFXPrefabEntityStructGPUI(prefabId));
|
||||||
structInitializer.Init(dbEntity);
|
structInitializer.Init(dbEntity);
|
||||||
structInitializer.Init(new PositionEntityStruct {position = position});
|
structInitializer.Init(new PositionEntityStruct {position = position});
|
||||||
|
@ -70,10 +72,14 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
});
|
});
|
||||||
structInitializer.Init(new UniformBlockScaleEntityStruct {scaleFactor = 1});
|
structInitializer.Init(new UniformBlockScaleEntityStruct {scaleFactor = 1});
|
||||||
structInitializer.Get<CubeMaterialStruct>().materialId = (byte) BlockMaterial.SteelBodywork;
|
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
|
structInitializer.Init(new BlockPlacementInfoStruct
|
||||||
{
|
{
|
||||||
loadedFromDisk = false,
|
loadedFromDisk = false,
|
||||||
placedBy = playerId,
|
placedByBuildingDrone = bssesopt.Get().buildingDroneReference,
|
||||||
triggerAutoWiring = autoWire && structInitializer.Has<BlockPortsStruct>()
|
triggerAutoWiring = autoWire && structInitializer.Has<BlockPortsStruct>()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,61 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
using Gamecraft.Blocks.BlockGroups;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
|
using RobocraftX.GroupTags;
|
||||||
|
using RobocraftX.StateSync;
|
||||||
using Svelto.ECS;
|
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.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
public class RemovalEngine : IApiEngine
|
public class RemovalEngine : IApiEngine, IDeterministicTimeStopped
|
||||||
{
|
{
|
||||||
private static IEntityFunctions _entityFunctions;
|
private static IEntityFunctions _entityFunctions;
|
||||||
private static MachineGraphConnectionEntityFactory _connectionFactory;
|
private static MachineGraphConnectionEntityFactory _connectionFactory;
|
||||||
|
private NativeHashSet<ulong> removedConnections;
|
||||||
|
|
||||||
public bool RemoveBlock(EGID target)
|
public bool RemoveBlock(EGID target)
|
||||||
{
|
{
|
||||||
if (!entitiesDB.Exists<MachineGraphConnectionsEntityStruct>(target))
|
if (!entitiesDB.Exists<MachineGraphConnectionsEntityStruct>(target))
|
||||||
return false;
|
return false;
|
||||||
var connections = entitiesDB.QueryEntity<MachineGraphConnectionsEntityStruct>(target);
|
using var connStructMapper =
|
||||||
var groups = entitiesDB.FindGroups<MachineGraphConnectionsEntityStruct>();
|
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(GroupTag<BLOCK_TAG>.Groups,
|
||||||
var connStructMapper =
|
Svelto.Common.Allocator.Temp);
|
||||||
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(groups);
|
if (entitiesDB.TryQueryNativeMappedEntities<MachineConnectionComponent>(
|
||||||
for (int i = connections.connections.Count<MachineConnectionStruct>() - 1; i >= 0; i--)
|
ConnectionsExclusiveGroups.MACHINE_CONNECTION_GROUP, out var mapper))
|
||||||
_connectionFactory.RemoveConnection(connections, i, connStructMapper);
|
{
|
||||||
|
BlockGroupUtility.RemoveBlockConnections(target, removedConnections, _connectionFactory,
|
||||||
|
connStructMapper, mapper, entitiesDB.GetEntityReferenceMap(), _entityFunctions);
|
||||||
|
}
|
||||||
|
|
||||||
_entityFunctions.RemoveEntity<BlockEntityDescriptor>(target);
|
_entityFunctions.RemoveEntity<BlockEntityDescriptor>(target);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
|
removedConnections = new(2000, Allocator.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitiesDB entitiesDB { get; set; }
|
public EntitiesDB entitiesDB { get; set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
removedConnections.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; } = "TechbloxModdingAPIRemovalGameEngine";
|
public string Name => "TechbloxModdingAPIRemovalGameEngine";
|
||||||
|
public string name => Name;
|
||||||
|
|
||||||
public bool isRemovable => false;
|
public bool isRemovable => false;
|
||||||
|
|
||||||
|
@ -59,5 +75,12 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine").GetConstructors()[0];
|
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine").GetConstructors()[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JobHandle DeterministicStep(in float deltaTime, JobHandle inputDeps)
|
||||||
|
{
|
||||||
|
if (removedConnections.IsCreated)
|
||||||
|
removedConnections.Clear();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.UECS;
|
using RobocraftX.DOTS;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.EntityStructs;
|
using Svelto.ECS.EntityStructs;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
@ -7,6 +7,7 @@ using UnityEngine;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
@ -40,7 +41,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
||||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
||||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(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
|
// main (persistent) rotation
|
||||||
Quaternion newRotation = rotStruct.rotation;
|
Quaternion newRotation = rotStruct.rotation;
|
||||||
newRotation.eulerAngles = vector;
|
newRotation.eulerAngles = vector;
|
||||||
|
@ -50,16 +51,18 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
// rendered rotation
|
// rendered rotation
|
||||||
transStruct.rotation = newRotation;
|
transStruct.rotation = newRotation;
|
||||||
// collision rotation
|
// collision rotation
|
||||||
if (phyStruct.ID != EGID.Empty)
|
if (phyStruct)
|
||||||
{ //It exists
|
{ //It exists
|
||||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity,
|
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity,
|
||||||
new Unity.Transforms.Rotation
|
new Unity.Transforms.Rotation
|
||||||
{
|
{
|
||||||
Value = rotStruct.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;
|
return ((Quaternion)rotStruct.rotation).eulerAngles;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.UECS;
|
using RobocraftX.DOTS;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Unity.Entities;
|
using Unity.Entities;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
public class ScalingEngine : IApiEngine
|
public class ScalingEngine : IApiEngine
|
||||||
{
|
{
|
||||||
private static IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> physicsEngine;
|
private static IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> physicsEngine;
|
||||||
|
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
|
@ -34,16 +34,16 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
if (_entityManager == default)
|
if (_entityManager == default)
|
||||||
_entityManager = FullGameFields._physicsWorld.EntityManager;
|
_entityManager = FullGameFields._physicsWorld.EntityManager;
|
||||||
//Assuming the block exists
|
//Assuming the block exists
|
||||||
var entity = entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(egid).uecsEntity;
|
var entity = entitiesDB.QueryEntity<DOTSPhysicsEntityStruct>(egid).dotsEntity;
|
||||||
var pes = new UECSPhysicsEntityCreationStruct();
|
var pes = new DOTSPhysicsEntityCreationStruct();
|
||||||
physicsEngine.Add(ref pes, egid); //Create new UECS entity
|
physicsEngine.Add(ref pes, egid); //Create new DOTS entity
|
||||||
_entityManager.DestroyEntity(entity);
|
_entityManager.DestroyEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
class PhysicsEnginePatch
|
class PhysicsEnginePatch
|
||||||
{
|
{
|
||||||
static void Postfix(IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> __instance)
|
static void Postfix(IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> __instance)
|
||||||
{
|
{
|
||||||
physicsEngine = __instance;
|
physicsEngine = __instance;
|
||||||
Logging.MetaDebugLog("Physics engine injected.");
|
Logging.MetaDebugLog("Physics engine injected.");
|
||||||
|
@ -51,7 +51,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
static MethodBase TargetMethod(Harmony instance)
|
static MethodBase TargetMethod(Harmony instance)
|
||||||
{
|
{
|
||||||
return AccessTools.Method("RobocraftX.StateSync.HandleUECSPhysicEntitiesWithPrefabCreationEngine" +
|
return AccessTools.Method("RobocraftX.StateSync.HandleDOTSPhysicEntitiesWithPrefabCreationEngine" +
|
||||||
":Ready");
|
":Ready");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Svelto.ECS;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
@ -41,19 +42,18 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
// implementations for block wiring
|
// implementations for block wiring
|
||||||
|
|
||||||
public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
|
public (WireEntityStruct Wire, EGID ID) CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
|
||||||
{
|
{
|
||||||
EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup<WiresGroup>.Group);
|
EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
|
||||||
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
|
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
|
||||||
wireInitializer.Init(new WireEntityStruct
|
wireInitializer.Init(new WireEntityStruct
|
||||||
{
|
{
|
||||||
sourceBlockEGID = startBlock,
|
sourceBlockEGID = startBlock,
|
||||||
sourcePortUsage = startPort,
|
sourcePortUsage = startPort,
|
||||||
destinationBlockEGID = endBlock,
|
destinationBlockEGID = endBlock,
|
||||||
destinationPortUsage = endPort,
|
destinationPortUsage = endPort
|
||||||
ID = wireEGID
|
|
||||||
});
|
});
|
||||||
return wireInitializer.Get<WireEntityStruct>();
|
return (wireInitializer.Get<WireEntityStruct>(), wireEGID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref WireEntityStruct GetWire(EGID wire)
|
public ref WireEntityStruct GetWire(EGID wire)
|
||||||
|
@ -77,8 +77,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
|
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
|
||||||
{
|
{
|
||||||
ExclusiveGroup group = input
|
ExclusiveGroup group = input
|
||||||
? NamedExclusiveGroup<InputPortsGroup>.Group
|
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
||||||
: NamedExclusiveGroup<OutputPortsGroup>.Group;
|
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
||||||
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
|
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
|
||||||
EGID egid = new EGID(id, group);
|
EGID egid = new EGID(id, group);
|
||||||
if (!entitiesDB.Exists<PortEntityStruct>(egid))
|
if (!entitiesDB.Exists<PortEntityStruct>(egid))
|
||||||
|
@ -116,9 +116,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public bool SetSignal(uint signalID, float signal, bool input = true)
|
public bool SetSignal(uint signalID, float signal, bool input = true)
|
||||||
{
|
{
|
||||||
var array = GetSignalStruct(signalID, out uint index, input);
|
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||||
var arrayB = array.ToBuffer();
|
if (count > 0) array[index].valueAsFloat = signal;
|
||||||
if (array.count > 0) arrayB.buffer[index].valueAsFloat = signal;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,11 +129,10 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
|
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
|
||||||
{
|
{
|
||||||
var array = GetSignalStruct(signalID, out uint index, input);
|
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||||
var arrayB = array.ToBuffer();
|
if (count > 0)
|
||||||
if (array.count > 0)
|
|
||||||
{
|
{
|
||||||
ref var channelData = ref arrayB.buffer[index];
|
ref var channelData = ref array[index];
|
||||||
channelData.valueAsFloat += signal;
|
channelData.valueAsFloat += signal;
|
||||||
if (clamp)
|
if (clamp)
|
||||||
{
|
{
|
||||||
|
@ -162,9 +160,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public float GetSignal(uint signalID, bool input = true)
|
public float GetSignal(uint signalID, bool input = true)
|
||||||
{
|
{
|
||||||
var array = GetSignalStruct(signalID, out uint index, input);
|
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||||
var arrayB = array.ToBuffer();
|
return count > 0 ? array[index].valueAsFloat : 0f;
|
||||||
return array.count > 0 ? arrayB.buffer[index].valueAsFloat : 0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint[] GetSignalIDs(EGID blockID, bool input = true)
|
public uint[] GetSignalIDs(EGID blockID, bool input = true)
|
||||||
|
@ -193,7 +190,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
EGID[] inputs = new EGID[ports.inputCount];
|
EGID[] inputs = new EGID[ports.inputCount];
|
||||||
for (uint i = 0; i < ports.inputCount; i++)
|
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;
|
return inputs;
|
||||||
}
|
}
|
||||||
|
@ -204,70 +201,55 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
EGID[] outputs = new EGID[ports.outputCount];
|
EGID[] outputs = new EGID[ports.outputCount];
|
||||||
for (uint i = 0; i < ports.outputCount; i++)
|
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;
|
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);
|
return MatchBlockIOToPort(block.Id, portUsage, output);
|
||||||
exists = ports;
|
|
||||||
return new EGID(ports.Get().firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
if (!entitiesDB.Exists<BlockPortsStruct>(block))
|
||||||
|
return default;
|
||||||
|
var group = output
|
||||||
|
? NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group
|
||||||
|
: NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group;
|
||||||
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
||||||
|
if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper))
|
||||||
|
return default;
|
||||||
|
for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i)
|
||||||
{
|
{
|
||||||
exists = false;
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
exists = true;
|
|
||||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
public OptionalRef<WireEntityStruct> MatchPortToWire(PortEntityStruct port, EGID blockID, out EGID wireID)
|
||||||
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
|
{
|
||||||
|
var (wires, ids, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|
||||||
|
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
|
||||||
|
{
|
||||||
|
wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group);
|
||||||
|
return new OptionalRef<WireEntityStruct>(wires, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
|
wireID = default;
|
||||||
{
|
|
||||||
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;
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
exists = true;
|
|
||||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
|
||||||
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
|
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue)
|
||||||
{
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
if ((wiresB[i].destinationPortUsage == port.usage && wiresB[i].destinationBlockEGID == blockID)
|
|
||||||
|| (wiresB[i].sourcePortUsage == port.usage && wiresB[i].sourceBlockEGID == blockID))
|
|
||||||
{
|
|
||||||
exists = true;
|
|
||||||
return ref wiresB[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exists = false;
|
|
||||||
WireEntityStruct[] defRef = new WireEntityStruct[1];
|
|
||||||
return ref defRef[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, byte startPort = byte.MaxValue,
|
|
||||||
byte endPort = byte.MaxValue)
|
|
||||||
{
|
{
|
||||||
EGID[] startPorts;
|
EGID[] startPorts;
|
||||||
if (startPort == byte.MaxValue)
|
if (startPort == byte.MaxValue)
|
||||||
|
@ -278,7 +260,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
|
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;
|
EGID[] endPorts;
|
||||||
|
@ -290,59 +272,49 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
|
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++)
|
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
|
||||||
{
|
{
|
||||||
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
|
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
|
||||||
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
|
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
|
||||||
{
|
{
|
||||||
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
|
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
|
||||||
for (int w = 0; w < wires.count; w++)
|
foreach (var wireOpt in entitiesDB.QueryEntitiesOptional<WireEntityStruct>(
|
||||||
|
NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group))
|
||||||
{
|
{
|
||||||
if ((wiresB[w].destinationPortUsage == endPES.usage && wiresB[w].destinationBlockEGID == endBlock)
|
var wire = wireOpt.Get();
|
||||||
&& (wiresB[w].sourcePortUsage == startPES.usage && wiresB[w].sourceBlockEGID == startBlock))
|
if ((wire.destinationPortUsage == endPES.usage && wire.destinationBlockEGID == endBlock)
|
||||||
|
&& (wire.sourcePortUsage == startPES.usage && wire.sourceBlockEGID == startBlock))
|
||||||
{
|
{
|
||||||
exists = true;
|
return wireOpt.EGID;
|
||||||
return ref wiresB[w];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exists = false;
|
return default;
|
||||||
WireEntityStruct[] defRef = new WireEntityStruct[1];
|
|
||||||
return ref defRef[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists)
|
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
|
||||||
{
|
{
|
||||||
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
|
var port = GetPort(portID);
|
||||||
var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
|
var (channels, count) = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
||||||
var channelsB = channels.ToBuffer();
|
return port.firstChannelIndexCachedInSim < count
|
||||||
if (port.firstChannelIndexCachedInSim < channels.count)
|
? new OptionalRef<ChannelDataStruct>(channels, port.firstChannelIndexCachedInSim)
|
||||||
{
|
: default;
|
||||||
exists = true;
|
|
||||||
return ref channelsB.buffer[port.firstChannelIndexCachedInSim];
|
|
||||||
}
|
|
||||||
exists = false;
|
|
||||||
ChannelDataStruct[] defRef = new ChannelDataStruct[1];
|
|
||||||
return ref defRef[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EGID[] GetElectricBlocks()
|
public EGID[] GetElectricBlocks()
|
||||||
{
|
{
|
||||||
var res = new FasterList<EGID>();
|
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 < count; i++)
|
||||||
for (int i = 0; i < coll.count; i++)
|
|
||||||
{
|
{
|
||||||
ref BlockPortsStruct s = ref collB.buffer[i];
|
ref BlockPortsStruct s = ref coll[i];
|
||||||
res.Add(s.ID);
|
//res.Add(s.ID); - TODO: Would need to search for the groups for each block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,55 +323,30 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public EGID[] WiredToInput(EGID block, byte port)
|
public EGID[] WiredToInput(EGID block, byte port)
|
||||||
{
|
{
|
||||||
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
|
return entitiesDB
|
||||||
(WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block);
|
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
||||||
EGID[] result = new EGID[wireEntityStructs.Length];
|
.ToArray(wire => wire.ID,
|
||||||
for (uint i = 0; i < wireEntityStructs.Length; i++)
|
wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block);
|
||||||
{
|
|
||||||
result[i] = wireEntityStructs[i].ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EGID[] WiredToOutput(EGID block, byte port)
|
public EGID[] WiredToOutput(EGID block, byte port)
|
||||||
{
|
{
|
||||||
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
|
return entitiesDB
|
||||||
(WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block);
|
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
||||||
EGID[] result = new EGID[wireEntityStructs.Length];
|
.ToArray(wire => wire.ID,
|
||||||
for (uint i = 0; i < wireEntityStructs.Length; i++)
|
wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block);
|
||||||
{
|
|
||||||
result[i] = wireEntityStructs[i].ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private T[] Search<T>(ExclusiveGroup group, Func<T, bool> isMatch) where T : unmanaged, IEntityComponent
|
|
||||||
{
|
|
||||||
FasterList<T> results = new FasterList<T>();
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
|
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
|
||||||
{
|
{
|
||||||
ExclusiveGroup group = input
|
ExclusiveGroup group = input
|
||||||
? NamedExclusiveGroup<InputPortsGroup>.Group
|
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
||||||
: NamedExclusiveGroup<OutputPortsGroup>.Group;
|
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
||||||
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
||||||
{
|
{
|
||||||
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex;
|
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).firstChannelIndexCachedInSim;
|
||||||
var channelData =
|
var channelData =
|
||||||
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
|
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
||||||
return channelData;
|
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>
|
/// <returns>The connected wire.</returns>
|
||||||
/// <param name="portId">Port identifier.</param>
|
/// <param name="portId">Port identifier.</param>
|
||||||
/// <param name="connected">Whether the port has a wire connected to it.</param>
|
/// <param name="connected">Whether the port has a wire connected to it.</param>
|
||||||
protected ref WireEntityStruct GetConnectedWire(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>
|
/// <summary>
|
||||||
|
@ -56,10 +56,9 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The channel data.</returns>
|
/// <returns>The channel data.</returns>
|
||||||
/// <param name="portId">Port identifier.</param>
|
/// <param name="portId">Port identifier.</param>
|
||||||
/// <param name="exists">Whether the channel actually exists.</param>
|
protected OptionalRef<ChannelDataStruct> GetChannelData(EGID portId)
|
||||||
protected ref ChannelDataStruct GetChannelData(EGID portId, out bool exists)
|
|
||||||
{
|
{
|
||||||
return ref SignalEngine.GetChannelDataStruct(portId, out exists);
|
return SignalEngine.GetChannelDataStruct(portId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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 Svelto.ECS.Experimental;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Blocks.Engines;
|
using TechbloxModdingAPI.Blocks.Engines;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks
|
namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class Wire
|
public class Wire : EcsObjectBase
|
||||||
{
|
{
|
||||||
internal static SignalEngine signalEngine;
|
internal static SignalEngine signalEngine;
|
||||||
|
|
||||||
|
@ -30,8 +31,8 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
|
|
||||||
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
|
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
|
||||||
{
|
{
|
||||||
WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
|
var (wire, id) = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
|
||||||
return new Wire(wire, start, end);
|
return new Wire(wire, start, end, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -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>
|
/// <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)
|
public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
|
||||||
{
|
{
|
||||||
EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
|
var port = signalEngine.MatchBlockIOToPort(end, endPort, false);
|
||||||
if (!exists) return null;
|
if (!port) return null;
|
||||||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists);
|
var wire = signalEngine.MatchPortToWire(port, end.Id, out var egid);
|
||||||
if (exists)
|
return wire
|
||||||
{
|
? new Wire(wire.Get().sourceBlockEGID, end.Id, wire.Get().sourcePortUsage, endPort, egid, false)
|
||||||
return new Wire(Block.New(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort);
|
: null;
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// <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)
|
public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
|
||||||
{
|
{
|
||||||
EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
|
var port = signalEngine.MatchBlockIOToPort(start, startPort, true);
|
||||||
if (!exists) return null;
|
if (!port) return null;
|
||||||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists);
|
var wire = signalEngine.MatchPortToWire(port, start.Id, out var egid);
|
||||||
if (exists)
|
return wire
|
||||||
{
|
? new Wire(start.Id, wire.Get().destinationBlockEGID, startPort, wire.Get().destinationPortUsage, egid, false)
|
||||||
return new Wire(start, Block.New(wire.destinationBlockEGID), startPort, wire.destinationPortUsage);
|
: null;
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Construct a wire object from an existing connection.
|
/// Construct a wire object froam n existing connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="start">Starting block ID.</param>
|
/// <param name="start">Starting block ID.</param>
|
||||||
/// <param name="end">Ending block ID.</param>
|
/// <param name="end">Ending block ID.</param>
|
||||||
/// <param name="startPort">Starting port number, or guess if omitted.</param>
|
/// <param name="startPort">Starting port number, or guess if omitted.</param>
|
||||||
/// <param name="endPort">Ending 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>
|
/// <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;
|
var th = (Wire)ecs;
|
||||||
endBlockEGID = end.Id;
|
th.startBlockEGID = start.Id;
|
||||||
|
th.endBlockEGID = end.Id;
|
||||||
|
bool flipped = false;
|
||||||
// find block ports
|
// find block ports
|
||||||
WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort);
|
EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
|
||||||
if (exists)
|
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;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// flip I/O around and try again
|
// flip I/O around and try again
|
||||||
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort);
|
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort);
|
||||||
if (exists)
|
flipped = true;
|
||||||
{
|
|
||||||
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.
|
// 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
|
// This makes wire traversal easier, but makes logic in this class a bit more complex
|
||||||
endPort = wire.sourcePortUsage;
|
}
|
||||||
startPort = wire.destinationPortUsage;
|
|
||||||
|
if (wire != default)
|
||||||
|
{
|
||||||
|
th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new WireInvalidException("Wire not found");
|
throw new WireInvalidException("Wire not found");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return th.wireEGID;
|
||||||
|
})
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -131,25 +118,25 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// <param name="wire">The wire ID.</param>
|
/// <param name="wire">The wire ID.</param>
|
||||||
/// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</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)
|
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.inputToOutput = inputToOutput;
|
||||||
this.wireEGID = wire;
|
this.wireEGID = wire;
|
||||||
if (inputToOutput)
|
endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).EGID;
|
||||||
{
|
if (endPortEGID == default) throw new WireInvalidException("Wire end port not found");
|
||||||
endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
|
startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).EGID;
|
||||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
if (startPortEGID == default) throw new WireInvalidException("Wire start 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");
|
|
||||||
}
|
|
||||||
this.startPort = startPort;
|
this.startPort = startPort;
|
||||||
this.endPort = endPort;
|
this.endPort = endPort;
|
||||||
}
|
}
|
||||||
|
@ -158,41 +145,16 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// Construct a wire object from an existing wire connection.
|
/// Construct a wire object from an existing wire connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="wireEgid">The wire ID.</param>
|
/// <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);
|
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
|
||||||
this.startBlockEGID = wire.sourceBlockEGID;
|
Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
|
||||||
this.endBlockEGID = wire.destinationBlockEGID;
|
wireEgid, false);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
/// <summary>
|
||||||
|
@ -202,16 +164,12 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat;
|
||||||
if (!exists) return 0f;
|
|
||||||
return cds.valueAsFloat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value;
|
||||||
if (!exists) return;
|
|
||||||
cds.valueAsFloat = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,16 +180,12 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
|
||||||
if (!exists) return "";
|
|
||||||
return cds.valueAsEcsString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value);
|
||||||
if (!exists) return;
|
|
||||||
cds.valueAsEcsString.Set(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,16 +196,12 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
|
||||||
if (!exists) return default;
|
|
||||||
return cds.valueAsEcsString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value;
|
||||||
if (!exists) return;
|
|
||||||
cds.valueAsEcsString = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,16 +213,12 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID;
|
||||||
if (!exists) return uint.MaxValue;
|
|
||||||
return cds.valueAsID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value;
|
||||||
if (!exists) return;
|
|
||||||
cds.valueAsID = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +227,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SignalingBlock Start
|
public SignalingBlock Start
|
||||||
{
|
{
|
||||||
get => new SignalingBlock(startBlockEGID);
|
get => (SignalingBlock)Block.New(startBlockEGID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -292,12 +238,20 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
get => startPort;
|
get => startPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The display name of the start port.
|
||||||
|
/// </summary>
|
||||||
|
public string StartPortName
|
||||||
|
{
|
||||||
|
get => signalEngine.GetPort(startPortEGID).portNameLocalised;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The block at the end of the wire.
|
/// The block at the end of the wire.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SignalingBlock End
|
public SignalingBlock End
|
||||||
{
|
{
|
||||||
get => new SignalingBlock(endBlockEGID);
|
get => (SignalingBlock)Block.New(endBlockEGID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -308,6 +262,14 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
get => endPort;
|
get => endPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The display name of the end port.
|
||||||
|
/// </summary>
|
||||||
|
public string EndPortName
|
||||||
|
{
|
||||||
|
get => signalEngine.GetPort(endPortEGID).portNameLocalised;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// 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).
|
/// 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>
|
/// <returns>A copy of the wire object.</returns>
|
||||||
public Wire OutputToInputCopy()
|
public Wire OutputToInputCopy()
|
||||||
{
|
{
|
||||||
return new Wire(wireEGID);
|
return GetInstance(wireEGID, egid => new Wire(egid));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -329,15 +291,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
inputToOutput = false;
|
inputToOutput = false;
|
||||||
// swap inputs and outputs
|
// swap inputs and outputs
|
||||||
EGID temp = endBlockEGID;
|
(endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
|
||||||
endBlockEGID = startBlockEGID;
|
var tempPort = endPortEGID;
|
||||||
startBlockEGID = temp;
|
|
||||||
temp = endPortEGID;
|
|
||||||
endPortEGID = startPortEGID;
|
endPortEGID = startPortEGID;
|
||||||
startPortEGID = temp;
|
startPortEGID = tempPort;
|
||||||
byte tempPortNumber = endPort;
|
(endPort, startPort) = (startPort, endPort);
|
||||||
endPort = startPort;
|
|
||||||
startPort = tempPortNumber;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +303,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
if (signalEngine.Exists<WireEntityStruct>(wireEGID))
|
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})";
|
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 Unity.Mathematics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using Gamecraft.Damage;
|
using Svelto.ECS;
|
||||||
using RobocraftX.Common;
|
using Techblox.TimeRunning.Clusters;
|
||||||
using Svelto.ECS;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
|
@ -10,35 +9,37 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Cluster : EcsObjectBase
|
public class Cluster : EcsObjectBase
|
||||||
{
|
{
|
||||||
public override EGID Id { get; }
|
public Cluster(EGID id) : base(id)
|
||||||
|
|
||||||
public Cluster(EGID id)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
public float CurrentHealth
|
||||||
{
|
{
|
||||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth;
|
get => 0f;
|
||||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth = value;
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public float HealthMultiplier
|
public float HealthMultiplier
|
||||||
{
|
{
|
||||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier;
|
get => 0f;
|
||||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier = value;
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mass of the cluster.
|
||||||
|
/// </summary>
|
||||||
|
public float Mass => Block.BlockEngine.GetBlockInfo<ClusterMassComponent>(this).mass;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the simulation-time rigid bodies for the chunks in this cluster.
|
/// Returns the simulation-time rigid bodies for the chunks in this cluster.
|
||||||
/// </summary>
|
/// </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.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using uREPL;
|
|
||||||
using RobocraftX.CommandLine.Custom;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Commands
|
namespace TechbloxModdingAPI.Commands
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -17,9 +14,7 @@ namespace TechbloxModdingAPI.Commands
|
||||||
{
|
{
|
||||||
public static void Register(string name, Action action, string desc, bool noConsole = false)
|
public static void Register(string name, Action action, string desc, bool noConsole = false)
|
||||||
{
|
{
|
||||||
RuntimeCommands.Register(name, action, desc);
|
CustomCommands.Register(name, action, desc);
|
||||||
if (noConsole) { return; }
|
|
||||||
ConsoleCommands.Register(name, action, desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Register(string name, Action<object> action, string desc, bool noConsole = false)
|
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)
|
public static void Register<Param0>(string name, Action<Param0> action, string desc, bool noConsole = false)
|
||||||
{
|
{
|
||||||
RuntimeCommands.Register<Param0>(name, action, desc);
|
CustomCommands.Register(name, action, desc);
|
||||||
if (noConsole) { return; }
|
|
||||||
ConsoleCommands.Register<Param0>(name, action, desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Register<Param0, Param1>(string name, Action<Param0, Param1> action, string desc, bool noConsole = false)
|
public static void Register<Param0, Param1>(string name, Action<Param0, Param1> action, string desc, bool noConsole = false)
|
||||||
{
|
{
|
||||||
RuntimeCommands.Register<Param0, Param1>(name, action, desc);
|
CustomCommands.Register(name, action, desc);
|
||||||
if (noConsole) { return; }
|
|
||||||
ConsoleCommands.Register<Param0, Param1>(name, action, desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Register<Param0, Param1, Param2>(string name, Action<Param0, Param1, Param2> action, string desc, bool noConsole = false)
|
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);
|
CustomCommands.Register(name, action, desc);
|
||||||
if (noConsole) { return; }
|
|
||||||
ConsoleCommands.Register<Param0, Param1, Param2>(name, action, desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Unregister(string name, bool noConsole = false)
|
public static void Unregister(string name, bool noConsole = false)
|
||||||
{
|
{
|
||||||
RuntimeCommands.Unregister(name);
|
CustomCommands.Unregister(name);
|
||||||
if (noConsole) { return; }
|
|
||||||
ConsoleCommands.Unregister(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Call(string name)
|
public static void Call(string name)
|
||||||
{
|
{
|
||||||
RuntimeCommands.Call(name);
|
CustomCommands.Call(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Call<Param0>(string name, Param0 param0)
|
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)
|
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)
|
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 System.Linq;
|
||||||
|
|
||||||
using uREPL;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Commands
|
namespace TechbloxModdingAPI.Commands
|
||||||
{
|
{
|
||||||
public static class ExistingCommands
|
public static class ExistingCommands
|
||||||
{
|
{
|
||||||
public static void Call(string commandName)
|
public static void Call(string commandName)
|
||||||
{
|
{
|
||||||
RuntimeCommands.Call(commandName);
|
CustomCommands.Call(commandName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Call<Arg0>(string commandName, Arg0 arg0)
|
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)
|
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)
|
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)
|
public static bool Exists(string commandName)
|
||||||
{
|
{
|
||||||
return RuntimeCommands.HasRegistered(commandName);
|
return CustomCommands.Exists(commandName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (string Name, string Description)[] GetCommandNamesAndDescriptions()
|
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,15 +1,78 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Internal;
|
using Svelto.ECS.Internal;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
public abstract class EcsObjectBase
|
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;
|
protected internal EcsInitData InitData;
|
||||||
|
|
||||||
|
@ -59,5 +122,7 @@ namespace TechbloxModdingAPI
|
||||||
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam });
|
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam });
|
||||||
return lambda.Compile();
|
return lambda.Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,25 +5,68 @@ using RobocraftX.CR.MainGame;
|
||||||
using RobocraftX.FrontEnd;
|
using RobocraftX.FrontEnd;
|
||||||
using RobocraftX.StateSync;
|
using RobocraftX.StateSync;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using Svelto.ECS.Schedulers;
|
||||||
|
using TechbloxModdingAPI.Commands;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Engines
|
namespace TechbloxModdingAPI.Engines
|
||||||
{
|
{
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
class GameLoadedEnginePatch
|
static class GameLoadedTimeStoppedEnginePatch
|
||||||
{
|
{
|
||||||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
||||||
{
|
{
|
||||||
// register all game engines, including deterministic
|
// register all game engines, including deterministic
|
||||||
GameEngineManager.RegisterEngines(stateSyncReg);
|
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()
|
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]
|
[HarmonyPatch]
|
||||||
class MenuLoadedEnginePatch
|
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>
|
/// <summary>
|
||||||
/// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines.
|
/// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines.
|
||||||
/// </summary>
|
/// </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 TechbloxModdingAPI.App;
|
||||||
using RobocraftX.Common.Input;
|
|
||||||
using Svelto.ECS;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Input
|
namespace TechbloxModdingAPI.Input
|
||||||
{
|
{
|
||||||
public static class FakeInput
|
public static class FakeInput
|
||||||
{
|
{
|
||||||
private static readonly FakeInputEngine inputEngine = new FakeInputEngine();
|
internal static readonly FakeInputEngine inputEngine = new FakeInputEngine();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Customize the local input.
|
/// Customize the local input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input">The custom input.</param>
|
/// <param name="input">The custom input.</param>
|
||||||
public static void CustomInput(LocalInputEntityStruct input)
|
public static void CustomInput(LocalCosmeticInputEntityComponent input)
|
||||||
{
|
{
|
||||||
inputEngine.SendCustomInput(input);
|
inputEngine.SendCustomInput(input);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +32,7 @@ namespace TechbloxModdingAPI.Input
|
||||||
inputEngine.SendCustomPlayerInput(input, playerID);
|
inputEngine.SendCustomPlayerInput(input, playerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalInputEntityStruct GetInput()
|
public static LocalCosmeticInputEntityComponent GetInput()
|
||||||
{
|
{
|
||||||
return inputEngine.GetInput();
|
return inputEngine.GetInput();
|
||||||
}
|
}
|
||||||
|
@ -53,26 +51,18 @@ namespace TechbloxModdingAPI.Input
|
||||||
/// Omit any parameter you do not want to affect.
|
/// Omit any parameter you do not want to affect.
|
||||||
/// Parameters that end with "?" don't do anything... yet.
|
/// Parameters that end with "?" don't do anything... yet.
|
||||||
/// </summary>
|
/// </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="hotbar">Select the hotbar slot by number.</param>
|
||||||
/// <param name="commandLine">Toggle the command line?</param>
|
/// <param name="commandLine">Toggle the command line?</param>
|
||||||
/// <param name="escape">Open escape menu?</param>
|
/// <param name="escape">Open escape menu?</param>
|
||||||
/// <param name="enter">Page return?</param>
|
/// <param name="enter">Page return?</param>
|
||||||
/// <param name="debug">Toggle debug display?</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="colour">Toggle to hotbar colour mode?</param>
|
||||||
/// <param name="hotbarPage">Select the hotbar page by number?</param>
|
/// <param name="hotbarPage">Select the hotbar page by number?</param>
|
||||||
/// <param name="quickSave">Quicksave?</param>
|
/// <param name="quickSave">Quicksave?</param>
|
||||||
/// <param name="paste">Paste?</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)
|
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)
|
ref LocalCosmeticInputEntityComponent currentInput = ref inputEngine.GetInputRef();
|
||||||
{
|
|
||||||
playerID = inputEngine.GetLocalPlayerID();
|
|
||||||
}
|
|
||||||
ref LocalInputEntityStruct currentInput = ref inputEngine.GetInputRef();
|
|
||||||
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
|
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
|
||||||
// set inputs
|
// set inputs
|
||||||
switch(hotbar)
|
switch(hotbar)
|
||||||
|
@ -92,9 +82,6 @@ namespace TechbloxModdingAPI.Input
|
||||||
if (escape) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Escape;
|
if (escape) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Escape;
|
||||||
if (enter) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Return;
|
if (enter) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Return;
|
||||||
if (debug) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleDebugDisplay;
|
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)
|
switch (hotbarPage)
|
||||||
{
|
{
|
||||||
case 1: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage1; break;
|
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 8: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage8; break;
|
||||||
case 9: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage9; break;
|
case 9: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage9; break;
|
||||||
case 10: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage10; break;
|
case 10: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage10; break;
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
//RewiredConsts.Action
|
//RewiredConsts.Action
|
||||||
if (quickSave) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.QuickSave;
|
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)
|
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)
|
||||||
{
|
{ // TODO: We can only alter our own inputs clientside
|
||||||
if (playerID == uint.MaxValue)
|
ref var currentInput = ref inputEngine._localInputCache;
|
||||||
{
|
|
||||||
playerID = inputEngine.GetLocalPlayerID();
|
|
||||||
}
|
|
||||||
ref LocalPlayerInputEntityStruct currentInput = ref inputEngine.GetPlayerInputRef(playerID);
|
|
||||||
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
|
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
|
||||||
// set inputs
|
// set inputs
|
||||||
if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningMode;
|
if (toggleMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningModePlay; //TODO: Test, play
|
||||||
if (forward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Forward;
|
if (forward) currentInput |= RobocraftX.Common.Input.ActionInput.Forward;
|
||||||
if (backward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Backward;
|
if (backward) currentInput |= RobocraftX.Common.Input.ActionInput.Backward;
|
||||||
if (up) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Up;
|
if (up) currentInput |= RobocraftX.Common.Input.ActionInput.Up;
|
||||||
if (down) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Down;
|
if (down) currentInput |= RobocraftX.Common.Input.ActionInput.Down;
|
||||||
if (left) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Left;
|
if (left) currentInput |= RobocraftX.Common.Input.ActionInput.Left;
|
||||||
if (right) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Right;
|
if (right) currentInput |= RobocraftX.Common.Input.ActionInput.Right;
|
||||||
if (sprint) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Sprint;
|
if (sprint) currentInput |= RobocraftX.Common.Input.ActionInput.Sprint;
|
||||||
//if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode;
|
//if (toggleFly) currentInput |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode;
|
||||||
//if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction;
|
//if (alt) currentInput |= RobocraftX.Common.Input.ActionInput.AltAction;
|
||||||
if (primary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryAction;
|
if (primary) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionClick;
|
||||||
if (secondary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryAction;
|
if (secondary) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionClick;
|
||||||
if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction;
|
if (tertiary) currentInput |= RobocraftX.Common.Input.ActionInput.TertiaryAction;
|
||||||
if (primaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld;
|
if (primaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld;
|
||||||
if (secondaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld;
|
if (secondaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld;
|
||||||
//if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid;
|
//if (toggleUnitGrid) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid;
|
||||||
if (ctrl) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CtrlAction;
|
if (ctrl) currentInput |= RobocraftX.Common.Input.ActionInput.CtrlAction;
|
||||||
if (toggleColourMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleColourMode;
|
if (toggleColourMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleColourMode;
|
||||||
if (scaleBlockUp) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp;
|
if (scaleBlockUp) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp;
|
||||||
if (scaleBlockDown) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown;
|
if (scaleBlockDown) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown;
|
||||||
if (rotateBlockClockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise;
|
if (rotateBlockClockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise;
|
||||||
if (rotateBlockCounterclockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise;
|
if (rotateBlockCounterclockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise;
|
||||||
if (cutSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CutSelection;
|
if (cutSelection) currentInput |= RobocraftX.Common.Input.ActionInput.CutSelection;
|
||||||
if (copySelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CopySelection;
|
if (copySelection) currentInput |= RobocraftX.Common.Input.ActionInput.CopySelection;
|
||||||
if (deleteSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.DeleteSelection;
|
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()
|
public static void Init()
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace TechbloxModdingAPI.Input
|
||||||
|
|
||||||
public bool IsReady = false;
|
public bool IsReady = false;
|
||||||
|
|
||||||
|
internal ActionInput _localInputCache;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
IsReady = false;
|
IsReady = false;
|
||||||
|
@ -30,12 +32,12 @@ namespace TechbloxModdingAPI.Input
|
||||||
IsReady = true;
|
IsReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SendCustomInput(LocalInputEntityStruct input)
|
public bool SendCustomInput(LocalCosmeticInputEntityComponent input)
|
||||||
{
|
{
|
||||||
EGID egid = CommonExclusiveGroups.GameStateEGID;
|
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;
|
ies = input;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -54,14 +56,14 @@ namespace TechbloxModdingAPI.Input
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalInputEntityStruct GetInput()
|
public LocalCosmeticInputEntityComponent GetInput()
|
||||||
{
|
{
|
||||||
EGID egid = CommonExclusiveGroups.GameStateEGID;
|
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)
|
public LocalPlayerInputEntityStruct GetPlayerInput(uint playerID, bool remote = false)
|
||||||
|
@ -74,10 +76,10 @@ namespace TechbloxModdingAPI.Input
|
||||||
else return default;
|
else return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref LocalInputEntityStruct GetInputRef()
|
public ref LocalCosmeticInputEntityComponent GetInputRef()
|
||||||
{
|
{
|
||||||
EGID egid = CommonExclusiveGroups.GameStateEGID;
|
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)
|
public ref LocalPlayerInputEntityStruct GetPlayerInputRef(uint playerID, bool remote = false)
|
||||||
|
@ -86,6 +88,14 @@ namespace TechbloxModdingAPI.Input
|
||||||
return ref entitiesDB.QueryEntity<LocalPlayerInputEntityStruct>(egid);
|
return ref entitiesDB.QueryEntity<LocalPlayerInputEntityStruct>(egid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void HandleCustomInput()
|
||||||
|
{
|
||||||
|
if (!LocalPlayerIDUtility.DoesLocalPlayerExist(entitiesDB))
|
||||||
|
return;
|
||||||
|
GetPlayerInputRef(GetLocalPlayerID()).actionMask |= _localInputCache;
|
||||||
|
_localInputCache = default;
|
||||||
|
}
|
||||||
|
|
||||||
public uint GetLocalPlayerID()
|
public uint GetLocalPlayerID()
|
||||||
{
|
{
|
||||||
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB);
|
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>
|
/// </summary>
|
||||||
public event EventHandler<bool> OnClick;
|
public event EventHandler<bool> OnClick;
|
||||||
|
|
||||||
public void OnGUI()
|
public override void OnGUI()
|
||||||
{
|
{
|
||||||
if (automaticLayout)
|
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>
|
/// <summary>
|
||||||
/// Initialize a new button with automatic layout.
|
/// Initialize a new button with automatic layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">The text to display on the button.</param>
|
/// <param name="text">The text to display on the button.</param>
|
||||||
/// <param name="name">The button's name.</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;
|
automaticLayout = true;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
if (name == null)
|
|
||||||
{
|
|
||||||
this.Name = typeof(Button).FullName + "::" + text;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.Name = name;
|
|
||||||
}
|
|
||||||
IMGUIManager.AddElement(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -83,10 +64,5 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
automaticLayout = false;
|
automaticLayout = false;
|
||||||
this.Box = box;
|
this.Box = box;
|
||||||
}
|
}
|
||||||
|
|
||||||
~Button()
|
|
||||||
{
|
|
||||||
IMGUIManager.RemoveElement(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,12 +20,12 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Rect Box { get; set; }
|
public Rect Box { get; set; }
|
||||||
|
|
||||||
public void OnGUI()
|
public override void OnGUI()
|
||||||
{
|
{
|
||||||
/*if (Constants.Default == null) return;
|
/*if (Constants.Default == null) return;
|
||||||
if (Constants.Default.box == null) return;*/
|
if (Constants.Default.box == null) return;*/
|
||||||
GUIStyle guiStyle = Constants.Default.box;
|
GUIStyle guiStyle = Constants.Default.box;
|
||||||
UIElement[] elems = elements.ToArrayFast(out uint count);
|
UIElement[] elems = elements.ToArrayFast(out int count);
|
||||||
if (automaticLayout)
|
if (automaticLayout)
|
||||||
{
|
{
|
||||||
GUILayout.BeginArea(Box, guiStyle);
|
GUILayout.BeginArea(Box, guiStyle);
|
||||||
|
@ -61,16 +61,6 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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>
|
/// <summary>
|
||||||
/// The amount of elements in the group.
|
/// The amount of elements in the group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -85,19 +75,10 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// <param name="box">The rectangular area to use in the window.</param>
|
/// <param name="box">The rectangular area to use in the window.</param>
|
||||||
/// <param name="name">Name of the group.</param>
|
/// <param name="name">Name of the group.</param>
|
||||||
/// <param name="automaticLayout">Whether to use automatic UI layout.</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;
|
Box = box;
|
||||||
if (name == null)
|
|
||||||
{
|
|
||||||
this.Name = typeof(Group).FullName + "::" + box.ToString().Replace(" ", "");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.Name = name;
|
|
||||||
}
|
|
||||||
this.automaticLayout = automaticLayout;
|
this.automaticLayout = automaticLayout;
|
||||||
IMGUIManager.AddElement(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -132,7 +113,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= elements.count) return false;
|
if (index < 0 || index >= elements.count) return false;
|
||||||
IMGUIManager.AddElement(elements[index]); // re-add to global manager
|
IMGUIManager.AddElement(elements[index]); // re-add to global manager
|
||||||
elements.RemoveAt(index);
|
elements.RemoveAt((uint) index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +124,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// <returns>The element's index, or -1 if not found.</returns>
|
/// <returns>The element's index, or -1 if not found.</returns>
|
||||||
public int IndexOf(UIElement element)
|
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++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
if (elems[i].Name == element.Name)
|
if (elems[i].Name == element.Name)
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using TechbloxModdingAPI.App;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
using Rewired.Internal;
|
|
||||||
using Svelto.DataStructures;
|
|
||||||
using Svelto.Tasks;
|
using Svelto.Tasks;
|
||||||
using Svelto.Tasks.ExtraLean;
|
using Svelto.Tasks.ExtraLean;
|
||||||
using Svelto.Tasks.ExtraLean.Unity;
|
using TechbloxModdingAPI.Tasks;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Interface.IMGUI
|
namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
|
@ -20,9 +15,9 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class IMGUIManager
|
public static class IMGUIManager
|
||||||
{
|
{
|
||||||
internal static OnGuiRunner ImguiScheduler = new OnGuiRunner("TechbloxModdingAPI_IMGUIScheduler");
|
internal static OnGuiRunner ImguiScheduler = new("TechbloxModdingAPI_IMGUIScheduler");
|
||||||
|
|
||||||
private static Dictionary<string, UIElement> _activeElements = new Dictionary<string,UIElement>();
|
private static readonly WeakDictionary<string, UIElement> _activeElements = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add an UIElement instance to be managed by IMGUIManager.
|
/// Add an UIElement instance to be managed by IMGUIManager.
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Rect Box { get; set; } = Rect.zero;
|
public Rect Box { get; set; } = Rect.zero;
|
||||||
|
|
||||||
public void OnGUI()
|
public override void OnGUI()
|
||||||
{
|
{
|
||||||
//if (Texture == null) return;
|
//if (Texture == null) return;
|
||||||
if (automaticLayout)
|
if (automaticLayout)
|
||||||
|
@ -33,42 +33,15 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Interface.IMGUI.Image"/> class with automatic layout.
|
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Interface.IMGUI.Image"/> class with automatic layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="texture">Image to display.</param>
|
/// <param name="texture">Image to display.</param>
|
||||||
/// <param name="name">The element's name.</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;
|
automaticLayout = true;
|
||||||
Texture = texture;
|
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>
|
/// <summary>
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Rect Box { get; set; } = Rect.zero;
|
public Rect Box { get; set; } = Rect.zero;
|
||||||
|
|
||||||
public void OnGUI()
|
public override void OnGUI()
|
||||||
{
|
{
|
||||||
if (automaticLayout)
|
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>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Interface.IMGUI.Label"/> class with automatic layout.
|
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Interface.IMGUI.Label"/> class with automatic layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialText">Initial string to display on the label.</param>
|
/// <param name="initialText">Initial string to display on the label.</param>
|
||||||
/// <param name="name">The element's name.</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;
|
automaticLayout = true;
|
||||||
Text = initialText;
|
Text = initialText;
|
||||||
if (name == null)
|
|
||||||
{
|
|
||||||
this.Name = typeof(Label).FullName + "::" + initialText;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.Name = name;
|
|
||||||
}
|
|
||||||
IMGUIManager.AddElement(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<string> OnEdit;
|
public event EventHandler<string> OnEdit;
|
||||||
|
|
||||||
public void OnGUI()
|
public override void OnGUI()
|
||||||
{
|
{
|
||||||
string editedText = null;
|
string editedText = null;
|
||||||
if (automaticLayout)
|
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>
|
/// <summary>
|
||||||
/// Initialize the text input field with automatic layout.
|
/// Initialize the text input field with automatic layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialText">Initial text in the input field.</param>
|
/// <param name="initialText">Initial text in the input field.</param>
|
||||||
/// <param name="name">The text field's name.</param>
|
/// <param name="name">The text field's name.</param>
|
||||||
/// <param name="multiline">Allow multiple lines?</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;
|
this.Multiline = multiline;
|
||||||
automaticLayout = true;
|
automaticLayout = true;
|
||||||
text = initialText ?? "";
|
text = initialText ?? "";
|
||||||
if (name == null)
|
|
||||||
{
|
|
||||||
this.Name = typeof(Text).FullName + "::" + text;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.Name = name;
|
|
||||||
}
|
|
||||||
IMGUIManager.AddElement(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -6,23 +6,34 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
/// GUI Element like a text field, button or picture.
|
/// GUI Element like a text field, button or picture.
|
||||||
/// This interface is used to wrap many elements from Unity's IMGUI system.
|
/// This interface is used to wrap many elements from Unity's IMGUI system.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// GUI operations to perform in the OnGUI scope.
|
/// GUI operations to perform in the OnGUI scope.
|
||||||
/// This is basically equivalent to a MonoBehaviour's OnGUI method.
|
/// This is basically equivalent to a MonoBehaviour's OnGUI method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnGUI();
|
public abstract void OnGUI();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The element's name.
|
/// The element's name.
|
||||||
/// This should be unique for every instance of the class.
|
/// This should be unique for every instance of the class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to display the UI element or not.
|
/// Whether to display the UI element or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool Enabled { get; }
|
public bool Enabled { get; set; } = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,13 +3,11 @@ using System.Reflection;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
|
||||||
using RobocraftX;
|
using RobocraftX;
|
||||||
using RobocraftX.Schedulers;
|
|
||||||
using RobocraftX.Services;
|
using RobocraftX.Services;
|
||||||
using Svelto.Context;
|
using Svelto.Context;
|
||||||
using Svelto.Tasks.ExtraLean;
|
|
||||||
using TechbloxModdingAPI.App;
|
using TechbloxModdingAPI.App;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Events;
|
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
@ -51,15 +49,13 @@ namespace TechbloxModdingAPI
|
||||||
harmony.PatchAll(currentAssembly);
|
harmony.PatchAll(currentAssembly);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{ //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet
|
{
|
||||||
Logging.Log(e.ToString());
|
HandleError(e, "Failed to patch Techblox. Attempting to patch to display error...", OnPatchError);
|
||||||
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 :(
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// init utility
|
// init utility
|
||||||
Logging.MetaDebugLog($"Initializing Utility");
|
Logging.MetaDebugLog($"Initializing Utility");
|
||||||
Utility.GameState.Init();
|
Utility.GameState.Init();
|
||||||
|
@ -72,14 +68,24 @@ namespace TechbloxModdingAPI
|
||||||
Block.Init();
|
Block.Init();
|
||||||
BlockGroup.Init();
|
BlockGroup.Init();
|
||||||
Wire.Init();
|
Wire.Init();
|
||||||
|
// init client
|
||||||
Logging.MetaDebugLog($"Initializing Client");
|
Logging.MetaDebugLog($"Initializing Client");
|
||||||
Client.Init();
|
Client.Init();
|
||||||
Game.Init();
|
Game.Init();
|
||||||
// init UI
|
// init UI
|
||||||
|
Logging.MetaDebugLog($"Initializing UI");
|
||||||
Interface.IMGUI.Constants.Init();
|
Interface.IMGUI.Constants.Init();
|
||||||
Interface.IMGUI.IMGUIManager.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");
|
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>
|
/// <summary>
|
||||||
/// Shuts down & cleans up the TechbloxModdingAPI.
|
/// Shuts down & cleans up the TechbloxModdingAPI.
|
||||||
|
@ -98,7 +104,7 @@ namespace TechbloxModdingAPI
|
||||||
}
|
}
|
||||||
Scheduler.Dispose();
|
Scheduler.Dispose();
|
||||||
var currentAssembly = Assembly.GetExecutingAssembly();
|
var currentAssembly = Assembly.GetExecutingAssembly();
|
||||||
harmony.UnpatchAll(currentAssembly.GetName().Name);
|
harmony.UnpatchSelf();
|
||||||
harmony = null;
|
harmony = null;
|
||||||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
|
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
|
||||||
}
|
}
|
||||||
|
@ -109,5 +115,26 @@ namespace TechbloxModdingAPI
|
||||||
ErrorBuilder.DisplayMustQuitError("Failed to patch Techblox!\n" +
|
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.");
|
"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
|
namespace TechbloxModdingAPI.Persistence
|
||||||
{
|
{
|
||||||
[HarmonyPatch]
|
//[HarmonyPatch] - TODO
|
||||||
class DeserializeFromDiskEntitiesEnginePatch
|
class DeserializeFromDiskEntitiesEnginePatch
|
||||||
{
|
{
|
||||||
internal static EntitiesDB entitiesDB = null;
|
internal static EntitiesDB entitiesDB = null;
|
||||||
|
@ -26,7 +26,7 @@ namespace TechbloxModdingAPI.Persistence
|
||||||
SerializerManager.RegisterSerializers(SaveAndLoadCompositionRootPatch.currentEnginesRoot);
|
SerializerManager.RegisterSerializers(SaveAndLoadCompositionRootPatch.currentEnginesRoot);
|
||||||
uint originalPos = ____serializationData.dataPos;
|
uint originalPos = ____serializationData.dataPos;
|
||||||
Logging.MetaDebugLog($"dataPos: {originalPos}");
|
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];
|
byte[] frameBuffer = new byte[frameStart.Length];
|
||||||
Logging.MetaDebugLog($"serial data count: {____serializationData.data.count} capacity: {____serializationData.data.capacity}");
|
Logging.MetaDebugLog($"serial data count: {____serializationData.data.count} capacity: {____serializationData.data.capacity}");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
|
@ -4,17 +4,18 @@ using RobocraftX.SaveAndLoad;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
using RobocraftX.StateSync;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Persistence
|
namespace TechbloxModdingAPI.Persistence
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(SaveAndLoadCompositionRoot), "Compose")]
|
[HarmonyPatch(typeof(SaveAndLoadCompositionRoot), "ClientComposeTimeStopped")]
|
||||||
class SaveAndLoadCompositionRootPatch
|
class SaveAndLoadCompositionRootPatch
|
||||||
{
|
{
|
||||||
public static EnginesRoot currentEnginesRoot;
|
public static EnginesRoot currentEnginesRoot;
|
||||||
|
|
||||||
public static void Prefix(EnginesRoot enginesRoot)
|
public static void Prefix(StateSyncRegistrationHelper stateSyncHelper)
|
||||||
{
|
{
|
||||||
currentEnginesRoot = enginesRoot;
|
currentEnginesRoot = stateSyncHelper.enginesRoot;
|
||||||
//SerializerManager.RegisterSerializers(enginesRoot);
|
//SerializerManager.RegisterSerializers(enginesRoot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ namespace TechbloxModdingAPI.Persistence
|
||||||
Logging.MetaDebugLog("Skipping component serialization: no serializers registered!");
|
Logging.MetaDebugLog("Skipping component serialization: no serializers registered!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
serializationData.data.ExpandBy((uint)frameStart.Length);
|
serializationData.data.IncreaseCapacityBy((uint)frameStart.Length);
|
||||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint buffLen), serializationData.dataPos);
|
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int buffLen), serializationData.dataPos);
|
||||||
uint originalPos = serializationData.dataPos;
|
uint originalPos = serializationData.dataPos;
|
||||||
Logging.MetaDebugLog($"dataPos: {originalPos}");
|
Logging.MetaDebugLog($"dataPos: {originalPos}");
|
||||||
// Add frame start so it's easier to find TechbloxModdingAPI-serialized components
|
// Add frame start so it's easier to find TechbloxModdingAPI-serialized components
|
||||||
|
@ -35,14 +35,14 @@ namespace TechbloxModdingAPI.Persistence
|
||||||
bbw.Write(frameStart[i]);
|
bbw.Write(frameStart[i]);
|
||||||
}
|
}
|
||||||
Logging.MetaDebugLog($"dataPos (after frame start): {bbw.Position}");
|
Logging.MetaDebugLog($"dataPos (after frame start): {bbw.Position}");
|
||||||
serializationData.data.ExpandBy(4u);
|
serializationData.data.IncreaseCapacityBy(4u);
|
||||||
bbw.Write((uint)SerializerManager.GetSerializersCount());
|
bbw.Write((uint)SerializerManager.GetSerializersCount());
|
||||||
string[] serializerKeys = SerializerManager.GetSerializerNames();
|
string[] serializerKeys = SerializerManager.GetSerializerNames();
|
||||||
for (uint c = 0; c < serializerKeys.Length; c++)
|
for (uint c = 0; c < serializerKeys.Length; c++)
|
||||||
{
|
{
|
||||||
Logging.MetaDebugLog($"dataPos (loop start): {bbw.Position}");
|
Logging.MetaDebugLog($"dataPos (loop start): {bbw.Position}");
|
||||||
// write component info
|
// 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);
|
bbw.Write((uint)serializerKeys[c].Length);
|
||||||
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
||||||
byte[] nameBytes = Encoding.UTF8.GetBytes(serializerKeys[c]);
|
byte[] nameBytes = Encoding.UTF8.GetBytes(serializerKeys[c]);
|
||||||
|
@ -51,7 +51,7 @@ namespace TechbloxModdingAPI.Persistence
|
||||||
bbw.Write(nameBytes[i]);
|
bbw.Write(nameBytes[i]);
|
||||||
}
|
}
|
||||||
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
||||||
serializationData.data.ExpandBy(4u);
|
serializationData.data.IncreaseCapacityBy(4u);
|
||||||
serializationData.dataPos = bbw.Position + 4u;
|
serializationData.dataPos = bbw.Position + 4u;
|
||||||
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
|
||||||
Logging.MetaDebugLog($"dataPos (appears to be): {serializationData.dataPos}");
|
Logging.MetaDebugLog($"dataPos (appears to be): {serializationData.dataPos}");
|
||||||
|
@ -74,7 +74,7 @@ namespace TechbloxModdingAPI.Persistence
|
||||||
|
|
||||||
public static MethodBase TargetMethod()
|
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)
|
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();
|
uint entityCount = bbr.ReadUint();
|
||||||
serializationData.dataPos = bbr.Position;
|
serializationData.dataPos = bbr.Position;
|
||||||
for (uint i = 0; i < entityCount; i++)
|
for (uint i = 0; i < entityCount; i++)
|
||||||
|
@ -46,8 +46,8 @@ namespace TechbloxModdingAPI.Persistence
|
||||||
|
|
||||||
public bool Serialize(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer)
|
public bool Serialize(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer)
|
||||||
{
|
{
|
||||||
serializationData.data.ExpandBy(4u);
|
serializationData.data.IncreaseCapacityBy(4u);
|
||||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos);
|
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int count), serializationData.dataPos);
|
||||||
EGID[] toSerialize = getEntitiesToSerialize(entitiesDB);
|
EGID[] toSerialize = getEntitiesToSerialize(entitiesDB);
|
||||||
bbw.Write((uint)toSerialize.Length);
|
bbw.Write((uint)toSerialize.Length);
|
||||||
serializationData.dataPos = bbw.Position;
|
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 System;
|
||||||
|
using Gamecraft.Wires;
|
||||||
using RobocraftX.Character;
|
using RobocraftX.Character;
|
||||||
using RobocraftX.Character.Movement;
|
using RobocraftX.Character.Movement;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.Common.Players;
|
using RobocraftX.Common.Players;
|
||||||
|
using RobocraftX.GUI.Wires;
|
||||||
using RobocraftX.Physics;
|
using RobocraftX.Physics;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using Techblox.BuildingDrone;
|
||||||
using Techblox.Camera;
|
using Techblox.Camera;
|
||||||
using Techblox.FlyCam;
|
using Techblox.Character;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Players;
|
using TechbloxModdingAPI.Players;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
@ -18,10 +21,11 @@ namespace TechbloxModdingAPI
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An in-game player character. Any Leo you see is a player.
|
/// An in-game player character. Any Leo you see is a player.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Player : IEquatable<Player>, IEquatable<EGID>
|
public partial class Player : EcsObjectBase, IEquatable<Player>, IEquatable<EGID>
|
||||||
{
|
{
|
||||||
// static functionality
|
// 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;
|
private static Player localPlayer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -61,23 +65,32 @@ namespace TechbloxModdingAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public static Player LocalPlayer
|
public static Player LocalPlayer
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer())
|
var playerId = playerEngine.GetLocalPlayer();
|
||||||
localPlayer = new Player(PlayerType.Local);
|
if (playerId == uint.MaxValue) return null;
|
||||||
|
if (localPlayer == null || localPlayer.Id != playerId)
|
||||||
|
localPlayer = GetInstance(playerId);
|
||||||
return localPlayer;
|
return localPlayer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static Player GetInstance(uint id)
|
||||||
|
{
|
||||||
|
return EcsObjectBase.GetInstance(new EGID(id, CharacterExclusiveGroups.OnFootGroup),
|
||||||
|
e => new Player(e.entityID));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The player's unique identifier.</param>
|
/// <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;
|
this.Id = id;
|
||||||
if (!Exists(id))
|
if (!Exists(id))
|
||||||
|
@ -91,22 +104,32 @@ namespace TechbloxModdingAPI
|
||||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
|
/// <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 =>
|
||||||
{
|
{
|
||||||
|
uint id;
|
||||||
switch (player)
|
switch (player)
|
||||||
{
|
{
|
||||||
case PlayerType.Local:
|
case PlayerType.Local:
|
||||||
this.Id = playerEngine.GetLocalPlayer();
|
id = playerEngine.GetLocalPlayer();
|
||||||
break;
|
break;
|
||||||
case PlayerType.Remote:
|
case PlayerType.Remote:
|
||||||
this.Id = playerEngine.GetRemotePlayer();
|
id = playerEngine.GetRemotePlayer();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
id = uint.MaxValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (this.Id == uint.MaxValue)
|
|
||||||
|
if (id == uint.MaxValue)
|
||||||
{
|
{
|
||||||
throw new PlayerNotFoundException($"No player of {player} type exists");
|
throw new PlayerNotFoundException($"No player of {player} type exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new EGID(id, CharacterExclusiveGroups.OnFootGroup);
|
||||||
|
})
|
||||||
|
{
|
||||||
this.Type = player;
|
this.Type = player;
|
||||||
|
Id = base.Id.entityID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// object fields & properties
|
// object fields & properties
|
||||||
|
@ -122,7 +145,7 @@ namespace TechbloxModdingAPI
|
||||||
/// The player's unique identifier.
|
/// The player's unique identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The identifier.</value>
|
/// <value>The identifier.</value>
|
||||||
public uint Id { get; }
|
public new uint Id { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The player's current position.
|
/// The player's current position.
|
||||||
|
@ -141,10 +164,10 @@ namespace TechbloxModdingAPI
|
||||||
public float3 Rotation
|
public float3 Rotation
|
||||||
{
|
{
|
||||||
get => ((Quaternion) (GameState.IsBuildMode()
|
get => ((Quaternion) (GameState.IsBuildMode()
|
||||||
? playerEngine.GetCameraStruct<CharacterCameraEntityStruct>(Id).Get().rotation
|
? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation
|
||||||
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation)).eulerAngles;
|
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation)).eulerAngles;
|
||||||
set => _ = GameState.IsBuildMode()
|
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);
|
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation = quaternion.Euler(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,10 +195,8 @@ namespace TechbloxModdingAPI
|
||||||
/// The player's mass.
|
/// The player's mass.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The mass.</value>
|
/// <value>The mass.</value>
|
||||||
public float Mass =>
|
[Obsolete] // We cannot get it clientside or something
|
||||||
1f / playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().physicsMass.InverseMass;
|
public float Mass => 0;
|
||||||
|
|
||||||
private float _ping = -1f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The player's latest network ping time.
|
/// The player's latest network ping time.
|
||||||
|
@ -185,12 +206,7 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var opt = playerEngine.GetPlayerStruct<PlayerNetworkStatsEntityStruct>(Id, Type);
|
return playerEngine.GetPing() / 1000f;
|
||||||
if (opt)
|
|
||||||
{
|
|
||||||
_ping = opt.Get().lastPingTimeSinceLevelLoad ?? _ping;
|
|
||||||
}
|
|
||||||
return _ping;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,15 +214,16 @@ namespace TechbloxModdingAPI
|
||||||
/// The player's initial health when entering Simulation (aka Time Running) mode.
|
/// The player's initial health when entering Simulation (aka Time Running) mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The initial health.</value>
|
/// <value>The initial health.</value>
|
||||||
|
[Obsolete("We can no longer get initial health, returns max health.")]
|
||||||
public float InitialHealth
|
public float InitialHealth
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
|
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id);
|
||||||
return opt ? opt.Get().initialHealth : -1f;
|
return opt ? opt.Get().maxHealth : -1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().initialHealth = value;
|
set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().maxHealth = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -217,30 +234,25 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
|
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id);
|
||||||
return opt ? opt.Get().currentHealth : -1f;
|
return opt ? opt.Get().currentHealth : -1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().currentHealth = value;
|
set => playerEngine.GetCharacterStruct<CharacterHealthEntityComponent>(Id).Get().currentHealth = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable.
|
/// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
|
||||||
|
[Obsolete("Players are probably always damageable")]
|
||||||
public bool Damageable
|
public bool Damageable
|
||||||
{
|
{
|
||||||
get
|
get => true;
|
||||||
{
|
|
||||||
var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
|
|
||||||
return opt.Get().canTakeDamageStat;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// ReSharper disable once ValueParameterNotUsed
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref var healthStruct = ref playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get();
|
|
||||||
healthStruct.canTakeDamage = value;
|
|
||||||
healthStruct.canTakeDamageStat = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,30 +260,26 @@ namespace TechbloxModdingAPI
|
||||||
/// The player's lives when initially entering Simulation (aka Time Running) mode.
|
/// The player's lives when initially entering Simulation (aka Time Running) mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The initial lives.</value>
|
/// <value>The initial lives.</value>
|
||||||
|
[Obsolete("The player has infinite lives")]
|
||||||
public uint InitialLives
|
public uint InitialLives
|
||||||
{
|
{
|
||||||
get
|
get => uint.MaxValue;
|
||||||
{
|
|
||||||
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
|
|
||||||
return opt ? opt.Get().initialLives : uint.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().initialLives = value;
|
// ReSharper disable once ValueParameterNotUsed
|
||||||
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The player's current lives in Simulation (aka Time Running) mode.
|
/// The player's current lives in Simulation (aka Time Running) mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The current lives.</value>
|
/// <value>The current lives.</value>
|
||||||
|
[Obsolete("The player has infinite lives")]
|
||||||
public uint CurrentLives
|
public uint CurrentLives
|
||||||
{
|
{
|
||||||
get
|
get => uint.MaxValue;
|
||||||
{
|
|
||||||
var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
|
|
||||||
return opt ? opt.Get().currentLives : uint.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().currentLives = value;
|
// ReSharper disable once ValueParameterNotUsed
|
||||||
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/*/// <summary>
|
/*/// <summary>
|
||||||
|
@ -302,7 +310,7 @@ namespace TechbloxModdingAPI
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var optstruct = playerEngine.GetCharacterStruct<EquippedPartStruct>(Id);
|
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>
|
/// <summary>
|
||||||
/// The player's mode in time stopped mode, determining what they place.
|
/// The player's mode in time stopped mode, determining what they place.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PlayerBuildingMode BuildingMode => (PlayerBuildingMode) playerEngine
|
public PlayerBuildingMode BuildingMode => (PlayerBuildingMode)Math.Log((double)playerEngine
|
||||||
.GetCharacterStruct<PlayerInputTimeStoppedContextStruct>(Id).Get().timeStoppedContext;
|
.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>
|
/// <summary>
|
||||||
/// Whether the player is sprinting.
|
/// Whether the player is sprinting.
|
||||||
|
@ -357,10 +377,10 @@ namespace TechbloxModdingAPI
|
||||||
public bool Sprinting
|
public bool Sprinting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementComponent>(Id).Get().sprinting
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting;
|
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementComponent>(Id).Get().sprinting = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,10 +390,10 @@ namespace TechbloxModdingAPI
|
||||||
public float SpeedSetting
|
public float SpeedSetting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speed
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speed = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,10 +403,10 @@ namespace TechbloxModdingAPI
|
||||||
public float SpeedSprintMultiplierSetting
|
public float SpeedSprintMultiplierSetting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speedSprintMultiplier
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().speedSprintMultiplier = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,10 +416,10 @@ namespace TechbloxModdingAPI
|
||||||
public float AccelerationSetting
|
public float AccelerationSetting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().acceleration
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameState.IsBuildMode()
|
||||||
? playerEngine.GetCharacterStruct<FlyCamMovementSettingsComponent>(Id).Get().acceleration = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,6 +443,39 @@ namespace TechbloxModdingAPI
|
||||||
playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
|
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>
|
/// <summary>
|
||||||
/// Returns the block the player is currently looking at in build mode.
|
/// Returns the block the player is currently looking at in build mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -431,7 +484,8 @@ namespace TechbloxModdingAPI
|
||||||
public Block GetBlockLookedAt(float maxDistance = -1f)
|
public Block GetBlockLookedAt(float maxDistance = -1f)
|
||||||
{
|
{
|
||||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
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)
|
? Block.New(egid)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
@ -444,8 +498,22 @@ namespace TechbloxModdingAPI
|
||||||
public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
|
public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
|
||||||
{
|
{
|
||||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
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
|
||||||
? new SimBody(egid)
|
? 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;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,6 +526,15 @@ namespace TechbloxModdingAPI
|
||||||
return playerEngine.GetSelectedBlocks(Id);
|
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)
|
public bool Equals(Player other)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, other)) return false;
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
@ -485,11 +562,17 @@ namespace TechbloxModdingAPI
|
||||||
return (int) Id;
|
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 methods
|
||||||
|
|
||||||
internal static void Init()
|
internal static void Init()
|
||||||
{
|
{
|
||||||
Utility.GameEngineManager.AddGameEngine(playerEngine);
|
Utility.GameEngineManager.AddGameEngine(playerEngine);
|
||||||
|
Utility.GameEngineManager.AddGameEngine(playerEventsEngine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
ColourMode,
|
ColourMode,
|
||||||
ConfigMode,
|
ConfigMode,
|
||||||
BlueprintMode,
|
BlueprintMode,
|
||||||
MaterialMode
|
MaterialMode,
|
||||||
|
LandscapeMode
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
using RobocraftX.Character;
|
using RobocraftX.Character;
|
||||||
|
@ -10,22 +8,24 @@ using RobocraftX.Common.Input;
|
||||||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||||
using RobocraftX.Physics;
|
using RobocraftX.Physics;
|
||||||
using RobocraftX.Blocks.Ghost;
|
using RobocraftX.Blocks.Ghost;
|
||||||
using Gamecraft.GUI.HUDFeedbackBlocks;
|
using RobocraftX.Common;
|
||||||
|
using RobocraftX.Multiplayer;
|
||||||
|
using RobocraftX.SimulationModeState;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Techblox.Camera;
|
using Techblox.Camera;
|
||||||
using Techblox.FlyCam;
|
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using Unity.Physics;
|
|
||||||
using UnityEngine;
|
|
||||||
using HarmonyLib;
|
|
||||||
using RobocraftX.Common;
|
|
||||||
using Svelto.ECS.DataStructures;
|
using Svelto.ECS.DataStructures;
|
||||||
|
using Techblox.BuildingDrone;
|
||||||
|
using Techblox.Character;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
|
using TechbloxModdingAPI.Input;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Players
|
namespace TechbloxModdingAPI.Players
|
||||||
{
|
{
|
||||||
internal class PlayerEngine : IApiEngine, IFactoryEngine
|
internal class PlayerEngine : IFunEngine
|
||||||
{
|
{
|
||||||
public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine";
|
public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine";
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ namespace TechbloxModdingAPI.Players
|
||||||
|
|
||||||
public bool isRemovable => false;
|
public bool isRemovable => false;
|
||||||
|
|
||||||
public IEntityFactory Factory { set; private get; }
|
public IEntityFunctions Functions { get; set; }
|
||||||
|
|
||||||
private bool isReady = false;
|
private bool isReady = false;
|
||||||
|
|
||||||
|
@ -50,10 +50,10 @@ namespace TechbloxModdingAPI.Players
|
||||||
public uint GetLocalPlayer()
|
public uint GetLocalPlayer()
|
||||||
{
|
{
|
||||||
if (!isReady) return uint.MaxValue;
|
if (!isReady) return uint.MaxValue;
|
||||||
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToBuffer();
|
var (localPlayers, count) = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
|
||||||
if (localPlayers.count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
return localPlayers.buffer[0].ID.entityID;
|
return localPlayers[0].ID.entityID;
|
||||||
}
|
}
|
||||||
return uint.MaxValue;
|
return uint.MaxValue;
|
||||||
}
|
}
|
||||||
|
@ -61,10 +61,10 @@ namespace TechbloxModdingAPI.Players
|
||||||
public uint GetRemotePlayer()
|
public uint GetRemotePlayer()
|
||||||
{
|
{
|
||||||
if (!isReady) return uint.MaxValue;
|
if (!isReady) return uint.MaxValue;
|
||||||
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToBuffer();
|
var (localPlayers, count) = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
||||||
if (localPlayers.count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
return localPlayers.buffer[0].ID.entityID;
|
return localPlayers[0].ID.entityID;
|
||||||
}
|
}
|
||||||
return uint.MaxValue;
|
return uint.MaxValue;
|
||||||
}
|
}
|
||||||
|
@ -107,26 +107,16 @@ namespace TechbloxModdingAPI.Players
|
||||||
return false;
|
return false;
|
||||||
if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
|
if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
|
||||||
{
|
{
|
||||||
EGID egid = new EGID(playerId, group);
|
ExitSeat(playerId);
|
||||||
entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
|
|
||||||
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
|
|
||||||
}
|
}
|
||||||
rbesOpt.Get().position = location;
|
rbesOpt.Get().position = location;
|
||||||
return true;
|
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)
|
public bool IsDead(uint playerId)
|
||||||
{
|
{
|
||||||
if (entitiesDB == null) return true;
|
if (entitiesDB == null) return true;
|
||||||
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
|
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reusable methods
|
// reusable methods
|
||||||
|
@ -140,7 +130,7 @@ namespace TechbloxModdingAPI.Players
|
||||||
{
|
{
|
||||||
group = default;
|
group = default;
|
||||||
if (GameState.IsBuildMode())
|
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;
|
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
||||||
for (int i = 0; i < characterGroups.count; i++)
|
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
|
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)
|
public EGID GetThingLookedAt(uint playerId, float maxDistance = -1f)
|
||||||
{
|
{
|
||||||
var opt = GetCameraStruct<CharacterCameraRayCastEntityStruct>(playerId);
|
var opt = GetCameraStruct<PhysicCameraRayCastEntityStruct>(playerId);
|
||||||
if (!opt) return EGID.Empty;
|
if (!opt) return default;
|
||||||
CharacterCameraRayCastEntityStruct rayCast = opt;
|
PhysicCameraRayCastEntityStruct rayCast = opt;
|
||||||
|
EGID physicCameraEgid = new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup);
|
||||||
float distance = maxDistance < 0
|
float distance = maxDistance < 0
|
||||||
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast,
|
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, physicCameraEgid,
|
||||||
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
|
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
|
||||||
: maxDistance;
|
: maxDistance;
|
||||||
if (rayCast.hit && rayCast.distance <= distance)
|
if (rayCast.hit && rayCast.distance <= distance)
|
||||||
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)
|
public unsafe Block[] GetSelectedBlocks(uint playerid)
|
||||||
{
|
{
|
||||||
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
|
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
|
||||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
|
||||||
return new Block[0];
|
return Array.Empty<Block>();
|
||||||
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
|
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
|
||||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||||
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
|
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
|
||||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||||
if (!state.active) return new Block[0];
|
if (!state.active) return Array.Empty<Block>();
|
||||||
var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
|
var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
|
||||||
var ret = new Block[blocks.count];
|
var ret = new Block[blocks.count];
|
||||||
for (int j = 0; j < blocks.count; j++)
|
for (int j = 0; j < blocks.count; j++)
|
||||||
|
@ -205,5 +196,79 @@ namespace TechbloxModdingAPI.Players
|
||||||
|
|
||||||
return ret;
|
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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Svelto.Tasks;
|
||||||
|
using Svelto.Tasks.Enumerators;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI;
|
using TechbloxModdingAPI.App;
|
||||||
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Tests;
|
using TechbloxModdingAPI.Tests;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Players
|
namespace TechbloxModdingAPI.Players
|
||||||
{
|
{
|
||||||
|
@ -24,13 +29,63 @@ namespace TechbloxModdingAPI.Players
|
||||||
[APITestCase(TestType.EditMode)]
|
[APITestCase(TestType.EditMode)]
|
||||||
public static void PositionTest()
|
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.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.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;
|
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.");
|
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)]
|
[APITestCase(TestType.Menu)]
|
||||||
public static void InvalidStateTest()
|
public static void InvalidStateTest()
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,7 @@ using UnityEngine;
|
||||||
using Gamecraft.Damage;
|
using Gamecraft.Damage;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.Physics;
|
using RobocraftX.Physics;
|
||||||
|
using Techblox.TimeRunning.Clusters;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
|
@ -14,20 +15,20 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SimBody : EcsObjectBase, IEquatable<SimBody>, IEquatable<EGID>
|
public class SimBody : EcsObjectBase, IEquatable<SimBody>, IEquatable<EGID>
|
||||||
{
|
{
|
||||||
public override EGID Id { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist.
|
/// 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.
|
/// Get the SimBody from a Block if possible for good performance here.
|
||||||
/// </summary>
|
/// </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 Cluster cluster;
|
||||||
private readonly uint clusterId = uint.MaxValue;
|
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))
|
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
|
public float Mass
|
||||||
{
|
{
|
||||||
get => math.rcp(GetStruct().physicsMass.InverseMass);
|
get => 0f;
|
||||||
//set => GetStruct().physicsMass.InverseMass = math.rcp(value);
|
//set => GetStruct().physicsMass.InverseMass = math.rcp(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float3 CenterOfMass
|
public float3 CenterOfMass
|
||||||
{
|
{
|
||||||
get => GetStruct().physicsMass.CenterOfMass;
|
get => 0f; //TODO
|
||||||
//set => GetStruct().physicsMass.CenterOfMass = value;
|
//set => GetStruct().physicsMass.CenterOfMass = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,20 +94,20 @@ namespace TechbloxModdingAPI
|
||||||
|
|
||||||
public float InitialHealth
|
public float InitialHealth
|
||||||
{
|
{
|
||||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth;
|
get => 0f;
|
||||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).initialHealth = value;
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public float CurrentHealth
|
public float CurrentHealth
|
||||||
{
|
{
|
||||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth;
|
get => 0f;
|
||||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).currentHealth = value;
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public float HealthMultiplier
|
public float HealthMultiplier
|
||||||
{
|
{
|
||||||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier;
|
get => 0f;
|
||||||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(this).healthMultiplier = value;
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
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
|
get
|
||||||
{
|
{
|
||||||
return RobocraftX.Schedulers.Lean.UIScheduler;
|
return RobocraftX.Schedulers.ClientLean.UIScheduler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace TechbloxModdingAPI.Tasks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return RobocraftX.Schedulers.ExtraLean.UIScheduler;
|
return RobocraftX.Schedulers.ClientExtraLean.UIScheduler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,64 +1,33 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TechbloxModdingAPI.App;
|
using BepInEx;
|
||||||
|
using BepInEx.Bootstrap;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using IllusionInjector;
|
|
||||||
// test
|
|
||||||
using RobocraftX.FrontEnd;
|
using RobocraftX.FrontEnd;
|
||||||
using Unity.Mathematics;
|
using TechbloxModdingAPI.App;
|
||||||
using UnityEngine;
|
|
||||||
using RobocraftX.Common.Input;
|
|
||||||
using TechbloxModdingAPI.Blocks;
|
|
||||||
using TechbloxModdingAPI.Commands;
|
using TechbloxModdingAPI.Commands;
|
||||||
using TechbloxModdingAPI.Input;
|
|
||||||
using TechbloxModdingAPI.Interface.IMGUI;
|
|
||||||
using TechbloxModdingAPI.Players;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Tests
|
namespace TechbloxModdingAPI.Tests
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG // The API should be loaded by other plugins, but it can be used by itself for testing
|
||||||
// unused by design
|
[BepInPlugin("org.exmods.TechbloxModdingAPIPluginTest", PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
|
||||||
/// <summary>
|
[BepInProcess("Techblox.exe")]
|
||||||
/// Modding API implemented as a standalone IPA Plugin.
|
public class TechbloxModdingAPIPluginTest : BaseUnityPlugin
|
||||||
/// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself
|
|
||||||
/// </summary>
|
|
||||||
class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin
|
|
||||||
{
|
{
|
||||||
|
private void Awake()
|
||||||
private static Harmony harmony { get; set; }
|
|
||||||
|
|
||||||
public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
|
|
||||||
|
|
||||||
public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
|
||||||
|
|
||||||
public string HarmonyID { get; } = "org.git.exmods.modtainers.techbloxmoddingapi";
|
|
||||||
|
|
||||||
public override void OnApplicationQuit()
|
|
||||||
{
|
{
|
||||||
Main.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnApplicationStart()
|
|
||||||
{
|
|
||||||
FileLog.Reset();
|
|
||||||
Harmony.DEBUG = true;
|
|
||||||
Main.Init();
|
Main.Init();
|
||||||
Logging.MetaDebugLog($"Version group id {(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!");
|
Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
|
||||||
|
Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler");
|
||||||
// debug/test commands
|
Game.Enter += (s, a) =>
|
||||||
if (Dependency.Hell("ExtraCommands"))
|
|
||||||
{
|
{
|
||||||
|
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()
|
CommandBuilder.Builder()
|
||||||
.Name("Exit")
|
.Name("Exit")
|
||||||
.Description("Close Techblox immediately, without any prompts")
|
.Description("Close Techblox immediately, without any prompts")
|
||||||
|
@ -71,218 +40,44 @@ namespace TechbloxModdingAPI.Tests
|
||||||
.Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
|
.Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
CommandBuilder.Builder()
|
|
||||||
.Name("MoveLastBlock")
|
|
||||||
.Description("Move the most-recently-placed block, and any connected blocks by the given offset")
|
|
||||||
.Action((float x, float y, float z) =>
|
|
||||||
{
|
|
||||||
if (GameState.IsBuildMode())
|
|
||||||
foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
|
|
||||||
block.Position += new Unity.Mathematics.float3(x, y, z);
|
|
||||||
else
|
|
||||||
Logging.CommandLogError("Blocks can only be moved in Build mode!");
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder()
|
|
||||||
.Name("PlaceAluminium")
|
|
||||||
.Description("Place a block of aluminium at the given coordinates")
|
|
||||||
.Action((float x, float y, float z) =>
|
|
||||||
{
|
|
||||||
var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z));
|
|
||||||
Logging.CommandLog("Block placed with type: " + block.Type);
|
|
||||||
})
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder()
|
|
||||||
.Name("PlaceAluminiumLots")
|
|
||||||
.Description("Place a lot of blocks of aluminium at the given coordinates")
|
|
||||||
.Action((float x, float y, float z) =>
|
|
||||||
{
|
|
||||||
Logging.CommandLog("Starting...");
|
|
||||||
var sw = Stopwatch.StartNew();
|
|
||||||
for (int i = 0; i < 100; i++)
|
|
||||||
for (int j = 0; j < 100; j++)
|
|
||||||
Block.PlaceNew(BlockIDs.Cube, new float3(x + i, y, z + j));
|
|
||||||
sw.Stop();
|
|
||||||
Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
|
|
||||||
})
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
Block b = null;
|
|
||||||
CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up")
|
|
||||||
.Action(() =>
|
|
||||||
{
|
|
||||||
if (b == null)
|
|
||||||
{
|
|
||||||
b = new Player(PlayerType.Local).GetBlockLookedAt();
|
|
||||||
Logging.CommandLog("Block saved: " + b);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0)));
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
|
|
||||||
.Action(() => { throw new Exception("Error Command always throws an error"); })
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("ColorBlock",
|
|
||||||
"Change color of the block looked at if there's any.")
|
|
||||||
.Action<string>(str =>
|
|
||||||
{
|
|
||||||
if (!Enum.TryParse(str, out BlockColors color))
|
|
||||||
{
|
|
||||||
Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values.");
|
|
||||||
var s = str.Split(' ');
|
|
||||||
new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]),
|
|
||||||
float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3]));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new Player(PlayerType.Local).GetBlockLookedAt().Color = color;
|
|
||||||
Logging.CommandLog("Colored block to " + color);
|
|
||||||
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("MoveBlockByID", "Gets a block based on its object identifier and teleports it up.")
|
|
||||||
.Action<char>(ch =>
|
|
||||||
{
|
|
||||||
foreach (var body in SimBody.GetFromObjectID(ch))
|
|
||||||
{
|
|
||||||
Logging.CommandLog("SimBody: " + body);
|
|
||||||
body.Position += new float3(0, 10, 0);
|
|
||||||
foreach (var bodyConnectedBody in body.GetConnectedBodies())
|
|
||||||
{
|
|
||||||
Logging.CommandLog("Moving " + bodyConnectedBody);
|
|
||||||
bodyConnectedBody.Position += new float3(0, 10, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.")
|
|
||||||
.Action((float val, float max) =>
|
|
||||||
{
|
|
||||||
var body = new Player(PlayerType.Local).GetSimBodyLookedAt();
|
|
||||||
if (body == null) return;
|
|
||||||
body.CurrentHealth = val;
|
|
||||||
body.InitialHealth = max;
|
|
||||||
Logging.CommandLog("Health set to: " + val);
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group")
|
|
||||||
.Action((float x, float y, float z) =>
|
|
||||||
{
|
|
||||||
var pos = new float3(x, y, z);
|
|
||||||
var group = BlockGroup.Create(new Block(BlockIDs.Cube, pos) {Color = BlockColors.Aqua});
|
|
||||||
new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
|
|
||||||
{Color = BlockColors.Blue, BlockGroup = group};
|
|
||||||
new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
|
|
||||||
{Color = BlockColors.Green, BlockGroup = group};
|
|
||||||
new Block(BlockIDs.Cube, pos + new float3(1, 0, 0))
|
|
||||||
{Color = BlockColors.Lime, BlockGroup = group};
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("placeCustomBlock", "Places a custom block, needs a custom catalog and assets.")
|
|
||||||
.Action((float x, float y, float z) =>
|
|
||||||
{
|
|
||||||
Logging.CommandLog("Block placed: " +
|
|
||||||
Block.PlaceNew((BlockIDs) 500, new float3(0, 0, 0)));
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("toggleTimeMode", "Enters or exits simulation.")
|
|
||||||
.Action((float x, float y, float z) =>
|
|
||||||
{
|
|
||||||
Game.CurrentGame().ToggleTimeMode();
|
|
||||||
}).Build();
|
|
||||||
|
|
||||||
CommandBuilder.Builder("testColorBlock", "Tests coloring a block to default color")
|
|
||||||
.Action(() => Player.LocalPlayer.GetBlockLookedAt().Color = BlockColors.Default).Build();
|
|
||||||
CommandBuilder.Builder("testMaterialBlock", "Tests materialing a block to default material")
|
|
||||||
.Action(() => Player.LocalPlayer.GetBlockLookedAt().Material = BlockMaterial.Default).Build();
|
|
||||||
CommandBuilder.Builder("testGameName", "Tests changing the game name")
|
|
||||||
.Action(() => Game.CurrentGame().Name = "Test").Build();
|
|
||||||
CommandBuilder.Builder("makeBlockStatic", "Makes a block you look at static")
|
|
||||||
.Action(() => Player.LocalPlayer.GetBlockLookedAt().Static = true).Build();
|
|
||||||
|
|
||||||
Game.AddPersistentDebugInfo("InstalledMods", InstalledMods);
|
Game.AddPersistentDebugInfo("InstalledMods", InstalledMods);
|
||||||
Block.Placed += (sender, args) =>
|
|
||||||
Logging.MetaDebugLog("Placed block " + args.Block);
|
|
||||||
Block.Removed += (sender, args) =>
|
|
||||||
Logging.MetaDebugLog("Removed block " + args.Block);
|
|
||||||
}
|
|
||||||
|
|
||||||
// dependency test
|
// Plugin startup logic
|
||||||
if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0")))
|
Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!");
|
||||||
{
|
|
||||||
Logging.LogWarning("You're in TechbloxScripting dependency hell");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logging.Log("Compatible TechbloxScripting detected");
|
|
||||||
}
|
|
||||||
// Interface test
|
|
||||||
/*Group uiGroup = new Group(new Rect(20, 20, 200, 500), "TechbloxModdingAPI_UITestGroup", true);
|
|
||||||
var button = new Button("TEST");
|
|
||||||
button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
|
|
||||||
var button2 = new Button("TEST2");
|
|
||||||
button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
|
|
||||||
Text uiText = new Text("<Input!>", multiline: true);
|
|
||||||
uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); };
|
|
||||||
Label uiLabel = new Label("Label!");
|
|
||||||
Image uiImg = new Image(name:"Behold this texture!");
|
|
||||||
uiImg.Enabled = false;
|
|
||||||
uiGroup.AddElement(button);
|
|
||||||
uiGroup.AddElement(button2);
|
|
||||||
uiGroup.AddElement(uiText);
|
|
||||||
uiGroup.AddElement(uiLabel);
|
|
||||||
uiGroup.AddElement(uiImg);*/
|
|
||||||
|
|
||||||
/*Addressables.LoadAssetAsync<Texture2D>("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg")
|
|
||||||
.Completed +=
|
|
||||||
handle =>
|
|
||||||
{
|
|
||||||
uiImg.Texture = handle.Result;
|
|
||||||
uiImg.Enabled = true;
|
|
||||||
Logging.MetaDebugLog($"Got blue bg asset {handle.Result}");
|
|
||||||
};*/
|
|
||||||
|
|
||||||
/*((FasterList<GuiInputMap.GuiInputMapElement>)AccessTools.Property(typeof(GuiInputMap), "GuiInputsButtonDown").GetValue(null))
|
|
||||||
.Add(new GuiInputMap.GuiInputMapElement(RewiredConsts.Action.ToggleCommandLine, GuiIn))*/
|
|
||||||
#if TEST
|
#if TEST
|
||||||
TestRoot.RunTests();
|
TestRoot.RunTests();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
Main.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
private string modsString;
|
private string modsString;
|
||||||
private string InstalledMods()
|
private string InstalledMods()
|
||||||
{
|
{
|
||||||
if (modsString != null) return modsString;
|
if (modsString != null) return modsString;
|
||||||
StringBuilder sb = new StringBuilder("Installed mods:");
|
StringBuilder sb = new StringBuilder("Installed mods:");
|
||||||
foreach (var plugin in PluginManager.Plugins)
|
foreach (var (_, plugin) in Chainloader.PluginInfos)
|
||||||
sb.Append("\n" + plugin.Name + " - " + plugin.Version);
|
sb.Append("\n" + plugin.Metadata.Name + " - " + plugin.Metadata.Version);
|
||||||
return modsString = sb.ToString();
|
return modsString = sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnUpdate()
|
|
||||||
{
|
|
||||||
if (UnityEngine.Input.GetKeyDown(KeyCode.End))
|
|
||||||
{
|
|
||||||
Console.WriteLine("Pressed button to toggle console");
|
|
||||||
FakeInput.CustomInput(new LocalInputEntityStruct {commandLineToggleInput = true});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
public class MinimumSpecsPatch
|
public class MinimumSpecsPatch
|
||||||
{
|
{
|
||||||
public static bool Prefix()
|
public static bool Prefix(ref bool __result)
|
||||||
{
|
{
|
||||||
|
__result = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodInfo TargetMethod()
|
public static MethodInfo TargetMethod()
|
||||||
{
|
{
|
||||||
return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method;
|
return ((Func<bool>) MinimumSpecsCheck.CheckRequirementsMet).Method;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,7 +7,9 @@ using System.Linq; // welcome to the dark side
|
||||||
using Svelto.Tasks;
|
using Svelto.Tasks;
|
||||||
using Svelto.Tasks.Lean;
|
using Svelto.Tasks.Lean;
|
||||||
using Svelto.Tasks.Enumerators;
|
using Svelto.Tasks.Enumerators;
|
||||||
|
using Svelto.Tasks.Lean.Unity;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
using TechbloxModdingAPI.App;
|
using TechbloxModdingAPI.App;
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
@ -64,7 +66,7 @@ namespace TechbloxModdingAPI.Tests
|
||||||
_testsCountPassed = 0;
|
_testsCountPassed = 0;
|
||||||
_testsCountFailed = 0;
|
_testsCountFailed = 0;
|
||||||
// flow control
|
// 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";
|
Game.Exit += (s, a) => state = "ReturningFromGame";
|
||||||
Client.EnterMenu += (sender, args) =>
|
Client.EnterMenu += (sender, args) =>
|
||||||
{
|
{
|
||||||
|
@ -129,7 +131,7 @@ namespace TechbloxModdingAPI.Tests
|
||||||
|
|
||||||
private static IEnumerator<TaskContract> GoToGameTests()
|
private static IEnumerator<TaskContract> GoToGameTests()
|
||||||
{
|
{
|
||||||
Client app = new Client();
|
Client app = Client.Instance;
|
||||||
int oldLength = 0;
|
int oldLength = 0;
|
||||||
while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
|
while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
|
||||||
{
|
{
|
||||||
|
@ -137,7 +139,15 @@ namespace TechbloxModdingAPI.Tests
|
||||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||||
}
|
}
|
||||||
yield return Yield.It;
|
yield return Yield.It;
|
||||||
|
try
|
||||||
|
{
|
||||||
app.MyGames[0].EnterGame();
|
app.MyGames[0].EnterGame();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to go to game tests");
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
/*Game newGame = Game.NewGame();
|
/*Game newGame = Game.NewGame();
|
||||||
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync
|
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync
|
||||||
newGame.EnterGame();*/
|
newGame.EnterGame();*/
|
||||||
|
@ -157,6 +167,7 @@ namespace TechbloxModdingAPI.Tests
|
||||||
};
|
};
|
||||||
for (var index = 0; index < testTypesToRun.Length; index++)
|
for (var index = 0; index < testTypesToRun.Length; index++)
|
||||||
{
|
{
|
||||||
|
Logging.MetaLog($"Running test type {testTypesToRun[index]}");
|
||||||
foreach (Type t in testTypes)
|
foreach (Type t in testTypes)
|
||||||
{
|
{
|
||||||
foreach (MethodBase m in t.GetMethods())
|
foreach (MethodBase m in t.GetMethods())
|
||||||
|
@ -189,16 +200,26 @@ namespace TechbloxModdingAPI.Tests
|
||||||
cont = false;
|
cont = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return Yield.It;
|
if (cont)
|
||||||
|
yield return enumerator.Current;
|
||||||
} while (cont);
|
} while (cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return Yield.It;
|
yield return new WaitForSecondsEnumerator(1f).Continue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index + 1 < testTypesToRun.Length) //Don't toggle on the last test
|
if (index + 1 < testTypesToRun.Length) //Don't toggle on the last test
|
||||||
|
{
|
||||||
|
bool running = currentGame.IsTimeRunning;
|
||||||
currentGame.ToggleTimeMode();
|
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();
|
yield return new WaitForSecondsEnumerator(5).Continue();
|
||||||
}
|
}
|
||||||
// exit game
|
// exit game
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using BepInEx.Bootstrap;
|
||||||
using IllusionInjector;
|
|
||||||
using IllusionPlugin;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
|
@ -15,11 +13,11 @@ namespace TechbloxModdingAPI.Utility
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The plugin.</returns>
|
/// <returns>The plugin.</returns>
|
||||||
/// <param name="name">The plugin's name.</param>
|
/// <param name="name">The plugin's name.</param>
|
||||||
public static IPlugin GetPlugin(string name)
|
public static BepInEx.PluginInfo GetPlugin(string name)
|
||||||
{
|
{
|
||||||
foreach(IPlugin plugin in PluginManager.Plugins)
|
foreach(var plugin in Chainloader.PluginInfos.Values)
|
||||||
{
|
{
|
||||||
if (plugin.Name == name)
|
if (plugin.Metadata.Name == name)
|
||||||
{
|
{
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
@ -35,45 +33,15 @@ namespace TechbloxModdingAPI.Utility
|
||||||
/// <param name="name">The plugin's name.</param>
|
/// <param name="name">The plugin's name.</param>
|
||||||
public static Version GetPluginVersion(string name)
|
public static Version GetPluginVersion(string name)
|
||||||
{
|
{
|
||||||
IPlugin plugin = GetPlugin(name);
|
var plugin = GetPlugin(name);
|
||||||
if (plugin != null) {
|
if (plugin != null) {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new Version(plugin.Version);
|
return plugin.Metadata.Version;
|
||||||
} catch (Exception e) when (
|
} catch (Exception e) when (e is ArgumentException or FormatException or OverflowException) {}
|
||||||
e is ArgumentException
|
|
||||||
|| e is ArgumentNullException
|
|
||||||
|| e is ArgumentOutOfRangeException
|
|
||||||
|| e is FormatException
|
|
||||||
|| e is OverflowException) {}
|
|
||||||
return plugin.GetType().Assembly.GetName().Version;
|
return plugin.GetType().Assembly.GetName().Version;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (I'm leaving the auto-generated version)
|
|
||||||
// <summary>
|
|
||||||
// Hell the specified name and version.
|
|
||||||
// </summary>
|
|
||||||
// <returns>The hell.</returns>
|
|
||||||
// <param name="name">Name.</param>
|
|
||||||
// <param name="version">Version.</param>
|
|
||||||
/// <summary>
|
|
||||||
/// Detect if you're in dependency hell with respect to the plugin.
|
|
||||||
/// ie Check if the plugin doesn't exist or is out of date.
|
|
||||||
/// When version is null, this only checks if the plugin exists.
|
|
||||||
/// The version is retrieved using GetPluginVersion(string name).
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Are you in dependency hell?</returns>
|
|
||||||
/// <param name="name">The plugin's name'</param>
|
|
||||||
/// <param name="version">The target version.</param>
|
|
||||||
public static bool Hell(string name, Version version = null)
|
|
||||||
{
|
|
||||||
Version pluginVersion = GetPluginVersion(name);
|
|
||||||
if (version == null) {
|
|
||||||
return pluginVersion == null;
|
|
||||||
}
|
|
||||||
return (pluginVersion == null || pluginVersion < version);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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 DataLoader;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using DataLoader;
|
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using RobocraftX;
|
using RobocraftX;
|
||||||
using RobocraftX.Common.Utilities;
|
using RobocraftX.CR.MainGame;
|
||||||
using RobocraftX.GUI;
|
using RobocraftX.GUI;
|
||||||
using RobocraftX.Multiplayer;
|
using RobocraftX.Multiplayer;
|
||||||
using RobocraftX.Rendering;
|
|
||||||
using Svelto.Context;
|
using Svelto.Context;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Schedulers;
|
using Svelto.ECS.GUI;
|
||||||
|
using Techblox.GameSelection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Unity.Entities;
|
using Unity.Entities;
|
||||||
using Unity.Physics.Systems;
|
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
|
public static BuildPhysicsWorld _physicsWorldSystem
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -112,14 +98,6 @@ namespace TechbloxModdingAPI.Utility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PhysicsUtility _physicsUtility
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (PhysicsUtility)fgcr?.Field("_physicsUtility").GetValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler
|
/*public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -144,11 +122,11 @@ namespace TechbloxModdingAPI.Utility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ECSGameObjectResourceManager _eCsGameObjectResourceManager
|
public static ECSMainGameResourceManagers _managers
|
||||||
{
|
{
|
||||||
get
|
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;
|
private static Traverse fgcr;
|
||||||
|
|
||||||
public static void Init(FullGameCompositionRoot instance)
|
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.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using RobocraftX.StateSync;
|
using RobocraftX.StateSync;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
@ -26,10 +23,10 @@ namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
Logging.MetaDebugLog($"Registering Game IApiEngine {engine.Name}");
|
Logging.MetaDebugLog($"Registering Game IApiEngine {engine.Name}");
|
||||||
_lastEngineRoot.AddEngine(engine);
|
_lastEngineRoot.AddEngine(engine);
|
||||||
if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType()))
|
if (engine is IFactoryEngine factoryEngine)
|
||||||
{
|
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||||
((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory();
|
if (engine is IFunEngine funEngine)
|
||||||
}
|
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +63,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
var enginesRoot = helper.enginesRoot;
|
var enginesRoot = helper.enginesRoot;
|
||||||
_lastEngineRoot = enginesRoot;
|
_lastEngineRoot = enginesRoot;
|
||||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||||
|
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
||||||
foreach (var key in _gameEngines.Keys)
|
foreach (var key in _gameEngines.Keys)
|
||||||
{
|
{
|
||||||
Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}");
|
Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}");
|
||||||
|
@ -75,6 +73,8 @@ namespace TechbloxModdingAPI.Utility
|
||||||
enginesRoot.AddEngine(_gameEngines[key]);
|
enginesRoot.AddEngine(_gameEngines[key]);
|
||||||
if (_gameEngines[key] is IFactoryEngine factEngine)
|
if (_gameEngines[key] is IFactoryEngine factEngine)
|
||||||
factEngine.Factory = factory;
|
factEngine.Factory = factory;
|
||||||
|
if (_gameEngines[key] is IFunEngine funEngine)
|
||||||
|
funEngine.Functions = functions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void CommandLog(string msg)
|
public static void CommandLog(string msg)
|
||||||
{
|
{
|
||||||
uREPL.Log.Output(msg);
|
Log(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -179,7 +179,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void CommandLogError(string msg)
|
public static void CommandLogError(string msg)
|
||||||
{
|
{
|
||||||
uREPL.Log.Error(msg);
|
LogError(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -195,7 +195,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void CommandLogWarning(string msg)
|
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}");
|
Logging.MetaDebugLog($"Registering Menu IApiEngine {engine.Name}");
|
||||||
_lastEngineRoot.AddEngine(engine);
|
_lastEngineRoot.AddEngine(engine);
|
||||||
if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType()))
|
if (engine is IFactoryEngine factoryEngine)
|
||||||
{
|
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||||
((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory();
|
if (engine is IFunEngine funEngine)
|
||||||
}
|
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,14 +65,13 @@ namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
_lastEngineRoot = enginesRoot;
|
_lastEngineRoot = enginesRoot;
|
||||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||||
|
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
||||||
foreach (var key in _menuEngines.Keys)
|
foreach (var key in _menuEngines.Keys)
|
||||||
{
|
{
|
||||||
Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}");
|
Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}");
|
||||||
enginesRoot.AddEngine(_menuEngines[key]);
|
enginesRoot.AddEngine(_menuEngines[key]);
|
||||||
if (_menuEngines[key] is IFactoryEngine factEngine)
|
if (_menuEngines[key] is IFactoryEngine factEngine) factEngine.Factory = factory;
|
||||||
{
|
if(_menuEngines[key] is IFunEngine funEngine) funEngine.Functions = functions;
|
||||||
factEngine.Factory = factory;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
public ref struct OptionalRef<T> where T : struct, IEntityComponent
|
public ref struct OptionalRef<T> where T : struct, IBaseEntityComponent
|
||||||
{
|
{
|
||||||
|
private readonly EGID entityId;
|
||||||
private readonly State state;
|
private readonly State state;
|
||||||
private readonly uint index;
|
private readonly uint index;
|
||||||
private NB<T> array;
|
private NB<T> array;
|
||||||
|
@ -14,19 +14,22 @@ namespace TechbloxModdingAPI.Utility
|
||||||
private readonly EntityInitializer initializer;
|
private readonly EntityInitializer initializer;
|
||||||
//The possible fields are: (index && (array || managedArray)) || initializer
|
//The possible fields are: (index && (array || managedArray)) || initializer
|
||||||
|
|
||||||
public OptionalRef(NB<T> array, uint index)
|
public OptionalRef(NB<T> array, uint index, EGID entityId = default)
|
||||||
{
|
{
|
||||||
state = State.Native;
|
state = State.Native;
|
||||||
this.array = array;
|
this.array = array;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
this.entityId = entityId;
|
||||||
initializer = default;
|
initializer = default;
|
||||||
|
managedArray = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OptionalRef(MB<T> array, uint index)
|
public OptionalRef(MB<T> array, uint index, EGID entityId = default)
|
||||||
{
|
{
|
||||||
state = State.Managed;
|
state = State.Managed;
|
||||||
managedArray = array;
|
managedArray = array;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
this.entityId = entityId;
|
||||||
initializer = default;
|
initializer = default;
|
||||||
this.array = default;
|
this.array = default;
|
||||||
}
|
}
|
||||||
|
@ -36,8 +39,9 @@ namespace TechbloxModdingAPI.Utility
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object with the initializer</param>
|
/// <param name="obj">The object with the initializer</param>
|
||||||
/// <param name="unmanaged">Whether the struct is unmanaged</param>
|
/// <param name="unmanaged">Whether the struct is unmanaged</param>
|
||||||
public OptionalRef(EcsObjectBase obj, bool unmanaged)
|
public OptionalRef(EcsObjectBase obj, bool unmanaged, EGID entityId = default)
|
||||||
{
|
{
|
||||||
|
this.entityId = entityId;
|
||||||
if (obj.InitData.Valid)
|
if (obj.InitData.Valid)
|
||||||
{
|
{
|
||||||
initializer = obj.InitData.Initializer(obj.Id);
|
initializer = obj.InitData.Initializer(obj.Id);
|
||||||
|
@ -50,6 +54,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
}
|
}
|
||||||
array = default;
|
array = default;
|
||||||
index = default;
|
index = default;
|
||||||
|
managedArray = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -58,22 +63,40 @@ namespace TechbloxModdingAPI.Utility
|
||||||
/// <returns>The value or the default value</returns>
|
/// <returns>The value or the default value</returns>
|
||||||
public ref T Get()
|
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.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];
|
if ((state & State.Native) != State.Empty) return ref array[index];
|
||||||
return ref managedArray[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 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 T(OptionalRef<T> opt) => opt.Get();
|
||||||
|
|
||||||
public static implicit operator bool(OptionalRef<T> opt) => opt.state != State.Empty;
|
public static implicit operator bool(OptionalRef<T> opt) => opt.state != State.Empty;
|
||||||
|
|
||||||
|
public static implicit operator EGID(OptionalRef<T> opt) => opt.entityId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an instance of a struct T that can be referenced.
|
/// Creates an instance of a struct T that can be referenced.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct CompRefCache
|
private struct CompRefCache
|
||||||
{
|
{
|
||||||
public static T Default;
|
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