Techblox Mod Manager / Launcher
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.

262 lines
12KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Resources;
  9. using System.Threading.Tasks;
  10. using System.Windows.Forms;
  11. namespace TBMM
  12. {
  13. public partial class MainForm : Form
  14. {
  15. public MainForm()
  16. {
  17. InitializeComponent();
  18. resources = new ResourceManager("TBMM.Localization", Assembly.GetExecutingAssembly());
  19. Configuration = Configuration.Load();
  20. }
  21. public Configuration Configuration { get; }
  22. private readonly ResourceManager resources;
  23. private readonly Dictionary<string, ModInfo> mods = new();
  24. private readonly ModInfo gcipa = new ModInfo { Author = "modtainers", Name = "GCIPA" };
  25. private readonly ModInfo tbmm = new ModInfo { Author = "NorbiPeti", Name = "TBMM" };
  26. private DateTime lastGameUpdateTime;
  27. private const string defaultInfo = @"
  28. Techblox Mod Manager
  29. If you click on a mod it will show some info about it. The install instructions there are usually for manual installs.
  30. To get started, click on a mod and select Install mod. Most mods need TechbloxModdingAPI as well so it'll be installed.
  31. Then launch Techblox by clicking on the Play button below. Mods are only loaded if you start the game from here.
  32. This will first download and run the patcher (GCIPA) if needed. If all goes well, after some time a modded Techblox should launch.
  33. After a Techblox update there's a good chance that mods will break. If this happens you may get errors when trying to start Techblox through the mod manager.
  34. Until updated versions are released, launch the game without mods through its own launcher.
  35. Disclaimer:
  36. This mod manager and the mods in the list are made by the ExMods developers. We are not associated with Freejam or Techblox. Modify Techblox at your own risk.
  37. If you encounter an issue while any mods are installed, report it to us. If you think it's an issue with the game, test again by launching the game through the official launcher before reporting to Freejam.
  38. ";
  39. private async void Form1_Load(object sender, EventArgs e)
  40. {
  41. if (mods.Values.All(mod => mod.LatestVersion == null)) //Not (fully) loaded yet
  42. await LoadEverything(true);
  43. }
  44. public async Task LoadEverything(bool evenMods)
  45. {
  46. modlist.Items.Clear();
  47. mods.Clear(); //This method may get called twice when ran from the command line
  48. UpdateButton(installbtn, false);
  49. modinfobox.Text = defaultInfo;
  50. if (string.IsNullOrWhiteSpace(Configuration.GamePath) || GetExe() == null)
  51. {
  52. Configuration.GamePath = GetGameFolder();
  53. if (string.IsNullOrWhiteSpace(Configuration.GamePath))
  54. {
  55. DialogUtils.ShowWarning(resources.GetString("Game_not_found"), "");
  56. Configuration.GamePath = SelectGameFolder();
  57. }
  58. else
  59. DialogUtils.ShowInfo(string.Format(resources.GetString("Found_game_at"), Configuration.GamePath), "");
  60. Configuration.Save();
  61. }
  62. if(string.IsNullOrWhiteSpace(Configuration.GamePath))
  63. {
  64. status.Text = resources.GetString("Status_Game_not_found");
  65. return;
  66. }
  67. DeleteEmptyPluginsDir(out _, out _);
  68. await RefreshEverything(evenMods);
  69. }
  70. private async void playbtn_Click(object sender, EventArgs e)
  71. {
  72. if (playbtn.ForeColor == Color.Green) return; //Disabled
  73. if (mods.Any(mod => mod.Value.Installed && mod.Value.Broken is true))
  74. if (MessageBox.Show("Some installed mods are known to be broken on the current version of the game. The game might crash.",
  75. "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.Cancel)
  76. return;
  77. await UpdateAPI();
  78. await PatchStartGame(); //It will call EndWork();
  79. }
  80. private void UpdatePlayButtonColor()
  81. {
  82. if (mods.Any(mod => mod.Value.Installed && mod.Value.Broken is true))
  83. playbtn.ForeColor = Color.Red;
  84. else if (mods.Any(mod => mod.Value.Installed && mod.Value.Outdated(lastGameUpdateTime)))
  85. playbtn.ForeColor = Color.DarkOrange;
  86. else
  87. playbtn.ForeColor = Color.Lime;
  88. }
  89. private void settingsbtn_Click(object sender, EventArgs e)
  90. {
  91. if (settingsbtn.ForeColor == Color.Green) return; //Disabled
  92. var sf = new SettingsForm();
  93. sf.ShowDialog(this);
  94. }
  95. private void modlist_SelectedIndexChanged(object sender, EventArgs e)
  96. {
  97. if (working) return;
  98. modinfobox.Clear();
  99. switch (modlist.SelectedItems.Count)
  100. {
  101. case 0:
  102. modinfobox.Text = defaultInfo;
  103. UpdateButton(installbtn, false);
  104. UpdateButton(uninstallbtn, false);
  105. break;
  106. case 1:
  107. default:
  108. installbtn.Text = "Install mod";
  109. UpdateButton(installbtn, false);
  110. UpdateButton(uninstallbtn, false);
  111. bool install = false, update = false;
  112. Action<string, Color> addText = (txt, color) =>
  113. {
  114. int start = modinfobox.Text.Length;
  115. modinfobox.AppendText(txt + Environment.NewLine + Environment.NewLine);
  116. modinfobox.Select(start, txt.Length);
  117. modinfobox.SelectionColor = color;
  118. modinfobox.DeselectAll();
  119. modinfobox.SelectionColor = modinfobox.ForeColor;
  120. };
  121. foreach (ListViewItem item in modlist.SelectedItems)
  122. {
  123. var mod = mods[item.Name];
  124. if (modlist.SelectedItems.Count == 1)
  125. {
  126. if (mod.Updatable)
  127. addText("New version available! " + mod.UpdateDetails, Color.Aqua);
  128. if (mod.Broken is true)
  129. addText("Outdated mod! It has been confirmed that the mod is broken on the current version of the game.", Color.Red);
  130. else if (mod.Outdated(lastGameUpdateTime))
  131. addText("Outdated mod! It may not work properly on the current version of the game.", Color.DarkOrange);
  132. if (mod.Description != null)
  133. modinfobox.AppendText(mod.Description.Replace("\n", Environment.NewLine));
  134. }
  135. else
  136. modinfobox.Text = modlist.SelectedItems.Count + " mods selected";
  137. if (mod.DownloadURL != null && !(mod.LatestVersion <= mod.Version))
  138. {
  139. UpdateButton(installbtn, true);
  140. if (mod.Version != null)
  141. update = true;
  142. else
  143. install = true;
  144. }
  145. if (mod.Version != null)
  146. UpdateButton(uninstallbtn, true);
  147. }
  148. if (install && update)
  149. installbtn.Text = "Install and update mod";
  150. else if (update)
  151. installbtn.Text = "Update mod";
  152. else
  153. installbtn.Text = "Install mod";
  154. break;
  155. }
  156. }
  157. private async void installbtn_Click(object sender, EventArgs e)
  158. {
  159. if (installbtn.ForeColor == Color.Green) return; //Disabled
  160. if (!BeginWork()) return;
  161. foreach (ListViewItem item in modlist.SelectedItems)
  162. {
  163. var mod = mods[item.Name];
  164. if (item.Group.Name == "installed" && (mod.DownloadURL == null || mod.LatestVersion <= mod.Version)) continue;
  165. await InstallMod(mod);
  166. }
  167. EndWork(CheckIfPatched());
  168. }
  169. private void uninstallbtn_Click(object sender, EventArgs e)
  170. {
  171. if (uninstallbtn.ForeColor == Color.Green) return; //Disabled
  172. foreach (ListViewItem item in modlist.SelectedItems)
  173. {
  174. if (item.Group.Name != "installed") continue;
  175. UninstallMod(mods[item.Name]);
  176. }
  177. EndWork(CheckIfPatched()); //Update button states
  178. }
  179. private void findlog_Click(object sender, EventArgs e)
  180. {
  181. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  182. {
  183. if (CheckNoExe(out string exe))
  184. return;
  185. Process.Start("explorer.exe", $@"/select,{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}Low\Freejam\{exe.Replace(".exe", "")}\Player.log");
  186. }
  187. }
  188. private void DeleteEmptyPluginsDir(out bool pexists, out bool dexists)
  189. {
  190. string plugins = GamePath("\\Plugins");
  191. string disabled = GamePath("\\Plugins_Disabled");
  192. pexists = Directory.Exists(plugins);
  193. dexists = Directory.Exists(disabled);
  194. if (pexists && !Directory.EnumerateFiles(plugins).Any())
  195. {
  196. Directory.Delete(plugins);
  197. pexists = false;
  198. }
  199. if (dexists && !Directory.EnumerateFiles(disabled).Any())
  200. {
  201. Directory.Delete(disabled);
  202. dexists = false;
  203. }
  204. }
  205. private async void refreshbtn_Click(object sender, EventArgs e)
  206. {
  207. await RefreshEverything(true);
  208. }
  209. private async Task RefreshEverything(bool evenMods)
  210. {
  211. if (CheckIfPatched() == GameState.Patched) //Set from placeholder & unpatch if game was patched
  212. HandleGameExit(null, EventArgs.Empty);
  213. lastGameUpdateTime = GetGameVersionAsDate();
  214. var mods = GetInstalledMods();
  215. if (evenMods)
  216. await GetAvailableMods();
  217. CheckUninstalledMods(mods);
  218. CheckIfPatched(); //Check after getting the available mods to show GCIPA updates
  219. }
  220. private void MainForm_Shown(object sender, EventArgs e)
  221. {
  222. Focus();
  223. }
  224. private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  225. {
  226. if (e.Cancel) return;
  227. if (Configuration.KeepPatched || CheckIfPatched(out bool patched) != GameState.InGame || !patched) return;
  228. if (MessageBox.Show("The game is still running. The mod manager needs to be running until the game closes to restore the game files." +
  229. " If you proceed you won't be able to play online until you start the mod manager again.\n\n" +
  230. "Are you sure you want TBMM to exit before the game does?", "Game still running",
  231. MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.No)
  232. e.Cancel = true;
  233. }
  234. private void MainForm_Activated(object sender, EventArgs e)
  235. {
  236. CheckIfPatched();
  237. }
  238. }
  239. }