229 lines
8.0 KiB
GDScript
229 lines
8.0 KiB
GDScript
class_name PackageDownloader
|
|
extends Control
|
|
|
|
const MAIN_MENU_SCENE_PATH: String = "res://sources/menus/main/main_menu.tscn"
|
|
const DEVICE_SELECTION_SCENE_PATH: String = "res://sources/menus/device_selection/device_selection.tscn"
|
|
const LOGIN_SCENE_PATH: String = "res://sources/menus/login/login.tscn"
|
|
const USER_LANGUAGE_RESOURCES_PATH: String = "user://language_resources"
|
|
const ERROR_MESSAGES: Array[String] = [
|
|
"DISCONNECTED_ERROR",
|
|
"NO_INTERNET_ACCESS",
|
|
"ERROR_DOWNLOADING",
|
|
"INVALID_LANGUAGE_DIRECTORY",
|
|
]
|
|
|
|
var device_language: String
|
|
var current_language_path: String
|
|
var mutex: Mutex
|
|
var thread: Thread
|
|
var server_language_version: Dictionary = {}
|
|
var current_language_version: Dictionary = {}
|
|
|
|
@onready var http_request: HTTPRequest = $HTTPRequest
|
|
@onready var checking_label: Label = %CheckingLabel
|
|
@onready var download_label: Label = %DownloadLabel
|
|
@onready var copy_label: Label = %CopyLabel
|
|
@onready var download_bar: ProgressBar = %DownloadProgressBar
|
|
@onready var download_info: Label = %DownloadInfo
|
|
@onready var extract_bar: ProgressBar = %ExtractProgressBar
|
|
@onready var extract_info: Label = %ExtractInfo
|
|
@onready var error_label: Label = %ErrorLabel
|
|
@onready var error_popup: ConfirmPopup = $ErrorPopup
|
|
|
|
|
|
func _ready() -> void:
|
|
await get_tree().process_frame
|
|
|
|
Logger.trace("PackageDownloader: Starting with device language %s" % UserDataManager.get_device_settings().language)
|
|
|
|
# Check the teacher settings, if we are logged in (this scene should not be accessible otherwise)
|
|
var teacher_settings: TeacherSettings = UserDataManager.teacher_settings
|
|
if not teacher_settings:
|
|
UserDataManager.logout()
|
|
_show_error(0)
|
|
return
|
|
|
|
device_language = UserDataManager.get_device_settings().language
|
|
current_language_path = USER_LANGUAGE_RESOURCES_PATH.path_join(device_language)
|
|
current_language_version = UserDataManager.get_device_settings().language_versions.get(device_language, {})
|
|
|
|
Logger.trace("PackageDownloader: Checking internet access")
|
|
if not await ServerManager.check_internet_access():
|
|
# Offline mode, if a pack is already downloaded, go to next scene
|
|
if DirAccess.dir_exists_absolute(current_language_path):
|
|
if is_language_directory_valid(current_language_path):
|
|
_go_to_next_scene()
|
|
else:
|
|
_show_error(3)
|
|
else:
|
|
_show_error(1)
|
|
return
|
|
|
|
# Gets the info of the language pack on the server
|
|
var res: Dictionary = await ServerManager.get_language_pack_url(device_language)
|
|
Logger.trace("PackageDownloader: Language pack info received with code %d" % res.code)
|
|
if res.code == 200:
|
|
server_language_version = Time.get_datetime_dict_from_datetime_string(res.body.last_modified as String, false)
|
|
# Authentication failed, disconnect the user
|
|
elif res.code == 401:
|
|
UserDataManager.logout()
|
|
_show_error(0)
|
|
return
|
|
else:
|
|
UserDataManager.logout()
|
|
_show_error(2)
|
|
return
|
|
|
|
# If the language pack is not already downloaded or an update is needed
|
|
if not DirAccess.dir_exists_absolute(current_language_path) or current_language_version != server_language_version:
|
|
Logger.trace("A new version of the language pack has been detected.\n Current version = " + str(current_language_version) + "\n Server version = " + str(server_language_version))
|
|
|
|
checking_label.hide()
|
|
download_label.show()
|
|
download_bar.show()
|
|
download_info.show()
|
|
extract_bar.show()
|
|
extract_info.show()
|
|
|
|
# Create the language_resources folder
|
|
if not DirAccess.dir_exists_absolute(USER_LANGUAGE_RESOURCES_PATH):
|
|
DirAccess.make_dir_recursive_absolute(USER_LANGUAGE_RESOURCES_PATH)
|
|
|
|
# Delete the files from old language pack
|
|
if DirAccess.dir_exists_absolute(current_language_path):
|
|
Utils.clean_dir(current_language_path)
|
|
|
|
# Download the pack
|
|
http_request.set_download_file(USER_LANGUAGE_RESOURCES_PATH.path_join(device_language + ".zip"))
|
|
Logger.trace("PackageDownloader: Downloading pack from %s" % res.body.url)
|
|
http_request.request(res.body.url as String)
|
|
else:
|
|
download_bar.value = 1
|
|
extract_bar.value = 1
|
|
_go_to_next_scene()
|
|
|
|
|
|
# Check that folder is not empty and contains a file language.db
|
|
func is_language_directory_valid(path: String) -> bool:
|
|
var dir: DirAccess = DirAccess.open(path)
|
|
var error: Error = DirAccess.get_open_error()
|
|
if error != OK:
|
|
Logger.error("PackageDownloader: Is language directory valid: Cannot open directory %s. Error: %s" % [path, error_string(error)])
|
|
return false
|
|
if not dir:
|
|
Logger.error("PackageDownloader: Is language directory valid: Cannot open directory %s. dir is null" % path)
|
|
return false
|
|
|
|
if dir.list_dir_begin() != OK:
|
|
dir.list_dir_end()
|
|
return false
|
|
|
|
var file_name: String = dir.get_next()
|
|
dir.list_dir_end()
|
|
return file_name != "" && dir.file_exists("language.db")
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
if http_request.get_body_size() > 0:
|
|
var maximum: int = int(http_request.get_body_size()/1024.0)
|
|
var current: int = int(http_request.get_downloaded_bytes()/1024.0)
|
|
download_bar.max_value = maximum
|
|
download_bar.value = current
|
|
download_info.text = str(current) + "KB/" + str(maximum) + "KB"
|
|
|
|
|
|
func _exit_tree() -> void:
|
|
if thread:
|
|
thread.wait_to_finish()
|
|
|
|
|
|
func _copy_data(this: PackageDownloader) -> void:
|
|
# Check if a zip exists for the complete locale
|
|
if not FileAccess.file_exists(USER_LANGUAGE_RESOURCES_PATH.path_join(device_language + ".zip")):
|
|
return
|
|
|
|
Logger.trace("PackageDownloader: Extracting downloaded package")
|
|
|
|
var language_zip: String = device_language + ".zip"
|
|
var language_zip_path: String = USER_LANGUAGE_RESOURCES_PATH.path_join(language_zip)
|
|
|
|
# Create and connect the unzipper to the UI
|
|
var unzipper: FolderUnzipper = FolderUnzipper.new()
|
|
unzipper.file_count.connect(
|
|
func(count: int) -> void:
|
|
mutex.lock()
|
|
this.extract_bar.set_deferred("max_value", count)
|
|
mutex.unlock()
|
|
)
|
|
unzipper.file_copied.connect(
|
|
func(count: int, filename: String) -> void:
|
|
mutex.lock()
|
|
this.extract_info.set_deferred("text", filename)
|
|
this.extract_bar.set_deferred("value", count)
|
|
mutex.unlock()
|
|
)
|
|
|
|
# Cleanup previous files
|
|
Utils.delete_directory_recursive(ProjectSettings.globalize_path(current_language_path))
|
|
|
|
# Extract the archive
|
|
var subfolder: String = unzipper.extract(language_zip_path, USER_LANGUAGE_RESOURCES_PATH, false)
|
|
if subfolder == "":
|
|
Logger.error("PackageDownloader: Extraction failed for %s" % language_zip_path)
|
|
return
|
|
|
|
# Move the data to the locale folder of the user
|
|
var error: Error = DirAccess.rename_absolute(USER_LANGUAGE_RESOURCES_PATH.path_join(subfolder), current_language_path)
|
|
if error != OK:
|
|
Logger.error("PackageDownloader: Error " + error_string(error) + " while renaming folder from %s to %s" % [USER_LANGUAGE_RESOURCES_PATH.path_join(subfolder), current_language_path])
|
|
else:
|
|
Logger.trace("PackageDownloader: Package extracted to %s" % current_language_path)
|
|
|
|
# Cleanup unnecessary files
|
|
DirAccess.remove_absolute(language_zip_path)
|
|
Logger.trace("PackageDownloader: Removed temporary archive %s" % language_zip_path)
|
|
|
|
# Go to main menu
|
|
this.call_thread_safe("_go_to_next_scene")
|
|
|
|
|
|
func _show_error(error: int) -> void:
|
|
error_popup.content_text = ERROR_MESSAGES[error]
|
|
error_popup.show()
|
|
|
|
|
|
func _go_to_main_menu() -> void:
|
|
get_tree().change_scene_to_file(MAIN_MENU_SCENE_PATH)
|
|
|
|
|
|
func _go_to_next_scene() -> void:
|
|
if not Database.is_open:
|
|
Database.connect_to_db()
|
|
|
|
if not server_language_version.is_empty():
|
|
UserDataManager.set_language_version(device_language, server_language_version)
|
|
|
|
# Check if we have a valid device id
|
|
if not UserDataManager.get_device_settings().device_id:
|
|
get_tree().change_scene_to_file(DEVICE_SELECTION_SCENE_PATH)
|
|
# Go directly to the login scene
|
|
else:
|
|
get_tree().change_scene_to_file(LOGIN_SCENE_PATH)
|
|
|
|
|
|
func _on_http_request_request_completed(_result: int, response_code: int, _headers: PackedStringArray, _body: PackedByteArray) -> void:
|
|
Logger.trace("PackageDownloader: Download completed with HTTP code %d" % response_code)
|
|
if response_code == 200:
|
|
mutex = Mutex.new()
|
|
thread = Thread.new()
|
|
download_label.hide()
|
|
copy_label.show()
|
|
|
|
thread.start(_copy_data.bind(self))
|
|
else:
|
|
error_label.show()
|
|
|
|
|
|
func _on_disconnected_popup_accepted() -> void:
|
|
_go_to_main_menu()
|