Compare commits

..

No commits in common. "master" and "v1.1.0" have entirely different histories.

194 changed files with 6241 additions and 15806 deletions

View file

@ -1,67 +0,0 @@
#!/usr/bin/python3
import argparse
import re
# this assumes a mostly semver-complient version number
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Increment TechbloxModdingAPI version")
parser.add_argument('version', metavar="VN", type=str, help="The version number to increment, or the index of the number (zero-indexed).")
args = parser.parse_args()
version_index = -1
try:
version_index = int(args.version)
except Exception:
if args.version.lower() == "major":
version_index = 0
elif args.version.lower() == "minor":
version_index = 1
elif args.version.lower() == "patch":
version_index = 2
if version_index < 0:
print("Could not parse version argument.")
exit(version_index)
print(version_index)
old_version = ""
new_version = ""
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
print("Parsing TechbloxModdingAPI.csproj")
fileStr = xmlFile.read()
versionMatch = re.search(r"<Version>(.+)</Version>", fileStr)
if versionMatch is None:
print("Unable to find version number in TechbloxModdingAPI.csproj")
exit(1)
old_version = versionMatch.group(1)
versionList = old_version.split(".")
if len(versionList) <= version_index:
print("Invalid version string")
exit(1)
versionList[version_index] = str(int(versionList[version_index]) + 1)
for i in range(version_index + 1, len(versionList)):
try:
int(versionList[i])
versionList[i] = "0"
except Exception:
tmp = versionList[i].split("-")
tmp[0] = "0"
versionList[i] = "-".join(tmp)
new_version = ".".join(versionList)
print(new_version)
newFileContents = fileStr.replace("<Version>"+old_version+"</Version>", "<Version>"+new_version+"</Version>")
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
print("Writing new version to project file")
xmlFile.write(newFileContents)
with open("../doxygen.conf", "r") as doxFile:
print("Parsing doxygen.conf")
doxStr = doxFile.read()
newFileContents = doxStr.replace("= \"v" + old_version + "\"", "= \"v" + new_version + "\"")
with open("../doxygen.conf", "w") as doxFile:
print("Writing new version to doxygen config")
doxFile.write(newFileContents)

27
Automation/gen_csproj.py Executable file → Normal file
View file

@ -3,24 +3,15 @@
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|IllusionInjector|IllusionPlugin|netstandard)\." DLL_EXCLUSIONS_REGEX = r"(System|Microsoft|Mono|IronPython|DiscordRPC)\."
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)) is None and str(child).lower().endswith(".dll"): if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child), re.I) is None and str(child).lower().endswith(".dll"):
childstr = str(child) result.append(str(child).replace("\\", "/"))
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):
@ -36,16 +27,16 @@ def buildReferencesXml(path):
return "<!--Start Dependencies-->\n <ItemGroup>\n" + "".join(result) + " </ItemGroup>\n<!--End Dependencies-->" return "<!--Start Dependencies-->\n <ItemGroup>\n" + "".join(result) + " </ItemGroup>\n<!--End Dependencies-->"
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate TechbloxModdingAPI.csproj") parser = argparse.ArgumentParser(description="Generate GamecraftModdingAPI.csproj")
# TODO (maybe?): add params for custom csproj read and write locations # TODO (maybe?): add params for custom csproj read and write locations
args = parser.parse_args() args = parser.parse_args()
print("Building Assembly references") print("Building Assembly references")
asmXml = buildReferencesXml("../ref_TB/Techblox_Data/Managed") asmXml = buildReferencesXml("../ref/Gamecraft_Data/Managed")
# print(asmXml) # print(asmXml)
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile: with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile:
print("Parsing TechbloxModdingAPI.csproj") print("Parsing GamecraftModdingAPI.csproj")
fileStr = xmlFile.read() fileStr = xmlFile.read()
# print(fileStr) # print(fileStr)
depsStart = re.search(r"\<!--\s*Start\s+Dependencies\s*--\>", fileStr) depsStart = re.search(r"\<!--\s*Start\s+Dependencies\s*--\>", fileStr)
@ -53,8 +44,8 @@ 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() - 1] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:] newFileStr = fileStr[:depsStart.start()] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile: with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "w") as xmlFile:
print("Writing Assembly references") print("Writing Assembly references")
xmlFile.write(newFileStr) xmlFile.write(newFileStr)
# print(newFileStr) # print(newFileStr)

View file

@ -1,10 +0,0 @@
<?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>

View file

@ -1,158 +0,0 @@
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);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,53 +0,0 @@
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));
}
}
}

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Lib.Harmony" version="2.2.0" targetFramework="net472" />
</packages>

25
GamecraftModdingAPI.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29411.108
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GamecraftModdingAPI", "GamecraftModdingAPI\GamecraftModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {72FB94D0-6C50-475B-81E0-C94C7D7A2A17}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,319 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
using RobocraftX.Common;
using RobocraftX.Blocks;
using Unity.Mathematics;
using Gamecraft.Blocks.GUI;
using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI
{
/// <summary>
/// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored.
/// For specific block type operations, use the specialised block classes in the GamecraftModdingAPI.Blocks namespace.
/// </summary>
public class Block
{
protected static readonly PlacementEngine PlacementEngine = new PlacementEngine();
protected static readonly MovementEngine MovementEngine = new MovementEngine();
protected static readonly RotationEngine RotationEngine = new RotationEngine();
protected static readonly RemovalEngine RemovalEngine = new RemovalEngine();
protected static readonly SignalEngine SignalEngine = new SignalEngine();
protected internal static readonly BlockEngine BlockEngine = new BlockEngine();
/// <summary>
/// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position.
/// Place blocks next to each other to connect them.
/// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
/// </summary>
/// <param name="block">The block's type</param>
/// <param name="color">The block's color</param>
/// <param name="darkness">The block color's darkness (0-9) - 0 is default color</param>
/// <param name="position">The block's position in the grid - default block size is 0.2</param>
/// <param name="rotation">The block's rotation in degrees</param>
/// <param name="uscale">The block's uniform scale - default scale is 1 (with 0.2 width)</param>
/// <param name="scale">The block's non-uniform scale - 0 means <paramref name="uscale"/> is used</param>
/// <param name="player">The player who placed the block</param>
/// <returns>The placed block or null if failed</returns>
public static Block PlaceNew(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
return new Block(PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation));
}
return null;
}
/// <summary>
/// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position.
/// Place blocks next to each other to connect them.
/// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
/// <para></para>
/// <para>This method waits for the block to be constructed in the game.</para>
/// </summary>
/// <param name="block">The block's type</param>
/// <param name="color">The block's color</param>
/// <param name="darkness">The block color's darkness (0-9) - 0 is default color</param>
/// <param name="position">The block's position in the grid - default block size is 0.2</param>
/// <param name="rotation">The block's rotation in degrees</param>
/// <param name="uscale">The block's uniform scale - default scale is 1 (with 0.2 width)</param>
/// <param name="scale">The block's non-uniform scale - 0 means <paramref name="uscale"/> is used</param>
/// <param name="player">The player who placed the block</param>
/// <returns>The placed block or null if failed</returns>
public static async Task<Block> PlaceNewAsync(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
try
{
var ret = new Block(PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation));
await AsyncUtils.WaitForSubmission();
return ret;
}
catch (Exception e)
{
Logging.MetaDebugLog(e);
}
}
return null;
}
/// <summary>
/// Returns the most recently placed block.
/// </summary>
/// <returns>The block object</returns>
public static Block GetLastPlacedBlock()
{
return new Block(BlockIdentifiers.LatestBlockID);
}
public Block(EGID id)
{
Id = id;
if (!BlockEngine.BlockExists(Id))
{
Sync();
if (!BlockEngine.BlockExists(Id))
{
throw new BlockDoesNotExistException($"Block {Id.entityID} must be placed using PlaceNew(...) since it does not exist yet");
}
}
}
public Block(uint id) : this(new EGID(id, CommonExclusiveGroups.OWNED_BLOCKS_GROUP))
{
}
/// <summary>
/// Synchronize newly created entity components with entities DB.
/// This forces a partial game tick, so it may be slow.
/// This also has the potential to make Gamecraft unstable.
/// Use this sparingly.
/// </summary>
protected static void Sync()
{
DeterministicStepCompositionRootPatch.SubmitEntitiesNow();
}
public EGID Id { get; protected set; }
/// <summary>
/// The block's current position or zero if the block no longer exists.
/// A block is 0.2 wide by default in terms of position.
/// </summary>
public float3 Position
{
get => Exists ? MovementEngine.GetPosition(Id.entityID) : float3.zero;
set
{
if (Exists) MovementEngine.MoveBlock(Id.entityID, value);
}
}
/// <summary>
/// The block's current rotation in degrees or zero if the block doesn't exist.
/// </summary>
public float3 Rotation
{
get => Exists ? RotationEngine.GetRotation(Id.entityID) : float3.zero;
set
{
if (Exists) RotationEngine.RotateBlock(Id.entityID, value);
}
}
/// <summary>
/// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling.
/// </summary>
public float3 Scale
{
get => BlockEngine.GetBlockInfo<ScalingEntityStruct>(Id).scale;
set
{
BlockEngine.GetBlockInfo<ScalingEntityStruct>(Id).scale = value;
}
}
/// <summary>
/// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale.
/// </summary>
public int UniformScale
{
get => BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(Id).scaleFactor;
set
{
ref var scaleStruct = ref BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(Id);
scaleStruct.scaleFactor = value;
Scale = new float3(value, value, value);
}
}
/// <summary>
/// The block's type (ID). Returns BlockIDs.Invalid if the block doesn't exist anymore.
/// </summary>
public BlockIDs Type
{
get
{
var id = (BlockIDs) BlockEngine.GetBlockInfo<DBEntityStruct>(Id, out var exists).DBID;
return exists ? id : BlockIDs.Invalid;
}
}
/// <summary>
/// The block's color. Returns BlockColors.Default if the block no longer exists.
/// </summary>
public BlockColor Color
{
get
{
byte index = BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id, out var exists).indexInPalette;
if (!exists) index = byte.MaxValue;
if (index == byte.MaxValue) return new BlockColor { Color = BlockColors.Default };
return new BlockColor { Color = (BlockColors)(index % 10), Darkness = (byte)(index / 10) };
}
set
{
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id);
color.indexInPalette = (byte)(value.Color + value.Darkness * 10);
color.overridePaletteColour = false;
color.needsUpdate = true;
BlockEngine.SetBlockColorFromPalette(ref color);
}
}
/// <summary>
/// The block's exact color. Gets reset to the palette color (Color property) after reentering the game.
/// </summary>
public float4 CustomColor
{
get => BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id).overriddenColour;
set
{
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id);
color.overriddenColour = value;
color.overridePaletteColour = true;
color.needsUpdate = true;
}
}
/// <summary>
/// The short text displayed on the block if applicable, or null.
/// Setting it is temporary to the session, it won't be saved.
/// </summary>
public string Label
{
get => BlockEngine.GetBlockInfo<TextLabelEntityViewStruct>(Id).textLabelComponent?.text;
set
{
ref var text = ref BlockEngine.GetBlockInfo<TextLabelEntityViewStruct>(Id);
if (text.textLabelComponent != null) text.textLabelComponent.text = value;
}
}
/// <summary>
/// Whether the block exists. The other properties will return a default value if the block doesn't exist.
/// </summary>
public bool Exists => BlockEngine.BlockExists(Id);
/// <summary>
/// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist.
/// </summary>
public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id);
/// <summary>
/// Removes this block.
/// </summary>
/// <returns>True if the block exists and could be removed.</returns>
public bool Remove() => RemovalEngine.RemoveBlock(Id);
/// <summary>
/// Returns the rigid body of the cluster of blocks this one belongs to during simulation.
/// Can be used to apply forces or move the block around while the simulation is running.
/// </summary>
/// <returns>The SimBody of the cluster</returns>
public SimBody GetSimBody()
{
uint id = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(Id).machineRigidBodyId;
return new SimBody(id);
}
public override string ToString()
{
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Type)}: {Type}, {nameof(Color)}: {Color}, {nameof(Exists)}: {Exists}";
}
public static void Init()
{
GameEngineManager.AddGameEngine(PlacementEngine);
GameEngineManager.AddGameEngine(MovementEngine);
GameEngineManager.AddGameEngine(RotationEngine);
GameEngineManager.AddGameEngine(RemovalEngine);
GameEngineManager.AddGameEngine(BlockEngine);
}
/// <summary>
/// Convert the block to a specialised block class.
/// </summary>
/// <returns>The block.</returns>
/// <typeparam name="T">The specialised block type.</typeparam>
public T Specialise<T>() where T : Block
{
// What have I gotten myself into?
// C# can't cast to a child of Block unless the object was originally that child type
// And C# doesn't let me make implicit cast operators for child types
// So thanks to Microsoft, we've got this horrible implementation using reflection
ConstructorInfo ctor = typeof(T).GetConstructor(types: new System.Type[] { typeof(EGID) });
if (ctor == null)
{
throw new BlockSpecializationException("Specialized block constructor does not accept an EGID");
}
return (T)ctor.Invoke(new object[] { Id });
}
#if DEBUG
public static EntitiesDB entitiesDB
{
get
{
return BlockEngine.GetEntitiesDB();
}
}
#endif
}
}

View file

@ -0,0 +1,31 @@
namespace GamecraftModdingAPI.Blocks
{
public struct BlockColor
{
public BlockColors Color;
public byte Darkness;
public override string ToString()
{
return $"{nameof(Color)}: {Color}, {nameof(Darkness)}: {Darkness}";
}
}
/// <summary>
/// Preset block colours
/// </summary>
public enum BlockColors
{
Default = byte.MaxValue,
White = 0,
Pink,
Purple,
Blue,
Aqua,
Green,
Lime,
Yellow,
Orange,
Red
}
}

View file

@ -0,0 +1,152 @@
using System.Collections.Generic;
using Gamecraft.Wires;
using RobocraftX.Blocks;
using RobocraftX.Common;
using RobocraftX.GUI.Hotbar.Colours;
using RobocraftX.Physics;
using RobocraftX.Scene.Simulation;
using Svelto.DataStructures;
using Svelto.ECS;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine for executing general block actions
/// </summary>
public class BlockEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
public void Dispose()
{
}
public void Ready()
{
}
public Block[] GetConnectedBlocks(EGID blockID)
{
if (!BlockExists(blockID)) return new Block[0];
Stack<uint> cubeStack = new Stack<uint>();
FasterList<uint> cubesToProcess = new FasterList<uint>();
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID.entityID, cubeStack, cubesToProcess, (in GridConnectionsEntityStruct g) => { return false; });
var ret = new Block[cubesToProcess.count];
for (int i = 0; i < cubesToProcess.count; i++)
ret[i] = new Block(cubesToProcess[i]);
return ret;
}
public void SetBlockColorFromPalette(ref ColourParameterEntityStruct color)
{
ref var paletteEntry = ref entitiesDB.QueryEntity<PaletteEntryEntityStruct>(color.indexInPalette,
CommonExclusiveGroups.COLOUR_PALETTE_GROUP);
color.paletteColour = paletteEntry.Colour;
}
/// <summary>
/// Get a struct of a block. Can be used to set properties.
/// Returns a default value if not found.
/// </summary>
/// <param name="blockID">The block's ID</param>
/// <typeparam name="T">The struct to query</typeparam>
/// <returns>An editable reference to the struct</returns>
public ref T GetBlockInfo<T>(EGID blockID) where T : struct, IEntityComponent
{
if (entitiesDB.Exists<T>(blockID))
return ref entitiesDB.QueryEntity<T>(blockID);
T[] structHolder = new T[1]; //Create something that can be referenced
return ref structHolder[0]; //Gets a default value automatically
}
/// <summary>
/// Get a struct of a block. Can be used to set properties.
/// Returns a default value if not found.
/// </summary>
/// <param name="blockID">The block's ID</param>
/// <param name="exists">Whether the specified struct exists for the block</param>
/// <typeparam name="T">The struct to query</typeparam>
/// <returns>An editable reference to the struct</returns>
public ref T GetBlockInfo<T>(EGID blockID, out bool exists) where T : struct, IEntityComponent
{
exists = entitiesDB.Exists<T>(blockID);
if (exists)
return ref entitiesDB.QueryEntity<T>(blockID);
T[] structHolder = new T[1];
//ref T defRef = ref structHolder[0];
return ref structHolder[0];
}
public bool BlockExists(EGID id)
{
return entitiesDB.Exists<DBEntityStruct>(id);
}
public bool GetBlockInfoExists<T>(EGID blockID) where T : struct, IEntityComponent
{
return entitiesDB.Exists<T>(blockID);
}
public SimBody[] GetSimBodiesFromID(byte id)
{
var ret = new FasterList<SimBody>(4);
if (!entitiesDB.HasAny<ObjectIdEntityStruct>(CommonExclusiveGroups.OWNED_BLOCKS_GROUP))
return new SimBody[0];
var oids = entitiesDB.QueryEntities<ObjectIdEntityStruct>(CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
var connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
foreach (ref ObjectIdEntityStruct oid in oids)
{
if (oid.objectId != id) continue;
var rid = connections.Entity(oid.ID.entityID).machineRigidBodyId;
foreach (var rb in ret)
{
if (rb.Id.entityID == rid)
goto DUPLICATE; //Multiple Object Identifiers on one rigid body
}
ret.Add(new SimBody(rid));
DUPLICATE: ;
}
return ret.ToArray();
}
public ObjectIdentifier[] GetObjectIDsFromID(byte id)
{
var ret = new FasterList<ObjectIdentifier>(4);
if (!entitiesDB.HasAny<ObjectIdEntityStruct>(CommonExclusiveGroups.OWNED_BLOCKS_GROUP))
return new ObjectIdentifier[0];
var oids = entitiesDB.QueryEntities<ObjectIdEntityStruct>(CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
foreach (ref ObjectIdEntityStruct oid in oids)
if (oid.objectId == id)
ret.Add(new ObjectIdentifier(oid.ID));
return ret.ToArray();
}
public SimBody[] GetConnectedSimBodies(uint id)
{
var joints = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP);
var list = new FasterList<SimBody>(4);
foreach (var joint in joints)
{
if (joint.jointState == JointState.Broken) continue;
if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB));
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));
}
return list.ToArray();
}
#if DEBUG
public EntitiesDB GetEntitiesDB()
{
return entitiesDB;
}
#endif
}
}

View file

@ -1,10 +1,10 @@
using System; using System;
using TechbloxModdingAPI; using GamecraftModdingAPI;
namespace TechbloxModdingAPI.Blocks namespace GamecraftModdingAPI.Blocks
{ {
public class BlockException : TechbloxModdingAPIException public class BlockException : GamecraftModdingAPIException
{ {
public BlockException() public BlockException()
{ {
@ -41,24 +41,13 @@ namespace TechbloxModdingAPI.Blocks
} }
} }
public class WiringException : BlockException public class BlockDoesNotExistException : BlockException
{ {
public WiringException() public BlockDoesNotExistException()
{ {
} }
public WiringException(string message) : base(message) public BlockDoesNotExistException(string message) : base(message)
{
}
}
public class WireInvalidException : WiringException
{
public WireInvalidException()
{
}
public WireInvalidException(string message) : base(message)
{ {
} }
} }

View file

@ -0,0 +1,239 @@
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Possible block types
/// </summary>
public enum BlockIDs : ushort
{
/// <summary>
/// A custom value for the API. Doesn't exist for Gamecraft.
/// </summary>
Invalid = ushort.MaxValue,
AluminiumCube = 0,
AxleS,
HingeS = 3,
MotorS,
HingeM,
MotorM,
TyreM,
AxleM,
IronCube,
RubberCube,
OiledCube,
AluminiumConeSegment, //12
AluminiumCorner,
AluminiumRoundedCorner,
AluminiumSlicedCube,
AluminiumRoundedSlicedCube,
AluminiumCylinder,
AluminiumPyramidSegment,
AluminiumSlope,
AluminiumRoundedSlope,
AluminiumSphere,
RubberConeSegment, //22
RubberCorner,
RubberRoundedCorner,
RubberSlicedCube,
RubberRoundedSlicedCube,
RubberCylinder,
RubberPyramidSegment,
RubberSlope,
RubberRoundedSlope,
RubberSphere,
OiledConeSegment, //32
OiledCorner,
OiledRoundedCorner,
OiledSlicedCube,
OiledRoundedSlicedCube,
OiledCylinder,
OiledPyramidSegment,
OiledSlope,
OiledRoundedSlope,
OiledSphere,
IronConeSegment, //42
IronCorner,
IronRoundedCorner,
IronSlicedCube,
IronRoundedSlicedCube,
IronCylinder,
IronPyramidSegment,
IronSlope,
IronRoundedSlope,
IronSphere,
GlassCube, //52
GlassSlicedCube,
GlassSlope,
GlassCorner,
GlassPyramidSegment,
GlassRoundedSlicedCube,
GlassRoundedSlope,
GlassRoundedCorner,
GlassConeSegment,
GlassCylinder,
GlassSphere,
Lever, //63 - two IDs skipped
PlayerSpawn = 66, //Crashes without special handling
SmallSpawn,
MediumSpawn,
LargeSpawn,
BallJoint,
UniversalJoint,
ServoAxle,
ServoHinge,
StepperAxle,
StepperHinge,
TelescopicJoint,
DampedSpring,
ServoPiston,
StepperPiston,
PneumaticPiston,
PneumaticHinge,
PneumaticAxle, //82
PilotSeat = 90, //Might crash
PassengerSeat,
PilotControls,
GrassCube,
DirtCube,
GrassConeSegment,
GrassCorner,
GrassRoundedCorner,
GrassSlicedCube,
GrassRoundedSlicedCube,
GrassPyramidSegment,
GrassSlope,
GrassRoundedSlope,
DirtConeSegment,
DirtCorner,
DirtRoundedCorner,
DirtSlicedCube,
DirtRoundedSlicedCube,
DirtPyramidSegment,
DirtSlope,
DirtRoundedSlope,
RubberHemisphere,
AluminiumHemisphere,
GrassInnerCornerBulged,
DirtInnerCornerBulged,
IronHemisphere,
OiledHemisphere,
GlassHemisphere,
TyreS,
ThreeWaySwitch,
Dial, //120
CharacterOnEnterTrigger, //Probably crashes
CharacterOnLeaveTrigger,
CharacterOnStayTrigger,
ObjectOnEnterTrigger,
ObjectOnLeaveTrigger,
ObjectOnStayTrigger,
Button,
Switch,
TextBlock, //Brings up a screen
ConsoleBlock, //Brings up a screen
Door,
GlassDoor,
PoweredDoor,
PoweredGlassDoor,
AluminiumTubeCorner,
IronTubeCorner,
WoodCube,
WoodSlicedCube,
WoodSlope,
WoodCorner,
WoodPyramidSegment,
WoodConeSegment,
WoodRoundedSlicedCube,
WoodRoundedSlope,
WoodRoundedCorner,
WoodCylinder,
WoodHemisphere,
WoodSphere,
BrickCube, //149
BrickSlicedCube = 151,
BrickSlope,
BrickCorner,
ConcreteCube,
ConcreteSlicedCube,
ConcreteSlope,
ConcreteCorner,
RoadCarTyre,
OffRoadCarTyre,
RacingCarTyre,
BicycleTyre,
FrontBikeTyre,
RearBikeTyre,
ChopperBikeTyre,
TractorTyre,
MonsterTruckTyre,
MotocrossBikeTyre,
CartTyre, //168
ObjectIdentifier,
ANDLogicBlock,
NANDLogicBlock,
NORLogicBlock,
NOTLogicBlock,
ORLogicBlock,
XNORLogicBlock,
XORLogicBlock,
AbsoluteMathsBlock,
AdderMathsBlock,
DividerMathsBlock,
SignMathsBlock, //180
MaxMathsBlock,
MinMathsBlock,
MultiplierMathsBlock,
SubtractorMathsBlock,
SimpleConnector,
MeanMathsBlock,
Bit,
Counter,
Timer,
ObjectFilter,
PlayerFilter,
TeamFilter,
Number2Text, //193
BeachTree1 = 200,
BeachTree2,
BeachTree3,
Rock1,
Rock2,
Rock3,
Rock4,
BirchTree1,
BirchTree2,
BirchTree3,
PineTree1,
PineTree2,
PineTree3,
Flower1,
Flower2,
Flower3,
Shrub1,
Shrub2,
Shrub3,
CliffCube,
CliffSlicedCorner,
CliffCornerA,
CliffCornerB,
CliffSlopeA,
CliffSlopeB,
GrassEdge,
GrassEdgeInnerCorner,
GrassEdgeCorner,
GrassEdgeSlope,
CentreHUD,
ObjectiveHUD,
GameStatsHUD, //231
GameOverBlock,
MovementConstrainer = 246,
RotationConstrainer,
AdvancedMovementDampener,
AdvancedRotationDampener,
Mover = 250,
Rotator,
MovementDampener,
RotationDampener,
AdvancedMover,
AdvancedRotator
}
}

View file

@ -0,0 +1,42 @@
using Svelto.ECS;
using RobocraftX.Common;
using HarmonyLib;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// ExclusiveGroups and IDs used with blocks
/// </summary>
public static class BlockIdentifiers
{
/// <summary>
/// Blocks placed by the player
/// </summary>
public static ExclusiveGroup OWNED_BLOCKS { get { return CommonExclusiveGroups.OWNED_BLOCKS_GROUP; } }
/// <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.BLOCKS_DISABLED_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
{
return ((uint) AccessTools.Field(typeof(CommonExclusiveGroups), "_nextBlockEntityID").GetValue(null)) - 1;
}
}
}
}

View file

@ -0,0 +1,89 @@
using System;
using RobocraftX.Blocks;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class ConsoleBlock : Block
{
public static ConsoleBlock PlaceNew(float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(BlockIDs.ConsoleBlock, color, darkness,
position, uscale, scale, player, rotation);
return new ConsoleBlock(id);
}
return null;
}
public ConsoleBlock(EGID id): base(id)
{
if (!BlockEngine.GetBlockInfoExists<ConsoleBlockEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public ConsoleBlock(uint id): base(id)
{
if (!BlockEngine.GetBlockInfoExists<ConsoleBlockEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom console block properties
public string Command
{
get
{
return BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).commandName;
}
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).commandName.Set(value);
}
}
public string Arg1
{
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg1;
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg1.Set(value);
}
}
public string Arg2
{
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg2;
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg2.Set(value);
}
}
public string Arg3
{
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg3;
set
{
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg3.Set(value);
}
}
}
}

View file

@ -0,0 +1,105 @@
using System;
using RobocraftX.Blocks;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Motor : Block
{
/// <summary>
/// Places a new motor.
/// Any valid motor type is accepted.
/// This re-implements Block.PlaceNew(...)
/// </summary>
public static new Motor PlaceNew(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (!(block == BlockIDs.MotorS || block == BlockIDs.MotorM))
{
throw new BlockTypeException($"Block is not a {typeof(Motor).Name} block");
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation);
return new Motor(id);
}
return null;
}
public Motor(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<MotorReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Motor(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<MotorReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom motor properties
/// <summary>
/// The motor's maximum rotational velocity.
/// </summary>
public float TopSpeed
{
get
{
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).maxVelocity;
}
set
{
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
motor.maxVelocity = value;
}
}
/// <summary>
/// The motor's maximum rotational force.
/// </summary>
public float Torque
{
get
{
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).maxForce;
}
set
{
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
motor.maxForce = value;
}
}
/// <summary>
/// The motor's direction.
/// </summary>
public bool Reverse
{
get
{
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).reverse;
}
set
{
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
motor.reverse = value;
}
}
}
}

View file

@ -0,0 +1,65 @@
using RobocraftX.Common;
using RobocraftX.UECS;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
using Unity.Transforms;
using Unity.Mathematics;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine which executes block movement actions
/// </summary>
public class MovementEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIMovementGameEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
public bool IsInGame = false;
public void Dispose()
{
IsInGame = false;
}
public void Ready()
{
IsInGame = true;
}
// implementations for Movement static class
public float3 MoveBlock(uint blockID, float3 vector)
{
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
// main (persistent) position
posStruct.position = vector;
// placement grid position
gridStruct.position = vector;
// rendered position
transStruct.position = vector;
// collision position
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation
{
Value = posStruct.position
});
entitiesDB.QueryEntity<GridConnectionsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP).isProcessed = false;
return posStruct.position;
}
public float3 GetPosition(uint blockID)
{
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
return posStruct.position;
}
}
}

View file

@ -0,0 +1,41 @@
using Gamecraft.Wires;
using Svelto.ECS;
namespace GamecraftModdingAPI.Blocks
{
public class ObjectIdentifier : Block
{
public ObjectIdentifier(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<ObjectIdEntityStruct>(Id))
{
throw new BlockTypeException($"Block is not a {GetType().Name} block");
}
}
public ObjectIdentifier(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<ObjectIdEntityStruct>(Id))
{
throw new BlockTypeException($"Block is not a {GetType().Name} block");
}
}
public char Identifier
{
get => (char) (BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).objectId + 'A');
set
{
BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).objectId = (byte) (value - 'A');
Label = value + ""; //The label isn't updated automatically
}
}
/// <summary>
/// Finds the identfier 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 ObjectIdentifier[] GetByID(char id) => BlockEngine.GetObjectIDsFromID((byte) (id - 'A'));
}
}

View file

@ -0,0 +1,82 @@
using System;
using RobocraftX.Blocks;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Piston : Block
{
/// <summary>
/// Places a new piston.
/// Any valid piston type is accepted.
/// This re-implements Block.PlaceNew(...)
/// </summary>
public static new Piston PlaceNew(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (!(block == BlockIDs.ServoPiston || block == BlockIDs.StepperPiston || block == BlockIDs.PneumaticPiston))
{
throw new BlockTypeException($"Block is not a {typeof(Piston).Name} block");
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation);
return new Piston(id);
}
return null;
}
public Piston(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<PistonReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Piston(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<PistonReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom piston properties
/// <summary>
/// The piston's max extension distance.
/// </summary>
public float MaximumExtension
{
get => BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id).maxDeviation;
set
{
ref PistonReadOnlyStruct piston = ref BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id);
piston.maxDeviation = value;
}
}
/// <summary>
/// The piston's max extension force.
/// </summary>
public float MaximumForce
{
get => BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id).maxForce;
set
{
ref PistonReadOnlyStruct piston = ref BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id);
piston.maxForce = value;
}
}
}
}

View file

@ -0,0 +1,144 @@
using System;
using System.Reflection;
using DataLoader;
using HarmonyLib;
using RobocraftX.Blocks;
using RobocraftX.Blocks.Scaling;
using RobocraftX.Character;
using RobocraftX.Common;
using RobocraftX.CR.MachineEditing;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
using Unity.Mathematics;
using UnityEngine;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Players;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine which executes block placement actions
/// </summary>
public class PlacementEngine : IApiEngine
{
public bool IsInGame = false;
public void Dispose()
{
IsInGame = false;
}
public void Ready()
{
IsInGame = true;
}
public EntitiesDB entitiesDB { get; set; }
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine
public EGID PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale,
float3 scale, Player player, float3 rotation)
{ //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one
if (darkness > 9)
throw new Exception("That is too dark. Make sure to use 0-9 as darkness. (0 is default.)");
return BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation,
(player ?? new Player(PlayerType.Local)).Id);
}
private EGID BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
{
if (_blockEntityFactory == null)
throw new Exception("The factory is null.");
if (uscale < 1)
throw new Exception("Scale needs to be at least 1");
if (scale.x < 4e-5) scale.x = uscale;
if (scale.y < 4e-5) scale.y = uscale;
if (scale.z < 4e-5) scale.z = uscale;
uint dbid = block;
if (!PrefabsID.DBIDMAP.ContainsKey(dbid))
throw new Exception("Block with ID " + dbid + " not found!");
//RobocraftX.CR.MachineEditing.PlaceBlockEngine
ScalingEntityStruct scaling = new ScalingEntityStruct {scale = scale};
Quaternion rotQ = Quaternion.Euler(rot);
RotationEntityStruct rotation = new RotationEntityStruct {rotation = rotQ};
GridRotationStruct gridRotation = new GridRotationStruct
{position = position, rotation = rotQ};
CubeCategoryStruct category = new CubeCategoryStruct
{category = CubeCategory.General, type = CubeType.Block};
DBEntityStruct dbEntity = new DBEntityStruct {DBID = dbid};
BlockPlacementScaleEntityStruct placementScale = new BlockPlacementScaleEntityStruct
{
blockPlacementHeight = uscale, blockPlacementWidth = uscale, desiredScaleFactor = uscale,
snapGridScale = uscale,
unitSnapOffset = 0, isUsingUnitSize = true
};
EquippedColourStruct colour = new EquippedColourStruct {indexInPalette = color};
EGID newBlockID;
switch (category.category)
{
case CubeCategory.SpawnPoint:
case CubeCategory.BuildingSpawnPoint:
newBlockID = MachineEditingGroups.NewUncheckedBlockEGID;
break;
default:
newBlockID = MachineEditingGroups.NewBlockID;
break;
}
EntityComponentInitializer
structInitializer =
_blockEntityFactory.Build(newBlockID, dbid); //The ghost block index is only used for triggers
if (colour.indexInPalette != byte.MaxValue)
structInitializer.Init(new ColourParameterEntityStruct
{
indexInPalette = colour.indexInPalette,
needsUpdate = true
});
uint prefabId = PrefabsID.GetPrefabId(dbid, 0);
structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId));
structInitializer.Init(new PhysicsPrefabEntityStruct(prefabId));
structInitializer.Init(dbEntity);
structInitializer.Init(new PositionEntityStruct {position = position});
structInitializer.Init(rotation);
structInitializer.Init(scaling);
structInitializer.Init(gridRotation);
structInitializer.Init(new UniformBlockScaleEntityStruct
{
scaleFactor = placementScale.desiredScaleFactor
});
structInitializer.Init(new BlockPlacementInfoStruct()
{
loadedFromDisk = false,
placedBy = playerId
});
PrimaryRotationUtility.InitialisePrimaryDirection(rotation.rotation, ref structInitializer);
EGID playerEGID = new EGID(playerId, CharacterExclusiveGroups.OnFootGroup);
ref PickedBlockExtraDataStruct pickedBlock = ref entitiesDB.QueryEntity<PickedBlockExtraDataStruct>(playerEGID);
pickedBlock.placedBlockEntityID = playerEGID;
pickedBlock.placedBlockWasAPickedBlock = false;
return newBlockID;
}
public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine";
public bool isRemovable => false;
[HarmonyPatch]
public class FactoryObtainerPatch
{
static void Postfix(BlockEntityFactory blockEntityFactory)
{
_blockEntityFactory = blockEntityFactory;
Logging.MetaDebugLog("Block entity factory injected.");
}
static MethodBase TargetMethod(Harmony instance)
{
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlockEngine").GetConstructors()[0];
}
}
}
}

View file

@ -0,0 +1,60 @@
using System.Reflection;
using HarmonyLib;
using RobocraftX.Blocks;
using RobocraftX.Common;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
public class RemovalEngine : IApiEngine
{
private static IEntityFunctions _entityFunctions;
private static MachineGraphConnectionEntityFactory _connectionFactory;
public bool RemoveBlock(EGID target)
{
if (!entitiesDB.Exists<MachineGraphConnectionsEntityStruct>(target))
return false;
var connections = entitiesDB.QueryEntity<MachineGraphConnectionsEntityStruct>(target);
for (int i = connections.connections.Length - 1; i >= 0; i--)
_connectionFactory.RemoveConnection(connections, i, entitiesDB);
_entityFunctions.RemoveEntity<BlockEntityDescriptor>(target);
return true;
}
public void Ready()
{
}
public EntitiesDB entitiesDB { get; set; }
public void Dispose()
{
}
public string Name { get; } = "GamecraftModdingAPIRemovalGameEngine";
public bool isRemovable => false;
[HarmonyPatch]
public class FactoryObtainerPatch
{
static void Postfix(IEntityFunctions entityFunctions,
MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
{
_entityFunctions = entityFunctions;
_connectionFactory = machineGraphConnectionEntityFactory;
Logging.MetaDebugLog("Requirements injected.");
}
static MethodBase TargetMethod(Harmony instance)
{
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine").GetConstructors()[0];
}
}
}
}

View file

@ -0,0 +1,72 @@
using RobocraftX.Common;
using RobocraftX.UECS;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
using Unity.Mathematics;
using UnityEngine;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine which executes block movement actions
/// </summary>
public class RotationEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIRotationGameEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
public bool IsInGame = false;
public void Dispose()
{
IsInGame = false;
}
public void Ready()
{
IsInGame = true;
}
// implementations for Rotation static class
public float3 RotateBlock(uint blockID, Vector3 vector)
{
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity<RotationEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
// main (persistent) position
Quaternion newRotation = (Quaternion)rotStruct.rotation;
newRotation.eulerAngles += vector;
rotStruct.rotation = (quaternion)newRotation;
// placement grid rotation
Quaternion newGridRotation = (Quaternion)gridStruct.rotation;
newGridRotation.eulerAngles += vector;
gridStruct.rotation = (quaternion)newGridRotation;
// rendered position
Quaternion newTransRotation = (Quaternion)rotStruct.rotation;
newTransRotation.eulerAngles += vector;
transStruct.rotation = newTransRotation;
// collision position
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Unity.Transforms.Rotation
{
Value = rotStruct.rotation
});
entitiesDB.QueryEntity<GridConnectionsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP).isProcessed = false;
return ((Quaternion)rotStruct.rotation).eulerAngles;
}
public float3 GetRotation(uint blockID)
{
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntity<RotationEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
return ((Quaternion) rotStruct.rotation).eulerAngles;
}
}
}

View file

@ -0,0 +1,110 @@
using System;
using RobocraftX.Blocks;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Servo : Block
{
/// <summary>
/// Places a new servo.
/// Any valid servo type is accepted.
/// This re-implements Block.PlaceNew(...)
/// </summary>
public static new Servo PlaceNew(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (!(block == BlockIDs.ServoAxle || block == BlockIDs.ServoHinge || block == BlockIDs.ServoPiston))
{
throw new BlockTypeException($"Block is not a {nameof(Servo)} block");
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation);
return new Servo(id);
}
return null;
}
public Servo(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<ServoReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Servo(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<ServoReadOnlyStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom servo properties
/// <summary>
/// The servo's minimum angle.
/// </summary>
public float MinimumAngle
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).minDeviation;
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.minDeviation = value;
}
}
/// <summary>
/// The servo's maximum angle.
/// </summary>
public float MaximumAngle
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).maxDeviation;
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.maxDeviation = value;
}
}
/// <summary>
/// The servo's maximum force.
/// </summary>
public float MaximumForce
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).maxForce;
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.maxForce = value;
}
}
/// <summary>
/// The servo's direction.
/// </summary>
public bool Reverse
{
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).reverse;
set
{
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
servo.reverse = value;
}
}
}
}

View file

@ -0,0 +1,204 @@
using Svelto.ECS;
using Gamecraft.Wires;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine which executes signal actions
/// </summary>
public class SignalEngine : IApiEngine
{
public const float POSITIVE_HIGH = 1.0f;
public const float NEGATIVE_HIGH = -1.0f;
public const float HIGH = 1.0f;
public const float ZERO = 0.0f;
public string Name { get; } = "GamecraftModdingAPISignalGameEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
public bool IsInGame = false;
public void Dispose()
{
IsInGame = false;
}
public void Ready()
{
IsInGame = true;
}
// implementations for Signal static class
public bool SetSignal(EGID blockID, float signal, out uint signalID, bool input = true)
{
signalID = GetSignalIDs(blockID, input)[0];
return SetSignal(signalID, signal);
}
public bool SetSignal(uint signalID, float signal, bool input = true)
{
var array = GetSignalStruct(signalID, out uint index, input);
if (array != null) array[index].valueAsFloat = signal;
return false;
}
public float AddSignal(EGID blockID, float signal, out uint signalID, bool clamp = true, bool input = true)
{
signalID = GetSignalIDs(blockID, input)[0];
return AddSignal(signalID, signal, clamp, input);
}
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
{
var array = GetSignalStruct(signalID, out uint index, input);
if (array != null)
{
ref var channelData = ref array[index];
channelData.valueAsFloat += signal;
if (clamp)
{
if (channelData.valueAsFloat > POSITIVE_HIGH)
{
channelData.valueAsFloat = POSITIVE_HIGH;
}
else if (channelData.valueAsFloat < NEGATIVE_HIGH)
{
channelData.valueAsFloat = NEGATIVE_HIGH;
}
return channelData.valueAsFloat;
}
}
return signal;
}
public float GetSignal(EGID blockID, out uint signalID, bool input = true)
{
signalID = GetSignalIDs(blockID, input)[0];
return GetSignal(signalID, input);
}
public float GetSignal(uint signalID, bool input = true)
{
var array = GetSignalStruct(signalID, out uint index, input);
return array?[index].valueAsFloat ?? 0f;
}
public uint[] GetSignalIDs(EGID blockID, bool input = true)
{
ref BlockPortsStruct bps = ref entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
uint[] signals;
if (input) {
signals = new uint[bps.inputCount];
for (uint i = 0u; i < bps.inputCount; i++)
{
signals[i] = bps.firstInputID + i;
}
} else {
signals = new uint[bps.outputCount];
for (uint i = 0u; i < bps.outputCount; i++)
{
signals[i] = bps.firstOutputID + i;
}
}
return signals;
}
public EGID[] GetSignalInputs(EGID blockID)
{
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
EGID[] inputs = new EGID[ports.inputCount];
for (uint i = 0; i < ports.inputCount; i++)
{
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<InputPortsGroup>.Group);
}
return inputs;
}
public EGID[] GetSignalOutputs(EGID blockID)
{
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
EGID[] outputs = new EGID[ports.outputCount];
for (uint i = 0; i < ports.outputCount; i++)
{
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<OutputPortsGroup>.Group);
}
return outputs;
}
public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
{
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
WireEntityStruct[] wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group).ToFastAccess(out uint count);
for (uint i = 0; i < count; i++)
{
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
{
exists = true;
return ref wires[i];
}
}
exists = false;
WireEntityStruct[] defRef = new WireEntityStruct[1];
return ref defRef[0];
}
public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists)
{
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
ChannelDataStruct[] channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group).ToFastAccess(out uint count);
if (port.firstChannelIndexCachedInSim < count)
{
exists = true;
return ref channels[port.firstChannelIndexCachedInSim];
}
exists = false;
ChannelDataStruct[] defRef = new ChannelDataStruct[1];
return ref defRef[0];
}
public EGID[] GetElectricBlocks()
{
uint count = entitiesDB.Count<BlockPortsStruct>(BlockIdentifiers.OWNED_BLOCKS) + entitiesDB.Count<BlockPortsStruct>(BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
uint i = 0;
EGID[] res = new EGID[count];
foreach (ref BlockPortsStruct s in entitiesDB.QueryEntities<BlockPortsStruct>(BlockIdentifiers.OWNED_BLOCKS))
{
res[i] = s.ID;
i++;
}
foreach (ref BlockPortsStruct s in entitiesDB.QueryEntities<BlockPortsStruct>(BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS))
{
res[i] = s.ID;
i++;
}
return res;
}
private ChannelDataStruct[] GetSignalStruct(uint signalID, out uint index, bool input = true)
{
ExclusiveGroup group = input
? NamedExclusiveGroup<InputPortsGroup>.Group
: NamedExclusiveGroup<OutputPortsGroup>.Group;
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
{
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex;
ChannelDataStruct[] channelData = entitiesDB
.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group)
.ToFastAccess(out uint _);
return channelData;
}
index = 0;
return null;
}
}
}

View file

@ -0,0 +1,123 @@
using System;
using Gamecraft.Wires;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Common implementation for blocks that support wiring.
/// </summary>
public class SignalingBlock : Block
{
/// <summary>
/// Places a new signaling block.
/// Any valid functional block type with IO ports will work.
/// This re-implements Block.PlaceNew(...)
/// </summary>
public static new SignalingBlock PlaceNew(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation);
return new SignalingBlock(id);
}
return null;
}
public SignalingBlock(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public SignalingBlock(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
protected ref BlockPortsStruct GetBlockPortsStruct()
{
return ref BlockEngine.GetBlockInfo<BlockPortsStruct>(Id);
}
/// <summary>
/// Generates the input port identifiers.
/// </summary>
/// <returns>The input identifiers.</returns>
protected EGID[] GetInputIds()
{
return SignalEngine.GetSignalInputs(Id);
}
/// <summary>
/// Generates the output port identifiers.
/// </summary>
/// <returns>The output identifiers.</returns>
protected EGID[] GetOutputIds()
{
return SignalEngine.GetSignalOutputs(Id);
}
/// <summary>
/// Gets the port struct.
/// </summary>
/// <returns>The port struct.</returns>
/// <param name="portId">Port identifier.</param>
protected ref PortEntityStruct GetPortStruct(EGID portId)
{
return ref BlockEngine.GetBlockInfo<PortEntityStruct>(portId);
}
/// <summary>
/// Gets the connected wire.
/// </summary>
/// <returns>The connected wire.</returns>
/// <param name="portId">Port identifier.</param>
/// <param name="connected">Whether the port has a wire connected to it.</param>
protected ref WireEntityStruct GetConnectedWire(EGID portId, out bool connected)
{
return ref SignalEngine.MatchPortToWire(portId, Id, out connected);
}
/// <summary>
/// [EXPERIMENTAL] Gets the channel data.
/// </summary>
/// <returns>The channel data.</returns>
/// <param name="portId">Port identifier.</param>
/// <param name="exists">Whether the channel actually exists.</param>
protected ref ChannelDataStruct GetChannelData(EGID portId, out bool exists)
{
return ref SignalEngine.GetChannelDataStruct(portId, out exists);
}
/// <summary>
/// The input port count.
/// </summary>
public uint InputCount
{
get => GetBlockPortsStruct().inputCount;
}
/// <summary>
/// The output port count.
/// </summary>
public uint OutputCount
{
get => GetBlockPortsStruct().outputCount;
}
}
}

View file

@ -0,0 +1,124 @@
using System;
using RobocraftX.Blocks;
using Gamecraft.CharacterVulnerability;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class SpawnPoint : Block
{
/// <summary>
/// Places a new spawn point.
/// Any valid spawn block type is accepted.
/// This re-implements Block.PlaceNew(...)
/// </summary>
public static new SpawnPoint PlaceNew(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (!(block == BlockIDs.LargeSpawn || block == BlockIDs.SmallSpawn || block == BlockIDs.MediumSpawn || block == BlockIDs.PlayerSpawn))
{
throw new BlockTypeException($"Block is not a {nameof(SpawnPoint)} block");
}
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation);
return new SpawnPoint(id);
}
return null;
}
public SpawnPoint(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<SpawnPointStatsEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public SpawnPoint(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<SpawnPointStatsEntityStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom spawn point properties
/// <summary>
/// The lives the player spawns in with.
/// </summary>
public uint Lives
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).lives;
}
set
{
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
spses.lives = value;
}
}
/// <summary>
/// Whether the spawned player can take damage.
/// </summary>
public bool Damageable
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).canTakeDamage;
}
set
{
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
spses.canTakeDamage = value;
}
}
/// <summary>
/// Whether the game over screen will be displayed
/// </summary>
public bool GameOverEnabled
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).gameOverScreen;
}
set
{
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
spses.gameOverScreen = value;
}
}
/// <summary>
/// The team id for players who spawn here.
/// </summary>
public byte Team
{
get
{
return BlockEngine.GetBlockInfo<SpawnPointIdsEntityStruct>(Id).teamId;
}
set
{
ref SpawnPointIdsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointIdsEntityStruct>(Id);
spses.teamId = value;
}
}
}
}

View file

@ -0,0 +1,83 @@
using System;
using Gamecraft.Blocks.GUI;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class TextBlock : Block
{
public static TextBlock PlaceNew(float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(BlockIDs.TextBlock, color, darkness,
position, uscale, scale, player, rotation);
return new TextBlock(id);
}
return null;
}
public TextBlock(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<TextBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public TextBlock(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<TextBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom text block properties
/// <summary>
/// The text block's current text.
/// </summary>
public string Text
{
get
{
return BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textCurrent;
}
set
{
ref TextBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id);
tbds.textCurrent.Set(value);
tbds.textStored.Set(value);
BlockEngine.GetBlockInfo<TextBlockNetworkDataStruct>(Id).newTextBlockStringContent.Set(value);
}
}
/// <summary>
/// The text block's current text block ID (used in ChangeTextBlockCommand).
/// </summary>
public string TextBlockId
{
get
{
return BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textBlockID;
}
set
{
BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textBlockID.Set(value);
BlockEngine.GetBlockInfo<TextBlockNetworkDataStruct>(Id).newTextBlockID.Set(value);
}
}
}
}

View file

@ -0,0 +1,118 @@
using System;
using RobocraftX.Blocks;
using Gamecraft.Blocks.TimerBlock;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Timer : Block
{
/// <summary>
/// Places a new timer block.
/// </summary>
public static Timer PlaceNew(float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null)
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
EGID id = PlacementEngine.PlaceBlock(BlockIDs.Timer, color, darkness,
position, uscale, scale, player, rotation);
return new Timer(id);
}
return null;
}
public Timer(EGID id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<TimerBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
public Timer(uint id) : base(id)
{
if (!BlockEngine.GetBlockInfoExists<TimerBlockDataStruct>(this.Id))
{
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
}
}
// custom timer properties
/// <summary>
/// The player-specified start time.
/// </summary>
public float Start
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).startTime;
}
set
{
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
tbds.startTime = value;
}
}
/// <summary>
/// The player-specified end time.
/// </summary>
public float End
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).endTime;
}
set
{
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
tbds.endTime = value;
}
}
/// <summary>
/// Whether to display time with millisecond precision.
/// </summary>
public bool DisplayMilliseconds
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).outputFormatHasMS;
}
set
{
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
tbds.outputFormatHasMS = value;
}
}
/// <summary>
/// Current time (as of the last video frame), in milliseconds.
/// </summary>
public int CurrentTime
{
get
{
return BlockEngine.GetBlockInfo<TimerBlockLabelCacheEntityStruct>(Id).timeLastRenderFrameMS;
}
set
{
ref TimerBlockLabelCacheEntityStruct tblces = ref BlockEngine.GetBlockInfo<TimerBlockLabelCacheEntityStruct>(Id);
tblces.timeLastRenderFrameMS = value;
}
}
}
}

View file

@ -1,9 +1,10 @@
using System; using System;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Commands using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// Custom Command builder. /// Custom Command builder.

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TechbloxModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// UNIMPLEMENTED! /// UNIMPLEMENTED!

View file

@ -1,7 +1,7 @@
using System; using System;
namespace TechbloxModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
public class CommandException : TechbloxModdingAPIException public class CommandException : GamecraftModdingAPIException
{ {
public CommandException() : base() {} public CommandException() : base() {}

View file

@ -5,9 +5,10 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Commands using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// Keeps track of custom commands /// Keeps track of custom commands

View file

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using HarmonyLib;
using Svelto.Context;
using Svelto.ECS;
using RobocraftX;
using RobocraftX.Multiplayer;
using RobocraftX.StateSync;
using Unity.Entities;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// Patch of RobocraftX.GUI.CommandLine.CommandLineCompositionRoot.Compose<T>()
/// </summary>
// TODO: fix
[HarmonyPatch]
//[HarmonyPatch(typeof(RobocraftX.GUI.CommandLine.CommandLineCompositionRoot))]
//[HarmonyPatch("Compose")]
//[HarmonyPatch("Compose", new Type[] { typeof(UnityContext<FullGameCompositionRoot>), typeof(EnginesRoot), typeof(World), typeof(Action), typeof(MultiplayerInitParameters), typeof(StateSyncRegistrationHelper)})]
static class CommandPatch
{
public static void Postfix(object contextHolder, EnginesRoot enginesRoot, World physicsWorld, Action reloadGame, MultiplayerInitParameters multiplayerParameters, ref StateSyncRegistrationHelper stateSyncReg)
{
// When a game is loaded, register the command engines
CommandManager.RegisterEngines(enginesRoot);
}
public static MethodBase TargetMethod(Harmony instance)
{
return typeof(RobocraftX.GUI.CommandLine.CommandLineCompositionRoot).GetMethod("Compose").MakeGenericMethod(typeof(object));
//return func.Method;
}
}
}

View file

@ -4,17 +4,22 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TechbloxModdingAPI.Commands using uREPL;
using RobocraftX.CommandLine.Custom;
namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// Convenient methods for registering commands to Techblox. /// Convenient methods for registering commands to Gamecraft.
/// All methods register to the command line and console block by default. /// All methods register to the command line and console block by default.
/// </summary> /// </summary>
public static class CommandRegistrationHelper public static class CommandRegistrationHelper
{ {
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)
{ {
CustomCommands.Register(name, action, desc); RuntimeCommands.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)
@ -34,42 +39,50 @@ 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)
{ {
CustomCommands.Register(name, action, desc); RuntimeCommands.Register<Param0>(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)
{ {
CustomCommands.Register(name, action, desc); RuntimeCommands.Register<Param0, Param1>(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)
{ {
CustomCommands.Register(name, action, desc); RuntimeCommands.Register<Param0, Param1, Param2>(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)
{ {
CustomCommands.Unregister(name); RuntimeCommands.Unregister(name);
if (noConsole) { return; }
ConsoleCommands.Unregister(name);
} }
public static void Call(string name) public static void Call(string name)
{ {
CustomCommands.Call(name); RuntimeCommands.Call(name);
} }
public static void Call<Param0>(string name, Param0 param0) public static void Call<Param0>(string name, Param0 param0)
{ {
CustomCommands.Call(name, param0); RuntimeCommands.Call<Param0>(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)
{ {
CustomCommands.Call(name, param0, param1); RuntimeCommands.Call<Param0, Param1>(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)
{ {
CustomCommands.Call(name, param0, param1, param2); RuntimeCommands.Call<Param0, Param1, Param2>(name, param0, param1, param2);
} }
} }
} }

View file

@ -0,0 +1,42 @@
using System;
using uREPL;
namespace GamecraftModdingAPI.Commands
{
public static class ExistingCommands
{
public static void Call(string commandName)
{
RuntimeCommands.Call(commandName);
}
public static void Call<Arg0>(string commandName, Arg0 arg0)
{
RuntimeCommands.Call<Arg0>(commandName, arg0);
}
public static void Call<Arg0, Arg1>(string commandName, Arg0 arg0, Arg1 arg1)
{
RuntimeCommands.Call<Arg0, Arg1>(commandName, arg0, arg1);
}
public static void Call<Arg0, Arg1, Arg2>(string commandName, Arg0 arg0, Arg1 arg1, Arg2 arg2)
{
RuntimeCommands.Call<Arg0, Arg1, Arg2>(commandName, arg0, arg1, arg2);
}
public static bool Exists(string commandName)
{
return RuntimeCommands.HasRegistered(commandName);
}
public static string[] GetCommandNames()
{
var keys = RuntimeCommands.table.Keys;
string[] res = new string[keys.Count];
keys.CopyTo(res, 0);
return res;
}
}
}

View file

@ -6,10 +6,10 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility; using GamecraftModdingAPI.Utility;
using TechbloxModdingAPI.Engines; using GamecraftModdingAPI.Engines;
namespace TechbloxModdingAPI.Commands namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// Engine interface to handle command operations. /// Engine interface to handle command operations.

View file

@ -5,9 +5,10 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Commands using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands. /// A simple implementation of ICustomCommandEngine sufficient for most commands.
@ -36,13 +37,13 @@ namespace TechbloxModdingAPI.Commands
public void Dispose() public void Dispose()
{ {
Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Unregister(this.Name); CommandRegistrationHelper.Unregister(this.Name);
} }
public void Ready() public void Ready()
{ {
Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description); CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description);
} }

View file

@ -5,9 +5,10 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Commands using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands. /// A simple implementation of ICustomCommandEngine sufficient for most commands.
@ -27,13 +28,13 @@ namespace TechbloxModdingAPI.Commands
public void Dispose() public void Dispose()
{ {
Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Unregister(this.Name); CommandRegistrationHelper.Unregister(this.Name);
} }
public void Ready() public void Ready()
{ {
Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register<A>(this.Name, this.InvokeCatchError, this.Description); CommandRegistrationHelper.Register<A>(this.Name, this.InvokeCatchError, this.Description);
} }

View file

@ -5,9 +5,10 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Commands using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands. /// A simple implementation of ICustomCommandEngine sufficient for most commands.
@ -27,13 +28,13 @@ namespace TechbloxModdingAPI.Commands
public void Dispose() public void Dispose()
{ {
Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Unregister(this.Name); CommandRegistrationHelper.Unregister(this.Name);
} }
public void Ready() public void Ready()
{ {
Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register<A,B>(this.Name, this.InvokeCatchError, this.Description); CommandRegistrationHelper.Register<A,B>(this.Name, this.InvokeCatchError, this.Description);
} }

View file

@ -5,9 +5,10 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Commands using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{ {
/// <summary> /// <summary>
/// A simple implementation of ICustomCommandEngine sufficient for most commands. /// A simple implementation of ICustomCommandEngine sufficient for most commands.
@ -27,13 +28,13 @@ namespace TechbloxModdingAPI.Commands
public void Dispose() public void Dispose()
{ {
Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Unregister(this.Name); CommandRegistrationHelper.Unregister(this.Name);
} }
public void Ready() public void Ready()
{ {
Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
CommandRegistrationHelper.Register<A,B,C>(this.Name, this.InvokeCatchError, this.Description); CommandRegistrationHelper.Register<A,B,C>(this.Name, this.InvokeCatchError, this.Description);
} }

View file

@ -6,10 +6,10 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
namespace TechbloxModdingAPI.Engines namespace GamecraftModdingAPI.Engines
{ {
/// <summary> /// <summary>
/// Base engine interface used by all TechbloxModdingAPI engines /// Base engine interface used by all GamecraftModdingAPI engines
/// </summary> /// </summary>
public interface IApiEngine : IEngine, IQueryingEntitiesEngine, IDisposable public interface IApiEngine : IEngine, IQueryingEntitiesEngine, IDisposable
{ {

View file

@ -6,9 +6,9 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Utility; using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Engines namespace GamecraftModdingAPI.Engines
{ {
/// <summary> /// <summary>
/// Engine interface to create a ModEventEntityStruct in entitiesDB when Emit() is called. /// Engine interface to create a ModEventEntityStruct in entitiesDB when Emit() is called.

View file

@ -7,14 +7,14 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Internal; using Svelto.ECS.Internal;
using TechbloxModdingAPI.Events; using GamecraftModdingAPI.Events;
namespace TechbloxModdingAPI.Engines namespace GamecraftModdingAPI.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> where T : unmanaged, IEntityComponent public interface IReactionaryEngine<T> : IApiEngine, IReactOnAddAndRemove<T>, IReactOnAddAndRemove where T : unmanaged, IEntityComponent
{ {
} }
} }

View file

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using Svelto.ECS;
using RobocraftX.Common;
using RobocraftX.StateSync;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Patch of RobocraftX.StateSync.DeterministicStepCompositionRoot.ComposeEnginesGroups(...)
/// </summary>
//[HarmonyPatch(typeof(DeterministicStepCompositionRoot), "DeterministicCompose")]
[HarmonyPatch]
class GameHostTransitionDeterministicGroupEnginePatch
{
public static readonly GameStateBuildEmitterEngine buildEngine = new GameStateBuildEmitterEngine();
public static readonly GameStateSimulationEmitterEngine simEngine = new GameStateSimulationEmitterEngine();
public static void Postfix()
{
//stateSyncReg.buildModeInitializationEngines.Add(buildEngine);
//stateSyncReg.simulationModeInitializationEngines.Add(simEngine);
//enginesRoot.AddEngine(buildEngine);
//enginesRoot.AddEngine(simEngine);
buildEngine.EmitIfBuildMode();
simEngine.EmitIfSimMode();
}
[HarmonyTargetMethod]
public static MethodBase TargetMethod(Harmony harmonyInstance)
{
return AccessTools.Method(AccessTools.TypeByName("RobocraftX.StateSync.GameHostTransitionDeterministicGroupEngine"), "EndTransition");
//.MakeGenericMethod(typeof(CosmeticEnginesSequenceBuildOrder), typeof(CosmeticEnginesSequenceSimOrder), typeof(DeterministicToCosmeticSyncBuildOrder), typeof(DeterministicToCosmeticSyncSimOrder));
}
}
}

View file

@ -0,0 +1,105 @@
using System;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
public class EmitterBuilder
{
private string name;
private int? type;
/// <summary>
/// Create a new event emitter builder.
/// </summary>
public EmitterBuilder()
{
}
/// <summary>
/// Create a new event emitter builder.
/// This is equivalent to new <code>EmitterBuilder().Name(name)</code>
/// </summary>
/// <param name="name">The emitter name.</param>
public EmitterBuilder(string name)
{
this.name = name;
}
/// <summary>
/// Create and return an event emitter builder.
/// </summary>
/// <returns>The builder.</returns>
public static EmitterBuilder Builder()
{
return new EmitterBuilder();
}
/// <summary>
/// Create and return an event emitter builder.
/// This is equivalent to <code>Builder().Name(name)</code>
/// </summary>
/// <returns>The builder.</returns>
/// <param name="name">The emitter name.</param>
public static EmitterBuilder Builder(string name)
{
return new EmitterBuilder(name);
}
/// <summary>
/// Name the event emitter.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="name">The event emitter name.</param>
public EmitterBuilder Name(string name)
{
this.name = name;
return this;
}
/// <summary>
/// Set the type of event to handle.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="eventType">The event type.</param>
public EmitterBuilder Handle(EventType eventType)
{
return Handle((int)eventType);
}
/// <summary>
/// Set the type of event to handle.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="eventType">The event type.</param>
public EmitterBuilder Handle(int eventType)
{
this.type = eventType;
return this;
}
/// <summary>
/// Build the event emitter.
/// </summary>
/// <returns>The event emitter.</returns>
/// <param name="register">Automatically register the event emitter with EventManager.AddEventemitter().</param>
public IEventEmitterEngine Build(bool register = true)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new EventParameterMissingException("Event emitter name must be defined before Build() is called");
}
if (!type.HasValue)
{
throw new EventParameterMissingException("Event emitter event type must be defined before Build() is called");
}
SimpleEventEmitterEngine result = new SimpleEventEmitterEngine(type.Value, name);
if (register)
{
EventManager.AddEventEmitter(result);
}
return result;
}
}
}

View file

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Convenient factories for mod event engines
/// </summary>
public static class EventEngineFactory
{
/// <summary>
/// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager
/// </summary>
/// <param name="name">The name of the engine</param>
/// <param name="type">The type of event to handle</param>
/// <param name="onActivated">The operation to do when the event is created</param>
/// <param name="onDestroyed">The operation to do when the event is destroyed (if applicable)</param>
/// <returns>The created object</returns>
public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, int type, Action onActivated, Action onDestroyed)
{
var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name);
EventManager.AddEventHandler(engine);
return engine;
}
/// <summary>
/// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager
/// </summary>
/// <param name="name">The name of the engine</param>
/// <param name="type">The type of event to handle</param>
/// <param name="onActivated">The operation to do when the event is created</param>
/// <param name="onDestroyed">The operation to do when the event is destroyed (if applicable)</param>
/// <returns>The created object</returns>
public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, int type, Action<EntitiesDB> onActivated, Action<EntitiesDB> onDestroyed)
{
var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name);
EventManager.AddEventHandler(engine);
return engine;
}
/// <summary>
/// Factory method which automatically adds the SimpleEventEmitterEngine to the Manager
/// </summary>
/// <param name="name">The name of the engine</param>
/// <param name="type">The type of event to emit</param>
/// <param name="isRemovable">Will removing this engine not break your code?</param>
/// <returns>The created object</returns>
public static SimpleEventEmitterEngine CreateAddSimpleEmitter(string name, int type, bool isRemovable = true)
{
var engine = new SimpleEventEmitterEngine(type, name, isRemovable);
EventManager.AddEventEmitter(engine);
return engine;
}
}
}

View file

@ -0,0 +1,66 @@
using System;
namespace GamecraftModdingAPI.Events
{
public class EventException : GamecraftModdingAPIException
{
public EventException()
{
}
public EventException(string message) : base(message)
{
}
public EventException(string message, Exception innerException) : base(message, innerException)
{
}
}
public class EventNotFoundException : EventException
{
public EventNotFoundException()
{
}
public EventNotFoundException(string message) : base(message)
{
}
}
public class EventAlreadyExistsException : EventException
{
public EventAlreadyExistsException()
{
}
public EventAlreadyExistsException(string message) : base(message)
{
}
}
public class EventRuntimeException : EventException
{
public EventRuntimeException()
{
}
public EventRuntimeException(string message) : base(message)
{
}
public EventRuntimeException(string message, Exception innerException) : base(message, innerException)
{
}
}
public class EventParameterMissingException : EventException
{
public EventParameterMissingException()
{
}
public EventParameterMissingException(string message) : base(message)
{
}
}
}

View file

@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Keeps track of event handlers and emitters.
/// This is used to add, remove and get API event handlers and emitters.
/// </summary>
public static class EventManager
{
private static Dictionary<string, IEventEmitterEngine> _eventEmitters = new Dictionary<string, IEventEmitterEngine>();
private static Dictionary<string, IEventHandlerEngine> _eventHandlers = new Dictionary<string, IEventHandlerEngine>();
private static EnginesRoot _lastEngineRoot;
// event handler management
public static void AddEventHandler(IEventHandlerEngine engine)
{
if (ExistsEventHandler(engine))
{
throw new EventAlreadyExistsException($"IEventHandlerEngine {engine.Name} already exists");
}
_eventHandlers[engine.Name] = engine;
if (_lastEngineRoot != null)
{
Logging.MetaDebugLog($"Registering IEventHandlerEngine {engine.Name}");
_lastEngineRoot.AddEngine(engine);
}
}
public static bool ExistsEventHandler(string name)
{
return _eventHandlers.ContainsKey(name);
}
public static bool ExistsEventHandler(IEventHandlerEngine engine)
{
return ExistsEventHandler(engine.Name);
}
public static IEventHandlerEngine GetEventHandler(string name)
{
return _eventHandlers[name];
}
public static string[] GetEventHandlerNames()
{
return _eventHandlers.Keys.ToArray();
}
public static void RemoveEventHandler(string name)
{
_eventHandlers.Remove(name);
}
// event emitter management
public static void AddEventEmitter(IEventEmitterEngine engine)
{
if (ExistsEventEmitter(engine))
{
throw new EventAlreadyExistsException($"IEventEmitterEngine {engine.Name} already exists");
}
_eventEmitters[engine.Name] = engine;
if (_lastEngineRoot != null)
{
Logging.MetaDebugLog($"Registering IEventEmitterEngine {engine.Name}");
_lastEngineRoot.AddEngine(engine);
}
}
public static bool ExistsEventEmitter(string name)
{
return _eventEmitters.ContainsKey(name);
}
public static bool ExistsEventEmitter(IEventEmitterEngine engine)
{
return ExistsEventEmitter(engine.Name);
}
public static IEventEmitterEngine GetEventEmitter(string name)
{
return _eventEmitters[name];
}
public static string[] GetEventEmitterNames()
{
return _eventEmitters.Keys.ToArray();
}
public static void RemoveEventEmitter(string name)
{
if (_eventEmitters[name].isRemovable)
{
_eventEmitters.Remove(name);
}
}
public static void RegisterEngines(EnginesRoot enginesRoot)
{
_lastEngineRoot = enginesRoot;
// Register handlers before emitters so no events are missed
var entityFactory = enginesRoot.GenerateEntityFactory();
foreach (var key in _eventHandlers.Keys)
{
Logging.MetaDebugLog($"Registering IEventHandlerEngine {_eventHandlers[key].Name}");
enginesRoot.AddEngine(_eventHandlers[key]);
}
foreach (var key in _eventEmitters.Keys)
{
Logging.MetaDebugLog($"Registering IEventEmitterEngine {_eventEmitters[key].Name}");
_eventEmitters[key].Factory = entityFactory;
enginesRoot.AddEngine(_eventEmitters[key]);
}
}
}
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Built-in event types.
/// These are configured to fire when the API is initialized.
/// </summary>
public enum EventType
{
ApplicationInitialized,
Menu,
MenuSwitchedTo,
Game,
GameReloaded,
GameSwitchedTo,
SimulationSwitchedTo,
BuildSwitchedTo
}
}

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RobocraftX;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
using RobocraftX.CR.MainGame;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame()
/// </summary>
[HarmonyPatch]
class GameActivatedComposePatch
{
public static bool IsGameSwitching = false;
public static bool IsGameReloading = false;
public static void Postfix(ref object contextHolder, ref EnginesRoot enginesRoot)
{
// register custom game engines
GameEngineManager.RegisterEngines(enginesRoot);
// initialize AsyncUtils
AsyncUtils.Setup(enginesRoot);
// A new EnginesRoot is always created when ActivateGame is called
// so all event emitters and handlers must be re-registered.
EventManager.RegisterEngines(enginesRoot);
Logging.Log("Dispatching Game Activated event");
EventManager.GetEventEmitter("GamecraftModdingAPIGameActivatedEventEmitter").Emit();
if (IsGameSwitching)
{
IsGameSwitching = false;
Logging.Log("Dispatching Game Switched To event");
EventManager.GetEventEmitter("GamecraftModdingAPIGameSwitchedToEventEmitter").Emit();
}
if (IsGameReloading)
{
IsGameReloading = false;
Logging.Log("Dispatching Game Reloaded event");
EventManager.GetEventEmitter("GamecraftModdingAPIGameReloadedEventEmitter").Emit();
}
}
public static MethodBase TargetMethod()
{
return typeof(MainGameCompositionRoot).GetMethods().First(m => m.Name == "Compose")
.MakeGenericMethod(typeof(object));
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RobocraftX;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Patch of RobocraftX.FullGameCompositionRoot.ReloadGame()
/// </summary>
[HarmonyPatch(typeof(FullGameCompositionRoot), "ReloadGame")]
class GameReloadedPatch
{
public static void Postfix()
{
GameActivatedComposePatch.IsGameReloading = true;
}
}
}

View file

@ -0,0 +1,54 @@
using System;
using Unity.Jobs;
using RobocraftX.SimulationModeState;
using RobocraftX.StateSync;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Event emitter engine for switching to to build mode.
/// </summary>
public class GameStateBuildEmitterEngine : IEventEmitterEngine, IUnorderedInitializeOnTimeStoppedModeEntered
{
public string Name { get; } = "GamecraftModdingAPIGameStateBuildEventEmitter" ;
public EntitiesDB entitiesDB { set; private get; }
public int type { get; } = (int)EventType.BuildSwitchedTo;
public bool isRemovable { get; } = false;
public IEntityFactory Factory { set; private get; }
public void Dispose() { }
public void Emit()
{
Logging.Log("Dispatching Build Switched To event");
if (Factory == null) { return; }
Factory.BuildEntity<ModEventEntityDescriptor>(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup)
.Init(new ModEventEntityStruct { type = type });
}
public void EmitIfBuildMode()
{
//Logging.MetaDebugLog($"nextSimulationMode: {entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode}");
if (entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode == SimulationMode.TimeStopped)
{
Emit();
}
}
public JobHandle OnInitializeTimeStoppedMode()
{
Emit();
return default(JobHandle);
}
public void Ready() { }
}
}

View file

@ -0,0 +1,53 @@
using System;
using Unity.Jobs;
using RobocraftX.SimulationModeState;
using RobocraftX.StateSync;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Event emitter engine for switching to simulation mode.
/// </summary>
public class GameStateSimulationEmitterEngine : IEventEmitterEngine, IUnorderedInitializeOnTimeRunningModeEntered
{
public string Name { get; } = "GamecraftModdingAPIGameStateSimulationEventEmitter" ;
public EntitiesDB entitiesDB { set; private get; }
public int type { get; } = (int)EventType.SimulationSwitchedTo;
public bool isRemovable { get; } = false;
public IEntityFactory Factory { set; private get; }
public void Dispose() { }
public void Emit()
{
Logging.Log("Dispatching Simulation Switched To event");
if (Factory == null) { return; }
Factory.BuildEntity<ModEventEntityDescriptor>(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup)
.Init(new ModEventEntityStruct { type = type });
}
public void EmitIfSimMode()
{
if (entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode == SimulationMode.TimeRunning)
{
Emit();
}
}
public JobHandle OnInitializeTimeRunningMode()
{
Emit();
return default(JobHandle);
}
public void Ready() { }
}
}

View file

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using HarmonyLib;
using RobocraftX;
using RobocraftX.CR.MainGame;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame()
/// (scheduled for execution during RobocraftX.FullGameCompositionRoot.SwitchToGame())
/// </summary>
[HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToGame")]
class GameSwitchedToPatch
{
public static void Prefix()
{
GameActivatedComposePatch.IsGameSwitching = true;
}
}
}

View file

@ -0,0 +1,165 @@
using System;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
public class HandlerBuilder
{
private string name;
private int? type;
private Action<EntitiesDB> activated;
private Action<EntitiesDB> destroyed;
/// <summary>
/// Create a new event handler builder.
/// </summary>
public HandlerBuilder()
{
}
/// <summary>
/// Create a new event handler builder.
/// This is equivalent to new <code>HandlerBuilder().Name(name)</code>
/// </summary>
/// <param name="name">The handler name.</param>
public HandlerBuilder(string name)
{
this.name = name;
}
/// <summary>
/// Create and return an event handler builder.
/// </summary>
/// <returns>The builder.</returns>
public static HandlerBuilder Builder()
{
return new HandlerBuilder();
}
/// <summary>
/// Create and return an event handler builder.
/// This is equivalent to <code>Builder().Name(name)</code>
/// </summary>
/// <returns>The builder.</returns>
/// <param name="name">The handler name.</param>
public static HandlerBuilder Builder(string name)
{
return new HandlerBuilder(name);
}
/// <summary>
/// Name the event handler.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="name">The event handler name.</param>
public HandlerBuilder Name(string name)
{
this.name = name;
return this;
}
/// <summary>
/// Set the action to perform on when the activated event occurs.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="action">The activated event action.</param>
public HandlerBuilder OnActivation(Action action)
{
return OnActivation((_) => { action(); });
}
/// <summary>
/// Set the action to perform on when the activated event occurs.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="action">The activated event action.</param>
public HandlerBuilder OnActivation(Action<EntitiesDB> action)
{
this.activated = action;
return this;
}
/// <summary>
/// Set the action to perform when the destroyed event occurs.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="action">The destroyed event action.</param>
public HandlerBuilder OnDestruction(Action action)
{
return OnDestruction((_) => { action(); });
}
/// <summary>
/// Set the action to perform when the destroyed event occurs.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="action">The destroyed event action.</param>
public HandlerBuilder OnDestruction(Action<EntitiesDB> action)
{
this.destroyed = action;
return this;
}
/// <summary>
/// Set the type of event to handle.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="eventType">The event type.</param>
public HandlerBuilder Handle(EventType eventType)
{
return Handle((int)eventType);
}
/// <summary>
/// Set the type of event to handle.
/// </summary>
/// <returns>The builder.</returns>
/// <param name="eventType">The event type.</param>
public HandlerBuilder Handle(int eventType)
{
this.type = eventType;
return this;
}
/// <summary>
/// Build the event handler.
/// </summary>
/// <returns>The event handler.</returns>
/// <param name="register">Automatically register the event handler with EventManager.AddEventHandler().</param>
public IEventHandlerEngine Build(bool register = true)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new EventParameterMissingException("Event handler name must be defined before Build() is called");
}
if (activated == null && destroyed == null)
{
throw new EventParameterMissingException("Event handler destruction or activated event action must be defined before Build() is called");
}
if (!type.HasValue)
{
throw new EventParameterMissingException("Event handler event type must be defined before Build() is called");
}
Action<EntitiesDB> validActivated = activated;
if (validActivated == null)
{
validActivated = (_) => { };
}
Action<EntitiesDB> validDestroyed = destroyed;
if (validDestroyed == null)
{
validDestroyed = (_) => { };
}
SimpleEventHandlerEngine result = new SimpleEventHandlerEngine(validActivated, validDestroyed, type.Value, name);
if (register)
{
EventManager.AddEventHandler(result);
}
return result;
}
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Engine interface to create a ModEventEntityStruct in entitiesDB when a specific event occurs.
/// </summary>
public interface IEventEmitterEngine : IFactoryEngine
{
/// <summary>
/// Emit the event. (Optional)
/// </summary>
void Emit();
}
}

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
using Svelto.ECS.Internal;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines.
/// </summary>
public interface IEventHandlerEngine : IReactionaryEngine<ModEventEntityStruct>
{
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RobocraftX;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateMenu()
/// </summary>
[HarmonyPatch(typeof(FullGameCompositionRoot), "ActivateMenu")]
class MenuActivatedPatch
{
private static bool firstLoad = true;
public static void Postfix(ref EnginesRoot ____frontEndEnginesRoot, FullGameCompositionRoot __instance)
{
// register custom menu engines
MenuEngineManager.RegisterEngines(____frontEndEnginesRoot);
// A new EnginesRoot is always created when ActivateMenu is called
// so all event emitters and handlers must be re-registered.
EventManager.RegisterEngines(____frontEndEnginesRoot);
if (firstLoad)
{
firstLoad = false;
FullGameFields.Init(__instance);
Logging.Log("Dispatching App Init event");
EventManager.GetEventEmitter("GamecraftModdingAPIApplicationInitializedEventEmitter").Emit();
}
Logging.Log("Dispatching Menu Activated event");
EventManager.GetEventEmitter("GamecraftModdingAPIMenuActivatedEventEmitter").Emit();
}
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RobocraftX;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Patch of RobocraftX.FullGameCompositionRoot.SwitchToMenu()
/// </summary>
[HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToMenu")]
class MenuSwitchedToPatch
{
public static void Postfix()
{
// Event emitters and handlers should already be registered by MenuActivated event
Logging.Log("Dispatching Menu Switched To event");
EventManager.GetEventEmitter("GamecraftModdingAPIMenuSwitchedToEventEmitter").Emit();
}
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// EntityDescriptor for creating ModEventEntityStructs
/// </summary>
public class ModEventEntityDescriptor : GenericEntityDescriptor<ModEventEntityStruct>
{
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// The event entity struct
/// </summary>
public struct ModEventEntityStruct : IEntityComponent, INeedEGID
{
/// <summary>
/// The type of event that has been emitted
/// </summary>
public int type;
public EGID ID { get; set; }
}
}

View file

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// A simple implementation of IEventEmitterEngine sufficient for most uses
/// </summary>
public class SimpleEventEmitterEngine : IEventEmitterEngine
{
public string Name { get; set; }
public int type { get; set; }
public bool isRemovable { get; }
public IEntityFactory Factory { private get; set; }
public EntitiesDB entitiesDB { set; private get; }
public void Ready() { }
/// <summary>
/// Emit the event
/// </summary>
public void Emit()
{
Factory.BuildEntity<ModEventEntityDescriptor>(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup)
.Init(new ModEventEntityStruct { type = type });
}
public void Dispose() { }
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="type">The EventType to use for ModEventEntityStruct.type</param>
/// <param name="name">The name of this engine</param>
/// <param name="isRemovable">Will removing this engine not break your code?</param>
public SimpleEventEmitterEngine(EventType type, string name, bool isRemovable = true)
{
this.type = (int)type;
this.Name = name;
this.isRemovable = isRemovable;
}
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="type">The object to use for ModEventEntityStruct.type</param>
/// <param name="name">The name of this engine</param>
/// <param name="isRemovable">Will removing this engine not break your code?</param>
public SimpleEventEmitterEngine(int type, string name, bool isRemovable = true)
{
this.type = type;
this.Name = name;
this.isRemovable = isRemovable;
}
}
}

View file

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// A simple implementation of IEventHandlerEngine sufficient for most uses
/// </summary>
public class SimpleEventHandlerEngine : IEventHandlerEngine
{
public int type { get; set; }
public string Name { get; set; }
private bool isActivated = false;
private readonly Action<EntitiesDB> onActivated;
private readonly Action<EntitiesDB> onDestroyed;
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => true;
public void Add(ref ModEventEntityStruct entityView, EGID egid)
{
if (entityView.type.Equals(this.type))
{
isActivated = true;
onActivatedInvokeCatchError(entitiesDB);
}
}
/// <summary>
/// Manually activate the EventHandler.
/// Once activated, the next remove event will not be ignored.
/// </summary>
/// <param name="handle">Whether to invoke the activated action</param>
public void Activate(bool handle = false)
{
isActivated = true;
if (handle && entitiesDB != null)
{
onActivatedInvokeCatchError(entitiesDB);
}
}
public void Ready() { }
public void Remove(ref ModEventEntityStruct entityView, EGID egid)
{
if (entityView.type.Equals(this.type) && isActivated)
{
isActivated = false;
onDestroyedInvokeCatchError(entitiesDB);
}
}
public void Dispose()
{
if (isActivated)
{
isActivated = false;
onDestroyedInvokeCatchError(entitiesDB);
}
}
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="activated">The operation to do when the event is created</param>
/// <param name="removed">The operation to do when the event is destroyed (if applicable)</param>
/// <param name="type">The type of event to handle</param>
/// <param name="name">The name of the engine</param>
/// <param name="simple">A useless parameter to use to avoid Python overload resolution errors</param>
public SimpleEventHandlerEngine(Action activated, Action removed, int type, string name, bool simple = true)
: this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, type, name) { }
/// <summary>
/// Construct the engine
/// </summary>
/// <param name="activated">The operation to do when the event is created</param>
/// <param name="removed">The operation to do when the event is destroyed (if applicable)</param>
/// <param name="type">The type of event to handler</param>
/// <param name="name">The name of the engine</param>
public SimpleEventHandlerEngine(Action<EntitiesDB> activated, Action<EntitiesDB> removed, int type, string name)
{
this.type = type;
this.Name = name;
this.onActivated = activated;
this.onDestroyed = removed;
}
private void onActivatedInvokeCatchError(EntitiesDB _entitiesDB)
{
try
{
onActivated.Invoke(_entitiesDB);
}
catch (Exception e)
{
EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when activated", e);
Logging.LogWarning(wrappedException.ToString());
}
}
private void onDestroyedInvokeCatchError(EntitiesDB _entitiesDB)
{
try
{
onDestroyed.Invoke(_entitiesDB);
}
catch (Exception e)
{
EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when destroyed", e);
Logging.LogWarning(wrappedException.ToString());
}
}
public SimpleEventHandlerEngine(Action activated, Action removed, EventType type, string name, bool simple = true)
: this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, (int)type, name) { }
public SimpleEventHandlerEngine(Action<EntitiesDB> activated, Action<EntitiesDB> removed, EventType type, string name, bool simple = true)
: this(activated, removed, (int)type, name) { }
}
}

View file

@ -0,0 +1,804 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Version>1.1.0</Version>
<Authors>Exmods</Authors>
<PackageLicenseExpression>GNU General Public Licence 3+</PackageLicenseExpression>
<PackageProjectUrl>https://git.exmods.org/modtainers/GamecraftModdingAPI</PackageProjectUrl>
<NeutralLanguage>en-CA</NeutralLanguage>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.0.0.10" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<!--Start Dependencies-->
<ItemGroup>
<Reference Include="IllusionInjector">
<HintPath>..\ref\Gamecraft_Data\Managed\IllusionInjector.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\IllusionInjector.dll</HintPath>
</Reference>
<Reference Include="IllusionPlugin">
<HintPath>..\ref\Gamecraft_Data\Managed\IllusionPlugin.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\IllusionPlugin.dll</HintPath>
</Reference>
<Reference Include="JWT">
<HintPath>..\ref\Gamecraft_Data\Managed\JWT.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\JWT.dll</HintPath>
</Reference>
<Reference Include="Unity.Burst.Unsafe">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Burst.Unsafe.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Burst.Unsafe.dll</HintPath>
</Reference>
<Reference Include="Facepunch.Steamworks.Win64">
<HintPath>..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll</HintPath>
</Reference>
<Reference Include="Rewired_Core">
<HintPath>..\ref\Gamecraft_Data\Managed\Rewired_Core.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Rewired_Core.dll</HintPath>
</Reference>
<Reference Include="Rewired_Windows">
<HintPath>..\ref\Gamecraft_Data\Managed\Rewired_Windows.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Rewired_Windows.dll</HintPath>
</Reference>
<Reference Include="mscorlib">
<HintPath>..\ref\Gamecraft_Data\Managed\mscorlib.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\mscorlib.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\ref\Gamecraft_Data\Managed\Newtonsoft.Json.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AccessibilityModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AccessibilityModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.AccessibilityModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AIModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AIModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.AIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AndroidJNIModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AndroidJNIModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.AndroidJNIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AnimationModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ARModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ARModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ARModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AssetBundleModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClothModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ClothModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ClothModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterInputModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ClusterInputModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ClusterInputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterRendererModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ClusterRendererModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ClusterRendererModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CrashReportingModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.CrashReportingModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.CrashReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DirectorModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.DirectorModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.DirectorModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DSPGraphModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.DSPGraphModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.DSPGraphModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GameCenterModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.GameCenterModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.GameCenterModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GridModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.GridModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.GridModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.HotReloadModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.HotReloadModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.HotReloadModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ImageConversionModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ImageConversionModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ImageConversionModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.InputModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.InputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.JSONSerializeModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.JSONSerializeModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.JSONSerializeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.LocalizationModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.LocalizationModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.LocalizationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ParticleSystemModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ParticleSystemModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ParticleSystemModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PerformanceReportingModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.PerformanceReportingModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.PerformanceReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.Physics2DModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.Physics2DModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.Physics2DModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ProfilerModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ProfilerModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ProfilerModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ScreenCaptureModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ScreenCaptureModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.ScreenCaptureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SharedInternalsModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SharedInternalsModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.SharedInternalsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteMaskModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SpriteMaskModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.SpriteMaskModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteShapeModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SpriteShapeModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.SpriteShapeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.StreamingModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.StreamingModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.StreamingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SubstanceModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SubstanceModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.SubstanceModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SubsystemsModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SubsystemsModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.SubsystemsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TerrainModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.TerrainModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainPhysicsModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TerrainPhysicsModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.TerrainPhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextCoreModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TextCoreModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.TextCoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TilemapModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TilemapModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.TilemapModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TLSModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TLSModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.TLSModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIElementsModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UIElementsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UIModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UmbraModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UmbraModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UmbraModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UNETModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UNETModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UNETModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityAnalyticsModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityAnalyticsModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityAnalyticsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityConnectModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityConnectModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityConnectModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityTestProtocolModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityTestProtocolModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityTestProtocolModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VehiclesModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VehiclesModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.VehiclesModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VFXModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VFXModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.VFXModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VideoModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VideoModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.VideoModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VRModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.VRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.WindModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.WindModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.WindModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XRModule">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.XRModule.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.XRModule.dll</HintPath>
</Reference>
<Reference Include="Analytics">
<HintPath>..\ref\Gamecraft_Data\Managed\Analytics.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Analytics.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>..\ref\Gamecraft_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\ref\Gamecraft_Data\Managed\Assembly-CSharp.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Authentication">
<HintPath>..\ref\Gamecraft_Data\Managed\Authentication.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Authentication.dll</HintPath>
</Reference>
<Reference Include="BlockEntityFactory">
<HintPath>..\ref\Gamecraft_Data\Managed\BlockEntityFactory.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\BlockEntityFactory.dll</HintPath>
</Reference>
<Reference Include="Blocks.HUDFeedbackBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Blocks.HUDFeedbackBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Blocks.HUDFeedbackBlocks.dll</HintPath>
</Reference>
<Reference Include="ChannelsCommon">
<HintPath>..\ref\Gamecraft_Data\Managed\ChannelsCommon.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\ChannelsCommon.dll</HintPath>
</Reference>
<Reference Include="ClusterToWireConversion.Mock">
<HintPath>..\ref\Gamecraft_Data\Managed\ClusterToWireConversion.Mock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\ClusterToWireConversion.Mock.dll</HintPath>
</Reference>
<Reference Include="CommandLine">
<HintPath>..\ref\Gamecraft_Data\Managed\CommandLine.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\CommandLine.dll</HintPath>
</Reference>
<Reference Include="DataLoader">
<HintPath>..\ref\Gamecraft_Data\Managed\DataLoader.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\DataLoader.dll</HintPath>
</Reference>
<Reference Include="DDNA">
<HintPath>..\ref\Gamecraft_Data\Managed\DDNA.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\DDNA.dll</HintPath>
</Reference>
<Reference Include="FMOD">
<HintPath>..\ref\Gamecraft_Data\Managed\FMOD.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\FMOD.dll</HintPath>
</Reference>
<Reference Include="FullGame">
<HintPath>..\ref\Gamecraft_Data\Managed\FullGame.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\FullGame.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Blocks.ConsoleBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Blocks.GenericPhysicsBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Blocks.LogicBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.LogicBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.LogicBlock.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Blocks.TimerBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.TimerBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.TimerBlock.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.CharacterVulnerability">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerability.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerability.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.CharacterVulnerabilityGui">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Effects">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Effects.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Effects.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GUI.ConsoleBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.ConsoleBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.ConsoleBlock.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GUI.GraphicsScreen">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GUI.HUDFeedbackBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GUI.Tweaks">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Tweaks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Tweaks.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GUI.Wires">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Wires.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Wires.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GUI.Wires.Mockup">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Wires.Mockup.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Wires.Mockup.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GUI.WorldSpaceGuis">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Tweaks">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Tweaks.Mockup">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.Mockup.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.Mockup.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Wires">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Wires.Input">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.Input.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.Input.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Wires.Mockup">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.Mockup.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.Mockup.dll</HintPath>
</Reference>
<Reference Include="GameState">
<HintPath>..\ref\Gamecraft_Data\Managed\GameState.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\GameState.dll</HintPath>
</Reference>
<Reference Include="GPUInstancer">
<HintPath>..\ref\Gamecraft_Data\Managed\GPUInstancer.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\GPUInstancer.dll</HintPath>
</Reference>
<Reference Include="Havok.Physics">
<HintPath>..\ref\Gamecraft_Data\Managed\Havok.Physics.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Havok.Physics.dll</HintPath>
</Reference>
<Reference Include="Havok.Physics.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Havok.Physics.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Havok.Physics.Hybrid.dll</HintPath>
</Reference>
<Reference Include="LZ4">
<HintPath>..\ref\Gamecraft_Data\Managed\LZ4.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\LZ4.dll</HintPath>
</Reference>
<Reference Include="MultiplayerNetworking">
<HintPath>..\ref\Gamecraft_Data\Managed\MultiplayerNetworking.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\MultiplayerNetworking.dll</HintPath>
</Reference>
<Reference Include="MultiplayerTest">
<HintPath>..\ref\Gamecraft_Data\Managed\MultiplayerTest.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\MultiplayerTest.dll</HintPath>
</Reference>
<Reference Include="RCX.ScreenshotTaker">
<HintPath>..\ref\Gamecraft_Data\Managed\RCX.ScreenshotTaker.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RCX.ScreenshotTaker.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.AccountPreferences">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.AccountPreferences.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.AccountPreferences.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Blocks">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Blocks.Ghost">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Ghost.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Ghost.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Blocks.Triggers">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Triggers.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Triggers.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Building.BoxSelect">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Building.BoxSelect.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Building.BoxSelect.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Building.Jobs">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Building.Jobs.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Building.Jobs.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Character">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Character.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Character.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.ClusterToWireConversion">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.ClusterToWireConversion.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.ClusterToWireConversion.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Common">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Common.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Common.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.ControlsScreen">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.ControlsScreen.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.ControlsScreen.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Crosshair">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Crosshair.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Crosshair.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.FrontEnd">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.FrontEnd.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.FrontEnd.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.GUI.BlockLabel">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.BlockLabel.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.BlockLabel.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.GUI.DebugDisplay">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.DebugDisplay.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.DebugDisplay.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.GUI">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.GUI.RemoveBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.RemoveBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.RemoveBlock.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.GUI.ScaleGhost">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.ScaleGhost.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.ScaleGhost.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.GUIs.WorkshopPrefabs">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Input">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Input.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Input.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.MachineEditor">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MachineEditor.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.MachineEditor.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.MainGame">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MainGame.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.MainGame.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.MainSimulation">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MainSimulation.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.MainSimulation.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.MockCharacter">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MockCharacter.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.MockCharacter.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Multiplayer">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Multiplayer.NetworkEntityStream">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.MultiplayerInput">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MultiplayerInput.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.MultiplayerInput.dll</HintPath>
</Reference>
<Reference Include="Robocraftx.ObjectIdBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Robocraftx.ObjectIdBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Robocraftx.ObjectIdBlocks.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Party">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Party.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Party.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.PartyGui">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.PartyGui.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.PartyGui.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Physics">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Physics.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Physics.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.PilotSeat">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.PilotSeat.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.PilotSeat.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Player">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Player.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Player.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Rendering">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Rendering.Mock">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.Mock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.Mock.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.SaveAndLoad">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.SaveAndLoad.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.SaveAndLoad.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.SaveGameDialog">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.SaveGameDialog.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.SaveGameDialog.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Serializers">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Serializers.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Serializers.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.Services">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Services.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.Services.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.SignalHandling">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.SignalHandling.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.SignalHandling.dll</HintPath>
</Reference>
<Reference Include="RobocraftX.StateSync">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.StateSync.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX.StateSync.dll</HintPath>
</Reference>
<Reference Include="RobocraftX_SpawnPoints">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX_SpawnPoints.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX_SpawnPoints.dll</HintPath>
</Reference>
<Reference Include="RobocraftX_TextBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX_TextBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftX_TextBlock.dll</HintPath>
</Reference>
<Reference Include="RobocratX.SimulationCompositionRoot">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocratX.SimulationCompositionRoot.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocratX.SimulationCompositionRoot.dll</HintPath>
</Reference>
<Reference Include="StringFormatter">
<HintPath>..\ref\Gamecraft_Data\Managed\StringFormatter.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\StringFormatter.dll</HintPath>
</Reference>
<Reference Include="Svelto.Common_3">
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.Common_3.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Svelto.Common_3.dll</HintPath>
</Reference>
<Reference Include="Svelto.ECS.Debugger">
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.ECS.Debugger.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Svelto.ECS.Debugger.dll</HintPath>
</Reference>
<Reference Include="Svelto.ECS.Debugger.Internal">
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.ECS.Debugger.Internal.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Svelto.ECS.Debugger.Internal.dll</HintPath>
</Reference>
<Reference Include="Svelto.ECS">
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.ECS.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Svelto.ECS.dll</HintPath>
</Reference>
<Reference Include="Svelto.Services">
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.Services.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Svelto.Services.dll</HintPath>
</Reference>
<Reference Include="Svelto.Tasks">
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.Tasks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Svelto.Tasks.dll</HintPath>
</Reference>
<Reference Include="Unity.Addressables">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Addressables.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Addressables.dll</HintPath>
</Reference>
<Reference Include="Unity.Burst">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Burst.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Burst.dll</HintPath>
</Reference>
<Reference Include="Unity.Collections">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Collections.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Collections.dll</HintPath>
</Reference>
<Reference Include="Unity.Deformations">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Deformations.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Deformations.dll</HintPath>
</Reference>
<Reference Include="Unity.Entities">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Entities.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Entities.dll</HintPath>
</Reference>
<Reference Include="Unity.Entities.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Entities.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Entities.Hybrid.dll</HintPath>
</Reference>
<Reference Include="Unity.Jobs">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Jobs.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Jobs.dll</HintPath>
</Reference>
<Reference Include="Unity.Mathematics">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Mathematics.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Mathematics.dll</HintPath>
</Reference>
<Reference Include="Unity.Mathematics.Extensions">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.dll</HintPath>
</Reference>
<Reference Include="Unity.Mathematics.Extensions.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll</HintPath>
</Reference>
<Reference Include="Unity.Physics">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Physics.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Physics.dll</HintPath>
</Reference>
<Reference Include="Unity.Physics.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Physics.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Physics.Hybrid.dll</HintPath>
</Reference>
<Reference Include="Unity.Platforms.Common">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Platforms.Common.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Platforms.Common.dll</HintPath>
</Reference>
<Reference Include="Unity.Postprocessing.Runtime">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.Properties">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Properties.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Properties.dll</HintPath>
</Reference>
<Reference Include="Unity.Properties.Reflection">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Properties.Reflection.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Properties.Reflection.dll</HintPath>
</Reference>
<Reference Include="Unity.Properties.UI">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Properties.UI.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Properties.UI.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipeline.Universal.ShaderLibrary">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipeline.Universal.ShaderLibrary.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipeline.Universal.ShaderLibrary.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Core.Runtime">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Core.ShaderLibrary">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Universal.Runtime">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Universal.Runtime.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Universal.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.RenderPipelines.Universal.Shaders">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Universal.Shaders.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Universal.Shaders.dll</HintPath>
</Reference>
<Reference Include="Unity.ResourceManager">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.ResourceManager.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.ResourceManager.dll</HintPath>
</Reference>
<Reference Include="Unity.Scenes.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Scenes.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Scenes.Hybrid.dll</HintPath>
</Reference>
<Reference Include="Unity.ScriptableBuildPipeline">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.ScriptableBuildPipeline.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.ScriptableBuildPipeline.dll</HintPath>
</Reference>
<Reference Include="Unity.Serialization">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Serialization.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Serialization.dll</HintPath>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.TextMeshPro.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="Unity.Timeline">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Timeline.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Timeline.dll</HintPath>
</Reference>
<Reference Include="Unity.Transforms">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Transforms.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Transforms.dll</HintPath>
</Reference>
<Reference Include="Unity.Transforms.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Transforms.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Transforms.Hybrid.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll</HintPath>
</Reference>
<Reference Include="uREPL">
<HintPath>..\ref\Gamecraft_Data\Managed\uREPL.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\uREPL.dll</HintPath>
</Reference>
<Reference Include="VisualProfiler">
<HintPath>..\ref\Gamecraft_Data\Managed\VisualProfiler.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\VisualProfiler.dll</HintPath>
</Reference>
</ItemGroup>
<!--End Dependencies-->
</Project>

View file

@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace GamecraftModdingAPI
{
public class GamecraftModdingAPIException : Exception
{
public GamecraftModdingAPIException()
{
}
public GamecraftModdingAPIException(string message) : base(message)
{
}
public GamecraftModdingAPIException(string message, Exception innerException) : base(message, innerException)
{
}
protected GamecraftModdingAPIException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

View file

@ -0,0 +1,153 @@
using System;
using RobocraftX.Common;
using RobocraftX.Common.Input;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Input
{
public static class FakeInput
{
private static readonly FakeInputEngine inputEngine = new FakeInputEngine();
/// <summary>
/// Customize the player input.
/// </summary>
/// <param name="input">The custom input.</param>
/// <param name="playerID">The player. Omit this to use the local player.</param>
public static void CustomInput(InputEntityStruct input, uint playerID = uint.MaxValue)
{
if (playerID == uint.MaxValue)
{
playerID = inputEngine.GetLocalPlayerID();
}
inputEngine.SendCustomInput(input, playerID);
}
public static InputEntityStruct GetInput(uint playerID = uint.MaxValue)
{
if (playerID == uint.MaxValue)
{
playerID = inputEngine.GetLocalPlayerID();
}
return inputEngine.GetInput(playerID);
}
/// <summary>
/// Fake a GUI input.
/// Omit any parameter you do not want to affect.
/// Parameters that end with "?" don't do anything... yet.
/// </summary>
/// <param name="playerID">The player. Omit this to use the local player.</param>
/// <param name="hotbar">Select the hotbar slot by number.</param>
/// <param name="hotbarHand">Select the hotbar hand.</param>
/// <param name="commandLine">Toggle the command line?</param>
/// <param name="inventory">Open inventory?</param>
/// <param name="escape">Open escape menu?</param>
/// <param name="enter">Page return?</param>
/// <param name="debug">Toggle debug display?</param>
/// <param name="next">Select next?</param>
/// <param name="previous">Select previous?</param>
/// <param name="tab">Tab?</param>
/// <param name="colour">Toggle to hotbar colour mode?</param>
/// <param name="hotbarPage">Select the hotbar page by number?</param>
/// <param name="quickSave">Quicksave?</param>
/// <param name="paste">Paste?</param>
public static void GuiInput(uint playerID = uint.MaxValue, int hotbar = -1, bool hotbarHand = false, bool commandLine = false, bool inventory = false, bool escape = false, bool enter = false, bool debug = false, bool next = false, bool previous = false, bool tab = false, bool colour = false, int hotbarPage = -1, bool quickSave = false, bool paste = false)
{
if (playerID == uint.MaxValue)
{
playerID = inputEngine.GetLocalPlayerID();
}
ref InputEntityStruct currentInput = ref inputEngine.GetInputRef(playerID);
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
// set inputs
switch(hotbar)
{
case 0: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_0; break;
case 1: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_1; break;
case 2: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_2; break;
case 3: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_3; break;
case 4: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_4; break;
case 5: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_5; break;
case 6: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_6; break;
case 7: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_7; break;
case 8: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_8; break;
case 9: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_9; break;
case 10: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_Hand; break;
default: break;
}
if (hotbarHand) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_Hand;
if (commandLine) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleCommandLine;
if (inventory) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Inventory;
if (escape) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Escape;
if (enter) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Return;
if (debug) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleDebugDisplay;
if (next) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectNext;
if (previous) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectPrev;
if (tab) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Tab;
if (colour) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_Colour;
switch (hotbarPage)
{
case 1: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage1; break;
case 2: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage2; break;
case 3: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage3; break;
case 4: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage4; break;
case 5: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage5; break;
case 6: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage6; break;
case 7: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage7; break;
case 8: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage8; break;
case 9: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage9; break;
case 10: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage10; break;
default: break;
}
if (quickSave) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.QuickSave;
if (paste) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.PasteSelection;
}
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 primaryRelease = false, bool primaryHeld = false, bool secondaryHeld = false, bool toggleUnitGrid = false, bool ctrl = false, bool toggleColourMode = false, bool scaleBlockUp = false, bool scaleBlockDown = false, bool rotateBlockClockwise = false, bool rotateBlockCounterclockwise = false, bool cutSelection = false, bool copySelection = false, bool deleteSelection = false)
{
if (playerID == uint.MaxValue)
{
playerID = inputEngine.GetLocalPlayerID();
}
ref InputEntityStruct currentInput = ref inputEngine.GetInputRef(playerID);
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
// set inputs
if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleSimulation;
if (forward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Forward;
if (backward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Backward;
if (up) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Up;
if (down) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Down;
if (left) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Left;
if (right) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Right;
if (sprint) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Sprint;
if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode;
if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction;
if (primary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryAction;
if (secondary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryAction;
if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction;
if (primaryRelease) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionRelease;
if (primaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld;
if (secondaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld;
if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid;
if (ctrl) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CtrlAction;
if (toggleColourMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleColourMode;
if (scaleBlockUp) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp;
if (scaleBlockDown) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown;
if (rotateBlockClockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise;
if (rotateBlockCounterclockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise;
if (cutSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CutSelection;
if (copySelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CopySelection;
if (deleteSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.DeleteSelection;
}
public static void Init()
{
GameEngineManager.AddGameEngine(inputEngine);
MenuEngineManager.AddMenuEngine(inputEngine);
}
}
}

View file

@ -0,0 +1,65 @@
using System;
using RobocraftX.Common.Input;
using RobocraftX.Players;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Input
{
public class FakeInputEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIFakeInputEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
public bool IsReady = false;
public void Dispose()
{
IsReady = false;
}
public void Ready()
{
IsReady = true;
}
public bool SendCustomInput(InputEntityStruct input, uint playerID, bool remote = false)
{
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
if (entitiesDB.Exists<InputEntityStruct>(egid))
{
ref InputEntityStruct ies = ref entitiesDB.QueryEntity<InputEntityStruct>(egid);
ies = input;
return true;
}
else return false;
}
public InputEntityStruct GetInput(uint playerID, bool remote = false)
{
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
if (entitiesDB.Exists<InputEntityStruct>(egid))
{
return entitiesDB.QueryEntity<InputEntityStruct>(egid);
}
else return default(InputEntityStruct);
}
public ref InputEntityStruct GetInputRef(uint playerID, bool remote = false)
{
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
return ref entitiesDB.QueryEntity<InputEntityStruct>(egid);
}
public uint GetLocalPlayerID()
{
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB);
}
}
}

View file

@ -0,0 +1,47 @@
using System;
using RobocraftX.Common.Input;
using RobocraftX.Multiplayer.Input;
using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Utility;
using HarmonyLib;
namespace GamecraftModdingAPI.Inventory
{
public static class Hotbar
{
private static readonly HotbarEngine hotbarEngine = new HotbarEngine();
/// <summary>
/// Switch the block in the player's hand
/// </summary>
/// <param name="block">The block to switch to.</param>
/// <param name="playerID">The player. Omit this to use the local player.</param>
public static void EquipBlock(BlockIDs block, uint playerID = uint.MaxValue)
{
if (playerID == uint.MaxValue)
{
playerID = hotbarEngine.GetLocalPlayerID();
}
hotbarEngine.SelectBlock((int) block, playerID);
// cubeSelectedByPick = true will crash the game
// (this would be equivalent to mouse middle click pick block action)
// reason: the game expects a Dictionary entry for the tweaked stats
}
/// <summary>
/// Gets the block in the player's hand
/// </summary>
/// <returns>The equipped block.</returns>
public static BlockIDs GetEquippedBlock()
{
return HotbarSlotSelectionHandlerEnginePatch.EquippedPartID;
}
public static void Init()
{
GameEngineManager.AddGameEngine(hotbarEngine);
}
}
}

View file

@ -0,0 +1,58 @@
using System;
using RobocraftX.Character;
using RobocraftX.GUI.Hotbar;
using RobocraftX.Players;
using RobocraftX.Common;
using RobocraftX.Common.Input;
using RobocraftX.Common.Players;
using Svelto.ECS;
using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Inventory
{
public class HotbarEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIHotbarGameEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
public bool IsInGame = false;
public void Dispose()
{
IsInGame = false;
}
public void Ready()
{
IsInGame = true;
}
public bool SelectBlock(int block, uint playerID, bool cubeSelectedByPick = false)
{
InputEntityStruct[] inputs = entitiesDB.QueryEntities<InputEntityStruct>(InputExclusiveGroups.LocalPlayers).ToFastAccess(out uint count);
if (count == 0) return false;
for (int i = 0; i < count; i++)
{
if (inputs[i].ID.entityID == playerID) {
inputs[i].cubeSelectedByPick = cubeSelectedByPick;
inputs[i].selectedCube = block;
return true;
}
}
// TODO: expose the rest of the input functionality
return false;
}
public uint GetLocalPlayerID()
{
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB);
}
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.Reflection;
using RobocraftX.GUI;
using RobocraftX.GUI.Hotbar;
using Svelto.ECS;
using HarmonyLib;
using GamecraftModdingAPI.Blocks;
namespace GamecraftModdingAPI.Inventory
{
[HarmonyPatch]
public class HotbarSlotSelectionHandlerEnginePatch
{
private static int selectedBlockInt = 0;
public static BlockIDs EquippedPartID { get => (BlockIDs)selectedBlockInt; }
private static MethodInfo PatchedMethod { get; } = AccessTools.Method(AccessTools.TypeByName("RobocraftX.GUI.Hotbar.HotbarSlotSelectionHandlerEngine"), "HandleEquippedCubeChanged", parameters: new Type[] { typeof(uint), typeof(int), typeof(ExclusiveGroupStruct) });
public static void Prefix(uint playerID, int selectedDBPartID, ExclusiveGroupStruct groupID)
{
selectedBlockInt = selectedDBPartID;
}
public static MethodBase TargetMethod(Harmony harmonyInstance)
{
return PatchedMethod;
}
}
}

101
GamecraftModdingAPI/Main.cs Normal file
View file

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using HarmonyLib;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Events;
using GamecraftModdingAPI.Players;
using GamecraftModdingAPI.Tasks;
using uREPL;
namespace GamecraftModdingAPI
{
/// <summary>
/// The main class of the GamecraftModdingAPI.
/// Use this to initialize the API before calling it.
/// </summary>
public static class Main
{
private static Harmony harmony;
public static bool IsInitialized {
get { return harmony != null; }
}
private static int referenceCount = 0;
/// <summary>
/// Initializes the GamecraftModdingAPI.
/// Call this as soon as possible after Gamecraft starts up.
/// Ideally, this should be called from your main Plugin class's OnApplicationStart() method.
/// </summary>
public static void Init()
{
referenceCount++;
if (referenceCount > 1) { return; }
if (IsInitialized)
{
Logging.LogWarning("GamecraftModdingAPI.Main.Init() called but API is already initialized!");
return;
}
Logging.MetaDebugLog($"Patching Gamecraft");
var currentAssembly = Assembly.GetExecutingAssembly();
harmony = new Harmony(currentAssembly.GetName().Name);
harmony.PatchAll(currentAssembly);
// init utility
Logging.MetaDebugLog($"Initializing Utility");
Utility.GameState.Init();
Utility.VersionTracking.Init();
// create default event emitters
Logging.MetaDebugLog($"Initializing Events");
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.ApplicationInitialized, "GamecraftModdingAPIApplicationInitializedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Menu, "GamecraftModdingAPIMenuActivatedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.MenuSwitchedTo, "GamecraftModdingAPIMenuSwitchedToEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Game, "GamecraftModdingAPIGameActivatedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false));
EventManager.AddEventEmitter(GameHostTransitionDeterministicGroupEnginePatch.buildEngine);
EventManager.AddEventEmitter(GameHostTransitionDeterministicGroupEnginePatch.simEngine);
// init block implementors
Logging.MetaDebugLog($"Initializing Blocks");
// init inventory
Inventory.Hotbar.Init();
// init input
Input.FakeInput.Init();
// init object-oriented classes
Player.Init();
Block.Init();
GameClient.Init();
AsyncUtils.Init();
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
}
/// <summary>
/// Shuts down & cleans up the GamecraftModdingAPI.
/// Call this as late as possible before Gamecraft quits.
/// Ideally, this should be called from your main Plugin class's OnApplicationQuit() method.
/// </summary>
public static void Shutdown()
{
if (referenceCount > 0) { referenceCount--; }
if (referenceCount == 0)
{
if (!IsInitialized)
{
Logging.LogWarning("GamecraftModdingAPI.Main.Shutdown() called but API is not initialized!");
return;
}
Scheduler.Dispose();
var currentAssembly = Assembly.GetExecutingAssembly();
harmony.UnpatchAll(currentAssembly.GetName().Name);
harmony = null;
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
}
}
}
}

View file

@ -8,16 +8,16 @@ using Svelto.ECS;
using Svelto.ECS.Serialization; using Svelto.ECS.Serialization;
using HarmonyLib; using HarmonyLib;
using TechbloxModdingAPI.Utility; using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Persistence namespace GamecraftModdingAPI.Persistence
{ {
//[HarmonyPatch] - TODO [HarmonyPatch]
class DeserializeFromDiskEntitiesEnginePatch class DeserializeFromDiskEntitiesEnginePatch
{ {
internal static EntitiesDB entitiesDB = null; internal static EntitiesDB entitiesDB = null;
private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0TechbloxModdingAPI\0\0\0"); private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0GamecraftModdingAPI\0\0\0");
public static void Prefix(ref ISerializationData ____serializationData, ref FasterList<byte> ____bytesStream, ref IEntitySerialization ____entitySerializer, bool ____spawnBlocksOnly) public static void Prefix(ref ISerializationData ____serializationData, ref FasterList<byte> ____bytesStream, ref IEntitySerialization ____entitySerializer, bool ____spawnBlocksOnly)
{ {
@ -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 int count), ____serializationData.dataPos); BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out uint 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;

View file

@ -3,16 +3,22 @@
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Serialization; using Svelto.ECS.Serialization;
using TechbloxModdingAPI.Utility; using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Persistence namespace GamecraftModdingAPI.Persistence
{ {
/// <summary> /// <summary>
/// Entity serializer and deserializer interface for storing and retrieving data in a Techblox save file (GameSave.GC). /// Entity serializer and deserializer interface for storing and retrieving data in a Gamecraft save file (GameSave.GC).
/// </summary> /// </summary>
public interface IEntitySerializer : IDeserializationFactory, IQueryingEntitiesEngine public interface IEntitySerializer : IDeserializationFactory, IQueryingEntitiesEngine
{ {
/// <summary> /// <summary>
/// The entity factory used for creating entities and entity components.
/// </summary>
/// <value>The entity factory.</value>
IEntityFactory EntityFactory { set; }
/// <summary>
/// Serialize the entities. /// Serialize the entities.
/// </summary> /// </summary>
/// <returns>Whether serialization was successful.</returns> /// <returns>Whether serialization was successful.</returns>

View file

@ -0,0 +1,21 @@
using System;
using RobocraftX.SaveAndLoad;
using Svelto.ECS;
using HarmonyLib;
namespace GamecraftModdingAPI.Persistence
{
[HarmonyPatch(typeof(SaveAndLoadCompositionRoot), "Compose")]
class SaveAndLoadCompositionRootPatch
{
public static EnginesRoot currentEnginesRoot;
public static void Prefix(EnginesRoot enginesRoot)
{
currentEnginesRoot = enginesRoot;
//SerializerManager.RegisterSerializers(enginesRoot);
}
}
}

View file

@ -7,15 +7,16 @@ using RobocraftX.SaveAndLoad;
using Svelto.DataStructures; using Svelto.DataStructures;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Serialization; using Svelto.ECS.Serialization;
using HarmonyLib;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Persistence using GamecraftModdingAPI.Utility;
using HarmonyLib;
namespace GamecraftModdingAPI.Persistence
{ {
//[HarmonyPatch] - TODO [HarmonyPatch]
class SaveGameEnginePatch class SaveGameEnginePatch
{ {
private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0TechbloxModdingAPI\0\0\0"); private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0GamecraftModdingAPI\0\0\0");
public static void Postfix(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer) public static void Postfix(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer)
{ {
@ -25,24 +26,24 @@ namespace TechbloxModdingAPI.Persistence
Logging.MetaDebugLog("Skipping component serialization: no serializers registered!"); Logging.MetaDebugLog("Skipping component serialization: no serializers registered!");
return; return;
} }
serializationData.data.IncreaseCapacityBy((uint)frameStart.Length); serializationData.data.ExpandBy((uint)frameStart.Length);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int buffLen), serializationData.dataPos); BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint 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 GamecraftModdingAPI-serialized components
for (int i = 0; i < frameStart.Length; i++) for (int i = 0; i < frameStart.Length; i++)
{ {
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.IncreaseCapacityBy(4u); serializationData.data.ExpandBy(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.IncreaseCapacityBy(4u + (uint)serializerKeys[c].Length); serializationData.data.ExpandBy(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 +52,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.IncreaseCapacityBy(4u); serializationData.data.ExpandBy(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}");
@ -73,8 +74,8 @@ namespace TechbloxModdingAPI.Persistence
} }
public static MethodBase TargetMethod() public static MethodBase TargetMethod()
{ {
return AccessTools.TypeByName("RobocraftX.SaveAndLoad.SaveGameEngine").GetMethod("SerializeGameToBuffer"); return typeof(SaveGameEngine).GetMethod("SerializeGameToBuffer");
} }
} }
} }

View file

@ -4,14 +4,15 @@ using System.Linq;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Serialization; using Svelto.ECS.Serialization;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Persistence using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Persistence
{ {
/// <summary> /// <summary>
/// Keeps track of serializers. /// Keeps track of serializers.
/// This is used to add and retrieve serializers. /// This is used to add and retrieve serializers.
/// Added IEntitySerializations are used in serializing and deserializing Techblox save files (GameSave.GC). /// Added IEntitySerializations are used in serializing and deserializing Gamecraft save files (GameSave.GC).
/// </summary> /// </summary>
public static class SerializerManager public static class SerializerManager
{ {
@ -28,6 +29,7 @@ namespace TechbloxModdingAPI.Persistence
_registrations[name] = (IEntitySerialization ies) => { ies.RegisterSerializationFactory<T>(serializer); }; _registrations[name] = (IEntitySerialization ies) => { ies.RegisterSerializationFactory<T>(serializer); };
if (_lastEnginesRoot != null) if (_lastEnginesRoot != null)
{ {
serializer.EntityFactory = _lastEnginesRoot.GenerateEntityFactory();
_registrations[name].Invoke(_lastEnginesRoot.GenerateEntitySerializer()); _registrations[name].Invoke(_lastEnginesRoot.GenerateEntitySerializer());
_lastEnginesRoot.AddEngine(serializer); _lastEnginesRoot.AddEngine(serializer);
} }
@ -58,13 +60,15 @@ namespace TechbloxModdingAPI.Persistence
return _serializers.Count; return _serializers.Count;
} }
internal static void RegisterSerializers(EnginesRoot enginesRoot) public static void RegisterSerializers(EnginesRoot enginesRoot)
{ {
_lastEnginesRoot = enginesRoot; _lastEnginesRoot = enginesRoot;
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
IEntitySerialization ies = enginesRoot.GenerateEntitySerializer(); IEntitySerialization ies = enginesRoot.GenerateEntitySerializer();
foreach (string key in _serializers.Keys) foreach (string key in _serializers.Keys)
{ {
Logging.MetaDebugLog($"Registering IEntitySerializer for {key}"); Logging.MetaDebugLog($"Registering IEntitySerializer for {key}");
_serializers[key].EntityFactory = factory;
_registrations[key].Invoke(ies); _registrations[key].Invoke(ies);
enginesRoot.AddEngine(_serializers[key]); enginesRoot.AddEngine(_serializers[key]);
} }

View file

@ -5,7 +5,7 @@ using Svelto.ECS.Serialization;
using RobocraftX.Common; using RobocraftX.Common;
namespace TechbloxModdingAPI.Persistence namespace GamecraftModdingAPI.Persistence
{ {
/// <summary> /// <summary>
/// Simple entity serializer sufficient for simple entity components. /// Simple entity serializer sufficient for simple entity components.
@ -21,18 +21,20 @@ namespace TechbloxModdingAPI.Persistence
protected int serializationType; protected int serializationType;
public IEntityFactory EntityFactory { set; protected get; }
public EntitiesDB entitiesDB { set; protected get; } public EntitiesDB entitiesDB { set; protected get; }
public EntityInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory, bool enginesRootIsDeserializationOnly) public EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor, int serializationType, IEntitySerialization entitySerialization)
{ {
EntityInitializer esi = factory.BuildEntity<Descriptor>(egid); EntityComponentInitializer esi = EntityFactory.BuildEntity<Descriptor>(egid);
entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref esi, serializationType); entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref esi, serializationType);
return esi; return esi;
} }
public bool Deserialize(ref ISerializationData serializationData, IEntitySerialization entitySerializer) public bool Deserialize(ref ISerializationData serializationData, IEntitySerialization entitySerializer)
{ {
BinaryBufferReader bbr = new BinaryBufferReader(serializationData.data.ToArrayFast(out int count), serializationData.dataPos); BinaryBufferReader bbr = new BinaryBufferReader(serializationData.data.ToArrayFast(out uint 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 +48,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.IncreaseCapacityBy(4u); serializationData.data.ExpandBy(4u);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int count), serializationData.dataPos); BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint 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;

View file

@ -0,0 +1,255 @@
using System;
using Unity.Mathematics;
using RobocraftX.Common;
using GamecraftModdingAPI.Players;
namespace GamecraftModdingAPI
{
/// <summary>
/// An in-game player character. Any Leo you see is a player.
/// </summary>
public class Player
{
// static functionality
private static PlayerEngine playerEngine = new PlayerEngine();
/// <summary>
/// Checks if the specified player exists.
/// </summary>
/// <returns>Whether the player exists.</returns>
/// <param name="player">Player type.</param>
public static bool Exists(PlayerType player)
{
switch (player)
{
case PlayerType.Remote:
return playerEngine.GetRemotePlayer() != uint.MaxValue;
case PlayerType.Local:
return playerEngine.GetLocalPlayer() != uint.MaxValue;
}
return false;
}
/// <summary>
/// Checks if the specified player exists.
/// </summary>
/// <returns>Whether the player exists.</returns>
/// <param name="player">The player's unique identifier.</param>
public static bool Exists(uint player)
{
return playerEngine.ExistsById(player);
}
/// <summary>
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.Player"/> class.
/// </summary>
/// <param name="id">The player's unique identifier.</param>
public Player(uint id)
{
this.Id = id;
if (!Exists(id))
{
throw new PlayerNotFoundException($"No player with id {id} exists");
}
this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.Player"/> class.
/// </summary>
/// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
public Player(PlayerType player)
{
uint localId = playerEngine.GetLocalPlayer();
switch (player)
{
case PlayerType.Local:
this.Id = playerEngine.GetLocalPlayer();
break;
case PlayerType.Remote:
this.Id = playerEngine.GetRemotePlayer();
break;
}
if (this.Id == uint.MaxValue)
{
throw new PlayerNotFoundException($"No player of {player} type exists");
}
this.Type = player;
}
// object fields & properties
/// <summary>
/// The player's type.
/// The player type is always relative to the current client, not the game host.
/// </summary>
/// <value>The enumerated player type.</value>
public PlayerType Type { get; }
/// <summary>
/// The player's unique identifier.
/// </summary>
/// <value>The identifier.</value>
public uint Id { get; private set; }
/// <summary>
/// The player's current position.
/// </summary>
/// <value>The position.</value>
public float3 Position
{
get
{
return playerEngine.GetLocation(Id);
}
set
{
playerEngine.SetLocation(Id, value, false);
}
}
/// <summary>
/// The player's current rotation.
/// </summary>
/// <value>The rotation.</value>
public quaternion Rotation
{
get
{
return playerEngine.GetRotation(Id);
}
set
{
playerEngine.SetRotation(Id, value);
}
}
/// <summary>
/// The player's current velocity.
/// </summary>
/// <value>The velocity.</value>
public float3 Velocity
{
get
{
return playerEngine.GetLinearVelocity(Id);
}
set
{
playerEngine.SetLinearVelocity(Id, value);
}
}
/// <summary>
/// The player's current angular velocity.
/// </summary>
/// <value>The angular velocity.</value>
public float3 AngularVelocity
{
get
{
return playerEngine.GetAngularVelocity(Id);
}
set
{
playerEngine.SetAngularVelocity(Id, value);
}
}
/// <summary>
/// The player's mass.
/// </summary>
/// <value>The mass.</value>
public float Mass
{
get
{
return 1f / playerEngine.GetMass(Id).InverseMass;
}
// FIXME: Setting mass doesn't do anything
/*set
{
playerEngine.SetInverseMass(Id, 1f / value);
}*/
}
private float _ping = -1f;
/// <summary>
/// The player's latest network ping time.
/// </summary>
/// <value>The ping (s).</value>
public float Ping
{
get
{
float? temp = playerEngine.GetLastPingTime(Id, Type);
if (temp.HasValue)
{
_ping = temp.Value;
}
return _ping;
}
}
// object methods
/// <summary>
/// Teleport the player to the specified coordinates.
/// </summary>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
/// <param name="z">The z coordinate.</param>
/// <param name="relative">If set to <c>true</c> teleport relative to the player's current position.</param>
/// <param name="exitSeat">If set to <c>true</c> exit any seat the player is in.</param>
public void Teleport(float x, float y, float z, bool relative = true, bool exitSeat = true)
{
float3 location = new float3(x, y, z);
if (relative)
{
location += playerEngine.GetLocation(Id);
}
playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
}
/// <summary>
/// Returns the block the player is currently looking at.
/// </summary>
/// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
/// <returns>The block or null if not found</returns>
public Block GetBlockLookedAt(float maxDistance = -1f)
{
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
return egid.HasValue && egid.Value.groupID == CommonExclusiveGroups.OWNED_BLOCKS_GROUP
? new Block(egid.Value)
: null;
}
/// <summary>
/// Returns the rigid body the player is currently looking at during simulation.
/// </summary>
/// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
/// <returns>The block or null if not found</returns>
public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
{
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
return egid.HasValue && egid.Value.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
? new SimBody(egid.Value)
: null;
}
// internal methods
public static void Init()
{
Utility.GameEngineManager.AddGameEngine(playerEngine);
}
}
}

View file

@ -0,0 +1,256 @@
using System;
using System.Runtime.CompilerServices;
using RobocraftX.Character;
using RobocraftX.Character.Movement;
using RobocraftX.Common.Players;
using RobocraftX.Common.Input;
using RobocraftX.Physics;
using Svelto.ECS;
using Unity.Mathematics;
using Unity.Physics;
using GamecraftModdingAPI.Engines;
using RobocraftX.Blocks.Ghost;
using RobocraftX.Character.Camera;
using RobocraftX.Character.Factories;
namespace GamecraftModdingAPI.Players
{
internal class PlayerEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIPlayerGameEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
private bool isReady = false;
public void Dispose()
{
isReady = false;
}
public void Ready()
{
isReady = true;
}
public uint GetLocalPlayer()
{
if (!isReady) return uint.MaxValue;
PlayerIDStruct[] localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToFastAccess(out uint count);
if (count > 0)
{
return localPlayers[0].ID.entityID;
}
return uint.MaxValue;
}
public uint GetRemotePlayer()
{
if (!isReady) return uint.MaxValue;
PlayerIDStruct[] localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToFastAccess(out uint count);
if (count > 0)
{
return localPlayers[0].ID.entityID;
}
return uint.MaxValue;
}
public bool ExistsById(uint playerId)
{
PlayerIDStruct[] players = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToFastAccess(out uint count);
for (int i = 0; i < count; i++)
{
if (players[i].ID.entityID == playerId)
{
return true;
}
}
players = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToFastAccess(out count);
for (int i = 0; i < count; i++)
{
if (players[i].ID.entityID == playerId)
{
return true;
}
}
return false;
}
public float3 GetLocation(uint playerId)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
return rbes.position;
}
return float3.zero;
}
public bool SetLocation(uint playerId, float3 location, bool exitSeat = true)
{
ExclusiveGroup[] characterGroups = CharacterExclusiveGroups.AllCharacters;
for (int i = 0; i < characterGroups.Length; i++)
{
EGID egid = new EGID(playerId, characterGroups[i]);
if (entitiesDB.Exists<RigidBodyEntityStruct>(egid))
{
ref RigidBodyEntityStruct rbes = ref entitiesDB.QueryEntity<RigidBodyEntityStruct>(egid);
if (characterGroups[i] == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
{
entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
}
rbes.position = location;
return true;
}
}
return false;
}
public quaternion GetRotation(uint playerId)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
return rbes.rotation;
}
return quaternion.identity;
}
public bool SetRotation(uint playerId, quaternion value)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
rbes.rotation = value;
return true;
}
return false;
}
public float3 GetLinearVelocity(uint playerId)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
return rbes.velocity;
}
return float3.zero;
}
public bool SetLinearVelocity(uint playerId, float3 value)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
rbes.velocity = value;
return true;
}
return false;
}
public float3 GetAngularVelocity(uint playerId)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
return rbes.angularVelocity;
}
return float3.zero;
}
public bool SetAngularVelocity(uint playerId, float3 value)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
rbes.angularVelocity = value;
return true;
}
return false;
}
public PhysicsMass GetMass(uint playerId)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
return rbes.physicsMass;
}
return default;
}
public bool SetInverseMass(uint playerId, float inverseMass)
{
if (GetCharacterStruct<RigidBodyEntityStruct>(playerId, out RigidBodyEntityStruct rbes))
{
rbes.physicsMass.InverseInertia = inverseMass;
return true;
}
return false;
}
public float? GetLastPingTime(uint playerId, PlayerType type)
{
EGID egid = new EGID(playerId, GroupFromEnum(type));
if (entitiesDB.Exists<PlayerNetworkStatsEntityStruct>(egid))
{
return entitiesDB.QueryEntity<PlayerNetworkStatsEntityStruct>(egid).lastPingTimeSinceLevelLoad;
}
return null;
}
// reusable methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ExclusiveGroup GroupFromEnum(PlayerType type)
{
return type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetCharacterStruct<T>(uint playerId, out T s) where T : unmanaged, IEntityComponent
{
ExclusiveGroup[] characterGroups = CharacterExclusiveGroups.AllCharacters;
for (int i = 0; i < characterGroups.Length; i++)
{
EGID egid = new EGID(playerId, characterGroups[i]);
if (entitiesDB.Exists<T>(egid))
{
s = entitiesDB.QueryEntity<T>(egid);
return true;
}
}
s = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetPlayerStruct<T>(uint playerId, out T s) where T : unmanaged, IEntityComponent
{
ExclusiveGroup[] playerGroups = PlayersExclusiveGroups.AllPlayers;
for (int i = 0; i < playerGroups.Length; i++)
{
EGID egid = new EGID(playerId, playerGroups[i]);
if (entitiesDB.Exists<T>(egid))
{
s = entitiesDB.QueryEntity<T>(egid);
return true;
}
}
s = default;
return false;
}
public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f)
{
if (!entitiesDB.TryQueryMappedEntities<CharacterCameraRayCastEntityStruct>(
CameraExclusiveGroups.CameraGroup, out var mapper))
return null;
mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast);
float distance = maxDistance < 0
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast)
: maxDistance;
if (rayCast.hit && rayCast.distance <= distance)
return rayCast.hitEgid;
return null;
}
}
}

View file

@ -1,7 +1,7 @@
using System; using System;
namespace TechbloxModdingAPI.Players namespace GamecraftModdingAPI.Players
{ {
public class PlayerException : TechbloxModdingAPIException public class PlayerException : GamecraftModdingAPIException
{ {
public PlayerException() public PlayerException()
{ {

View file

@ -1,5 +1,5 @@
using System; using System;
namespace TechbloxModdingAPI.Players namespace GamecraftModdingAPI.Players
{ {
public enum PlayerType public enum PlayerType
{ {

View file

@ -1,45 +1,27 @@
using System; using RobocraftX.Common;
using RobocraftX.Physics;
using Svelto.ECS; using Svelto.ECS;
using Unity.Mathematics; using Unity.Mathematics;
using UnityEngine; using UnityEngine;
using Gamecraft.Damage; namespace GamecraftModdingAPI
using RobocraftX.Common;
using RobocraftX.Physics;
using Techblox.TimeRunning.Clusters;
namespace TechbloxModdingAPI
{ {
/// <summary> /// <summary>
/// A rigid body (like a chunk of connected blocks) during simulation. /// A rigid body (like a cluster of connected blocks) during simulation.
/// </summary> /// </summary>
public class SimBody : EcsObjectBase, IEquatable<SimBody>, IEquatable<EGID> public class SimBody
{ {
/// <summary> public EGID Id { get; }
/// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist.
/// Get the SimBody from a Block if possible for good performance here.
/// </summary>
public Cluster Cluster => cluster ??= 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; public SimBody(EGID id)
private readonly uint clusterId = uint.MaxValue;
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))
{ {
} }
internal SimBody(uint id, uint clusterID) : this(id)
{
clusterId = clusterID;
}
/// <summary> /// <summary>
/// The position of this body. When setting the position, update the position of the connected bodies as well, /// The position of this body. When setting the position, update the position of the connected bodies as well,
/// otherwise unexpected forces may arise. /// otherwise unexpected forces may arise.
@ -74,46 +56,22 @@ namespace TechbloxModdingAPI
} }
} }
[Obsolete] //Cannot get mass even from UECS
public float Mass public float Mass
{ {
get => 0f; get => math.rcp(GetStruct().physicsMass.InverseMass);
//set => GetStruct().physicsMass.InverseMass = math.rcp(value); //set => GetStruct().physicsMass.InverseMass = math.rcp(value);
} }
public float3 CenterOfMass public float3 CenterOfMass
{ {
get => 0f; //TODO get => GetStruct().physicsMass.CenterOfMass;
//set => GetStruct().physicsMass.CenterOfMass = value; //set => GetStruct().physicsMass.CenterOfMass = value;
} }
public float Volume
{
get => GetStruct().volume;
}
public float InitialHealth
{
get => 0f;
set { }
}
public float CurrentHealth
{
get => 0f;
set { }
}
public float HealthMultiplier
{
get => 0f;
set { }
}
/// <summary> /// <summary>
/// Whether the body can be moved or static. /// Whether the body can be moved or static.
/// </summary> /// </summary>
public bool Static => Block.BlockEngine.GetBlockInfo<MassEntityStruct>(this).isStatic; //Setting it doesn't have any effect public bool Static => Block.BlockEngine.GetBlockInfo<MassEntityStruct>(Id).isStatic; //Setting it doesn't have any effect
/// <summary> /// <summary>
/// The rigid bodies connected to this one via functional joints (broken ones don't count). /// The rigid bodies connected to this one via functional joints (broken ones don't count).
@ -123,18 +81,9 @@ namespace TechbloxModdingAPI
return Block.BlockEngine.GetConnectedSimBodies(Id.entityID); return Block.BlockEngine.GetConnectedSimBodies(Id.entityID);
} }
/// <summary>
/// The blocks that form this rigid body.
/// </summary>
/// <returns></returns>
public Block[] GetBlocks()
{
return Block.BlockEngine.GetBodyBlocks(Id.entityID);
}
private ref RigidBodyEntityStruct GetStruct() private ref RigidBodyEntityStruct GetStruct()
{ {
return ref Block.BlockEngine.GetBlockInfo<RigidBodyEntityStruct>(this); return ref Block.BlockEngine.GetBlockInfo<RigidBodyEntityStruct>(Id);
} }
public override string ToString() public override string ToString()
@ -142,31 +91,6 @@ namespace TechbloxModdingAPI
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Mass)}: {Mass}, {nameof(Static)}: {Static}"; return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Mass)}: {Mass}, {nameof(Static)}: {Static}";
} }
public bool Equals(SimBody other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id.Equals(other.Id);
}
public bool Equals(EGID other)
{
return Id.Equals(other);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((SimBody) obj);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
/// <summary> /// <summary>
/// Returns the object identified by the given ID (A-Z). /// Returns the object identified by the given ID (A-Z).
/// This has the same result as calling ObjectIdentifier.GetByID(id) and then GetRigidBody() with the duplicates filtered out. /// This has the same result as calling ObjectIdentifier.GetByID(id) and then GetRigidBody() with the duplicates filtered out.

View file

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Svelto.Tasks; using Svelto.Tasks;
namespace TechbloxModdingAPI.Tasks namespace GamecraftModdingAPI.Tasks
{ {
/// <summary> /// <summary>
/// Interface for asynchronous tasks /// Interface for asynchronous tasks

View file

@ -7,7 +7,7 @@ using System.Threading.Tasks;
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.Enumerators; using Svelto.Tasks.Enumerators;
namespace TechbloxModdingAPI.Tasks namespace GamecraftModdingAPI.Tasks
{ {
/// <summary> /// <summary>
/// An asynchronous task to be performed once. /// An asynchronous task to be performed once.

View file

@ -7,7 +7,7 @@ using System.Threading.Tasks;
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.Enumerators; using Svelto.Tasks.Enumerators;
namespace TechbloxModdingAPI.Tasks namespace GamecraftModdingAPI.Tasks
{ {
/// <summary> /// <summary>
/// An asynchronous repeating task. /// An asynchronous repeating task.

View file

@ -7,7 +7,7 @@ using System.Threading.Tasks;
using Svelto.Tasks.Lean; using Svelto.Tasks.Lean;
using Svelto.Tasks.ExtraLean; using Svelto.Tasks.ExtraLean;
namespace TechbloxModdingAPI.Tasks namespace GamecraftModdingAPI.Tasks
{ {
/// <summary> /// <summary>
/// Asynchronous task scheduling for ISchedulables. /// Asynchronous task scheduling for ISchedulables.
@ -20,7 +20,7 @@ namespace TechbloxModdingAPI.Tasks
{ {
get get
{ {
return RobocraftX.Schedulers.ClientLean.UIScheduler; return RobocraftX.Schedulers.Lean.UIScheduler;
} }
} }
@ -28,13 +28,13 @@ namespace TechbloxModdingAPI.Tasks
{ {
get get
{ {
return RobocraftX.Schedulers.ClientExtraLean.UIScheduler; return RobocraftX.Schedulers.ExtraLean.UIScheduler;
} }
} }
public static readonly Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner extraLeanRunner = new Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner("TechbloxModdingAPIExtraLean"); public static readonly Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner extraLeanRunner = new Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner("GamecraftModdingAPIExtraLean");
public static readonly Svelto.Tasks.Lean.Unity.UpdateMonoRunner leanRunner = new Svelto.Tasks.Lean.Unity.UpdateMonoRunner("TechbloxModdingAPILean"); public static readonly Svelto.Tasks.Lean.Unity.UpdateMonoRunner leanRunner = new Svelto.Tasks.Lean.Unity.UpdateMonoRunner("GamecraftModdingAPILean");
/// <summary> /// <summary>
/// Schedule a task to run asynchronously. /// Schedule a task to run asynchronously.
@ -42,7 +42,7 @@ namespace TechbloxModdingAPI.Tasks
/// </summary> /// </summary>
/// <param name="toRun">The task to run</param> /// <param name="toRun">The task to run</param>
/// <param name="extraLean">Schedule toRun on an extra lean runner?</param> /// <param name="extraLean">Schedule toRun on an extra lean runner?</param>
/// <param name="ui">Schedule toRun on Techblox's built-in UI task runner?</param> /// <param name="ui">Schedule toRun on Gamecraft's built-in UI task runner?</param>
public static void Schedule(ISchedulable toRun, bool extraLean = false, bool ui = false) public static void Schedule(ISchedulable toRun, bool extraLean = false, bool ui = false)
{ {
if (extraLean) if (extraLean)

View file

@ -0,0 +1,346 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;
using IllusionInjector;
// test
using Svelto.ECS;
using RobocraftX.Blocks;
using RobocraftX.Common;
using RobocraftX.SimulationModeState;
using RobocraftX.FrontEnd;
using Unity.Mathematics;
using GamecraftModdingAPI.Commands;
using GamecraftModdingAPI.Events;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Players;
namespace GamecraftModdingAPI.Tests
{
// unused by design
/// <summary>
/// Modding API implemented as a standalone IPA Plugin.
/// Ideally, GamecraftModdingAPI should be loaded by another mod; not itself
/// </summary>
public class GamecraftModdingAPIPluginTest
#if DEBUG
: IllusionPlugin.IEnhancedPlugin
#endif
{
private static Harmony harmony { get; set; }
public string[] Filter { get; } = new string[] { "Gamecraft", "GamecraftPreview" };
public string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
public string HarmonyID { get; } = "org.git.exmods.modtainers.gamecraftmoddingapi";
public void OnApplicationQuit()
{
GamecraftModdingAPI.Main.Shutdown();
}
public void OnApplicationStart()
{
FileLog.Reset();
Harmony.DEBUG = true;
GamecraftModdingAPI.Main.Init();
Logging.MetaDebugLog($"Version group id {(uint)ApiExclusiveGroups.versionGroup}");
// in case Steam is not installed/running
// this will crash the game slightly later during startup
//SteamInitPatch.ForcePassSteamCheck = true;
// in case running in a VM
//MinimumSpecsCheckPatch.ForcePassMinimumSpecCheck = true;
// disable some Gamecraft analytics
//AnalyticsDisablerPatch.DisableAnalytics = true;
// 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
HandlerBuilder.Builder()
.Name("appinit API debug")
.Handle(EventType.ApplicationInitialized)
.OnActivation(() => { Logging.Log("App Inited event!"); })
.Build();
HandlerBuilder.Builder("menuact API debug")
.Handle(EventType.Menu)
.OnActivation(() => { Logging.Log("Menu Activated event!"); })
.OnDestruction(() => { Logging.Log("Menu Destroyed event!"); })
.Build();
HandlerBuilder.Builder("menuswitch API debug")
.Handle(EventType.MenuSwitchedTo)
.OnActivation(() => { Logging.Log("Menu Switched To event!"); })
.Build();
HandlerBuilder.Builder("gameact API debug")
.Handle(EventType.Menu)
.OnActivation(() => { Logging.Log("Game Activated event!"); })
.OnDestruction(() => { Logging.Log("Game Destroyed event!"); })
.Build();
HandlerBuilder.Builder("gamerel API debug")
.Handle(EventType.GameReloaded)
.OnActivation(() => { Logging.Log("Game Reloaded event!"); })
.Build();
HandlerBuilder.Builder("gameswitch API debug")
.Handle(EventType.GameSwitchedTo)
.OnActivation(() => { Logging.Log("Game Switched To event!"); })
.Build();
HandlerBuilder.Builder("simulationswitch API debug")
.Handle(EventType.SimulationSwitchedTo)
.OnActivation(() => { Logging.Log("Game Mode Simulation Switched To event!"); })
.Build();
HandlerBuilder.Builder("buildswitch API debug")
.Handle(EventType.BuildSwitchedTo)
.OnActivation(() => { Logging.Log("Game Mode Build Switched To event!"); })
.Build();
HandlerBuilder.Builder("menu activated API error thrower test")
.Handle(EventType.Menu)
.OnActivation(() => { throw new Exception("Event Handler always throws an exception!"); })
.Build();
// debug/test commands
if (Dependency.Hell("ExtraCommands"))
{
CommandBuilder.Builder()
.Name("Exit")
.Description("Close Gamecraft immediately, without any prompts")
.Action(() => { UnityEngine.Application.Quit(); })
.Build();
CommandBuilder.Builder()
.Name("SetFOV")
.Description("Set the player camera's field of view")
.Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
.Build();
CommandBuilder.Builder()
.Name("MoveLastBlock")
.Description("Move the most-recently-placed block, and any connected blocks by the given offset")
.Action((float x, float y, float z) =>
{
if (GameState.IsBuildMode())
foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
block.Position += new Unity.Mathematics.float3(x, y, z);
else
GamecraftModdingAPI.Utility.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.AluminiumCube, 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.AluminiumCube, new float3(x + i, y, z + j));
//Block.Sync();
sw.Stop();
Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
})
.Build();
//With Sync(): 1135ms
//Without Sync(): 134ms
//Async: 348 794ms, doesn't freeze game
//Without Sync() but wait for submission: 530ms
//With Sync() at the end: 380ms
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 =
new BlockColor {Color = color};
Logging.CommandLog("Colored block to " + color);
}).Build();
CommandBuilder.Builder("GetBlockByID", "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();
GameClient.SetDebugInfo("lookedAt", LookedAt);
GameClient.SetDebugInfo("InstalledMods", InstalledMods);
/*
CommandManager.AddCommand(new SimpleCustomCommandEngine<float>((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
"SetFOV", "Set the player camera's field of view"));
CommandManager.AddCommand(new SimpleCustomCommandEngine<float, float, float>(
(x, y, z) => {
bool success = GamecraftModdingAPI.Blocks.Movement.MoveConnectedBlocks(
GamecraftModdingAPI.Blocks.BlockIdentifiers.LatestBlockID,
new Unity.Mathematics.float3(x, y, z));
if (!success)
{
GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
}
}, "MoveLastBlock", "Move the most-recently-placed block, and any connected blocks by the given offset"));
CommandManager.AddCommand(new SimpleCustomCommandEngine<float, float, float>(
(x, y, z) => { Blocks.Placement.PlaceBlock(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); },
"PlaceAluminium", "Place a block of aluminium at the given coordinates"));
System.Random random = new System.Random(); // for command below
CommandManager.AddCommand(new SimpleCustomCommandEngine(
() => {
if (!GameState.IsSimulationMode())
{
Logging.CommandLogError("You must be in simulation mode for this to work!");
return;
}
Tasks.Repeatable task = new Tasks.Repeatable(() => {
uint count = 0;
EGID[] eBlocks = Blocks.Signals.GetElectricBlocks();
for (uint i = 0u; i < eBlocks.Length; i++)
{
uint[] ids = Blocks.Signals.GetSignalIDs(eBlocks[i]);
for (uint j = 0u; j < ids.Length; j++)
{
Blocks.Signals.SetSignalByID(ids[j], (float)random.NextDouble());
count++;
}
}
Logging.MetaDebugLog($"Did the thing on {count} inputs");
},
() => { return GameState.IsSimulationMode(); });
Tasks.Scheduler.Schedule(task);
}, "RandomizeSignalsInputs", "Do the thing"));
*/
}
// dependency test
if (Dependency.Hell("GamecraftScripting", new Version("0.0.1.0")))
{
Logging.LogWarning("You're in GamecraftScripting dependency hell");
}
else
{
Logging.Log("Compatible GamecraftScripting detected");
}
}
private Player player;
private string LookedAt()
{
if (player == null)
player = new Player(PlayerType.Local);
if (GameState.IsBuildMode())
{
Block block = player.GetBlockLookedAt();
if (block == null) return "Block: none";
return "Block: " + block.Type + "\nColor: " + block.Color + "\n" + "At: " + block.Position
+ "\nText: " + block.Label;
}
if (GameState.IsSimulationMode())
{
SimBody body = player.GetSimBodyLookedAt();
if (body == null) return "Body: none";
return "Body: " + (body.Static ? "static" : "non-static")
+ "\nAt: " + body.Position + " - rotated: " + body.Rotation
+ "\nWith mass: " + body.Mass + " - center: " + body.CenterOfMass
+ "\nVelocity: " + body.Velocity + " - angular: " + body.AngularVelocity;
}
return "Switching modes...";
}
private string modsString;
private string InstalledMods()
{
if (modsString != null) return modsString;
StringBuilder sb = new StringBuilder("Installed mods:");
foreach (var plugin in PluginManager.Plugins)
sb.Append("\n" + plugin.Name + " - " + plugin.Version);
return modsString = sb.ToString();
}
public void OnFixedUpdate() { }
public void OnLateUpdate() { }
public void OnLevelWasInitialized(int level) { }
public void OnLevelWasLoaded(int level) { }
public void OnUpdate() { }
[HarmonyPatch]
public class MinimumSpecsPatch
{
public static bool Prefix(ref bool __result)
{
__result = true;
return false;
}
public static MethodInfo TargetMethod()
{
return ((Func<bool>)MinimumSpecsCheck.CheckRequirementsMet).Method;
}
}
}
}

View file

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Svelto.ECS; using Svelto.ECS;
namespace TechbloxModdingAPI.Utility namespace GamecraftModdingAPI.Utility
{ {
public static class ApiExclusiveGroups public static class ApiExclusiveGroups
{ {
@ -18,7 +18,7 @@ namespace TechbloxModdingAPI.Utility
{ {
if (_eventsExclusiveGroup == null) if (_eventsExclusiveGroup == null)
{ {
_eventsExclusiveGroup = new ExclusiveGroup("TechbloxModdingAPI EventGroup"); _eventsExclusiveGroup = new ExclusiveGroup("GamecraftModdingAPI EventGroup");
} }
return _eventsExclusiveGroup; return _eventsExclusiveGroup;
} }
@ -34,22 +34,10 @@ namespace TechbloxModdingAPI.Utility
{ {
if (_versionGroup == null) if (_versionGroup == null)
{ {
_versionGroup = new ExclusiveGroup("TechbloxModdingAPI VersionGroup"); _versionGroup = new ExclusiveGroup("GamecraftModdingAPI VersionGroup");
} }
return _versionGroup; return _versionGroup;
} }
} }
private static ExclusiveGroup _customBlockGroup;
public static ExclusiveGroup customBlockGroup
{
get
{
if (_customBlockGroup == null)
_customBlockGroup = new ExclusiveGroup("TechbloxModdingAPI CustomBlockGroup");
return _customBlockGroup;
}
}
} }
} }

View file

@ -0,0 +1,29 @@
using System.Threading.Tasks;
using Svelto.ECS;
namespace GamecraftModdingAPI.Utility
{
public static class AsyncUtils
{
private static AsyncUtilsEngine gameEngine = new AsyncUtilsEngine();
/// <summary>
/// Waits for entity submission asynchronously.
/// </summary>
public static async Task WaitForSubmission()
{
await gameEngine.WaitForSubmission();
}
public static void Setup(EnginesRoot enginesRoot)
{
gameEngine.Setup(enginesRoot.GenerateEntityFunctions(), enginesRoot.GenerateEntityFactory());
}
public static void Init()
{
GameEngineManager.AddGameEngine(gameEngine);
}
}
}

View file

@ -0,0 +1,50 @@
using System.Collections;
using System.Threading.Tasks;
using RobocraftX.Schedulers;
using Svelto.ECS;
using Svelto.Tasks.ExtraLean;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Utility
{
public class AsyncUtilsEngine : IApiEngine
{
private IEntityFunctions _efu;
private IEntityFactory _efa;
private IEnumerator WaitForSubmissionInternal(IEntityFunctions efu, IEntityFactory efa,
EntitiesDB entitiesDB, TaskCompletionSource<object> task)
{
var waitEnumerator = new WaitForSubmissionEnumerator(efu, efa, entitiesDB);
while (waitEnumerator.MoveNext())
yield return null;
task.SetResult(null);
}
public Task WaitForSubmission()
{
var task = new TaskCompletionSource<object>();
WaitForSubmissionInternal(_efu, _efa, entitiesDB, task).RunOn(ExtraLean.EveryFrameStepRunner);
return task.Task;
}
public void Setup(IEntityFunctions efu, IEntityFactory efa)
{
_efu = efu;
_efa = efa;
}
public void Ready()
{
}
public EntitiesDB entitiesDB { get; set; }
public void Dispose()
{
}
public string Name { get; } = "GamecraftModdingAPIAsyncUtilsGameEngine";
public bool isRemovable { get; } = false;
}
}

View file

@ -7,7 +7,7 @@ using System.Threading.Tasks;
using FMODUnity; using FMODUnity;
using FMOD.Studio; using FMOD.Studio;
namespace TechbloxModdingAPI.Utility namespace GamecraftModdingAPI.Utility
{ {
/// <summary> /// <summary>
/// Common operations on audio objects /// Common operations on audio objects

View file

@ -3,17 +3,16 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Text;
using System.Text.Formatting; using System.Text.Formatting;
using TechbloxModdingAPI.Blocks; using GamecraftModdingAPI.Blocks;
using TechbloxModdingAPI.Players; using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Players;
using HarmonyLib; using HarmonyLib;
using RobocraftX.GUI.Debug; using RobocraftX.GUI.Debug;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Experimental; using Svelto.ECS.Experimental;
using TechbloxModdingAPI.Engines;
namespace TechbloxModdingAPI.Utility namespace GamecraftModdingAPI.Utility
{ {
public class DebugInterfaceEngine : IApiEngine public class DebugInterfaceEngine : IApiEngine
{ {
@ -31,8 +30,8 @@ namespace TechbloxModdingAPI.Utility
public void SetInfo(string id, Func<string> contentGetter) => _extraInfo[id] = contentGetter; public void SetInfo(string id, Func<string> contentGetter) => _extraInfo[id] = contentGetter;
public bool RemoveInfo(string id) => _extraInfo.Remove(id); public bool RemoveInfo(string id) => _extraInfo.Remove(id);
public string Name => "TechbloxModdingAPIDebugInterfaceGameEngine"; public string Name { get; } = "GamecraftModdingAPIDebugInterfaceGameEngine";
public bool isRemovable => true; public bool isRemovable { get; } = true;
[HarmonyPatch] [HarmonyPatch]
private class Patch private class Patch
@ -47,9 +46,9 @@ namespace TechbloxModdingAPI.Utility
var array = new CodeInstruction[] var array = new CodeInstruction[]
{ {
new CodeInstruction(OpCodes.Ldloc_0), //StringBuffer new CodeInstruction(OpCodes.Ldloc_0), //StringBuffer
new CodeInstruction(OpCodes.Call, ((Action<StringBuilder>)AddInfo).Method) new CodeInstruction(OpCodes.Call, ((Action<StringBuffer>)AddInfo).Method)
}; };
list.InsertRange(index - 1, array); //-1: ldloc.1 ("local") before ldfld list.InsertRange(index, array);
} }
catch (Exception e) catch (Exception e)
{ {
@ -59,15 +58,13 @@ namespace TechbloxModdingAPI.Utility
return list; return list;
} }
public static void AddInfo(StringBuilder sb) public static void AddInfo(StringBuffer sb)
{ {
foreach (var info in _extraInfo) foreach (var info in _extraInfo)
{ {
try try
{ {
string text = info.Value().Trim(); sb.Append(info.Value() + "\n");
if (text.Length != 0)
sb.Append(text + "\n");
} }
catch (Exception e) catch (Exception e)
{ {

View file

@ -0,0 +1,79 @@
using System;
using IllusionInjector;
using IllusionPlugin;
namespace GamecraftModdingAPI.Utility
{
/// <summary>
/// Simple plugin interaction operations
/// </summary>
public static class Dependency
{
/// <summary>
/// Find a plugin by name
/// </summary>
/// <returns>The plugin.</returns>
/// <param name="name">The plugin's name.</param>
public static IPlugin GetPlugin(string name)
{
foreach(IPlugin plugin in PluginManager.Plugins)
{
if (plugin.Name == name)
{
return plugin;
}
}
return null;
}
/// <summary>
/// Gets the plugin version.
/// This gives priority to the plugin's Version string but falls back to the Assembly's version
/// </summary>
/// <returns>The plugin's version.</returns>
/// <param name="name">The plugin's name.</param>
public static Version GetPluginVersion(string name)
{
IPlugin plugin = GetPlugin(name);
if (plugin != null) {
try
{
return new Version(plugin.Version);
} catch (Exception e) when (
e is ArgumentException
|| e is ArgumentNullException
|| e is ArgumentOutOfRangeException
|| e is FormatException
|| e is OverflowException) {}
return plugin.GetType().Assembly.GetName().Version;
}
return null;
}
// (I'm leaving the auto-generated version)
// <summary>
// Hell the specified name and version.
// </summary>
// <returns>The hell.</returns>
// <param name="name">Name.</param>
// <param name="version">Version.</param>
/// <summary>
/// Detect if you're in dependency hell with respect to the plugin.
/// ie Check if the plugin doesn't exist or is out of date.
/// When version is null, this only checks if the plugin exists.
/// The version is retrieved using GetPluginVersion(string name).
/// </summary>
/// <returns>Are you in dependency hell?</returns>
/// <param name="name">The plugin's name'</param>
/// <param name="version">The target version.</param>
public static bool Hell(string name, Version version = null)
{
Version pluginVersion = GetPluginVersion(name);
if (version == null) {
return pluginVersion == null;
}
return (pluginVersion == null || pluginVersion < version);
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using RobocraftX.StateSync;
using Svelto.ECS;
using HarmonyLib;
namespace GamecraftModdingAPI.Utility
{
[HarmonyPatch(typeof(DeterministicStepCompositionRoot), "ResetWorld")]
public static class DeterministicStepCompositionRootPatch
{
private static SimpleEntitiesSubmissionScheduler engineRootScheduler;
public static void Postfix(SimpleEntitiesSubmissionScheduler scheduler)
{
engineRootScheduler = scheduler;
}
internal static void SubmitEntitiesNow()
{
if (engineRootScheduler != null)
engineRootScheduler.SubmitEntities();
}
}
}

View file

@ -1,34 +1,32 @@
using DataLoader; using System;
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.CR.MainGame; using RobocraftX.Common.Utilities;
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.ECS; using Svelto.ECS;
using Svelto.ECS.GUI; using Svelto.ECS.Schedulers.Unity;
using Techblox.GameSelection;
using UnityEngine; using UnityEngine;
using Unity.Entities; using Unity.Entities;
using Unity.Physics.Systems; using Unity.Physics.Systems;
namespace TechbloxModdingAPI.Utility namespace GamecraftModdingAPI.Utility
{ {
/// <summary> /// <summary>
/// Public access to the private variables in RobocraftX.FullGameCompositionRoot /// Public access to the private variables in RobocraftX.FullGameCompositionRoot
/// </summary> /// </summary>
public static class FullGameFields public static class FullGameFields
{ {
public static FullGameCompositionRoot Instance
{
private set;
get;
} = null;
public static MultiplayerInitParameters _multiplayerParams public static MultiplayerInitParameters _multiplayerParams
{ { get
get
{ {
return (MultiplayerInitParameters)fgcr?.Field("_multiplayerParams").GetValue(); return (MultiplayerInitParameters)fgcr?.Field("_multiplayerParams").GetValue();
} }
@ -66,6 +64,14 @@ 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
@ -98,6 +104,14 @@ namespace TechbloxModdingAPI.Utility
} }
} }
public static PhysicsUtility _physicsUtility
{
get
{
return (PhysicsUtility)fgcr?.Field("_physicsUtility").GetValue();
}
}
/*public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler /*public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler
{ {
get get
@ -122,11 +136,11 @@ namespace TechbloxModdingAPI.Utility
} }
} }
public static ECSMainGameResourceManagers _managers public static ECSGameObjectResourceManager _eCsGameObjectResourceManager
{ {
get get
{ {
return (ECSMainGameResourceManagers)fgcr?.Field("_gameManagers").GetValue(); return (ECSGameObjectResourceManager)fgcr?.Field("_eCsGameObjectResourceManager").GetValue();
} }
} }
@ -138,36 +152,11 @@ namespace TechbloxModdingAPI.Utility
} }
} }
public static FasterList<EGID> _deserialisedBlockMap
{
get
{
return (FasterList<EGID>) fgcr?.Field("_deserialisedBlockMap").GetValue();
}
}
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)
{ {
fgcr = new Traverse(instance); fgcr = new Traverse(instance);
FullGameFields.Instance = instance;
} }
} }
} }

View file

@ -0,0 +1,30 @@
using System;
using GamecraftModdingAPI.Blocks;
namespace GamecraftModdingAPI.Utility
{
public static class GameClient
{
private static DebugInterfaceEngine _engine = new DebugInterfaceEngine();
/// <summary>
/// Saves the extra information to be displayed on the debug view.
/// The provided getter function is called each time the view updates so make sure it returns quickly.
/// </summary>
/// <param name="id">A global ID for the custom information</param>
/// <param name="contentGetter">A function that returns the current information</param>
public static void SetDebugInfo(string id, Func<string> contentGetter) => _engine.SetInfo(id, contentGetter);
/// <summary>
/// Removes an information provided by a plugin.
/// </summary>
/// <param name="id">The ID of the custom information</param>
/// <returns></returns>
public static bool RemoveDebugInfo(string id) => _engine.RemoveInfo(id);
public static void Init()
{
GameEngineManager.AddGameEngine(_engine);
}
}
}

View file

@ -1,11 +1,14 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RobocraftX.StateSync;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Engines;
namespace TechbloxModdingAPI.Utility using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Utility
{ {
/// <summary> /// <summary>
/// Keeps track of custom game-modifying engines /// Keeps track of custom game-modifying engines
@ -23,10 +26,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 (engine is IFactoryEngine factoryEngine) if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType()))
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory(); {
if (engine is IFunEngine funEngine) ((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory();
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions(); }
} }
} }
@ -58,23 +61,18 @@ namespace TechbloxModdingAPI.Utility
} }
} }
public static void RegisterEngines(StateSyncRegistrationHelper helper) public static void RegisterEngines(EnginesRoot 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}");
if (_gameEngines[key] is IDeterministicEngine detEngine) enginesRoot.AddEngine(_gameEngines[key]);
helper.AddDeterministicEngine(detEngine); if (typeof(IFactoryEngine).IsAssignableFrom(_gameEngines[key].GetType()))
else {
enginesRoot.AddEngine(_gameEngines[key]); ((IFactoryEngine)_gameEngines[key]).Factory = factory;
if (_gameEngines[key] is IFactoryEngine factEngine) }
factEngine.Factory = factory;
if (_gameEngines[key] is IFunEngine funEngine)
funEngine.Functions = functions;
} }
} }
} }

View file

@ -4,10 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TechbloxModdingAPI.Utility namespace GamecraftModdingAPI.Utility
{ {
/// <summary> /// <summary>
/// Utility to get the state of the current Techblox game /// Utility to get the state of the current Gamecraft game
/// </summary> /// </summary>
public static class GameState public static class GameState
{ {
@ -34,7 +34,7 @@ namespace TechbloxModdingAPI.Utility
/// <summary> /// <summary>
/// Is a game loaded? /// Is a game loaded?
/// </summary> /// </summary>
/// <returns>Whether Techblox has a game open (false = Main Menu)</returns> /// <returns>Whether Gamecraft has a game open (false = Main Menu)</returns>
public static bool IsInGame() public static bool IsInGame()
{ {
return gameEngine.IsInGame; return gameEngine.IsInGame;

View file

@ -6,13 +6,14 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using RobocraftX.SimulationModeState; using RobocraftX.SimulationModeState;
using TechbloxModdingAPI.Engines;
namespace TechbloxModdingAPI.Utility using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Utility
{ {
class GameStateEngine : IApiEngine class GameStateEngine : IApiEngine
{ {
public string Name { get; } = "TechbloxModdingAPIGameStateGameEngine"; public string Name { get; } = "GamecraftModdingAPIGameStateGameEngine";
public EntitiesDB entitiesDB { set; private get; } public EntitiesDB entitiesDB { set; private get; }

View file

@ -6,11 +6,11 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TechbloxModdingAPI.Utility namespace GamecraftModdingAPI.Utility
{ {
/// <summary> /// <summary>
/// Utility class to access Techblox's built-in logging capabilities. /// Utility class to access Gamecraft's built-in logging capabilities.
/// The log is saved to %APPDATA%\..\LocalLow\FreeJam\Techblox\Player.Log /// The log is saved to %APPDATA%\..\LocalLow\FreeJam\Gamecraft\Player.Log
/// </summary> /// </summary>
public static class Logging public static class Logging
{ {
@ -21,7 +21,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write a regular message to Techblox's log /// Write a regular message to Gamecraft's log
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -37,7 +37,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write a debug message to Techblox's log /// Write a debug message to Gamecraft's log
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -53,7 +53,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write a debug message and object to Techblox's log /// Write a debug message and object to Gamecraft's log
/// The reason this method exists in Svelto.Console is beyond my understanding /// The reason this method exists in Svelto.Console is beyond my understanding
/// </summary> /// </summary>
/// <typeparam name="T">The type of the extra debug object</typeparam> /// <typeparam name="T">The type of the extra debug object</typeparam>
@ -72,7 +72,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write an error message to Techblox's log /// Write an error message to Gamecraft's log
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
/// <param name="extraData">The extra data to pass to the ILogger</param> /// <param name="extraData">The extra data to pass to the ILogger</param>
@ -83,7 +83,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write an exception to Techblox's log and to the screen and exit game /// Write an exception to Gamecraft's log and to the screen and exit game
/// </summary> /// </summary>
/// <param name="e">The exception to log</param> /// <param name="e">The exception to log</param>
/// <param name="extraData">The extra data to pass to the ILogger. /// <param name="extraData">The extra data to pass to the ILogger.
@ -95,7 +95,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write an exception message to Techblox's log and to the screen and exit game /// Write an exception message to Gamecraft's log and to the screen and exit game
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
/// <param name="e">The exception to log</param> /// <param name="e">The exception to log</param>
@ -114,7 +114,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write a warning message to Techblox's log /// Write a warning message to Gamecraft's log
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -123,10 +123,28 @@ namespace TechbloxModdingAPI.Utility
Svelto.Console.LogWarning(obj.ToString()); Svelto.Console.LogWarning(obj.ToString());
} }
[Obsolete("SystemLog was removed from Svelto.Common")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SystemLog(string msg)
{
Svelto.Console.Log(msg);
}
/// <summary>
/// Write a message to stdout (ie the terminal, like Command Prompt or PowerShell)
/// </summary>
/// <param name="obj">The object to log</param>
[Obsolete("SystemLog was removed from Svelto.Common")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SystemLog(object obj)
{
Svelto.Console.Log(obj.ToString());
}
// descriptive logging // descriptive logging
/// <summary> /// <summary>
/// Write a descriptive message to Techblox's log only when the API is a Debug build /// Write a descriptive message to Gamecraft's log only when the API is a Debug build
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -138,7 +156,7 @@ namespace TechbloxModdingAPI.Utility
} }
/// <summary> /// <summary>
/// Write a descriptive message to Techblox's log including the current time and the calling method's name /// Write a descriptive message to Gamecraft's log including the current time and the calling method's name
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -151,7 +169,7 @@ namespace TechbloxModdingAPI.Utility
// CLI logging // CLI logging
/// <summary> /// <summary>
/// Write a message to Techblox's command line /// Write a message to Gamecraft's command line
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -163,11 +181,11 @@ namespace TechbloxModdingAPI.Utility
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CommandLog(string msg) public static void CommandLog(string msg)
{ {
Log(msg); uREPL.Log.Output(msg);
} }
/// <summary> /// <summary>
/// Write an error message to Techblox's command line /// Write an error message to Gamecraft's command line
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -179,11 +197,11 @@ namespace TechbloxModdingAPI.Utility
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CommandLogError(string msg) public static void CommandLogError(string msg)
{ {
LogError(msg); uREPL.Log.Error(msg);
} }
/// <summary> /// <summary>
/// Write a warning message to Techblox's command line /// Write a warning message to Gamecraft's command line
/// </summary> /// </summary>
/// <param name="obj">The object to log</param> /// <param name="obj">The object to log</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -195,7 +213,7 @@ namespace TechbloxModdingAPI.Utility
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CommandLogWarning(string msg) public static void CommandLogWarning(string msg)
{ {
LogWarning(msg); uREPL.Log.Warn(msg);
} }
} }

Some files were not shown because too many files have changed in this diff Show more