2022-03-15 13:29:32 +01:00
/*************************************************************************/
/* export_template_manager.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
# include "export_template_manager.h"
# include "core/io/json.h"
# include "core/io/zip_io.h"
# include "core/os/dir_access.h"
# include "core/os/input.h"
# include "core/os/keyboard.h"
# include "core/version.h"
# include "editor_node.h"
# include "editor_scale.h"
# include "progress_dialog.h"
2022-03-17 12:06:40 +01:00
# include "scene/gui/box_container.h"
2022-03-18 19:00:13 +01:00
# include "scene/gui/link_button.h"
2022-03-15 13:29:32 +01:00
void ExportTemplateManager : : _update_template_status ( ) {
// Fetch installed templates from the file system.
DirAccess * da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
const String & templates_dir = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) ;
Error err = da - > change_dir ( templates_dir ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not access templates directory at ' " + templates_dir + " '. " ) ;
Set < String > templates ;
da - > list_dir_begin ( ) ;
if ( err = = OK ) {
String c = da - > get_next ( ) ;
while ( c ! = String ( ) ) {
if ( da - > current_is_dir ( ) & & ! c . begins_with ( " . " ) ) {
templates . insert ( c ) ;
}
c = da - > get_next ( ) ;
}
}
da - > list_dir_end ( ) ;
memdelete ( da ) ;
// Update the state of the current version.
String current_version = VERSION_FULL_CONFIG ;
current_value - > set_text ( current_version ) ;
if ( templates . has ( current_version ) ) {
current_missing_label - > hide ( ) ;
current_installed_label - > show ( ) ;
current_installed_hb - > show ( ) ;
current_version_exists = true ;
} else {
current_installed_label - > hide ( ) ;
current_missing_label - > show ( ) ;
current_installed_hb - > hide ( ) ;
current_version_exists = false ;
}
if ( is_downloading_templates ) {
install_options_vb - > hide ( ) ;
download_progress_hb - > show ( ) ;
} else {
download_progress_hb - > hide ( ) ;
install_options_vb - > show ( ) ;
if ( templates . has ( current_version ) ) {
current_installed_path - > set_text ( templates_dir . plus_file ( current_version ) ) ;
}
}
// Update the list of other installed versions.
installed_table - > clear ( ) ;
TreeItem * installed_root = installed_table - > create_item ( ) ;
for ( Set < String > : : Element * E = templates . back ( ) ; E ; E = E - > prev ( ) ) {
String version_string = E - > get ( ) ;
if ( version_string = = current_version ) {
continue ;
}
TreeItem * ti = installed_table - > create_item ( installed_root ) ;
ti - > set_text ( 0 , version_string ) ;
ti - > add_button ( 0 , get_icon ( " Folder " , " EditorIcons " ) , OPEN_TEMPLATE_FOLDER , false , TTR ( " Open the folder containing these templates. " ) ) ;
ti - > add_button ( 0 , get_icon ( " Remove " , " EditorIcons " ) , UNINSTALL_TEMPLATE , false , TTR ( " Uninstall these templates. " ) ) ;
}
minimum_size_changed ( ) ;
update ( ) ;
}
void ExportTemplateManager : : _download_current ( ) {
if ( is_downloading_templates ) {
return ;
}
is_downloading_templates = true ;
install_options_vb - > hide ( ) ;
download_progress_hb - > show ( ) ;
if ( mirrors_available ) {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . empty ( ) ) {
_set_current_progress_status ( TTR ( " There are no mirrors available. " ) , true ) ;
return ;
}
_download_template ( mirror_url , true ) ;
} else if ( ! mirrors_available & & ! is_refreshing_mirrors ) {
_set_current_progress_status ( TTR ( " Retrieving the mirror list... " ) ) ;
_refresh_mirrors ( ) ;
}
}
void ExportTemplateManager : : _download_template ( const String & p_url , bool p_skip_check ) {
if ( ! p_skip_check & & is_downloading_templates ) {
return ;
}
is_downloading_templates = true ;
install_options_vb - > hide ( ) ;
download_progress_hb - > show ( ) ;
_set_current_progress_status ( TTR ( " Starting the download... " ) ) ;
download_templates - > set_download_file ( EditorSettings : : get_singleton ( ) - > get_cache_dir ( ) . plus_file ( " tmp_templates.tpz " ) ) ;
download_templates - > set_use_threads ( true ) ;
const String proxy_host = EDITOR_DEF ( " network/http_proxy/host " , " " ) ;
const int proxy_port = EDITOR_DEF ( " network/http_proxy/port " , - 1 ) ;
download_templates - > set_http_proxy ( proxy_host , proxy_port ) ;
download_templates - > set_https_proxy ( proxy_host , proxy_port ) ;
Error err = download_templates - > request ( p_url ) ;
if ( err ! = OK ) {
_set_current_progress_status ( TTR ( " Error requesting URL: " ) + " " + p_url , true ) ;
return ;
}
set_process ( true ) ;
_set_current_progress_status ( TTR ( " Connecting to the mirror... " ) ) ;
}
void ExportTemplateManager : : _download_template_completed ( int p_status , int p_code , const PoolStringArray & headers , const PoolByteArray & p_data ) {
switch ( p_status ) {
case HTTPRequest : : RESULT_CANT_RESOLVE : {
_set_current_progress_status ( TTR ( " Can't resolve the requested address. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_BODY_SIZE_LIMIT_EXCEEDED :
case HTTPRequest : : RESULT_CONNECTION_ERROR :
case HTTPRequest : : RESULT_CHUNKED_BODY_SIZE_MISMATCH :
case HTTPRequest : : RESULT_SSL_HANDSHAKE_ERROR :
case HTTPRequest : : RESULT_CANT_CONNECT : {
_set_current_progress_status ( TTR ( " Can't connect to the mirror. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_NO_RESPONSE : {
_set_current_progress_status ( TTR ( " No response from the mirror. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_REQUEST_FAILED : {
_set_current_progress_status ( TTR ( " Request failed. " ) , true ) ;
} break ;
case HTTPRequest : : RESULT_REDIRECT_LIMIT_REACHED : {
_set_current_progress_status ( TTR ( " Request ended up in a redirect loop. " ) , true ) ;
} break ;
default : {
if ( p_code ! = 200 ) {
_set_current_progress_status ( TTR ( " Request failed: " ) + " " + itos ( p_code ) , true ) ;
} else {
_set_current_progress_status ( TTR ( " Download complete; extracting templates... " ) ) ;
String path = download_templates - > get_download_file ( ) ;
is_downloading_templates = false ;
bool ret = _install_file_selected ( path , true ) ;
if ( ret ) {
// Clean up downloaded file.
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
Error err = da - > remove ( path ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > add_io_error ( TTR ( " Cannot remove temporary file: " ) + " \n " + path + " \n " ) ;
}
} else {
EditorNode : : get_singleton ( ) - > add_io_error ( vformat ( TTR ( " Templates installation failed. \n The problematic templates archives can be found at '%s'. " ) , path ) ) ;
}
}
} break ;
}
set_process ( false ) ;
}
void ExportTemplateManager : : _cancel_template_download ( ) {
if ( ! is_downloading_templates ) {
return ;
}
download_templates - > cancel_request ( ) ;
download_progress_hb - > hide ( ) ;
install_options_vb - > show ( ) ;
is_downloading_templates = false ;
}
void ExportTemplateManager : : _refresh_mirrors ( ) {
if ( is_refreshing_mirrors ) {
return ;
}
is_refreshing_mirrors = true ;
String current_version = VERSION_FULL_CONFIG ;
const String mirrors_metadata_url = " https://godotengine.org/mirrorlist/ " + current_version + " .json " ;
request_mirrors - > request ( mirrors_metadata_url ) ;
}
void ExportTemplateManager : : _refresh_mirrors_completed ( int p_status , int p_code , const PoolStringArray & headers , const PoolByteArray & p_data ) {
if ( p_status ! = HTTPRequest : : RESULT_SUCCESS | | p_code ! = 200 ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error getting the list of mirrors. " ) ) ;
is_refreshing_mirrors = false ;
if ( is_downloading_templates ) {
_cancel_template_download ( ) ;
}
return ;
}
String response_json ;
{
PoolByteArray : : Read r = p_data . read ( ) ;
response_json . parse_utf8 ( ( const char * ) r . ptr ( ) , p_data . size ( ) ) ;
}
Variant json ;
String errs ;
int errline ;
Error err = JSON : : parse ( response_json , json , errs , errline ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error parsing JSON with the list of mirrors. Please report this issue! " ) ) ;
is_refreshing_mirrors = false ;
if ( is_downloading_templates ) {
_cancel_template_download ( ) ;
}
return ;
}
mirrors_list - > clear ( ) ;
mirrors_list - > add_item ( TTR ( " Best available mirror " ) , 0 ) ;
mirrors_available = false ;
Dictionary data = json ;
if ( data . has ( " mirrors " ) ) {
Array mirrors = data [ " mirrors " ] ;
for ( int i = 0 ; i < mirrors . size ( ) ; i + + ) {
Dictionary m = mirrors [ i ] ;
ERR_CONTINUE ( ! m . has ( " url " ) | | ! m . has ( " name " ) ) ;
mirrors_list - > add_item ( m [ " name " ] ) ;
mirrors_list - > set_item_metadata ( i + 1 , m [ " url " ] ) ;
mirrors_available = true ;
}
}
if ( ! mirrors_available ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No download links found for this version. Direct download is only available for official releases. " ) ) ;
if ( is_downloading_templates ) {
_cancel_template_download ( ) ;
}
}
is_refreshing_mirrors = false ;
if ( is_downloading_templates ) {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . empty ( ) ) {
_set_current_progress_status ( TTR ( " There are no mirrors available. " ) , true ) ;
return ;
}
_download_template ( mirror_url , true ) ;
}
}
bool ExportTemplateManager : : _humanize_http_status ( HTTPRequest * p_request , String * r_status , int * r_downloaded_bytes , int * r_total_bytes ) {
* r_status = " " ;
* r_downloaded_bytes = - 1 ;
* r_total_bytes = - 1 ;
bool success = true ;
switch ( p_request - > get_http_client_status ( ) ) {
case HTTPClient : : STATUS_DISCONNECTED :
* r_status = TTR ( " Disconnected " ) ;
success = false ;
break ;
case HTTPClient : : STATUS_RESOLVING :
* r_status = TTR ( " Resolving " ) ;
break ;
case HTTPClient : : STATUS_CANT_RESOLVE :
* r_status = TTR ( " Can't Resolve " ) ;
success = false ;
break ;
case HTTPClient : : STATUS_CONNECTING :
* r_status = TTR ( " Connecting... " ) ;
break ;
case HTTPClient : : STATUS_CANT_CONNECT :
* r_status = TTR ( " Can't Connect " ) ;
success = false ;
break ;
case HTTPClient : : STATUS_CONNECTED :
* r_status = TTR ( " Connected " ) ;
break ;
case HTTPClient : : STATUS_REQUESTING :
* r_status = TTR ( " Requesting... " ) ;
break ;
case HTTPClient : : STATUS_BODY :
* r_status = TTR ( " Downloading " ) ;
* r_downloaded_bytes = p_request - > get_downloaded_bytes ( ) ;
* r_total_bytes = p_request - > get_body_size ( ) ;
if ( p_request - > get_body_size ( ) > 0 ) {
* r_status + = " " + String : : humanize_size ( p_request - > get_downloaded_bytes ( ) ) + " / " + String : : humanize_size ( p_request - > get_body_size ( ) ) ;
} else {
* r_status + = " " + String : : humanize_size ( p_request - > get_downloaded_bytes ( ) ) ;
}
break ;
case HTTPClient : : STATUS_CONNECTION_ERROR :
* r_status = TTR ( " Connection Error " ) ;
success = false ;
break ;
case HTTPClient : : STATUS_SSL_HANDSHAKE_ERROR :
* r_status = TTR ( " SSL Handshake Error " ) ;
success = false ;
break ;
}
return success ;
}
void ExportTemplateManager : : _set_current_progress_status ( const String & p_status , bool p_error ) {
download_progress_bar - > hide ( ) ;
download_progress_label - > set_text ( p_status ) ;
if ( p_error ) {
download_progress_label - > add_color_override ( " font_color " , get_color ( " error_color " , " Editor " ) ) ;
} else {
download_progress_label - > add_color_override ( " font_color " , get_color ( " font_color " , " Label " ) ) ;
}
}
void ExportTemplateManager : : _set_current_progress_value ( float p_value , const String & p_status ) {
download_progress_bar - > show ( ) ;
download_progress_bar - > set_value ( p_value ) ;
download_progress_label - > set_text ( p_status ) ;
// Progress cannot be happening with an error, so make sure that the color is correct.
download_progress_label - > add_color_override ( " font_color " , get_color ( " font_color " , " Label " ) ) ;
}
void ExportTemplateManager : : _install_file ( ) {
install_file_dialog - > popup_centered_ratio ( ) ;
}
bool ExportTemplateManager : : _install_file_selected ( const String & p_file , bool p_skip_progress ) {
// unzClose() will take care of closing the file stored in the unzFile,
// so we don't need to `memdelete(fa)` in this method.
FileAccess * fa = nullptr ;
zlib_filefunc_def io = zipio_create_io_from_file ( & fa ) ;
unzFile pkg = unzOpen2 ( p_file . utf8 ( ) . get_data ( ) , & io ) ;
if ( ! pkg ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Can't open the export templates file. " ) ) ;
return false ;
}
int ret = unzGoToFirstFile ( pkg ) ;
// Count them and find version.
int fc = 0 ;
String version ;
String contents_dir ;
while ( ret = = UNZ_OK ) {
unz_file_info info ;
char fname [ 16384 ] ;
ret = unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , nullptr , 0 , nullptr , 0 ) ;
String file = String : : utf8 ( fname ) ;
if ( file . ends_with ( " version.txt " ) ) {
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
// Read.
unzOpenCurrentFile ( pkg ) ;
ret = unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
unzCloseCurrentFile ( pkg ) ;
String data_str ;
data_str . parse_utf8 ( ( const char * ) data . ptr ( ) , data . size ( ) ) ;
data_str = data_str . strip_edges ( ) ;
// Version number should be of the form major.minor[.patch].status[.module_config]
// so it can in theory have 3 or more slices.
if ( data_str . get_slice_count ( " . " ) < 3 ) {
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " Invalid version.txt format inside the export templates file: %s. " ) , data_str ) ) ;
unzClose ( pkg ) ;
return false ;
}
version = data_str ;
contents_dir = file . get_base_dir ( ) . trim_suffix ( " / " ) . trim_suffix ( " \\ " ) ;
}
if ( file . get_file ( ) . size ( ) ! = 0 ) {
fc + + ;
}
ret = unzGoToNextFile ( pkg ) ;
}
if ( version = = String ( ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No version.txt found inside the export templates file. " ) ) ;
unzClose ( pkg ) ;
return false ;
}
DirAccessRef d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
String template_path = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( version ) ;
Error err = d - > make_dir_recursive ( template_path ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error creating path for extracting templates: " ) + " \n " + template_path ) ;
unzClose ( pkg ) ;
return false ;
}
EditorProgress * p = nullptr ;
if ( ! p_skip_progress ) {
p = memnew ( EditorProgress ( " ltask " , TTR ( " Extracting Export Templates " ) , fc ) ) ;
}
fc = 0 ;
ret = unzGoToFirstFile ( pkg ) ;
while ( ret = = UNZ_OK ) {
// Get filename.
unz_file_info info ;
char fname [ 16384 ] ;
unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , nullptr , 0 , nullptr , 0 ) ;
String file_path ( String : : utf8 ( fname ) . simplify_path ( ) ) ;
String file = file_path . get_file ( ) ;
if ( file . size ( ) = = 0 ) {
ret = unzGoToNextFile ( pkg ) ;
continue ;
}
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
// Read
unzOpenCurrentFile ( pkg ) ;
unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
unzCloseCurrentFile ( pkg ) ;
String base_dir = file_path . get_base_dir ( ) . trim_suffix ( " / " ) ;
if ( base_dir ! = contents_dir & & base_dir . begins_with ( contents_dir ) ) {
base_dir = base_dir . substr ( contents_dir . length ( ) , file_path . length ( ) ) . trim_prefix ( " / " ) ;
file = base_dir . plus_file ( file ) ;
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_CONTINUE ( ! da ) ;
String output_dir = template_path . plus_file ( base_dir ) ;
if ( ! DirAccess : : exists ( output_dir ) ) {
Error mkdir_err = da - > make_dir_recursive ( output_dir ) ;
ERR_CONTINUE ( mkdir_err ! = OK ) ;
}
}
if ( p ) {
p - > step ( TTR ( " Importing: " ) + " " + file , fc ) ;
}
String to_write = template_path . plus_file ( file ) ;
FileAccessRef f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
if ( ! f ) {
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
ERR_CONTINUE_MSG ( true , " Can't open file from path ' " + String ( to_write ) + " '. " ) ;
}
f - > store_buffer ( data . ptr ( ) , data . size ( ) ) ;
# ifndef WINDOWS_ENABLED
FileAccess : : set_unix_permissions ( to_write , ( info . external_fa > > 16 ) & 0x01FF ) ;
# endif
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
}
if ( p ) {
memdelete ( p ) ;
}
unzClose ( pkg ) ;
_update_template_status ( ) ;
return true ;
}
void ExportTemplateManager : : _uninstall_template ( const String & p_version ) {
uninstall_confirm - > set_text ( vformat ( TTR ( " Remove templates for the version '%s'? " ) , p_version ) ) ;
uninstall_confirm - > popup_centered ( ) ;
uninstall_version = p_version ;
}
void ExportTemplateManager : : _uninstall_template_confirmed ( ) {
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
const String & templates_dir = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) ;
Error err = da - > change_dir ( templates_dir ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not access templates directory at ' " + templates_dir + " '. " ) ;
err = da - > change_dir ( uninstall_version ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not access templates directory at ' " + templates_dir . plus_file ( uninstall_version ) + " '. " ) ;
err = da - > erase_contents_recursive ( ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not remove all templates in ' " + templates_dir . plus_file ( uninstall_version ) + " '. " ) ;
da - > change_dir ( " .. " ) ;
err = da - > remove ( uninstall_version ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not remove templates directory at ' " + templates_dir . plus_file ( uninstall_version ) + " '. " ) ;
_update_template_status ( ) ;
}
String ExportTemplateManager : : _get_selected_mirror ( ) const {
if ( mirrors_list - > get_item_count ( ) = = 1 ) {
return " " ;
}
int selected = mirrors_list - > get_selected_id ( ) ;
if ( selected = = 0 ) {
// This is a special "best available" value; so pick the first available mirror from the rest of the list.
selected = 1 ;
}
return mirrors_list - > get_item_metadata ( selected ) ;
}
void ExportTemplateManager : : _mirror_options_button_cbk ( int p_id ) {
switch ( p_id ) {
case VISIT_WEB_MIRROR : {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . empty ( ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " There are no mirrors available. " ) ) ;
return ;
}
OS : : get_singleton ( ) - > shell_open ( mirror_url ) ;
} break ;
case COPY_MIRROR_URL : {
String mirror_url = _get_selected_mirror ( ) ;
if ( mirror_url . empty ( ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " There are no mirrors available. " ) ) ;
return ;
}
OS : : get_singleton ( ) - > set_clipboard ( mirror_url ) ;
} break ;
}
}
void ExportTemplateManager : : _installed_table_button_cbk ( Object * p_item , int p_column , int p_id ) {
TreeItem * ti = Object : : cast_to < TreeItem > ( p_item ) ;
if ( ! ti ) {
return ;
}
switch ( p_id ) {
case OPEN_TEMPLATE_FOLDER : {
String version_string = ti - > get_text ( 0 ) ;
_open_template_folder ( version_string ) ;
} break ;
case UNINSTALL_TEMPLATE : {
String version_string = ti - > get_text ( 0 ) ;
_uninstall_template ( version_string ) ;
} break ;
}
}
void ExportTemplateManager : : _open_template_folder ( const String & p_version ) {
const String & templates_dir = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) ;
OS : : get_singleton ( ) - > shell_open ( " file:// " + templates_dir . plus_file ( p_version ) ) ;
}
void ExportTemplateManager : : popup_manager ( ) {
_update_template_status ( ) ;
_refresh_mirrors ( ) ;
popup_centered ( Size2 ( 720 , 280 ) * EDSCALE ) ;
}
void ExportTemplateManager : : ok_pressed ( ) {
if ( ! is_downloading_templates ) {
hide ( ) ;
return ;
}
hide_dialog_accept - > popup_centered ( ) ;
}
void ExportTemplateManager : : cancel_pressed ( ) {
// This won't stop the window from closing, but will show the alert if the download is active.
ok_pressed ( ) ;
}
void ExportTemplateManager : : _hide_dialog ( ) {
hide ( ) ;
}
bool ExportTemplateManager : : can_install_android_template ( ) {
const String templates_dir = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( VERSION_FULL_CONFIG ) ;
return FileAccess : : exists ( templates_dir . plus_file ( " android_source.zip " ) ) ;
}
Error ExportTemplateManager : : install_android_template ( ) {
const String & templates_path = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( VERSION_FULL_CONFIG ) ;
const String & source_zip = templates_path . plus_file ( " android_source.zip " ) ;
ERR_FAIL_COND_V ( ! FileAccess : : exists ( source_zip ) , ERR_CANT_OPEN ) ;
return install_android_template_from_file ( source_zip ) ;
}
Error ExportTemplateManager : : install_android_template_from_file ( const String & p_file ) {
// To support custom Android builds, we install the Java source code and buildsystem
// from android_source.zip to the project's res://android folder.
DirAccessRef da = DirAccess : : open ( " res:// " ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
// Make res://android dir (if it does not exist).
da - > make_dir ( " android " ) ;
{
// Add version, to ensure building won't work if template and Godot version don't match.
FileAccessRef f = FileAccess : : open ( " res://android/.build_version " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V ( ! f , ERR_CANT_CREATE ) ;
f - > store_line ( VERSION_FULL_CONFIG ) ;
f - > close ( ) ;
}
// Create the android plugins directory.
Error err = da - > make_dir_recursive ( " android/plugins " ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
err = da - > make_dir_recursive ( " android/build " ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
{
// Add an empty .gdignore file to avoid scan.
FileAccessRef f = FileAccess : : open ( " res://android/build/.gdignore " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V ( ! f , ERR_CANT_CREATE ) ;
f - > store_line ( " " ) ;
f - > close ( ) ;
}
// Uncompress source template.
FileAccess * src_f = nullptr ;
zlib_filefunc_def io = zipio_create_io_from_file ( & src_f ) ;
unzFile pkg = unzOpen2 ( p_file . utf8 ( ) . get_data ( ) , & io ) ;
ERR_FAIL_COND_V_MSG ( ! pkg , ERR_CANT_OPEN , " Android sources not in ZIP format. " ) ;
int ret = unzGoToFirstFile ( pkg ) ;
int total_files = 0 ;
// Count files to unzip.
while ( ret = = UNZ_OK ) {
total_files + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ret = unzGoToFirstFile ( pkg ) ;
ProgressDialog : : get_singleton ( ) - > add_task ( " uncompress_src " , TTR ( " Uncompressing Android Build Sources " ) , total_files ) ;
Set < String > dirs_tested ;
int idx = 0 ;
while ( ret = = UNZ_OK ) {
// Get file path.
unz_file_info info ;
char fpath [ 16384 ] ;
ret = unzGetCurrentFileInfo ( pkg , & info , fpath , 16384 , nullptr , 0 , nullptr , 0 ) ;
String path = String : : utf8 ( fpath ) ;
String base_dir = path . get_base_dir ( ) ;
if ( ! path . ends_with ( " / " ) ) {
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
// Read.
unzOpenCurrentFile ( pkg ) ;
unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
unzCloseCurrentFile ( pkg ) ;
if ( ! dirs_tested . has ( base_dir ) ) {
da - > make_dir_recursive ( String ( " android/build " ) . plus_file ( base_dir ) ) ;
dirs_tested . insert ( base_dir ) ;
}
String to_write = String ( " res://android/build " ) . plus_file ( path ) ;
FileAccess * f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
if ( f ) {
f - > store_buffer ( data . ptr ( ) , data . size ( ) ) ;
memdelete ( f ) ;
# ifndef WINDOWS_ENABLED
FileAccess : : set_unix_permissions ( to_write , ( info . external_fa > > 16 ) & 0x01FF ) ;
# endif
} else {
ERR_PRINT ( " Can't uncompress file: " + to_write ) ;
}
}
ProgressDialog : : get_singleton ( ) - > task_step ( " uncompress_src " , path , idx ) ;
idx + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ProgressDialog : : get_singleton ( ) - > end_task ( " uncompress_src " ) ;
unzClose ( pkg ) ;
return OK ;
}
void ExportTemplateManager : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE :
case NOTIFICATION_THEME_CHANGED : {
current_value - > add_font_override ( " font " , get_font ( " bold " , " EditorFonts " ) ) ;
current_missing_label - > add_color_override ( " font_color " , get_color ( " error_color " , " Editor " ) ) ;
current_installed_label - > add_color_override ( " font_color " , get_color ( " disabled_font_color " , " Editor " ) ) ;
mirror_options_button - > set_icon ( get_icon ( " GuiTabMenuHl " , " EditorIcons " ) ) ;
} break ;
case NOTIFICATION_VISIBILITY_CHANGED : {
if ( ! is_visible ( ) ) {
set_process ( false ) ;
} else if ( is_visible ( ) & & is_downloading_templates ) {
set_process ( true ) ;
}
} break ;
case NOTIFICATION_PROCESS : {
update_countdown - = get_process_delta_time ( ) ;
if ( update_countdown > 0 ) {
return ;
}
update_countdown = 0.5 ;
String status ;
int downloaded_bytes ;
int total_bytes ;
bool success = _humanize_http_status ( download_templates , & status , & downloaded_bytes , & total_bytes ) ;
if ( downloaded_bytes > = 0 ) {
if ( total_bytes > 0 ) {
_set_current_progress_value ( float ( downloaded_bytes ) / total_bytes , status ) ;
} else {
_set_current_progress_value ( 0 , status ) ;
}
} else {
_set_current_progress_status ( status ) ;
}
if ( ! success ) {
set_process ( false ) ;
}
} break ;
}
}
void ExportTemplateManager : : _bind_methods ( ) {
ClassDB : : bind_method ( " _hide_dialog " , & ExportTemplateManager : : _hide_dialog ) ;
ClassDB : : bind_method ( " _open_template_folder " , & ExportTemplateManager : : _open_template_folder ) ;
ClassDB : : bind_method ( " _refresh_mirrors_completed " , & ExportTemplateManager : : _refresh_mirrors_completed ) ;
ClassDB : : bind_method ( " _mirror_options_button_cbk " , & ExportTemplateManager : : _mirror_options_button_cbk ) ;
ClassDB : : bind_method ( " _download_template " , & ExportTemplateManager : : _download_template ) ;
ClassDB : : bind_method ( " _download_current " , & ExportTemplateManager : : _download_current ) ;
ClassDB : : bind_method ( " _cancel_template_download " , & ExportTemplateManager : : _cancel_template_download ) ;
ClassDB : : bind_method ( " _download_template_completed " , & ExportTemplateManager : : _download_template_completed ) ;
ClassDB : : bind_method ( " _uninstall_template " , & ExportTemplateManager : : _uninstall_template ) ;
ClassDB : : bind_method ( " _uninstall_template_confirmed " , & ExportTemplateManager : : _uninstall_template_confirmed ) ;
ClassDB : : bind_method ( " _install_file " , & ExportTemplateManager : : _install_file ) ;
ClassDB : : bind_method ( " _installed_table_button_cbk " , & ExportTemplateManager : : _installed_table_button_cbk ) ;
ClassDB : : bind_method ( " _install_file_selected " , & ExportTemplateManager : : _install_file_selected ) ;
}
ExportTemplateManager : : ExportTemplateManager ( ) {
set_title ( TTR ( " Export Template Manager " ) ) ;
set_hide_on_ok ( false ) ;
get_ok ( ) - > set_text ( TTR ( " Close " ) ) ;
// Downloadable export templates are only available for stable and official alpha/beta/RC builds
// (which always have a number following their status, e.g. "alpha1").
// Therefore, don't display download-related features when using a development version
// (whose builds aren't numbered).
downloads_available =
String ( VERSION_STATUS ) ! = String ( " dev " ) & &
String ( VERSION_STATUS ) ! = String ( " alpha " ) & &
String ( VERSION_STATUS ) ! = String ( " beta " ) & &
String ( VERSION_STATUS ) ! = String ( " rc " ) ;
VBoxContainer * main_vb = memnew ( VBoxContainer ) ;
add_child ( main_vb ) ;
// Current version controls.
HBoxContainer * current_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( current_hb ) ;
Label * current_label = memnew ( Label ) ;
current_label - > set_text ( TTR ( " Current Version: " ) ) ;
current_hb - > add_child ( current_label ) ;
current_value = memnew ( Label ) ;
current_hb - > add_child ( current_value ) ;
// Current version statuses.
// Status: Current version is missing.
current_missing_label = memnew ( Label ) ;
current_missing_label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
current_missing_label - > set_align ( Label : : ALIGN_RIGHT ) ;
current_missing_label - > set_text ( TTR ( " Export templates are missing. Download them or install from a file. " ) ) ;
current_hb - > add_child ( current_missing_label ) ;
// Status: Current version is installed.
current_installed_label = memnew ( Label ) ;
current_installed_label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
current_installed_label - > set_align ( Label : : ALIGN_RIGHT ) ;
current_installed_label - > set_text ( TTR ( " Export templates are installed and ready to be used. " ) ) ;
current_hb - > add_child ( current_installed_label ) ;
current_installed_label - > hide ( ) ;
// Currently installed template.
current_installed_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( current_installed_hb ) ;
current_installed_path = memnew ( LineEdit ) ;
current_installed_path - > set_editable ( false ) ;
current_installed_path - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
current_installed_hb - > add_child ( current_installed_path ) ;
current_open_button = memnew ( Button ) ;
current_open_button - > set_text ( TTR ( " Open Folder " ) ) ;
current_open_button - > set_tooltip ( TTR ( " Open the folder containing installed templates for the current version. " ) ) ;
current_installed_hb - > add_child ( current_open_button ) ;
current_open_button - > connect ( " pressed " , this , " _open_template_folder " , varray ( VERSION_FULL_CONFIG ) ) ;
current_uninstall_button = memnew ( Button ) ;
current_uninstall_button - > set_text ( TTR ( " Uninstall " ) ) ;
current_uninstall_button - > set_tooltip ( TTR ( " Uninstall templates for the current version. " ) ) ;
current_installed_hb - > add_child ( current_uninstall_button ) ;
current_uninstall_button - > connect ( " pressed " , this , " _uninstall_template " , varray ( VERSION_FULL_CONFIG ) ) ;
main_vb - > add_child ( memnew ( HSeparator ) ) ;
// Download and install section.
HBoxContainer * install_templates_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( install_templates_hb ) ;
// Download and install buttons are available.
install_options_vb = memnew ( VBoxContainer ) ;
install_options_vb - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
install_templates_hb - > add_child ( install_options_vb ) ;
HBoxContainer * download_install_hb = memnew ( HBoxContainer ) ;
install_options_vb - > add_child ( download_install_hb ) ;
Label * mirrors_label = memnew ( Label ) ;
mirrors_label - > set_text ( TTR ( " Download from: " ) ) ;
download_install_hb - > add_child ( mirrors_label ) ;
mirrors_list = memnew ( OptionButton ) ;
mirrors_list - > set_custom_minimum_size ( Size2 ( 280 , 0 ) * EDSCALE ) ;
download_install_hb - > add_child ( mirrors_list ) ;
mirrors_list - > add_item ( TTR ( " Best available mirror " ) , 0 ) ;
request_mirrors = memnew ( HTTPRequest ) ;
mirrors_list - > add_child ( request_mirrors ) ;
request_mirrors - > connect ( " request_completed " , this , " _refresh_mirrors_completed " ) ;
mirror_options_button = memnew ( MenuButton ) ;
mirror_options_button - > get_popup ( ) - > add_item ( TTR ( " Open in Web Browser " ) , VISIT_WEB_MIRROR ) ;
mirror_options_button - > get_popup ( ) - > add_item ( TTR ( " Copy Mirror URL " ) , COPY_MIRROR_URL ) ;
download_install_hb - > add_child ( mirror_options_button ) ;
mirror_options_button - > get_popup ( ) - > connect ( " id_pressed " , this , " _mirror_options_button_cbk " ) ;
download_install_hb - > add_spacer ( ) ;
Button * download_current_button = memnew ( Button ) ;
download_current_button - > set_text ( TTR ( " Download and Install " ) ) ;
download_current_button - > set_tooltip ( TTR ( " Download and install templates for the current version from the best possible mirror. " ) ) ;
download_install_hb - > add_child ( download_current_button ) ;
download_current_button - > connect ( " pressed " , this , " _download_current " ) ;
// Update downloads buttons to prevent unsupported downloads.
if ( ! downloads_available ) {
download_current_button - > set_disabled ( true ) ;
download_current_button - > set_tooltip ( TTR ( " Official export templates aren't available for development builds. " ) ) ;
}
HBoxContainer * install_file_hb = memnew ( HBoxContainer ) ;
install_file_hb - > set_alignment ( BoxContainer : : ALIGN_END ) ;
install_options_vb - > add_child ( install_file_hb ) ;
install_file_button = memnew ( Button ) ;
install_file_button - > set_text ( TTR ( " Install from File " ) ) ;
install_file_button - > set_tooltip ( TTR ( " Install templates from a local file. " ) ) ;
install_file_hb - > add_child ( install_file_button ) ;
install_file_button - > connect ( " pressed " , this , " _install_file " ) ;
// Templates are being downloaded; buttons unavailable.
download_progress_hb = memnew ( HBoxContainer ) ;
download_progress_hb - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
install_templates_hb - > add_child ( download_progress_hb ) ;
download_progress_hb - > hide ( ) ;
download_progress_bar = memnew ( ProgressBar ) ;
download_progress_bar - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
download_progress_bar - > set_v_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
download_progress_bar - > set_min ( 0 ) ;
download_progress_bar - > set_max ( 1 ) ;
download_progress_bar - > set_value ( 0 ) ;
download_progress_bar - > set_step ( 0.01 ) ;
download_progress_hb - > add_child ( download_progress_bar ) ;
download_progress_label = memnew ( Label ) ;
download_progress_label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
download_progress_label - > set_clip_text ( true ) ;
download_progress_hb - > add_child ( download_progress_label ) ;
Button * download_cancel_button = memnew ( Button ) ;
download_cancel_button - > set_text ( TTR ( " Cancel " ) ) ;
download_cancel_button - > set_tooltip ( TTR ( " Cancel the download of the templates. " ) ) ;
download_progress_hb - > add_child ( download_cancel_button ) ;
download_cancel_button - > connect ( " pressed " , this , " _cancel_template_download " ) ;
download_templates = memnew ( HTTPRequest ) ;
install_templates_hb - > add_child ( download_templates ) ;
download_templates - > connect ( " request_completed " , this , " _download_template_completed " ) ;
main_vb - > add_child ( memnew ( HSeparator ) ) ;
// Other installed templates table.
HBoxContainer * installed_versions_hb = memnew ( HBoxContainer ) ;
main_vb - > add_child ( installed_versions_hb ) ;
Label * installed_label = memnew ( Label ) ;
installed_label - > set_text ( TTR ( " Other Installed Versions: " ) ) ;
installed_versions_hb - > add_child ( installed_label ) ;
installed_table = memnew ( Tree ) ;
installed_table - > set_hide_root ( true ) ;
installed_table - > set_custom_minimum_size ( Size2 ( 0 , 100 ) * EDSCALE ) ;
installed_table - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
main_vb - > add_child ( installed_table ) ;
installed_table - > connect ( " button_pressed " , this , " _installed_table_button_cbk " ) ;
// Dialogs.
uninstall_confirm = memnew ( ConfirmationDialog ) ;
uninstall_confirm - > set_title ( TTR ( " Uninstall Template " ) ) ;
add_child ( uninstall_confirm ) ;
uninstall_confirm - > connect ( " confirmed " , this , " _uninstall_template_confirmed " ) ;
install_file_dialog = memnew ( FileDialog ) ;
install_file_dialog - > set_title ( TTR ( " Select Template File " ) ) ;
install_file_dialog - > set_access ( FileDialog : : ACCESS_FILESYSTEM ) ;
install_file_dialog - > set_mode ( FileDialog : : MODE_OPEN_FILE ) ;
install_file_dialog - > add_filter ( " *.tpz ; " + TTR ( " Godot Export Templates " ) ) ;
install_file_dialog - > connect ( " file_selected " , this , " _install_file_selected " , varray ( false ) ) ;
add_child ( install_file_dialog ) ;
hide_dialog_accept = memnew ( AcceptDialog ) ;
hide_dialog_accept - > set_text ( TTR ( " The templates will continue to download. \n You may experience a short editor freeze when they finish. " ) ) ;
add_child ( hide_dialog_accept ) ;
hide_dialog_accept - > connect ( " confirmed " , this , " _hide_dialog " ) ;
}