Merge branch 'preview'

# Conflicts:
#	Automation/gen_csproj.py
#	TechbloxModdingAPI/TechbloxModdingAPI.csproj
This commit is contained in:
Norbi Peti 2022-01-29 20:53:07 +01:00
commit 09d3c5e81c
43 changed files with 623 additions and 317 deletions

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using Svelto.Tasks;
namespace TechbloxModdingAPI.App
{
public static class AntiAntiCheatPatch
{
private delegate bool AntiAnticheatDelegate(ref object __result);
private delegate bool AntiAnticheatDelegateBool(ref bool __result);
private delegate bool AntiAnticheatDelegateTask(ref IEnumerator<TaskContract> __result);
public static void Init(Harmony harmony)
{
var type = AccessTools.TypeByName("Techblox.Services.Eos.Anticheat.Client.Services.AnticheatClientService");
harmony.Patch(type.GetConstructors()[0], new HarmonyMethod(((Func<bool>) AntiAntiCheat).Method));
harmony.Patch(AccessTools.Method(type, "Shutdown"), new HarmonyMethod(((Func<bool>) AntiAntiCheat).Method));
harmony.Patch(AccessTools.Method(type, "StartProtectedSession"), new HarmonyMethod(((AntiAnticheatDelegate) AntiAntiCheat).Method));
harmony.Patch(AccessTools.Method(type, "StopProtectedSession"), new HarmonyMethod(((AntiAnticheatDelegateBool) AntiAntiCheat).Method));
harmony.Patch(AccessTools.Method("Techblox.Services.Eos.Anticheat.Client.EosGetPendingMessagesToSendServiceRequest:Execute"), new HarmonyMethod(((AntiAnticheatDelegateTask)AntiAntiCheatTask).Method));
harmony.Patch(AccessTools.Method("Techblox.Anticheat.Client.Engines.ShowFeedbackDialogEngine:PollAnticheatStatus"), new HarmonyMethod(((AntiAnticheatDelegateTask)AntiAntiCheatTask).Method));
}
private static bool AntiAntiCheat() => false;
private static bool AntiAntiCheat(ref object __result)
{
var targetType =
AccessTools.TypeByName("Techblox.Services.Eos.Anticheat.Client.Services.StartProtectedSessionResult");
var target = Activator.CreateInstance(targetType);
targetType.GetField("Success").SetValue(target, true);
__result = target;
return false;
}
private static bool AntiAntiCheat(ref bool __result)
{
__result = true;
return false;
}
private static bool AntiAntiCheatTask(ref IEnumerator<TaskContract> __result)
{
IEnumerator<TaskContract> Func()
{
yield return Yield.It;
}
__result = Func();
return false;
}
}
}

View file

@ -1,44 +0,0 @@
using System;
using RobocraftX.GUI.MyGamesScreen;
using Svelto.ECS;
using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.App
{
public class AppEngine : IFactoryEngine
{
public WrappedHandler<MenuEventArgs> EnterMenu;
public WrappedHandler<MenuEventArgs> ExitMenu;
public IEntityFactory Factory { set; private get; }
public string Name => "TechbloxModdingAPIAppEngine";
public bool isRemovable => false;
public EntitiesDB entitiesDB { set; private get; }
public void Dispose()
{
IsInMenu = false;
ExitMenu.Invoke(this, new MenuEventArgs { });
}
public void Ready()
{
IsInMenu = true;
EnterMenu.Invoke(this, new MenuEventArgs { });
}
// app functionality
public bool IsInMenu
{
get;
private set;
} = false;
}
}

View file

@ -14,12 +14,12 @@ namespace TechbloxModdingAPI.App
/// </summary> /// </summary>
public class Client public class Client
{ {
public static Client Instance { get; } = new Client();
protected static Func<object> ErrorHandlerInstanceGetter; protected static Func<object> ErrorHandlerInstanceGetter;
protected static Action<object, Error> EnqueueError; protected static Action<object, Error> EnqueueError;
protected static Action<object> HandleErrorClosed;
/// <summary> /// <summary>
/// An event that fires whenever the main menu is loaded. /// An event that fires whenever the main menu is loaded.
/// </summary> /// </summary>
@ -93,14 +93,31 @@ namespace TechbloxModdingAPI.App
EnqueueError(errorHandlerInstance, popup); EnqueueError(errorHandlerInstance, popup);
} }
// TODO public void CloseCurrentPrompt()
/*public void CloseCurrentPrompt()
{ {
// RobocraftX.Services.ErrorHandler.Instance.HandlePopupClosed();
// FIXME: this is a call that is also called when closing, not the actual closing action itself (so it doesn't work)
object errorHandlerInstance = ErrorHandlerInstanceGetter(); object errorHandlerInstance = ErrorHandlerInstanceGetter();
HandleErrorClosed(errorHandlerInstance); var popup = GetPopupCloseMethods(errorHandlerInstance);
}*/ popup.Close();
}
public void SelectFirstPromptButton()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.FirstButton();
}
public void SelectSecondPromptButton()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.SecondButton();
}
internal void CloseBetaPopup()
{
Game.menuEngine.CloseBetaPopup();
}
internal static void Init() internal static void Init()
{ {
@ -113,9 +130,6 @@ namespace TechbloxModdingAPI.App
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError") EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
.MakeGenericMethod(errorHandler, errorHandle) .MakeGenericMethod(errorHandler, errorHandle)
.Invoke(null, new object[0]); .Invoke(null, new object[0]);
/*HandleErrorClosed = (Action<object>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenHandlePopupClosed")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);*/
} }
// Creating delegates once is faster than reflection every time // Creating delegates once is faster than reflection every time
@ -140,14 +154,23 @@ namespace TechbloxModdingAPI.App
return enqueueCasted; return enqueueCasted;
} }
private static Action<object> GenHandlePopupClosed<T>() private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
{ {
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler"); if (_errorPopup.Close != null)
MethodInfo handlePopupClosed = AccessTools.Method(errorHandler, "HandlePopupClosed"); return _errorPopup;
Action<T> handleSimple = Type errorHandler = handler.GetType();
(Action<T>) Delegate.CreateDelegate(typeof(Action<T>), handlePopupClosed); FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
Action<object> handleCasted = (object instance) => handleSimple((T) instance); var errorPopup = (ErrorPopup)field.GetValue(handler);
return handleCasted; MethodInfo info = AccessTools.Method(errorPopup.GetType(), "ClosePopup");
var close = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleFirstOption");
var first = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleSecondOption");
var second = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
_errorPopup = (close, first, second);
return _errorPopup;
} }
} }
} }

View file

@ -42,17 +42,25 @@ namespace TechbloxModdingAPI.App
[APITestCase(TestType.Menu)] [APITestCase(TestType.Menu)]
public static void TestPopUp2() public static void TestPopUp2()
{ {
Client c = new Client(); Client.Instance.PromptUser(popup2);
c.PromptUser(popup2);
//c.CloseCurrentPrompt();
} }
[APITestCase(TestType.Menu)] [APITestCase(TestType.Menu)]
public static void TestPopUp1() public static void TestPopUp1()
{ {
Client c = new Client(); Client.Instance.PromptUser(popup1);
c.PromptUser(popup1); }
//c.CloseCurrentPrompt();
[APITestCase(TestType.Menu)]
public static void TestPopUpClose1()
{
Client.Instance.CloseCurrentPrompt();
}
[APITestCase(TestType.Menu)]
public static void TestPopUpClose2()
{
Client.Instance.CloseCurrentPrompt();
} }
} }
#endif #endif

View file

@ -1,23 +1,27 @@
namespace TechbloxModdingAPI.App using System;
namespace TechbloxModdingAPI.App
{ {
public enum CurrentGameMode public enum CurrentGameMode
{ {
None, None,
/// <summary> /// <summary>
/// Building a game /// Building a world
/// </summary> /// </summary>
Build, Build,
/// <summary> /// <summary>
/// Playing a game /// Playing on a map
/// </summary> /// </summary>
Play, Play,
/// <summary> /// <summary>
/// Viewing a prefab /// Viewing a prefab (doesn't exist anymore)
/// </summary> /// </summary>
[Obsolete]
View, View,
/// <summary> /// <summary>
/// Viewing a tutorial /// Viewing a tutorial (doesn't exist anymore)
/// </summary> /// </summary>
[Obsolete]
Tutorial Tutorial
} }
} }

View file

@ -2,12 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using RobocraftX.Common;
using RobocraftX.GUI.MyGamesScreen; using RobocraftX.GUI.MyGamesScreen;
using RobocraftX.StateSync;
using Svelto.ECS; using Svelto.ECS;
using Techblox.GameSelection;
using TechbloxModdingAPI;
using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Tasks;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
@ -318,7 +316,7 @@ namespace TechbloxModdingAPI.App
get get
{ {
if (menuMode || !VerifyMode()) return CurrentGameMode.None; if (menuMode || !VerifyMode()) return CurrentGameMode.None;
return (CurrentGameMode) GameMode.CurrentMode; return gameEngine.GetGameData().gameMode == GameMode.CreateWorld ? CurrentGameMode.Build : CurrentGameMode.Play;
} }
} }

View file

@ -27,12 +27,14 @@ namespace TechbloxModdingAPI.App
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps) public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
{ {
Console.WriteLine("Init time running mode");
SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO
return inputDeps; return inputDeps;
} }
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps) public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
{ {
Console.WriteLine("Init time stopped mode");
BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" });
return inputDeps; return inputDeps;
} }

View file

@ -1,5 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using HarmonyLib;
using RobocraftX;
using RobocraftX.Common; using RobocraftX.Common;
using RobocraftX.Schedulers; using RobocraftX.Schedulers;
using RobocraftX.SimulationModeState; using RobocraftX.SimulationModeState;
@ -8,11 +10,15 @@ using Svelto.Tasks;
using Svelto.Tasks.Lean; using Svelto.Tasks.Lean;
using RobocraftX.Blocks; using RobocraftX.Blocks;
using RobocraftX.Common.Loading; using RobocraftX.Common.Loading;
using RobocraftX.Multiplayer;
using RobocraftX.ScreenshotTaker; using RobocraftX.ScreenshotTaker;
using Techblox.Environment.Transition; using Techblox.Environment.Transition;
using Techblox.GameSelection; using Techblox.GameSelection;
using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Input;
using TechbloxModdingAPI.Players;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.App namespace TechbloxModdingAPI.App
@ -30,16 +36,34 @@ namespace TechbloxModdingAPI.App
public EntitiesDB entitiesDB { set; private get; } public EntitiesDB entitiesDB { set; private get; }
private bool enteredGame; private bool enteredGame;
private bool loadingFinished;
private bool playerJoined;
public void Dispose() public void Dispose()
{ {
if (GameReloadedPatch.IsReload)
return; // Toggling time mode
ExitGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID }); ExitGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
IsInGame = false; IsInGame = false;
loadingFinished = false;
playerJoined = false;
enteredGame = false;
} }
public void Ready() public void Ready()
{ {
if (GameReloadedPatch.IsReload)
return; // Toggling time mode
enteredGame = true; enteredGame = true;
Player.Joined += OnPlayerJoined;
}
private void OnPlayerJoined(object sender, PlayerEventArgs args)
{
if (args.Player.Type != PlayerType.Local) return;
playerJoined = true;
Player.Joined -= OnPlayerJoined;
CheckJoinEvent();
} }
// game functionality // game functionality
@ -54,7 +78,7 @@ namespace TechbloxModdingAPI.App
{ {
if (async) if (async)
{ {
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped); ExitCurrentGameAsync().RunOn(ClientLean.EveryFrameStepRunner_TimeRunningAndStopped);
} }
else else
{ {
@ -94,9 +118,22 @@ namespace TechbloxModdingAPI.App
public void ToggleTimeMode() public void ToggleTimeMode()
{ {
if (!entitiesDB.FoundInGroups<BlockTagEntityStruct>()) if (TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB))
throw new AppStateException("At least one block must exist in the world to enter simulation"); FakeInput.ActionInput(toggleMode: true);
else
{
IEnumerator<TaskContract> ReloadBuildModeTask()
{
SwitchAnimationUtil.Start(entitiesDB); SwitchAnimationUtil.Start(entitiesDB);
while (SwitchAnimationUtil.IsFadeOutActive(entitiesDB))
yield return (TaskContract)Yield.It;
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
AccessTools.Method(typeof(FullGameCompositionRoot), "ReloadGame")
.Invoke(FullGameFields.Instance, new object[] { });
}
ReloadBuildModeTask().RunOn(ClientLean.UIScheduler);
}
} }
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid) public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
@ -142,9 +179,16 @@ namespace TechbloxModdingAPI.App
public void Remove(ref LoadingActionEntityStruct entityComponent, EGID egid) public void Remove(ref LoadingActionEntityStruct entityComponent, EGID egid)
{ // Finished loading { // Finished loading
if (!enteredGame) return; if (!enteredGame) return;
enteredGame = false;
loadingFinished = true;
CheckJoinEvent();
}
private void CheckJoinEvent()
{
if (!loadingFinished || !playerJoined) return;
EnterGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID }); EnterGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
IsInGame = true; IsInGame = true;
enteredGame = false;
} }
} }
} }

View file

@ -3,16 +3,15 @@ using System.Reflection;
using HarmonyLib; using HarmonyLib;
using RobocraftX; using RobocraftX;
using RobocraftX.Common;
using RobocraftX.FrontEnd;
using RobocraftX.GUI; using RobocraftX.GUI;
using RobocraftX.GUI.MyGamesScreen; using RobocraftX.GUI.MyGamesScreen;
using RobocraftX.Multiplayer;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Experimental; using Svelto.ECS.Experimental;
using Techblox.GameSelection; using Techblox.GameSelection;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
using GameMode = RobocraftX.Common.GameMode;
namespace TechbloxModdingAPI.App namespace TechbloxModdingAPI.App
{ {
@ -105,20 +104,16 @@ namespace TechbloxModdingAPI.App
return EnterGame(mgdes.GameName, mgdes.FileId); return EnterGame(mgdes.GameName, mgdes.FileId);
} }
public bool EnterGame(string gameName, string fileId, bool autoEnterSim = false) public bool EnterGame(ECSString gameName, string fileId, bool autoEnterSim = false)
{ {
GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build; FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
var data = new GameSelectionData ref var selection = ref entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
{ selection.userContentID.Set(fileId);
gameMode = Techblox.GameSelection.GameMode.PlayGame, selection.triggerStart = true;
gameType = GameType.MachineEditor, selection.saveType = SaveType.ExistingSave;
saveName = gameName, selection.saveName = gameName;
saveType = SaveType.ExistingSave, selection.gameMode = GameMode.PlayGame;
gameID = "GAMEID_Road_Track", //TODO: Expose to the API selection.gameID.Set("GAMEID_Road_Track"); //TODO: Expose to the API
userContentID = fileId
};
// the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[]{data});
return true; return true;
} }
@ -161,6 +156,16 @@ namespace TechbloxModdingAPI.App
{ {
return ref entitiesDB.QueryEntity<T>(id); return ref entitiesDB.QueryEntity<T>(id);
} }
internal void CloseBetaPopup()
{
var (buffer, count) = entitiesDB.QueryEntities<TogglePanelButtonEntityViewStruct>(ExclusiveGroup.Search("BetaPopup"));
for (int index = 0; index < count; ++index)
{
entitiesDB.QueryEntity<GUIEntityViewStruct>(buffer[index].TogglePanelButtonComponent.targetPanel)
.guiRoot.enabled = false;
}
}
} }
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { } internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }
@ -178,7 +183,7 @@ namespace TechbloxModdingAPI.App
public static MethodBase TargetMethod() public static MethodBase TargetMethod()
{ {
return AccessTools.Method(typeof(FullGameCompositionRoot), "GoToMenu"); return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu");
} }
} }

View file

@ -8,9 +8,10 @@ using Svelto.ECS.EntityStructs;
using RobocraftX.Common; using RobocraftX.Common;
using RobocraftX.Blocks; using RobocraftX.Blocks;
using Unity.Mathematics; using Unity.Mathematics;
using Gamecraft.Blocks.GUI;
using HarmonyLib; using HarmonyLib;
using RobocraftX.PilotSeat; using RobocraftX.PilotSeat;
using RobocraftX.Rendering;
using Techblox.BlockLabels;
using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Blocks.Engines; using TechbloxModdingAPI.Blocks.Engines;
@ -339,11 +340,9 @@ namespace TechbloxModdingAPI
[TestValue(null)] [TestValue(null)]
public string Label public string Label
{ {
get => BlockEngine.GetBlockInfoViewComponent<TextLabelEntityViewStruct>(this).textLabelComponent?.text; get => BlockEngine.GetBlockInfo<LabelResourceIDComponent>(this).ToString(); //TODO: Block labels
set set
{ { //TODO
var comp = BlockEngine.GetBlockInfoViewComponent<TextLabelEntityViewStruct>(this).textLabelComponent;
if (comp != null) comp.text = value;
} }
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;

View file

@ -273,6 +273,12 @@ namespace TechbloxModdingAPI.Blocks
/// <summary> /// <summary>
/// The grid block used by the world editor, named Small Grid like the other one /// The grid block used by the world editor, named Small Grid like the other one
/// </summary> /// </summary>
SmallGridInWorldEditor SmallGridInWorldEditor,
SegoeUITextblock = 376,
GravtracTextblock,
HauserTextblock,
TechnopollasTextblock,
BitBlock = 385,
Timer
} }
} }

View file

@ -22,7 +22,7 @@ namespace TechbloxModdingAPI.Blocks
base(new EGID(id, CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP)) base(new EGID(id, CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP))
{ {
} }
/*
/// <summary> /// <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>
@ -34,6 +34,7 @@ namespace TechbloxModdingAPI.Blocks
} }
set set
{ {
Techblox.BlockColours.BlockColoursCompositionRoot
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn = value; BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn = value;
} }
} }
@ -377,6 +378,6 @@ namespace TechbloxModdingAPI.Blocks
{ {
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value; BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
} }
} }*/
} }
} }

View file

@ -60,7 +60,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
copyToBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock}); copyToBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock});
ExclusiveGroupStruct group = WiresExclusiveGroups.WIRES_COPY_GROUP + playerID; ExclusiveGroupStruct group = BuildModeWiresGroups.WIRES_COPY_GROUP + playerID;
copyWireToBlock.Invoke(Patch.createWireEngine, new object[] {group, pickedBlock.ID}); copyWireToBlock.Invoke(Patch.createWireEngine, new object[] {group, pickedBlock.ID});
pickedBlock.placedBlockTweaksMustCopy = false; pickedBlock.placedBlockTweaksMustCopy = false;

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using Gamecraft.Blocks.BlockGroups; using Gamecraft.Blocks.BlockGroups;
@ -173,7 +173,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
foreach (var block in blocks) foreach (var block in blocks)
{ {
GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB, GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB,
BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference); BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference,
FullGameFields._managers.blockLabelResourceManager);
} }
} }
@ -272,7 +273,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
uint ghostChildBlockId = CommonExclusiveGroups.GetNewGhostChildBlockID(); uint ghostChildBlockId = CommonExclusiveGroups.GetNewGhostChildBlockID();
var ghostEntityReference = GhostBlockUtils.GetGhostEntityReference(sourceId.entityID, entitiesDB); var ghostEntityReference = GhostBlockUtils.GetGhostEntityReference(sourceId.entityID, entitiesDB);
var entityInitializer = BuildGhostBlueprintFactory.Build( var entityInitializer = BuildGhostBlueprintFactory.Build(
new EGID(ghostChildBlockId, BoxSelectExclusiveGroups.GhostChildEntitiesExclusiveGroup), /*dbStruct.DBID*/ (uint)BlockIDs.Cube); new EGID(ghostChildBlockId, BoxSelectExclusiveGroups.GhostChildEntitiesExclusiveGroup), /*dbStruct.DBID*/ (uint)BlockIDs.Cube,
FullGameFields._managers.blockLabelResourceManager);
entityInitializer.Init(dbStruct); entityInitializer.Init(dbStruct);
entityInitializer.Init(new GFXPrefabEntityStructGPUI( entityInitializer.Init(new GFXPrefabEntityStructGPUI(
PrefabsID.GetOrCreatePrefabID((ushort)entityInitializer.Get<PrefabAssetIDComponent>().prefabAssetID, PrefabsID.GetOrCreatePrefabID((ushort)entityInitializer.Get<PrefabAssetIDComponent>().prefabAssetID,

View file

@ -1,5 +1,5 @@
using RobocraftX.Common; using RobocraftX.Common;
using RobocraftX.UECS; using RobocraftX.DOTS;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.EntityStructs; using Svelto.ECS.EntityStructs;
using Unity.Mathematics; using Unity.Mathematics;
@ -40,7 +40,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block); ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block); ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block); ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<UECSPhysicsEntityStruct>(block); ref DOTSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<DOTSPhysicsEntityStruct>(block);
// main (persistent) position // main (persistent) position
posStruct.position = vector; posStruct.position = vector;
// placement grid position // placement grid position
@ -50,7 +50,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
// collision position // collision position
if (phyStruct.ID != default) if (phyStruct.ID != default)
{ //It exists { //It exists
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.dotsEntity, new Translation
{ {
Value = posStruct.position Value = posStruct.position
}); });

View file

@ -54,7 +54,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine //RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
DBEntityStruct dbEntity = new DBEntityStruct {DBID = block}; DBEntityStruct dbEntity = new DBEntityStruct {DBID = block};
EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, block); //The ghost block index is only used for triggers //TODO: Test
EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.blockIDGeneratorClient.Next(), block); //The ghost block index is only used for triggers
uint prefabAssetID = structInitializer.Has<PrefabAssetIDComponent>() uint prefabAssetID = structInitializer.Has<PrefabAssetIDComponent>()
? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID ? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID
: throw new BlockException("Prefab asset ID not found!"); //Set by the game : throw new BlockException("Prefab asset ID not found!"); //Set by the game

View file

@ -1,5 +1,5 @@
using RobocraftX.Common; using RobocraftX.Common;
using RobocraftX.UECS; using RobocraftX.DOTS;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.EntityStructs; using Svelto.ECS.EntityStructs;
using Unity.Mathematics; using Unity.Mathematics;
@ -40,7 +40,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block); ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block); ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block); ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<UECSPhysicsEntityStruct>(block); ref DOTSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<DOTSPhysicsEntityStruct>(block);
// main (persistent) rotation // main (persistent) rotation
Quaternion newRotation = rotStruct.rotation; Quaternion newRotation = rotStruct.rotation;
newRotation.eulerAngles = vector; newRotation.eulerAngles = vector;
@ -52,7 +52,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
// collision rotation // collision rotation
if (phyStruct.ID != default) if (phyStruct.ID != default)
{ //It exists { //It exists
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.dotsEntity,
new Unity.Transforms.Rotation new Unity.Transforms.Rotation
{ {
Value = rotStruct.rotation Value = rotStruct.rotation

View file

@ -2,7 +2,7 @@
using HarmonyLib; using HarmonyLib;
using RobocraftX.Common; using RobocraftX.Common;
using RobocraftX.UECS; using RobocraftX.DOTS;
using Svelto.ECS; using Svelto.ECS;
using Unity.Entities; using Unity.Entities;
@ -13,7 +13,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
{ {
public class ScalingEngine : IApiEngine public class ScalingEngine : IApiEngine
{ {
private static IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> physicsEngine; private static IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> physicsEngine;
public void Ready() public void Ready()
{ {
@ -34,16 +34,16 @@ namespace TechbloxModdingAPI.Blocks.Engines
if (_entityManager == default) if (_entityManager == default)
_entityManager = FullGameFields._physicsWorld.EntityManager; _entityManager = FullGameFields._physicsWorld.EntityManager;
//Assuming the block exists //Assuming the block exists
var entity = entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(egid).uecsEntity; var entity = entitiesDB.QueryEntity<DOTSPhysicsEntityStruct>(egid).dotsEntity;
var pes = new UECSPhysicsEntityCreationStruct(); var pes = new DOTSPhysicsEntityCreationStruct();
physicsEngine.Add(ref pes, egid); //Create new UECS entity physicsEngine.Add(ref pes, egid); //Create new DOTS entity
_entityManager.DestroyEntity(entity); _entityManager.DestroyEntity(entity);
} }
[HarmonyPatch] [HarmonyPatch]
class PhysicsEnginePatch class PhysicsEnginePatch
{ {
static void Postfix(IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> __instance) static void Postfix(IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> __instance)
{ {
physicsEngine = __instance; physicsEngine = __instance;
Logging.MetaDebugLog("Physics engine injected."); Logging.MetaDebugLog("Physics engine injected.");
@ -51,7 +51,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
static MethodBase TargetMethod(Harmony instance) static MethodBase TargetMethod(Harmony instance)
{ {
return AccessTools.Method("RobocraftX.StateSync.HandleUECSPhysicEntitiesWithPrefabCreationEngine" + return AccessTools.Method("RobocraftX.StateSync.HandleDOTSPhysicEntitiesWithPrefabCreationEngine" +
":Ready"); ":Ready");
} }
} }

View file

@ -43,7 +43,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort) public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
{ {
EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup<WiresGroup>.Group); EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID); EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
wireInitializer.Init(new WireEntityStruct wireInitializer.Init(new WireEntityStruct
{ {
@ -77,8 +77,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input) public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
{ {
ExclusiveGroup group = input ExclusiveGroup group = input
? NamedExclusiveGroup<InputPortsGroup>.Group ? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
: NamedExclusiveGroup<OutputPortsGroup>.Group; : NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber; uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
EGID egid = new EGID(id, group); EGID egid = new EGID(id, group);
if (!entitiesDB.Exists<PortEntityStruct>(egid)) if (!entitiesDB.Exists<PortEntityStruct>(egid))
@ -193,7 +193,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
EGID[] inputs = new EGID[ports.inputCount]; EGID[] inputs = new EGID[ports.inputCount];
for (uint i = 0; i < ports.inputCount; i++) for (uint i = 0; i < ports.inputCount; i++)
{ {
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<InputPortsGroup>.Group); inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group);
} }
return inputs; return inputs;
} }
@ -204,7 +204,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
EGID[] outputs = new EGID[ports.outputCount]; EGID[] outputs = new EGID[ports.outputCount];
for (uint i = 0; i < ports.outputCount; i++) for (uint i = 0; i < ports.outputCount; i++)
{ {
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<OutputPortsGroup>.Group); outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group);
} }
return outputs; return outputs;
} }
@ -219,8 +219,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
if (!entitiesDB.Exists<BlockPortsStruct>(block)) if (!entitiesDB.Exists<BlockPortsStruct>(block))
return default; return default;
var group = output var group = output
? NamedExclusiveGroup<OutputPortsGroup>.Group ? NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group
: NamedExclusiveGroup<InputPortsGroup>.Group; : NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group;
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block); BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper)) if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper))
return default; return default;
@ -237,7 +237,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public ref WireEntityStruct MatchPortToWire(PortEntityStruct port, EGID blockID, out bool exists) public ref WireEntityStruct MatchPortToWire(PortEntityStruct port, EGID blockID, out bool exists)
{ {
var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group); var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
var wiresB = wires.ToBuffer().buffer; var wiresB = wires.ToBuffer().buffer;
for (uint i = 0; i < wires.count; i++) for (uint i = 0; i < wires.count; i++)
{ {
@ -264,7 +264,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
else else
{ {
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock); BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<OutputPortsGroup>.Group) }; startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
} }
EGID[] endPorts; EGID[] endPorts;
@ -276,10 +276,10 @@ namespace TechbloxModdingAPI.Blocks.Engines
else else
{ {
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock); BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<InputPortsGroup>.Group) }; endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
} }
EntityCollection<WireEntityStruct> wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group); EntityCollection<WireEntityStruct> wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
var wiresB = wires.ToBuffer().buffer; var wiresB = wires.ToBuffer().buffer;
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++) for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
{ {
@ -304,7 +304,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID) public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
{ {
var port = GetPort(portID); var port = GetPort(portID);
var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group); var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
var channelsB = channels.ToBuffer(); var channelsB = channels.ToBuffer();
return port.firstChannelIndexCachedInSim < channels.count return port.firstChannelIndexCachedInSim < channels.count
? new OptionalRef<ChannelDataStruct>(channelsB.buffer, port.firstChannelIndexCachedInSim) ? new OptionalRef<ChannelDataStruct>(channelsB.buffer, port.firstChannelIndexCachedInSim)
@ -329,7 +329,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public EGID[] WiredToInput(EGID block, byte port) public EGID[] WiredToInput(EGID block, byte port)
{ {
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group, WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group,
(WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block); (WireEntityStruct wes) => wes.destinationPortUsage == port && wes.destinationBlockEGID == block);
EGID[] result = new EGID[wireEntityStructs.Length]; EGID[] result = new EGID[wireEntityStructs.Length];
for (uint i = 0; i < wireEntityStructs.Length; i++) for (uint i = 0; i < wireEntityStructs.Length; i++)
@ -342,7 +342,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
public EGID[] WiredToOutput(EGID block, byte port) public EGID[] WiredToOutput(EGID block, byte port)
{ {
WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<WiresGroup>.Group, WireEntityStruct[] wireEntityStructs = Search(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group,
(WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block); (WireEntityStruct wes) => wes.sourcePortUsage == port && wes.sourceBlockEGID == block);
EGID[] result = new EGID[wireEntityStructs.Length]; EGID[] result = new EGID[wireEntityStructs.Length];
for (uint i = 0; i < wireEntityStructs.Length; i++) for (uint i = 0; i < wireEntityStructs.Length; i++)
@ -371,13 +371,13 @@ namespace TechbloxModdingAPI.Blocks.Engines
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true) private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
{ {
ExclusiveGroup group = input ExclusiveGroup group = input
? NamedExclusiveGroup<InputPortsGroup>.Group ? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
: NamedExclusiveGroup<OutputPortsGroup>.Group; : NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
if (entitiesDB.Exists<PortEntityStruct>(signalID, group)) if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
{ {
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex; index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).anyChannelIndex;
var channelData = var channelData =
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group); entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
return channelData; return channelData;
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using Unity.Mathematics; using Unity.Mathematics;
using UnityEngine; using UnityEngine;

View file

@ -1,34 +0,0 @@
using System;
using System.Reflection;
using HarmonyLib;
using Svelto.ECS;
using RobocraftX.CR.MainGame;
using RobocraftX.Multiplayer;
using RobocraftX.StateSync;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Commands
{
/// <summary>
/// Patch of RobocraftX.CR.MainGame.MainGameCompositionRoot.DeterministicCompose<T>()
/// Initializes custom commands
/// </summary>
[HarmonyPatch]
static class CommandPatch
{
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
{
/*CommandLineCompositionRoot.Compose(contextHolder, stateSyncReg.enginesRoot, reloadGame, multiplayerParameters,
stateSyncReg); - uREPL C# compilation not supported anymore */
var enginesRoot = stateSyncReg.enginesRoot;
CommandManager.RegisterEngines(enginesRoot);
}
public static MethodInfo TargetMethod()
{
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose")
.MakeGenericMethod(typeof(object));
}
}
}

View file

@ -62,6 +62,7 @@ namespace TechbloxModdingAPI
var id = initializer(this); var id = initializer(this);
if (!dict.ContainsKey(id)) // Multiple instances may be created if (!dict.ContainsKey(id)) // Multiple instances may be created
dict.Add(id, this); dict.Add(id, this);
Id = id;
} }
#region ECS initializer stuff #region ECS initializer stuff

View file

@ -6,27 +6,67 @@ using RobocraftX.FrontEnd;
using RobocraftX.StateSync; using RobocraftX.StateSync;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Schedulers; using Svelto.ECS.Schedulers;
using TechbloxModdingAPI.Commands;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Engines namespace TechbloxModdingAPI.Engines
{ {
[HarmonyPatch] [HarmonyPatch]
class GameLoadedEnginePatch static class GameLoadedTimeStoppedEnginePatch
{ {
public static EntitiesSubmissionScheduler Scheduler { get; private set; }
public static void Postfix(StateSyncRegistrationHelper stateSyncReg) public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
{ {
// register all game engines, including deterministic // register all game engines, including deterministic
GameEngineManager.RegisterEngines(stateSyncReg); GameEngineManager.RegisterEngines(stateSyncReg);
Scheduler = stateSyncReg.enginesRoot.scheduler; // register command engines
/*CommandLineCompositionRoot.Compose(contextHolder, stateSyncReg.enginesRoot, reloadGame, multiplayerParameters,
stateSyncReg); - uREPL C# compilation not supported anymore */
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
} }
public static MethodBase TargetMethod() public static MethodBase TargetMethod()
{ {
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose").MakeGenericMethod(typeof(object)); return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicTimeStoppedCompose").MakeGenericMethod(typeof(object));
} }
} }
[HarmonyPatch]
static class GameLoadedTimeRunningEnginePatch
{
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
{
GameEngineManager.RegisterEngines(stateSyncReg);
CommandManager.RegisterEngines(stateSyncReg.enginesRoot);
}
public static MethodBase TargetMethod()
{
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicTimeRunningCompose").MakeGenericMethod(typeof(object));
}
}
[HarmonyPatch]
static class GameReloadedPatch
{
internal static bool IsReload;
public static void Prefix() => IsReload = true;
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "ReloadGame");
}
[HarmonyPatch]
static class GameSwitchedToPatch
{
public static void Prefix() => GameReloadedPatch.IsReload = false;
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame");
}
[HarmonyPatch]
static class MenuSwitchedToPatch
{
public static void Prefix() => GameReloadedPatch.IsReload = false;
public static MethodBase TargetMethod() => AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu");
}
[HarmonyPatch] [HarmonyPatch]
class MenuLoadedEnginePatch class MenuLoadedEnginePatch
{ {

View file

@ -1,15 +1,13 @@
using System; using RobocraftX.Common.Input;
using RobocraftX.Common; using TechbloxModdingAPI.App;
using RobocraftX.Common.Input;
using Svelto.ECS;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Input namespace TechbloxModdingAPI.Input
{ {
public static class FakeInput public static class FakeInput
{ {
private static readonly FakeInputEngine inputEngine = new FakeInputEngine(); internal static readonly FakeInputEngine inputEngine = new FakeInputEngine();
/// <summary> /// <summary>
/// Customize the local input. /// Customize the local input.
@ -103,39 +101,39 @@ namespace TechbloxModdingAPI.Input
} }
public static void ActionInput(uint playerID = uint.MaxValue, bool toggleMode = false, bool forward = false, bool backward = false, bool up = false, bool down = false, bool left = false, bool right = false, bool sprint = false, bool toggleFly = false, bool alt = false, bool primary = false, bool secondary = false, bool tertiary = false, bool primaryHeld = false, bool secondaryHeld = false, bool toggleUnitGrid = false, bool ctrl = false, bool toggleColourMode = false, bool scaleBlockUp = false, bool scaleBlockDown = false, bool rotateBlockClockwise = false, bool rotateBlockCounterclockwise = false, bool cutSelection = false, bool copySelection = false, bool deleteSelection = false) public static void ActionInput(uint playerID = uint.MaxValue, bool toggleMode = false, bool forward = false, bool backward = false, bool up = false, bool down = false, bool left = false, bool right = false, bool sprint = false, bool toggleFly = false, bool alt = false, bool primary = false, bool secondary = false, bool tertiary = false, bool primaryHeld = false, bool secondaryHeld = false, bool toggleUnitGrid = false, bool ctrl = false, bool toggleColourMode = false, bool scaleBlockUp = false, bool scaleBlockDown = false, bool rotateBlockClockwise = false, bool rotateBlockCounterclockwise = false, bool cutSelection = false, bool copySelection = false, bool deleteSelection = false)
{ { // TODO: We can only alter our own inputs clientside
if (playerID == uint.MaxValue) ref var currentInput = ref inputEngine._localInputCache;
{
playerID = inputEngine.GetLocalPlayerID();
}
ref LocalPlayerInputEntityStruct currentInput = ref inputEngine.GetPlayerInputRef(playerID);
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}"); //Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
// set inputs // set inputs
if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningMode; if (toggleMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningModePlay; //TODO: Test, play
if (forward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Forward; if (forward) currentInput |= RobocraftX.Common.Input.ActionInput.Forward;
if (backward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Backward; if (backward) currentInput |= RobocraftX.Common.Input.ActionInput.Backward;
if (up) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Up; if (up) currentInput |= RobocraftX.Common.Input.ActionInput.Up;
if (down) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Down; if (down) currentInput |= RobocraftX.Common.Input.ActionInput.Down;
if (left) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Left; if (left) currentInput |= RobocraftX.Common.Input.ActionInput.Left;
if (right) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Right; if (right) currentInput |= RobocraftX.Common.Input.ActionInput.Right;
if (sprint) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Sprint; if (sprint) currentInput |= RobocraftX.Common.Input.ActionInput.Sprint;
//if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode; //if (toggleFly) currentInput |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode;
//if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction; //if (alt) currentInput |= RobocraftX.Common.Input.ActionInput.AltAction;
if (primary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryAction; if (primary) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionClick;
if (secondary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryAction; if (secondary) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionClick;
if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction; if (tertiary) currentInput |= RobocraftX.Common.Input.ActionInput.TertiaryAction;
if (primaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld; if (primaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld;
if (secondaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld; if (secondaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld;
//if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid; //if (toggleUnitGrid) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid;
if (ctrl) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CtrlAction; if (ctrl) currentInput |= RobocraftX.Common.Input.ActionInput.CtrlAction;
if (toggleColourMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleColourMode; if (toggleColourMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleColourMode;
if (scaleBlockUp) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp; if (scaleBlockUp) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp;
if (scaleBlockDown) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown; if (scaleBlockDown) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown;
if (rotateBlockClockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise; if (rotateBlockClockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise;
if (rotateBlockCounterclockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise; if (rotateBlockCounterclockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise;
if (cutSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CutSelection; if (cutSelection) currentInput |= RobocraftX.Common.Input.ActionInput.CutSelection;
if (copySelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CopySelection; if (copySelection) currentInput |= RobocraftX.Common.Input.ActionInput.CopySelection;
if (deleteSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.DeleteSelection; if (deleteSelection) currentInput |= RobocraftX.Common.Input.ActionInput.DeleteSelection;
if(Game.CurrentGame().IsTimeStopped)
inputEngine.HandleCustomInput(); // Only gets called when online, so calling it here as well
} }
public static void Init() public static void Init()

View file

@ -20,6 +20,8 @@ namespace TechbloxModdingAPI.Input
public bool IsReady = false; public bool IsReady = false;
internal ActionInput _localInputCache;
public void Dispose() public void Dispose()
{ {
IsReady = false; IsReady = false;
@ -86,6 +88,14 @@ namespace TechbloxModdingAPI.Input
return ref entitiesDB.QueryEntity<LocalPlayerInputEntityStruct>(egid); return ref entitiesDB.QueryEntity<LocalPlayerInputEntityStruct>(egid);
} }
internal void HandleCustomInput()
{
if (!LocalPlayerIDUtility.DoesLocalPlayerExist(entitiesDB))
return;
GetPlayerInputRef(GetLocalPlayerID()).actionMask |= _localInputCache;
_localInputCache = default;
}
public uint GetLocalPlayerID() public uint GetLocalPlayerID()
{ {
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB); return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB);

View file

@ -0,0 +1,19 @@
using System.Reflection;
using HarmonyLib;
namespace TechbloxModdingAPI.Input
{
[HarmonyPatch]
public static class FakeInputPatch
{
public static void Prefix()
{
FakeInput.inputEngine.HandleCustomInput(); // This gets called right before the input is sent to the server
}
public static MethodBase TargetMethod()
{
return AccessTools.Method("RobocraftX.Multiplayer.Input.NetworkInputRecorderEngine:RecordDeterministicInput");
}
}
}

View file

@ -1,13 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using TechbloxModdingAPI.App;
using TechbloxModdingAPI.Utility;
using Rewired.Internal;
using Svelto.DataStructures;
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.ExtraLean; using Svelto.Tasks.ExtraLean;
using Svelto.Tasks.ExtraLean.Unity; using TechbloxModdingAPI.Tasks;
using UnityEngine; using UnityEngine;
namespace TechbloxModdingAPI.Interface.IMGUI namespace TechbloxModdingAPI.Interface.IMGUI

View file

@ -3,13 +3,11 @@ using System.Reflection;
using HarmonyLib; using HarmonyLib;
using RobocraftX; using RobocraftX;
using RobocraftX.Schedulers;
using RobocraftX.Services; using RobocraftX.Services;
using Svelto.Context; using Svelto.Context;
using Svelto.Tasks.ExtraLean;
using TechbloxModdingAPI.App; using TechbloxModdingAPI.App;
using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Events;
using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Tasks;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
@ -51,15 +49,13 @@ namespace TechbloxModdingAPI
harmony.PatchAll(currentAssembly); harmony.PatchAll(currentAssembly);
} }
catch (Exception e) catch (Exception e)
{ //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet {
Logging.Log(e.ToString()); HandleError(e, "Failed to patch Techblox. Attempting to patch to display error...", OnPatchError);
Logging.LogWarning("Failed to patch Techblox. Attempting to patch to display error...");
harmony.Patch(AccessTools.Method(typeof(FullGameCompositionRoot), "OnContextInitialized")
.MakeGenericMethod(typeof(UnityContext<FullGameCompositionRoot>)),
new HarmonyMethod(((Action) OnPatchError).Method)); //Can't use lambdas here :(
return; return;
} }
try
{
// init utility // init utility
Logging.MetaDebugLog($"Initializing Utility"); Logging.MetaDebugLog($"Initializing Utility");
Utility.GameState.Init(); Utility.GameState.Init();
@ -72,14 +68,24 @@ namespace TechbloxModdingAPI
Block.Init(); Block.Init();
BlockGroup.Init(); BlockGroup.Init();
Wire.Init(); Wire.Init();
// init client
Logging.MetaDebugLog($"Initializing Client"); Logging.MetaDebugLog($"Initializing Client");
Client.Init(); Client.Init();
Game.Init(); Game.Init();
// init UI // init UI
Logging.MetaDebugLog($"Initializing UI");
Interface.IMGUI.Constants.Init(); Interface.IMGUI.Constants.Init();
Interface.IMGUI.IMGUIManager.Init(); Interface.IMGUI.IMGUIManager.Init();
// init anti-anticheat
Logging.MetaDebugLog("Initializing anti-anticheat");
AntiAntiCheatPatch.Init(harmony);
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized"); Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
} }
catch (Exception e)
{
HandleError(e, "Failed to initialize the API! Attempting to patch to display error...", OnInitError);
}
}
/// <summary> /// <summary>
/// Shuts down & cleans up the TechbloxModdingAPI. /// Shuts down & cleans up the TechbloxModdingAPI.
@ -109,5 +115,26 @@ namespace TechbloxModdingAPI
ErrorBuilder.DisplayMustQuitError("Failed to patch Techblox!\n" + ErrorBuilder.DisplayMustQuitError("Failed to patch Techblox!\n" +
"Make sure you're using the latest version of TechbloxModdingAPI or disable mods if the API isn't released yet."); "Make sure you're using the latest version of TechbloxModdingAPI or disable mods if the API isn't released yet.");
} }
private static void OnInitError()
{
ErrorBuilder.DisplayMustQuitError("Failed to initialize the modding API!\n" +
"Make sure you're using the latest version. If you are, please report the error.");
}
/// <summary>
/// Handles an init error. Logs the exception, a log message, and allows displaying an error in-game.
/// </summary>
/// <param name="e">The exception</param>
/// <param name="logMsg">The log message</param>
/// <param name="onInit">The action to run when the game is ready to display error messages</param>
private static void HandleError(Exception e, string logMsg, Action onInit)
{ //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet
Logging.Log(e.ToString());
Logging.LogWarning(logMsg);
harmony.Patch(AccessTools.Method(typeof(FullGameCompositionRoot), "OnContextInitialized")
.MakeGenericMethod(typeof(UnityContext<FullGameCompositionRoot>)),
new HarmonyMethod(onInit.Method)); //Can't use lambdas here :(
}
} }
} }

View file

@ -25,7 +25,7 @@ namespace TechbloxModdingAPI.Persistence
Logging.MetaDebugLog("Skipping component serialization: no serializers registered!"); Logging.MetaDebugLog("Skipping component serialization: no serializers registered!");
return; return;
} }
serializationData.data.ExpandBy((uint)frameStart.Length); serializationData.data.IncreaseCapacityBy((uint)frameStart.Length);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int buffLen), serializationData.dataPos); BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int buffLen), serializationData.dataPos);
uint originalPos = serializationData.dataPos; uint originalPos = serializationData.dataPos;
Logging.MetaDebugLog($"dataPos: {originalPos}"); Logging.MetaDebugLog($"dataPos: {originalPos}");
@ -35,14 +35,14 @@ namespace TechbloxModdingAPI.Persistence
bbw.Write(frameStart[i]); bbw.Write(frameStart[i]);
} }
Logging.MetaDebugLog($"dataPos (after frame start): {bbw.Position}"); Logging.MetaDebugLog($"dataPos (after frame start): {bbw.Position}");
serializationData.data.ExpandBy(4u); serializationData.data.IncreaseCapacityBy(4u);
bbw.Write((uint)SerializerManager.GetSerializersCount()); bbw.Write((uint)SerializerManager.GetSerializersCount());
string[] serializerKeys = SerializerManager.GetSerializerNames(); string[] serializerKeys = SerializerManager.GetSerializerNames();
for (uint c = 0; c < serializerKeys.Length; c++) for (uint c = 0; c < serializerKeys.Length; c++)
{ {
Logging.MetaDebugLog($"dataPos (loop start): {bbw.Position}"); Logging.MetaDebugLog($"dataPos (loop start): {bbw.Position}");
// write component info // write component info
serializationData.data.ExpandBy(4u + (uint)serializerKeys[c].Length); serializationData.data.IncreaseCapacityBy(4u + (uint)serializerKeys[c].Length);
bbw.Write((uint)serializerKeys[c].Length); bbw.Write((uint)serializerKeys[c].Length);
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}"); Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
byte[] nameBytes = Encoding.UTF8.GetBytes(serializerKeys[c]); byte[] nameBytes = Encoding.UTF8.GetBytes(serializerKeys[c]);
@ -51,7 +51,7 @@ namespace TechbloxModdingAPI.Persistence
bbw.Write(nameBytes[i]); bbw.Write(nameBytes[i]);
} }
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}"); Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
serializationData.data.ExpandBy(4u); serializationData.data.IncreaseCapacityBy(4u);
serializationData.dataPos = bbw.Position + 4u; serializationData.dataPos = bbw.Position + 4u;
Logging.MetaDebugLog($"dataPos (now): {bbw.Position}"); Logging.MetaDebugLog($"dataPos (now): {bbw.Position}");
Logging.MetaDebugLog($"dataPos (appears to be): {serializationData.dataPos}"); Logging.MetaDebugLog($"dataPos (appears to be): {serializationData.dataPos}");
@ -74,7 +74,7 @@ namespace TechbloxModdingAPI.Persistence
public static MethodBase TargetMethod() public static MethodBase TargetMethod()
{ {
return typeof(SaveGameEngine).GetMethod("SerializeGameToBuffer"); return AccessTools.TypeByName("RobocraftX.SaveAndLoad.SaveGameEngine").GetMethod("SerializeGameToBuffer");
} }
} }
} }

View file

@ -46,7 +46,7 @@ namespace TechbloxModdingAPI.Persistence
public bool Serialize(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer) public bool Serialize(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer)
{ {
serializationData.data.ExpandBy(4u); serializationData.data.IncreaseCapacityBy(4u);
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int count), serializationData.dataPos); BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int count), serializationData.dataPos);
EGID[] toSerialize = getEntitiesToSerialize(entitiesDB); EGID[] toSerialize = getEntitiesToSerialize(entitiesDB);
bbw.Write((uint)toSerialize.Length); bbw.Write((uint)toSerialize.Length);

View file

@ -20,6 +20,22 @@ namespace TechbloxModdingAPI
add => seatExited += value; add => seatExited += value;
remove => seatExited -= value; remove => seatExited -= value;
} }
internal static WrappedHandler<PlayerEventArgs> joined;
public static event EventHandler<PlayerEventArgs> Joined
{
add => joined += value;
remove => joined -= value;
}
internal static WrappedHandler<PlayerEventArgs> left;
public static event EventHandler<PlayerEventArgs> Left
{
add => left += value;
remove => left -= value;
}
} }
public struct PlayerSeatEventArgs public struct PlayerSeatEventArgs
@ -27,4 +43,10 @@ namespace TechbloxModdingAPI
public EGID SeatId; public EGID SeatId;
public Seat Seat => (Seat)Block.New(SeatId); public Seat Seat => (Seat)Block.New(SeatId);
} }
public struct PlayerEventArgs
{
public EGID PlayerId;
public Player Player => Player.GetInstance(PlayerId.entityID);
}
} }

View file

@ -23,8 +23,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 PlayerEngine playerEngine = new PlayerEngine(); private static readonly PlayerEngine playerEngine = new PlayerEngine();
private static PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine(); private static readonly PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine();
private static Player localPlayer; private static Player localPlayer;
/// <summary> /// <summary>
@ -76,6 +76,12 @@ namespace TechbloxModdingAPI
} }
} }
internal static Player GetInstance(uint id)
{
return EcsObjectBase.GetInstance(new EGID(id, CharacterExclusiveGroups.OnFootGroup),
e => new Player(e.entityID));
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class. /// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
/// </summary> /// </summary>
@ -119,6 +125,7 @@ namespace TechbloxModdingAPI
}) })
{ {
this.Type = player; this.Type = player;
Id = base.Id.entityID;
} }
// object fields & properties // object fields & properties
@ -187,8 +194,6 @@ namespace TechbloxModdingAPI
public float Mass => public float Mass =>
1f / playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().physicsMass.InverseMass; 1f / playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().physicsMass.InverseMass;
private float _ping = -1f;
/// <summary> /// <summary>
/// The player's latest network ping time. /// The player's latest network ping time.
/// </summary> /// </summary>
@ -197,12 +202,7 @@ namespace TechbloxModdingAPI
{ {
get get
{ {
var opt = playerEngine.GetPlayerStruct<PlayerNetworkStatsEntityStruct>(Id, Type); return playerEngine.GetPing() / 1000f;
if (opt)
{
_ping = opt.Get().lastPingTimeSinceLevelLoad ?? _ping;
}
return _ping;
} }
} }
@ -435,16 +435,39 @@ namespace TechbloxModdingAPI
playerEngine.SetLocation(Id, location, exitSeat: exitSeat); playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
} }
/// <summary>
/// Enter the given seat.
/// </summary>
/// <param name="seat">The seat to enter.</param>
public void EnterSeat(Seat seat) public void EnterSeat(Seat seat)
{ {
playerEngine.EnterSeat(Id, seat.Id); playerEngine.EnterSeat(Id, seat.Id);
} }
/// <summary>
/// Exit the seat the player is currently in.
/// </summary>
public void ExitSeat() public void ExitSeat()
{ {
playerEngine.ExitSeat(Id); playerEngine.ExitSeat(Id);
} }
/// <summary>
/// Spawn the machine the player is holding in time running mode.
/// </summary>
public bool SpawnMachine()
{
return playerEngine.SpawnMachine(Id);
}
/// <summary>
/// Despawn the player's machine in time running mode and place it in their hand.
/// </summary>
public bool DespawnMachine()
{
return playerEngine.DespawnMachine(Id);
}
/// <summary> /// <summary>
/// Returns the block the player is currently looking at in build mode. /// Returns the block the player is currently looking at in build mode.
/// </summary> /// </summary>
@ -481,7 +504,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, NamedExclusiveGroup<WiresGroup>.Group), ? EcsObjectBase.GetInstance(new EGID(egid.entityID, BuildModeWiresGroups.WiresGroup.Group),
e => new Wire(e)) e => new Wire(e))
: null; : null;
} }

View file

@ -10,7 +10,9 @@ using RobocraftX.Physics;
using RobocraftX.Blocks.Ghost; using RobocraftX.Blocks.Ghost;
using Gamecraft.GUI.HUDFeedbackBlocks; using Gamecraft.GUI.HUDFeedbackBlocks;
using RobocraftX.Blocks; using RobocraftX.Blocks;
using RobocraftX.Multiplayer;
using RobocraftX.PilotSeat; using RobocraftX.PilotSeat;
using RobocraftX.SimulationModeState;
using Svelto.ECS; using Svelto.ECS;
using Techblox.Camera; using Techblox.Camera;
using Unity.Mathematics; using Unity.Mathematics;
@ -19,6 +21,7 @@ using Svelto.ECS.EntityStructs;
using Techblox.BuildingDrone; using Techblox.BuildingDrone;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Input;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Players namespace TechbloxModdingAPI.Players
@ -204,6 +207,8 @@ namespace TechbloxModdingAPI.Players
public void EnterSeat(uint playerId, EGID seatId) public void EnterSeat(uint playerId, EGID seatId)
{ {
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
return;
PilotSeatGroupUtils.SwapTagTo<OCCUPIED_TAG>(Functions, seatId); PilotSeatGroupUtils.SwapTagTo<OCCUPIED_TAG>(Functions, seatId);
var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(playerId, out var group); var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(playerId, out var group);
if (!opt) return; if (!opt) return;
@ -221,11 +226,46 @@ namespace TechbloxModdingAPI.Players
public void ExitSeat(uint playerId) public void ExitSeat(uint playerId)
{ {
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
return;
EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup); EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup);
var opt = entitiesDB.QueryEntityOptional<CharacterPilotSeatEntityStruct>(egid); var opt = entitiesDB.QueryEntityOptional<CharacterPilotSeatEntityStruct>(egid);
if (!opt) return; if (!opt) return;
opt.Get().instantExit = true; opt.Get().instantExit = true;
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid); entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
} }
public bool SpawnMachine(uint playerId)
{
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
return false;
EGID egid = new EGID(playerId, CharacterExclusiveGroups.MachineSpawningGroup);
if (!entitiesDB.Exists<CharacterTagEntityStruct>(egid))
return false;
//Functions.SwapEntityGroup<CharacterEntityDescriptor>(egid, CharacterExclusiveGroups.OnFootGroup);
FakeInput.ActionInput(playerId, primary: true);
return true;
}
public bool DespawnMachine(uint playerId)
{
if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
return false;
GetCharacterStruct<CharacterTagEntityStruct>(playerId, out var group);
if (group.isInvalid)
return false;
EGID egid = new EGID(playerId, group);
if (!entitiesDB.Exists<CharacterTagEntityStruct>(egid))
return false;
Functions.SwapEntityGroup<CharacterEntityDescriptor>(egid, CharacterExclusiveGroups.MachineSpawningGroup);
return true;
}
public uint GetPing()
{
return entitiesDB
.QueryUniqueEntity<NetworkStatsEntityStruct>(MultiplayerExclusiveGroups.MultiplayerStateGroup)
.networkStats.PingMs;
}
} }
} }

View file

@ -1,11 +1,13 @@
using RobocraftX.Character; using RobocraftX.Character;
using RobocraftX.Character.Movement; using RobocraftX.Character.Movement;
using RobocraftX.Common.Input;
using Svelto.ECS; using Svelto.ECS;
using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Engines;
namespace TechbloxModdingAPI.Players namespace TechbloxModdingAPI.Players
{ {
public class PlayerEventsEngine : IApiEngine, IReactOnSwap<CharacterPilotSeatEntityStruct> public class PlayerEventsEngine : IApiEngine, IReactOnSwap<CharacterPilotSeatEntityStruct>, IReactOnAddAndRemove<PlayerIDStruct>
{ {
public void Ready() public void Ready()
{ {
@ -21,13 +23,22 @@ namespace TechbloxModdingAPI.Players
public void MovedTo(ref CharacterPilotSeatEntityStruct entityComponent, ExclusiveGroupStruct previousGroup, EGID egid) public void MovedTo(ref CharacterPilotSeatEntityStruct entityComponent, ExclusiveGroupStruct previousGroup, EGID egid)
{ {
var seatId = entityComponent.pilotSeatEntity.ToEGID(entitiesDB); entitiesDB.TryGetEGID(entityComponent.pilotSeatEntity, out var seatId); //TODO: Can't get EGID
var player = EcsObjectBase.GetInstance(new EGID(egid.entityID, CharacterExclusiveGroups.OnFootGroup), var player = Player.GetInstance(egid.entityID);
e => new Player(e.entityID));
if (previousGroup == CharacterExclusiveGroups.InPilotSeatGroup) if (previousGroup == CharacterExclusiveGroups.InPilotSeatGroup)
player.seatExited.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId}); player.seatExited.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId});
else if (egid.groupID == CharacterExclusiveGroups.InPilotSeatGroup) else if (egid.groupID == CharacterExclusiveGroups.InPilotSeatGroup)
player.seatEntered.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId }); player.seatEntered.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId });
} }
public void Add(ref PlayerIDStruct entityComponent, EGID egid)
{
Player.joined.Invoke(this, new PlayerEventArgs { PlayerId = egid });
}
public void Remove(ref PlayerIDStruct entityComponent, EGID egid)
{
Player.left.Invoke(this, new PlayerEventArgs { PlayerId = egid });
}
} }
} }

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.Enumerators; using Svelto.Tasks.Enumerators;
@ -7,6 +8,7 @@ using Unity.Mathematics;
using TechbloxModdingAPI.App; using TechbloxModdingAPI.App;
using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Tests; using TechbloxModdingAPI.Tests;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Players namespace TechbloxModdingAPI.Players
{ {
@ -45,7 +47,20 @@ namespace TechbloxModdingAPI.Players
[APITestCase(TestType.SimulationMode)] [APITestCase(TestType.SimulationMode)]
public static IEnumerator<TaskContract> SeatEventTestSim() public static IEnumerator<TaskContract> SeatEventTestSim()
{ {
yield return new WaitForSecondsEnumerator(1).Continue();
Assert.Equal(Player.LocalPlayer.SpawnMachine(), true, "Failed to spawn the player's machine.", "Successfully spawned the player's machine.");
yield return new WaitForSecondsEnumerator(1).Continue();
var seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat); var seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat);
int c = 0;
while (seats.Length == 0 && c < 10)
{
Logging.MetaLog("Waiting for a seat to be spawned...");
yield return new WaitForSecondsEnumerator(1).Continue();
Console.WriteLine("Spawn machine: " + Player.LocalPlayer.SpawnMachine());
seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat);
c++;
}
if (seats.Length == 0) if (seats.Length == 0)
{ {
Assert.Fail("No driver seat found!"); Assert.Fail("No driver seat found!");

View file

@ -0,0 +1,16 @@
using System.Collections;
using Svelto.Tasks;
using Svelto.Tasks.ExtraLean;
using Svelto.Tasks.Unity.Internal;
namespace TechbloxModdingAPI.Tasks
{
public class OnGuiRunner : BaseRunner<ExtraLeanSveltoTask<IEnumerator>>
{
public OnGuiRunner(string name, uint runningOrder = 0)
: base(name)
{
UnityCoroutineRunner.StartOnGuiCoroutine(this._processEnumerator, runningOrder);
}
}
}

View file

@ -20,7 +20,7 @@ namespace TechbloxModdingAPI.Tasks
{ {
get get
{ {
return RobocraftX.Schedulers.Lean.UIScheduler; return RobocraftX.Schedulers.ClientLean.UIScheduler;
} }
} }
@ -28,7 +28,7 @@ namespace TechbloxModdingAPI.Tasks
{ {
get get
{ {
return RobocraftX.Schedulers.ExtraLean.UIScheduler; return RobocraftX.Schedulers.ClientExtraLean.UIScheduler;
} }
} }

View file

@ -10,12 +10,10 @@ using IllusionInjector;
using RobocraftX.FrontEnd; using RobocraftX.FrontEnd;
using Unity.Mathematics; using Unity.Mathematics;
using UnityEngine; using UnityEngine;
using RobocraftX.Common.Input;
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.Lean; using Svelto.Tasks.Lean;
using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Commands; using TechbloxModdingAPI.Commands;
using TechbloxModdingAPI.Input;
using TechbloxModdingAPI.Players; using TechbloxModdingAPI.Players;
using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Tasks;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
@ -345,6 +343,11 @@ namespace TechbloxModdingAPI.Tests
Logging.CommandLog(asset); Logging.CommandLog(asset);
} }
}).Build(); }).Build();
Client.EnterMenu += (sender, args) => Scheduler.Schedule(new Once(() => Client.Instance.CloseBetaPopup()));
Game.Enter += (sender, args) =>
Console.WriteLine(
$"Current game selection data: {FullGameFields._gameSelectionData.gameMode} - {FullGameFields._gameSelectionData.saveType}");
#if TEST #if TEST
TestRoot.RunTests(); TestRoot.RunTests();
#endif #endif
@ -360,15 +363,6 @@ namespace TechbloxModdingAPI.Tests
return modsString = sb.ToString(); return modsString = sb.ToString();
} }
public override void OnUpdate()
{
if (UnityEngine.Input.GetKeyDown(KeyCode.End))
{
Console.WriteLine("Pressed button to toggle console");
FakeInput.CustomInput(new LocalCosmeticInputEntityComponent {commandLineToggleInput = true});
}
}
[HarmonyPatch] [HarmonyPatch]
public class MinimumSpecsPatch public class MinimumSpecsPatch
{ {

View file

@ -7,7 +7,9 @@ using System.Linq; // welcome to the dark side
using Svelto.Tasks; using Svelto.Tasks;
using Svelto.Tasks.Lean; using Svelto.Tasks.Lean;
using Svelto.Tasks.Enumerators; using Svelto.Tasks.Enumerators;
using Svelto.Tasks.Lean.Unity;
using UnityEngine; using UnityEngine;
using TechbloxModdingAPI.App; using TechbloxModdingAPI.App;
using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Tasks;
using TechbloxModdingAPI.Utility; using TechbloxModdingAPI.Utility;
@ -64,7 +66,7 @@ namespace TechbloxModdingAPI.Tests
_testsCountPassed = 0; _testsCountPassed = 0;
_testsCountFailed = 0; _testsCountFailed = 0;
// flow control // flow control
Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.Lean.EveryFrameStepRunner_TimeRunningAndStopped); }; Game.Enter += (sender, args) => { GameTests().RunOn(new UpdateMonoRunner("TechbloxModdingAPITestRunner")); };
Game.Exit += (s, a) => state = "ReturningFromGame"; Game.Exit += (s, a) => state = "ReturningFromGame";
Client.EnterMenu += (sender, args) => Client.EnterMenu += (sender, args) =>
{ {
@ -129,7 +131,7 @@ namespace TechbloxModdingAPI.Tests
private static IEnumerator<TaskContract> GoToGameTests() private static IEnumerator<TaskContract> GoToGameTests()
{ {
Client app = new Client(); Client app = Client.Instance;
int oldLength = 0; int oldLength = 0;
while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length) while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
{ {
@ -137,7 +139,15 @@ namespace TechbloxModdingAPI.Tests
yield return new WaitForSecondsEnumerator(1).Continue(); yield return new WaitForSecondsEnumerator(1).Continue();
} }
yield return Yield.It; yield return Yield.It;
try
{
app.MyGames[0].EnterGame(); app.MyGames[0].EnterGame();
}
catch (Exception e)
{
Console.WriteLine("Failed to go to game tests");
Console.WriteLine(e);
}
/*Game newGame = Game.NewGame(); /*Game newGame = Game.NewGame();
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync
newGame.EnterGame();*/ newGame.EnterGame();*/
@ -157,6 +167,7 @@ namespace TechbloxModdingAPI.Tests
}; };
for (var index = 0; index < testTypesToRun.Length; index++) for (var index = 0; index < testTypesToRun.Length; index++)
{ {
Logging.MetaLog($"Running test type {testTypesToRun[index]}");
foreach (Type t in testTypes) foreach (Type t in testTypes)
{ {
foreach (MethodBase m in t.GetMethods()) foreach (MethodBase m in t.GetMethods())
@ -198,7 +209,16 @@ 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;
currentGame.ToggleTimeMode(); currentGame.ToggleTimeMode();
while (running ? !currentGame.IsTimeStopped : !currentGame.IsTimeRunning)
{
Logging.MetaLog($"Waiting for time to {(running?"stop":"start")}...");
yield return new WaitForSecondsEnumerator(1).Continue();
}
}
yield return new WaitForSecondsEnumerator(5).Continue(); yield return new WaitForSecondsEnumerator(5).Continue();
} }
// exit game // exit game

View file

@ -1,20 +1,14 @@
using System; using DataLoader;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataLoader;
using HarmonyLib; using HarmonyLib;
using RobocraftX; using RobocraftX;
using RobocraftX.Common.Utilities; using RobocraftX.CR.MainGame;
using RobocraftX.GUI; using RobocraftX.GUI;
using RobocraftX.Multiplayer; using RobocraftX.Multiplayer;
using RobocraftX.Rendering;
using Svelto.Context; using Svelto.Context;
using Svelto.DataStructures; using Svelto.DataStructures;
using Svelto.ECS; using Svelto.ECS;
using Svelto.ECS.Schedulers; using Svelto.ECS.GUI;
using Techblox.GameSelection;
using UnityEngine; using UnityEngine;
using Unity.Entities; using Unity.Entities;
using Unity.Physics.Systems; using Unity.Physics.Systems;
@ -104,14 +98,6 @@ namespace TechbloxModdingAPI.Utility
} }
} }
public static PhysicsUtility _physicsUtility
{
get
{
return (PhysicsUtility)fgcr?.Field("_physicsUtility").GetValue();
}
}
/*public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler /*public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler
{ {
get get
@ -136,11 +122,11 @@ namespace TechbloxModdingAPI.Utility
} }
} }
public static ECSGameObjectResourceManager _eCsGameObjectResourceManager public static ECSResourceManagers _managers
{ {
get get
{ {
return (ECSGameObjectResourceManager)fgcr?.Field("_eCsGameObjectResourceManager").GetValue(); return (ECSResourceManagers)fgcr?.Field("_managers").GetValue();
} }
} }
@ -160,6 +146,22 @@ namespace TechbloxModdingAPI.Utility
} }
} }
public static SveltoGUI _frontEndGUI
{
get
{
return (SveltoGUI)fgcr?.Field("_frontEndGUI").GetValue();
}
}
public static GameSelectionData _gameSelectionData
{
get
{
return (GameSelectionData)fgcr?.Field("_gameSelectionData").GetValue();
}
}
private static Traverse fgcr; private static Traverse fgcr;
public static void Init(FullGameCompositionRoot instance) public static void Init(FullGameCompositionRoot instance)

View file

@ -1,8 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RobocraftX.StateSync; using RobocraftX.StateSync;
using Svelto.ECS; using Svelto.ECS;

View file

@ -34,6 +34,12 @@ namespace TechbloxModdingAPI.Utility
Logging.LogWarning(wrappedException.ToString()); Logging.LogWarning(wrappedException.ToString());
} }
}; };
if (wrappers.ContainsKey(added))
{
original.eventHandler -= wrapped;
wrappers.Remove(added);
}
wrappers.Add(added, wrapped); wrappers.Add(added, wrapped);
return new WrappedHandler<T> { eventHandler = original.eventHandler + wrapped }; return new WrappedHandler<T> { eventHandler = original.eventHandler + wrapped };
} }