using System.IO.Compression; using System.IO; using System; using UnityEngine.Networking; using UnityEngine.UI; using UnityEngine; using System.Collections; using System.Reflection; using Newtonsoft.Json.Linq; public class UpdateHandler : MonoBehaviour { private string _serverVersion = ""; private float _messageTime = 0; private bool _readyForDeletion = false; private const string PluginVersion = "1.1.1"; private const string defaultRepositoryUrl = "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 static void Create() { #if UNITY_EDITOR var prefab = AssetBundle.LoadFromFile(@"R:\Unity\KF3Modder2\Assets\AssetBundles\UpdateHandler"); #else var prefab = AssetBundle.LoadFromMemory(LoadResource("UpdateHandler.asset")); #endif Instantiate(prefab.LoadAsset("UpdateHandler")); prefab.Unload(false); } public static byte[] LoadResource(string fileName) { Assembly asm = Assembly.GetExecutingAssembly(); fileName = asm.GetName().Name + "." + fileName; var bytes = new byte[0]; using (Stream resFilestream = asm.GetManifestResourceStream(fileName)) { if (resFilestream == null) return null; byte[] byteArray = new byte[resFilestream.Length]; resFilestream.Read(byteArray, 0, byteArray.Length); bytes = byteArray; } Console.WriteLine(bytes.Length); return bytes; } public IEnumerator Start() { yield return 0; transform.SetAsLastSibling(); PanelUpdateChecker.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 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); } } private void Update() { if (_messageTime > 0) { _messageTime -= Time.deltaTime; if (_messageTime <= 0) { PanelMessage.SetActive(false); } } else if (_readyForDeletion && !PanelSettings.activeInHierarchy) { Destroy(this.gameObject); } } public IEnumerator UpdateTranslation() { PanelUpdateProgress.SetActive(true); SlUpdateProgress.value = 0; byte[] bytes = null; string repositoryUrl = PlayerPrefs.GetInt(customRepositoryKey, 0) == 0 ? defaultRepositoryUrl : PlayerPrefs.GetString(repositoryUrlKey, defaultRepositoryUrl); yield return DownloadBytes(repositoryUrl + "/archive/master.zip", (newBytes) => { bytes = newBytes; }, SetUpdateProgress ); if (bytes.Length <= 0) { PanelUpdateProgress.SetActive(false); yield return SetAwaitableMessage("Failed to download the update!\nCheck your settings.", 5); OpenSettings(); _readyForDeletion = true; yield break; } try { using (var compressedFileStream = new MemoryStream(bytes)) { using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Read, false)) { foreach (var file in zipArchive.Entries) { if (file.Name != "") { //remove repo name from file path var outFilePath = file.FullName.Split(new[] { '/' }, 2)[1]; Directory.CreateDirectory(Path.GetDirectoryName(outFilePath)); file.ExtractToFile(outFilePath, true); } } } } } catch (System.Exception e) { SetMessage("Failed to install the update!\nError:" + e.Message, 5); _readyForDeletion = true; yield break; } PlayerPrefs.SetString(translationVersionKey, _serverVersion); PlayerPrefs.Save(); PanelUpdateProgress.SetActive(false); SetMessage("Update completed! Please restart the game to apply it.", 5); _readyForDeletion = true; } #region Message [Header("Message")] public GameObject PanelMessage; public Text TextMessage; private void SetMessage(string message, float time = 2) { PanelMessage.SetActive(true); TextMessage.text = message; _messageTime = time; } private IEnumerator SetAwaitableMessage(string message, float time) { SetMessage(message, time); do { yield return 0; } while (PanelMessage.activeInHierarchy); } #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) { SlUpdateProgress.value = value; TextUpdateProgress.text = $"{Mathf.Round(value * 10000) / 100}%"; } #endregion #region UpdateAvailable [Header("UpdateAvailable")] public GameObject PanelUpdateAvailable; public void OpenUpdateAvailable() { PanelUpdateAvailable.SetActive(true); } 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); var request = www.SendWebRequest(); while (!request.isDone) { onProgressChanged?.Invoke(request.progress); yield return 0; } if (www.result != UnityWebRequest.Result.Success) { Debug.Log(url); Debug.Log(www.error); callback(null); } else { callback(www.downloadHandler.data); } } public static IEnumerator DownloadText(string url, System.Action callback) { UnityWebRequest www = UnityWebRequest.Get(url); yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.Success) { Debug.Log(url); Debug.Log(www.error); callback(null); } else { callback(www.downloadHandler.text); } } }