From 966fdd4c3a936aa782ebbeac0e86ab6c2a7740f8 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Fri, 7 Jan 2022 02:14:58 +0100 Subject: [PATCH] Fix even more issues uncovered by tests - Fixed the time mode toggle not working during testing (changed the runner) - Made the testing thing wait until the time toggle finishes - Fixed the Game.Enter/Exit event being triggered on time mode change - Added a way to spawn and despawn the player's machine (which doesn't work yet) - Fixed the Player.Id property always being 0 - Attempted to fix the fake action inputs not working in simulation --- TechbloxModdingAPI/App/GameGameEngine.cs | 4 ++ TechbloxModdingAPI/EcsObjectBase.cs | 1 + TechbloxModdingAPI/Engines/EnginePatches.cs | 28 ++++++-- TechbloxModdingAPI/Input/FakeInput.cs | 70 +++++++++---------- TechbloxModdingAPI/Input/FakeInputEngine.cs | 10 +++ TechbloxModdingAPI/Input/FakeInputPatch.cs | 19 +++++ TechbloxModdingAPI/Player.cs | 24 +++++++ TechbloxModdingAPI/Players/PlayerEngine.cs | 32 +++++++++ TechbloxModdingAPI/Players/PlayerTests.cs | 17 ++++- TechbloxModdingAPI/Tests/TestRoot.cs | 14 +++- .../Utility/GameEngineManager.cs | 5 +- 11 files changed, 178 insertions(+), 46 deletions(-) create mode 100644 TechbloxModdingAPI/Input/FakeInputPatch.cs diff --git a/TechbloxModdingAPI/App/GameGameEngine.cs b/TechbloxModdingAPI/App/GameGameEngine.cs index 279b170..9856da8 100644 --- a/TechbloxModdingAPI/App/GameGameEngine.cs +++ b/TechbloxModdingAPI/App/GameGameEngine.cs @@ -41,6 +41,8 @@ namespace TechbloxModdingAPI.App public void Dispose() { + if (GameReloadedPatch.IsReload) + return; // Toggling time mode ExitGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID }); IsInGame = false; loadingFinished = false; @@ -50,6 +52,8 @@ namespace TechbloxModdingAPI.App public void Ready() { + if (GameReloadedPatch.IsReload) + return; // Toggling time mode enteredGame = true; Player.Joined += OnPlayerJoined; } diff --git a/TechbloxModdingAPI/EcsObjectBase.cs b/TechbloxModdingAPI/EcsObjectBase.cs index 9698eaa..ba42dc8 100644 --- a/TechbloxModdingAPI/EcsObjectBase.cs +++ b/TechbloxModdingAPI/EcsObjectBase.cs @@ -62,6 +62,7 @@ namespace TechbloxModdingAPI var id = initializer(this); if (!dict.ContainsKey(id)) // Multiple instances may be created dict.Add(id, this); + Id = id; } #region ECS initializer stuff diff --git a/TechbloxModdingAPI/Engines/EnginePatches.cs b/TechbloxModdingAPI/Engines/EnginePatches.cs index 7c9524b..d6b61a9 100644 --- a/TechbloxModdingAPI/Engines/EnginePatches.cs +++ b/TechbloxModdingAPI/Engines/EnginePatches.cs @@ -14,12 +14,10 @@ namespace TechbloxModdingAPI.Engines [HarmonyPatch] static class GameLoadedTimeStoppedEnginePatch { - public static EntitiesSubmissionScheduler Scheduler { get; private set; } public static void Postfix(StateSyncRegistrationHelper stateSyncReg) { // register all game engines, including deterministic GameEngineManager.RegisterEngines(stateSyncReg); - Scheduler = stateSyncReg.enginesRoot.scheduler; // register command engines /*CommandLineCompositionRoot.Compose(contextHolder, stateSyncReg.enginesRoot, reloadGame, multiplayerParameters, stateSyncReg); - uREPL C# compilation not supported anymore */ @@ -35,10 +33,10 @@ namespace TechbloxModdingAPI.Engines [HarmonyPatch] static class GameLoadedTimeRunningEnginePatch { - public static EntitiesSubmissionScheduler Scheduler { get; private set; } public static void Postfix(StateSyncRegistrationHelper stateSyncReg) { - GameLoadedTimeStoppedEnginePatch.Postfix(stateSyncReg); + GameEngineManager.RegisterEngines(stateSyncReg); + CommandManager.RegisterEngines(stateSyncReg.enginesRoot); } public static MethodBase TargetMethod() @@ -46,6 +44,28 @@ namespace TechbloxModdingAPI.Engines 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] class MenuLoadedEnginePatch diff --git a/TechbloxModdingAPI/Input/FakeInput.cs b/TechbloxModdingAPI/Input/FakeInput.cs index bcf8367..0b0a7f9 100644 --- a/TechbloxModdingAPI/Input/FakeInput.cs +++ b/TechbloxModdingAPI/Input/FakeInput.cs @@ -1,15 +1,13 @@ -using System; +using RobocraftX.Common.Input; -using RobocraftX.Common; -using RobocraftX.Common.Input; -using Svelto.ECS; +using TechbloxModdingAPI.App; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.Input { public static class FakeInput { - private static readonly FakeInputEngine inputEngine = new FakeInputEngine(); + internal static readonly FakeInputEngine inputEngine = new FakeInputEngine(); /// /// 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) - { - if (playerID == uint.MaxValue) - { - playerID = inputEngine.GetLocalPlayerID(); - } - ref LocalPlayerInputEntityStruct currentInput = ref inputEngine.GetPlayerInputRef(playerID); + { // TODO: We can only alter our own inputs clientside + ref var currentInput = ref inputEngine._localInputCache; + //Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}"); // set inputs - if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningModePlay; //TODO: Test, play - if (forward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Forward; - if (backward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Backward; - if (up) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Up; - if (down) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Down; - if (left) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Left; - if (right) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Right; - if (sprint) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Sprint; - //if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode; - //if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction; - if (primary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryAction; - if (secondary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryAction; - if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction; - if (primaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld; - if (secondaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld; - //if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid; - if (ctrl) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CtrlAction; - if (toggleColourMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleColourMode; - if (scaleBlockUp) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp; - if (scaleBlockDown) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown; - if (rotateBlockClockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise; - if (rotateBlockCounterclockwise) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise; - if (cutSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CutSelection; - if (copySelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CopySelection; - if (deleteSelection) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.DeleteSelection; + if (toggleMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningModePlay; //TODO: Test, play + if (forward) currentInput |= RobocraftX.Common.Input.ActionInput.Forward; + if (backward) currentInput |= RobocraftX.Common.Input.ActionInput.Backward; + if (up) currentInput |= RobocraftX.Common.Input.ActionInput.Up; + if (down) currentInput |= RobocraftX.Common.Input.ActionInput.Down; + if (left) currentInput |= RobocraftX.Common.Input.ActionInput.Left; + if (right) currentInput |= RobocraftX.Common.Input.ActionInput.Right; + if (sprint) currentInput |= RobocraftX.Common.Input.ActionInput.Sprint; + //if (toggleFly) currentInput |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode; + //if (alt) currentInput |= RobocraftX.Common.Input.ActionInput.AltAction; + if (primary) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionClick; + if (secondary) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionClick; + if (tertiary) currentInput |= RobocraftX.Common.Input.ActionInput.TertiaryAction; + if (primaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld; + if (secondaryHeld) currentInput |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld; + //if (toggleUnitGrid) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid; + if (ctrl) currentInput |= RobocraftX.Common.Input.ActionInput.CtrlAction; + if (toggleColourMode) currentInput |= RobocraftX.Common.Input.ActionInput.ToggleColourMode; + if (scaleBlockUp) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp; + if (scaleBlockDown) currentInput |= RobocraftX.Common.Input.ActionInput.ScaleBlockDown; + if (rotateBlockClockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockClockwise; + if (rotateBlockCounterclockwise) currentInput |= RobocraftX.Common.Input.ActionInput.RotateBlockAnticlockwise; + if (cutSelection) currentInput |= RobocraftX.Common.Input.ActionInput.CutSelection; + if (copySelection) currentInput |= RobocraftX.Common.Input.ActionInput.CopySelection; + 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() diff --git a/TechbloxModdingAPI/Input/FakeInputEngine.cs b/TechbloxModdingAPI/Input/FakeInputEngine.cs index 7b4528c..cc6a6c2 100644 --- a/TechbloxModdingAPI/Input/FakeInputEngine.cs +++ b/TechbloxModdingAPI/Input/FakeInputEngine.cs @@ -20,6 +20,8 @@ namespace TechbloxModdingAPI.Input public bool IsReady = false; + internal ActionInput _localInputCache; + public void Dispose() { IsReady = false; @@ -86,6 +88,14 @@ namespace TechbloxModdingAPI.Input return ref entitiesDB.QueryEntity(egid); } + internal void HandleCustomInput() + { + if (!LocalPlayerIDUtility.DoesLocalPlayerExist(entitiesDB)) + return; + GetPlayerInputRef(GetLocalPlayerID()).actionMask |= _localInputCache; + _localInputCache = default; + } + public uint GetLocalPlayerID() { return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB); diff --git a/TechbloxModdingAPI/Input/FakeInputPatch.cs b/TechbloxModdingAPI/Input/FakeInputPatch.cs new file mode 100644 index 0000000..f1a3281 --- /dev/null +++ b/TechbloxModdingAPI/Input/FakeInputPatch.cs @@ -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"); + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Player.cs b/TechbloxModdingAPI/Player.cs index 462e44b..63d13e0 100644 --- a/TechbloxModdingAPI/Player.cs +++ b/TechbloxModdingAPI/Player.cs @@ -125,6 +125,7 @@ namespace TechbloxModdingAPI }) { this.Type = player; + Id = base.Id.entityID; } // object fields & properties @@ -434,15 +435,38 @@ namespace TechbloxModdingAPI playerEngine.SetLocation(Id, location, exitSeat: exitSeat); } + /// + /// Enter the given seat. + /// + /// The seat to enter. public void EnterSeat(Seat seat) { playerEngine.EnterSeat(Id, seat.Id); } + /// + /// Exit the seat the player is currently in. + /// public void ExitSeat() { playerEngine.ExitSeat(Id); } + + /// + /// Spawn the machine the player is holding in time running mode. + /// + public bool SpawnMachine() + { + return playerEngine.SpawnMachine(Id); + } + + /// + /// Despawn the player's machine in time running mode and place it in their hand. + /// + public bool DespawnMachine() + { + return playerEngine.DespawnMachine(Id); + } /// /// Returns the block the player is currently looking at in build mode. diff --git a/TechbloxModdingAPI/Players/PlayerEngine.cs b/TechbloxModdingAPI/Players/PlayerEngine.cs index 2722258..bf9c548 100644 --- a/TechbloxModdingAPI/Players/PlayerEngine.cs +++ b/TechbloxModdingAPI/Players/PlayerEngine.cs @@ -12,6 +12,7 @@ using Gamecraft.GUI.HUDFeedbackBlocks; using RobocraftX.Blocks; using RobocraftX.Multiplayer; using RobocraftX.PilotSeat; +using RobocraftX.SimulationModeState; using Svelto.ECS; using Techblox.Camera; using Unity.Mathematics; @@ -20,6 +21,7 @@ using Svelto.ECS.EntityStructs; using Techblox.BuildingDrone; using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Input; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.Players @@ -205,6 +207,8 @@ namespace TechbloxModdingAPI.Players public void EnterSeat(uint playerId, EGID seatId) { + if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB)) + return; PilotSeatGroupUtils.SwapTagTo(Functions, seatId); var opt = GetCharacterStruct(playerId, out var group); if (!opt) return; @@ -222,6 +226,8 @@ namespace TechbloxModdingAPI.Players public void ExitSeat(uint playerId) { + if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB)) + return; EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup); var opt = entitiesDB.QueryEntityOptional(egid); if (!opt) return; @@ -229,6 +235,32 @@ namespace TechbloxModdingAPI.Players entitiesDB.PublishEntityChange(egid); } + public bool SpawnMachine(uint playerId) + { + if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB)) + return false; + EGID egid = new EGID(playerId, CharacterExclusiveGroups.MachineSpawningGroup); + if (!entitiesDB.Exists(egid)) + return false; + //Functions.SwapEntityGroup(egid, CharacterExclusiveGroups.OnFootGroup); + FakeInput.ActionInput(playerId, primary: true); + return true; + } + + public bool DespawnMachine(uint playerId) + { + if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB)) + return false; + GetCharacterStruct(playerId, out var group); + if (group.isInvalid) + return false; + EGID egid = new EGID(playerId, group); + if (!entitiesDB.Exists(egid)) + return false; + Functions.SwapEntityGroup(egid, CharacterExclusiveGroups.MachineSpawningGroup); + return true; + } + public uint GetPing() { return entitiesDB diff --git a/TechbloxModdingAPI/Players/PlayerTests.cs b/TechbloxModdingAPI/Players/PlayerTests.cs index c974518..1fc4f49 100644 --- a/TechbloxModdingAPI/Players/PlayerTests.cs +++ b/TechbloxModdingAPI/Players/PlayerTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Svelto.Tasks; using Svelto.Tasks.Enumerators; @@ -7,6 +8,7 @@ using Unity.Mathematics; using TechbloxModdingAPI.App; using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Tests; +using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.Players { @@ -45,7 +47,20 @@ namespace TechbloxModdingAPI.Players [APITestCase(TestType.SimulationMode)] public static IEnumerator 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); + 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) { Assert.Fail("No driver seat found!"); diff --git a/TechbloxModdingAPI/Tests/TestRoot.cs b/TechbloxModdingAPI/Tests/TestRoot.cs index 16b85d0..bffae51 100644 --- a/TechbloxModdingAPI/Tests/TestRoot.cs +++ b/TechbloxModdingAPI/Tests/TestRoot.cs @@ -7,7 +7,9 @@ using System.Linq; // welcome to the dark side using Svelto.Tasks; using Svelto.Tasks.Lean; using Svelto.Tasks.Enumerators; +using Svelto.Tasks.Lean.Unity; using UnityEngine; + using TechbloxModdingAPI.App; using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Utility; @@ -64,7 +66,7 @@ namespace TechbloxModdingAPI.Tests _testsCountPassed = 0; _testsCountFailed = 0; // flow control - Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.ClientLean.EveryFrameStepRunner_TimeRunningAndStopped); }; + Game.Enter += (sender, args) => { GameTests().RunOn(new UpdateMonoRunner("TechbloxModdingAPITestRunner")); }; Game.Exit += (s, a) => state = "ReturningFromGame"; Client.EnterMenu += (sender, args) => { @@ -165,6 +167,7 @@ namespace TechbloxModdingAPI.Tests }; for (var index = 0; index < testTypesToRun.Length; index++) { + Logging.MetaLog($"Running test type {testTypesToRun[index]}"); foreach (Type t in testTypes) { foreach (MethodBase m in t.GetMethods()) @@ -206,7 +209,16 @@ namespace TechbloxModdingAPI.Tests } if (index + 1 < testTypesToRun.Length) //Don't toggle on the last test + { + bool running = currentGame.IsTimeRunning; 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(); } // exit game diff --git a/TechbloxModdingAPI/Utility/GameEngineManager.cs b/TechbloxModdingAPI/Utility/GameEngineManager.cs index 16bf4e2..04b0da7 100644 --- a/TechbloxModdingAPI/Utility/GameEngineManager.cs +++ b/TechbloxModdingAPI/Utility/GameEngineManager.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using RobocraftX.StateSync; using Svelto.ECS;