|
- using System;
- using System.Reflection;
- using HarmonyLib;
-
- using RobocraftX.Services;
- using UnityEngine;
- using RobocraftX.Common;
- using TechbloxModdingAPI.Utility;
-
- namespace TechbloxModdingAPI.App
- {
- /// <summary>
- /// The Techblox application that is running this code right now.
- /// </summary>
- public class Client
- {
- public static Client Instance { get; } = new Client();
-
- protected static Func<object> ErrorHandlerInstanceGetter;
-
- protected static Action<object, Error> EnqueueError;
-
- /// <summary>
- /// An event that fires whenever the main menu is loaded.
- /// </summary>
- public static event EventHandler<MenuEventArgs> EnterMenu
- {
- add => Game.menuEngine.EnterMenu += value;
- remove => Game.menuEngine.EnterMenu -= value;
- }
-
- /// <summary>
- /// An event that fire whenever the main menu is exited.
- /// </summary>
- public static event EventHandler<MenuEventArgs> ExitMenu
- {
- add => Game.menuEngine.ExitMenu += value;
- remove => Game.menuEngine.ExitMenu -= value;
- }
-
- /// <summary>
- /// Techblox build version string.
- /// Usually this is in the form YYYY.mm.DD.HH.MM.SS
- /// </summary>
- /// <value>The version.</value>
- public string Version
- {
- get => Application.version;
- }
-
- /// <summary>
- /// Unity version string.
- /// </summary>
- /// <value>The unity version.</value>
- public string UnityVersion
- {
- get => Application.unityVersion;
- }
-
- /// <summary>
- /// Game saves currently visible in the menu.
- /// These take a second to completely populate after the EnterMenu event fires.
- /// </summary>
- /// <value>My games.</value>
- public Game[] MyGames
- {
- get
- {
- if (!Game.menuEngine.IsInMenu) return Array.Empty<Game>();
- return Game.menuEngine.GetMyGames();
- }
- }
-
- /// <summary>
- /// Whether Techblox is in the Main Menu
- /// </summary>
- /// <value><c>true</c> if in menu; <c>false</c> when loading or in a game.</value>
- public bool InMenu
- {
- get => Game.menuEngine.IsInMenu;
- }
-
- /// <summary>
- /// Open a popup which prompts the user to click a button.
- /// This reuses Techblox's error dialog popup
- /// </summary>
- /// <param name="popup">The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt.</param>
- public void PromptUser(Error popup)
- {
- // if the stuff wasn't mostly set to internal, this would be written as:
- // RobocraftX.Services.ErrorHandler.Instance.EqueueError(error);
- object errorHandlerInstance = ErrorHandlerInstanceGetter();
- EnqueueError(errorHandlerInstance, popup);
- }
-
- public void CloseCurrentPrompt()
- {
- object errorHandlerInstance = ErrorHandlerInstanceGetter();
- var popup = GetPopupCloseMethods(errorHandlerInstance);
- popup.Close();
- }
-
- public void SelectFirstPromptButton()
- {
- object errorHandlerInstance = ErrorHandlerInstanceGetter();
- var popup = GetPopupCloseMethods(errorHandlerInstance);
- popup.FirstButton();
- }
-
- public void SelectSecondPromptButton()
- {
- object errorHandlerInstance = ErrorHandlerInstanceGetter();
- var popup = GetPopupCloseMethods(errorHandlerInstance);
- popup.SecondButton();
- }
-
- internal void CloseBetaPopup()
- {
- Game.menuEngine.CloseBetaPopup();
- }
-
- internal static void Init()
- {
- // this would have been so much simpler if this didn't involve a bunch of internal fields & classes
- Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
- Type errorHandle = AccessTools.TypeByName("RobocraftX.Services.ErrorHandle");
- ErrorHandlerInstanceGetter = (Func<object>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenInstanceGetter")
- .MakeGenericMethod(errorHandler)
- .Invoke(null, new object[0]);
- EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
- .MakeGenericMethod(errorHandler, errorHandle)
- .Invoke(null, new object[0]);
- }
-
- // Creating delegates once is faster than reflection every time
- // Admittedly, this way is more difficult to code and less readable
- private static Func<object> GenInstanceGetter<T>()
- {
- Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
- MethodInfo instance = AccessTools.PropertyGetter(errorHandler, "Instance");
- Func<T> getterSimple = (Func<T>) Delegate.CreateDelegate(typeof(Func<T>), null, instance);
- Func<object> getterCasted = () => (object) getterSimple();
- return getterCasted;
- }
-
- private static Action<object, Error> GenEnqueueError<T, TRes>()
- {
- Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
- MethodInfo enqueueError = AccessTools.Method(errorHandler, "EnqueueError");
- Func<T, Error, TRes> enqueueSimple =
- (Func<T, Error, TRes>) Delegate.CreateDelegate(typeof(Func<T, Error, TRes>), enqueueError);
- Action<object, Error> enqueueCasted =
- (object instance, Error error) => { enqueueSimple((T) instance, error); };
- return enqueueCasted;
- }
-
- private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
-
- private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
- {
- if (_errorPopup.Close != null)
- return _errorPopup;
- Type errorHandler = handler.GetType();
- FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
- var errorPopup = (ErrorPopup)field.GetValue(handler);
- MethodInfo info = AccessTools.Method(errorPopup.GetType(), "ClosePopup");
- var close = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
- info = AccessTools.Method(errorPopup.GetType(), "HandleFirstOption");
- var first = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
- info = AccessTools.Method(errorPopup.GetType(), "HandleSecondOption");
- var second = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
- _errorPopup = (close, first, second);
- return _errorPopup;
- }
- }
- }
|