]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Editor/Common/update_manager.gd
Initial Godot project with Dialogic 2.0-Alpha-17
[wolf-seeking-sheep.git] / addons / dialogic / Editor / Common / update_manager.gd
1 @tool
2 extends Node
3
4 ## Script that checks for new versions and can install them.
5
6 signal update_check_completed(result:UpdateCheckResult)
7 signal downdload_completed(result:DownloadResult)
8
9 enum UpdateCheckResult {UPDATE_AVAILABLE, UP_TO_DATE, NO_ACCESS}
10 enum DownloadResult {SUCCESS, FAILURE}
11 enum ReleaseState {ALPHA, BETA, STABLE}
12
13 const REMOTE_RELEASES_URL := "https://api.github.com/repos/dialogic-godot/dialogic/releases"
14 const TEMP_FILE_NAME := "user://temp.zip"
15
16 var current_version := ""
17 var update_info: Dictionary
18 var current_info: Dictionary
19
20 var version_indicator: Button
21
22 func _ready() -> void:
23         request_update_check()
24
25         setup_version_indicator()
26
27
28
29 func get_current_version() -> String:
30         var plugin_cfg := ConfigFile.new()
31         plugin_cfg.load("res://addons/dialogic/plugin.cfg")
32         return plugin_cfg.get_value('plugin', 'version', 'unknown version')
33
34
35 func request_update_check() -> void:
36         if $UpdateCheckRequest.get_http_client_status() == HTTPClient.STATUS_DISCONNECTED:
37                 $UpdateCheckRequest.request(REMOTE_RELEASES_URL)
38
39
40 func _on_UpdateCheck_request_completed(result:int, response_code:int, headers:PackedStringArray, body:PackedByteArray) -> void:
41         if result != HTTPRequest.RESULT_SUCCESS:
42                 update_check_completed.emit(UpdateCheckResult.NO_ACCESS)
43                 return
44
45         # Work out the next version from the releases information on GitHub
46         var response: Variant = JSON.parse_string(body.get_string_from_utf8())
47         if typeof(response) != TYPE_ARRAY: return
48
49
50         var current_release_info := get_release_tag_info(get_current_version())
51
52         # GitHub releases are in order of creation, not order of version
53         var versions: Array = (response as Array).filter(compare_versions.bind(current_release_info))
54         if versions.size() > 0:
55                 update_info = versions[0]
56                 update_check_completed.emit(UpdateCheckResult.UPDATE_AVAILABLE)
57         else:
58                 update_info = current_info
59                 update_check_completed.emit(UpdateCheckResult.UP_TO_DATE)
60
61
62 func compare_versions(release, current_release_info:Dictionary) -> bool:
63         var checked_release_info := get_release_tag_info(release.tag_name)
64
65         if checked_release_info.major < current_release_info.major:
66                 return false
67
68         if checked_release_info.minor < current_release_info.minor:
69                 return false
70
71         if checked_release_info.state < current_release_info.state:
72                 return false
73
74         elif checked_release_info.state == current_release_info.state:
75                 if checked_release_info.state_version < current_release_info.state_version:
76                         return false
77
78                 if checked_release_info.state_version == current_release_info.state_version:
79                         current_info = release
80                         return false
81
82                 if checked_release_info.state == ReleaseState.STABLE:
83                         if checked_release_info.minor == current_release_info.minor:
84                                 current_info = release
85                                 return false
86
87         return true
88
89
90 func get_release_tag_info(release_tag:String) -> Dictionary:
91         release_tag = release_tag.strip_edges().trim_prefix('v')
92         release_tag = release_tag.substr(0, release_tag.find('('))
93         release_tag = release_tag.to_lower()
94
95         var regex := RegEx.create_from_string(r"(?<major>\d+\.\d+)(-(?<state>alpha|beta)-)?(?(2)(?<stateversion>\d*)|\.(?<minor>\d*))?")
96
97         var result: RegExMatch = regex.search(release_tag)
98         if !result:
99                 return {}
100
101         var info: Dictionary = {'tag':release_tag}
102         info['major'] = float(result.get_string('major'))
103         info['minor'] = int(result.get_string('minor'))
104
105         match result.get_string('state'):
106                 'alpha':
107                         info['state'] = ReleaseState.ALPHA
108                 'beta':
109                         info['state'] = ReleaseState.BETA
110                 _:
111                         info['state'] = ReleaseState.STABLE
112
113         info['state_version'] = int(result.get_string('stateversion'))
114
115         return info
116
117
118 func request_update_download() -> void:
119         # Safeguard the actual dialogue manager repo from accidentally updating itself
120         if DirAccess.dir_exists_absolute("res://test-project/"):
121                 prints("[Dialogic] Looks like you are working on the addon. You can't update the addon from within itself.")
122                 downdload_completed.emit(DownloadResult.FAILURE)
123                 return
124
125         $DownloadRequest.request(update_info.zipball_url)
126
127
128 func _on_DownloadRequest_completed(result:int, response_code:int, headers:PackedStringArray, body:PackedByteArray):
129         if result != HTTPRequest.RESULT_SUCCESS:
130                 downdload_completed.emit(DownloadResult.FAILURE)
131                 return
132
133         # Save the downloaded zip
134         var zip_file: FileAccess = FileAccess.open(TEMP_FILE_NAME, FileAccess.WRITE)
135         zip_file.store_buffer(body)
136         zip_file.close()
137
138         OS.move_to_trash(ProjectSettings.globalize_path("res://addons/dialogic"))
139
140         var zip_reader: ZIPReader = ZIPReader.new()
141         zip_reader.open(TEMP_FILE_NAME)
142         var files: PackedStringArray = zip_reader.get_files()
143
144         var base_path: String = files[0].path_join('addons/')
145         for path in files:
146                 if not "dialogic/" in path:
147                         continue
148
149                 var new_file_path: String = path.replace(base_path, "")
150                 if path.ends_with("/"):
151                         DirAccess.make_dir_recursive_absolute("res://addons/".path_join(new_file_path))
152                 else:
153                         var file: FileAccess = FileAccess.open("res://addons/".path_join(new_file_path), FileAccess.WRITE)
154                         file.store_buffer(zip_reader.read_file(path))
155
156         zip_reader.close()
157         DirAccess.remove_absolute(TEMP_FILE_NAME)
158
159         downdload_completed.emit(DownloadResult.SUCCESS)
160
161
162 ######################  SOME UI MANAGEMENT #####################################
163 ################################################################################
164
165 func setup_version_indicator() -> void:
166         version_indicator = %Sidebar.get_node('%CurrentVersion')
167         version_indicator.pressed.connect($Window/UpdateInstallWindow.open)
168         version_indicator.text = get_current_version()
169
170
171 func _on_update_check_completed(result:int):
172         var result_color: Color
173         match result:
174                 UpdateCheckResult.UPDATE_AVAILABLE:
175                         result_color = version_indicator.get_theme_color("warning_color", "Editor")
176                         version_indicator.icon = version_indicator.get_theme_icon("StatusWarning", "EditorIcons")
177                         $Window/UpdateInstallWindow.load_info(update_info, result)
178                 UpdateCheckResult.UP_TO_DATE:
179                         result_color = version_indicator.get_theme_color("success_color", "Editor")
180                         version_indicator.icon = version_indicator.get_theme_icon("StatusSuccess", "EditorIcons")
181                         $Window/UpdateInstallWindow.load_info(current_info, result)
182                 UpdateCheckResult.NO_ACCESS:
183                         result_color = version_indicator.get_theme_color("success_color", "Editor")
184                         version_indicator.icon = version_indicator.get_theme_icon("GuiRadioCheckedDisabled", "EditorIcons")
185                         $Window/UpdateInstallWindow.load_info(update_info, result)
186
187         version_indicator.add_theme_color_override('font_color', result_color)
188         version_indicator.add_theme_color_override('font_hover_color', result_color.lightened(0.5))
189         version_indicator.add_theme_color_override('font_pressed_color', result_color)
190         version_indicator.add_theme_color_override('font_focus_color', result_color)