Browse Source

All kinds of fixes of issues during automatic tests

- Fixed toggling time running mode
- Fixed closing popups
- Added support for pressing the buttons on a popup
- Added error handling to Main.Init()
- Automatically closing the beta message in the test plugin
- Fixed Game.EnterGame() causing a crash in the game
tags/v2.2.0
NorbiPeti 2 years ago
parent
commit
5602ef9268
11 changed files with 188 additions and 123 deletions
  1. +0
    -44
      TechbloxModdingAPI/App/AppEngine.cs
  2. +41
    -18
      TechbloxModdingAPI/App/Client.cs
  3. +14
    -6
      TechbloxModdingAPI/App/ClientAlertTest.cs
  4. +2
    -0
      TechbloxModdingAPI/App/GameBuildSimEventEngine.cs
  5. +23
    -10
      TechbloxModdingAPI/App/GameGameEngine.cs
  6. +20
    -12
      TechbloxModdingAPI/App/GameMenuEngine.cs
  7. +1
    -1
      TechbloxModdingAPI/Input/FakeInput.cs
  8. +54
    -30
      TechbloxModdingAPI/Main.cs
  9. +5
    -0
      TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
  10. +10
    -2
      TechbloxModdingAPI/Tests/TestRoot.cs
  11. +18
    -0
      TechbloxModdingAPI/Utility/FullGameFields.cs

+ 0
- 44
TechbloxModdingAPI/App/AppEngine.cs 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;
}
}

+ 41
- 18
TechbloxModdingAPI/App/Client.cs View File

@@ -14,12 +14,12 @@ namespace TechbloxModdingAPI.App
/// </summary>
public class Client
{
public static Client Instance { get; } = new Client();
protected static Func<object> ErrorHandlerInstanceGetter;

protected static Action<object, Error> EnqueueError;

protected static Action<object> HandleErrorClosed;

/// <summary>
/// An event that fires whenever the main menu is loaded.
/// </summary>
@@ -93,14 +93,31 @@ namespace TechbloxModdingAPI.App
EnqueueError(errorHandlerInstance, popup);
}

// TODO
/*public void CloseCurrentPrompt()
public void CloseCurrentPrompt()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.Close();
}

public void SelectFirstPromptButton()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.FirstButton();
}

public void SelectSecondPromptButton()
{
// 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();
HandleErrorClosed(errorHandlerInstance);
}*/
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.SecondButton();
}

internal void CloseBetaPopup()
{
Game.menuEngine.CloseBetaPopup();
}

internal static void Init()
{
@@ -113,9 +130,6 @@ namespace TechbloxModdingAPI.App
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
.MakeGenericMethod(errorHandler, errorHandle)
.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
@@ -140,14 +154,23 @@ namespace TechbloxModdingAPI.App
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");
MethodInfo handlePopupClosed = AccessTools.Method(errorHandler, "HandlePopupClosed");
Action<T> handleSimple =
(Action<T>) Delegate.CreateDelegate(typeof(Action<T>), handlePopupClosed);
Action<object> handleCasted = (object instance) => handleSimple((T) instance);
return handleCasted;
if (_errorPopup.Close != null)
return _errorPopup;
Type errorHandler = handler.GetType();
FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
var errorPopup = (ErrorPopup)field.GetValue(handler);
MethodInfo info = AccessTools.Method(errorPopup.GetType(), "ClosePopup");
var close = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleFirstOption");
var first = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleSecondOption");
var second = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
_errorPopup = (close, first, second);
return _errorPopup;
}
}
}

+ 14
- 6
TechbloxModdingAPI/App/ClientAlertTest.cs View File

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

[APITestCase(TestType.Menu)]
public static void TestPopUpClose1()
{
Client.Instance.CloseCurrentPrompt();
}
[APITestCase(TestType.Menu)]
public static void TestPopUpClose2()
{
Client.Instance.CloseCurrentPrompt();
}
}
#endif

+ 2
- 0
TechbloxModdingAPI/App/GameBuildSimEventEngine.cs View File

@@ -27,12 +27,14 @@ namespace TechbloxModdingAPI.App

public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
{
Console.WriteLine("Init time running mode");
SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO
return inputDeps;
}

public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
{
Console.WriteLine("Init time stopped mode");
BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" });
return inputDeps;
}


+ 23
- 10
TechbloxModdingAPI/App/GameGameEngine.cs View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;

using HarmonyLib;
using RobocraftX;
using RobocraftX.Common;
using RobocraftX.Schedulers;
using RobocraftX.SimulationModeState;
@@ -9,11 +10,14 @@ using Svelto.Tasks;
using Svelto.Tasks.Lean;
using RobocraftX.Blocks;
using RobocraftX.Common.Loading;
using RobocraftX.Multiplayer;
using RobocraftX.ScreenshotTaker;
using Techblox.Environment.Transition;
using Techblox.GameSelection;

using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Engines;
using TechbloxModdingAPI.Input;
using TechbloxModdingAPI.Players;
using TechbloxModdingAPI.Utility;

@@ -52,9 +56,7 @@ namespace TechbloxModdingAPI.App

private void OnPlayerJoined(object sender, PlayerEventArgs args)
{
Console.WriteLine("Player joined: " + args.PlayerId + " asd");
if (args.Player.Type != PlayerType.Local) return;
Console.WriteLine("Player joined is local asd");
playerJoined = true;
Player.Joined -= OnPlayerJoined;
CheckJoinEvent();
@@ -112,10 +114,23 @@ namespace TechbloxModdingAPI.App

public void ToggleTimeMode()
{
if (!entitiesDB.FoundInGroups<BlockTagEntityStruct>())
throw new AppStateException("At least one block must exist in the world to enter simulation");
SwitchAnimationUtil.Start(entitiesDB);
}
if (TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB))
FakeInput.ActionInput(toggleMode: true);
else
{
IEnumerator<TaskContract> ReloadBuildModeTask()
{
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)
{
@@ -162,13 +177,11 @@ namespace TechbloxModdingAPI.App
if (!enteredGame) return;
enteredGame = false;
loadingFinished = true;
Console.WriteLine("Loading finished - asd");
CheckJoinEvent();
}

private void CheckJoinEvent()
{
Console.WriteLine($"Check: {loadingFinished} {playerJoined}");
if (!loadingFinished || !playerJoined) return;
EnterGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
IsInGame = true;


+ 20
- 12
TechbloxModdingAPI/App/GameMenuEngine.cs View File

@@ -5,6 +5,7 @@ using HarmonyLib;
using RobocraftX;
using RobocraftX.GUI;
using RobocraftX.GUI.MyGamesScreen;
using RobocraftX.Multiplayer;
using Svelto.ECS;
using Svelto.ECS.Experimental;
using Techblox.GameSelection;
@@ -103,19 +104,16 @@ namespace TechbloxModdingAPI.App
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)
{
var data = new GameSelectionData
{
gameMode = Techblox.GameSelection.GameMode.PlayGame,
isOnline = false,
saveName = gameName,
saveType = SaveType.ExistingSave,
gameID = "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});
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
ref var selection = ref entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
selection.userContentID.Set(fileId);
selection.triggerStart = true;
selection.saveType = SaveType.ExistingSave;
selection.saveName = gameName;
selection.gameMode = GameMode.PlayGame;
selection.gameID.Set("GAMEID_Road_Track"); //TODO: Expose to the API
return true;
}

@@ -158,6 +156,16 @@ namespace TechbloxModdingAPI.App
{
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> { }


+ 1
- 1
TechbloxModdingAPI/Input/FakeInput.cs View File

@@ -111,7 +111,7 @@ namespace TechbloxModdingAPI.Input
ref LocalPlayerInputEntityStruct currentInput = ref inputEngine.GetPlayerInputRef(playerID);
//Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}");
// set inputs
if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningModeTest; //TODO: Test, play
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;


+ 54
- 30
TechbloxModdingAPI/Main.cs View File

@@ -49,39 +49,42 @@ namespace TechbloxModdingAPI
harmony.PatchAll(currentAssembly);
}
catch (Exception e)
{ //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet
Logging.Log(e.ToString());
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 :(
{
HandleError(e, "Failed to patch Techblox. Attempting to patch to display error...", OnPatchError);
return;
}

// init utility
Logging.MetaDebugLog($"Initializing Utility");
Utility.GameState.Init();
// init block implementors
Logging.MetaDebugLog($"Initializing Blocks");
// init input
Input.FakeInput.Init();
// init object-oriented classes
Player.Init();
Block.Init();
BlockGroup.Init();
Wire.Init();
// init client
Logging.MetaDebugLog($"Initializing Client");
Client.Init();
Game.Init();
// init UI
Logging.MetaDebugLog($"Initializing UI");
Interface.IMGUI.Constants.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");
try
{
// init utility
Logging.MetaDebugLog($"Initializing Utility");
Utility.GameState.Init();
// init block implementors
Logging.MetaDebugLog($"Initializing Blocks");
// init input
Input.FakeInput.Init();
// init object-oriented classes
Player.Init();
Block.Init();
BlockGroup.Init();
Wire.Init();
// init client
Logging.MetaDebugLog($"Initializing Client");
Client.Init();
Game.Init();
// init UI
Logging.MetaDebugLog($"Initializing UI");
Interface.IMGUI.Constants.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");
}
catch (Exception e)
{
HandleError(e, "Failed to initialize the API! Attempting to patch to display error...", OnInitError);
}
}

/// <summary>
@@ -112,5 +115,26 @@ namespace TechbloxModdingAPI
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.");
}

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 :(
}
}
}

+ 5
- 0
TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs View File

@@ -343,6 +343,11 @@ namespace TechbloxModdingAPI.Tests
Logging.CommandLog(asset);
}
}).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
TestRoot.RunTests();
#endif


+ 10
- 2
TechbloxModdingAPI/Tests/TestRoot.cs View File

@@ -129,7 +129,7 @@ namespace TechbloxModdingAPI.Tests

private static IEnumerator<TaskContract> GoToGameTests()
{
Client app = new Client();
Client app = Client.Instance;
int oldLength = 0;
while (app.MyGames.Length == 0 || oldLength != app.MyGames.Length)
{
@@ -137,7 +137,15 @@ namespace TechbloxModdingAPI.Tests
yield return new WaitForSecondsEnumerator(1).Continue();
}
yield return Yield.It;
app.MyGames[0].EnterGame();
try
{
app.MyGames[0].EnterGame();
}
catch (Exception e)
{
Console.WriteLine("Failed to go to game tests");
Console.WriteLine(e);
}
/*Game newGame = Game.NewGame();
yield return new WaitForSecondsEnumerator(5).Continue(); // wait for sync
newGame.EnterGame();*/


+ 18
- 0
TechbloxModdingAPI/Utility/FullGameFields.cs View File

@@ -7,6 +7,8 @@ using RobocraftX.Multiplayer;
using Svelto.Context;
using Svelto.DataStructures;
using Svelto.ECS;
using Svelto.ECS.GUI;
using Techblox.GameSelection;
using UnityEngine;
using Unity.Entities;
using Unity.Physics.Systems;
@@ -144,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;

public static void Init(FullGameCompositionRoot instance)


Loading…
Cancel
Save