diff --git a/TranslationUpdater.cs b/TranslationUpdater.cs index 815ce18..5e642b1 100644 --- a/TranslationUpdater.cs +++ b/TranslationUpdater.cs @@ -8,10 +8,11 @@ namespace TranslationUpdater { public const string pluginGuid = "katboi01.TranslationUpdater"; public const string pluginName = "KF3 Translation Updater"; - public const string pluginVersion = "1.1.1"; + public const string pluginVersion = "1.2.0"; public void Awake() { + UpdateHandler.PluginVersion = pluginVersion; UpdateHandler.Create(); } } diff --git a/TranslationUpdater.csproj b/TranslationUpdater.csproj index 75142dd..d580a09 100644 --- a/TranslationUpdater.csproj +++ b/TranslationUpdater.csproj @@ -86,6 +86,11 @@ + + + + + diff --git a/UI/PopupMessage.cs b/UI/PopupMessage.cs new file mode 100644 index 0000000..e6351e7 --- /dev/null +++ b/UI/PopupMessage.cs @@ -0,0 +1,38 @@ +using UnityEngine; +using UnityEngine.UI; + +public class PopupMessage : MonoBehaviour +{ + public Text Content; + public float MessageTime; + + public System.Action OnFinishedAction; + + public PopupMessage Init(string message, float time = 2) + { + Content.text = message; + MessageTime = time; + gameObject.SetActive(true); + return this; + } + + private void Update() + { + if (MessageTime > 0) + { + MessageTime -= Time.deltaTime; + + if (MessageTime <= 0) + { + OnFinishedAction?.Invoke(); + Destroy(gameObject); + } + } + } + + public void OnClick() + { + Destroy(this.gameObject); + OnFinishedAction?.Invoke(); + } +} \ No newline at end of file diff --git a/UI/ProgressSlider.cs b/UI/ProgressSlider.cs new file mode 100644 index 0000000..9534080 --- /dev/null +++ b/UI/ProgressSlider.cs @@ -0,0 +1,24 @@ +using UnityEngine; +using UnityEngine.UI; + +public class ProgressSlider : MonoBehaviour +{ + public Slider SlUpdateProgress; + public Text TextUpdateProgress; + + public void SetUpdateProgress(float value) + { + if (value == 0 || value >= 1) { SetActive(false); } + else + { + SetActive(true); + SlUpdateProgress.value = value; + TextUpdateProgress.text = $"{Mathf.Round(value * 10000) / 100}%"; + } + } + + public void SetActive(bool active) + { + gameObject.SetActive(active); + } +} \ No newline at end of file diff --git a/UI/SettingsPanel.cs b/UI/SettingsPanel.cs new file mode 100644 index 0000000..fbc24f6 --- /dev/null +++ b/UI/SettingsPanel.cs @@ -0,0 +1,86 @@ +using UnityEngine; +using UnityEngine.UI; + +public class SettingsPanel : MonoBehaviour +{ + public UpdateHandler Handler; + public Dropdown DdRepository; + public InputField InRepositoryUrl; + + public Text TextUpdaterVersion; + public Text TextTranslatorVersion; + public Text TextTranslationVersion; + + private bool _settingsChanged; + + public SettingsPanel Init(string updaterVersion) + { + _settingsChanged = false; + TextUpdaterVersion.text = "Updater Version: " + updaterVersion; + TextTranslatorVersion.text = "KF3TL Version:\n" + PlayerPrefs.GetString(UpdateHandler.TRANSLATOR_VER, "undefined"); + TextTranslationVersion.text = "Translation Version:\n" + PlayerPrefs.GetString(UpdateHandler.TRANSLATION_VER, "undefined"); + + int mode = PlayerPrefs.GetInt(UpdateHandler.REPO_CUSTOM_NAME, 0); + OnSourceDropdown(mode); + DdRepository.SetValueWithoutNotify(mode); + + _settingsChanged = false; + gameObject.SetActive(true); + transform.SetAsLastSibling(); + return this; + } + + public void OnSourceDropdown(int choice) + { + switch (choice) + { + case 0: + PlayerPrefs.SetInt(UpdateHandler.REPO_CUSTOM_NAME, 0); + InRepositoryUrl.text = UpdateHandler.REPO_DEFAULT; + InRepositoryUrl.interactable = false; + break; + case 1: + PlayerPrefs.SetInt(UpdateHandler.REPO_CUSTOM_NAME, 1); + InRepositoryUrl.text = PlayerPrefs.GetString(UpdateHandler.REPO_CUSTOM_URL, UpdateHandler.REPO_DEFAULT); + InRepositoryUrl.interactable = true; + break; + } + PlayerPrefs.Save(); + _settingsChanged = true; + } + + public void OnSourceEditFinished(string text) + { + PlayerPrefs.SetString(UpdateHandler.REPO_CUSTOM_URL, text); + PlayerPrefs.Save(); + _settingsChanged = true; + } + + public void OnClearTranslatorButton() + { + PlayerPrefs.SetString(UpdateHandler.TRANSLATOR_VER, "undefined"); + PlayerPrefs.Save(); + TextTranslatorVersion.text = "KF3TL Version:\n" + PlayerPrefs.GetString(UpdateHandler.TRANSLATOR_VER, "undefined"); + _settingsChanged = true; + } + + public void OnClearTranslationButton() + { + PlayerPrefs.SetString(UpdateHandler.TRANSLATION_VER, "undefined"); + PlayerPrefs.Save(); + TextTranslationVersion.text = "Translation Version:\n" + PlayerPrefs.GetString(UpdateHandler.TRANSLATION_VER, "undefined"); + _settingsChanged = true; + } + + public void OnSettingsClose() + { + if (_settingsChanged) + { + Handler.StopAllCoroutines(); + Destroy(Handler.gameObject); + UpdateHandler.Create(); + return; + } + gameObject.SetActive(false); + } +} \ No newline at end of file diff --git a/UI/UpdateAvailable.cs b/UI/UpdateAvailable.cs new file mode 100644 index 0000000..46dc17a --- /dev/null +++ b/UI/UpdateAvailable.cs @@ -0,0 +1,38 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.UI; + +public class UpdateAvailable : MonoBehaviour +{ + public Text Header; + public Text CurrentVersion; + public Text NewVersion; + public System.Action OnDownloadAction; + public System.Action OnIgnoreAction; + public System.Action OnIgnorePersistentAction; + public Button IgnorePersistentButton; + + public UpdateAvailable Init(string title, string currentVersion, string newVersion) + { + Header.text = title; + CurrentVersion.text = "Current: " + currentVersion; + NewVersion.text = "Update: " + newVersion; + gameObject.SetActive(true); + return this; + } + + public void OnDownload() + { + OnDownloadAction?.Invoke(); + } + + public void OnIgnore() + { + OnIgnoreAction?.Invoke(); + } + + public void OnIgnorePersistent() + { + OnIgnorePersistentAction?.Invoke(); + } +} \ No newline at end of file diff --git a/UI/UpdateChecker.cs b/UI/UpdateChecker.cs new file mode 100644 index 0000000..5845d5c --- /dev/null +++ b/UI/UpdateChecker.cs @@ -0,0 +1,16 @@ +using UnityEngine; + +public class UpdateChecker : MonoBehaviour +{ + public UpdateHandler Handler; + + public void OpenSettings() + { + Handler.OpenSettingsDetached(); + } + + public void SetActive(bool active) + { + gameObject.SetActive(active); + } +} \ No newline at end of file diff --git a/UpdateHandler.asset b/UpdateHandler.asset index 7d8f44d..44426cf 100644 Binary files a/UpdateHandler.asset and b/UpdateHandler.asset differ diff --git a/UpdateHandler.cs b/UpdateHandler.cs index c198b82..acac0d6 100644 --- a/UpdateHandler.cs +++ b/UpdateHandler.cs @@ -1,31 +1,48 @@ -using System.IO.Compression; -using System.IO; using System; -using UnityEngine.Networking; -using UnityEngine.UI; -using UnityEngine; using System.Collections; +using System.IO; +using System.IO.Compression; using System.Reflection; +using UnityEngine; +using UnityEngine.Networking; using Newtonsoft.Json.Linq; public class UpdateHandler : MonoBehaviour { - private string _serverVersion = ""; - private float _messageTime = 0; - private bool _readyForDeletion = false; + /// Set by harmony/bepinex + public static string PluginVersion = "1.0.0"; - private const string PluginVersion = "1.1.1"; - private const string defaultRepositoryUrl = "https://git.japari.cafe/api/v1/repos/Vorked/VorkedTranslationPack"; + [Header("Message")] + public PopupMessage PanelMessage; + + [Header("UpdateChecker")] + public UpdateChecker UpdateChecker; + + [Header("UpdateProgress")] + public ProgressSlider UpdateProgress; + + [Header("UpdateAvailable")] + public UpdateAvailable UpdatePanel; + + [Header("Settings")] + public SettingsPanel PanelSettings; + + public const string REPO_DEFAULT = "https://git.japari.cafe/api/v1/repos/Vorked/VorkedTranslationPack"; //playerprefs keys - private const string translationVersionKey = "lastVersion"; - private const string repositoryUrlKey = "customRepositoryUrl"; - private const string customRepositoryKey = "customRepository"; + public const string TRANSLATION_VER = "translationVersion"; + public const string TRANSLATOR_VER = "KF3TLVersion"; + public const string REPO_CUSTOM_URL = "customRepositoryUrl"; + public const string REPO_CUSTOM_NAME = "customRepository"; + + private int _idleFrames = 0; public static void Create() { #if UNITY_EDITOR - var prefab = AssetBundle.LoadFromFile(@"R:\Unity\KF3Modder2\Assets\AssetBundles\UpdateHandler"); + //var prefab = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/UpdateHandler"); + var prefab = Resources.Load("UpdateHandler"); + Instantiate(prefab); #else var prefab = AssetBundle.LoadFromMemory(LoadResource("UpdateHandler.asset")); #endif @@ -55,103 +72,204 @@ public class UpdateHandler : MonoBehaviour { yield return 0; transform.SetAsLastSibling(); - PanelUpdateChecker.SetActive(true); + UpdateChecker.SetActive(true); - string repositoryUrl = PlayerPrefs.GetInt(customRepositoryKey, 0) == 0 ? defaultRepositoryUrl : PlayerPrefs.GetString(repositoryUrlKey, defaultRepositoryUrl); - string currentVersion = PlayerPrefs.GetString(translationVersionKey, "undefined"); - string newVersion = currentVersion; - string downloadError = ""; - bool updateSuccess = false; + yield return CheckKF3TLUpdate(); + yield return CheckTranslationUpdate(); - yield return DownloadText(repositoryUrl + "/branches/master", - (json) => - { - try - { - if (string.IsNullOrEmpty(json)) - { - throw new System.Exception("No response from " + repositoryUrl); - } - var responseObject = JObject.Parse(json); - newVersion = responseObject["commit"]["id"].ToString(); - updateSuccess = true; - } - catch (System.Exception e) - { - downloadError = e.Message; - updateSuccess = false; - } - } - ); - - if (!updateSuccess) - { - PanelUpdateChecker.SetActive(false); - yield return SetAwaitableMessage("Failed to check for update!\nError: " + downloadError, 5); - OpenSettings(); - _readyForDeletion = true; - yield break; - } - - Debug.Log("local version: " + currentVersion); - Debug.Log("server version: " + newVersion); - - if (newVersion != currentVersion) - { - OpenUpdateAvailable(); - _serverVersion = newVersion; - PanelUpdateChecker.SetActive(false); - } - else - { - float time = 0; - while (time < 3 || PanelSettings.activeInHierarchy) - { - time += Time.deltaTime; - yield return 0; - } - Destroy(this.gameObject); - } + yield return new WaitForSeconds(5); + UpdateChecker.SetActive(false); } private void Update() { - if (_messageTime > 0) + for (int i = 0; i < transform.childCount; i++) { - _messageTime -= Time.deltaTime; - - if (_messageTime <= 0) + if (transform.GetChild(i).gameObject.activeSelf) { - PanelMessage.SetActive(false); + _idleFrames = 0; + return; } } - else if (_readyForDeletion && !PanelSettings.activeInHierarchy) + + if (_idleFrames >= 5) { Destroy(this.gameObject); } + else + { + _idleFrames++; + } } - public IEnumerator UpdateTranslation() + public IEnumerator CheckKF3TLUpdate() { - PanelUpdateProgress.SetActive(true); - SlUpdateProgress.value = 0; + Debug.Log("Checking KF3TL Update"); + string currentVersion = PlayerPrefs.GetString(TRANSLATOR_VER, "undefined"); + var firstRun = currentVersion == "undefined"; + BranchCheckResult result = null; + + yield return GetRepoVersion("https://api.github.com/repos/Vorked/KF3TL/branches/main", data => + { + result = data; + }); + + if (result == null || !result.Success) + { + UpdateChecker.SetActive(false); + if (result == null) + { + yield return SetAwaitableMessage("Failed to get response from server", 5); + } + else + { + yield return SetAwaitableMessage("Failed to check for update!\nError: " + result.Error, 5); + } + yield break; + } + + Debug.Log("local version: " + currentVersion); + Debug.Log("server version: " + result.Version); + + if (firstRun) + { + yield return SetAwaitableMessage("First run detected. Press Ignore on next prompt if you have the newest version of KF3TL", 150); + } + + if (result.Version != currentVersion) + { + var panel = Instantiate(UpdatePanel, transform); + panel.Init("KF3TL Plugin Update", currentVersion, result.Version); + panel.OnDownloadAction = () => + { + StartCoroutine(UpdateKF3TL(result.Version, () => Destroy(panel.gameObject))); + panel.gameObject.SetActive(false); + }; + panel.OnIgnoreAction = () => + { + Destroy(panel.gameObject); + }; + if (firstRun) + { + panel.OnIgnorePersistentAction = () => + { + PlayerPrefs.SetString(TRANSLATOR_VER, result.Version); + PlayerPrefs.Save(); + Destroy(panel.gameObject); + }; + } + else + { + panel.IgnorePersistentButton.gameObject.SetActive(false); + } + while (panel != null) yield return 0; + } + } + + public IEnumerator UpdateKF3TL(string version, System.Action onComplete) + { byte[] bytes = null; - string repositoryUrl = PlayerPrefs.GetInt(customRepositoryKey, 0) == 0 ? defaultRepositoryUrl : PlayerPrefs.GetString(repositoryUrlKey, defaultRepositoryUrl); + yield return DownloadBytes("https://api.github.com/repos/Vorked/KF3TL/zipball/main", + (newBytes) => + { + bytes = newBytes; + }, + UpdateProgress.SetUpdateProgress + ); + + UpdateProgress.SetUpdateProgress(1); + + if (bytes.Length <= 0) + { + yield return SetAwaitableMessage("Failed to download the update!\nDownload new version from https://github.com/Vorked/KF3TL", 15); + yield break; + } + + var path = Application.dataPath + "/../" + "KF3TL.zip"; + File.WriteAllBytes(path, bytes); + + PlayerPrefs.SetString(TRANSLATOR_VER, version); + PlayerPrefs.Save(); + UpdateProgress.SetActive(false); + var message = Instantiate(PanelMessage, transform).Init($"New version downloaded as KF3TL.zip. Please close the game and install it.", 150); + message.OnFinishedAction = () => + { + System.Diagnostics.Process.Start("explorer.exe", "/select," + path.Replace("/", "\\")); + onComplete?.Invoke(); + }; + } + + public IEnumerator CheckTranslationUpdate() + { + Debug.Log("Checking Translation Update"); + + string repositoryUrl = PlayerPrefs.GetInt(REPO_CUSTOM_NAME, 0) == 0 ? REPO_DEFAULT : PlayerPrefs.GetString(REPO_CUSTOM_URL, REPO_DEFAULT); + string currentVersion = PlayerPrefs.GetString(TRANSLATION_VER, "undefined"); + BranchCheckResult result = null; + + yield return GetRepoVersion(repositoryUrl + "/branches/master", data => + { + result = data; + }); + + if (result == null || !result.Success) + { + UpdateChecker.SetActive(false); + if (result == null) + { + yield return SetAwaitableMessage("Failed to get response from server", 5); + } + else + { + yield return SetAwaitableMessage("Failed to check for update!\nError: " + result.Error, 5); + } + yield break; + } + + Debug.Log("local version: " + currentVersion); + Debug.Log("server version: " + result.Version); + + if (result.Version != currentVersion) + { + var panel = Instantiate(UpdatePanel, transform); + panel.Init("Translation pack", currentVersion, result.Version); + panel.OnDownloadAction = () => + { + StartCoroutine(UpdateTranslation(result.Version, () => Destroy(panel.gameObject))); + panel.gameObject.SetActive(false); + }; + panel.OnIgnoreAction = () => + { + Destroy(panel.gameObject); + }; + panel.OnIgnorePersistentAction = () => + { + PlayerPrefs.SetString(TRANSLATION_VER, result.Version); + PlayerPrefs.Save(); + Destroy(panel.gameObject); + }; + do yield return 0; while (panel != null); + } + } + + public IEnumerator UpdateTranslation(string version, System.Action onComplete) + { + byte[] bytes = null; + string repositoryUrl = PlayerPrefs.GetInt(REPO_CUSTOM_NAME, 0) == 0 ? REPO_DEFAULT : PlayerPrefs.GetString(REPO_CUSTOM_URL, REPO_DEFAULT); yield return DownloadBytes(repositoryUrl + "/archive/master.zip", (newBytes) => { bytes = newBytes; }, - SetUpdateProgress + UpdateProgress.SetUpdateProgress ); + UpdateProgress.SetUpdateProgress(1); + if (bytes.Length <= 0) { - PanelUpdateProgress.SetActive(false); yield return SetAwaitableMessage("Failed to download the update!\nCheck your settings.", 5); - OpenSettings(); - _readyForDeletion = true; yield break; } @@ -176,149 +294,62 @@ public class UpdateHandler : MonoBehaviour } catch (System.Exception e) { - SetMessage("Failed to install the update!\nError:" + e.Message, 5); - _readyForDeletion = true; + Instantiate(PanelMessage, transform).Init("Failed to install the update!\nError:" + e.Message, 5); yield break; } - PlayerPrefs.SetString(translationVersionKey, _serverVersion); + PlayerPrefs.SetString(TRANSLATION_VER, version); PlayerPrefs.Save(); - PanelUpdateProgress.SetActive(false); - SetMessage("Update completed! Please restart the game to apply it.", 5); - _readyForDeletion = true; + UpdateProgress.SetActive(false); + var message = Instantiate(PanelMessage, transform).Init("Update completed! Please restart the game to apply it.", 5); + message.OnFinishedAction = onComplete; } - #region Message - [Header("Message")] - public GameObject PanelMessage; - public Text TextMessage; - - private void SetMessage(string message, float time = 2) + private IEnumerator GetRepoVersion(string url, System.Action callback) { - PanelMessage.SetActive(true); - TextMessage.text = message; - _messageTime = time; + BranchCheckResult result = null; + yield return DownloadText(url, + (json) => + { + try + { + if (string.IsNullOrEmpty(json)) + { + throw new System.Exception("No response from " + url); + } + result = new BranchCheckResult(); + var newData = JObject.Parse(json).ToObject(); + result.Version = newData.commit.timestamp /* gitea */ ?? newData.commit.commit.author.date; //github + result.Success = true; + } + catch (System.Exception e) + { + result.Error = e.Message; + result.Success = false; + } + } + ); + callback(result); } private IEnumerator SetAwaitableMessage(string message, float time) { - SetMessage(message, time); - do - { - yield return 0; - } - while (PanelMessage.activeInHierarchy); + var msg = Instantiate(PanelMessage, transform).Init(message, time); + do yield return 0; while (msg != null); } - #endregion - #region UpdateChecker - [Header("UpdateChecker")] - public GameObject PanelUpdateChecker; - #endregion - - #region UpdateProgress - [Header("UpdateProgress")] - public GameObject PanelUpdateProgress; - public Slider SlUpdateProgress; - public Text TextUpdateProgress; - - public void SetUpdateProgress(float value) + public void OpenSettingsDetached() { - SlUpdateProgress.value = value; - TextUpdateProgress.text = $"{Mathf.Round(value * 10000) / 100}%"; + if (PanelSettings.gameObject.activeSelf) return; + StartCoroutine(OpenSettings()); } - #endregion - #region UpdateAvailable - [Header("UpdateAvailable")] - public GameObject PanelUpdateAvailable; - - public void OpenUpdateAvailable() + private IEnumerator OpenSettings() { - PanelUpdateAvailable.SetActive(true); + PanelSettings.Init(PluginVersion); + do yield return 0; while (PanelSettings.gameObject.activeSelf); } - public void OnIgnore() - { - Destroy(this.gameObject); - } - - public void OnDownload() - { - PanelUpdateAvailable.SetActive(false); - StartCoroutine(UpdateTranslation()); - } - #endregion - - #region Settings - private bool _settingsChanged = false; - [Header("Settings")] - public GameObject PanelSettings; - public Dropdown DdRepository; - public InputField InRepositoryUrl; - public Text TextUpdaterVersion; - public Text TextTranslationVersion; - - public void OpenSettings() - { - PanelSettings.gameObject.SetActive(true); - int mode = PlayerPrefs.GetInt(customRepositoryKey, 0); - OnSourceDropdown(mode); - DdRepository.SetValueWithoutNotify(mode); - TextUpdaterVersion.text = "Updater Version: " + PluginVersion; - TextTranslationVersion.text = "Translation Version: " + PlayerPrefs.GetString(translationVersionKey, "undefined"); - _settingsChanged = false; - } - - public void OnSourceDropdown(int choice) - { - switch (choice) - { - case 0: - PlayerPrefs.SetInt(customRepositoryKey, 0); - InRepositoryUrl.text = defaultRepositoryUrl; - InRepositoryUrl.interactable = false; - break; - case 1: - PlayerPrefs.SetInt(customRepositoryKey, 1); - InRepositoryUrl.text = PlayerPrefs.GetString(repositoryUrlKey, defaultRepositoryUrl); - InRepositoryUrl.interactable = true; - break; - } - PlayerPrefs.Save(); - _settingsChanged = true; - } - - public void OnSourceEditFinished(string text) - { - PlayerPrefs.SetString(repositoryUrlKey, text); - PlayerPrefs.Save(); - _settingsChanged = true; - } - - public void OnClearButton() - { - PlayerPrefs.SetString(translationVersionKey, "undefined"); - PlayerPrefs.Save(); - OpenSettings(); - _settingsChanged = true; - } - - public void OnSettingsClose() - { - if (_settingsChanged) - { - Destroy(this.gameObject); - Create(); - } - else - { - PanelSettings.SetActive(false); - } - } - - #endregion - public static IEnumerator DownloadBytes(string url, System.Action callback, System.Action onProgressChanged = null) { UnityWebRequest www = UnityWebRequest.Get(url); @@ -359,4 +390,38 @@ public class UpdateHandler : MonoBehaviour callback(www.downloadHandler.text); } } + + [System.Serializable] + public class BranchInfo + { + public CommitInfo commit; + + [System.Serializable] + public class CommitInfo + { + public string id; + public string sha; + public string timestamp; + public CommitInfo2 commit; + + [System.Serializable] + public class CommitInfo2 + { + public AuthorInfo author; + + [System.Serializable] + public class AuthorInfo + { + public string date; + } + } + } + } + + public class BranchCheckResult + { + public bool Success; + public string Version; + public string Error; + } } \ No newline at end of file