Files
Kalulu/sources/menus/language_selection/package_downloader.gd
T
2025-01-28 15:20:38 +01:00

213 lines
6.6 KiB
GDScript

extends Control
class_name PackageDownloader
const ConfirmPopup: = preload("res://sources/ui/popup.gd")
const main_menu_scene_path: = "res://sources/menus/main/main_menu.tscn"
const device_selection_scene_path: = "res://sources/menus/device_selection/device_selection.tscn"
const login_scene_path := "res://sources/menus/login/login.tscn"
const user_language_resources_path: = "user://language_resources"
const error_messages: Array[String] = [
"DISCONECTED_ERROR",
"NO_INTERNET_ACCESS",
"ERROR_DOWNLOADING"
]
@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
var device_language: String
var current_language_path: String
var mutex: Mutex
var thread: Thread
var server_version: Dictionary
func _ready() -> void:
await get_tree().process_frame
# 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)
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):
_go_to_next_scene()
else:
_show_error(1)
return
# Check the configuration
var server_configuration: = await ServerManager.get_configuration()
if server_configuration.code == 401:
UserDataManager.logout()
_show_error(0)
return
elif server_configuration.code != 200:
UserDataManager.logout()
_show_error(2)
return
# Check if the last_updated date is superior on the server, then update the configuration
UserDataManager.update_configuration(server_configuration.body as Dictionary)
# Get the current language version
var current_version: Dictionary = UserDataManager.get_device_settings().language_versions.get(device_language, {})
# Gets the info of the language pack on the server
var res: = await ServerManager.get_language_pack_url(device_language)
if res.code == 200:
server_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_version != server_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):
_delete_dir(current_language_path)
# Download the pack
http_request.set_download_file(user_language_resources_path.path_join(device_language + ".zip"))
http_request.request(res.body.url as String)
else:
download_bar.value = 1
extract_bar.value = 1
_go_to_next_scene()
func _process(_delta: float) -> void:
if http_request.get_body_size() > 0:
@warning_ignore("integer_division")
var maximum: = int(http_request.get_body_size()/1024)
@warning_ignore("integer_division")
var current: = int(http_request.get_downloaded_bytes()/1024)
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
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.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()
)
# Extract the archive
var subfolder: String = unzipper.extract(language_zip_path, user_language_resources_path, false)
# Move the data to the locale folder of the user
DirAccess.rename_absolute(user_language_resources_path.path_join(subfolder), current_language_path)
# Cleanup unnecessary files
DirAccess.remove_absolute(language_zip_path)
# Go to main menu
this.call_thread_safe("_go_to_next_scene")
func _delete_dir(path: String) -> void:
var dir: = DirAccess.open(path)
for file in dir.get_files():
dir.remove(file)
for subfolder in dir.get_directories():
_delete_dir(path.path_join(subfolder))
dir.remove(subfolder)
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()
UserDataManager.set_language_version(device_language, server_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:
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()