Compare commits
7 commits
master
...
feature/re
Author | SHA1 | Date | |
---|---|---|---|
ef075d414a | |||
1c6d2bda89 | |||
bf08b61788 | |||
27218aeb8d | |||
8a52095263 | |||
9be1b5fdaf | |||
9a195215f9 |
83 changed files with 1507 additions and 1212 deletions
|
@ -8,7 +8,7 @@
|
||||||
<PackageProjectUrl>https://git.exmods.org/modtainers/GamecraftModdingAPI</PackageProjectUrl>
|
<PackageProjectUrl>https://git.exmods.org/modtainers/GamecraftModdingAPI</PackageProjectUrl>
|
||||||
<NeutralLanguage>en-CA</NeutralLanguage>
|
<NeutralLanguage>en-CA</NeutralLanguage>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>9</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
|
67
CodeGenerator/ECSAnalyzer.cs
Normal file
67
CodeGenerator/ECSAnalyzer.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.CodeDom;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using Gamecraft.Tweaks;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public static class ECSAnalyzer
|
||||||
|
{
|
||||||
|
public static ECSClassInfo AnalyzeEntityDescriptor(Type entityDescriptorType)
|
||||||
|
{
|
||||||
|
// TODO: Add support for creating/deleting entities (getting an up to date server/client engines root)
|
||||||
|
var templateType = typeof(EntityDescriptorTemplate<>).MakeGenericType(entityDescriptorType);
|
||||||
|
var templateDescriptor = AccessTools.Property(templateType, "descriptor");
|
||||||
|
var getDescriptorExpr = Expression.MakeMemberAccess(null, templateDescriptor ?? throw new InvalidOperationException());
|
||||||
|
var getTemplateDescriptorExpr =
|
||||||
|
Expression.Lambda<Func<IEntityDescriptor>>(getDescriptorExpr);
|
||||||
|
var getTemplateDescriptor = getTemplateDescriptorExpr.Compile();
|
||||||
|
// TODO: Crashes on constructing the descriptor type. Maybe move the analysis part to a mod.
|
||||||
|
var builders = getTemplateDescriptor().componentsToBuild;
|
||||||
|
return new ECSClassInfo
|
||||||
|
{
|
||||||
|
Name = entityDescriptorType.Name.Replace("EntityComponent", "").Replace("EntityStruct", ""),
|
||||||
|
Properties = builders.Select(builder => builder.GetEntityComponentType()).SelectMany(AnalyzeFields).ToArray()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ECSPropertyInfo[] AnalyzeFields(Type componentType)
|
||||||
|
{
|
||||||
|
bool useReflection = componentType.IsNotPublic;
|
||||||
|
var result = new List<ECSPropertyInfo>();
|
||||||
|
foreach (var field in componentType.GetFields())
|
||||||
|
{
|
||||||
|
var attr = field.GetCustomAttribute<TweakableStatAttribute>();
|
||||||
|
string propName = field.Name;
|
||||||
|
if (attr != null)
|
||||||
|
propName = attr.propertyName;
|
||||||
|
|
||||||
|
propName = char.ToUpper(propName[0]) + propName[1..];
|
||||||
|
if (useReflection)
|
||||||
|
{
|
||||||
|
result.Add(new ECSReflectedPropertyInfo
|
||||||
|
{
|
||||||
|
Name = propName,
|
||||||
|
Type = field.FieldType,
|
||||||
|
OriginalClassName = componentType.FullName,
|
||||||
|
ComponentType = componentType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(new ECSPropertyInfo
|
||||||
|
{
|
||||||
|
Name = propName,
|
||||||
|
Type = field.FieldType,
|
||||||
|
ComponentType = componentType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
129
CodeGenerator/ECSClassGenerator.cs
Normal file
129
CodeGenerator/ECSClassGenerator.cs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
using System;
|
||||||
|
using System.CodeDom;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public static class ECSClassGenerator
|
||||||
|
{
|
||||||
|
public static void Generate(Type entityDescriptorType)
|
||||||
|
{
|
||||||
|
var info = ECSAnalyzer.AnalyzeEntityDescriptor(entityDescriptorType);
|
||||||
|
|
||||||
|
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(info.Name);
|
||||||
|
cl.BaseTypes.Add(new CodeTypeReference("SignalingBlock"));
|
||||||
|
cl.Members.Add(new CodeConstructor
|
||||||
|
{
|
||||||
|
Parameters = {new CodeParameterDeclarationExpression("EGID", "egid")},
|
||||||
|
Comments =
|
||||||
|
{
|
||||||
|
_start,
|
||||||
|
new CodeCommentStatement($"Constructs a(n) {info.Name} object representing an existing block.", true),
|
||||||
|
_end
|
||||||
|
},
|
||||||
|
BaseConstructorArgs = {new CodeVariableReferenceExpression("egid")},
|
||||||
|
Attributes = MemberAttributes.Public | MemberAttributes.Final
|
||||||
|
});
|
||||||
|
foreach (var propertyInfo in info.Properties)
|
||||||
|
{
|
||||||
|
if (propertyInfo is ECSReflectedPropertyInfo reflectedPropertyInfo)
|
||||||
|
GenerateReflectedProperty(reflectedPropertyInfo, cl);
|
||||||
|
else
|
||||||
|
GenerateProperty(propertyInfo, cl);
|
||||||
|
}
|
||||||
|
ns.Types.Add(cl);
|
||||||
|
codeUnit.Namespaces.Add(ns);
|
||||||
|
|
||||||
|
var provider = CodeDomProvider.CreateProvider("CSharp");
|
||||||
|
var path = $"../../../../TechbloxModdingAPI/Blocks/{info.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 void GenerateProperty(ECSPropertyInfo info, CodeTypeDeclaration cl)
|
||||||
|
{
|
||||||
|
var getStruct = new CodeMethodInvokeExpression(
|
||||||
|
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||||
|
"GetBlockInfo", new CodeTypeReference(info.ComponentType)),
|
||||||
|
new CodeThisReferenceExpression());
|
||||||
|
CodeExpression structFieldReference = new CodeFieldReferenceExpression(getStruct, info.Name);
|
||||||
|
cl.Members.Add(new CodeMemberProperty
|
||||||
|
{
|
||||||
|
Name = info.Name,
|
||||||
|
HasGet = true,
|
||||||
|
HasSet = true,
|
||||||
|
GetStatements =
|
||||||
|
{
|
||||||
|
new CodeMethodReturnStatement(structFieldReference)
|
||||||
|
},
|
||||||
|
SetStatements =
|
||||||
|
{
|
||||||
|
new CodeAssignStatement(structFieldReference, new CodePropertySetValueReferenceExpression())
|
||||||
|
},
|
||||||
|
Type = new CodeTypeReference(info.Type),
|
||||||
|
Attributes = MemberAttributes.Public | MemberAttributes.Final,
|
||||||
|
Comments =
|
||||||
|
{
|
||||||
|
_start,
|
||||||
|
new CodeCommentStatement($"Gets or sets the {info.ComponentType.Name}'s {info.Name} property." +
|
||||||
|
" May not be saved.", // TODO: Doesn't know if tweakable stat
|
||||||
|
true),
|
||||||
|
_end
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateReflectedProperty(ECSReflectedPropertyInfo info, CodeTypeDeclaration cl)
|
||||||
|
{
|
||||||
|
var reflectedType = new CodeSnippetExpression($"HarmonyLib.AccessTools.TypeByName(\"{info.OriginalClassName}\")");
|
||||||
|
|
||||||
|
CodeExpression reflectedGet = new CodeCastExpression(info.Type, new CodeMethodInvokeExpression(
|
||||||
|
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||||
|
"GetBlockInfo"),
|
||||||
|
new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(info.Name)));
|
||||||
|
CodeExpression reflectedSet = new CodeMethodInvokeExpression(
|
||||||
|
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||||
|
"SetBlockInfo"),
|
||||||
|
new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(info.Name),
|
||||||
|
new CodePropertySetValueReferenceExpression());
|
||||||
|
cl.Members.Add(new CodeMemberProperty
|
||||||
|
{
|
||||||
|
Name = info.Name,
|
||||||
|
HasGet = true,
|
||||||
|
HasSet = true,
|
||||||
|
GetStatements =
|
||||||
|
{
|
||||||
|
new CodeMethodReturnStatement(reflectedGet)
|
||||||
|
},
|
||||||
|
SetStatements =
|
||||||
|
{
|
||||||
|
(CodeStatement)new CodeExpressionStatement(reflectedSet)
|
||||||
|
},
|
||||||
|
Type = new CodeTypeReference(info.Type),
|
||||||
|
Attributes = MemberAttributes.Public | MemberAttributes.Final,
|
||||||
|
Comments =
|
||||||
|
{
|
||||||
|
_start,
|
||||||
|
new CodeCommentStatement($"Gets or sets the {info.ComponentType.Name}'s {info.Name} property." +
|
||||||
|
" May not be saved.", // TODO: Doesn't know if tweakable stat
|
||||||
|
true),
|
||||||
|
_end
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly CodeCommentStatement _start = new("<summary>", true);
|
||||||
|
private static readonly CodeCommentStatement _end = new("</summary>", true);
|
||||||
|
}
|
||||||
|
}
|
10
CodeGenerator/ECSClassInfo.cs
Normal file
10
CodeGenerator/ECSClassInfo.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public class ECSClassInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public ECSPropertyInfo[] Properties { get; set; }
|
||||||
|
}
|
||||||
|
}
|
11
CodeGenerator/ECSPropertyInfo.cs
Normal file
11
CodeGenerator/ECSPropertyInfo.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public class ECSPropertyInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Type Type { get; set; }
|
||||||
|
public Type ComponentType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
7
CodeGenerator/ECSReflectedPropertyInfo.cs
Normal file
7
CodeGenerator/ECSReflectedPropertyInfo.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public class ECSReflectedPropertyInfo : ECSPropertyInfo
|
||||||
|
{
|
||||||
|
public string OriginalClassName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,8 @@ namespace CodeGenerator
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
GenerateBlockClasses();
|
//GenerateBlockClasses();
|
||||||
|
ECSClassGenerator.Generate(typeof(EngineBlockEntityDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateBlockClasses()
|
private static void GenerateBlockClasses()
|
||||||
|
|
|
@ -20,8 +20,8 @@ namespace TechbloxModdingAPI.App
|
||||||
Game.Exit += Assert.CallsBack<GameEventArgs>("GameExit");
|
Game.Exit += Assert.CallsBack<GameEventArgs>("GameExit");
|
||||||
Game.Simulate += Assert.CallsBack<GameEventArgs>("GameSimulate");
|
Game.Simulate += Assert.CallsBack<GameEventArgs>("GameSimulate");
|
||||||
Game.Edit += Assert.CallsBack<GameEventArgs>("GameEdit");
|
Game.Edit += Assert.CallsBack<GameEventArgs>("GameEdit");
|
||||||
Client.EnterMenu += Assert.CallsBack<MenuEventArgs>("MenuEnter");
|
/*Client.EnterMenu += Assert.CallsBack<MenuEventArgs>("MenuEnter");
|
||||||
Client.ExitMenu += Assert.CallsBack<MenuEventArgs>("MenuExit");
|
Client.ExitMenu += Assert.CallsBack<MenuEventArgs>("MenuExit");*/
|
||||||
}
|
}
|
||||||
|
|
||||||
[APITestCase(TestType.Game)]
|
[APITestCase(TestType.Game)]
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
using RobocraftX.Services;
|
|
||||||
using UnityEngine;
|
|
||||||
using RobocraftX.Common;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.App
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The Techblox application that is running this code right now.
|
|
||||||
/// </summary>
|
|
||||||
public class Client
|
|
||||||
{
|
|
||||||
public static Client Instance { get; } = new Client();
|
|
||||||
|
|
||||||
protected static Func<object> ErrorHandlerInstanceGetter;
|
|
||||||
|
|
||||||
protected static Action<object, Error> EnqueueError;
|
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An event that fire whenever the main menu is exited.
|
|
||||||
/// </summary>
|
|
||||||
public static event EventHandler<MenuEventArgs> ExitMenu
|
|
||||||
{
|
|
||||||
add => Game.menuEngine.ExitMenu += value;
|
|
||||||
remove => Game.menuEngine.ExitMenu -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Techblox build version string.
|
|
||||||
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The version.</value>
|
|
||||||
public string Version
|
|
||||||
{
|
|
||||||
get => Application.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unity version string.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The unity version.</value>
|
|
||||||
public string UnityVersion
|
|
||||||
{
|
|
||||||
get => Application.unityVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Game saves currently visible in the menu.
|
|
||||||
/// These take a second to completely populate after the EnterMenu event fires.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>My games.</value>
|
|
||||||
public Game[] MyGames
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!Game.menuEngine.IsInMenu) return Array.Empty<Game>();
|
|
||||||
return Game.menuEngine.GetMyGames();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether Techblox 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Open a popup which prompts the user to click a button.
|
|
||||||
/// This reuses Techblox'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)
|
|
||||||
{
|
|
||||||
// if the stuff wasn't mostly set to internal, this would be written as:
|
|
||||||
// RobocraftX.Services.ErrorHandler.Instance.EqueueError(error);
|
|
||||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
|
||||||
EnqueueError(errorHandlerInstance, popup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CloseCurrentPrompt()
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
.MakeGenericMethod(errorHandler)
|
|
||||||
.Invoke(null, new object[0]);
|
|
||||||
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
|
|
||||||
.MakeGenericMethod(errorHandler, errorHandle)
|
|
||||||
.Invoke(null, new object[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating delegates once is faster than reflection every time
|
|
||||||
// Admittedly, this way is more difficult to code and less readable
|
|
||||||
private static Func<object> GenInstanceGetter<T>()
|
|
||||||
{
|
|
||||||
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
|
|
||||||
MethodInfo instance = AccessTools.PropertyGetter(errorHandler, "Instance");
|
|
||||||
Func<T> getterSimple = (Func<T>) Delegate.CreateDelegate(typeof(Func<T>), null, instance);
|
|
||||||
Func<object> getterCasted = () => (object) getterSimple();
|
|
||||||
return getterCasted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Action<object, Error> GenEnqueueError<T, TRes>()
|
|
||||||
{
|
|
||||||
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
|
|
||||||
MethodInfo enqueueError = AccessTools.Method(errorHandler, "EnqueueError");
|
|
||||||
Func<T, Error, TRes> enqueueSimple =
|
|
||||||
(Func<T, Error, TRes>) Delegate.CreateDelegate(typeof(Func<T, Error, TRes>), enqueueError);
|
|
||||||
Action<object, Error> enqueueCasted =
|
|
||||||
(object instance, Error error) => { enqueueSimple((T) instance, error); };
|
|
||||||
return enqueueCasted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
|
|
||||||
|
|
||||||
private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
|
||||||
using RobocraftX.Services;
|
using RobocraftX.Services;
|
||||||
|
using TechbloxModdingAPI.Client.App;
|
||||||
using TechbloxModdingAPI.Tests;
|
using TechbloxModdingAPI.Tests;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.App
|
namespace TechbloxModdingAPI.App
|
||||||
|
@ -42,25 +42,25 @@ namespace TechbloxModdingAPI.App
|
||||||
[APITestCase(TestType.Menu)]
|
[APITestCase(TestType.Menu)]
|
||||||
public static void TestPopUp2()
|
public static void TestPopUp2()
|
||||||
{
|
{
|
||||||
Client.Instance.PromptUser(popup2);
|
Popup.PromptUser(popup2);
|
||||||
}
|
}
|
||||||
|
|
||||||
[APITestCase(TestType.Menu)]
|
[APITestCase(TestType.Menu)]
|
||||||
public static void TestPopUp1()
|
public static void TestPopUp1()
|
||||||
{
|
{
|
||||||
Client.Instance.PromptUser(popup1);
|
Popup.PromptUser(popup1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[APITestCase(TestType.Menu)]
|
[APITestCase(TestType.Menu)]
|
||||||
public static void TestPopUpClose1()
|
public static void TestPopUpClose1()
|
||||||
{
|
{
|
||||||
Client.Instance.CloseCurrentPrompt();
|
Popup.CloseCurrentPrompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
[APITestCase(TestType.Menu)]
|
[APITestCase(TestType.Menu)]
|
||||||
public static void TestPopUpClose2()
|
public static void TestPopUpClose2()
|
||||||
{
|
{
|
||||||
Client.Instance.CloseCurrentPrompt();
|
Popup.CloseCurrentPrompt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Svelto.ECS;
|
||||||
using Techblox.GameSelection;
|
using Techblox.GameSelection;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
@ -20,12 +21,12 @@ namespace TechbloxModdingAPI.App
|
||||||
public class Game
|
public class Game
|
||||||
{
|
{
|
||||||
// extensible engines
|
// extensible engines
|
||||||
protected static GameGameEngine gameEngine = new GameGameEngine();
|
protected static GameGameEngine gameEngine = new();
|
||||||
protected internal static GameMenuEngine menuEngine = new GameMenuEngine();
|
protected internal static GameMenuEngine menuEngine = new();
|
||||||
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
|
protected static DebugInterfaceEngine debugOverlayEngine = new();
|
||||||
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
|
protected static GameBuildSimEventEngine buildSimEventEngine = new();
|
||||||
|
|
||||||
private List<string> debugIds = new List<string>();
|
private List<string> debugIds = new();
|
||||||
|
|
||||||
private bool menuMode = true;
|
private bool menuMode = true;
|
||||||
private bool hasId = false;
|
private bool hasId = false;
|
||||||
|
@ -481,10 +482,10 @@ namespace TechbloxModdingAPI.App
|
||||||
|
|
||||||
internal static void Init()
|
internal static void Init()
|
||||||
{
|
{
|
||||||
GameEngineManager.AddGameEngine(gameEngine);
|
EngineManager.AddEngine(gameEngine, ApiEngineType.Build, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
GameEngineManager.AddGameEngine(debugOverlayEngine);
|
EngineManager.AddEngine(debugOverlayEngine, ApiEngineType.Build, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
GameEngineManager.AddGameEngine(buildSimEventEngine);
|
EngineManager.AddEngine(buildSimEventEngine, ApiEngineType.Build, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
MenuEngineManager.AddMenuEngine(menuEngine);
|
EngineManager.AddEngine(menuEngine, ApiEngineType.Menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
using System;
|
using RobocraftX.StateSync;
|
||||||
|
|
||||||
using RobocraftX.Common;
|
|
||||||
using RobocraftX.StateSync;
|
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using Unity.Jobs;
|
using Unity.Jobs;
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.App
|
namespace TechbloxModdingAPI.App
|
||||||
|
|
|
@ -16,7 +16,8 @@ using Techblox.Environment.Transition;
|
||||||
using Techblox.GameSelection;
|
using Techblox.GameSelection;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Common;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Input;
|
using TechbloxModdingAPI.Input;
|
||||||
using TechbloxModdingAPI.Players;
|
using TechbloxModdingAPI.Players;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
|
@ -9,8 +9,7 @@ using RobocraftX.Multiplayer;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Experimental;
|
using Svelto.ECS.Experimental;
|
||||||
using Techblox.GameSelection;
|
using Techblox.GameSelection;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.App
|
namespace TechbloxModdingAPI.App
|
||||||
|
|
|
@ -8,14 +8,19 @@ using Svelto.ECS.EntityStructs;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using HarmonyLib;
|
|
||||||
using RobocraftX.PilotSeat;
|
using RobocraftX.PilotSeat;
|
||||||
using RobocraftX.Rendering;
|
using RobocraftX.Rendering;
|
||||||
using Techblox.BlockLabelsServer;
|
using Techblox.BlockLabelsServer;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Blocks.Engines;
|
using TechbloxModdingAPI.Blocks.Engines;
|
||||||
|
using TechbloxModdingAPI.Client.App;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
using TechbloxModdingAPI.Common.Traits;
|
||||||
using TechbloxModdingAPI.Tests;
|
using TechbloxModdingAPI.Tests;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using Unity.Transforms;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
|
@ -23,18 +28,16 @@ namespace TechbloxModdingAPI
|
||||||
/// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored.
|
/// 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 TechbloxModdingAPI.Blocks namespace.
|
/// For specific block type operations, use the specialised block classes in the TechbloxModdingAPI.Blocks namespace.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Block : EcsObjectBase, IEquatable<Block>, IEquatable<EGID>
|
public class Block : EcsObjectBase<BlockEntityDescriptor>, IHasPhysics, IEquatable<Block>, IEquatable<EGID>
|
||||||
{
|
{
|
||||||
protected static readonly PlacementEngine PlacementEngine = new PlacementEngine();
|
protected static readonly PlacementEngine PlacementEngine = new();
|
||||||
protected static readonly MovementEngine MovementEngine = new MovementEngine();
|
protected static readonly RemovalEngine RemovalEngine = new();
|
||||||
protected static readonly RotationEngine RotationEngine = new RotationEngine();
|
protected static readonly SignalEngine SignalEngine = new();
|
||||||
protected static readonly RemovalEngine RemovalEngine = new RemovalEngine();
|
protected static readonly BlockEventsEngine BlockEventsEngine = new();
|
||||||
protected static readonly SignalEngine SignalEngine = new SignalEngine();
|
protected static readonly ScalingEngine ScalingEngine = new();
|
||||||
protected static readonly BlockEventsEngine BlockEventsEngine = new BlockEventsEngine();
|
protected static readonly BlockCloneEngine BlockCloneEngine = new();
|
||||||
protected static readonly ScalingEngine ScalingEngine = new ScalingEngine();
|
|
||||||
protected static readonly BlockCloneEngine BlockCloneEngine = new BlockCloneEngine();
|
|
||||||
|
|
||||||
protected internal static readonly BlockEngine BlockEngine = new BlockEngine();
|
protected internal static readonly BlockEngine BlockEngine = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <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 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.
|
||||||
|
@ -48,12 +51,10 @@ namespace TechbloxModdingAPI
|
||||||
/// <returns>The placed block or null if failed</returns>
|
/// <returns>The placed block or null if failed</returns>
|
||||||
public static Block PlaceNew(BlockIDs block, float3 position, bool autoWire = false, Player player = null)
|
public static Block PlaceNew(BlockIDs block, float3 position, bool autoWire = false, Player player = null)
|
||||||
{
|
{
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
if (PlacementEngine.IsInGame && GameClient.IsBuildMode)
|
||||||
{
|
{
|
||||||
var initializer = PlacementEngine.PlaceBlock(block, position, player, autoWire);
|
var initializer = PlacementEngine.PlaceBlock(block, position, player, autoWire);
|
||||||
var egid = initializer.EGID;
|
var bl = New(initializer);
|
||||||
var bl = New(egid);
|
|
||||||
bl.InitData = initializer;
|
|
||||||
Placed += bl.OnPlacedInit;
|
Placed += bl.OnPlacedInit;
|
||||||
return bl;
|
return bl;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +97,7 @@ namespace TechbloxModdingAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<ExclusiveBuildGroup, (Func<EGID, Block> Constructor, Type Type)> GroupToConstructor =
|
private static readonly Dictionary<ExclusiveBuildGroup, (Func<EGID, Block> Constructor, Type Type)> GroupToConstructor =
|
||||||
new Dictionary<ExclusiveBuildGroup, (Func<EGID, Block>, Type)>
|
new()
|
||||||
{
|
{
|
||||||
{CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP, (id => new DampedSpring(id), typeof(DampedSpring))},
|
{CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP, (id => new DampedSpring(id), typeof(DampedSpring))},
|
||||||
{CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))},
|
{CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))},
|
||||||
|
@ -121,16 +122,27 @@ namespace TechbloxModdingAPI
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal static Block New(EGID egid, bool signaling = false)
|
internal static Block New(EGID egid, bool signaling = false)
|
||||||
{
|
{
|
||||||
if (egid == default) return null;
|
return New(egid, default, signaling);
|
||||||
if (GroupToConstructor.ContainsKey(egid.groupID))
|
|
||||||
{
|
|
||||||
var (constructor, type) = GroupToConstructor[egid.groupID];
|
|
||||||
return GetInstance(egid, constructor, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return signaling
|
private static Block New(EcsInitData initData, bool signaling = false)
|
||||||
? GetInstance(egid, e => new SignalingBlock(e))
|
{
|
||||||
: GetInstance(egid, e => new Block(e));
|
return New(initData.EGID, initData, signaling);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Block New(EGID egid, EcsInitData initData, bool signaling)
|
||||||
|
{
|
||||||
|
if (egid == default) return null;
|
||||||
|
Func<EGID, Block> constructor;
|
||||||
|
Type type = null;
|
||||||
|
if (GroupToConstructor.TryGetValue(egid.groupID, out var value))
|
||||||
|
(constructor, type) = value;
|
||||||
|
else
|
||||||
|
constructor = signaling ? e => new SignalingBlock(e) : e => new Block(e);
|
||||||
|
|
||||||
|
return initData != default
|
||||||
|
? GetInstanceNew(initData, constructor, type)
|
||||||
|
: GetInstanceExisting(egid, constructor, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block(EGID id) : base(id)
|
public Block(EGID id) : base(id)
|
||||||
|
@ -153,26 +165,6 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Places a new block in the world.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The block's type</param>
|
|
||||||
/// <param name="position">The block's position (a block is 0.2 wide in terms of position)</param>
|
|
||||||
/// <param name="autoWire">Whether the block should be auto-wired (if functional)</param>
|
|
||||||
/// <param name="player">The player who placed the block</param>
|
|
||||||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null)
|
|
||||||
: base(block =>
|
|
||||||
{
|
|
||||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode())
|
|
||||||
throw new BlockException("Blocks can only be placed in build mode.");
|
|
||||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
|
||||||
block.InitData = initializer;
|
|
||||||
Placed += ((Block)block).OnPlacedInit;
|
|
||||||
return initializer.EGID;
|
|
||||||
})
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private EGID copiedFrom;
|
private EGID copiedFrom;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -181,13 +173,21 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float3 Position
|
public float3 Position
|
||||||
{
|
{
|
||||||
get => MovementEngine.GetPosition(this);
|
get => GetComponent<PositionEntityStruct>().position;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
MovementEngine.MoveBlock(this, value);
|
// main (persistent) position
|
||||||
|
GetComponent<PositionEntityStruct>().position = value;
|
||||||
|
// placement grid position
|
||||||
|
GetComponent<GridRotationStruct>().position = value;
|
||||||
|
// rendered position
|
||||||
|
GetComponent<LocalTransformEntityStruct>().position = value;
|
||||||
|
this.UpdatePhysicsUECSComponent(new Translation { Value = value });
|
||||||
|
|
||||||
|
GetComponent<GridConnectionsEntityStruct>().areConnectionsAssigned = false;
|
||||||
if (blockGroup != null)
|
if (blockGroup != null)
|
||||||
blockGroup.PosAndRotCalculated = false;
|
blockGroup.PosAndRotCalculated = false;
|
||||||
BlockEngine.UpdateDisplayedBlock(Id);
|
BlockEngine.UpdateDisplayData(Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,13 +196,24 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float3 Rotation
|
public float3 Rotation
|
||||||
{
|
{
|
||||||
get => RotationEngine.GetRotation(this);
|
get => ((Quaternion)GetComponent<RotationEntityStruct>().rotation).eulerAngles;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
RotationEngine.RotateBlock(this, value);
|
// main (persistent) rotation
|
||||||
|
Quaternion newRotation = GetComponent<RotationEntityStruct>().rotation;
|
||||||
|
newRotation.eulerAngles = value;
|
||||||
|
GetComponent<RotationEntityStruct>().rotation = newRotation;
|
||||||
|
// placement grid rotation
|
||||||
|
GetComponent<GridRotationStruct>().rotation = newRotation;
|
||||||
|
// rendered rotation
|
||||||
|
GetComponent<LocalTransformEntityStruct>().rotation = newRotation;
|
||||||
|
this.UpdatePhysicsUECSComponent(new Rotation { Value = newRotation });
|
||||||
|
|
||||||
|
// They are assigned during machine processing anyway
|
||||||
|
GetComponent<GridConnectionsEntityStruct>().areConnectionsAssigned = false;
|
||||||
if (blockGroup != null)
|
if (blockGroup != null)
|
||||||
blockGroup.PosAndRotCalculated = false;
|
blockGroup.PosAndRotCalculated = false;
|
||||||
BlockEngine.UpdateDisplayedBlock(Id);
|
BlockEngine.UpdateDisplayData(Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,18 +223,19 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float3 Scale
|
public float3 Scale
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ScalingEntityStruct>(this).scale;
|
get => GetComponent<ScalingEntityStruct>().scale;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
int uscale = UniformScale;
|
int uscale = UniformScale;
|
||||||
if (value.x < 4e-5) value.x = uscale;
|
if (value.x < 4e-5) value.x = uscale;
|
||||||
if (value.y < 4e-5) value.y = uscale;
|
if (value.y < 4e-5) value.y = uscale;
|
||||||
if (value.z < 4e-5) value.z = uscale;
|
if (value.z < 4e-5) value.z = uscale;
|
||||||
BlockEngine.GetBlockInfo<ScalingEntityStruct>(this).scale = value;
|
GetComponent<ScalingEntityStruct>().scale = value;
|
||||||
|
GetComponent<GridConnectionsEntityStruct>().areConnectionsAssigned = false;
|
||||||
//BlockEngine.GetBlockInfo<GridScaleStruct>(this).gridScale = value - (int3) value + 1;
|
//BlockEngine.GetBlockInfo<GridScaleStruct>(this).gridScale = value - (int3) value + 1;
|
||||||
if (!Exists) return; //UpdateCollision needs the block to exist
|
if (!Exists) return; //UpdateCollision needs the block to exist
|
||||||
ScalingEngine.UpdateCollision(Id);
|
ScalingEngine.UpdateCollision(Id);
|
||||||
BlockEngine.UpdateDisplayedBlock(Id);
|
BlockEngine.UpdateDisplayData(Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,11 +245,12 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int UniformScale
|
public int UniformScale
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(this).scaleFactor;
|
get => GetComponent<UniformBlockScaleEntityStruct>().scaleFactor;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
//It appears that only the non-uniform scale has any visible effect so we'll set that as well
|
||||||
if (value < 1) value = 1;
|
if (value < 1) value = 1;
|
||||||
BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(this).scaleFactor = value;
|
GetComponent<UniformBlockScaleEntityStruct>().scaleFactor = value;
|
||||||
Scale = new float3(value, value, value);
|
Scale = new float3(value, value, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,12 +260,12 @@ namespace TechbloxModdingAPI
|
||||||
*/
|
*/
|
||||||
public bool Flipped
|
public bool Flipped
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ScalingEntityStruct>(this).scale.x < 0;
|
get => GetComponent<ScalingEntityStruct>().scale.x < 0;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref var st = ref BlockEngine.GetBlockInfo<ScalingEntityStruct>(this);
|
ref var st = ref GetComponent<ScalingEntityStruct>();
|
||||||
st.scale.x = math.abs(st.scale.x) * (value ? -1 : 1);
|
st.scale.x = math.abs(st.scale.x) * (value ? -1 : 1);
|
||||||
BlockEngine.UpdatePrefab(this, (byte) Material, value);
|
BlockEngine.UpdateDisplayedPrefab(this, (byte) Material, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +276,7 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var opt = BlockEngine.GetBlockInfoOptional<DBEntityStruct>(this);
|
var opt = GetComponentOptional<DBEntityStruct>();
|
||||||
return opt ? (BlockIDs) opt.Get().DBID : BlockIDs.Invalid;
|
return opt ? (BlockIDs) opt.Get().DBID : BlockIDs.Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,16 +288,17 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var opt = BlockEngine.GetBlockInfoOptional<ColourParameterEntityStruct>(this);
|
var opt = GetComponentOptional<ColourParameterEntityStruct>();
|
||||||
return new BlockColor(opt ? opt.Get().indexInPalette : byte.MaxValue);
|
return new BlockColor(opt ? opt.Get().indexInPalette : byte.MaxValue);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
// TODO: Expose CubeListData in the API
|
||||||
if (value.Color == BlockColors.Default)
|
if (value.Color == BlockColors.Default)
|
||||||
value = new BlockColor(FullGameFields._dataDb.TryGetValue((int) Type, out CubeListData cld)
|
value = new BlockColor(FullGameFields._dataDb.TryGetValue((int) Type, out CubeListData cld)
|
||||||
? cld.DefaultColour
|
? cld.DefaultColour
|
||||||
: throw new BlockTypeException("Unknown block type! Could not set default color."));
|
: throw new BlockTypeException("Unknown block type! Could not set default color."));
|
||||||
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(this);
|
ref var color = ref GetComponent<ColourParameterEntityStruct>();
|
||||||
color.indexInPalette = value.Index;
|
color.indexInPalette = value.Index;
|
||||||
color.hasNetworkChange = true;
|
color.hasNetworkChange = true;
|
||||||
color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); //Setting to 255 results in black
|
color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); //Setting to 255 results in black
|
||||||
|
@ -297,10 +311,10 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float4 CustomColor
|
public float4 CustomColor
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(this).paletteColour;
|
get => GetComponent<ColourParameterEntityStruct>().paletteColour;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(this);
|
ref var color = ref GetComponent<ColourParameterEntityStruct>();
|
||||||
color.paletteColour = value;
|
color.paletteColour = value;
|
||||||
color.hasNetworkChange = true;
|
color.hasNetworkChange = true;
|
||||||
}
|
}
|
||||||
|
@ -313,7 +327,7 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var opt = BlockEngine.GetBlockInfoOptional<CubeMaterialStruct>(this);
|
var opt = GetComponentOptional<CubeMaterialStruct>();
|
||||||
return opt ? (BlockMaterial) opt.Get().materialId : BlockMaterial.Default;
|
return opt ? (BlockMaterial) opt.Get().materialId : BlockMaterial.Default;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
|
@ -325,11 +339,11 @@ namespace TechbloxModdingAPI
|
||||||
: throw new BlockTypeException("Unknown block type! Could not set default material.");
|
: throw new BlockTypeException("Unknown block type! Could not set default material.");
|
||||||
if (!FullGameFields._dataDb.ContainsKey<MaterialPropertiesData>(val))
|
if (!FullGameFields._dataDb.ContainsKey<MaterialPropertiesData>(val))
|
||||||
throw new BlockException($"Block material {value} does not exist!");
|
throw new BlockException($"Block material {value} does not exist!");
|
||||||
ref var comp = ref BlockEngine.GetBlockInfo<CubeMaterialStruct>(this);
|
ref var comp = ref GetComponent<CubeMaterialStruct>();
|
||||||
if (comp.materialId == val)
|
if (comp.materialId == val)
|
||||||
return;
|
return;
|
||||||
comp.materialId = val;
|
comp.materialId = val;
|
||||||
BlockEngine.UpdatePrefab(this, val, Flipped); //The default causes the screen to go black
|
BlockEngine.UpdateDisplayedPrefab(this, val, Flipped); //The default causes the screen to go black
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,12 +356,12 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var opt = BlockEngine.GetBlockInfoOptional<LabelResourceIDComponent>(this);
|
var opt = GetComponentOptional<LabelResourceIDComponent>();
|
||||||
return opt ? FullGameFields._managers.blockLabelResourceManager.GetText(opt.Get().instanceID) : null;
|
return opt ? FullGameFields._managers.blockLabelResourceManager.GetText(opt.Get().instanceID) : null;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
var opt = BlockEngine.GetBlockInfoOptional<LabelResourceIDComponent>(this);
|
var opt = GetComponentOptional<LabelResourceIDComponent>();
|
||||||
if (opt) FullGameFields._managers.blockLabelResourceManager.SetText(opt.Get().instanceID, value);
|
if (opt) FullGameFields._managers.blockLabelResourceManager.SetText(opt.Get().instanceID, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,11 +380,11 @@ namespace TechbloxModdingAPI
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (blockGroup != null) return blockGroup;
|
if (blockGroup != null) return blockGroup;
|
||||||
if (!GameState.IsBuildMode()) return null; // Breaks in simulation
|
if (!GameClient.IsBuildMode) return null; // Breaks in simulation
|
||||||
var bgec = BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this);
|
var bgec = GetComponent<BlockGroupEntityComponent>();
|
||||||
return blockGroup = bgec.currentBlockGroup == -1
|
return blockGroup = bgec.currentBlockGroup == -1
|
||||||
? null
|
? null
|
||||||
: GetInstance(new EGID((uint)bgec.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup),
|
: GetInstanceExisting(new EGID((uint)bgec.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup),
|
||||||
egid => new BlockGroup((int)egid.entityID, this));
|
egid => new BlockGroup((int)egid.entityID, this));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
|
@ -384,7 +398,7 @@ namespace TechbloxModdingAPI
|
||||||
blockGroup?.RemoveInternal(this);
|
blockGroup?.RemoveInternal(this);
|
||||||
if (!InitData.Valid)
|
if (!InitData.Valid)
|
||||||
return;
|
return;
|
||||||
BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this).currentBlockGroup = (int?) value?.Id.entityID ?? -1;
|
GetComponent<BlockGroupEntityComponent>().currentBlockGroup = (int?) value?.Id.entityID ?? -1;
|
||||||
value?.AddInternal(this);
|
value?.AddInternal(this);
|
||||||
blockGroup = value;
|
blockGroup = value;
|
||||||
}
|
}
|
||||||
|
@ -395,8 +409,8 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Static
|
public bool Static
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<BlockStaticComponent>(this).isStatic;
|
get => GetComponent<BlockStaticComponent>().isStatic;
|
||||||
set => BlockEngine.GetBlockInfo<BlockStaticComponent>(this).isStatic = value;
|
set => GetComponent<BlockStaticComponent>().isStatic = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -404,7 +418,7 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float Mass
|
public float Mass
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<MassStruct>(this).mass;
|
get => GetComponent<MassStruct>().mass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -412,16 +426,10 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BlockComplexity Complexity
|
public BlockComplexity Complexity
|
||||||
{
|
{
|
||||||
get => new(BlockEngine.GetBlockInfo<BlockComplexityComponent>(this));
|
get => new(GetComponent<BlockComplexityComponent>());
|
||||||
set => BlockEngine.GetBlockInfo<BlockComplexityComponent>(this) = value;
|
set => GetComponent<BlockComplexityComponent>() = 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>
|
/// <summary>
|
||||||
/// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist.
|
/// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -440,7 +448,7 @@ namespace TechbloxModdingAPI
|
||||||
/// <returns>The SimBody of the chunk or null if the block doesn't exist or not in simulation mode.</returns>
|
/// <returns>The SimBody of the chunk or null if the block doesn't exist or not in simulation mode.</returns>
|
||||||
public SimBody GetSimBody()
|
public SimBody GetSimBody()
|
||||||
{
|
{
|
||||||
var st = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(this);
|
var st = GetComponent<GridConnectionsEntityStruct>();
|
||||||
/*return st.machineRigidBodyId != uint.MaxValue
|
/*return st.machineRigidBodyId != uint.MaxValue
|
||||||
? new SimBody(st.machineRigidBodyId, st.clusterId) - TODO:
|
? new SimBody(st.machineRigidBodyId, st.clusterId) - TODO:
|
||||||
: null;*/
|
: null;*/
|
||||||
|
@ -467,7 +475,6 @@ namespace TechbloxModdingAPI
|
||||||
{ //Member method instead of lambda to avoid constantly creating delegates
|
{ //Member method instead of lambda to avoid constantly creating delegates
|
||||||
if (e.ID != Id) return;
|
if (e.ID != Id) return;
|
||||||
Placed -= OnPlacedInit; //And we can reference it
|
Placed -= OnPlacedInit; //And we can reference it
|
||||||
InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again
|
|
||||||
if (copiedFrom != default)
|
if (copiedFrom != default)
|
||||||
BlockCloneEngine.CopyBlockStats(copiedFrom, Id);
|
BlockCloneEngine.CopyBlockStats(copiedFrom, Id);
|
||||||
}
|
}
|
||||||
|
@ -504,15 +511,13 @@ namespace TechbloxModdingAPI
|
||||||
|
|
||||||
public static void Init()
|
public static void Init()
|
||||||
{
|
{
|
||||||
GameEngineManager.AddGameEngine(PlacementEngine);
|
EngineManager.AddEngine(PlacementEngine, ApiEngineType.Build);
|
||||||
GameEngineManager.AddGameEngine(MovementEngine);
|
EngineManager.AddEngine(RemovalEngine, ApiEngineType.Build);
|
||||||
GameEngineManager.AddGameEngine(RotationEngine);
|
EngineManager.AddEngine(BlockEngine, ApiEngineType.Build, ApiEngineType.PlayServer, ApiEngineType.PlayClient);
|
||||||
GameEngineManager.AddGameEngine(RemovalEngine);
|
EngineManager.AddEngine(BlockEventsEngine, ApiEngineType.Build);
|
||||||
GameEngineManager.AddGameEngine(BlockEngine);
|
EngineManager.AddEngine(ScalingEngine, ApiEngineType.Build);
|
||||||
GameEngineManager.AddGameEngine(BlockEventsEngine);
|
EngineManager.AddEngine(SignalEngine, ApiEngineType.Build);
|
||||||
GameEngineManager.AddGameEngine(ScalingEngine);
|
EngineManager.AddEngine(BlockCloneEngine, ApiEngineType.Build);
|
||||||
GameEngineManager.AddGameEngine(SignalEngine);
|
|
||||||
GameEngineManager.AddGameEngine(BlockCloneEngine);
|
|
||||||
Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine
|
Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ using UnityEngine;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
using TechbloxModdingAPI.Blocks.Engines;
|
using TechbloxModdingAPI.Blocks.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Common;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
|
@ -18,14 +19,14 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
||||||
{
|
{
|
||||||
internal static BlueprintEngine _engine = new BlueprintEngine();
|
internal static BlueprintEngine _engine = new();
|
||||||
private readonly Block sourceBlock;
|
private readonly Block sourceBlock;
|
||||||
private readonly List<Block> blocks;
|
private readonly List<Block> blocks;
|
||||||
private float3 position, rotation;
|
private float3 position, rotation;
|
||||||
internal bool PosAndRotCalculated;
|
internal bool PosAndRotCalculated;
|
||||||
|
|
||||||
internal BlockGroup(int id, Block block) : base(new EGID((uint)id,
|
internal BlockGroup(int id, Block block) : base(new EGID((uint)id,
|
||||||
BlockGroupExclusiveGroups.BlockGroupEntityGroup))
|
BlockGroupExclusiveGroups.BlockGroupEntityGroup), typeof(BlockGroupEntityDescriptor))
|
||||||
{
|
{
|
||||||
if (id == BlockGroupUtility.GROUP_UNASSIGNED)
|
if (id == BlockGroupUtility.GROUP_UNASSIGNED)
|
||||||
throw new BlockException("Cannot create a block group for blocks without a group!");
|
throw new BlockException("Cannot create a block group for blocks without a group!");
|
||||||
|
@ -149,7 +150,7 @@ namespace TechbloxModdingAPI
|
||||||
|
|
||||||
internal static void Init()
|
internal static void Init()
|
||||||
{
|
{
|
||||||
GameEngineManager.AddGameEngine(_engine);
|
EngineManager.AddEngine(_engine, ApiEngineType.Build);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<Block> GetEnumerator() => blocks.GetEnumerator();
|
public IEnumerator<Block> GetEnumerator() => blocks.GetEnumerator();
|
||||||
|
|
|
@ -2,7 +2,7 @@ using RobocraftX.Blocks;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks
|
namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public record BlockComplexity(int Cpu, int Power)
|
public readonly record struct BlockComplexity(int Cpu, int Power)
|
||||||
{
|
{
|
||||||
public BlockComplexity(BlockComplexityComponent component) : this(component.cpu, component.power)
|
public BlockComplexity(BlockComplexityComponent component) : this(component.cpu, component.power)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,18 +23,18 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*/// <summary> - TODO: Internal struct access
|
/// <summary>
|
||||||
/// Gets or sets the Engine's On property. May not be saved.
|
/// Gets or sets the Engine's On property. May not be saved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool On
|
public bool On
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn;
|
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "engineOn")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "engineOn", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,11 +45,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear;
|
return ((int)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentGear")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentGear", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,11 +60,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "gearChangeCountdown")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "gearChangeCountdown", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +75,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmAV")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmAV", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,11 +90,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmLV")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentRpmLV", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,11 +105,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmAV")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmAV", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,11 +120,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmLV")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "targetRpmLV", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,11 +135,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentTorque")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "currentTorque", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,11 +150,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityAV")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityAV", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,11 +165,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityLV")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelVelocityLV", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,11 +180,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount;
|
return ((int)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelCount")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "totalWheelCount", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,11 +195,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput;
|
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearUpInput")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearUpInput", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,11 +210,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput;
|
return ((bool)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearDownInput")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "lastGearDownInput", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,11 +225,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "manualToAutoGearCoolOffCounter")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "manualToAutoGearCoolOffCounter", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,11 +240,11 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load;
|
return ((float)(BlockEngine.GetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "load")));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load = value;
|
BlockEngine.SetBlockInfo(this, HarmonyLib.AccessTools.TypeByName("Techblox.EngineBlock.EngineBlockComponent"), "load", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,13 +324,17 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Engine's GearDownRpms property. May not be saved.
|
/// Gets or sets the Engine's GearDownRpms property. May not be saved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float[] GearDownRpms
|
public Svelto.ECS.DataStructures.NativeDynamicArray GearDownRpms
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearDownRpms.ToManagedArray<float>();
|
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearDownRpms;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearDownRpms = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +381,21 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
|
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Engine's EngineBlockDataId property. May not be saved.
|
||||||
|
/// </summary>
|
||||||
|
public int EngineBlockDataId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).engineBlockDataId;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).engineBlockDataId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,9 @@ using Gamecraft.Wires;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
using RobocraftX.Character;
|
using RobocraftX.Character;
|
||||||
using RobocraftX.Common;
|
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
@ -98,8 +97,5 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; } = "TechbloxModdingAPIBlockCloneGameEngine";
|
|
||||||
public bool isRemovable { get; } = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
using Gamecraft.ColourPalette;
|
using Gamecraft.ColourPalette;
|
||||||
using Gamecraft.Wires;
|
using Gamecraft.Wires;
|
||||||
using RobocraftX.Blocks;
|
using RobocraftX.Blocks;
|
||||||
|
@ -14,13 +12,9 @@ using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.EntityStructs;
|
using Svelto.ECS.EntityStructs;
|
||||||
using Svelto.ECS.Experimental;
|
using Svelto.ECS.Experimental;
|
||||||
using Svelto.ECS.Hybrid;
|
|
||||||
using Techblox.BuildingDrone;
|
|
||||||
using Techblox.ObjectIDBlockServer;
|
using Techblox.ObjectIDBlockServer;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
using TechbloxModdingAPI.Utility.ECS;
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
using PrefabsID = RobocraftX.Common.PrefabsID;
|
using PrefabsID = RobocraftX.Common.PrefabsID;
|
||||||
|
|
||||||
|
@ -74,50 +68,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
|
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
|
||||||
ColourPaletteExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
ColourPaletteExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
||||||
|
|
||||||
public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent
|
public void UpdateDisplayData(EGID id)
|
||||||
{
|
|
||||||
return entitiesDB.QueryEntityOptional<T>(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref T GetBlockInfo<T>(Block block) where T : unmanaged, IEntityComponent
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
if (!typeof(BlockTagEntityStruct).IsAssignableFrom(typeof(T)) && block.Exists && block.InitData.Valid)
|
|
||||||
throw new ArgumentException("The block exists but the init data has not been removed!");
|
|
||||||
#endif
|
|
||||||
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ref T GetBlockInfo<T>(EcsObjectBase obj) where T : unmanaged, IEntityComponent
|
|
||||||
{
|
|
||||||
return ref entitiesDB.QueryEntityOrDefault<T>(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref T GetBlockInfoViewComponent<T>(Block block) where T : struct, IEntityViewComponent
|
|
||||||
{
|
|
||||||
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object GetBlockInfo(Block block, Type type, string name)
|
|
||||||
{
|
|
||||||
var opt = AccessTools.Method(typeof(NativeApiExtensions), "QueryEntityOptional",
|
|
||||||
new[] { typeof(EntitiesDB), typeof(EcsObjectBase), typeof(ExclusiveGroupStruct) }, new[] { type })
|
|
||||||
.Invoke(null, new object[] { entitiesDB, block, null });
|
|
||||||
var str = AccessTools.Property(opt.GetType(), "Value").GetValue(opt);
|
|
||||||
return AccessTools.Field(str.GetType(), name).GetValue(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetBlockInfo(Block block, Type type, string name, object value)
|
|
||||||
{
|
|
||||||
var opt = AccessTools.Method(typeof(BlockEngine), "GetBlockInfoOptional", generics: new[] { type })
|
|
||||||
.Invoke(this, new object[] { block });
|
|
||||||
var prop = AccessTools.Property(opt.GetType(), "Value");
|
|
||||||
var str = prop.GetValue(opt);
|
|
||||||
AccessTools.Field(str.GetType(), name).SetValue(str, value);
|
|
||||||
prop.SetValue(opt, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateDisplayedBlock(EGID id)
|
|
||||||
{
|
{
|
||||||
if (!BlockExists(id)) return;
|
if (!BlockExists(id)) return;
|
||||||
var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
|
var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
|
||||||
|
@ -129,7 +80,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(id); // Signal a prefab change so it updates the render buffers
|
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(id); // Signal a prefab change so it updates the render buffers
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void UpdatePrefab(Block block, byte material, bool flipped)
|
internal void UpdateDisplayedPrefab(Block block, byte material, bool flipped)
|
||||||
{
|
{
|
||||||
var prefabAssetIDOpt = entitiesDB.QueryEntityOptional<PrefabAssetIDComponent>(block);
|
var prefabAssetIDOpt = entitiesDB.QueryEntityOptional<PrefabAssetIDComponent>(block);
|
||||||
uint prefabAssetID = prefabAssetIDOpt
|
uint prefabAssetID = prefabAssetIDOpt
|
||||||
|
@ -150,12 +101,12 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
entitiesDB.PublishEntityChangeDelayed<CubeMaterialStruct>(block.Id);
|
entitiesDB.PublishEntityChangeDelayed<CubeMaterialStruct>(block.Id);
|
||||||
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(block.Id);
|
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(block.Id);
|
||||||
|
|
||||||
ref BuildingActionComponent local =
|
/*ref BuildingActionComponent local =
|
||||||
ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
|
ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
|
||||||
.GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
|
.GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
|
||||||
local.buildAction = BuildAction.ChangeMaterial;
|
local.buildAction = BuildAction.ChangeMaterial;
|
||||||
local.targetPosition = block.Position;
|
local.targetPosition = block.Position; - TODO: This probably only plays the audio
|
||||||
this.entitiesDB.PublishEntityChangeDelayed<BuildingActionComponent>(local.ID);
|
this.entitiesDB.PublishEntityChangeDelayed<BuildingActionComponent>(local.ID);*/
|
||||||
}
|
}
|
||||||
//Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
|
//Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ using Svelto.ECS.EntityStructs;
|
||||||
using Svelto.ECS.Native;
|
using Svelto.ECS.Native;
|
||||||
using Svelto.ECS.Serialization;
|
using Svelto.ECS.Serialization;
|
||||||
using Techblox.Blocks.Connections;
|
using Techblox.Blocks.Connections;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
using TechbloxModdingAPI.Utility.ECS;
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
@ -35,7 +36,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
AccessTools.Method("RobocraftX.CR.MachineEditing.PlaceBlockUtility:GetBlocksSharingBlockgroup");
|
AccessTools.Method("RobocraftX.CR.MachineEditing.PlaceBlockUtility:GetBlocksSharingBlockgroup");
|
||||||
|
|
||||||
private NativeDynamicArray selectedBlocksInGroup;
|
private NativeDynamicArray selectedBlocksInGroup;
|
||||||
private NativeHashSet<ulong> removedConnections = new NativeHashSet<ulong>();
|
private NativeHashSet<ulong> removedConnections = new();
|
||||||
private int addingToBlockGroup = -1;
|
private int addingToBlockGroup = -1;
|
||||||
|
|
||||||
private static readonly Type PlaceBlueprintUtilityType =
|
private static readonly Type PlaceBlueprintUtilityType =
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
using RobocraftX.Common;
|
|
||||||
using RobocraftX.DOTS;
|
|
||||||
using Svelto.ECS;
|
|
||||||
using Svelto.ECS.EntityStructs;
|
|
||||||
using Unity.Mathematics;
|
|
||||||
using Unity.Transforms;
|
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
using TechbloxModdingAPI.Utility.ECS;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Engine which executes block movement actions
|
|
||||||
/// </summary>
|
|
||||||
public class MovementEngine : IApiEngine
|
|
||||||
{
|
|
||||||
public string Name { get; } = "TechbloxModdingAPIMovementGameEngine";
|
|
||||||
|
|
||||||
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(Block block, float3 vector)
|
|
||||||
{
|
|
||||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block);
|
|
||||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
|
||||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
|
|
||||||
var phyStruct = this.entitiesDB.QueryEntityOptional<DOTSPhysicsEntityStruct>(block);
|
|
||||||
// main (persistent) position
|
|
||||||
posStruct.position = vector;
|
|
||||||
// placement grid position
|
|
||||||
gridStruct.position = vector;
|
|
||||||
// rendered position
|
|
||||||
transStruct.position = vector;
|
|
||||||
// collision position
|
|
||||||
if (phyStruct)
|
|
||||||
{ //It exists
|
|
||||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity, new Translation
|
|
||||||
{
|
|
||||||
Value = posStruct.position
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
|
|
||||||
return posStruct.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal float3 GetPosition(Block block)
|
|
||||||
{
|
|
||||||
return entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block).position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,11 +9,10 @@ using RobocraftX.StateSync;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Native;
|
using Svelto.ECS.Native;
|
||||||
using Techblox.Blocks.Connections;
|
using Techblox.Blocks.Connections;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Jobs;
|
using Unity.Jobs;
|
||||||
using Allocator = Unity.Collections.Allocator;
|
using Allocator = Unity.Collections.Allocator;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
using RobocraftX.Common;
|
|
||||||
using RobocraftX.DOTS;
|
|
||||||
using Svelto.ECS;
|
|
||||||
using Svelto.ECS.EntityStructs;
|
|
||||||
using Unity.Mathematics;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
using TechbloxModdingAPI.Utility.ECS;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Engine which executes block movement actions
|
|
||||||
/// </summary>
|
|
||||||
public class RotationEngine : IApiEngine
|
|
||||||
{
|
|
||||||
public string Name { get; } = "TechbloxModdingAPIRotationGameEngine";
|
|
||||||
|
|
||||||
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(Block block, Vector3 vector)
|
|
||||||
{
|
|
||||||
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
|
||||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
|
||||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
|
|
||||||
var phyStruct = this.entitiesDB.QueryEntityOptional<DOTSPhysicsEntityStruct>(block);
|
|
||||||
// main (persistent) rotation
|
|
||||||
Quaternion newRotation = rotStruct.rotation;
|
|
||||||
newRotation.eulerAngles = vector;
|
|
||||||
rotStruct.rotation = newRotation;
|
|
||||||
// placement grid rotation
|
|
||||||
gridStruct.rotation = newRotation;
|
|
||||||
// rendered rotation
|
|
||||||
transStruct.rotation = newRotation;
|
|
||||||
// collision rotation
|
|
||||||
if (phyStruct)
|
|
||||||
{ //It exists
|
|
||||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity,
|
|
||||||
new Unity.Transforms.Rotation
|
|
||||||
{
|
|
||||||
Value = rotStruct.rotation
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Connections probably need to be assigned (maybe)
|
|
||||||
// They are assigned during machine processing anyway
|
|
||||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
|
|
||||||
return ((Quaternion)rotStruct.rotation).eulerAngles;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal float3 GetRotation(Block block)
|
|
||||||
{
|
|
||||||
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
|
||||||
return ((Quaternion) rotStruct.rotation).eulerAngles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,9 +4,8 @@ using HarmonyLib;
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.DOTS;
|
using RobocraftX.DOTS;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using Unity.Entities;
|
using Unity.Entities;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
@ -30,7 +29,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
private EntityManager _entityManager; //Unity entity manager
|
private EntityManager _entityManager; //Unity entity manager
|
||||||
|
|
||||||
public void UpdateCollision(EGID egid)
|
public void UpdateCollision(EGID egid)
|
||||||
{
|
{ // TODO: Move to BlockEngine/IHasPhysics
|
||||||
if (_entityManager == default)
|
if (_entityManager == default)
|
||||||
_entityManager = FullGameFields._physicsWorld.EntityManager;
|
_entityManager = FullGameFields._physicsWorld.EntityManager;
|
||||||
//Assuming the block exists
|
//Assuming the block exists
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
using Gamecraft.Wires;
|
using Gamecraft.Wires;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
using TechbloxModdingAPI.Utility.ECS;
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
@ -260,7 +261,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
|
||||||
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
|
startPorts = new EGID[] {new(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
|
||||||
}
|
}
|
||||||
|
|
||||||
EGID[] endPorts;
|
EGID[] endPorts;
|
||||||
|
@ -272,7 +273,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
|
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
|
||||||
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
|
endPorts = new EGID[] {new(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
|
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using Gamecraft.Wires;
|
using Gamecraft.Wires;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Experimental;
|
|
||||||
|
|
||||||
using TechbloxModdingAPI.Blocks.Engines;
|
using TechbloxModdingAPI.Blocks.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks
|
namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
|
@ -69,45 +65,6 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Construct a wire object froam n existing connection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Starting block ID.</param>
|
|
||||||
/// <param name="end">Ending block ID.</param>
|
|
||||||
/// <param name="startPort">Starting port number, or guess if omitted.</param>
|
|
||||||
/// <param name="endPort">Ending port number, or guess if omitted.</param>
|
|
||||||
/// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
|
|
||||||
public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) : base(ecs =>
|
|
||||||
{
|
|
||||||
var th = (Wire)ecs;
|
|
||||||
th.startBlockEGID = start.Id;
|
|
||||||
th.endBlockEGID = end.Id;
|
|
||||||
bool flipped = false;
|
|
||||||
// find block ports
|
|
||||||
EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
|
|
||||||
if (wire == default)
|
|
||||||
{
|
|
||||||
// flip I/O around and try again
|
|
||||||
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort);
|
|
||||||
flipped = true;
|
|
||||||
// NB: start and end are handled exactly as they're received as params.
|
|
||||||
// This makes wire traversal easier, but makes logic in this class a bit more complex
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wire != default)
|
|
||||||
{
|
|
||||||
th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new WireInvalidException("Wire not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
return th.wireEGID;
|
|
||||||
})
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Construct a wire object from an existing wire connection.
|
/// Construct a wire object from an existing wire connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -120,9 +77,9 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
|
public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
|
||||||
: this(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
|
: this(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
|
||||||
{
|
{
|
||||||
}
|
} // TODO: Convert all constructors (including the removed one) to static methods
|
||||||
|
|
||||||
private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire)
|
private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire, typeof(WireEntityDescriptor))
|
||||||
{
|
{
|
||||||
Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
|
Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +102,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// Construct a wire object from an existing wire connection.
|
/// Construct a wire object from an existing wire connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="wireEgid">The wire ID.</param>
|
/// <param name="wireEgid">The wire ID.</param>
|
||||||
public Wire(EGID wireEgid) : base(wireEgid)
|
public Wire(EGID wireEgid) : base(wireEgid, typeof(WireEntityDescriptor))
|
||||||
{
|
{
|
||||||
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
|
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
|
||||||
Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
|
Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
|
||||||
|
@ -189,22 +146,6 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The wire's raw string signal.
|
|
||||||
/// </summary>
|
|
||||||
public ECSString ECSString
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The wire's signal id.
|
/// The wire's signal id.
|
||||||
/// I'm 50% sure this is useless.
|
/// I'm 50% sure this is useless.
|
||||||
|
@ -277,7 +218,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
/// <returns>A copy of the wire object.</returns>
|
/// <returns>A copy of the wire object.</returns>
|
||||||
public Wire OutputToInputCopy()
|
public Wire OutputToInputCopy()
|
||||||
{
|
{
|
||||||
return GetInstance(wireEGID, egid => new Wire(egid));
|
return GetInstanceExisting(wireEGID, egid => new Wire(egid));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -292,9 +233,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
inputToOutput = false;
|
inputToOutput = false;
|
||||||
// swap inputs and outputs
|
// swap inputs and outputs
|
||||||
(endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
|
(endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
|
||||||
var tempPort = endPortEGID;
|
(endPortEGID, startPortEGID) = (startPortEGID, endPortEGID);
|
||||||
endPortEGID = startPortEGID;
|
|
||||||
startPortEGID = tempPort;
|
|
||||||
(endPort, startPort) = (startPort, endPort);
|
(endPort, startPort) = (startPort, endPort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
46
TechbloxModdingAPI/Client/App/ClientEngine.cs
Normal file
46
TechbloxModdingAPI/Client/App/ClientEngine.cs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
using RobocraftX.GUI.MyGamesScreen;
|
||||||
|
using RobocraftX.Multiplayer;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using Techblox.GameSelection;
|
||||||
|
using TechbloxModdingAPI.Client.Game;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Client.App;
|
||||||
|
|
||||||
|
internal class ClientEngine : IApiEngine
|
||||||
|
{
|
||||||
|
public void Ready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitiesDB entitiesDB { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientMachine[] GetMachines()
|
||||||
|
{
|
||||||
|
var (mgsevs, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||||
|
var games = new ClientMachine[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
Logging.MetaDebugLog($"Found game named {mgsevs[i].GameName}");
|
||||||
|
games[i] = new ClientMachine(mgsevs[i].ID);
|
||||||
|
}
|
||||||
|
return games;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnterBuildMode(ClientEnvironment environment, ClientMachine machine)
|
||||||
|
{ // TODO: Move to using a single engine per 'type' (see AddEngine())
|
||||||
|
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
|
||||||
|
ref var selection = ref entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
|
||||||
|
selection.userContentID.Set(machine.ContentID);
|
||||||
|
selection.triggerStart = true;
|
||||||
|
selection.saveType = SaveType.ExistingSave;
|
||||||
|
selection.saveName.Set(machine.Name);
|
||||||
|
selection.gameMode = machine is ClientWorld ? GameMode.CreateWorld : GameMode.PlayGame;
|
||||||
|
selection.gameID.Set(environment.Id); //TODO: Expose to the API
|
||||||
|
}
|
||||||
|
}
|
116
TechbloxModdingAPI/Client/App/GameClient.cs
Normal file
116
TechbloxModdingAPI/Client/App/GameClient.cs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Svelto.Tasks;
|
||||||
|
using TechbloxModdingAPI.Client.Game;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Client.App;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains information about the game client's current state.
|
||||||
|
/// </summary>
|
||||||
|
public static class GameClient
|
||||||
|
{
|
||||||
|
private static readonly ClientEngine _engine = new();
|
||||||
|
|
||||||
|
public static GameState CurrentState
|
||||||
|
{
|
||||||
|
get => _currentState;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
_currentState = value;
|
||||||
|
var old = _currentState;
|
||||||
|
_stateChanged.Invoke(null, new GameStateChangedArgs { OldState = old, NewState = value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GameState _currentState;
|
||||||
|
|
||||||
|
public static bool IsBuildMode =>
|
||||||
|
CurrentState is GameState.InMachineEditor or GameState.InWorldEditor;
|
||||||
|
|
||||||
|
public static bool IsSimulationMode =>
|
||||||
|
CurrentState is GameState.InTestMode or GameState.InWorldTestMode or GameState.InOnlineMatch;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An event that fires whenever the game's state changes
|
||||||
|
/// </summary>
|
||||||
|
public static event EventHandler<GameStateChangedArgs> StateChanged
|
||||||
|
{
|
||||||
|
add => _stateChanged += value;
|
||||||
|
remove => _stateChanged -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WrappedHandler<GameStateChangedArgs> _stateChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Techblox build version string.
|
||||||
|
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The version.</value>
|
||||||
|
public static string Version => Application.version;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unity version string.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The unity version.</value>
|
||||||
|
public static string UnityVersion => Application.unityVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Environments (maps) currently visible in the menu.
|
||||||
|
/// These take a second to completely populate after the EnterMenu event fires.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Available environments.</value>
|
||||||
|
public static ClientEnvironment[] Environments { get; }
|
||||||
|
|
||||||
|
public static ClientMachine[] Machines { get; }
|
||||||
|
|
||||||
|
public static void EnterBuildMode(ClientEnvironment environment, ClientMachine machine)
|
||||||
|
{
|
||||||
|
if (CurrentState == GameState.InMenu)
|
||||||
|
throw new InvalidOperationException($"Can only enter test mode from build mode! Current mode: {CurrentState}");
|
||||||
|
var env = new ClientEnvironment("GAMEID_Road_Track"); // TODO: The options are hardcoded
|
||||||
|
_engine.EnterBuildMode(env, machine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator<TaskContract> EnterTestMode()
|
||||||
|
{
|
||||||
|
if (!IsBuildMode)
|
||||||
|
throw new InvalidOperationException($"Can only enter test mode from build mode! Current mode: {CurrentState}");
|
||||||
|
// TODO
|
||||||
|
//return Task.CompletedTask;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator<TaskContract> ExitSimulationMode()
|
||||||
|
{ // TODO: Separate these based on the current game state?
|
||||||
|
if (!IsSimulationMode)
|
||||||
|
throw new InvalidOperationException($"Can only exit test mode when in it! Current mode: {CurrentState}");
|
||||||
|
// TODO
|
||||||
|
//return Task.CompletedTask;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator<TaskContract> ExitBuildMode()
|
||||||
|
{
|
||||||
|
if (!IsBuildMode)
|
||||||
|
throw new InvalidOperationException($"Can only exit test mode when in it! Current mode: {CurrentState}");
|
||||||
|
// TODO
|
||||||
|
//return Task.CompletedTask;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
EngineManager.AddEngine(_engine, ApiEngineType.Menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct GameStateChangedArgs
|
||||||
|
{
|
||||||
|
public GameState OldState { get; set; }
|
||||||
|
public GameState NewState { get; set; }
|
||||||
|
}
|
||||||
|
}
|
33
TechbloxModdingAPI/Client/App/GameState.cs
Normal file
33
TechbloxModdingAPI/Client/App/GameState.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
namespace TechbloxModdingAPI.Client.App;
|
||||||
|
|
||||||
|
public enum GameState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// In the environment/game selection menu.
|
||||||
|
/// </summary>
|
||||||
|
InMenu,
|
||||||
|
/// <summary>
|
||||||
|
/// In machine editor mode in a selected environment.
|
||||||
|
/// </summary>
|
||||||
|
InMachineEditor,
|
||||||
|
/// <summary>
|
||||||
|
/// In world editor mode.
|
||||||
|
/// </summary>
|
||||||
|
InWorldEditor,
|
||||||
|
/// <summary>
|
||||||
|
/// In test mode in a selected environment.
|
||||||
|
/// </summary>
|
||||||
|
InTestMode,
|
||||||
|
/// <summary>
|
||||||
|
/// In world test mode.
|
||||||
|
/// </summary>
|
||||||
|
InWorldTestMode,
|
||||||
|
/// <summary>
|
||||||
|
/// In an online match as a client.
|
||||||
|
/// </summary>
|
||||||
|
InOnlineMatch,
|
||||||
|
/// <summary>
|
||||||
|
/// The loading screen is active or otherwise transitioning between modes.
|
||||||
|
/// </summary>
|
||||||
|
Loading
|
||||||
|
}
|
88
TechbloxModdingAPI/Client/App/Popup.cs
Normal file
88
TechbloxModdingAPI/Client/App/Popup.cs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using RobocraftX.Services;
|
||||||
|
using TechbloxModdingAPI.Common.Utils;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Client.App;
|
||||||
|
|
||||||
|
public static class Popup
|
||||||
|
{
|
||||||
|
private static Func<object> ErrorHandlerInstanceGetter;
|
||||||
|
|
||||||
|
private static Action<object, Error> EnqueueError;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open a popup which prompts the user to click a button.
|
||||||
|
/// This reuses Techblox's error dialog popup
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="popup">The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt.</param>
|
||||||
|
public static void PromptUser(Error popup)
|
||||||
|
{
|
||||||
|
// if the stuff wasn't mostly set to internal, this would be written as:
|
||||||
|
// RobocraftX.Services.ErrorHandler.Instance.EnqueueError(error);
|
||||||
|
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||||
|
EnqueueError(errorHandlerInstance, popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CloseCurrentPrompt()
|
||||||
|
{
|
||||||
|
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||||
|
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||||
|
popup.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SelectFirstPromptButton()
|
||||||
|
{
|
||||||
|
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||||
|
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||||
|
popup.FirstButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SelectSecondPromptButton()
|
||||||
|
{
|
||||||
|
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||||
|
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||||
|
popup.SecondButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Init()
|
||||||
|
{
|
||||||
|
var errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
|
||||||
|
ErrorHandlerInstanceGetter = GenInstanceGetter(errorHandler);
|
||||||
|
EnqueueError = GenEnqueueError(errorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating delegates once is faster than reflection every time
|
||||||
|
// Admittedly, this way is more difficult to code and less readable
|
||||||
|
|
||||||
|
private static Func<object> GenInstanceGetter(Type handler)
|
||||||
|
{
|
||||||
|
return Reflections.CreateAccessor<Func<object>>("Instance", handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<object, Error> GenEnqueueError(Type handler)
|
||||||
|
{
|
||||||
|
var enqueueError = AccessTools.Method(handler, "EnqueueError");
|
||||||
|
return Reflections.CreateMethodCall<Action<object, Error>>(enqueueError, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
|
||||||
|
|
||||||
|
private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,8 @@ using RobocraftX.Rendering;
|
||||||
using RobocraftX.Rendering.GPUI;
|
using RobocraftX.Rendering.GPUI;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.EntityStructs;
|
using Svelto.ECS.EntityStructs;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
using TechbloxModdingAPI.Utility.ECS;
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
|
@ -42,7 +41,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
private static IEntityFactory _entityFactory;
|
private static IEntityFactory _entityFactory;
|
||||||
|
|
||||||
public EntityInitializer PlaceBlock(BlockIDs block, float3 position, Player player, bool autoWire)
|
public EntityInitializer PlaceBlock(BlockIDs block, float3 position, Player player, bool autoWire)
|
||||||
{ //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
|
{
|
||||||
return BuildBlock((ushort) block, position, autoWire, (player ?? Player.LocalPlayer).Id);
|
return BuildBlock((ushort) block, position, autoWire, (player ?? Player.LocalPlayer).Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,15 +52,19 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
if(!FullGameFields._dataDb.ContainsKey<CubeListData>(block))
|
if(!FullGameFields._dataDb.ContainsKey<CubeListData>(block))
|
||||||
throw new BlockException("Block with ID " + block + " not found!");
|
throw new BlockException("Block with ID " + block + " not found!");
|
||||||
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
|
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
|
||||||
DBEntityStruct dbEntity = new DBEntityStruct {DBID = block};
|
|
||||||
|
|
||||||
EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.blockIDGeneratorClient.Next(), block); //The ghost block index is only used for triggers
|
var structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.blockIDGeneratorClient.Next(), block); //The ghost block index is only used for triggers
|
||||||
|
|
||||||
|
// Use the default steel material for the block until it's changed and set up the graphics for it
|
||||||
uint prefabAssetID = structInitializer.Has<PrefabAssetIDComponent>()
|
uint prefabAssetID = structInitializer.Has<PrefabAssetIDComponent>()
|
||||||
? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID
|
? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID
|
||||||
: throw new BlockException("Prefab asset ID not found!"); //Set by the game
|
: throw new BlockException("Prefab asset ID not found!"); //Set by the game
|
||||||
uint prefabId = PrefabsID.GetOrAddPrefabID((ushort) prefabAssetID, (byte) BlockMaterial.SteelBodywork, 1, false);
|
uint prefabId = PrefabsID.GetOrAddPrefabID((ushort) prefabAssetID, (byte) BlockMaterial.SteelBodywork, 1, false);
|
||||||
structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId));
|
structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId));
|
||||||
structInitializer.Init(dbEntity);
|
structInitializer.Get<CubeMaterialStruct>().materialId = (byte) BlockMaterial.SteelBodywork;
|
||||||
|
|
||||||
|
// The DBID is the block's type (cube, servo, seat etc.)
|
||||||
|
structInitializer.Init(new DBEntityStruct {DBID = block});
|
||||||
structInitializer.Init(new PositionEntityStruct {position = position});
|
structInitializer.Init(new PositionEntityStruct {position = position});
|
||||||
structInitializer.Init(new RotationEntityStruct {rotation = quaternion.identity});
|
structInitializer.Init(new RotationEntityStruct {rotation = quaternion.identity});
|
||||||
structInitializer.Init(new ScalingEntityStruct {scale = new float3(1, 1, 1)});
|
structInitializer.Init(new ScalingEntityStruct {scale = new float3(1, 1, 1)});
|
||||||
|
@ -71,7 +74,6 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
rotation = quaternion.identity
|
rotation = quaternion.identity
|
||||||
});
|
});
|
||||||
structInitializer.Init(new UniformBlockScaleEntityStruct {scaleFactor = 1});
|
structInitializer.Init(new UniformBlockScaleEntityStruct {scaleFactor = 1});
|
||||||
structInitializer.Get<CubeMaterialStruct>().materialId = (byte) BlockMaterial.SteelBodywork;
|
|
||||||
var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerId,
|
var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerId,
|
||||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
|
||||||
if (!bssesopt)
|
if (!bssesopt)
|
||||||
|
@ -108,10 +110,6 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
return structInitializer;
|
return structInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => "TechbloxModdingAPIPlacementGameEngine";
|
|
||||||
|
|
||||||
public bool isRemovable => false;
|
|
||||||
|
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
class FactoryObtainerPatch
|
class FactoryObtainerPatch
|
||||||
{
|
{
|
14
TechbloxModdingAPI/Client/Game/ClientEnvironment.cs
Normal file
14
TechbloxModdingAPI/Client/Game/ClientEnvironment.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace TechbloxModdingAPI.Client.Game;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A build or simulation environment.
|
||||||
|
/// </summary>
|
||||||
|
public class ClientEnvironment
|
||||||
|
{
|
||||||
|
public string Id { get; }
|
||||||
|
|
||||||
|
public ClientEnvironment(string id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
}
|
15
TechbloxModdingAPI/Client/Game/ClientMachine.cs
Normal file
15
TechbloxModdingAPI/Client/Game/ClientMachine.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using RobocraftX.GUI.MyGamesScreen;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Client.Game;
|
||||||
|
|
||||||
|
public class ClientMachine : EcsObjectBase
|
||||||
|
{
|
||||||
|
public ClientMachine(EGID id) : base(id, typeof(MyGameDataEntityDescriptor))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ContentID { get; set; } // TODO
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
10
TechbloxModdingAPI/Client/Game/ClientWorld.cs
Normal file
10
TechbloxModdingAPI/Client/Game/ClientWorld.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Client.Game;
|
||||||
|
|
||||||
|
public class ClientWorld : ClientMachine
|
||||||
|
{
|
||||||
|
public ClientWorld(EGID id) : base(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,18 @@
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using Techblox.Destruction;
|
||||||
using Techblox.TimeRunning.Clusters;
|
using Techblox.TimeRunning.Clusters;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represnts a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints.
|
/// Represents a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints.
|
||||||
/// Only exists if a cluster destruction manager is present. Static blocks like grass and dirt aren't part of a cluster.
|
/// Only exists if a cluster destruction manager is present. Static blocks like grass and dirt aren't part of a cluster.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Cluster : EcsObjectBase
|
public class Cluster : EcsObjectBase
|
||||||
{
|
{
|
||||||
public Cluster(EGID id) : base(id)
|
public Cluster(EGID id) : base(id, typeof(ResetDestructionUtility))
|
||||||
{
|
{ // TODO: Damage has been connection-based for a while
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cluster(uint id) : this(new EGID(id, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
public Cluster(uint id) : this(new EGID(id, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace TechbloxModdingAPI.Commands
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class CommandManager
|
public static class CommandManager
|
||||||
{
|
{
|
||||||
private static Dictionary<string, ICustomCommandEngine> _customCommands = new Dictionary<string, ICustomCommandEngine>();
|
private static Dictionary<string, ICustomCommandEngine> _customCommands = new();
|
||||||
|
|
||||||
private static EnginesRoot _lastEngineRoot;
|
private static EnginesRoot _lastEngineRoot;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace TechbloxModdingAPI.Commands
|
||||||
public Delegate Action;
|
public Delegate Action;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<string, CommandData> _commands = new Dictionary<string, CommandData>();
|
private static Dictionary<string, CommandData> _commands = new();
|
||||||
public static void Register(string name, Delegate action, string desc)
|
public static void Register(string name, Delegate action, string desc)
|
||||||
{
|
{
|
||||||
_commands.Add(name, new CommandData
|
_commands.Add(name, new CommandData
|
||||||
|
@ -51,7 +51,6 @@ namespace TechbloxModdingAPI.Commands
|
||||||
|
|
||||||
public static bool Exists(string name) => _commands.ContainsKey(name);
|
public static bool Exists(string name) => _commands.ContainsKey(name);
|
||||||
|
|
||||||
public static ReadOnlyDictionary<string, CommandData> GetAllCommandData() =>
|
public static ReadOnlyDictionary<string, CommandData> GetAllCommandData() => new(_commands);
|
||||||
new ReadOnlyDictionary<string, CommandData>(_commands);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,4 @@
|
||||||
using System;
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Svelto.ECS;
|
|
||||||
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Commands
|
namespace TechbloxModdingAPI.Commands
|
||||||
{
|
{
|
||||||
|
@ -16,7 +7,7 @@ namespace TechbloxModdingAPI.Commands
|
||||||
/// If you are using implementing this yourself, you must manually register the command.
|
/// If you are using implementing this yourself, you must manually register the command.
|
||||||
/// See SimpleCustomCommandEngine's Ready() and Dispose() methods for an example of command registration.
|
/// See SimpleCustomCommandEngine's Ready() and Dispose() methods for an example of command registration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ICustomCommandEngine : IApiEngine
|
public interface ICustomCommandEngine : INamedApiEngine
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The command's description, shown in command help messages
|
/// The command's description, shown in command help messages
|
||||||
|
|
177
TechbloxModdingAPI/Common/EcsObjectBase.cs
Normal file
177
TechbloxModdingAPI/Common/EcsObjectBase.cs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using Svelto.ECS.Hybrid;
|
||||||
|
using Svelto.ECS.Internal;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
using TechbloxModdingAPI.Common.Utils;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
|
public abstract class EcsObjectBase<TDescriptor> : EcsObjectBase where TDescriptor : IEntityDescriptor, new()
|
||||||
|
{
|
||||||
|
protected EcsObjectBase(EGID id) : base(id, typeof(TDescriptor))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EcsObjectBase(EntityReference reference) : base(reference, typeof(TDescriptor))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool RemoveEntity()
|
||||||
|
{
|
||||||
|
if (!Exists) return false;
|
||||||
|
_engine.Functions.RemoveEntity<TDescriptor>(Id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class EcsObjectBase {
|
||||||
|
public EGID Id => _engine.GetEgid(Reference);
|
||||||
|
/// <summary>
|
||||||
|
/// A reference to a specific entity that persists through group swaps and such.
|
||||||
|
/// May be an invalid reference, in that case operations do not have any effect.
|
||||||
|
/// </summary>
|
||||||
|
public EntityReference Reference { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the entity reference is still valid. Returns false if this object no longer exists.
|
||||||
|
/// </summary>
|
||||||
|
public bool Exists => Id != default; // TODO: Might need extra code to support IDs during init
|
||||||
|
|
||||||
|
public readonly Type EntityDescriptorType;
|
||||||
|
public readonly Type[] AllowedEntityComponents;
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, WeakDictionary<EntityReference, EcsObjectBase>> _instances = new();
|
||||||
|
internal static readonly EcsObjectBaseEngine _engine = new();
|
||||||
|
|
||||||
|
private static WeakDictionary<EntityReference, EcsObjectBase> GetInstances(Type type)
|
||||||
|
{
|
||||||
|
return _instances.TryGetValue(type, out var dict) ? dict : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a cached instance if there's an actively used instance of the object already.
|
||||||
|
/// Objects still get garbage collected and then they will be removed from the cache.<br />
|
||||||
|
/// <b>Only use for existing entities!</b> Use the other overload for newly created entities.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="egid">The EGID of the entity</param>
|
||||||
|
/// <param name="constructor">The constructor to construct the object</param>
|
||||||
|
/// <typeparam name="T">The object type</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static T GetInstanceExisting<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase
|
||||||
|
{
|
||||||
|
var instances = GetInstances(type ?? typeof(T));
|
||||||
|
if (instances == null || !instances.TryGetValue(_engine.GetEntityReference(egid), out var instance))
|
||||||
|
return constructor(egid); // It will be added by the constructor
|
||||||
|
return (T)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a cached instance if there's an actively used instance of the object already.
|
||||||
|
/// Objects still get garbage collected and then they will be removed from the cache.<br />
|
||||||
|
/// <b>Only use for newly created entities!</b> Use the other overload for existing entities.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="egid">The EGID of the entity</param>
|
||||||
|
/// <param name="constructor">The constructor to construct the object</param>
|
||||||
|
/// <typeparam name="T">The object type</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static T GetInstanceNew<T>(EcsInitData initData, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase
|
||||||
|
{
|
||||||
|
var instances = GetInstances(type ?? typeof(T));
|
||||||
|
if (instances == null || !instances.TryGetValue(initData.Reference, out var instance))
|
||||||
|
{
|
||||||
|
var ret = constructor(initData.EGID);
|
||||||
|
ret.InitData = initData;
|
||||||
|
return ret; // It will be added by the constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static V CreateEntity<U, V>(EGID egid, Func<EGID, V> constructor, Type type = null) where U : IEntityDescriptor, new() where V : EcsObjectBase<U>
|
||||||
|
{
|
||||||
|
return GetInstanceNew(_engine.Factory.BuildEntity<U>(egid), constructor, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EcsObjectBase(EGID id, Type entityDescriptorType) : this(_engine.GetEntityReference(id), entityDescriptorType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EcsObjectBase(EntityReference reference, Type entityDescriptorType)
|
||||||
|
{
|
||||||
|
if (!_instances.TryGetValue(GetType(), out var dict))
|
||||||
|
{
|
||||||
|
dict = new();
|
||||||
|
_instances.Add(GetType(), dict);
|
||||||
|
}
|
||||||
|
if (!dict.ContainsKey(reference)) // Multiple instances may be created
|
||||||
|
dict.Add(reference, this);
|
||||||
|
Reference = reference;
|
||||||
|
EntityDescriptorType = entityDescriptorType;
|
||||||
|
AllowedEntityComponents = EcsUtils.GetValidEntityComponents(entityDescriptorType);
|
||||||
|
// Remove init data once the entity gets submitted so that it won't be used again once the entity is removed
|
||||||
|
if (InitData != default) _engine.TrackNewEntity(this, obj => obj.InitData = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal OptionalRef<T> GetComponentOptional<T>() where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
return _engine.GetComponentOptional<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal ref T GetComponent<T>() where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
return ref _engine.GetComponent<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal ref T GetViewComponent<T>() where T : struct, IEntityViewComponent
|
||||||
|
{
|
||||||
|
return ref _engine.GetViewComponent<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal object GetComponent(Type type, string name)
|
||||||
|
{
|
||||||
|
return _engine.GetComponent(this, type, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal void SetComponent(Type type, string name, object value)
|
||||||
|
{
|
||||||
|
_engine.SetComponent(this, type, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region ECS initializer stuff
|
||||||
|
|
||||||
|
internal EcsInitData InitData { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds information needed to construct a component initializer.
|
||||||
|
/// Necessary because the initializer is a ref struct which cannot be assigned to a field.
|
||||||
|
/// </summary>
|
||||||
|
protected internal readonly record struct EcsInitData(FasterDictionary<RefWrapperType, ITypeSafeDictionary> group, EntityReference Reference, EGID EGID)
|
||||||
|
{
|
||||||
|
public static implicit operator EcsInitData(EntityInitializer initializer) => new(GetInitGroup(initializer), initializer.reference, initializer.EGID);
|
||||||
|
|
||||||
|
private readonly FasterDictionary<RefWrapperType, ITypeSafeDictionary> group = group;
|
||||||
|
public readonly EntityReference Reference = Reference;
|
||||||
|
public readonly EGID EGID = EGID;
|
||||||
|
public EntityInitializer Initializer(EGID id = default) => new(id == default ? EGID : id, group, Reference);
|
||||||
|
public bool Valid => group != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc(
|
||||||
|
EntityInitializer initializer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accesses the group field of the initializer
|
||||||
|
/// </summary>
|
||||||
|
private static readonly GetInitGroupFunc GetInitGroup = Reflections.CreateAccessor<GetInitGroupFunc>("_group");
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
EngineManager.AddEngine(_engine, ApiEngineType.Build, ApiEngineType.Menu, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
|
}
|
||||||
|
}
|
95
TechbloxModdingAPI/Common/EcsObjectBaseEngine.cs
Normal file
95
TechbloxModdingAPI/Common/EcsObjectBaseEngine.cs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using HarmonyLib;
|
||||||
|
using RobocraftX.Schedulers;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using Svelto.ECS.Hybrid;
|
||||||
|
using Svelto.Tasks;
|
||||||
|
using Svelto.Tasks.Lean;
|
||||||
|
using TechbloxModdingAPI.Blocks.Engines;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
|
public class EcsObjectBaseEngine : IFactoryEngine, IFunEngine
|
||||||
|
{
|
||||||
|
public void Ready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitiesDB entitiesDB { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityReference GetEntityReference(EGID egid)
|
||||||
|
{
|
||||||
|
return entitiesDB is not null && egid != default ? egid.ToEntityReference(entitiesDB) : EntityReference.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EGID GetEgid(EntityReference reference)
|
||||||
|
{
|
||||||
|
return entitiesDB is not null && reference.ToEGID(entitiesDB, out var egid) ? egid : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalRef<T> GetComponentOptional<T>(EcsObjectBase obj) where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
return entitiesDB.QueryEntityOptional<T>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref T GetComponent<T>(EcsObjectBase obj) where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (entitiesDB.Exists<T>(obj.Id) && obj.InitData.Valid)
|
||||||
|
throw new ArgumentException("The block exists but the init data has not been removed!");
|
||||||
|
#endif
|
||||||
|
return ref entitiesDB.QueryEntityOrDefault<T>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref T GetViewComponent<T>(EcsObjectBase obj) where T : struct, IEntityViewComponent
|
||||||
|
{
|
||||||
|
return ref entitiesDB.QueryEntityOrDefault<T>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetComponent(EcsObjectBase obj, Type type, string name)
|
||||||
|
{
|
||||||
|
var opt = AccessTools.Method(typeof(NativeApiExtensions), "QueryEntityOptional",
|
||||||
|
new[] { typeof(EntitiesDB), typeof(EcsObjectBase), typeof(ExclusiveGroupStruct) }, new[] { type })
|
||||||
|
.Invoke(null, new object[] { entitiesDB, obj, null });
|
||||||
|
var str = AccessTools.Property(opt.GetType(), "Value").GetValue(opt);
|
||||||
|
return AccessTools.Field(str.GetType(), name).GetValue(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponent(EcsObjectBase obj, Type type, string name, object value)
|
||||||
|
{
|
||||||
|
var opt = AccessTools.Method(typeof(BlockEngine), "GetBlockInfoOptional", generics: new[] { type })
|
||||||
|
.Invoke(this, new object[] { obj });
|
||||||
|
var prop = AccessTools.Property(opt.GetType(), "Value");
|
||||||
|
var str = prop.GetValue(opt);
|
||||||
|
AccessTools.Field(str.GetType(), name).SetValue(str, value);
|
||||||
|
prop.SetValue(opt, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Dictionary<EcsObjectBase, Action<EcsObjectBase>> _waitingForSubmission = new();
|
||||||
|
|
||||||
|
public void TrackNewEntity(EcsObjectBase obj, Action<EcsObjectBase> done)
|
||||||
|
{
|
||||||
|
if (_waitingForSubmission.ContainsKey(obj))
|
||||||
|
throw new InvalidOperationException("Something has gone horribly wrong here");
|
||||||
|
_waitingForSubmission.Add(obj, done);
|
||||||
|
WaitUntilEntitySubmission().RunOn(ClientLean.UIScheduler); // TODO: Pick the right scheduler
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator<TaskContract> WaitUntilEntitySubmission()
|
||||||
|
{
|
||||||
|
// TODO: Get the scheduler instance based on the engine (inject in engine manager)
|
||||||
|
yield return new WaitForSubmissionEnumerator(FullGameFields._mainGameEnginesRoot.scheduler).Continue();
|
||||||
|
foreach (var (obj, done) in _waitingForSubmission) done(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEntityFactory Factory { get; set; }
|
||||||
|
public IEntityFunctions Functions { get; set; }
|
||||||
|
}
|
21
TechbloxModdingAPI/Common/Engines/ApiEngineType.cs
Normal file
21
TechbloxModdingAPI/Common/Engines/ApiEngineType.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
public enum ApiEngineType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets created and registered when loading the game and stays loaded until it's quit. Intended for menu changes.
|
||||||
|
/// </summary>
|
||||||
|
Menu,
|
||||||
|
/// <summary>
|
||||||
|
/// Gets created and registered when entering build mode.
|
||||||
|
/// </summary>
|
||||||
|
Build,
|
||||||
|
/// <summary>
|
||||||
|
/// Gets created and registered on the client's side when starting simulation (test or not).
|
||||||
|
/// </summary>
|
||||||
|
PlayClient,
|
||||||
|
/// <summary>
|
||||||
|
/// Gets created and registered on the server's side when starting simulation (test or not).
|
||||||
|
/// </summary>
|
||||||
|
PlayServer
|
||||||
|
}
|
54
TechbloxModdingAPI/Common/Engines/EngineManager.cs
Normal file
54
TechbloxModdingAPI/Common/Engines/EngineManager.cs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using RobocraftX.StateSync;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
public class EngineManager
|
||||||
|
{
|
||||||
|
private static Dictionary<ApiEngineType, List<IApiEngine>> _engines = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register an engine to a given game state and type. Or multiple.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="engine">The engine</param>
|
||||||
|
/// <param name="types">The types to register to</param>
|
||||||
|
public static void AddEngine(IApiEngine engine, params ApiEngineType[] types)
|
||||||
|
{
|
||||||
|
if (types.Length == 0)
|
||||||
|
Logging.LogWarning($"Engine {engine.GetType().FullName} added without any types! This doesn't do anything.");
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
if (!_engines.ContainsKey(type))
|
||||||
|
_engines.Add(type, new List<IApiEngine>());
|
||||||
|
_engines[type].Add(engine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterEngines(StateSyncRegistrationHelper helper, EnginesRoot enginesRoot, ApiEngineType type)
|
||||||
|
{
|
||||||
|
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||||
|
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
||||||
|
foreach (var engine in _engines[type])
|
||||||
|
{
|
||||||
|
string name = engine is INamedApiEngine namedEngine ? namedEngine.Name : engine.ToString();
|
||||||
|
Logging.MetaDebugLog($"Registering {type} IApiEngine {name}");
|
||||||
|
if (engine is IDeterministicEngine detEngine)
|
||||||
|
if (helper is not null) helper.AddDeterministicEngine(detEngine);
|
||||||
|
else throw new InvalidOperationException($"Attempting to add deterministic engine to non-deterministic state {type}");
|
||||||
|
else
|
||||||
|
enginesRoot.AddEngine(engine);
|
||||||
|
switch (engine)
|
||||||
|
{
|
||||||
|
case IFactoryEngine factEngine:
|
||||||
|
factEngine.Factory = factory;
|
||||||
|
break;
|
||||||
|
case IFunEngine funEngine:
|
||||||
|
funEngine.Functions = functions;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,22 +5,21 @@ using RobocraftX.CR.MainGame;
|
||||||
using RobocraftX.FrontEnd;
|
using RobocraftX.FrontEnd;
|
||||||
using RobocraftX.StateSync;
|
using RobocraftX.StateSync;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Schedulers;
|
|
||||||
using TechbloxModdingAPI.Commands;
|
using TechbloxModdingAPI.Commands;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using GameState = TechbloxModdingAPI.Client.App.GameState;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Engines
|
namespace TechbloxModdingAPI.Common.Engines
|
||||||
{
|
{
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
static class GameLoadedTimeStoppedEnginePatch
|
static class GameLoadedTimeStoppedEnginePatch
|
||||||
{
|
{
|
||||||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
||||||
{
|
{
|
||||||
|
Client.App.GameClient.CurrentState = GameState.InMachineEditor; // TODO: World editor
|
||||||
// register all game engines, including deterministic
|
// register all game engines, including deterministic
|
||||||
GameEngineManager.RegisterEngines(stateSyncReg);
|
EngineManager.RegisterEngines(stateSyncReg, stateSyncReg.enginesRoot, ApiEngineType.Build);
|
||||||
// register command engines
|
// register command engines
|
||||||
/*CommandLineCompositionRoot.Compose(contextHolder, stateSyncReg.enginesRoot, reloadGame, multiplayerParameters,
|
|
||||||
stateSyncReg); - uREPL C# compilation not supported anymore */
|
|
||||||
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
|
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +34,8 @@ namespace TechbloxModdingAPI.Engines
|
||||||
{
|
{
|
||||||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
||||||
{
|
{
|
||||||
GameEngineManager.RegisterEngines(stateSyncReg);
|
Client.App.GameClient.CurrentState = GameState.InTestMode; // TODO: Client/server
|
||||||
|
EngineManager.RegisterEngines(stateSyncReg, stateSyncReg.enginesRoot, ApiEngineType.PlayClient); // TODO: Client/server
|
||||||
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
|
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,21 +49,36 @@ namespace TechbloxModdingAPI.Engines
|
||||||
static class GameReloadedPatch
|
static class GameReloadedPatch
|
||||||
{
|
{
|
||||||
internal static bool IsReload;
|
internal static bool IsReload;
|
||||||
public static void Prefix() => IsReload = true;
|
public static void Prefix()
|
||||||
|
{
|
||||||
|
IsReload = true;
|
||||||
|
Client.App.GameClient.CurrentState = GameState.Loading;
|
||||||
|
}
|
||||||
|
|
||||||
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "ReloadGame");
|
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "ReloadGame");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
static class GameSwitchedToPatch
|
static class GameSwitchedToPatch
|
||||||
{
|
{
|
||||||
public static void Prefix() => GameReloadedPatch.IsReload = false;
|
public static void Prefix()
|
||||||
|
{
|
||||||
|
GameReloadedPatch.IsReload = false;
|
||||||
|
Client.App.GameClient.CurrentState = GameState.Loading;
|
||||||
|
}
|
||||||
|
|
||||||
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame");
|
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPatch]
|
[HarmonyPatch]
|
||||||
static class MenuSwitchedToPatch
|
static class MenuSwitchedToPatch
|
||||||
{
|
{
|
||||||
public static void Prefix() => GameReloadedPatch.IsReload = false;
|
public static void Prefix()
|
||||||
|
{
|
||||||
|
GameReloadedPatch.IsReload = false;
|
||||||
|
Client.App.GameClient.CurrentState = GameState.Loading;
|
||||||
|
}
|
||||||
|
|
||||||
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu");
|
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +87,9 @@ namespace TechbloxModdingAPI.Engines
|
||||||
{
|
{
|
||||||
public static void Postfix(EnginesRoot enginesRoot)
|
public static void Postfix(EnginesRoot enginesRoot)
|
||||||
{
|
{
|
||||||
|
Client.App.GameClient.CurrentState = GameState.InMenu; // TODO: Loaded states
|
||||||
// register menu engines
|
// register menu engines
|
||||||
MenuEngineManager.RegisterEngines(enginesRoot);
|
EngineManager.RegisterEngines(null, enginesRoot, ApiEngineType.Menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodBase TargetMethod()
|
public static MethodBase TargetMethod()
|
||||||
|
@ -87,6 +103,7 @@ namespace TechbloxModdingAPI.Engines
|
||||||
{
|
{
|
||||||
public static void Postfix(FullGameCompositionRoot __instance)
|
public static void Postfix(FullGameCompositionRoot __instance)
|
||||||
{
|
{
|
||||||
|
Client.App.GameClient.CurrentState = GameState.Loading;
|
||||||
FullGameFields.Init(__instance);
|
FullGameFields.Init(__instance);
|
||||||
}
|
}
|
||||||
|
|
11
TechbloxModdingAPI/Common/Engines/IApiEngine.cs
Normal file
11
TechbloxModdingAPI/Common/Engines/IApiEngine.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base engine interface used by all TechbloxModdingAPI engines
|
||||||
|
/// </summary>
|
||||||
|
public interface IApiEngine : IQueryingEntitiesEngine, IDisposable
|
||||||
|
{
|
||||||
|
}
|
15
TechbloxModdingAPI/Common/Engines/IFactoryEngine.cs
Normal file
15
TechbloxModdingAPI/Common/Engines/IFactoryEngine.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Engine interface to create entities using the given Factory.
|
||||||
|
/// </summary>
|
||||||
|
public interface IFactoryEngine : IApiEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The EntityFactory for the entitiesDB.
|
||||||
|
/// Use this to create entities in ECS.
|
||||||
|
/// </summary>
|
||||||
|
IEntityFactory Factory { set; }
|
||||||
|
}
|
11
TechbloxModdingAPI/Common/Engines/IFunEngine.cs
Normal file
11
TechbloxModdingAPI/Common/Engines/IFunEngine.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Engine interface to use entity functions to remove entities or swap their groups.
|
||||||
|
/// </summary>
|
||||||
|
public interface IFunEngine : IApiEngine
|
||||||
|
{
|
||||||
|
public IEntityFunctions Functions { set; }
|
||||||
|
}
|
9
TechbloxModdingAPI/Common/Engines/INamedApiEngine.cs
Normal file
9
TechbloxModdingAPI/Common/Engines/INamedApiEngine.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
public interface INamedApiEngine : IApiEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the engine
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
}
|
12
TechbloxModdingAPI/Common/Engines/IReactionaryEngine.cs
Normal file
12
TechbloxModdingAPI/Common/Engines/IReactionaryEngine.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Engines
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Engine interface to react on an entity component being added or removed.
|
||||||
|
/// </summary>
|
||||||
|
public interface IReactionaryEngine<T> : IApiEngine, IReactOnAddAndRemove<T> where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
21
TechbloxModdingAPI/Common/Traits/HasPhysics.cs
Normal file
21
TechbloxModdingAPI/Common/Traits/HasPhysics.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using RobocraftX.DOTS;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using Unity.Entities;
|
||||||
|
using Unity.Transforms;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Traits;
|
||||||
|
|
||||||
|
public interface IHasPhysics
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HasPhysicsExtensions
|
||||||
|
{
|
||||||
|
internal static void UpdatePhysicsUECSComponent<T, O>(this O obj, T componentData)
|
||||||
|
where O : EcsObjectBase, IHasPhysics where T : struct, IComponentData
|
||||||
|
{
|
||||||
|
var phyStruct = obj.GetComponentOptional<DOTSPhysicsEntityStruct>();
|
||||||
|
if (phyStruct) //It exists
|
||||||
|
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity, componentData);
|
||||||
|
}
|
||||||
|
}
|
38
TechbloxModdingAPI/Common/Utils/AsyncUtils.cs
Normal file
38
TechbloxModdingAPI/Common/Utils/AsyncUtils.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using RobocraftX.Schedulers;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using Techblox.Server.Schedulers;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Utils;
|
||||||
|
|
||||||
|
public static class AsyncUtils
|
||||||
|
{
|
||||||
|
private static AsyncUtilsEngine gameEngine = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for entity submission asynchronously.
|
||||||
|
/// Use after placing a block or otherwise creating things in the game to access their properties.
|
||||||
|
/// </summary>
|
||||||
|
public static async Task WaitForSubmission()
|
||||||
|
{
|
||||||
|
await gameEngine.WaitForSubmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task WaitForNextFrame()
|
||||||
|
{
|
||||||
|
await gameEngine.WaitForNextFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Setup(EnginesRoot enginesRoot, bool clientside)
|
||||||
|
{
|
||||||
|
gameEngine.Setup(enginesRoot,
|
||||||
|
clientside ? ClientExtraLean.UIScheduler : ServerExtraLean.DeterministicTimeRunningStepRunner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
EngineManager.AddEngine(gameEngine, ApiEngineType.Build, ApiEngineType.Menu, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
|
}
|
||||||
|
}
|
56
TechbloxModdingAPI/Common/Utils/AsyncUtilsEngine.cs
Normal file
56
TechbloxModdingAPI/Common/Utils/AsyncUtilsEngine.cs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using Svelto.Tasks;
|
||||||
|
using Svelto.Tasks.ExtraLean;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Utils;
|
||||||
|
|
||||||
|
public class AsyncUtilsEngine : IApiEngine
|
||||||
|
{
|
||||||
|
private EnginesRoot _enginesRoot;
|
||||||
|
private IRunner<ExtraLeanSveltoTask<IEnumerator>> _runner;
|
||||||
|
private IEnumerator WaitForSubmissionInternal(TaskCompletionSource<object> task)
|
||||||
|
{
|
||||||
|
var waitEnumerator = new WaitForSubmissionEnumerator(_enginesRoot.scheduler);
|
||||||
|
while (waitEnumerator.MoveNext())
|
||||||
|
yield return null;
|
||||||
|
task.SetResult(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator WaitForNextFrameInternal(TaskCompletionSource<object> task)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
task.SetResult(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WaitForSubmission()
|
||||||
|
{
|
||||||
|
var task = new TaskCompletionSource<object>();
|
||||||
|
WaitForSubmissionInternal(task).RunOn(_runner);
|
||||||
|
return task.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WaitForNextFrame()
|
||||||
|
{
|
||||||
|
var task = new TaskCompletionSource<object>();
|
||||||
|
WaitForNextFrameInternal(task).RunOn(_runner);
|
||||||
|
return task.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Setup(EnginesRoot enginesRoot, IRunner<ExtraLeanSveltoTask<IEnumerator>> runner)
|
||||||
|
{ // TODO: Different engines roots for different sides
|
||||||
|
_enginesRoot = enginesRoot;
|
||||||
|
_runner = runner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Ready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitiesDB entitiesDB { get; set; }
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
23
TechbloxModdingAPI/Common/Utils/EcsUtils.cs
Normal file
23
TechbloxModdingAPI/Common/Utils/EcsUtils.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Utils
|
||||||
|
{
|
||||||
|
public static class EcsUtils
|
||||||
|
{
|
||||||
|
public static Type[] GetValidEntityComponents(Type entityDescriptorType)
|
||||||
|
{
|
||||||
|
// TODO: Cache
|
||||||
|
var templateType = typeof(EntityDescriptorTemplate<>).MakeGenericType(entityDescriptorType);
|
||||||
|
var templateDescriptor = AccessTools.Property(templateType, "descriptor");
|
||||||
|
var getDescriptorExpr = Expression.MakeMemberAccess(null, templateDescriptor ?? throw new InvalidOperationException());
|
||||||
|
var getTemplateDescriptorExpr = Expression.Lambda<Func<IEntityDescriptor>>(getDescriptorExpr);
|
||||||
|
var getTemplateDescriptor = getTemplateDescriptorExpr.Compile();
|
||||||
|
var builders = getTemplateDescriptor().componentsToBuild;
|
||||||
|
return builders.Select(builder => builder.GetEntityComponentType()).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
TechbloxModdingAPI/Common/Utils/Reflections.cs
Normal file
45
TechbloxModdingAPI/Common/Utils/Reflections.cs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Common.Utils;
|
||||||
|
|
||||||
|
public static class Reflections
|
||||||
|
{
|
||||||
|
//https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection
|
||||||
|
public static TDelegate CreateAccessor<TDelegate>(string memberName, Type thisType = null) where TDelegate : Delegate
|
||||||
|
{
|
||||||
|
return CreateSomeCall<TDelegate>(memberName, thisType, (objParam, _) => Expression.PropertyOrField(objParam, memberName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TDelegate CreateMethodCall<TDelegate>(MethodInfo method, Type thisType = null) where TDelegate : Delegate
|
||||||
|
{
|
||||||
|
return CreateSomeCall<TDelegate>(method.Name, thisType, (objParam, parameters) => Expression.Call(objParam, method, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TDelegate CreateSomeCall<TDelegate>(string memberName, Type thisType, Func<ParameterExpression, ParameterExpression[], Expression> memberExpressionGetter) 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 = thisType ?? delegateParameters[0].ParameterType;
|
||||||
|
|
||||||
|
var objParam = Expression.Parameter(paramType, "obj");
|
||||||
|
var otherParams = delegateParameters.Skip(1)
|
||||||
|
.Select(pinfo => Expression.Parameter(pinfo.ParameterType, pinfo.Name)).ToArray();
|
||||||
|
var memberExpr = memberExpressionGetter(objParam, otherParams);
|
||||||
|
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 }.Concat(otherParams));
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,128 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using Svelto.DataStructures;
|
|
||||||
using Svelto.ECS;
|
|
||||||
using Svelto.ECS.Internal;
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
|
||||||
{
|
|
||||||
public abstract class EcsObjectBase
|
|
||||||
{
|
|
||||||
public EGID Id { get; }
|
|
||||||
|
|
||||||
private static readonly Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>> _instances =
|
|
||||||
new Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>>();
|
|
||||||
|
|
||||||
private static readonly WeakDictionary<EGID, EcsObjectBase> _noInstance =
|
|
||||||
new WeakDictionary<EGID, EcsObjectBase>();
|
|
||||||
|
|
||||||
internal static WeakDictionary<EGID, EcsObjectBase> GetInstances(Type type)
|
|
||||||
{
|
|
||||||
return _instances.TryGetValue(type, out var dict) ? dict : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a cached instance if there's an actively used instance of the object already.
|
|
||||||
/// Objects still get garbage collected and then they will be removed from the cache.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="egid">The EGID of the entity</param>
|
|
||||||
/// <param name="constructor">The constructor to construct the object</param>
|
|
||||||
/// <typeparam name="T">The object type</typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
internal static T GetInstance<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase
|
|
||||||
{
|
|
||||||
var instances = GetInstances(type ?? typeof(T));
|
|
||||||
if (instances == null || !instances.TryGetValue(egid, out var instance))
|
|
||||||
return constructor(egid); // It will be added by the constructor
|
|
||||||
return (T)instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EcsObjectBase(EGID id)
|
|
||||||
{
|
|
||||||
if (!_instances.TryGetValue(GetType(), out var dict))
|
|
||||||
{
|
|
||||||
dict = new WeakDictionary<EGID, EcsObjectBase>();
|
|
||||||
_instances.Add(GetType(), dict);
|
|
||||||
}
|
|
||||||
if (!dict.ContainsKey(id)) // Multiple instances may be created
|
|
||||||
dict.Add(id, this);
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EcsObjectBase(Func<EcsObjectBase, EGID> initializer)
|
|
||||||
{
|
|
||||||
if (!_instances.TryGetValue(GetType(), out var dict))
|
|
||||||
{
|
|
||||||
dict = new WeakDictionary<EGID, EcsObjectBase>();
|
|
||||||
_instances.Add(GetType(), dict);
|
|
||||||
}
|
|
||||||
|
|
||||||
var id = initializer(this);
|
|
||||||
if (!dict.ContainsKey(id)) // Multiple instances may be created
|
|
||||||
dict.Add(id, this);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logging.MetaDebugLog($"An object of this type and ID is already stored: {GetType()} - {id}");
|
|
||||||
Logging.MetaDebugLog(this);
|
|
||||||
Logging.MetaDebugLog(dict[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ECS initializer stuff
|
|
||||||
|
|
||||||
protected internal EcsInitData InitData;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Holds information needed to construct a component initializer
|
|
||||||
/// </summary>
|
|
||||||
protected internal struct EcsInitData
|
|
||||||
{
|
|
||||||
private FasterDictionary<RefWrapperType, ITypeSafeDictionary> group;
|
|
||||||
private EntityReference reference;
|
|
||||||
|
|
||||||
public static implicit operator EcsInitData(EntityInitializer initializer) => new EcsInitData
|
|
||||||
{ group = GetInitGroup(initializer), reference = initializer.reference };
|
|
||||||
|
|
||||||
public EntityInitializer Initializer(EGID id) => new EntityInitializer(id, group, reference);
|
|
||||||
public bool Valid => group != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc(
|
|
||||||
EntityInitializer initializer);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Accesses the group field of the initializer
|
|
||||||
/// </summary>
|
|
||||||
private static GetInitGroupFunc GetInitGroup = CreateAccessor<GetInitGroupFunc>("_group");
|
|
||||||
|
|
||||||
//https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection
|
|
||||||
private 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Svelto.ECS;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Engines
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base engine interface used by all TechbloxModdingAPI engines
|
|
||||||
/// </summary>
|
|
||||||
public interface IApiEngine : IEngine, IQueryingEntitiesEngine, IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the engine
|
|
||||||
/// </summary>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the emitter can be removed with Manager.RemoveEventEmitter(name)
|
|
||||||
/// </summary>
|
|
||||||
bool isRemovable { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Svelto.ECS;
|
|
||||||
|
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Engines
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Engine interface to create a ModEventEntityStruct in entitiesDB when Emit() is called.
|
|
||||||
/// </summary>
|
|
||||||
public interface IFactoryEngine : IApiEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The EntityFactory for the entitiesDB.
|
|
||||||
/// Use this to create a ModEventEntityStruct when Emit() is called.
|
|
||||||
/// </summary>
|
|
||||||
IEntityFactory Factory { set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
using Svelto.ECS;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Engines
|
|
||||||
{
|
|
||||||
public interface IFunEngine : IApiEngine
|
|
||||||
{
|
|
||||||
public IEntityFunctions Functions { set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Svelto.ECS;
|
|
||||||
using Svelto.ECS.Internal;
|
|
||||||
|
|
||||||
using TechbloxModdingAPI.Events;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Engines
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines.
|
|
||||||
/// </summary>
|
|
||||||
public interface IReactionaryEngine<T> : IApiEngine, IReactOnAddAndRemove<T> where T : unmanaged, IEntityComponent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,14 @@
|
||||||
using RobocraftX.Common.Input;
|
using RobocraftX.Common.Input;
|
||||||
|
|
||||||
using TechbloxModdingAPI.App;
|
using TechbloxModdingAPI.App;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Input
|
namespace TechbloxModdingAPI.Input
|
||||||
{
|
{
|
||||||
public static class FakeInput
|
public static class FakeInput
|
||||||
{
|
{
|
||||||
internal static readonly FakeInputEngine inputEngine = new FakeInputEngine();
|
internal static readonly FakeInputEngine inputEngine = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Customize the local input.
|
/// Customize the local input.
|
||||||
|
@ -138,8 +139,7 @@ namespace TechbloxModdingAPI.Input
|
||||||
|
|
||||||
public static void Init()
|
public static void Init()
|
||||||
{
|
{
|
||||||
GameEngineManager.AddGameEngine(inputEngine);
|
EngineManager.AddEngine(inputEngine, ApiEngineType.Build, ApiEngineType.Menu, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
MenuEngineManager.AddMenuEngine(inputEngine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
using System;
|
using RobocraftX.Common;
|
||||||
|
|
||||||
using RobocraftX.Common;
|
|
||||||
using RobocraftX.Common.Input;
|
using RobocraftX.Common.Input;
|
||||||
using RobocraftX.Players;
|
using RobocraftX.Players;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Input
|
namespace TechbloxModdingAPI.Input
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace TechbloxModdingAPI.Interface.IMGUI
|
||||||
{
|
{
|
||||||
private bool automaticLayout;
|
private bool automaticLayout;
|
||||||
|
|
||||||
private FasterList<UIElement> elements = new FasterList<UIElement>();
|
private FasterList<UIElement> elements = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The rectangular area in the window that the UI group can use
|
/// The rectangular area in the window that the UI group can use
|
||||||
|
|
|
@ -8,6 +8,7 @@ using Svelto.Context;
|
||||||
|
|
||||||
using TechbloxModdingAPI.App;
|
using TechbloxModdingAPI.App;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
@ -56,21 +57,18 @@ namespace TechbloxModdingAPI
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// init utility
|
|
||||||
Logging.MetaDebugLog($"Initializing Utility");
|
|
||||||
Utility.GameState.Init();
|
|
||||||
// init block implementors
|
// init block implementors
|
||||||
Logging.MetaDebugLog($"Initializing Blocks");
|
Logging.MetaDebugLog($"Initializing Blocks");
|
||||||
// init input
|
// init input
|
||||||
Input.FakeInput.Init();
|
Input.FakeInput.Init();
|
||||||
// init object-oriented classes
|
// init object-oriented classes
|
||||||
|
EcsObjectBase.Init();
|
||||||
Player.Init();
|
Player.Init();
|
||||||
Block.Init();
|
Block.Init();
|
||||||
BlockGroup.Init();
|
BlockGroup.Init();
|
||||||
Wire.Init();
|
Wire.Init();
|
||||||
// init client
|
// init client
|
||||||
Logging.MetaDebugLog($"Initializing Client");
|
Logging.MetaDebugLog($"Initializing Client");
|
||||||
Client.Init();
|
|
||||||
Game.Init();
|
Game.Init();
|
||||||
// init UI
|
// init UI
|
||||||
Logging.MetaDebugLog($"Initializing UI");
|
Logging.MetaDebugLog($"Initializing UI");
|
||||||
|
|
|
@ -15,9 +15,9 @@ namespace TechbloxModdingAPI.Persistence
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class SerializerManager
|
public static class SerializerManager
|
||||||
{
|
{
|
||||||
private static Dictionary<string, IEntitySerializer> _serializers = new Dictionary<string, IEntitySerializer>();
|
private static Dictionary<string, IEntitySerializer> _serializers = new();
|
||||||
|
|
||||||
private static Dictionary<string, Action<IEntitySerialization>> _registrations = new Dictionary<string, Action<IEntitySerialization>>();
|
private static Dictionary<string, Action<IEntitySerialization>> _registrations = new();
|
||||||
|
|
||||||
private static EnginesRoot _lastEnginesRoot;
|
private static EnginesRoot _lastEnginesRoot;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using Svelto.ECS;
|
||||||
|
|
||||||
using Svelto.ECS;
|
|
||||||
using Svelto.ECS.Serialization;
|
using Svelto.ECS.Serialization;
|
||||||
|
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
|
|
|
@ -12,8 +12,10 @@ using Techblox.BuildingDrone;
|
||||||
using Techblox.Camera;
|
using Techblox.Camera;
|
||||||
using Techblox.Character;
|
using Techblox.Character;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
|
using TechbloxModdingAPI.Client.App;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Players;
|
using TechbloxModdingAPI.Players;
|
||||||
using TechbloxModdingAPI.Utility;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
|
@ -24,8 +26,8 @@ namespace TechbloxModdingAPI
|
||||||
public partial class Player : EcsObjectBase, IEquatable<Player>, IEquatable<EGID>
|
public partial class Player : EcsObjectBase, IEquatable<Player>, IEquatable<EGID>
|
||||||
{
|
{
|
||||||
// static functionality
|
// static functionality
|
||||||
private static readonly PlayerEngine playerEngine = new PlayerEngine();
|
private static readonly PlayerEngine playerEngine = new();
|
||||||
private static readonly PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine();
|
private static readonly PlayerEventsEngine playerEventsEngine = new();
|
||||||
private static Player localPlayer;
|
private static Player localPlayer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -38,7 +40,7 @@ namespace TechbloxModdingAPI
|
||||||
switch (player)
|
switch (player)
|
||||||
{
|
{
|
||||||
case PlayerType.Remote:
|
case PlayerType.Remote:
|
||||||
return playerEngine.GetRemotePlayer() != uint.MaxValue;
|
return playerEngine.GetRemotePlayers().Length > 0;
|
||||||
case PlayerType.Local:
|
case PlayerType.Local:
|
||||||
return playerEngine.GetLocalPlayer() != uint.MaxValue;
|
return playerEngine.GetLocalPlayer() != uint.MaxValue;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +84,7 @@ namespace TechbloxModdingAPI
|
||||||
|
|
||||||
internal static Player GetInstance(uint id)
|
internal static Player GetInstance(uint id)
|
||||||
{
|
{
|
||||||
return EcsObjectBase.GetInstance(new EGID(id, CharacterExclusiveGroups.OnFootGroup),
|
return EcsObjectBase.GetInstanceExisting(new EGID(id, CharacterExclusiveGroups.OnFootGroup),
|
||||||
e => new Player(e.entityID));
|
e => new Player(e.entityID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ namespace TechbloxModdingAPI
|
||||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The player's unique identifier.</param>
|
/// <param name="id">The player's unique identifier.</param>
|
||||||
public Player(uint id) : base(new EGID(id, CharacterExclusiveGroups.OnFootGroup))
|
public Player(uint id) : base(new EGID(id, CharacterExclusiveGroups.OnFootGroup), typeof(CharacterEntityDescriptor))
|
||||||
{
|
{
|
||||||
this.Id = id;
|
this.Id = id;
|
||||||
if (!Exists(id))
|
if (!Exists(id))
|
||||||
|
@ -100,38 +102,6 @@ namespace TechbloxModdingAPI
|
||||||
this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
|
this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
|
|
||||||
public Player(PlayerType player) : base(ecs =>
|
|
||||||
{
|
|
||||||
uint id;
|
|
||||||
switch (player)
|
|
||||||
{
|
|
||||||
case PlayerType.Local:
|
|
||||||
id = playerEngine.GetLocalPlayer();
|
|
||||||
break;
|
|
||||||
case PlayerType.Remote:
|
|
||||||
id = playerEngine.GetRemotePlayer();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
id = uint.MaxValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == uint.MaxValue)
|
|
||||||
{
|
|
||||||
throw new PlayerNotFoundException($"No player of {player} type exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new EGID(id, CharacterExclusiveGroups.OnFootGroup);
|
|
||||||
})
|
|
||||||
{
|
|
||||||
this.Type = player;
|
|
||||||
Id = base.Id.entityID;
|
|
||||||
}
|
|
||||||
|
|
||||||
// object fields & properties
|
// object fields & properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -163,10 +133,10 @@ namespace TechbloxModdingAPI
|
||||||
/// <value>The rotation.</value>
|
/// <value>The rotation.</value>
|
||||||
public float3 Rotation
|
public float3 Rotation
|
||||||
{
|
{
|
||||||
get => ((Quaternion) (GameState.IsBuildMode()
|
get => ((Quaternion) (GameClient.IsBuildMode
|
||||||
? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation
|
? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation
|
||||||
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation)).eulerAngles;
|
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation)).eulerAngles;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameClient.IsBuildMode
|
||||||
? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation = quaternion.Euler(value)
|
? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation = quaternion.Euler(value)
|
||||||
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation = quaternion.Euler(value);
|
: playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation = quaternion.Euler(value);
|
||||||
}
|
}
|
||||||
|
@ -376,10 +346,10 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Sprinting
|
public bool Sprinting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting;
|
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting = value;
|
||||||
}
|
}
|
||||||
|
@ -389,10 +359,10 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float SpeedSetting
|
public float SpeedSetting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed = value;
|
||||||
}
|
}
|
||||||
|
@ -402,10 +372,10 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float SpeedSprintMultiplierSetting
|
public float SpeedSprintMultiplierSetting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier = value;
|
||||||
}
|
}
|
||||||
|
@ -415,10 +385,10 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float AccelerationSetting
|
public float AccelerationSetting
|
||||||
{
|
{
|
||||||
get => GameState.IsBuildMode()
|
get => GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration;
|
||||||
set => _ = GameState.IsBuildMode()
|
set => _ = GameClient.IsBuildMode
|
||||||
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration = value
|
? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration = value
|
||||||
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration = value;
|
: playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration = value;
|
||||||
}
|
}
|
||||||
|
@ -499,7 +469,7 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||||
return egid != default && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
return egid != default && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||||
? EcsObjectBase.GetInstance(egid, e => new SimBody(e))
|
? EcsObjectBase.GetInstanceExisting(egid, e => new SimBody(e))
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +482,7 @@ namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||||
return egid != default && egid.groupID == WiresGUIExclusiveGroups.WireGroup
|
return egid != default && egid.groupID == WiresGUIExclusiveGroups.WireGroup
|
||||||
? EcsObjectBase.GetInstance(new EGID(egid.entityID, BuildModeWiresGroups.WiresGroup.Group),
|
? EcsObjectBase.GetInstanceExisting(new EGID(egid.entityID, BuildModeWiresGroups.WiresGroup.Group),
|
||||||
e => new Wire(e))
|
e => new Wire(e))
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
@ -571,8 +541,9 @@ namespace TechbloxModdingAPI
|
||||||
|
|
||||||
internal static void Init()
|
internal static void Init()
|
||||||
{
|
{
|
||||||
Utility.GameEngineManager.AddGameEngine(playerEngine);
|
// TODO: Separate build mode, client and server into separate classes
|
||||||
Utility.GameEngineManager.AddGameEngine(playerEventsEngine);
|
EngineManager.AddEngine(playerEngine, ApiEngineType.Build, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
|
EngineManager.AddEngine(playerEventsEngine, ApiEngineType.Build, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,10 @@ using RobocraftX.SimulationModeState;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Techblox.Camera;
|
using Techblox.Camera;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using Svelto.ECS.DataStructures;
|
|
||||||
using Techblox.BuildingDrone;
|
using Techblox.BuildingDrone;
|
||||||
using Techblox.Character;
|
using Techblox.Character;
|
||||||
|
using TechbloxModdingAPI.Client.App;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Input;
|
using TechbloxModdingAPI.Input;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
using TechbloxModdingAPI.Utility.ECS;
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
@ -58,15 +57,17 @@ namespace TechbloxModdingAPI.Players
|
||||||
return uint.MaxValue;
|
return uint.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint GetRemotePlayer()
|
public uint[] GetRemotePlayers()
|
||||||
{
|
{
|
||||||
if (!isReady) return uint.MaxValue;
|
if (!isReady) return Array.Empty<uint>();
|
||||||
var (localPlayers, count) = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
var (localPlayers, count) = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
||||||
if (count > 0)
|
var players = new uint[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
return localPlayers[0].ID.entityID;
|
players[i] = localPlayers[i].ID.entityID;
|
||||||
}
|
}
|
||||||
return uint.MaxValue;
|
|
||||||
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetAllPlayerCount()
|
public long GetAllPlayerCount()
|
||||||
|
@ -129,7 +130,7 @@ namespace TechbloxModdingAPI.Players
|
||||||
public OptionalRef<T> GetCharacterStruct<T>(uint playerId, out ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
|
public OptionalRef<T> GetCharacterStruct<T>(uint playerId, out ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
|
||||||
{
|
{
|
||||||
group = default;
|
group = default;
|
||||||
if (GameState.IsBuildMode())
|
if (GameClient.IsBuildMode)
|
||||||
return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, LocalBuildingDrone.BuildGroup));
|
return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, LocalBuildingDrone.BuildGroup));
|
||||||
|
|
||||||
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
using System;
|
|
||||||
using RobocraftX.Character;
|
using RobocraftX.Character;
|
||||||
using RobocraftX.Character.Movement;
|
using RobocraftX.Character.Movement;
|
||||||
using RobocraftX.Common.Input;
|
using RobocraftX.Common.Input;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Players
|
namespace TechbloxModdingAPI.Players
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.App;
|
using TechbloxModdingAPI.App;
|
||||||
using TechbloxModdingAPI.Blocks;
|
using TechbloxModdingAPI.Blocks;
|
||||||
|
using TechbloxModdingAPI.Client.App;
|
||||||
using TechbloxModdingAPI.Tests;
|
using TechbloxModdingAPI.Tests;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ namespace TechbloxModdingAPI.Players
|
||||||
while (Player.LocalPlayer.State != PlayerState.InSeat)
|
while (Player.LocalPlayer.State != PlayerState.InSeat)
|
||||||
{
|
{
|
||||||
bool cont = false;
|
bool cont = false;
|
||||||
Client.Instance.PromptUser(new SingleChoicePrompt("Testing", $"Enter the seat at {seat.Position} pls", "OK", () => cont = true));
|
Popup.PromptUser(new SingleChoicePrompt("Testing", $"Enter the seat at {seat.Position} pls", "OK", () => cont = true));
|
||||||
while (!cont)
|
while (!cont)
|
||||||
yield return Yield.It;
|
yield return Yield.It;
|
||||||
yield return new WaitForSecondsEnumerator(5f).Continue();
|
yield return new WaitForSecondsEnumerator(5f).Continue();
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
using Gamecraft.Damage;
|
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using RobocraftX.Physics;
|
using RobocraftX.Physics;
|
||||||
using Techblox.TimeRunning.Clusters;
|
using Techblox.TimeRunning.Clusters;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI
|
namespace TechbloxModdingAPI
|
||||||
{
|
{
|
||||||
|
@ -21,13 +20,13 @@ namespace TechbloxModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Cluster Cluster => cluster ??= clusterId == uint.MaxValue // Return cluster or if it's null then set it
|
public Cluster Cluster => cluster ??= clusterId == uint.MaxValue // Return cluster or if it's null then set it
|
||||||
? Block.BlockEngine.GetCluster(Id.entityID) // If we don't have a clusterId set then get it from the game
|
? Block.BlockEngine.GetCluster(Id.entityID) // If we don't have a clusterId set then get it from the game
|
||||||
: GetInstance(new EGID(clusterId, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP),
|
: GetInstanceExisting(new EGID(clusterId, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP),
|
||||||
egid => new Cluster(egid)); // Otherwise get the cluster from the ID
|
egid => new Cluster(egid)); // Otherwise get the cluster from the ID
|
||||||
|
|
||||||
private Cluster cluster;
|
private Cluster cluster;
|
||||||
private readonly uint clusterId = uint.MaxValue;
|
private readonly uint clusterId = uint.MaxValue;
|
||||||
|
|
||||||
public SimBody(EGID id) : base(id)
|
public SimBody(EGID id) : base(id, typeof(ClusterEntityDescriptor))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ namespace TechbloxModdingAPI.Tasks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner extraLeanRunner = new Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner("TechbloxModdingAPIExtraLean");
|
public static readonly Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner extraLeanRunner = new("TechbloxModdingAPIExtraLean");
|
||||||
|
|
||||||
public static readonly Svelto.Tasks.Lean.Unity.UpdateMonoRunner leanRunner = new Svelto.Tasks.Lean.Unity.UpdateMonoRunner("TechbloxModdingAPILean");
|
public static readonly Svelto.Tasks.Lean.Unity.UpdateMonoRunner leanRunner = new("TechbloxModdingAPILean");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Schedule a task to run asynchronously.
|
/// Schedule a task to run asynchronously.
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
<NeutralLanguage>en-CA</NeutralLanguage>
|
<NeutralLanguage>en-CA</NeutralLanguage>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Configurations>Debug;Release;Test</Configurations>
|
||||||
|
<Platforms>AnyCPU</Platforms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace TechbloxModdingAPI.Tests
|
||||||
{
|
{
|
||||||
private static StreamWriter logFile = null;
|
private static StreamWriter logFile = null;
|
||||||
|
|
||||||
private static ConcurrentDictionary<string, string> callbacks = new ConcurrentDictionary<string, string>();
|
private static ConcurrentDictionary<string, string> callbacks = new();
|
||||||
|
|
||||||
private const string PASS = "SUCCESS: ";
|
private const string PASS = "SUCCESS: ";
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,13 @@ using Svelto.Tasks;
|
||||||
using Svelto.Tasks.Lean;
|
using Svelto.Tasks.Lean;
|
||||||
using Svelto.Tasks.Enumerators;
|
using Svelto.Tasks.Enumerators;
|
||||||
using Svelto.Tasks.Lean.Unity;
|
using Svelto.Tasks.Lean.Unity;
|
||||||
|
using TechbloxModdingAPI.Client.App;
|
||||||
|
using TechbloxModdingAPI.Client.Game;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
using TechbloxModdingAPI.App;
|
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using GameState = TechbloxModdingAPI.Client.App.GameState;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Tests
|
namespace TechbloxModdingAPI.Tests
|
||||||
{
|
{
|
||||||
|
@ -66,20 +68,34 @@ namespace TechbloxModdingAPI.Tests
|
||||||
_testsCountPassed = 0;
|
_testsCountPassed = 0;
|
||||||
_testsCountFailed = 0;
|
_testsCountFailed = 0;
|
||||||
// flow control
|
// flow control
|
||||||
Game.Enter += (sender, args) => { GameTests().RunOn(new UpdateMonoRunner("TechbloxModdingAPITestRunner")); };
|
Client.App.GameClient.StateChanged += (sender, args) =>
|
||||||
Game.Exit += (s, a) => state = "ReturningFromGame";
|
{
|
||||||
Client.EnterMenu += (sender, args) =>
|
switch (args.NewState)
|
||||||
|
{
|
||||||
|
case GameState.InMenu:
|
||||||
{
|
{
|
||||||
if (state == "EnteringMenu")
|
if (state == "EnteringMenu")
|
||||||
{
|
{
|
||||||
MenuTests().RunOn(Scheduler.leanRunner);
|
MenuTests().RunOn(Scheduler.leanRunner);
|
||||||
state = "EnteringGame";
|
state = "EnteringGame";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == "ReturningFromGame")
|
if (state == "ReturningFromGame")
|
||||||
{
|
{
|
||||||
TearDown().RunOn(Scheduler.leanRunner);
|
TearDown().RunOn(Scheduler.leanRunner);
|
||||||
state = "ShuttingDown";
|
state = "ShuttingDown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GameState.InMachineEditor:
|
||||||
|
GameTests().RunOn(new UpdateMonoRunner("TechbloxModdingAPITestRunner"));
|
||||||
|
break;
|
||||||
|
case GameState.Loading:
|
||||||
|
if (args.OldState == GameState.InTestMode)
|
||||||
|
state = "ReturningFromGame";
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// init tests here
|
// init tests here
|
||||||
foreach (Type t in testTypes)
|
foreach (Type t in testTypes)
|
||||||
|
@ -131,17 +147,16 @@ namespace TechbloxModdingAPI.Tests
|
||||||
|
|
||||||
private static IEnumerator<TaskContract> GoToGameTests()
|
private static IEnumerator<TaskContract> GoToGameTests()
|
||||||
{
|
{
|
||||||
Client app = Client.Instance;
|
|
||||||
int oldLength = 0;
|
int oldLength = 0;
|
||||||
while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
|
while (GameClient.Machines.Length == 0 || oldLength != GameClient.Machines.Length)
|
||||||
{
|
{
|
||||||
oldLength = app.MyGames.Length;
|
oldLength = GameClient.Machines.Length;
|
||||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||||
}
|
}
|
||||||
yield return Yield.It;
|
yield return Yield.It;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
app.MyGames[0].EnterGame();
|
GameClient.EnterBuildMode(new ClientEnvironment("GAMEID_Road_Track"), GameClient.Machines[0]);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -156,7 +171,6 @@ namespace TechbloxModdingAPI.Tests
|
||||||
private static IEnumerator<TaskContract> GameTests()
|
private static IEnumerator<TaskContract> GameTests()
|
||||||
{
|
{
|
||||||
yield return Yield.It;
|
yield return Yield.It;
|
||||||
Game currentGame = Game.CurrentGame();
|
|
||||||
// in-game tests
|
// in-game tests
|
||||||
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for game to finish loading
|
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for game to finish loading
|
||||||
var testTypesToRun = new[]
|
var testTypesToRun = new[]
|
||||||
|
@ -211,13 +225,9 @@ namespace TechbloxModdingAPI.Tests
|
||||||
|
|
||||||
if (index + 1 < testTypesToRun.Length) //Don't toggle on the last test
|
if (index + 1 < testTypesToRun.Length) //Don't toggle on the last test
|
||||||
{
|
{
|
||||||
bool running = currentGame.IsTimeRunning;
|
bool running = GameClient.IsSimulationMode;
|
||||||
currentGame.ToggleTimeMode();
|
if (running) yield return GameClient.EnterTestMode().Continue();
|
||||||
while (running ? !currentGame.IsTimeStopped : !currentGame.IsTimeRunning)
|
else yield return GameClient.ExitSimulationMode().Continue();
|
||||||
{
|
|
||||||
Logging.MetaLog($"Waiting for time to {(running?"stop":"start")}...");
|
|
||||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return new WaitForSecondsEnumerator(5).Continue();
|
yield return new WaitForSecondsEnumerator(5).Continue();
|
||||||
|
@ -230,7 +240,7 @@ namespace TechbloxModdingAPI.Tests
|
||||||
{
|
{
|
||||||
Logging.MetaLog("Returning to main menu");
|
Logging.MetaLog("Returning to main menu");
|
||||||
yield return Yield.It;
|
yield return Yield.It;
|
||||||
Game.CurrentGame().ExitGame();
|
yield return GameClient.ExitSimulationMode().Continue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerator<TaskContract> TearDown()
|
private static IEnumerator<TaskContract> TearDown()
|
||||||
|
|
|
@ -1,23 +1,17 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Formatting;
|
|
||||||
using TechbloxModdingAPI.Blocks;
|
|
||||||
using TechbloxModdingAPI.Players;
|
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using RobocraftX.GUI.Debug;
|
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Experimental;
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
public class DebugInterfaceEngine : IApiEngine
|
public class DebugInterfaceEngine : IApiEngine
|
||||||
{
|
{
|
||||||
private static Dictionary<string, Func<string>> _extraInfo=new Dictionary<string, Func<string>>();
|
private static Dictionary<string, Func<string>> _extraInfo=new();
|
||||||
public void Ready()
|
public void Ready()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -46,8 +40,8 @@ namespace TechbloxModdingAPI.Utility
|
||||||
int index = list.FindLastIndex(inst => inst.opcode == OpCodes.Ldfld);
|
int index = list.FindLastIndex(inst => inst.opcode == OpCodes.Ldfld);
|
||||||
var array = new CodeInstruction[]
|
var array = new CodeInstruction[]
|
||||||
{
|
{
|
||||||
new CodeInstruction(OpCodes.Ldloc_0), //StringBuffer
|
new(OpCodes.Ldloc_0), //StringBuffer
|
||||||
new CodeInstruction(OpCodes.Call, ((Action<StringBuilder>)AddInfo).Method)
|
new(OpCodes.Call, ((Action<StringBuilder>)AddInfo).Method)
|
||||||
};
|
};
|
||||||
list.InsertRange(index - 1, array); //-1: ldloc.1 ("local") before ldfld
|
list.InsertRange(index - 1, array); //-1: ldloc.1 ("local") before ldfld
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
using System.Linq;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Hybrid;
|
using Svelto.ECS.Hybrid;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility.ECS
|
namespace TechbloxModdingAPI.Utility.ECS
|
||||||
{
|
{
|
||||||
|
@ -53,7 +55,10 @@ namespace TechbloxModdingAPI.Utility.ECS
|
||||||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||||
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||||
if (opt) return ref opt.Get();
|
if (opt) return ref opt.Get();
|
||||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd<T>();
|
// If initializing the entity, check if the component is allowed by the descriptor, otherwise it could cause
|
||||||
|
// issues in the game with Add() calls running unexpectedly
|
||||||
|
if (obj.InitData.Valid && obj.AllowedEntityComponents.Contains(typeof(T)))
|
||||||
|
return ref obj.InitData.Initializer(id).GetOrAdd<T>();
|
||||||
return ref opt.Get(); //Default value
|
return ref opt.Get(); //Default value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Svelto.DataStructures;
|
using System.Linq;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.Tasks;
|
using Svelto.Tasks;
|
||||||
using Svelto.Tasks.Lean;
|
using Svelto.Tasks.Lean;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility.ECS
|
namespace TechbloxModdingAPI.Utility.ECS
|
||||||
|
@ -58,12 +59,10 @@ namespace TechbloxModdingAPI.Utility.ECS
|
||||||
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
|
||||||
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
var opt = QueryEntityOptional<T>(entitiesDB, id);
|
||||||
if (opt) return ref opt.Get();
|
if (opt) return ref opt.Get();
|
||||||
if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd<T>();
|
// If initializing the entity, check if the component is allowed by the descriptor, otherwise it could cause
|
||||||
/*if (!obj.InitData.Valid) return ref opt.Get(); //Default value
|
// issues in the game with Add() calls running unexpectedly
|
||||||
var init = obj.InitData.Initializer(id);
|
if (obj.InitData.Valid && obj.AllowedEntityComponents.Contains(typeof(T)))
|
||||||
// Do not create the component if missing, as that can trigger Add() listeners that, in some cases, may be
|
return ref obj.InitData.Initializer(id).GetOrAdd<T>();
|
||||||
// invalid if (ab)using the classes in an unusual way - TODO: Check entity descriptor or something
|
|
||||||
if (init.Has<T>()) return ref init.Get<T>();*/
|
|
||||||
return ref opt.Get(); //Default value
|
return ref opt.Get(); //Default value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using RobocraftX.StateSync;
|
|
||||||
using Svelto.ECS;
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Keeps track of custom game-modifying engines
|
|
||||||
/// </summary>
|
|
||||||
public static class GameEngineManager
|
|
||||||
{
|
|
||||||
private static Dictionary<string, IApiEngine> _gameEngines = new Dictionary<string, IApiEngine>();
|
|
||||||
|
|
||||||
private static EnginesRoot _lastEngineRoot;
|
|
||||||
|
|
||||||
public static void AddGameEngine(IApiEngine engine)
|
|
||||||
{
|
|
||||||
_gameEngines[engine.Name] = engine;
|
|
||||||
if (_lastEngineRoot != null)
|
|
||||||
{
|
|
||||||
Logging.MetaDebugLog($"Registering Game IApiEngine {engine.Name}");
|
|
||||||
_lastEngineRoot.AddEngine(engine);
|
|
||||||
if (engine is IFactoryEngine factoryEngine)
|
|
||||||
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
|
||||||
if (engine is IFunEngine funEngine)
|
|
||||||
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExistsGameEngine(string name)
|
|
||||||
{
|
|
||||||
return _gameEngines.ContainsKey(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExistsGameEngine(IApiEngine engine)
|
|
||||||
{
|
|
||||||
return ExistsGameEngine(engine.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IApiEngine GetGameEngine(string name)
|
|
||||||
{
|
|
||||||
return _gameEngines[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string[] GetGameEngineNames()
|
|
||||||
{
|
|
||||||
return _gameEngines.Keys.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveGameEngine(string name)
|
|
||||||
{
|
|
||||||
if (_gameEngines[name].isRemovable)
|
|
||||||
{
|
|
||||||
_gameEngines.Remove(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RegisterEngines(StateSyncRegistrationHelper helper)
|
|
||||||
{
|
|
||||||
var enginesRoot = helper.enginesRoot;
|
|
||||||
_lastEngineRoot = enginesRoot;
|
|
||||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
|
||||||
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
|
||||||
foreach (var key in _gameEngines.Keys)
|
|
||||||
{
|
|
||||||
Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}");
|
|
||||||
if (_gameEngines[key] is IDeterministicEngine detEngine)
|
|
||||||
helper.AddDeterministicEngine(detEngine);
|
|
||||||
else
|
|
||||||
enginesRoot.AddEngine(_gameEngines[key]);
|
|
||||||
if (_gameEngines[key] is IFactoryEngine factEngine)
|
|
||||||
factEngine.Factory = factory;
|
|
||||||
if (_gameEngines[key] is IFunEngine funEngine)
|
|
||||||
funEngine.Functions = functions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Utility to get the state of the current Techblox game
|
|
||||||
/// </summary>
|
|
||||||
public static class GameState
|
|
||||||
{
|
|
||||||
private static GameStateEngine gameEngine = new GameStateEngine();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is the game in edit mode?
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Whether the game is in build mode</returns>
|
|
||||||
public static bool IsBuildMode()
|
|
||||||
{
|
|
||||||
return gameEngine.IsBuildMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is the game in simulation mode?
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Whether the game is in simulation mode</returns>
|
|
||||||
public static bool IsSimulationMode()
|
|
||||||
{
|
|
||||||
return gameEngine.IsSimulationMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is a game loaded?
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Whether Techblox has a game open (false = Main Menu)</returns>
|
|
||||||
public static bool IsInGame()
|
|
||||||
{
|
|
||||||
return gameEngine.IsInGame;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Init()
|
|
||||||
{
|
|
||||||
GameEngineManager.AddGameEngine(gameEngine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,6 @@
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using RobocraftX.SimulationModeState;
|
using RobocraftX.SimulationModeState;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Common.Engines;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Svelto.ECS;
|
|
||||||
using TechbloxModdingAPI.Engines;
|
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Keeps track of custom menu-modifying engines
|
|
||||||
/// </summary>
|
|
||||||
public static class MenuEngineManager
|
|
||||||
{
|
|
||||||
private static Dictionary<string, IApiEngine> _menuEngines = new Dictionary<string, IApiEngine>();
|
|
||||||
|
|
||||||
private static EnginesRoot _lastEngineRoot;
|
|
||||||
|
|
||||||
// menu engine management
|
|
||||||
public static void AddMenuEngine(IApiEngine engine)
|
|
||||||
{
|
|
||||||
_menuEngines[engine.Name] = engine;
|
|
||||||
if (_lastEngineRoot != null)
|
|
||||||
{
|
|
||||||
Logging.MetaDebugLog($"Registering Menu IApiEngine {engine.Name}");
|
|
||||||
_lastEngineRoot.AddEngine(engine);
|
|
||||||
if (engine is IFactoryEngine factoryEngine)
|
|
||||||
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
|
||||||
if (engine is IFunEngine funEngine)
|
|
||||||
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExistsMenuEngine(string name)
|
|
||||||
{
|
|
||||||
return _menuEngines.ContainsKey(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExistsMenuEngine(IApiEngine engine)
|
|
||||||
{
|
|
||||||
return ExistsMenuEngine(engine.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IApiEngine GetMenuEngine(string name)
|
|
||||||
{
|
|
||||||
return _menuEngines[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string[] GetMenuEngineNames()
|
|
||||||
{
|
|
||||||
return _menuEngines.Keys.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveMenuEngine(string name)
|
|
||||||
{
|
|
||||||
if (_menuEngines[name].isRemovable)
|
|
||||||
{
|
|
||||||
_menuEngines.Remove(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RegisterEngines(EnginesRoot enginesRoot)
|
|
||||||
{
|
|
||||||
_lastEngineRoot = enginesRoot;
|
|
||||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
|
||||||
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
|
||||||
foreach (var key in _menuEngines.Keys)
|
|
||||||
{
|
|
||||||
Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}");
|
|
||||||
enginesRoot.AddEngine(_menuEngines[key]);
|
|
||||||
if (_menuEngines[key] is IFactoryEngine factEngine) factEngine.Factory = factory;
|
|
||||||
if(_menuEngines[key] is IFunEngine funEngine) funEngine.Functions = functions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
using TechbloxModdingAPI.Common;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
|
@ -13,6 +15,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
private MB<T> managedArray;
|
private MB<T> managedArray;
|
||||||
private readonly EntityInitializer initializer;
|
private readonly EntityInitializer initializer;
|
||||||
//The possible fields are: (index && (array || managedArray)) || initializer
|
//The possible fields are: (index && (array || managedArray)) || initializer
|
||||||
|
private readonly EcsObjectBase obj;
|
||||||
|
|
||||||
public OptionalRef(NB<T> array, uint index, EGID entityId = default)
|
public OptionalRef(NB<T> array, uint index, EGID entityId = default)
|
||||||
{
|
{
|
||||||
|
@ -22,6 +25,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
this.entityId = entityId;
|
this.entityId = entityId;
|
||||||
initializer = default;
|
initializer = default;
|
||||||
managedArray = default;
|
managedArray = default;
|
||||||
|
obj = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OptionalRef(MB<T> array, uint index, EGID entityId = default)
|
public OptionalRef(MB<T> array, uint index, EGID entityId = default)
|
||||||
|
@ -32,6 +36,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
this.entityId = entityId;
|
this.entityId = entityId;
|
||||||
initializer = default;
|
initializer = default;
|
||||||
this.array = default;
|
this.array = default;
|
||||||
|
obj = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -55,6 +60,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
array = default;
|
array = default;
|
||||||
index = default;
|
index = default;
|
||||||
managedArray = default;
|
managedArray = default;
|
||||||
|
this.obj = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -65,7 +71,10 @@ namespace TechbloxModdingAPI.Utility
|
||||||
{
|
{
|
||||||
CompRefCache.Default = default; //The default value can be changed by mods
|
CompRefCache.Default = default; //The default value can be changed by mods
|
||||||
if (state == State.Empty) return ref CompRefCache.Default;
|
if (state == State.Empty) return ref CompRefCache.Default;
|
||||||
if ((state & State.Initializer) != State.Empty) return ref initializer.GetOrAdd<T>();
|
// If initializing the entity, check if the component is allowed by the descriptor, otherwise it could cause
|
||||||
|
// issues in the game with Add() calls running unexpectedly
|
||||||
|
if ((state & State.Initializer) != State.Empty && obj.AllowedEntityComponents.Contains(typeof(T)))
|
||||||
|
return ref initializer.GetOrAdd<T>();
|
||||||
if ((state & State.Native) != State.Empty) return ref array[index];
|
if ((state & State.Native) != State.Empty) return ref array[index];
|
||||||
return ref managedArray[index];
|
return ref managedArray[index];
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,7 @@ namespace TechbloxModdingAPI.Utility
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Store wrappers so we can unregister them properly
|
/// Store wrappers so we can unregister them properly
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static Dictionary<EventHandler<T>, EventHandler<T>> wrappers =
|
private static Dictionary<EventHandler<T>, EventHandler<T>> wrappers = new();
|
||||||
new Dictionary<EventHandler<T>, EventHandler<T>>();
|
|
||||||
|
|
||||||
public static WrappedHandler<T> operator +(WrappedHandler<T> original, EventHandler<T> added)
|
public static WrappedHandler<T> operator +(WrappedHandler<T> original, EventHandler<T> added)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue