Compare commits

..

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

203 changed files with 8254 additions and 13829 deletions

View file

@ -5,7 +5,7 @@ import re
# this assumes a mostly semver-complient version number
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Increment TechbloxModdingAPI version")
parser = argparse.ArgumentParser(description="Increment GamecraftModdingAPI version")
parser.add_argument('version', metavar="VN", type=str, help="The version number to increment, or the index of the number (zero-indexed).")
args = parser.parse_args()
@ -28,12 +28,12 @@ if __name__ == "__main__":
old_version = ""
new_version = ""
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
print("Parsing TechbloxModdingAPI.csproj")
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile:
print("Parsing GamecraftModdingAPI.csproj")
fileStr = xmlFile.read()
versionMatch = re.search(r"<Version>(.+)</Version>", fileStr)
if versionMatch is None:
print("Unable to find version number in TechbloxModdingAPI.csproj")
print("Unable to find version number in GamecraftModdingAPI.csproj")
exit(1)
old_version = versionMatch.group(1)
versionList = old_version.split(".")
@ -53,7 +53,7 @@ if __name__ == "__main__":
print(new_version)
newFileContents = fileStr.replace("<Version>"+old_version+"</Version>", "<Version>"+new_version+"</Version>")
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "w") as xmlFile:
print("Writing new version to project file")
xmlFile.write(newFileContents)

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

@ -3,24 +3,15 @@
import argparse
from pathlib import Path, PurePath
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):
asmDir = Path(path)
result = list()
addedPath = ""
if not asmDir.exists():
addedPath = "../"
asmDir = Path(addedPath + path)
for child in asmDir.iterdir():
if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child)) is None and str(child).lower().endswith(".dll"):
childstr = str(child)
childstr = os.path.relpath(childstr, addedPath).replace("\\", "/")
result.append(childstr)
result.sort(key=str.lower)
result = [path + "/IllusionInjector.dll", path + "/IllusionPlugin.dll"] + result # Always put it on top
if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child), re.I) is None and str(child).lower().endswith(".dll"):
result.append(str(child).replace("\\", "/"))
return result
def buildReferencesXml(path):
@ -36,16 +27,16 @@ def buildReferencesXml(path):
return "<!--Start Dependencies-->\n <ItemGroup>\n" + "".join(result) + " </ItemGroup>\n<!--End Dependencies-->"
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
args = parser.parse_args()
print("Building Assembly references")
asmXml = buildReferencesXml("../ref_TB/Techblox_Data/Managed")
asmXml = buildReferencesXml("../ref/Gamecraft_Data/Managed")
# print(asmXml)
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
print("Parsing TechbloxModdingAPI.csproj")
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile:
print("Parsing GamecraftModdingAPI.csproj")
fileStr = xmlFile.read()
# print(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:
print("Unable to find dependency XML comments, aborting!")
exit(1)
newFileStr = fileStr[:depsStart.start() - 1] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
newFileStr = fileStr[:depsStart.start()] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "w") as xmlFile:
print("Writing Assembly references")
xmlFile.write(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>

28
GamecraftModdingAPI.sln Normal file
View file

@ -0,0 +1,28 @@

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
Test|Any CPU = Test|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
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.ActiveCfg = Test|Any CPU
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.Build.0 = Test|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {72FB94D0-6C50-475B-81E0-C94C7D7A2A17}
EndGlobalSection
EndGlobal

View file

@ -1,8 +1,8 @@
using System;
using TechbloxModdingAPI.Tests;
using GamecraftModdingAPI.Tests;
namespace TechbloxModdingAPI.App
namespace GamecraftModdingAPI.App
{
#if TEST
/// <summary>

View file

@ -0,0 +1,63 @@
using System;
using RobocraftX.GUI.MyGamesScreen;
using RobocraftX.GUI;
using Svelto.ECS;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.App
{
public class AppEngine : IFactoryEngine
{
public event EventHandler<MenuEventArgs> EnterMenu;
public event EventHandler<MenuEventArgs> ExitMenu;
public IEntityFactory Factory { set; private get; }
public string Name => "GamecraftModdingAPIAppEngine";
public bool isRemovable => false;
public EntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
IsInMenu = false;
ExceptionUtil.InvokeEvent(ExitMenu, this, new MenuEventArgs { });
}
public void Ready()
{
IsInMenu = true;
ExceptionUtil.InvokeEvent(EnterMenu, this, new MenuEventArgs { });
}
// app functionality
public bool IsInMenu
{
get;
private set;
} = false;
public Game[] GetMyGames()
{
EntityCollection<MyGameDataEntityStruct> mgsevs = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
Game[] games = new Game[mgsevs.count];
for (int i = 0; i < mgsevs.count; i++)
{
Utility.Logging.MetaDebugLog($"Found game named {mgsevs[i].GameName}");
games[i] = new Game(mgsevs[i].ID);
}
return games;
}
}
public struct MenuEventArgs
{
}
}

View file

@ -1,9 +1,9 @@
using System;
using System.Runtime.Serialization;
namespace TechbloxModdingAPI.App
namespace GamecraftModdingAPI.App
{
public class AppException : TechbloxModdingAPIException
public class AppException : GamecraftModdingAPIException
{
public AppException()
{

View file

@ -4,29 +4,33 @@ using HarmonyLib;
using RobocraftX.Services;
using UnityEngine;
using RobocraftX.Common;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.App
using GamecraftModdingAPI.Utility;
using RobocraftX.Common;
namespace GamecraftModdingAPI.App
{
/// <summary>
/// The Techblox application that is running this code right now.
/// The Gamecraft application that is running this code right now.
/// </summary>
public class Client
{
public static Client Instance { get; } = new Client();
// extensible engine
protected static AppEngine appEngine = new AppEngine();
protected static Func<object> ErrorHandlerInstanceGetter;
protected static Func<object> ErrorHandlerInstanceGetter;
protected static Action<object, Error> EnqueueError;
/// <summary>
protected static Action<object> HandleErrorClosed;
/// <summary>
/// An event that fires whenever the main menu is loaded.
/// </summary>
public static event EventHandler<MenuEventArgs> EnterMenu
{
add => Game.menuEngine.EnterMenu += value;
remove => Game.menuEngine.EnterMenu -= value;
{
add => appEngine.EnterMenu += value;
remove => appEngine.EnterMenu -= value;
}
/// <summary>
@ -34,12 +38,12 @@ namespace TechbloxModdingAPI.App
/// </summary>
public static event EventHandler<MenuEventArgs> ExitMenu
{
add => Game.menuEngine.ExitMenu += value;
remove => Game.menuEngine.ExitMenu -= value;
add => appEngine.ExitMenu += value;
remove => appEngine.ExitMenu -= value;
}
/// <summary>
/// Techblox build version string.
/// Gamecraft build version string.
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
/// </summary>
/// <value>The version.</value>
@ -66,23 +70,23 @@ namespace TechbloxModdingAPI.App
{
get
{
if (!Game.menuEngine.IsInMenu) return Array.Empty<Game>();
return Game.menuEngine.GetMyGames();
if (!appEngine.IsInMenu) return new Game[0];
return appEngine.GetMyGames();
}
}
/// <summary>
/// Whether Techblox is in the Main Menu
/// Whether Gamecraft is in the Main Menu
/// </summary>
/// <value><c>true</c> if in menu; <c>false</c> when loading or in a game.</value>
public bool InMenu
{
get => Game.menuEngine.IsInMenu;
get => appEngine.IsInMenu;
}
/// <summary>
/// Open a popup which prompts the user to click a button.
/// This reuses Techblox's error dialog popup
/// This reuses Gamecraft's error dialog popup
/// </summary>
/// <param name="popup">The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt.</param>
public void PromptUser(Error popup)
@ -93,38 +97,31 @@ namespace TechbloxModdingAPI.App
EnqueueError(errorHandlerInstance, popup);
}
public void CloseCurrentPrompt()
// TODO
/*public void CloseCurrentPrompt()
{
// RobocraftX.Services.ErrorHandler.Instance.HandlePopupClosed();
// FIXME: this is a call that is also called when closing, not the actual closing action itself (so it doesn't work)
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.Close();
}
public void SelectFirstPromptButton()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.FirstButton();
}
public void SelectSecondPromptButton()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.SecondButton();
}
HandleErrorClosed(errorHandlerInstance);
}*/
internal static void Init()
{
// this would have been so much simpler if this didn't involve a bunch of internal fields & classes
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
Type errorHandle = AccessTools.TypeByName("RobocraftX.Services.ErrorHandle");
ErrorHandlerInstanceGetter = (Func<object>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenInstanceGetter")
ErrorHandlerInstanceGetter = (Func<object>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenInstanceGetter")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
EnqueueError = (Action<object, Error>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenEnqueueError")
.MakeGenericMethod(errorHandler, errorHandle)
.Invoke(null, new object[0]);
/*HandleErrorClosed = (Action<object>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenHandlePopupClosed")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);*/
// register engines
MenuEngineManager.AddMenuEngine(appEngine);
}
// Creating delegates once is faster than reflection every time
@ -149,23 +146,14 @@ namespace TechbloxModdingAPI.App
return enqueueCasted;
}
private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
private static Action<object> GenHandlePopupClosed<T>()
{
if (_errorPopup.Close != null)
return _errorPopup;
Type errorHandler = handler.GetType();
FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
var errorPopup = (ErrorPopup)field.GetValue(handler);
MethodInfo info = AccessTools.Method(errorPopup.GetType(), "ClosePopup");
var close = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleFirstOption");
var first = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleSecondOption");
var second = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
_errorPopup = (close, first, second);
return _errorPopup;
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo handlePopupClosed = AccessTools.Method(errorHandler, "HandlePopupClosed");
Action<T> handleSimple =
(Action<T>) Delegate.CreateDelegate(typeof(Action<T>), handlePopupClosed);
Action<object> handleCasted = (object instance) => handleSimple((T) instance);
return handleCasted;
}
}
}

View file

@ -3,9 +3,9 @@ using HarmonyLib;
using RobocraftX.Services;
using TechbloxModdingAPI.Tests;
using GamecraftModdingAPI.Tests;
namespace TechbloxModdingAPI.App
namespace GamecraftModdingAPI.App
{
#if TEST
/// <summary>
@ -42,25 +42,17 @@ namespace TechbloxModdingAPI.App
[APITestCase(TestType.Menu)]
public static void TestPopUp2()
{
Client.Instance.PromptUser(popup2);
Client c = new Client();
c.PromptUser(popup2);
//c.CloseCurrentPrompt();
}
[APITestCase(TestType.Menu)]
public static void TestPopUp1()
{
Client.Instance.PromptUser(popup1);
}
[APITestCase(TestType.Menu)]
public static void TestPopUpClose1()
{
Client.Instance.CloseCurrentPrompt();
}
[APITestCase(TestType.Menu)]
public static void TestPopUpClose2()
{
Client.Instance.CloseCurrentPrompt();
Client c = new Client();
c.PromptUser(popup1);
//c.CloseCurrentPrompt();
}
}
#endif

View file

@ -2,15 +2,17 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using RobocraftX.Common;
using RobocraftX.GUI.MyGamesScreen;
using RobocraftX.StateSync;
using Svelto.ECS;
using Techblox.GameSelection;
using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Tasks;
using TechbloxModdingAPI.Utility;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Tasks;
using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.App
namespace GamecraftModdingAPI.App
{
/// <summary>
/// An in-game save.
@ -21,7 +23,7 @@ namespace TechbloxModdingAPI.App
{
// extensible engines
protected static GameGameEngine gameEngine = new GameGameEngine();
protected internal static GameMenuEngine menuEngine = new GameMenuEngine();
protected static GameMenuEngine menuEngine = new GameMenuEngine();
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
@ -31,7 +33,7 @@ namespace TechbloxModdingAPI.App
private bool hasId = false;
/// <summary>
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.App.Game"/> class.
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
/// </summary>
/// <param name="id">Menu identifier.</param>
public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames))
@ -39,7 +41,7 @@ namespace TechbloxModdingAPI.App
}
/// <summary>
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.App.Game"/> class.
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
/// </summary>
/// <param name="id">Menu identifier.</param>
public Game(EGID id)
@ -52,7 +54,7 @@ namespace TechbloxModdingAPI.App
}
/// <summary>
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.App.Game"/> class without id.
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class without id.
/// This is assumed to be the current game.
/// </summary>
public Game()
@ -116,7 +118,7 @@ namespace TechbloxModdingAPI.App
/// <summary>
/// An event that fires right before a game returns to the main menu.
/// At this point, Techblox is transitioning state so many things are invalid/unstable here.
/// At this point, Gamecraft is transitioning state so many things are invalid/unstable here.
/// </summary>
public static event EventHandler<GameEventArgs> Exit
{
@ -163,7 +165,7 @@ namespace TechbloxModdingAPI.App
{
if (!VerifyMode()) return null;
if (menuMode) return menuEngine.GetGameInfo(EGID).GameName;
return gameEngine.GetGameData().saveName;
return GameMode.SaveGameDetails.Name;
}
set
@ -172,7 +174,11 @@ namespace TechbloxModdingAPI.App
if (menuMode)
{
menuEngine.SetGameName(EGID, value);
} // Save details are directly saved from user input or not changed at all when in game
}
else
{
GameMode.SaveGameDetails.Name = value;
}
}
}
@ -195,7 +201,11 @@ namespace TechbloxModdingAPI.App
if (menuMode)
{
menuEngine.SetGameDescription(EGID, value);
} // No description exists in-game
}
else
{
// No description exists in-game
}
}
}
@ -209,7 +219,7 @@ namespace TechbloxModdingAPI.App
{
if (!VerifyMode()) return null;
if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath;
return gameEngine.GetGameData().gameID;
return GameMode.SaveGameDetails.Folder;
}
set
@ -219,6 +229,11 @@ namespace TechbloxModdingAPI.App
{
menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value);
}
else
{
// this likely breaks things
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, value, GameMode.SaveGameDetails.WorkshopId);
}
}
}
@ -227,16 +242,28 @@ namespace TechbloxModdingAPI.App
/// In most cases this is invalid and returns 0, so this can be ignored.
/// </summary>
/// <value>The workshop identifier.</value>
[Obsolete]
public ulong WorkshopId
{
get
{
return 0uL; // Not supported anymore
if (!VerifyMode()) return 0uL;
if (menuMode) return 0uL; // MyGames don't have workshop IDs
return GameMode.SaveGameDetails.WorkshopId;
}
set
{
VerifyMode();
if (menuMode)
{
// MyGames don't have workshop IDs
// menuEngine.GetGameInfo(EGID).GameName.Set(value);
}
else
{
// this likely breaks things
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, GameMode.SaveGameDetails.Folder, value);
}
}
}
@ -308,18 +335,6 @@ namespace TechbloxModdingAPI.App
gameEngine.ToggleTimeMode();
}
/// <summary>
/// The mode of the game.
/// </summary>
public CurrentGameMode Mode
{
get
{
if (menuMode || !VerifyMode()) return CurrentGameMode.None;
return gameEngine.GetGameData().gameMode == GameMode.CreateWorld ? CurrentGameMode.Build : CurrentGameMode.Play;
}
}
/// <summary>
/// Load the game save.
/// This happens asynchronously, so when this method returns the game not loaded yet.
@ -369,20 +384,19 @@ namespace TechbloxModdingAPI.App
/// <summary>
/// Add information to the in-game debug display.
/// When this object is garbage collected, this debug info is automatically removed.
/// The provided getter function is called each frame so make sure it returns quickly.
/// When this object is garbage collected, this debug info is automatically removed.
/// </summary>
/// <param name="id">Debug info identifier.</param>
/// <param name="contentGetter">A function that returns the current information.</param>
public void AddDebugInfo(string id, Func<string> contentGetter)
/// <param name="contentGetter">Content getter.</param>
public void AddDebugInfo(string id, Func<string> contentGetter)
{
if (!VerifyMode()) return;
if (menuMode)
{
throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game");
}
debugOverlayEngine.SetInfo("game_" + id, contentGetter);
debugIds.Add(id);
if (!VerifyMode()) return;
if (menuMode)
{
throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game");
}
debugOverlayEngine.SetInfo(id, contentGetter);
debugIds.Add(id);
}
/// <summary>
@ -392,36 +406,14 @@ namespace TechbloxModdingAPI.App
/// <param name="id">Debug info identifier.</param>
public bool RemoveDebugInfo(string id)
{
if (!VerifyMode()) return false;
if (menuMode)
{
throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game");
}
if (!debugIds.Contains(id)) return false;
debugOverlayEngine.RemoveInfo("game_" + id);
return debugIds.Remove(id);
}
/// <summary>
/// Add information to the in-game debug display.
/// This debug info will be present for all games until it is manually removed.
/// The provided getter function is called each frame so make sure it returns quickly.
/// </summary>
/// <param name="id">Debug info identifier.</param>
/// <param name="contentGetter">A function that returns the current information.</param>
public static void AddPersistentDebugInfo(string id, Func<string> contentGetter)
{
debugOverlayEngine.SetInfo("persistent_" + id, contentGetter);
}
/// <summary>
/// Remove persistent information from the in-game debug display.
/// </summary>
/// <returns><c>true</c>, if debug info was removed, <c>false</c> otherwise.</returns>
/// <param name="id">Debug info identifier.</param>
public static bool RemovePersistentDebugInfo(string id)
{
return debugOverlayEngine.RemoveInfo("persistent_" + id);
if (!VerifyMode()) return false;
if (menuMode)
{
throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game");
}
if (!debugIds.Contains(id)) return false;
debugOverlayEngine.RemoveInfo(id);
return debugIds.Remove(id);
}
/// <summary>
@ -441,20 +433,11 @@ namespace TechbloxModdingAPI.App
Block[] blocks = new Block[blockEGIDs.Length];
for (int b = 0; b < blockEGIDs.Length; b++)
{
blocks[b] = Block.New(blockEGIDs[b]);
blocks[b] = new Block(blockEGIDs[b]);
}
return blocks;
}
/// <summary>
/// Enable the screenshot taker for updating the game's screenshot. Breaks the pause menu in a new save.
/// </summary>
public void EnableScreenshotTaker()
{
if (!VerifyMode()) return;
gameEngine.EnableScreenshotTaker();
}
~Game()
{
foreach (string id in debugIds)
@ -483,8 +466,12 @@ namespace TechbloxModdingAPI.App
{
GameEngineManager.AddGameEngine(gameEngine);
GameEngineManager.AddGameEngine(debugOverlayEngine);
GameEngineManager.AddGameEngine(buildSimEventEngine);
MenuEngineManager.AddMenuEngine(menuEngine);
}
internal static void InitDeterministic(StateSyncRegistrationHelper stateSyncReg)
{
stateSyncReg.AddDeterministicEngine(buildSimEventEngine);
}
}
}

View file

@ -4,18 +4,19 @@ using RobocraftX.Common;
using RobocraftX.StateSync;
using Svelto.ECS;
using Unity.Jobs;
using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.App
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.App
{
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered
{
public WrappedHandler<GameEventArgs> SimulationMode;
public event EventHandler<GameEventArgs> SimulationMode;
public WrappedHandler<GameEventArgs> BuildMode;
public event EventHandler<GameEventArgs> BuildMode;
public string Name => "TechbloxModdingAPIBuildSimEventGameEngine";
public string Name => "GamecraftModdingAPIBuildSimEventGameEngine";
public bool isRemovable => false;
@ -27,13 +28,13 @@ namespace TechbloxModdingAPI.App
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
{
SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO
ExceptionUtil.InvokeEvent(SimulationMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
return inputDeps;
}
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
{
BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" });
ExceptionUtil.InvokeEvent(BuildMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
return inputDeps;
}
}

View file

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using RobocraftX;
using RobocraftX.Common;
using RobocraftX.Schedulers;
using RobocraftX.SimulationModeState;
using Svelto.ECS;
using Svelto.Tasks;
using Svelto.Tasks.Lean;
using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.App
{
public class GameGameEngine : IApiEngine
{
public event EventHandler<GameEventArgs> EnterGame;
public event EventHandler<GameEventArgs> ExitGame;
public string Name => "GamecraftModdingAPIGameInfoMenuEngine";
public bool isRemovable => false;
public EntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
ExceptionUtil.InvokeEvent(ExitGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
IsInGame = false;
}
public void Ready()
{
ExceptionUtil.InvokeEvent(EnterGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
IsInGame = true;
}
// game functionality
public bool IsInGame
{
get;
private set;
} = false;
public void ExitCurrentGame(bool async = false)
{
if (async)
{
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING);
}
else
{
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
}
}
public IEnumerator<TaskContract> ExitCurrentGameAsync()
{
/*
while (Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING.isStopping) { yield return Yield.It; }
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu").Invoke(FullGameFields.Instance, new object[0]);*/
yield return Yield.It;
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
}
public void SaveCurrentGame()
{
ref GameSceneEntityStruct gses = ref entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
gses.LoadAfterSaving = false;
gses.SaveNow = true;
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
}
public bool IsTimeRunningMode()
{
return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB);
}
public bool IsTimeStoppedMode()
{
return TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB);
}
public void ToggleTimeMode()
{
TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB);
}
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
{
var allBlocks = entitiesDB.QueryEntities<DBEntityStruct>();
List<EGID> blockEGIDs = new List<EGID>();
if (filter == BlockIDs.Invalid)
{
foreach (var (blocks, _) in allBlocks)
foreach (var block in blocks)
blockEGIDs.Add(block.ID);
return blockEGIDs.ToArray();
}
else
{
foreach (var (blocks, _) in allBlocks)
foreach (var block in blocks)
if (block.DBID == (ulong) filter)
blockEGIDs.Add(block.ID);
return blockEGIDs.ToArray();
}
}
}
}

View file

@ -0,0 +1,139 @@
using System;
using HarmonyLib;
using RobocraftX;
using RobocraftX.Common;
using RobocraftX.GUI;
using RobocraftX.GUI.MyGamesScreen;
using Svelto.ECS;
using Svelto.ECS.Experimental;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
using Svelto.DataStructures;
namespace GamecraftModdingAPI.App
{
public class GameMenuEngine : IFactoryEngine
{
public IEntityFactory Factory { set; private get; }
public string Name => "GamecraftModdingAPIGameInfoGameEngine";
public bool isRemovable => false;
public EntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
IsInMenu = false;
}
public void Ready()
{
IsInMenu = true;
}
// game functionality
public bool IsInMenu
{
get;
private set;
} = false;
public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L)
{
EntityComponentInitializer eci = Factory.BuildEntity<MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal>(id);
eci.Init(new MyGameDataEntityStruct
{
SavedGamePath = new ECSString(path),
ThumbnailId = thumbnailId,
GameName = new ECSString(gameName),
CreatorName = new ECSString(creatorName),
GameDescription = new ECSString(description),
CreatedDate = createdDate,
});
// entitiesDB.PublishEntityChange<MyGameDataEntityStruct>(id); // this will always fail
return true;
}
public uint HighestID()
{
EntityCollection<MyGameDataEntityStruct> games = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
uint max = 0;
for (int i = 0; i < games.count; i++)
{
if (games[i].ID.entityID > max)
{
max = games[i].ID.entityID;
}
}
return max;
}
public bool EnterGame(EGID id)
{
if (!ExistsGameInfo(id)) return false;
ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id);
return EnterGame(mgdes.GameName, mgdes.SavedGamePath);
}
public bool EnterGame(string gameName, string path, ulong workshopId = 0uL, bool autoEnterSim = false)
{
GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build;
GameMode.SaveGameDetails = new SaveGameDetails(gameName, path, workshopId);
// the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[0]);
return true;
}
public bool SetGameName(EGID id, string name)
{
if (!ExistsGameInfo(id)) return false;
GetGameInfo(id).GameName.Set(name);
GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name);
return true;
}
public bool SetGameDescription(EGID id, string name)
{
if (!ExistsGameInfo(id)) return false;
GetGameInfo(id).GameDescription.Set(name);
GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name);
return true;
}
public bool ExistsGameInfo(EGID id)
{
return entitiesDB.Exists<MyGameDataEntityStruct>(id);
}
public ref MyGameDataEntityStruct GetGameInfo(EGID id)
{
return ref GetComponent<MyGameDataEntityStruct>(id);
}
public ref MyGamesSlotEntityViewStruct GetGameViewInfo(EGID id)
{
EntityCollection<MyGamesSlotEntityViewStruct> entities =
entitiesDB.QueryEntities<MyGamesSlotEntityViewStruct>(MyGamesScreenExclusiveGroups.GameSlotGuiEntities);
for (int i = 0; i < entities.count; i++)
{
if (entities[i].ID.entityID == id.entityID)
{
return ref entities[i];
}
}
MyGamesSlotEntityViewStruct[] defRef = new MyGamesSlotEntityViewStruct[1];
return ref defRef[0];
}
public ref T GetComponent<T>(EGID id) where T: unmanaged, IEntityComponent
{
return ref entitiesDB.QueryEntity<T>(id);
}
}
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }
}

View file

@ -0,0 +1,26 @@
using System;
using System.Reflection;
using RobocraftX.CR.MainGame;
using RobocraftX.StateSync;
using HarmonyLib;
namespace GamecraftModdingAPI.App
{
[HarmonyPatch]
class StateSyncRegPatch
{
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
{
// register sim/build events engines
Game.InitDeterministic(stateSyncReg);
}
[HarmonyTargetMethod]
public static MethodBase Target()
{
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose").MakeGenericMethod(typeof(object));
}
}
}

View file

@ -3,7 +3,7 @@ using HarmonyLib;
using RobocraftX.Services;
namespace TechbloxModdingAPI.App
namespace GamecraftModdingAPI.App
{
public class DualChoicePrompt : MultiChoiceError
{

View file

@ -0,0 +1,460 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
using RobocraftX.Common;
using RobocraftX.Blocks;
using Unity.Mathematics;
using Unity.Entities;
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 : IEquatable<Block>, IEquatable<EGID>
{
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 static readonly BlockEventsEngine BlockEventsEngine = new BlockEventsEngine();
protected static readonly ScalingEngine ScalingEngine = new ScalingEngine();
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.
/// <para></para>
/// <para>When placing multiple blocks, do not access properties immediately after creation as this
/// triggers a sync each time which can affect performance and may cause issues with the game.
/// You may either use AsyncUtils.WaitForSubmission() after placing all of the blocks
/// or simply access the block properties which will trigger the synchronization the first time a property is used.</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 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)
{
return PlaceNew<Block>(block, position, rotation, color, darkness, uscale, scale, player);
}
/// <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 T PlaceNew<T>(BlockIDs block, float3 position,
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
int uscale = 1, float3 scale = default, Player player = null) where T : Block
{
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
{
var egid = PlacementEngine.PlaceBlock(block, color, darkness,
position, uscale, scale, player, rotation, out var initializer);
var bl = New<T>(egid.entityID, egid.groupID);
bl.InitData.Group = BlockEngine.InitGroup(initializer);
Placed += bl.OnPlacedInit;
return bl;
}
return null;
}
/// <summary>
/// Returns the most recently placed block.
/// </summary>
/// <returns>The block object</returns>
public static Block GetLastPlacedBlock()
{
return New<Block>(BlockIdentifiers.LatestBlockID);
}
/// <summary>
/// An event that fires each time a block is placed.
/// </summary>
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
{
add => BlockEventsEngine.Placed += value;
remove => BlockEventsEngine.Placed -= value;
}
/// <summary>
/// An event that fires each time a block is removed.
/// </summary>
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
{
add => BlockEventsEngine.Removed += value;
remove => BlockEventsEngine.Removed -= value;
}
private static Dictionary<Type, Func<EGID, Block>> initializers = new Dictionary<Type, Func<EGID, Block>>();
private static Dictionary<Type, ExclusiveGroupStruct[]> typeToGroup =
new Dictionary<Type, ExclusiveGroupStruct[]>
{
{typeof(ConsoleBlock), new[] {CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP}},
{typeof(LogicGate), new [] {CommonExclusiveGroups.BUILD_LOGIC_BLOCK_GROUP}},
{typeof(Motor), new[] {CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP}},
{typeof(MusicBlock), new[] {CommonExclusiveGroups.BUILD_MUSIC_BLOCK_GROUP}},
{typeof(Piston), new[] {CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP}},
{typeof(Servo), new[] {CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP}},
{
typeof(SpawnPoint),
new[]
{
CommonExclusiveGroups.BUILD_SPAWNPOINT_BLOCK_GROUP,
CommonExclusiveGroups.BUILD_BUILDINGSPAWN_BLOCK_GROUP
}
},
{typeof(TextBlock), new[] {CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP}},
{typeof(Timer), new[] {CommonExclusiveGroups.BUILD_TIMER_BLOCK_GROUP}}
};
/// <summary>
/// Constructs a new instance of T with the given ID and group using dynamically created delegates.
/// It's equivalent to new T(EGID) with a minimal overhead thanks to caching the created delegates.
/// </summary>
/// <param name="id">The block ID</param>
/// <param name="group">The block group</param>
/// <typeparam name="T">The block's type or Block itself</typeparam>
/// <returns>An instance of the provided type</returns>
/// <exception cref="BlockTypeException">The block group doesn't match or cannot be found</exception>
/// <exception cref="MissingMethodException">The block class doesn't have the needed constructor</exception>
private static T New<T>(uint id, ExclusiveGroupStruct? group = null) where T : Block
{
var type = typeof(T);
EGID egid;
if (!group.HasValue)
{
if (typeToGroup.TryGetValue(type, out var gr) && gr.Length == 1)
egid = new EGID(id, gr[0]);
else
egid = BlockEngine.FindBlockEGID(id) ?? throw new BlockTypeException("Could not find block group!");
}
else
{
egid = new EGID(id, group.Value);
if (typeToGroup.TryGetValue(type, out var gr)
&& gr.All(egs => egs != group.Value)) //If this subclass has a specific group, then use that - so Block should still work
throw new BlockTypeException($"Incompatible block type! Type {type.Name} belongs to group {gr.Select(g => g.ToString()).Aggregate((a, b) => a + ", " + b)} instead of {group.Value}");
}
if (initializers.TryGetValue(type, out var func))
{
var bl = (T) func(egid);
return bl;
}
//https://stackoverflow.com/a/10593806/2703239
var ctor = type.GetConstructor(new[] {typeof(EGID)});
if (ctor == null)
throw new MissingMethodException("There is no constructor with an EGID parameter for this object");
DynamicMethod dynamic = new DynamicMethod(string.Empty,
type,
new[] {typeof(EGID)},
type);
ILGenerator il = dynamic.GetILGenerator();
il.DeclareLocal(type);
il.Emit(OpCodes.Ldarg_0); //Load EGID and pass to constructor
il.Emit(OpCodes.Newobj, ctor); //Call constructor
//il.Emit(OpCodes.Stloc_0); - doesn't seem like we need these
//il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
func = (Func<EGID, T>) dynamic.CreateDelegate(typeof(Func<EGID, T>));
initializers.Add(type, func);
var block = (T) func(egid);
return block;
}
public Block(EGID id)
{
Id = id;
var type = GetType();
if (typeToGroup.TryGetValue(type, out var groups))
{
if (groups.All(gr => gr != id.groupID))
throw new BlockTypeException("The block has the wrong group! The type is " + GetType() +
" while the group is " + id.groupID);
}
else if (type != typeof(Block))
Logging.LogWarning($"Unknown block type! Add {type} to the dictionary.");
}
/// <summary>
/// This overload searches for the correct group the block is in.
/// It will throw an exception if the block doesn't exist.
/// Use the EGID constructor where possible or subclasses of Block as those specify the group.
/// </summary>
public Block(uint id)
{
Id = BlockEngine.FindBlockEGID(id) ?? throw new BlockTypeException("Could not find the appropriate group for the block. The block probably doesn't exist or hasn't been submitted.");
}
public EGID Id { get; }
internal BlockEngine.BlockInitData InitData;
/// <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 => MovementEngine.GetPosition(Id, InitData);
set
{
MovementEngine.MoveBlock(Id, InitData, value);
}
}
/// <summary>
/// The block's current rotation in degrees or zero if the block doesn't exist.
/// </summary>
public float3 Rotation
{
get => RotationEngine.GetRotation(Id, InitData);
set
{
RotationEngine.RotateBlock(Id, InitData, value);
}
}
/// <summary>
/// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling.
/// The default scale of 1 means 0.2 in terms of position.
/// </summary>
public float3 Scale
{
get => BlockEngine.GetBlockInfo(this, (ScalingEntityStruct st) => st.scale);
set
{
BlockEngine.SetBlockInfo(this, (ref ScalingEntityStruct st, float3 val) => st.scale = val, value);
if (!Exists) return; //UpdateCollision needs the block to exist
ScalingEngine.UpdateCollision(Id);
}
}
/// <summary>
/// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale.
/// The default scale of 1 means 0.2 in terms of position.
/// </summary>
public int UniformScale
{
get => BlockEngine.GetBlockInfo(this, (UniformBlockScaleEntityStruct st) => st.scaleFactor);
set
{
BlockEngine.SetBlockInfo(this, (ref UniformBlockScaleEntityStruct st, int val) => st.scaleFactor = val,
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
{
return BlockEngine.GetBlockInfo(this, (DBEntityStruct st) => (BlockIDs) st.DBID, 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(this, (ColourParameterEntityStruct st) => st.indexInPalette,
byte.MaxValue);
return new BlockColor(index);
}
set
{
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, BlockColor val) =>
{
color.indexInPalette = (byte) (val.Color + val.Darkness * 10);
color.overridePaletteColour = false;
color.needsUpdate = true;
BlockEngine.SetBlockColorFromPalette(ref color);
}, value);
}
}
/// <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(this, (ColourParameterEntityStruct st) => st.overriddenColour);
set
{
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, float4 val) =>
{
color.overriddenColour = val;
color.overridePaletteColour = true;
color.needsUpdate = true;
}, value);
}
}
/// <summary>
/// The 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.GetBlockInfoViewStruct(this, (TextLabelEntityViewStruct st) => st.textLabelComponent?.text);
set
{
BlockEngine.SetBlockInfoViewStruct(this, (ref TextLabelEntityViewStruct text, string val) =>
{
if (text.textLabelComponent != null) text.textLabelComponent.text = val;
}, value);
}
}
/// <summary>
/// Whether the block exists. The other properties will return a default value if the block doesn't exist.
/// If the block was just placed, then this will also return false but the properties will work correctly.
/// </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 chunk 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 chunk or null if the block doesn't exist.</returns>
public SimBody GetSimBody()
{
return BlockEngine.GetBlockInfo(this,
(GridConnectionsEntityStruct st) => new SimBody(st.machineRigidBodyId, st.clusterId));
}
private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e)
{ //Member method instead of lambda to avoid constantly creating delegates
if (e.ID != Id) return;
Placed -= OnPlacedInit; //And we can reference it
InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again
}
public override string ToString()
{
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Type)}: {Type}, {nameof(Color)}: {Color}, {nameof(Exists)}: {Exists}";
}
public bool Equals(Block 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((Block) obj);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
public static void Init()
{
GameEngineManager.AddGameEngine(PlacementEngine);
GameEngineManager.AddGameEngine(MovementEngine);
GameEngineManager.AddGameEngine(RotationEngine);
GameEngineManager.AddGameEngine(RemovalEngine);
GameEngineManager.AddGameEngine(BlockEngine);
GameEngineManager.AddGameEngine(BlockEventsEngine);
GameEngineManager.AddGameEngine(ScalingEngine);
GameEngineManager.AddGameEngine(SignalEngine);
Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine
}
/// <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
//Lets improve that using delegates
var block = New<T>(Id.entityID, Id.groupID);
block.InitData = this.InitData;
return block;
}
#if DEBUG
public static EntitiesDB entitiesDB
{
get
{
return BlockEngine.GetEntitiesDB();
}
}
#endif
internal static void Setup(World physicsWorld)
{
ScalingEngine.Setup(physicsWorld.EntityManager);
}
}
}

View file

@ -0,0 +1,51 @@
namespace GamecraftModdingAPI.Blocks
{
public struct BlockColor
{
public BlockColors Color;
public byte Darkness;
public BlockColor(byte index)
{
if (index == byte.MaxValue)
{
Color = BlockColors.Default;
Darkness = 0;
}
else
{
Color = (BlockColors) (index % 10);
Darkness = (byte) (index / 10);
}
}
public BlockColor(BlockColors color, byte darkness)
{
Color = color;
Darkness = 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,259 @@
using System;
using System.Collections.Generic;
using System.Linq;
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 Svelto.ECS.Hybrid;
using GamecraftModdingAPI.Engines;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine for executing general block actions
/// </summary>
public partial 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<EGID> cubeStack = new Stack<EGID>();
FasterList<EGID> cubes = new FasterList<EGID>(10);
var coll = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
foreach (var (ecoll, _) in coll)
foreach (ref var conn in ecoll)
conn.isProcessed = false;
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
(in GridConnectionsEntityStruct g) => { return false; });
var ret = new Block[cubes.count];
for (int i = 0; i < cubes.count; i++)
ret[i] = new Block(cubes[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;
}
public ref T GetBlockInfo<T>(EGID blockID) where T : unmanaged, 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
}
public ref T GetBlockInfoViewStruct<T>(EGID blockID) where T : struct, INeedEGID, IEntityComponent
{
if (entitiesDB.Exists<T>(blockID))
{
// TODO: optimize by using EntitiesDB internal calls instead of iterating over everything
EntityCollection<T> entities = entitiesDB.QueryEntities<T>(blockID.groupID);
for (int i = 0; i < entities.count; i++)
{
if (entities[i].ID == blockID)
{
return ref entities[i];
}
}
}
T[] structHolder = new T[1]; //Create something that can be referenced
return ref structHolder[0]; //Gets a default value automatically
}
public U GetBlockInfo<T, U>(Block block, Func<T, U> getter,
U def = default) where T : unmanaged, IEntityComponent
{
if (entitiesDB.Exists<T>(block.Id))
return getter(entitiesDB.QueryEntity<T>(block.Id));
return GetBlockInitInfo(block, getter, def);
}
public U GetBlockInfoViewStruct<T, U>(Block block, Func<T, U> getter,
U def = default) where T : struct, IEntityViewComponent
{
if (entitiesDB.Exists<T>(block.Id))
return getter(entitiesDB.QueryEntity<T>(block.Id));
return GetBlockInitInfo(block, getter, def);
}
private U GetBlockInitInfo<T, U>(Block block, Func<T, U> getter, U def) where T : struct, IEntityComponent
{
if (block.InitData.Group == null) return def;
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
if (initializer.Has<T>())
return getter(initializer.Get<T>());
return def;
}
public delegate void Setter<T, U>(ref T component, U value) where T : struct, IEntityComponent;
public void SetBlockInfoViewStruct<T, U>(Block block, Setter<T, U> setter, U value) where T : struct, IEntityViewComponent
{
if (entitiesDB.Exists<T>(block.Id))
setter(ref entitiesDB.QueryEntity<T>(block.Id), value);
else
SetBlockInitInfo(block, setter, value);
}
public void SetBlockInfo<T, U>(Block block, Setter<T, U> setter, U value) where T : unmanaged, IEntityComponent
{
if (entitiesDB.Exists<T>(block.Id))
setter(ref entitiesDB.QueryEntity<T>(block.Id), value);
else
SetBlockInitInfo(block, setter, value);
}
private void SetBlockInitInfo<T, U>(Block block, Setter<T, U> setter, U value)
where T : struct, IEntityComponent
{
if (block.InitData.Group != null)
{
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
T component = initializer.Has<T>() ? initializer.Get<T>() : default;
ref T structRef = ref component;
setter(ref structRef, value);
initializer.Init(structRef);
}
}
public bool BlockExists(EGID blockID)
{
return entitiesDB.Exists<DBEntityStruct>(blockID);
}
public bool GetBlockInfoExists<T>(Block block) where T : struct, IEntityComponent
{
if (entitiesDB.Exists<T>(block.Id))
return true;
if (block.InitData.Group == null)
return false;
var init = new EntityComponentInitializer(block.Id, block.InitData.Group);
return init.Has<T>();
}
public SimBody[] GetSimBodiesFromID(byte id)
{
var ret = new FasterList<SimBody>(4);
if (!entitiesDB.HasAny<ObjectIdEntityStruct>(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP))
return new SimBody[0];
var oids = entitiesDB.QueryEntities<ObjectIdEntityStruct>(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP);
var connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(CommonExclusiveGroups.BUILD_OBJID_BLOCK_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, bool sim)
{
var ret = new FasterList<ObjectIdentifier>(4);
if (!entitiesDB.HasAny<ObjectIdEntityStruct>(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP))
return new ObjectIdentifier[0];
var oids = entitiesDB.QueryEntities<ObjectIdEntityStruct>(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP);
foreach (ref ObjectIdEntityStruct oid in oids)
if (sim ? oid.simObjectId == id : 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();
}
public SimBody[] GetClusterBodies(uint cid)
{
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
var bodies = new HashSet<uint>();
foreach (var (coll, _) in groups)
{
foreach (var conn in coll)
{
if (conn.clusterId == cid)
bodies.Add(conn.machineRigidBodyId);
}
}
return bodies.Select(id => new SimBody(id)).ToArray();
}
public EGID? FindBlockEGID(uint id)
{
var groups = entitiesDB.FindGroups<DBEntityStruct>();
foreach (ExclusiveGroupStruct group in groups)
{
if (entitiesDB.Exists<DBEntityStruct>(id, group))
return new EGID(id, group);
}
return null;
}
public Cluster GetCluster(uint sbid)
{
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
foreach (var (coll, _) in groups)
{
foreach (var conn in coll)
{
if (conn.machineRigidBodyId == sbid)
return new Cluster(conn.clusterId);
}
}
return null;
}
#if DEBUG
public EntitiesDB GetEntitiesDB()
{
return entitiesDB;
}
#endif
}
}

View file

@ -0,0 +1,52 @@
using System;
using System.Linq.Expressions;
using Svelto.DataStructures;
using Svelto.ECS;
using Svelto.ECS.Internal;
namespace GamecraftModdingAPI.Blocks
{
public partial class BlockEngine
{
/// <summary>
/// Holds information needed to construct a component initializer
/// </summary>
internal struct BlockInitData
{
public FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> Group;
}
internal delegate FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetInitGroup(
EntityComponentInitializer initializer);
/// <summary>
/// Accesses the group field of the initializer
/// </summary>
internal GetInitGroup InitGroup = CreateAccessor<GetInitGroup>("_group");
//https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection
internal static TDelegate CreateAccessor<TDelegate>(string memberName) where TDelegate : Delegate
{
var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
if (invokeMethod == null)
throw new InvalidOperationException($"{typeof(TDelegate)} signature could not be determined.");
var delegateParameters = invokeMethod.GetParameters();
if (delegateParameters.Length != 1)
throw new InvalidOperationException("Delegate must have a single parameter.");
var paramType = delegateParameters[0].ParameterType;
var objParam = Expression.Parameter(paramType, "obj");
var memberExpr = Expression.PropertyOrField(objParam, memberName);
Expression returnExpr = memberExpr;
if (invokeMethod.ReturnType != memberExpr.Type)
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType);
var lambda =
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam});
return lambda.Compile();
}
}
}

View file

@ -0,0 +1,55 @@
using System;
using RobocraftX.Common;
using Svelto.ECS;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class BlockEventsEngine : IReactionaryEngine<DBEntityStruct>
{
public event EventHandler<BlockPlacedRemovedEventArgs> Placed;
public event EventHandler<BlockPlacedRemovedEventArgs> Removed;
public void Ready()
{
}
public EntitiesDB entitiesDB { get; set; }
public void Dispose()
{
}
public string Name { get; } = "GamecraftModdingAPIBlockEventsEngine";
public bool isRemovable { get; } = false;
private bool shouldAddRemove;
public void Add(ref DBEntityStruct entityComponent, EGID egid)
{
if (!(shouldAddRemove = !shouldAddRemove))
return;
ExceptionUtil.InvokeEvent(Placed, this,
new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
}
public void Remove(ref DBEntityStruct entityComponent, EGID egid)
{
if (!(shouldAddRemove = !shouldAddRemove))
return;
ExceptionUtil.InvokeEvent(Removed, this,
new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
}
}
public struct BlockPlacedRemovedEventArgs
{
public EGID ID;
public BlockIDs Type;
private Block block;
public Block Block => block ?? (block = new Block(ID));
}
}

View file

@ -1,10 +1,10 @@
using System;
using TechbloxModdingAPI;
using GamecraftModdingAPI;
namespace TechbloxModdingAPI.Blocks
namespace GamecraftModdingAPI.Blocks
{
public class BlockException : TechbloxModdingAPIException
public class BlockException : GamecraftModdingAPIException
{
public BlockException()
{

View file

@ -0,0 +1,275 @@
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Possible block types
/// </summary>
public enum BlockIDs : ushort
{
/// <summary>
/// Called "nothing" in Gamecraft. (DBID.NOTHING)
/// </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
DestructionManager = 260,
ChunkHealthModifier,
ClusterHealthModifier, //262
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,
SFXBlockGameplay=240,
SFXBlock8Bit,
SFXBlockInstrument,
SFXBlockSciFi,
SFXBlockLoops,
SFXBlockVocal,
MovementConstrainer, //246
RotationConstrainer,
AdvancedMovementDampener,
AdvancedRotationDampener,
Mover = 250,
Rotator,
MovementDampener,
RotationDampener,
AdvancedMover,
AdvancedRotator,
MusicBlock, //256
PlasmaCannonBlock,
QuantumRiflePickup = 300,
QuantumRifleAmmoPickup,
MagmaRockCube=777,
MagmaRockCubeSliced,
MagmaRockSlope,
MagmaRockCorner,
MagmaRockPyramidSegment,
MagmaRockConeSegment,
MagmaRockSlicedRounded,
MagmaRockSlopeRounded,
MagmaRockCornerRounded,
HexNetCube,
HexNetCubeSliced,
HexNetSlope,
HexNetCorner,
HexNetPyramidSegment,
HexNetConeSegment,
HexNetSlicedRounded,
HexNetSlopeRounded,
HexNetCornerRounded, //794
MagmaRockBulgedInner,
HexNetCylinder=797,
HexNetHemisphere,
HexNetSphere,
HexNetTubeCorner //800
}
}

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> - TODO
//public static ExclusiveGroup OWNED_BLOCKS { get { return CommonExclusiveGroups.REAL_BLOCKS_GROUPS_DON_T_USE_IN_NEW_CODE; } }
/// <summary>
/// Extra parts used in functional blocks
/// </summary>
public static ExclusiveGroup FUNCTIONAL_BLOCK_PARTS { get { return CommonExclusiveGroups.FUNCTIONAL_BLOCK_PART_GROUP; } }
/// <summary>
/// Blocks which are disabled in Simulation mode
/// </summary>
public static ExclusiveGroup SIM_BLOCKS_DISABLED { get { return CommonExclusiveGroups.DISABLED_JOINTS_IN_SIM_GROUP; } }
//public static ExclusiveGroup SPAWN_POINTS { get { return CommonExclusiveGroups.SPAWN_POINTS_GROUP; } }
//public static ExclusiveGroup SPAWN_POINTS_DISABLED { get { return CommonExclusiveGroups.SPAWN_POINTS_DISABLED_GROUP; } }
/// <summary>
/// The ID of the most recently placed block
/// </summary>
public static uint LatestBlockID {
get
{ //Need the private field as the property increments itself
return ((uint) AccessTools.Field(typeof(CommonExclusiveGroups), "_nextBlockEntityID").GetValue(null)) - 1;
}
}
}
}

View file

@ -0,0 +1,124 @@
using System;
using Gamecraft.Wires;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Tests;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
#if TEST
/// <summary>
/// Block test cases. Not accessible in release versions.
/// </summary>
[APITestClass]
public static class BlockTests
{
[APITestCase(TestType.EditMode)]
public static void TestPlaceNew()
{
Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero);
Assert.NotNull(newBlock.Id, "Newly placed block is missing Id. This should be populated when the block is placed.", "Newly placed block Id is not null, block successfully placed.");
}
[APITestCase(TestType.EditMode)]
public static void TestInitProperty()
{
Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero + 2);
if (!Assert.CloseTo(newBlock.Position, (Unity.Mathematics.float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return;
//Assert.Equal(newBlock.Exists, true, "Newly placed block does not exist, possibly because Sync() skipped/missed/failed.", "Newly placed block exists, Sync() successful.");
}
[APITestCase(TestType.EditMode)]
public static void TestTextBlock()
{
TextBlock textBlock = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
Assert.Errorless(() => { textBlock = Block.PlaceNew<TextBlock>(BlockIDs.TextBlock, Unity.Mathematics.float3.zero + 1); }, "Block.PlaceNew<TextBlock>() raised an exception: ", "Block.PlaceNew<TextBlock>() completed without issue.");
if (!Assert.NotNull(textBlock, "Block.PlaceNew<TextBlock>() returned null, possibly because it failed silently.", "Specialized TextBlock is not null.")) return;
if (!Assert.NotNull(textBlock.Text, "TextBlock.Text is null, possibly because it failed silently.", "TextBlock.Text is not null.")) return;
if (!Assert.NotNull(textBlock.TextBlockId, "TextBlock.TextBlockId is null, possibly because it failed silently.", "TextBlock.TextBlockId is not null.")) return;
}
[APITestCase(TestType.EditMode)]
public static void TestMotor()
{
Block newBlock = Block.PlaceNew(BlockIDs.MotorS, Unity.Mathematics.float3.zero + 1);
Motor b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
Assert.Errorless(() => { b = newBlock.Specialise<Motor>(); }, "Block.Specialize<Motor>() raised an exception: ", "Block.Specialize<Motor>() completed without issue.");
if (!Assert.NotNull(b, "Block.Specialize<Motor>() returned null, possibly because it failed silently.", "Specialized Motor is not null.")) return;
if (!Assert.CloseTo(b.Torque, 75f, $"Motor.Torque {b.Torque} does not equal default value, possibly because it failed silently.", "Motor.Torque close enough to default.")) return;
if (!Assert.CloseTo(b.TopSpeed, 30f, $"Motor.TopSpeed {b.TopSpeed} does not equal default value, possibly because it failed silently.", "Motor.Torque is close enough to default.")) return;
if (!Assert.Equal(b.Reverse, false, $"Motor.Reverse {b.Reverse} does not equal default value, possibly because it failed silently.", "Motor.Reverse is default.")) return;
}
[APITestCase(TestType.EditMode)]
public static void TestPiston()
{
Block newBlock = Block.PlaceNew(BlockIDs.PneumaticPiston, Unity.Mathematics.float3.zero + 1);
Piston b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
Assert.Errorless(() => { b = newBlock.Specialise<Piston>(); }, "Block.Specialize<Piston>() raised an exception: ", "Block.Specialize<Piston>() completed without issue.");
if (!Assert.NotNull(b, "Block.Specialize<Piston>() returned null, possibly because it failed silently.", "Specialized Piston is not null.")) return;
if (!Assert.CloseTo(b.MaximumExtension, 1.01f, $"Piston.MaximumExtension {b.MaximumExtension} does not equal default value, possibly because it failed silently.", "Piston.MaximumExtension is close enough to default.")) return;
if (!Assert.CloseTo(b.MaximumForce, 750f, $"Piston.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Piston.MaximumForce is close enough to default.")) return;
}
[APITestCase(TestType.EditMode)]
public static void TestServo()
{
Block newBlock = Block.PlaceNew(BlockIDs.ServoAxle, Unity.Mathematics.float3.zero + 1);
Servo b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
Assert.Errorless(() => { b = newBlock.Specialise<Servo>(); }, "Block.Specialize<Servo>() raised an exception: ", "Block.Specialize<Servo>() completed without issue.");
if (!Assert.NotNull(b, "Block.Specialize<Servo>() returned null, possibly because it failed silently.", "Specialized Servo is not null.")) return;
if (!Assert.CloseTo(b.MaximumAngle, 180f, $"Servo.MaximumAngle {b.MaximumAngle} does not equal default value, possibly because it failed silently.", "Servo.MaximumAngle is close enough to default.")) return;
if (!Assert.CloseTo(b.MinimumAngle, -180f, $"Servo.MinimumAngle {b.MinimumAngle} does not equal default value, possibly because it failed silently.", "Servo.MinimumAngle is close enough to default.")) return;
if (!Assert.CloseTo(b.MaximumForce, 750f, $"Servo.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Servo.MaximumForce is close enough to default.")) return;
}
[APITestCase(TestType.Game)]
public static void TestMusicBlock1()
{
Block newBlock = Block.PlaceNew(BlockIDs.MusicBlock, Unity.Mathematics.float3.zero + 2);
MusicBlock b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
Assert.Errorless(() => { b = newBlock.Specialise<MusicBlock>(); }, "Block.Specialize<MusicBlock>() raised an exception: ", "Block.Specialize<MusicBlock>() completed without issue.");
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
if (!Assert.CloseTo(b.Volume, 100f, $"MusicBlock.Volume {b.Volume} does not equal default value, possibly because it failed silently.", "MusicBlock.Volume is close enough to default.")) return;
if (!Assert.Equal(b.TrackIndex, 0, $"MusicBlock.TrackIndex {b.TrackIndex} does not equal default value, possibly because it failed silently.", "MusicBlock.TrackIndex is equal to default.")) return;
_musicBlock = b;
}
private static MusicBlock _musicBlock;
[APITestCase(TestType.EditMode)]
public static void TestMusicBlock2()
{
//Block newBlock = Block.GetLastPlacedBlock();
var b = _musicBlock;
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
b.IsPlaying = true; // play sfx
if (!Assert.Equal(b.IsPlaying, true, $"MusicBlock.IsPlaying {b.IsPlaying} does not equal true, possibly because it failed silently.", "MusicBlock.IsPlaying is set properly.")) return;
if (!Assert.Equal(b.ChannelType, ChannelType.None, $"MusicBlock.ChannelType {b.ChannelType} does not equal default value, possibly because it failed silently.", "MusicBlock.ChannelType is equal to default.")) return;
//Assert.Log(b.Track.ToString());
if (!Assert.Equal(b.Track.ToString(), new Guid("3237ff8f-f5f2-4f84-8144-496ca280f8c0").ToString(), $"MusicBlock.Track {b.Track} does not equal default value, possibly because it failed silently.", "MusicBlock.Track is equal to default.")) return;
}
[APITestCase(TestType.EditMode)]
public static void TestLogicGate()
{
Block newBlock = Block.PlaceNew(BlockIDs.NOTLogicBlock, Unity.Mathematics.float3.zero + 1);
LogicGate b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
Assert.Errorless(() => { b = newBlock.Specialise<LogicGate>(); }, "Block.Specialize<LogicGate>() raised an exception: ", "Block.Specialize<LogicGate>() completed without issue.");
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
if (!Assert.Equal(b.InputCount, 1u, $"LogicGate.InputCount {b.InputCount} does not equal default value, possibly because it failed silently.", "LogicGate.InputCount is default.")) return;
if (!Assert.Equal(b.OutputCount, 1u, $"LogicGate.OutputCount {b.OutputCount} does not equal default value, possibly because it failed silently.", "LogicGate.OutputCount is default.")) return;
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
//if (!Assert.Equal(b.PortName(0, true), "Input", $"LogicGate.PortName(0, input:true) {b.PortName(0, true)} does not equal default value, possibly because it failed silently.", "LogicGate.PortName(0, input:true) is close enough to default.")) return;
LogicGate target = null;
if (!Assert.Errorless(() => { target = Block.PlaceNew<LogicGate>(BlockIDs.ANDLogicBlock, Unity.Mathematics.float3.zero + 2); })) return;
Wire newWire = null;
if (!Assert.Errorless(() => { newWire = b.Connect(0, target, 0);})) return;
if (!Assert.NotNull(newWire, "SignalingBlock.Connect(...) returned null, possible because it failed silently.", "SignalingBlock.Connect(...) returned a non-null value.")) return;
}
}
#endif
}

View file

@ -0,0 +1,75 @@
using System;
using RobocraftX.Blocks;
using RobocraftX.Common;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class ConsoleBlock : SignalingBlock
{
public ConsoleBlock(EGID id): base(id)
{
}
public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP))
{
}
// custom console block properties
/// <summary>
/// Setting a nonexistent command will crash the game when switching to simulation
/// </summary>
public string Command
{
get
{
return BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.commandName);
}
set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.commandName.Set(val),
value);
}
}
public string Arg1
{
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg1);
set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg1.Set(val),
value);
}
}
public string Arg2
{
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg2);
set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg2.Set(val),
value);
}
}
public string Arg3
{
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg3);
set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg3.Set(val),
value);
}
}
}
}

View file

@ -0,0 +1,16 @@
using RobocraftX.Common;
using Svelto.ECS;
namespace GamecraftModdingAPI.Blocks
{
public class LogicGate : SignalingBlock
{
public LogicGate(EGID id) : base(id)
{
}
public LogicGate(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_LOGIC_BLOCK_GROUP))
{
}
}
}

View file

@ -0,0 +1,72 @@
using System;
using RobocraftX.Blocks;
using RobocraftX.Common;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Motor : SignalingBlock
{
public Motor(EGID id) : base(id)
{
}
public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP))
{
}
// custom motor properties
/// <summary>
/// The motor's maximum rotational velocity.
/// </summary>
public float TopSpeed
{
get
{
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxVelocity);
}
set
{
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxVelocity = val, value);
}
}
/// <summary>
/// The motor's maximum rotational force.
/// </summary>
public float Torque
{
get
{
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxForce);
}
set
{
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxForce = val, value);
}
}
/// <summary>
/// The motor's direction.
/// </summary>
public bool Reverse
{
get
{
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.reverse);
}
set
{
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, bool val) => st.reverse = val, value);
}
}
}
}

View file

@ -0,0 +1,80 @@
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
internal float3 MoveBlock(EGID blockID, BlockEngine.BlockInitData data, float3 vector)
{
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
init.Init(new PositionEntityStruct {position = vector});
init.Init(new GridRotationStruct {position = vector});
init.Init(new LocalTransformEntityStruct {position = vector});
return vector;
}
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID);
// 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).isProcessed = false;
return posStruct.position;
}
internal float3 GetPosition(EGID blockID, BlockEngine.BlockInitData data)
{
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
return init.Has<PositionEntityStruct>() ? init.Get<PositionEntityStruct>().position : float3.zero;
}
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
return posStruct.position;
}
}
}

View file

@ -0,0 +1,146 @@
using System;
using FMOD.Studio;
using FMODUnity;
using Gamecraft.Wires;
using RobocraftX.Common;
using RobocraftX.Blocks;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Tests;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class MusicBlock : SignalingBlock
{
public MusicBlock(EGID id) : base(id)
{
}
public MusicBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_MUSIC_BLOCK_GROUP))
{
}
public byte TrackIndex
{
get
{
return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct st) => st.trackIndx);
}
set
{
BlockEngine.SetBlockInfo(this,
(ref MusicBlockDataEntityStruct msdes, byte val) => msdes.trackIndx = val, value);
}
}
public Guid Track
{
get
{
return BlockEngine.GetBlockInfo(this,
(MusicBlockDataEntityStruct msdes) => msdes.fmod2DEventPaths.Get<Guid>(msdes.trackIndx));
}
set
{
BlockEngine.SetBlockInfo(this, (ref MusicBlockDataEntityStruct msdes, Guid val) =>
{
for (byte i = 0; i < msdes.fmod2DEventPaths.Count<Guid>(); i++)
{
Guid track = msdes.fmod2DEventPaths.Get<Guid>(i);
if (track == val)
{
msdes.trackIndx = i;
break;
}
}
}, value);
}
}
public Guid[] Tracks
{
get
{
return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct msdes) =>
{
Guid[] tracks = new Guid[msdes.fmod2DEventPaths.Count<Guid>()];
for (byte i = 0; i < tracks.Length; i++)
{
tracks[i] = msdes.fmod2DEventPaths.Get<Guid>(i);
}
return tracks;
});
}
}
public float Volume
{
get
{
return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct msdes) => msdes.tweakableVolume);
}
set
{
BlockEngine.SetBlockInfo(this,
(ref MusicBlockDataEntityStruct msdes, float val) => msdes.tweakableVolume = val, value);
}
}
public ChannelType ChannelType
{
get
{
Assert.Log("Block exists: " + Exists);
return BlockEngine.GetBlockInfo(this,
(MusicBlockDataEntityStruct msdes) => (ChannelType) msdes.channelType);
}
set
{
BlockEngine.SetBlockInfo(this,
(ref MusicBlockDataEntityStruct msdes, ChannelType val) => msdes.channelType = (byte) val, value);
}
}
public bool IsPlaying
{
get
{
return BlockEngine.GetBlockInfo(this,
(MusicBlockDataEntityStruct msdes) => msdes.isPlaying);
}
set
{
BlockEngine.SetBlockInfo(this, (ref MusicBlockDataEntityStruct msdes, bool val) =>
{
if (msdes.isPlaying == val) return;
if (val)
{
// start playing
EventInstance inst = RuntimeManager.CreateInstance(msdes.fmod2DEventPaths.Get<Guid>(msdes.trackIndx));
inst.setVolume(msdes.tweakableVolume / 100f);
inst.start();
msdes.eventHandle = inst.handle;
}
else
{
// stop playing
EventInstance inst = default(EventInstance);
inst.handle = msdes.eventHandle;
inst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
inst.release();
}
msdes.isPlaying = val;
}, value);
}
}
}
}

View file

@ -0,0 +1,52 @@
using Gamecraft.Wires;
using RobocraftX.Common;
using Svelto.ECS;
namespace GamecraftModdingAPI.Blocks
{
public class ObjectIdentifier : Block
{
public ObjectIdentifier(EGID id) : base(id)
{
}
public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP))
{
}
public char Identifier
{
get => (char) BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.objectId + 'A');
set
{
BlockEngine.SetBlockInfo(this, (ref ObjectIdEntityStruct st, char val) =>
{
st.objectId = (byte) (val - 'A');
Label = val + ""; //The label isn't updated automatically
}, value);
}
}
/// <summary>
/// Simulation-time ID. Assigned by the game starting from 0.
/// </summary>
public byte SimID
{
get => BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.simObjectId);
}
/// <summary>
/// Finds the identifier blocks with the given ID.
/// </summary>
/// <param name="id">The ID to look for</param>
/// <returns>An array that may be empty</returns>
public static ObjectIdentifier[] GetByID(char id) => BlockEngine.GetObjectIDsFromID((byte) (id - 'A'), false);
/// <summary>
/// Finds the identifier blocks with the given simulation-time ID. This ID is assigned by the game starting from 0.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static ObjectIdentifier[] GetBySimID(byte id) => BlockEngine.GetObjectIDsFromID(id, true);
}
}

View file

@ -0,0 +1,51 @@
using System;
using RobocraftX.Blocks;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI.Utility;
using RobocraftX.Common;
namespace GamecraftModdingAPI.Blocks
{
public class Piston : SignalingBlock
{
public Piston(EGID id) : base(id)
{
}
public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP))
{
}
// custom piston properties
/// <summary>
/// The piston's max extension distance.
/// </summary>
public float MaximumExtension
{
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxDeviation);
set
{
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxDeviation = val,
value);
}
}
/// <summary>
/// The piston's max extension force.
/// </summary>
public float MaximumForce
{
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxForce);
set
{
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxForce = val, value);
}
}
}
}

View file

@ -0,0 +1,132 @@
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;
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, out EntityComponentInitializer initializer)
{ //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.)");
initializer = BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation,
(player ?? new Player(PlayerType.Local)).Id);
return initializer.EGID;
}
private EntityComponentInitializer 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};
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};
EntityComponentInitializer
structInitializer =
_blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, 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 = structInitializer.EGID;
pickedBlock.placedBlockWasAPickedBlock = false;
return structInitializer;
}
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.Count<MachineConnectionStruct>() - 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,90 @@
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
internal float3 RotateBlock(EGID blockID, BlockEngine.BlockInitData data, Vector3 vector)
{
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
init.Init(new RotationEntityStruct {rotation = new Quaternion {eulerAngles = vector}});
init.Init(new GridRotationStruct {rotation = new Quaternion {eulerAngles = vector}});
init.Init(new LocalTransformEntityStruct {rotation = new Quaternion {eulerAngles = vector}});
return vector;
}
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID);
// main (persistent) position
Quaternion newRotation = rotStruct.rotation;
newRotation.eulerAngles = vector;
rotStruct.rotation = newRotation;
// placement grid rotation
Quaternion newGridRotation = gridStruct.rotation;
newGridRotation.eulerAngles = vector;
gridStruct.rotation = newGridRotation;
// rendered position
Quaternion newTransRotation = 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).isProcessed = false;
return ((Quaternion)rotStruct.rotation).eulerAngles;
}
internal float3 GetRotation(EGID blockID, BlockEngine.BlockInitData data)
{
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
return init.Has<RotationEntityStruct>()
? (float3) ((Quaternion) init.Get<RotationEntityStruct>().rotation).eulerAngles
: float3.zero;
}
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
return ((Quaternion) rotStruct.rotation).eulerAngles;
}
}
}

View file

@ -2,18 +2,18 @@
using HarmonyLib;
using RobocraftX.Common;
using RobocraftX.DOTS;
using RobocraftX.UECS;
using Svelto.ECS;
using Unity.Entities;
using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Blocks.Engines
namespace GamecraftModdingAPI.Blocks
{
public class ScalingEngine : IApiEngine
{
private static IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> physicsEngine;
private static IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> physicsEngine;
public void Ready()
{
@ -24,26 +24,29 @@ namespace TechbloxModdingAPI.Blocks.Engines
{
}
public string Name { get; } = "TechbloxModdingAPIScalingEngine";
public string Name { get; } = "GamecraftModdingAPIScalingEngine";
public bool isRemovable { get; } = false;
private EntityManager _entityManager; //Unity entity manager
private static EntityManager _entityManager; //Unity entity manager
public void UpdateCollision(EGID egid)
{
if (_entityManager == default)
_entityManager = FullGameFields._physicsWorld.EntityManager;
//Assuming the block exists
var entity = entitiesDB.QueryEntity<DOTSPhysicsEntityStruct>(egid).dotsEntity;
var pes = new DOTSPhysicsEntityCreationStruct();
physicsEngine.Add(ref pes, egid); //Create new DOTS entity
var entity = entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(egid).uecsEntity;
var pes = new UECSPhysicsEntityCreationStruct();
physicsEngine.Add(ref pes, egid); //Create new UECS entity
_entityManager.DestroyEntity(entity);
}
internal void Setup(EntityManager entityManager)
{
_entityManager = entityManager;
}
[HarmonyPatch]
class PhysicsEnginePatch
public class PhysicsEnginePatch
{
static void Postfix(IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> __instance)
static void Postfix(IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> __instance)
{
physicsEngine = __instance;
Logging.MetaDebugLog("Physics engine injected.");
@ -51,7 +54,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
static MethodBase TargetMethod(Harmony instance)
{
return AccessTools.Method("RobocraftX.StateSync.HandleDOTSPhysicEntitiesWithPrefabCreationEngine" +
return AccessTools.Method("RobocraftX.StateSync.HandleUECSPhysicEntitiesWithPrefabCreationEngine" +
":Ready");
}
}

View file

@ -0,0 +1,76 @@
using System;
using RobocraftX.Blocks;
using RobocraftX.Common;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Servo : SignalingBlock
{
public Servo(EGID id) : base(id)
{
}
public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP))
{
}
// custom servo properties
/// <summary>
/// The servo's minimum angle.
/// </summary>
public float MinimumAngle
{
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.minDeviation);
set
{
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.minDeviation = val, value);
}
}
/// <summary>
/// The servo's maximum angle.
/// </summary>
public float MaximumAngle
{
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxDeviation);
set
{
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxDeviation = val, value);
}
}
/// <summary>
/// The servo's maximum force.
/// </summary>
public float MaximumForce
{
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxForce);
set
{
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxForce = val, value);
}
}
/// <summary>
/// The servo's direction.
/// </summary>
public bool Reverse
{
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.reverse);
set
{
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, bool val) => st.reverse = val, value);
}
}
}
}

View file

@ -1,14 +1,11 @@
using System;
using Gamecraft.Wires;
using Svelto.DataStructures;
using Svelto.ECS;
using Svelto.DataStructures;
using Gamecraft.Wires;
using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Utility.ECS;
using GamecraftModdingAPI.Engines;
namespace TechbloxModdingAPI.Blocks.Engines
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine which executes signal actions
@ -20,7 +17,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public const float HIGH = 1.0f;
public const float ZERO = 0.0f;
public string Name { get; } = "TechbloxModdingAPISignalGameEngine";
public string Name { get; } = "GamecraftModdingAPISignalGameEngine";
public EntitiesDB entitiesDB { set; private get; }
@ -42,18 +39,19 @@ namespace TechbloxModdingAPI.Blocks.Engines
// implementations for block wiring
public (WireEntityStruct Wire, EGID ID) CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
{
EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup<WiresGroup>.Group);
EntityComponentInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
wireInitializer.Init(new WireEntityStruct
{
sourceBlockEGID = startBlock,
sourcePortUsage = startPort,
destinationBlockEGID = endBlock,
destinationPortUsage = endPort
destinationPortUsage = endPort,
ID = wireEGID
});
return (wireInitializer.Get<WireEntityStruct>(), wireEGID);
return wireInitializer.Get<WireEntityStruct>();
}
public ref WireEntityStruct GetWire(EGID wire)
@ -77,8 +75,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
{
ExclusiveGroup group = input
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
? NamedExclusiveGroup<InputPortsGroup>.Group
: NamedExclusiveGroup<OutputPortsGroup>.Group;
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
EGID egid = new EGID(id, group);
if (!entitiesDB.Exists<PortEntityStruct>(egid))
@ -90,8 +88,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input)
{
var bps = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
if (!bps)
BlockPortsStruct bps = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out bool exists);
if (!exists)
{
throw new BlockException("Block does not exist");
}
@ -116,8 +114,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public bool SetSignal(uint signalID, float signal, bool input = true)
{
var (array, count) = GetSignalStruct(signalID, out uint index, input);
if (count > 0) array[index].valueAsFloat = signal;
var array = GetSignalStruct(signalID, out uint index, input);
if (array.count > 0) array[index].valueAsFloat = signal;
return false;
}
@ -129,8 +127,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
{
var (array, count) = GetSignalStruct(signalID, out uint index, input);
if (count > 0)
var array = GetSignalStruct(signalID, out uint index, input);
if (array.count > 0)
{
ref var channelData = ref array[index];
channelData.valueAsFloat += signal;
@ -160,8 +158,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public float GetSignal(uint signalID, bool input = true)
{
var (array, count) = GetSignalStruct(signalID, out uint index, input);
return count > 0 ? array[index].valueAsFloat : 0f;
var array = GetSignalStruct(signalID, out uint index, input);
return array.count > 0 ? array[index].valueAsFloat : 0f;
}
public uint[] GetSignalIDs(EGID blockID, bool input = true)
@ -190,7 +188,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
EGID[] inputs = new EGID[ports.inputCount];
for (uint i = 0; i < ports.inputCount; i++)
{
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group);
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<InputPortsGroup>.Group);
}
return inputs;
}
@ -201,55 +199,67 @@ namespace TechbloxModdingAPI.Blocks.Engines
EGID[] outputs = new EGID[ports.outputCount];
for (uint i = 0; i < ports.outputCount; i++)
{
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group);
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<OutputPortsGroup>.Group);
}
return outputs;
}
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(Block block, byte portUsage, bool output)
public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists)
{
return MatchBlockIOToPort(block.Id, portUsage, output);
BlockPortsStruct ports = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out exists);
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
}
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(EGID block, byte portUsage, bool output)
public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists)
{
if (!entitiesDB.Exists<BlockPortsStruct>(block))
return default;
var group = output
? NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group
: NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group;
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper))
return default;
for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i)
{
uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i;
if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) ||
array[index].usage != portUsage) continue;
return new OptionalRef<PortEntityStruct>(array, index, new EGID(entityID, group));
exists = false;
return default;
}
return default;
exists = true;
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
}
public OptionalRef<WireEntityStruct> MatchPortToWire(PortEntityStruct port, EGID blockID, out EGID wireID)
public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
{
var (wires, ids, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
for (uint i = 0; i < count; i++)
BlockPortsStruct ports = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out exists);
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
}
public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists)
{
if (!entitiesDB.Exists<BlockPortsStruct>(block))
{
exists = false;
return default;
}
exists = true;
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
}
public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
{
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group);
for (uint i = 0; i < wires.count; i++)
{
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
{
wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group);
return new OptionalRef<WireEntityStruct>(wires, i);
exists = true;
return ref wires[i];
}
}
wireID = default;
return default;
exists = false;
WireEntityStruct[] defRef = new WireEntityStruct[1];
return ref defRef[0];
}
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue)
public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, byte startPort = byte.MaxValue,
byte endPort = byte.MaxValue)
{
EGID[] startPorts;
if (startPort == byte.MaxValue)
@ -260,7 +270,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
else
{
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<OutputPortsGroup>.Group) };
}
EGID[] endPorts;
@ -272,81 +282,129 @@ namespace TechbloxModdingAPI.Blocks.Engines
else
{
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<InputPortsGroup>.Group) };
}
EntityCollection<WireEntityStruct> wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group);
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
{
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
{
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
foreach (var wireOpt in entitiesDB.QueryEntitiesOptional<WireEntityStruct>(
NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group))
for (int w = 0; w < wires.count; w++)
{
var wire = wireOpt.Get();
if ((wire.destinationPortUsage == endPES.usage && wire.destinationBlockEGID == endBlock)
&& (wire.sourcePortUsage == startPES.usage && wire.sourceBlockEGID == startBlock))
if ((wires[w].destinationPortUsage == endPES.usage && wires[w].destinationBlockEGID == endBlock)
&& (wires[w].sourcePortUsage == startPES.usage && wires[w].sourceBlockEGID == startBlock))
{
return wireOpt.EGID;
exists = true;
return ref wires[w];
}
}
}
}
return default;
exists = false;
WireEntityStruct[] defRef = new WireEntityStruct[1];
return ref defRef[0];
}
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
{
var port = GetPort(portID);
var (channels, count) = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
return port.firstChannelIndexCachedInSim < count
? new OptionalRef<ChannelDataStruct>(channels, port.firstChannelIndexCachedInSim)
: default;
public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists)
{
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
if (port.firstChannelIndexCachedInSim < channels.count)
{
exists = true;
return ref channels[port.firstChannelIndexCachedInSim];
}
exists = false;
ChannelDataStruct[] defRef = new ChannelDataStruct[1];
return ref defRef[0];
}
public EGID[] GetElectricBlocks()
{
var res = new FasterList<EGID>();
foreach (var ((coll, ids, count), _) in entitiesDB.QueryEntities<BlockPortsStruct>())
{
for (int i = 0; i < count; i++)
{
ref BlockPortsStruct s = ref coll[i];
//res.Add(s.ID); - TODO: Would need to search for the groups for each block
}
}
foreach (var (coll, _) in entitiesDB.QueryEntities<BlockPortsStruct>())
foreach (ref BlockPortsStruct s in coll)
res.Add(s.ID);
return res.ToArray();
}
public EGID[] WiredToInput(EGID block, byte port)
{
return entitiesDB
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
.ToArray(wire => wire.ID,
wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block);
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
(WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block);
EGID[] result = new EGID[wireEntityStructs.Length];
for (uint i = 0; i < wireEntityStructs.Length; i++)
{
result[i] = wireEntityStructs[i].ID;
}
return result;
}
public EGID[] WiredToOutput(EGID block, byte port)
{
return entitiesDB
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
.ToArray(wire => wire.ID,
wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block);
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group,
(WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block);
EGID[] result = new EGID[wireEntityStructs.Length];
for (uint i = 0; i < wireEntityStructs.Length; i++)
{
result[i] = wireEntityStructs[i].ID;
}
return result;
}
private T[] Search<T>(ExclusiveGroup group, Func<T, bool> isMatch) where T : struct, IEntityComponent
{
FasterList<T> results = new FasterList<T>();
EntityCollection<T> components = entitiesDB.QueryEntities<T>(group);
for (uint i = 0; i < components.count; i++)
{
if (isMatch(components[i]))
{
results.Add(components[i]);
}
}
return results.ToArray();
}
private ref T GetFromDbOrInitData<T>(Block block, EGID id, out bool exists) where T : unmanaged, IEntityComponent
{
T[] defRef = new T[1];
if (entitiesDB.Exists<T>(id))
{
exists = true;
return ref entitiesDB.QueryEntity<T>(id);
}
if (block == null || block.InitData.Group == null)
{
exists = false;
return ref defRef[0];
}
EntityComponentInitializer initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
if (initializer.Has<T>())
{
exists = true;
return ref initializer.Get<T>();
}
exists = false;
return ref defRef[0];
}
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
{
ExclusiveGroup group = input
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
? NamedExclusiveGroup<InputPortsGroup>.Group
: NamedExclusiveGroup<OutputPortsGroup>.Group;
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
{
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).firstChannelIndexCachedInSim;
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex;
var channelData =
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
return channelData;
}

View file

@ -4,10 +4,10 @@ using Gamecraft.Wires;
using Svelto.ECS;
using Unity.Mathematics;
using TechbloxModdingAPI;
using TechbloxModdingAPI.Utility;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Blocks
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Common implementation for blocks that support wiring.
@ -46,9 +46,9 @@ namespace TechbloxModdingAPI.Blocks
/// <returns>The connected wire.</returns>
/// <param name="portId">Port identifier.</param>
/// <param name="connected">Whether the port has a wire connected to it.</param>
protected OptionalRef<WireEntityStruct> GetConnectedWire(PortEntityStruct port, out EGID egid)
protected ref WireEntityStruct GetConnectedWire(EGID portId, out bool connected)
{
return SignalEngine.MatchPortToWire(port, Id, out egid);
return ref SignalEngine.MatchPortToWire(portId, Id, out connected);
}
/// <summary>
@ -56,9 +56,10 @@ namespace TechbloxModdingAPI.Blocks
/// </summary>
/// <returns>The channel data.</returns>
/// <param name="portId">Port identifier.</param>
protected OptionalRef<ChannelDataStruct> GetChannelData(EGID portId)
/// <param name="exists">Whether the channel actually exists.</param>
protected ref ChannelDataStruct GetChannelData(EGID portId, out bool exists)
{
return SignalEngine.GetChannelDataStruct(portId);
return ref SignalEngine.GetChannelDataStruct(portId, out exists);
}
/// <summary>
@ -66,7 +67,7 @@ namespace TechbloxModdingAPI.Blocks
/// </summary>
public uint InputCount
{
get => BlockEngine.GetBlockInfo<BlockPortsStruct>(this).inputCount;
get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.inputCount);
}
/// <summary>
@ -74,7 +75,7 @@ namespace TechbloxModdingAPI.Blocks
/// </summary>
public uint OutputCount
{
get => BlockEngine.GetBlockInfo<BlockPortsStruct>(this).outputCount;
get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.outputCount);
}
/// <summary>
@ -108,6 +109,7 @@ namespace TechbloxModdingAPI.Blocks
/// <returns>The localized port name.</returns>
public string PortName(byte port, bool input)
{
BlockPortsStruct bps = BlockEngine.GetBlockInfo(this, (BlockPortsStruct a) => a);
PortEntityStruct pes = SignalEngine.GetPortByOffset(this, port, input);
return pes.portNameLocalised;
}

View file

@ -0,0 +1,78 @@
using System;
using RobocraftX.Blocks;
using RobocraftX.Common;
using Gamecraft.CharacterVulnerability;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class SpawnPoint : Block
{
public SpawnPoint(EGID id) : base(id)
{
}
public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SPAWNPOINT_BLOCK_GROUP))
{
}
// custom spawn point properties
/// <summary>
/// The lives the player spawns in with.
/// </summary>
public uint Lives
{
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.lives);
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, uint val) => st.lives = val, value);
}
}
/// <summary>
/// Whether the spawned player can take damage.
/// </summary>
public bool Damageable
{
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.canTakeDamage);
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.canTakeDamage = val, value);
}
}
/// <summary>
/// Whether the game over screen will be displayed
/// </summary>
public bool GameOverEnabled
{
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.gameOverScreen);
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.gameOverScreen = val, value);
}
}
/// <summary>
/// The team id for players who spawn here.
/// </summary>
public byte Team
{
get => BlockEngine.GetBlockInfo(this, (SpawnPointIdsEntityStruct st) => st.teamId);
set
{
BlockEngine.SetBlockInfo(this, (ref SpawnPointIdsEntityStruct st, byte val) => st.teamId = val, value);
}
}
}
}

View file

@ -0,0 +1,60 @@
using System;
using Gamecraft.Blocks.GUI;
using RobocraftX.Common;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class TextBlock : SignalingBlock
{
public TextBlock(EGID id) : base(id)
{
}
public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP))
{
}
// custom text block properties
/// <summary>
/// The text block's current text.
/// </summary>
public string Text
{
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textCurrent);
set
{
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
{
tbds.textCurrent.Set(val);
tbds.textStored.Set(val);
}, value);
BlockEngine.SetBlockInfo(this,
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockStringContent.Set(val), value);
}
}
/// <summary>
/// The text block's current text block ID (used in ChangeTextBlockCommand).
/// </summary>
public string TextBlockId
{
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textBlockID);
set
{
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
tbds.textBlockID.Set(val), value);
BlockEngine.SetBlockInfo(this,
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockID.Set(val), value);
}
}
}
}

View file

@ -0,0 +1,82 @@
using System;
using RobocraftX.Blocks;
using RobocraftX.Common;
using Gamecraft.Blocks.TimerBlock;
using Svelto.ECS;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Timer : SignalingBlock
{
public Timer(EGID id) : base(id)
{
}
public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TIMER_BLOCK_GROUP))
{
}
// custom timer properties
/// <summary>
/// The player-specified start time.
/// </summary>
public float Start
{
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.startTime);
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.startTime = val,
value);
}
}
/// <summary>
/// The player-specified end time.
/// </summary>
public float End
{
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.endTime);
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.endTime = val,
value);
}
}
/// <summary>
/// Whether to display time with millisecond precision.
/// </summary>
public bool DisplayMilliseconds
{
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.outputFormatHasMS);
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, bool val) => tbds.outputFormatHasMS = val,
value);
}
}
/// <summary>
/// Current time (as of the last video frame), in milliseconds.
/// </summary>
public int CurrentTime
{
get => BlockEngine.GetBlockInfo(this, (TimerBlockLabelCacheEntityStruct st) => st.timeLastRenderFrameMS);
set
{
BlockEngine.SetBlockInfo(this, (ref TimerBlockLabelCacheEntityStruct tbds, int val) => tbds.timeLastRenderFrameMS = val,
value);
}
}
}
}

View file

@ -0,0 +1,355 @@
using System;
using Gamecraft.Wires;
using Svelto.ECS;
using Svelto.ECS.Experimental;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
public class Wire
{
internal static SignalEngine signalEngine;
protected EGID startPortEGID;
protected EGID endPortEGID;
protected EGID startBlockEGID;
protected EGID endBlockEGID;
protected EGID wireEGID;
protected bool inputToOutput;
protected byte startPort;
protected byte endPort;
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
{
WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
return new Wire(wire, start, end);
}
/// <summary>
/// An existing wire connection ending at the specified input.
/// If multiple exist, this will return the first one found.
/// </summary>
/// <param name="end">Destination block.</param>
/// <param name="endPort">Port number.</param>
/// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
{
EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
if (!exists) return null;
WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists);
if (exists)
{
return new Wire(new Block(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort);
}
return null;
}
/// <summary>
/// An existing wire connection starting at the specified output.
/// If multiple exist, this will return the first one found.
/// </summary>
/// <param name="start">Source block entity ID.</param>
/// <param name="startPort">Port number.</param>
/// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
{
EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
if (!exists) return null;
WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists);
if (exists)
{
return new Wire(start, new Block(wire.destinationBlockEGID), startPort, wire.destinationPortUsage);
}
return null;
}
/// <summary>
/// Construct a wire object from an existing connection.
/// </summary>
/// <param name="start">Starting block ID.</param>
/// <param name="end">Ending block ID.</param>
/// <param name="startPort">Starting port number, or guess if omitted.</param>
/// <param name="endPort">Ending port number, or guess if omitted.</param>
/// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue)
{
startBlockEGID = start.Id;
endBlockEGID = end.Id;
// find block ports
WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort);
if (exists)
{
wireEGID = wire.ID;
endPortEGID = signalEngine.MatchBlockInputToPort(end, wire.destinationPortUsage, out exists);
if (!exists) throw new WireInvalidException("Wire end port not found");
startPortEGID = signalEngine.MatchBlockOutputToPort(start, wire.sourcePortUsage, out exists);
if (!exists) throw new WireInvalidException("Wire start port not found");
inputToOutput = false;
endPort = wire.destinationPortUsage;
startPort = wire.sourcePortUsage;
}
else
{
// flip I/O around and try again
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort);
if (exists)
{
wireEGID = wire.ID;
endPortEGID = signalEngine.MatchBlockOutputToPort(end, wire.sourcePortUsage, out exists);
if (!exists) throw new WireInvalidException("Wire end port not found");
startPortEGID = signalEngine.MatchBlockInputToPort(start, wire.destinationPortUsage, out exists);
if (!exists) throw new WireInvalidException("Wire start port not found");
inputToOutput = true; // end is actually the source
// NB: start and end are handled exactly as they're received as params.
// This makes wire traversal easier, but makes logic in this class a bit more complex
endPort = wire.sourcePortUsage;
startPort = wire.destinationPortUsage;
}
else
{
throw new WireInvalidException("Wire not found");
}
}
}
/// <summary>
/// Construct a wire object from an existing wire connection.
/// </summary>
/// <param name="start">Starting block ID.</param>
/// <param name="end">Ending block ID.</param>
/// <param name="startPort">Starting port number.</param>
/// <param name="endPort">Ending port number.</param>
/// <param name="wire">The wire ID.</param>
/// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
{
this.startBlockEGID = start.Id;
this.endBlockEGID = end.Id;
this.inputToOutput = inputToOutput;
this.wireEGID = wire;
if (inputToOutput)
{
endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
if (!exists) throw new WireInvalidException("Wire end port not found");
startPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out exists);
if (!exists) throw new WireInvalidException("Wire start port not found");
}
else
{
endPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
if (!exists) throw new WireInvalidException("Wire end port not found");
startPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out exists);
if (!exists) throw new WireInvalidException("Wire start port not found");
}
this.startPort = startPort;
this.endPort = endPort;
}
/// <summary>
/// Construct a wire object from an existing wire connection.
/// </summary>
/// <param name="wireEgid">The wire ID.</param>
public Wire(EGID wireEgid)
{
this.wireEGID = wireEgid;
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
this.startBlockEGID = wire.sourceBlockEGID;
this.endBlockEGID = wire.destinationBlockEGID;
this.inputToOutput = false;
endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists);
if (!exists) throw new WireInvalidException("Wire end port not found");
startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists);
if (!exists) throw new WireInvalidException("Wire start port not found");
this.endPort = wire.destinationPortUsage;
this.startPort = wire.sourcePortUsage;
}
internal Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest)
{
this.wireEGID = wire.ID;
this.startBlockEGID = wire.sourceBlockEGID;
this.endBlockEGID = wire.destinationBlockEGID;
inputToOutput = false;
endPortEGID = signalEngine.MatchBlockInputToPort(dest, wire.destinationPortUsage, out bool exists);
if (!exists) throw new WireInvalidException("Wire end port not found");
startPortEGID = signalEngine.MatchBlockOutputToPort(src, wire.sourcePortUsage, out exists);
if (!exists) throw new WireInvalidException("Wire start port not found");
this.endPort = wire.destinationPortUsage;
this.startPort = wire.sourcePortUsage;
}
/// <summary>
/// The wire's in-game id.
/// </summary>
public EGID Id
{
get => wireEGID;
}
/// <summary>
/// The wire's signal value, as a float.
/// </summary>
public float Float
{
get
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return 0f;
return cds.valueAsFloat;
}
set
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return;
cds.valueAsFloat = value;
}
}
/// <summary>
/// The wire's string signal.
/// </summary>
public string String
{
get
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return "";
return cds.valueAsEcsString;
}
set
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return;
cds.valueAsEcsString.Set(value);
}
}
/// <summary>
/// The wire's raw string signal.
/// </summary>
public ECSString ECSString
{
get
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return default;
return cds.valueAsEcsString;
}
set
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return;
cds.valueAsEcsString = value;
}
}
/// <summary>
/// The wire's signal id.
/// I'm 50% sure this is useless.
/// </summary>
public uint SignalId
{
get
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return uint.MaxValue;
return cds.valueAsID;
}
set
{
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
if (!exists) return;
cds.valueAsID = value;
}
}
/// <summary>
/// The block at the beginning of the wire.
/// </summary>
public SignalingBlock Start
{
get => new SignalingBlock(startBlockEGID);
}
/// <summary>
/// The port number that the beginning of the wire connects to.
/// </summary>
public byte StartPort
{
get => startPort;
}
/// <summary>
/// The block at the end of the wire.
/// </summary>
public SignalingBlock End
{
get => new SignalingBlock(endBlockEGID);
}
/// <summary>
/// The port number that the end of the wire connects to.
/// </summary>
public byte EndPort
{
get => endPort;
}
/// <summary>
/// Create a copy of the wire object where the direction of the wire is guaranteed to be from a block output to a block input.
/// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
/// </summary>
/// <returns>A copy of the wire object.</returns>
public Wire OutputToInputCopy()
{
return new Wire(wireEGID);
}
/// <summary>
/// Convert the wire object to the direction the signal flows.
/// Signals on wires always flow from a block output port to a block input port.
/// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
/// </summary>
public void OutputToInputInPlace()
{
if (inputToOutput)
{
inputToOutput = false;
// swap inputs and outputs
EGID temp = endBlockEGID;
endBlockEGID = startBlockEGID;
startBlockEGID = temp;
temp = endPortEGID;
endPortEGID = startPortEGID;
startPortEGID = temp;
byte tempPortNumber = endPort;
endPort = startPort;
startPort = tempPortNumber;
}
}
public override string ToString()
{
if (signalEngine.Exists<WireEntityStruct>(wireEGID))
{
return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {Start.PortName(StartPort, inputToOutput)}) -> ({End.Type}::{EndPort} aka {End.PortName(EndPort, !inputToOutput)})";
}
return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})";
}
internal static void Init() { }
}
}

View file

@ -0,0 +1,41 @@
using Gamecraft.Damage;
using RobocraftX.Common;
using Svelto.ECS;
namespace GamecraftModdingAPI
{
/// <summary>
/// Represnts a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints.
/// </summary>
public class Cluster
{
public EGID Id { get; }
public Cluster(EGID id)
{
Id = id;
}
public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
{
}
public float InitialHealth
{
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).initialHealth;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).initialHealth = value;
}
public float CurrentHealth
{
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).currentHealth;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).currentHealth = value;
}
public float HealthMultiplier
{
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).healthMultiplier;
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).healthMultiplier = value;
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,38 @@
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 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(EnginesRoot enginesRoot)
{
// 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.Threading.Tasks;
namespace TechbloxModdingAPI.Commands
using uREPL;
using RobocraftX.CommandLine.Custom;
namespace GamecraftModdingAPI.Commands
{
/// <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.
/// </summary>
public static class CommandRegistrationHelper
{
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)
@ -34,42 +39,50 @@ namespace TechbloxModdingAPI.Commands
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)
{
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)
{
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)
{
CustomCommands.Unregister(name);
RuntimeCommands.Unregister(name);
if (noConsole) { return; }
ConsoleCommands.Unregister(name);
}
public static void Call(string name)
{
CustomCommands.Call(name);
RuntimeCommands.Call(name);
}
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)
{
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)
{
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 TechbloxModdingAPI.Utility;
using TechbloxModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
using GamecraftModdingAPI.Engines;
namespace TechbloxModdingAPI.Commands
namespace GamecraftModdingAPI.Commands
{
/// <summary>
/// Engine interface to handle command operations.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,9 +6,9 @@ using System.Threading.Tasks;
using Svelto.ECS;
using TechbloxModdingAPI.Utility;
using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Engines
namespace GamecraftModdingAPI.Engines
{
/// <summary>
/// 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.Internal;
using TechbloxModdingAPI.Events;
using GamecraftModdingAPI.Events;
namespace TechbloxModdingAPI.Engines
namespace GamecraftModdingAPI.Engines
{
/// <summary>
/// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines.
/// </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,47 @@
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")]
[Obsolete]
[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,106 @@
using System;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
[Obsolete]
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,61 @@
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>
[Obsolete]
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,129 @@
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>
[Obsolete("This will be removed in an upcoming update. Use the new C# event architecture from GamecraftModdingAPI.App")]
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,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RobocraftX.CR.MainGame;
using Svelto.ECS;
using Unity.Entities;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame()
/// </summary>
[Obsolete]
[HarmonyPatch]
class GameActivatedComposePatch
{
public static bool IsGameSwitching = false;
public static bool IsGameReloading = false;
public static void Postfix(ref object contextHolder, ref EnginesRoot enginesRoot, World physicsWorld)
{
// register custom game engines
GameEngineManager.RegisterEngines(enginesRoot);
// initialize AsyncUtils
AsyncUtils.Setup(enginesRoot);
// initialize Block
Block.Setup(physicsWorld);
// 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,26 @@
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>
[Obsolete]
[HarmonyPatch(typeof(FullGameCompositionRoot), "ReloadGame")]
class GameReloadedPatch
{
public static void Postfix()
{
GameActivatedComposePatch.IsGameReloading = true;
}
}
}

View file

@ -0,0 +1,55 @@
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>
[Obsolete]
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(JobHandle inputDeps)
{
Emit();
return inputDeps;
}
public void Ready() { }
}
}

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 simulation mode.
/// </summary>
[Obsolete]
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(JobHandle inputDeps)
{
Emit();
return inputDeps;
}
public void Ready() { }
}
}

View file

@ -0,0 +1,30 @@
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>
[Obsolete]
[HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToGame")]
class GameSwitchedToPatch
{
public static void Prefix()
{
GameActivatedComposePatch.IsGameSwitching = true;
}
}
}

View file

@ -0,0 +1,166 @@
using System;
using Svelto.ECS;
namespace GamecraftModdingAPI.Events
{
[Obsolete]
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,24 @@
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>
[Obsolete]
public interface IEventEmitterEngine : IFactoryEngine
{
/// <summary>
/// Emit the event. (Optional)
/// </summary>
void Emit();
}
}

View file

@ -0,0 +1,21 @@
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>
[Obsolete]
public interface IEventHandlerEngine : IReactionaryEngine<ModEventEntityStruct>
{
}
}

View file

@ -0,0 +1,43 @@
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>
[Obsolete]
[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);
//Application.Application.SetFullGameCompositionRoot(__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,29 @@
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>
[Obsolete]
[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,66 @@
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>
[Obsolete]
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,146 @@
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>
[Obsolete]
public class SimpleEventHandlerEngine : IEventHandlerEngine
{
public int type { get; set; }
public string Name { get; set; }
private bool isActivated = false;
private bool jankActivateFix = false;
private bool jankDestroyFix = 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))
{
jankActivateFix = !jankActivateFix;
if (jankActivateFix) return;
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 Deactivate()
{
isActivated = false;
}
public void Ready() { }
public void Remove(ref ModEventEntityStruct entityView, EGID egid)
{
if (entityView.type.Equals(this.type) && isActivated)
{
jankDestroyFix = !jankDestroyFix;
if (jankDestroyFix) return;
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,946 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Version>1.5.0</Version>
<Authors>Exmods</Authors>
<PackageLicenseExpression>GNU General Public Licence 3+</PackageLicenseExpression>
<PackageProjectUrl>https://git.exmods.org/modtainers/GamecraftModdingAPI</PackageProjectUrl>
<NeutralLanguage>en-CA</NeutralLanguage>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Test|AnyCPU' ">
<DefineConstants>DEBUG;TEST;TRACE</DefineConstants>
</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="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="Blocks.HUDFeedbackBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Blocks.HUDFeedbackBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Blocks.HUDFeedbackBlocks.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="CommandLineCompositionRoot">
<HintPath>..\ref\Gamecraft_Data\Managed\CommandLineCompositionRoot.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\CommandLineCompositionRoot.dll</HintPath>
</Reference>
<Reference Include="ConsoleBlockComposotionRoot">
<HintPath>..\ref\Gamecraft_Data\Managed\ConsoleBlockComposotionRoot.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\ConsoleBlockComposotionRoot.dll</HintPath>
</Reference>
<Reference Include="ConsoleCommand">
<HintPath>..\ref\Gamecraft_Data\Managed\ConsoleCommand.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\ConsoleCommand.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.AudioBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.AudioBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.AudioBlocks.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.BlockCompositionRoot">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.BlockCompositionRoot.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.BlockCompositionRoot.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.BlockEntityFactory">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.BlockEntityFactory.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.BlockEntityFactory.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.DamagingSurfaceBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.DamagingSurfaceBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.DamagingSurfaceBlock.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Blocks.DestructionBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.DestructionBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.DestructionBlocks.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.ProjectileBlock">
<HintPath>..\ref\Gamecraft_Data\Managed\GameCraft.Blocks.ProjectileBlock.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\GameCraft.Blocks.ProjectileBlock.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Blocks.TextBlock.CompositionRoot">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.TextBlock.CompositionRoot.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.TextBlock.CompositionRoot.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.BlocksEntityDescriptors">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.BlocksEntityDescriptors.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.BlocksEntityDescriptors.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.Damage">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Damage.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Damage.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.ExplosionFragments">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.ExplosionFragments.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.ExplosionFragments.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.GraphicsSettings">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GraphicsSettings.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.GraphicsSettings.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.InventoryTimeRunning">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.InventoryTimeRunning.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.InventoryTimeRunning.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.JointBlocks">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.JointBlocks.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.JointBlocks.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Music">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Music.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Music.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.PerformanceWarnings">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.PerformanceWarnings.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.PerformanceWarnings.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.PickupBlck">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.PickupBlck.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.PickupBlck.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.PickupsCommon">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.PickupsCommon.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.PickupsCommon.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.PopupMessage">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.PopupMessage.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.PopupMessage.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.Projectiles">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Projectiles.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.Projectiles.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.VisualEffects.Decals">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.VisualEffects.Decals.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.VisualEffects.Decals.dll</HintPath>
</Reference>
<Reference Include="Gamecraft.VisualEffects">
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.VisualEffects.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Gamecraft.VisualEffects.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.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="RobocraftECS">
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftECS.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\RobocraftECS.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="SpawningPointCompositionRoot">
<HintPath>..\ref\Gamecraft_Data\Managed\SpawningPointCompositionRoot.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\SpawningPointCompositionRoot.dll</HintPath>
</Reference>
<Reference Include="SpecializedDescriptors">
<HintPath>..\ref\Gamecraft_Data\Managed\SpecializedDescriptors.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\SpecializedDescriptors.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">
<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="UltimateDecals">
<HintPath>..\ref\Gamecraft_Data\Managed\UltimateDecals.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\UltimateDecals.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.Animation.Curves">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.dll</HintPath>
</Reference>
<Reference Include="Unity.Animation.Curves.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.Hybrid.dll</HintPath>
</Reference>
<Reference Include="Unity.Animation.DefaultGraphPipeline">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.dll</HintPath>
</Reference>
<Reference Include="Unity.Animation.DefaultGraphPipeline.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.Hybrid.dll</HintPath>
</Reference>
<Reference Include="Unity.Animation">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Animation.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Animation.dll</HintPath>
</Reference>
<Reference Include="Unity.Animation.Graph">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Animation.Graph.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Graph.dll</HintPath>
</Reference>
<Reference Include="Unity.Animation.Hybrid">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Animation.Hybrid.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Hybrid.dll</HintPath>
</Reference>
<Reference Include="Unity.Build.SlimPlayerRuntime">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Build.SlimPlayerRuntime.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.Build.SlimPlayerRuntime.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.DataFlowGraph">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.DataFlowGraph.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.DataFlowGraph.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.MemoryProfiler">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.MemoryProfiler.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.MemoryProfiler.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="Unity.VisualEffectGraph.Runtime">
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.VisualEffectGraph.Runtime.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Unity.VisualEffectGraph.Runtime.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>
<Reference Include="Accessibility">
<HintPath>..\ref\Gamecraft_Data\Managed\Accessibility.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Accessibility.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="JWT">
<HintPath>..\ref\Gamecraft_Data\Managed\JWT.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\JWT.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="netstandard">
<HintPath>..\ref\Gamecraft_Data\Managed\netstandard.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\netstandard.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="Novell.Directory.Ldap">
<HintPath>..\ref\Gamecraft_Data\Managed\Novell.Directory.Ldap.dll</HintPath>
<HintPath>..\..\ref\Gamecraft_Data\Managed\Novell.Directory.Ldap.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="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="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>
</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,146 @@
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(LocalInputEntityStruct input, uint playerID = uint.MaxValue)
{
if (playerID == uint.MaxValue)
{
playerID = inputEngine.GetLocalPlayerID();
}
inputEngine.SendCustomInput(input, playerID);
}
public static LocalInputEntityStruct 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="commandLine">Toggle the command line?</param>
/// <param name="escape">Open escape menu?</param>
/// <param name="enter">Page return?</param>
/// <param name="debug">Toggle debug display?</param>
/// <param name="next">Select next?</param>
/// <param name="previous">Select previous?</param>
/// <param name="tab">Tab?</param>
/// <param name="colour">Toggle to hotbar colour mode?</param>
/// <param name="hotbarPage">Select the hotbar page by number?</param>
/// <param name="quickSave">Quicksave?</param>
/// <param name="paste">Paste?</param>
public static void GuiInput(uint playerID = uint.MaxValue, int hotbar = -1, bool commandLine = 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 LocalInputEntityStruct 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;
default: break;
}
if (commandLine) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleCommandLine;
if (escape) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Escape;
if (enter) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Return;
if (debug) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleDebugDisplay;
if (next) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectNext;
if (previous) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectPrev;
if (tab) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Tab;
switch (hotbarPage)
{
case 1: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage1; break;
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 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 LocalInputEntityStruct currentInput = ref inputEngine.GetInputRef(playerID);
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
// set inputs
if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningMode;
if (forward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Forward;
if (backward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Backward;
if (up) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Up;
if (down) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Down;
if (left) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Left;
if (right) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Right;
if (sprint) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Sprint;
if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode;
if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction;
if (primary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryAction;
if (secondary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryAction;
if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction;
if (primaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld;
if (secondaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld;
if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid;
if (ctrl) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CtrlAction;
if (toggleColourMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleColourMode;
if (scaleBlockUp) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp;
if (scaleBlockDown) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown;
if (rotateBlockClockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise;
if (rotateBlockCounterclockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise;
if (cutSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CutSelection;
if (copySelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CopySelection;
if (deleteSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.DeleteSelection;
}
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(LocalInputEntityStruct input, uint playerID, bool remote = false)
{
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
if (entitiesDB.Exists<LocalInputEntityStruct>(egid))
{
ref LocalInputEntityStruct ies = ref entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
ies = input;
return true;
}
else return false;
}
public LocalInputEntityStruct GetInput(uint playerID, bool remote = false)
{
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
if (entitiesDB.Exists<LocalInputEntityStruct>(egid))
{
return entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
}
else return default(LocalInputEntityStruct);
}
public ref LocalInputEntityStruct GetInputRef(uint playerID, bool remote = false)
{
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
return ref entitiesDB.QueryEntity<LocalInputEntityStruct>(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)
{
var inputs = entitiesDB.QueryEntities<LocalInputEntityStruct>(InputExclusiveGroups.LocalPlayers);
if (inputs.count == 0) return false;
for (int i = 0; i < inputs.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"), "ActivateSlotForCube", 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;
}
}
}

106
GamecraftModdingAPI/Main.cs Normal file
View file

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using GamecraftModdingAPI.Blocks;
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");
#pragma warning disable 0612,0618
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);
#pragma warning restore 0612,0618
// 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();
Wire.Init();
GameClient.Init();
AsyncUtils.Init();
GamecraftModdingAPI.App.Client.Init();
GamecraftModdingAPI.App.Game.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 HarmonyLib;
using TechbloxModdingAPI.Utility;
using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Persistence
namespace GamecraftModdingAPI.Persistence
{
//[HarmonyPatch] - TODO
[HarmonyPatch]
class DeserializeFromDiskEntitiesEnginePatch
{
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)
{
@ -26,7 +26,7 @@ namespace TechbloxModdingAPI.Persistence
SerializerManager.RegisterSerializers(SaveAndLoadCompositionRootPatch.currentEnginesRoot);
uint originalPos = ____serializationData.dataPos;
Logging.MetaDebugLog($"dataPos: {originalPos}");
BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out int count), ____serializationData.dataPos);
BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out uint count), ____serializationData.dataPos);
byte[] frameBuffer = new byte[frameStart.Length];
Logging.MetaDebugLog($"serial data count: {____serializationData.data.count} capacity: {____serializationData.data.capacity}");
int i = 0;

View file

@ -3,12 +3,12 @@
using Svelto.ECS;
using Svelto.ECS.Serialization;
using TechbloxModdingAPI.Utility;
using GamecraftModdingAPI.Utility;
namespace TechbloxModdingAPI.Persistence
namespace GamecraftModdingAPI.Persistence
{
/// <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>
public interface IEntitySerializer : IDeserializationFactory, IQueryingEntitiesEngine
{

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.ECS;
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
{
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)
{
@ -25,24 +26,24 @@ namespace TechbloxModdingAPI.Persistence
Logging.MetaDebugLog("Skipping component serialization: no serializers registered!");
return;
}
serializationData.data.IncreaseCapacityBy((uint)frameStart.Length);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int buffLen), serializationData.dataPos);
serializationData.data.ExpandBy((uint)frameStart.Length);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint buffLen), serializationData.dataPos);
uint originalPos = serializationData.dataPos;
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++)
{
bbw.Write(frameStart[i]);
}
Logging.MetaDebugLog($"dataPos (after frame start): {bbw.Position}");
serializationData.data.IncreaseCapacityBy(4u);
serializationData.data.ExpandBy(4u);
bbw.Write((uint)SerializerManager.GetSerializersCount());
string[] serializerKeys = SerializerManager.GetSerializerNames();
for (uint c = 0; c < serializerKeys.Length; c++)
{
Logging.MetaDebugLog($"dataPos (loop start): {bbw.Position}");
// write component info
serializationData.data.IncreaseCapacityBy(4u + (uint)serializerKeys[c].Length);
serializationData.data.ExpandBy(4u + (uint)serializerKeys[c].Length);
bbw.Write((uint)serializerKeys[c].Length);
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
byte[] nameBytes = Encoding.UTF8.GetBytes(serializerKeys[c]);
@ -51,7 +52,7 @@ namespace TechbloxModdingAPI.Persistence
bbw.Write(nameBytes[i]);
}
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
serializationData.data.IncreaseCapacityBy(4u);
serializationData.data.ExpandBy(4u);
serializationData.dataPos = bbw.Position + 4u;
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
Logging.MetaDebugLog($"dataPos (appears to be): {serializationData.dataPos}");
@ -73,8 +74,8 @@ namespace TechbloxModdingAPI.Persistence
}
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.Serialization;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Persistence
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Persistence
{
/// <summary>
/// Keeps track of 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>
public static class SerializerManager
{
@ -58,7 +59,7 @@ namespace TechbloxModdingAPI.Persistence
return _serializers.Count;
}
internal static void RegisterSerializers(EnginesRoot enginesRoot)
public static void RegisterSerializers(EnginesRoot enginesRoot)
{
_lastEnginesRoot = enginesRoot;
IEntitySerialization ies = enginesRoot.GenerateEntitySerializer();

View file

@ -5,7 +5,7 @@ using Svelto.ECS.Serialization;
using RobocraftX.Common;
namespace TechbloxModdingAPI.Persistence
namespace GamecraftModdingAPI.Persistence
{
/// <summary>
/// Simple entity serializer sufficient for simple entity components.
@ -23,16 +23,16 @@ namespace TechbloxModdingAPI.Persistence
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, IEntityFactory factory, bool enginesRootIsDeserializationOnly)
{
EntityInitializer esi = factory.BuildEntity<Descriptor>(egid);
EntityComponentInitializer esi = factory.BuildEntity<Descriptor>(egid);
entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref esi, serializationType);
return esi;
}
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();
serializationData.dataPos = bbr.Position;
for (uint i = 0; i < entityCount; i++)
@ -46,8 +46,8 @@ namespace TechbloxModdingAPI.Persistence
public bool Serialize(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer)
{
serializationData.data.IncreaseCapacityBy(4u);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int count), serializationData.dataPos);
serializationData.data.ExpandBy(4u);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos);
EGID[] toSerialize = getEntitiesToSerialize(entitiesDB);
bbw.Write((uint)toSerialize.Length);
serializationData.dataPos = bbw.Position;

View file

@ -0,0 +1,421 @@
using System;
using Unity.Mathematics;
using RobocraftX.Common;
using RobocraftX.Common.Players;
using Svelto.ECS;
using GamecraftModdingAPI.Players;
using GamecraftModdingAPI.Blocks;
namespace GamecraftModdingAPI
{
/// <summary>
/// An in-game player character. Any Leo you see is a player.
/// </summary>
public class Player : IEquatable<Player>, IEquatable<EGID>
{
// 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>
/// The amount of Players in the current game.
/// </summary>
/// <returns>The count.</returns>
public static uint Count()
{
return (uint) playerEngine.GetAllPlayerCount();
}
/// <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)
{
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; }
/// <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 float3 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;
}
}
/// <summary>
/// The player's initial health when entering Simulation (aka Time Running) mode.
/// </summary>
/// <value>The initial health.</value>
public float InitialHealth
{
get => playerEngine.GetInitialHealth(Id);
set
{
playerEngine.SetInitialHealth(Id, value);
}
}
/// <summary>
/// The player's current health in Simulation (aka Time Running) mode.
/// </summary>
/// <value>The current health.</value>
public float CurrentHealth
{
get => playerEngine.GetCurrentHealth(Id);
set
{
playerEngine.DamagePlayer(Id, CurrentHealth - value);
}
}
/// <summary>
/// Whether this <see cref="T:GamecraftModdingAPI.Player"/> is damageable.
/// </summary>
/// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
public bool Damageable
{
get => playerEngine.GetDamageable(Id);
set
{
playerEngine.SetDamageable(Id, value);
}
}
/// <summary>
/// The player's lives when initially entering Simulation (aka Time Running) mode.
/// </summary>
/// <value>The initial lives.</value>
public uint InitialLives
{
get => playerEngine.GetInitialLives(Id);
set => playerEngine.SetInitialLives(Id, value);
}
/// <summary>
/// The player's current lives in Simulation (aka Time Running) mode.
/// </summary>
/// <value>The current lives.</value>
public uint CurrentLives
{
get => playerEngine.GetCurrentLives(Id);
set => playerEngine.SetCurrentLives(Id, value);
}
/// <summary>
/// Whether the Game Over screen is displayed for the player.
/// </summary>
/// <value><c>true</c> if game over; otherwise, <c>false</c>.</value>
public bool GameOver
{
get => playerEngine.GetGameOverScreen(Id);
}
/// <summary>
/// Whether the player is dead.
/// If <c>true</c>, hopefully it was quick.
/// </summary>
/// <value><c>true</c> if dead; otherwise, <c>false</c>.</value>
public bool Dead
{
get => playerEngine.IsDead(Id);
}
/// <summary>
/// The player's selected block ID in their hand.
/// </summary>
/// <value>The selected block.</value>
public BlockIDs SelectedBlock
{
get
{
return (BlockIDs)playerEngine.GetSelectedBlock(Id);
}
}
/// <summary>
/// The player's selected block color in their hand.
/// </summary>
/// <value>The selected block's color.</value>
public BlockColor SelectedColor
{
get
{
return new BlockColor(playerEngine.GetSelectedColor(Id));
}
}
/// <summary>
/// The player's selected block colour in their hand.
/// </summary>
/// <value>The selected block's colour.</value>
public BlockColor SelectedColour
{
get
{
return new BlockColor(playerEngine.GetSelectedColor(Id));
}
}
// 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 in build mode.
/// </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.SIMULATION_BODIES_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;
}
/// <summary>
/// Returns the blocks that are in the player's current selection.
/// </summary>
/// <returns>An array of blocks or an empty array</returns>
public Block[] GetSelectedBlocks()
{
return playerEngine.GetSelectedBlocks(Id);
}
public bool Equals(Player other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id;
}
public bool Equals(EGID other)
{
return Id == other.entityID && other.groupID == (Type == PlayerType.Local
? PlayersExclusiveGroups.LocalPlayers
: PlayersExclusiveGroups.RemotePlayers);
}
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((Player) obj);
}
public override int GetHashCode()
{
return (int) Id;
}
// internal methods
internal static void Init()
{
Utility.GameEngineManager.AddGameEngine(playerEngine);
}
}
}

View file

@ -0,0 +1,479 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using RobocraftX.Character;
using RobocraftX.Character.Movement;
using RobocraftX.Common.Players;
using RobocraftX.Common.Input;
using RobocraftX.CR.MachineEditing.BoxSelect;
using RobocraftX.Physics;
using RobocraftX.Blocks.Ghost;
using RobocraftX.Character.Camera;
using RobocraftX.Character.Factories;
using Gamecraft.GUI.HUDFeedbackBlocks;
using Svelto.ECS;
using Unity.Mathematics;
using Unity.Physics;
using UnityEngine;
using GamecraftModdingAPI.Engines;
using HarmonyLib;
using RobocraftX.Common;
using Svelto.ECS.DataStructures;
namespace GamecraftModdingAPI.Players
{
internal class PlayerEngine : IApiEngine, IFactoryEngine
{
public string Name { get; } = "GamecraftModdingAPIPlayerGameEngine";
public EntitiesDB entitiesDB { set; private get; }
public bool isRemovable => false;
public IEntityFactory Factory { set; private get; }
private bool isReady = false;
public void Dispose()
{
isReady = false;
}
public void Ready()
{
isReady = true;
}
public uint GetLocalPlayer()
{
if (!isReady) return uint.MaxValue;
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
if (localPlayers.count > 0)
{
return localPlayers[0].ID.entityID;
}
return uint.MaxValue;
}
public uint GetRemotePlayer()
{
if (!isReady) return uint.MaxValue;
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
if (localPlayers.count > 0)
{
return localPlayers[0].ID.entityID;
}
return uint.MaxValue;
}
public long GetAllPlayerCount()
{
if (entitiesDB == null) return 0;
long count = 0;
foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers)
{
count += entitiesDB.Count<PlayerIDStruct>(eg);
}
return count;
}
public long GetLocalPlayerCount()
{
if (entitiesDB == null) return 0;
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
}
public long GetRemotePlayerCount()
{
if (entitiesDB == null) return 0;
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
}
public bool ExistsById(uint playerId)
{
if (entitiesDB == null) return false;
return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
|| entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.RemotePlayers);
}
public float3 GetLocation(uint playerId)
{
if (entitiesDB == null) return float3.zero;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
return rbes.position;
}
return float3.zero;
}
public bool SetLocation(uint playerId, float3 location, bool exitSeat = true)
{
if (entitiesDB == null) return false;
var characterGroups = CharacterExclusiveGroups.AllCharacters;
for (int i = 0; i < characterGroups.count; 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 float3 GetRotation(uint playerId)
{
if (entitiesDB == null) return float3.zero;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
return ((Quaternion) rbes.rotation).eulerAngles;
}
return default(float3);
}
public bool SetRotation(uint playerId, float3 value)
{
if (entitiesDB == null) return false;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
Quaternion q = rbes.rotation;
q.eulerAngles = value;
rbes.rotation = q;
return true;
}
return false;
}
public float3 GetLinearVelocity(uint playerId)
{
if (entitiesDB == null) return float3.zero;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
return rbes.velocity;
}
return float3.zero;
}
public bool SetLinearVelocity(uint playerId, float3 value)
{
if (entitiesDB == null) return false;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
rbes.velocity = value;
return true;
}
return false;
}
public float3 GetAngularVelocity(uint playerId)
{
if (entitiesDB == null) return float3.zero;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
return rbes.angularVelocity;
}
return float3.zero;
}
public bool SetAngularVelocity(uint playerId, float3 value)
{
if (entitiesDB == null) return false;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
rbes.angularVelocity = value;
return true;
}
return false;
}
public PhysicsMass GetMass(uint playerId)
{
if (entitiesDB == null) return default(PhysicsMass);
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
return rbes.physicsMass;
}
return default(PhysicsMass);
}
public bool SetInverseMass(uint playerId, float inverseMass)
{
if (entitiesDB == null) return false;
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
if (exists)
{
rbes.physicsMass.InverseInertia = inverseMass;
return true;
}
return false;
}
public float? GetLastPingTime(uint playerId, PlayerType type)
{
if (entitiesDB == null) return null;
EGID egid = new EGID(playerId, PlayerGroupFromEnum(type));
if (entitiesDB.Exists<PlayerNetworkStatsEntityStruct>(egid))
{
return entitiesDB.QueryEntity<PlayerNetworkStatsEntityStruct>(egid).lastPingTimeSinceLevelLoad;
}
return null;
}
public float GetInitialHealth(uint playerId)
{
if (entitiesDB == null) return 0;
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
if (exists)
{
return c.initialHealth;
}
return -1f;
}
public bool SetInitialHealth(uint playerId, float val)
{
if (entitiesDB == null) return false;
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
if (exists)
{
c.initialHealth = val;
return true;
}
return false;
}
public float GetCurrentHealth(uint playerId)
{
if (entitiesDB == null) return 0;
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
if (exists)
{
return c.currentHealth;
}
return -1f;
}
public bool SetCurrentHealth(uint playerId, float val)
{
if (entitiesDB == null) return false;
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
if (exists)
{
c.currentHealth = val;
return true;
}
return false;
}
public bool DamagePlayer(uint playerId, float amount)
{
if (entitiesDB == null) return false;
return SetCurrentHealth(playerId, GetCurrentHealth(playerId) - amount);
}
public bool GetDamageable(uint playerId)
{
if (entitiesDB == null) return false;
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
if (exists)
{
return c.canTakeDamageStat;
}
return false;
}
public bool SetDamageable(uint playerId, bool val)
{
if (entitiesDB == null) return false;
ref var ches = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
if (exists)
{
ches.canTakeDamage = val;
ches.canTakeDamage = val;
return true;
}
return false;
}
public uint GetInitialLives(uint playerId)
{
if (entitiesDB == null) return 0;
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
if (exists)
{
return c.initialLives;
}
return uint.MaxValue;
}
public bool SetInitialLives(uint playerId, uint val)
{
if (entitiesDB == null) return false;
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
if (exists)
{
c.initialLives = val;
return true;
}
return false;
}
public uint GetCurrentLives(uint playerId)
{
if (entitiesDB == null) return 0;
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
if (exists)
{
return c.currentLives;
}
return uint.MaxValue;
}
public bool SetCurrentLives(uint playerId, uint val)
{
if (entitiesDB == null) return false;
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
if (exists)
{
c.currentLives = val;
return true;
}
return false;
}
public bool GetGameOverScreen(uint playerId)
{
if (entitiesDB == null) return false;
ref HudActivatedBlocksEntityStruct habes = ref entitiesDB.QueryEntity<HudActivatedBlocksEntityStruct>(HUDFeedbackBlocksGUIExclusiveGroups.GameOverHudEgid);
NativeDynamicArrayCast<EGID> nativeDynamicArrayCast = new NativeDynamicArrayCast<EGID>(habes.activatedBlocksOrdered);
return nativeDynamicArrayCast.count > 0;
}
public bool IsDead(uint playerId)
{
if (entitiesDB == null) return true;
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
}
public int GetSelectedBlock(uint playerId)
{
if (entitiesDB == null) return 0;
ref var c = ref GetCharacterStruct<EquippedPartStruct>(playerId, out bool exists);
if (exists)
{
return c.SelectedDBPartID;
}
return ushort.MaxValue;
}
public byte GetSelectedColor(uint playerId)
{
if (entitiesDB == null) return 0;
ref var c = ref GetCharacterStruct<EquippedColourStruct>(playerId, out bool exists);
if (exists)
{
return c.indexInPalette;
}
return 255;
}
// reusable methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ExclusiveGroup PlayerGroupFromEnum(PlayerType type)
{
return type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T GetCharacterStruct<T>(uint playerId, out bool exists) where T : unmanaged, IEntityComponent
{
var characterGroups = CharacterExclusiveGroups.AllCharacters;
for (int i = 0; i < characterGroups.count; i++)
{
EGID egid = new EGID(playerId, characterGroups[i]);
if (entitiesDB.Exists<T>(egid))
{
exists = true;
return ref entitiesDB.QueryEntity<T>(egid);
}
}
exists = false;
T[] arr = new T[1];
return ref arr[0]; //Return default value
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetPlayerStruct<T>(uint playerId, out T s) where T : unmanaged, IEntityComponent
{
var playerGroups = PlayersExclusiveGroups.AllPlayers;
for (int i = 0; i < playerGroups.count; 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,
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
: maxDistance;
if (rayCast.hit && rayCast.distance <= distance)
return rayCast.hitEgid;
return null;
}
public unsafe Block[] GetSelectedBlocks(uint playerid)
{
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
return new Block[0];
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
if (!state.active) return new Block[0];
var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
var ret = new Block[blocks.count];
for (int j = 0; j < blocks.count; j++)
{
var egid = pointer[j];
ret[j] = new Block(egid);
}
return ret;
}
}
}

View file

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

View file

@ -0,0 +1,42 @@
using System;
using Unity.Mathematics;
using GamecraftModdingAPI;
using GamecraftModdingAPI.Tests;
namespace GamecraftModdingAPI.Players
{
#if TEST
/// <summary>
/// Player test cases. Not accessible in release versions.
/// </summary>
[APITestClass]
public static class PlayerTests
{
[APITestCase(TestType.EditMode)]
public static void ExistsTest()
{
if (!Assert.Equal(Player.Exists(PlayerType.Local), true, "Local player does not exist.", "Local player detected.")) return;
Assert.Equal(Player.Count(), 1u, "Player.Count() is not one, possibly because it failed silently.", "Player count is one for single player game.");
}
[APITestCase(TestType.EditMode)]
public static void PositionTest()
{
Player p = new Player(PlayerType.Local);
if (!Assert.Errorless(() => { p.Teleport(0, 0, 0, relative: false); }, "Player.Teleport(origin) errored: ", "Player teleported to origin successfully.")) return;
if (!Assert.CloseTo(p.Position, float3.zero, "Player is not close to origin despite being teleported there.", "Player.Position is at origin.")) return;
if (!Assert.Errorless(() => { p.Position = float3.zero + 1; }, "Player.Position = origin+1 errored: ", "Player moved to origin+1.")) return;
Assert.CloseTo(p.Position, float3.zero + 1, "Player is not close to origin+1 despite being teleported there.", "Player.Position is at origin+1.");
}
[APITestCase(TestType.Menu)]
public static void InvalidStateTest()
{
if (!Assert.Errorless(() => { Player.Count(); }, "Player.Count() errored in menu.", "Player.Count() succeeded in menu.")) return;
Assert.Equal(Player.Count(), 0u, "Player.Count() is not zero in menu.", "Player count is zero in menu as expected.");
}
}
#endif
}

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