- 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 simulationtags/v2.2.0
@@ -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; | |||
} | |||
@@ -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 | |||
@@ -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 | |||
@@ -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(); | |||
/// <summary> | |||
/// 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() | |||
@@ -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<LocalPlayerInputEntityStruct>(egid); | |||
} | |||
internal void HandleCustomInput() | |||
{ | |||
if (!LocalPlayerIDUtility.DoesLocalPlayerExist(entitiesDB)) | |||
return; | |||
GetPlayerInputRef(GetLocalPlayerID()).actionMask |= _localInputCache; | |||
_localInputCache = default; | |||
} | |||
public uint GetLocalPlayerID() | |||
{ | |||
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB); | |||
@@ -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"); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
/// <summary> | |||
/// Enter the given seat. | |||
/// </summary> | |||
/// <param name="seat">The seat to enter.</param> | |||
public void EnterSeat(Seat seat) | |||
{ | |||
playerEngine.EnterSeat(Id, seat.Id); | |||
} | |||
/// <summary> | |||
/// Exit the seat the player is currently in. | |||
/// </summary> | |||
public void ExitSeat() | |||
{ | |||
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> | |||
/// Returns the block the player is currently looking at in build mode. | |||
@@ -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<OCCUPIED_TAG>(Functions, seatId); | |||
var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(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<CharacterPilotSeatEntityStruct>(egid); | |||
if (!opt) return; | |||
@@ -229,6 +235,32 @@ namespace TechbloxModdingAPI.Players | |||
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 | |||
@@ -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<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); | |||
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!"); | |||
@@ -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 | |||
@@ -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; | |||