|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- using System;
- using System.Reflection;
- using HarmonyLib;
-
- using RobocraftX.Services;
- using UnityEngine;
-
- using GamecraftModdingAPI.Utility;
- using RobocraftX.Common;
-
- namespace GamecraftModdingAPI.App
- {
- /// <summary>
- /// The Gamecraft application that is running this code right now.
- /// </summary>
- public class Client
- {
- // extensible engine
- protected static AppEngine appEngine = new AppEngine();
-
- 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>
- public static event EventHandler<MenuEventArgs> EnterMenu
- {
- add => appEngine.EnterMenu += value;
- remove => appEngine.EnterMenu -= value;
- }
-
- /// <summary>
- /// An event that fire whenever the main menu is exited.
- /// </summary>
- public static event EventHandler<MenuEventArgs> ExitMenu
- {
- add => appEngine.ExitMenu += value;
- remove => appEngine.ExitMenu -= value;
- }
-
- /// <summary>
- /// Gamecraft 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 (!appEngine.IsInMenu) return new Game[0];
- return appEngine.GetMyGames();
- }
- }
-
- /// <summary>
- /// Whether Gamecraft 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 => appEngine.IsInMenu;
- }
-
- /// <summary>
- /// Open a popup which prompts the user to click a button.
- /// This reuses Gamecraft'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);
- }
-
- // TODO
- /*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();
- HandleErrorClosed(errorHandlerInstance);
- }*/
-
- 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("GamecraftModdingAPI.App.Client:GenInstanceGetter")
- .MakeGenericMethod(errorHandler)
- .Invoke(null, new object[0]);
- EnqueueError = (Action<object, Error>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenEnqueueError")
- .MakeGenericMethod(errorHandler, errorHandle)
- .Invoke(null, new object[0]);
- /*HandleErrorClosed = (Action<object>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenHandlePopupClosed")
- .MakeGenericMethod(errorHandler)
- .Invoke(null, new object[0]);*/
- // register engines
- MenuEngineManager.AddMenuEngine(appEngine);
- }
-
- // 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<object> GenHandlePopupClosed<T>()
- {
- 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;
- }
- }
- }
|