A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
5.7KB

  1. using System;
  2. using System.Reflection;
  3. using HarmonyLib;
  4. using RobocraftX;
  5. using RobocraftX.GUI;
  6. using RobocraftX.GUI.MyGamesScreen;
  7. using RobocraftX.Multiplayer;
  8. using Svelto.ECS;
  9. using Svelto.ECS.Experimental;
  10. using Techblox.GameSelection;
  11. using TechbloxModdingAPI.Engines;
  12. using TechbloxModdingAPI.Utility;
  13. namespace TechbloxModdingAPI.App
  14. {
  15. public class GameMenuEngine : IFactoryEngine
  16. {
  17. public WrappedHandler<MenuEventArgs> EnterMenu;
  18. public WrappedHandler<MenuEventArgs> ExitMenu;
  19. public IEntityFactory Factory { set; private get; }
  20. public string Name => "TechbloxModdingAPIGameInfoGameEngine";
  21. public bool isRemovable => false;
  22. public EntitiesDB entitiesDB { set; private get; }
  23. public GameMenuEngine()
  24. {
  25. MenuEnteredEnginePatch.EnteredExitedMenu = () =>
  26. {
  27. if (IsInMenu)
  28. EnterMenu.Invoke(this, new MenuEventArgs { });
  29. else
  30. ExitMenu.Invoke(this, new MenuEventArgs { });
  31. };
  32. }
  33. public void Dispose()
  34. {
  35. }
  36. public void Ready()
  37. {
  38. MenuEnteredEnginePatch.IsInMenu = true; // At first it uses ActivateMenu(), then GoToMenu() which is patched
  39. MenuEnteredEnginePatch.EnteredExitedMenu();
  40. }
  41. // game functionality
  42. public bool IsInMenu => MenuEnteredEnginePatch.IsInMenu;
  43. public Game[] GetMyGames()
  44. {
  45. var (mgsevs, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
  46. Game[] games = new Game[count];
  47. for (int i = 0; i < count; i++)
  48. {
  49. Utility.Logging.MetaDebugLog($"Found game named {mgsevs[i].GameName}");
  50. games[i] = new Game(mgsevs[i].ID);
  51. }
  52. return games;
  53. }
  54. public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L)
  55. {
  56. EntityInitializer eci = Factory.BuildEntity<MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal>(id);
  57. eci.Init(new MyGameDataEntityStruct
  58. {
  59. SavedGamePath = new ECSString(path),
  60. ThumbnailId = thumbnailId,
  61. GameName = new ECSString(gameName),
  62. CreatorName = new ECSString(creatorName),
  63. GameDescription = new ECSString(description),
  64. CreatedDate = createdDate,
  65. });
  66. // entitiesDB.PublishEntityChange<MyGameDataEntityStruct>(id); // this will always fail
  67. return true;
  68. }
  69. public uint HighestID()
  70. {
  71. var (games, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
  72. uint max = 0;
  73. for (int i = 0; i < count; i++)
  74. {
  75. if (games[i].ID.entityID > max)
  76. {
  77. max = games[i].ID.entityID;
  78. }
  79. }
  80. return max;
  81. }
  82. public bool EnterGame(EGID id)
  83. {
  84. if (!ExistsGameInfo(id)) return false;
  85. ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id);
  86. return EnterGame(mgdes.GameName, mgdes.FileId);
  87. }
  88. public bool EnterGame(ECSString gameName, string fileId, bool autoEnterSim = false)
  89. {
  90. FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
  91. ref var selection = ref entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
  92. selection.userContentID.Set(fileId);
  93. selection.triggerStart = true;
  94. selection.saveType = SaveType.ExistingSave;
  95. selection.saveName = gameName;
  96. selection.gameMode = GameMode.PlayGame;
  97. selection.gameID.Set("GAMEID_Road_Track"); //TODO: Expose to the API
  98. return true;
  99. }
  100. public bool SetGameName(EGID id, string name)
  101. {
  102. if (!ExistsGameInfo(id)) return false;
  103. GetGameInfo(id).GameName.Set(name);
  104. GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name);
  105. return true;
  106. }
  107. public bool SetGameDescription(EGID id, string name)
  108. {
  109. if (!ExistsGameInfo(id)) return false;
  110. GetGameInfo(id).GameDescription.Set(name);
  111. GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name);
  112. return true;
  113. }
  114. public bool ExistsGameInfo(EGID id)
  115. {
  116. return entitiesDB.Exists<MyGameDataEntityStruct>(id);
  117. }
  118. public ref MyGameDataEntityStruct GetGameInfo(EGID id)
  119. {
  120. return ref GetComponent<MyGameDataEntityStruct>(id);
  121. }
  122. public dynamic GetGameViewInfo(EGID id)
  123. {
  124. dynamic structOptional = AccessTools.Method("TechbloxModdingAPI.Utility.NativeApiExtensions:QueryEntityOptional", new []{typeof(EntitiesDB), typeof(EGID)})
  125. .MakeGenericMethod(AccessTools.TypeByName("RobocraftX.GUI.MyGamesScreen.MyGamesSlotEntityViewStruct"))
  126. .Invoke(null, new object[] {entitiesDB, new EGID(id.entityID, MyGamesScreenExclusiveGroups.GameSlotGuiEntities)});
  127. if (structOptional == null) throw new Exception("Could not get game slot entity");
  128. return structOptional ? structOptional : null;
  129. }
  130. public ref T GetComponent<T>(EGID id) where T: unmanaged, IEntityComponent
  131. {
  132. return ref entitiesDB.QueryEntity<T>(id);
  133. }
  134. }
  135. internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }
  136. [HarmonyPatch]
  137. static class MenuEnteredEnginePatch
  138. {
  139. internal static bool IsInMenu;
  140. internal static Action EnteredExitedMenu;
  141. public static void Postfix()
  142. {
  143. IsInMenu = true;
  144. EnteredExitedMenu();
  145. }
  146. public static MethodBase TargetMethod()
  147. {
  148. return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu");
  149. }
  150. }
  151. [HarmonyPatch]
  152. static class MenuExitedEnginePatch
  153. {
  154. public static void Prefix()
  155. {
  156. MenuEnteredEnginePatch.IsInMenu = false;
  157. MenuEnteredEnginePatch.EnteredExitedMenu();
  158. }
  159. public static MethodBase TargetMethod()
  160. {
  161. return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame");
  162. }
  163. }
  164. public struct MenuEventArgs
  165. {
  166. }
  167. }