2017-04-28 20:04:09 +02:00
/*************************************************************************/
/* export_template_manager.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2017-04-28 20:04:09 +02:00
/*************************************************************************/
2020-01-01 11:16:22 +01:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2017-04-28 20:04:09 +02:00
/* */
/* 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. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-03-21 03:31:41 +01:00
# include "export_template_manager.h"
2017-04-28 20:04:09 +02:00
2020-04-28 15:19:37 +02:00
# include "core/input/input.h"
2018-09-11 18:13:45 +02:00
# include "core/io/json.h"
# include "core/io/zip_io.h"
# include "core/os/dir_access.h"
2018-01-26 20:38:08 +01:00
# include "core/os/keyboard.h"
2018-09-11 18:13:45 +02:00
# include "core/version.h"
2017-03-21 03:31:41 +01:00
# include "editor_node.h"
# include "editor_scale.h"
2019-12-24 08:17:23 +01:00
# include "progress_dialog.h"
# include "scene/gui/link_button.h"
2017-11-17 21:48:24 +01:00
2017-03-21 03:31:41 +01:00
void ExportTemplateManager : : _update_template_list ( ) {
while ( current_hb - > get_child_count ( ) ) {
memdelete ( current_hb - > get_child ( 0 ) ) ;
}
while ( installed_vb - > get_child_count ( ) ) {
memdelete ( installed_vb - > get_child ( 0 ) ) ;
}
DirAccess * d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2017-11-17 21:48:24 +01:00
Error err = d - > change_dir ( EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) ) ;
2017-03-21 03:31:41 +01:00
Set < String > templates ;
2019-07-25 11:09:57 +02:00
d - > list_dir_begin ( ) ;
2017-03-21 03:31:41 +01:00
if ( err = = OK ) {
2019-07-25 11:09:57 +02:00
String c = d - > get_next ( ) ;
2017-03-21 03:31:41 +01:00
while ( c ! = String ( ) ) {
2019-07-25 11:09:57 +02:00
if ( d - > current_is_dir ( ) & & ! c . begins_with ( " . " ) ) {
2017-03-21 03:31:41 +01:00
templates . insert ( c ) ;
}
2019-07-25 11:09:57 +02:00
c = d - > get_next ( ) ;
2017-03-21 03:31:41 +01:00
}
}
d - > list_dir_end ( ) ;
memdelete ( d ) ;
Refactor version macros and fix related bugs
The previous logic with VERSION_MKSTRING was a bit unwieldy, so there were
several places hardcoding their own variant of the version string, potentially
with bugs (e.g. forgetting the patch number when defined).
The new logic defines:
- VERSION_BRANCH, the main 'major.minor' version (e.g. 3.1)
- VERSION_NUMBER, which can be 'major.minor' or 'major.minor.patch',
depending on whether the latter is defined (e.g. 3.1.4)
- VERSION_FULL_CONFIG, which contains the version status (e.g. stable)
and the module-specific suffix (e.g. mono)
- VERSION_FULL_BUILD, same as above but with build/reference name
(e.g. official, custom_build, mageia, etc.)
Note: Slight change here, as the previous format had the build name
*before* the module-specific suffix; now it's after
- VERSION_FULL_NAME, same as before, so VERSION_FULL_BUILD prefixed
with "Godot v" for readability
Bugs fixed thanks to that:
- Export templates version matching now properly takes VERSION_PATCH
into account by relying on VERSION_FULL_CONFIG.
- ClassDB hash no longer takes the build name into account, but limits
itself to VERSION_FULL_CONFIG (build name is cosmetic, not relevant
for the API hash).
- Docs XML no longer hardcode the VERSION_STATUS, this was annoying.
- Small cleanup in Windows .rc file thanks to new macros.
2018-02-23 19:48:49 +01:00
String current_version = VERSION_FULL_CONFIG ;
2019-10-13 23:40:19 +02:00
// 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").
2019-08-12 17:05:25 +02:00
// Therefore, don't display download-related features when using a development version
2019-10-13 23:40:19 +02:00
// (whose builds aren't numbered).
const bool downloads_available =
String ( VERSION_STATUS ) ! = String ( " dev " ) & &
String ( VERSION_STATUS ) ! = String ( " alpha " ) & &
String ( VERSION_STATUS ) ! = String ( " beta " ) & &
String ( VERSION_STATUS ) ! = String ( " rc " ) ;
2017-03-21 03:31:41 +01:00
Label * current = memnew ( Label ) ;
2020-03-06 18:00:16 +01:00
current - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2017-03-21 03:31:41 +01:00
current_hb - > add_child ( current ) ;
if ( templates . has ( current_version ) ) {
2020-03-12 13:37:40 +01:00
current - > add_theme_color_override ( " font_color " , current - > get_theme_color ( " success_color " , " Editor " ) ) ;
2019-08-12 17:05:25 +02:00
// Only display a redownload button if it can be downloaded in the first place
if ( downloads_available ) {
Button * redownload = memnew ( Button ) ;
redownload - > set_text ( TTR ( " Redownload " ) ) ;
current_hb - > add_child ( redownload ) ;
2020-02-21 18:28:45 +01:00
redownload - > connect ( " pressed " , callable_mp ( this , & ExportTemplateManager : : _download_template ) , varray ( current_version ) ) ;
2019-08-12 17:05:25 +02:00
}
2017-03-21 03:31:41 +01:00
Button * uninstall = memnew ( Button ) ;
uninstall - > set_text ( TTR ( " Uninstall " ) ) ;
current_hb - > add_child ( uninstall ) ;
current - > set_text ( current_version + " " + TTR ( " (Installed) " ) ) ;
2020-02-21 18:28:45 +01:00
uninstall - > connect ( " pressed " , callable_mp ( this , & ExportTemplateManager : : _uninstall_template ) , varray ( current_version ) ) ;
2017-03-21 03:31:41 +01:00
} else {
2020-03-12 13:37:40 +01:00
current - > add_theme_color_override ( " font_color " , current - > get_theme_color ( " error_color " , " Editor " ) ) ;
2017-03-21 03:31:41 +01:00
Button * redownload = memnew ( Button ) ;
redownload - > set_text ( TTR ( " Download " ) ) ;
2019-08-12 17:05:25 +02:00
if ( ! downloads_available ) {
redownload - > set_disabled ( true ) ;
redownload - > set_tooltip ( TTR ( " Official export templates aren't available for development builds. " ) ) ;
}
2020-02-21 18:28:45 +01:00
redownload - > connect ( " pressed " , callable_mp ( this , & ExportTemplateManager : : _download_template ) , varray ( current_version ) ) ;
2017-03-21 03:31:41 +01:00
current_hb - > add_child ( redownload ) ;
current - > set_text ( current_version + " " + TTR ( " (Missing) " ) ) ;
}
for ( Set < String > : : Element * E = templates . back ( ) ; E ; E = E - > prev ( ) ) {
HBoxContainer * hbc = memnew ( HBoxContainer ) ;
Label * version = memnew ( Label ) ;
2020-03-12 13:37:40 +01:00
version - > set_modulate ( current - > get_theme_color ( " disabled_font_color " , " Editor " ) ) ;
2017-03-21 03:31:41 +01:00
String text = E - > get ( ) ;
if ( text = = current_version ) {
text + = " " + TTR ( " (Current) " ) ;
}
version - > set_text ( text ) ;
2020-03-06 18:00:16 +01:00
version - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2017-03-21 03:31:41 +01:00
hbc - > add_child ( version ) ;
Button * uninstall = memnew ( Button ) ;
uninstall - > set_text ( TTR ( " Uninstall " ) ) ;
hbc - > add_child ( uninstall ) ;
2020-02-21 18:28:45 +01:00
uninstall - > connect ( " pressed " , callable_mp ( this , & ExportTemplateManager : : _uninstall_template ) , varray ( E - > get ( ) ) ) ;
2017-03-21 03:31:41 +01:00
installed_vb - > add_child ( hbc ) ;
}
}
void ExportTemplateManager : : _download_template ( const String & p_version ) {
2017-11-02 03:12:28 +01:00
while ( template_list - > get_child_count ( ) ) {
memdelete ( template_list - > get_child ( 0 ) ) ;
}
2020-03-06 18:00:16 +01:00
template_downloader - > popup_centered ( ) ;
2018-04-22 19:36:01 +02:00
template_list_state - > set_text ( TTR ( " Retrieving mirrors, please wait... " ) ) ;
2017-11-02 03:12:28 +01:00
template_download_progress - > set_max ( 100 ) ;
template_download_progress - > set_value ( 0 ) ;
2017-11-04 19:08:13 +01:00
request_mirror - > request ( " https://godotengine.org/mirrorlist/ " + p_version + " .json " ) ;
2017-11-02 03:12:28 +01:00
template_list_state - > show ( ) ;
template_download_progress - > show ( ) ;
2017-03-21 03:31:41 +01:00
}
void ExportTemplateManager : : _uninstall_template ( const String & p_version ) {
remove_confirm - > set_text ( vformat ( TTR ( " Remove template version '%s'? " ) , p_version ) ) ;
2020-03-06 18:00:16 +01:00
remove_confirm - > popup_centered ( ) ;
2017-03-21 03:31:41 +01:00
to_remove = p_version ;
}
void ExportTemplateManager : : _uninstall_template_confirm ( ) {
2019-10-24 12:48:29 +02:00
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 ( to_remove ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not access templates directory at ' " + templates_dir . plus_file ( to_remove ) + " '. " ) ;
2017-03-21 03:31:41 +01:00
2019-10-24 12:48:29 +02:00
err = da - > erase_contents_recursive ( ) ;
ERR_FAIL_COND_MSG ( err ! = OK , " Could not remove all templates in ' " + templates_dir . plus_file ( to_remove ) + " '. " ) ;
2017-03-21 03:31:41 +01:00
2019-10-24 12:48:29 +02:00
da - > change_dir ( " .. " ) ;
2019-12-10 05:13:02 +01:00
err = da - > remove ( to_remove ) ;
2019-10-24 12:48:29 +02:00
ERR_FAIL_COND_MSG ( err ! = OK , " Could not remove templates directory at ' " + templates_dir . plus_file ( to_remove ) + " '. " ) ;
2017-03-21 03:31:41 +01:00
_update_template_list ( ) ;
}
2018-08-13 12:46:02 +02:00
bool ExportTemplateManager : : _install_from_file ( const String & p_file , bool p_use_progress ) {
2019-10-24 12:48:29 +02:00
// unzClose() will take care of closing the file stored in the unzFile,
// so we don't need to `memdelete(fa)` in this method.
2020-04-02 01:20:12 +02:00
FileAccess * fa = nullptr ;
2017-03-21 03:31:41 +01:00
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 export templates zip. " ) ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
int ret = unzGoToFirstFile ( pkg ) ;
int fc = 0 ; //count them and find version
String version ;
2019-02-25 19:37:51 +01:00
String contents_dir ;
2017-03-21 03:31:41 +01:00
while ( ret = = UNZ_OK ) {
unz_file_info info ;
char fname [ 16384 ] ;
2020-04-02 01:20:12 +02:00
ret = unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , nullptr , 0 , nullptr , 0 ) ;
2017-03-21 03:31:41 +01:00
String file = fname ;
if ( file . ends_with ( " version.txt " ) ) {
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
//read
2017-08-21 21:15:36 +02:00
unzOpenCurrentFile ( pkg ) ;
2017-11-25 04:07:54 +01:00
ret = unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
2017-03-21 03:31:41 +01:00
unzCloseCurrentFile ( pkg ) ;
String data_str ;
data_str . parse_utf8 ( ( const char * ) data . ptr ( ) , data . size ( ) ) ;
data_str = data_str . strip_edges ( ) ;
2018-02-25 15:24:33 +01:00
// 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 templates: %s. " ) , data_str ) ) ;
2017-03-21 03:31:41 +01:00
unzClose ( pkg ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
2018-02-25 15:24:33 +01:00
version = data_str ;
2019-02-25 19:37:51 +01:00
contents_dir = file . get_base_dir ( ) . trim_suffix ( " / " ) . trim_suffix ( " \\ " ) ;
2017-03-21 03:31:41 +01:00
}
2018-07-16 09:05:45 +02:00
if ( file . get_file ( ) . size ( ) ! = 0 ) {
fc + + ;
}
2017-03-21 03:31:41 +01:00
ret = unzGoToNextFile ( pkg ) ;
}
if ( version = = String ( ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No version.txt found inside templates. " ) ) ;
unzClose ( pkg ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
2017-11-17 21:48:24 +01:00
String template_path = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( version ) ;
2017-03-21 03:31:41 +01:00
2019-10-24 12:48:29 +02:00
DirAccessRef d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2017-03-21 03:31:41 +01:00
Error err = d - > make_dir_recursive ( template_path ) ;
if ( err ! = OK ) {
2018-01-04 20:00:39 +01:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error creating path for templates: " ) + " \n " + template_path ) ;
2017-03-21 03:31:41 +01:00
unzClose ( pkg ) ;
2018-08-13 12:46:02 +02:00
return false ;
2017-03-21 03:31:41 +01:00
}
ret = unzGoToFirstFile ( pkg ) ;
2020-04-02 01:20:12 +02:00
EditorProgress * p = nullptr ;
2017-12-17 19:47:50 +01:00
if ( p_use_progress ) {
p = memnew ( EditorProgress ( " ltask " , TTR ( " Extracting Export Templates " ) , fc ) ) ;
}
2017-03-21 03:31:41 +01:00
fc = 0 ;
while ( ret = = UNZ_OK ) {
//get filename
unz_file_info info ;
char fname [ 16384 ] ;
2020-04-02 01:20:12 +02:00
unzGetCurrentFileInfo ( pkg , & info , fname , 16384 , nullptr , 0 , nullptr , 0 ) ;
2017-03-21 03:31:41 +01:00
2019-02-28 00:25:55 +01:00
String file_path ( String ( fname ) . simplify_path ( ) ) ;
2019-02-25 19:37:51 +01:00
String file = file_path . get_file ( ) ;
2017-03-21 03:31:41 +01:00
2018-07-16 09:05:45 +02:00
if ( file . size ( ) = = 0 ) {
ret = unzGoToNextFile ( pkg ) ;
continue ;
}
2017-03-21 03:31:41 +01:00
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
//read
2017-08-21 21:15:36 +02:00
unzOpenCurrentFile ( pkg ) ;
2017-11-25 04:07:54 +01:00
unzReadCurrentFile ( pkg , data . ptrw ( ) , data . size ( ) ) ;
2017-03-21 03:31:41 +01:00
unzCloseCurrentFile ( pkg ) ;
2019-02-28 00:25:55 +01:00
String base_dir = file_path . get_base_dir ( ) . trim_suffix ( " / " ) ;
2019-02-25 19:37:51 +01:00
if ( base_dir ! = contents_dir & & base_dir . begins_with ( contents_dir ) ) {
2019-02-28 00:25:55 +01:00
base_dir = base_dir . substr ( contents_dir . length ( ) , file_path . length ( ) ) . trim_prefix ( " / " ) ;
2019-02-25 19:37:51 +01:00
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 ) ;
}
}
2017-12-17 19:47:50 +01:00
if ( p ) {
p - > step ( TTR ( " Importing: " ) + " " + file , fc ) ;
}
2017-03-21 03:31:41 +01:00
2019-04-07 20:46:52 +02:00
String to_write = template_path . plus_file ( file ) ;
2019-10-24 12:48:29 +02:00
FileAccessRef f = FileAccess : : open ( to_write , FileAccess : : WRITE ) ;
2017-03-21 03:31:41 +01:00
2017-12-18 20:44:19 +01:00
if ( ! f ) {
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
2019-09-25 10:28:50 +02:00
ERR_CONTINUE_MSG ( true , " Can't open file from path ' " + String ( to_write ) + " '. " ) ;
2017-12-18 20:44:19 +01:00
}
2017-03-21 03:31:41 +01:00
f - > store_buffer ( data . ptr ( ) , data . size ( ) ) ;
2019-04-07 20:46:52 +02:00
# ifndef WINDOWS_ENABLED
FileAccess : : set_unix_permissions ( to_write , ( info . external_fa > > 16 ) & 0x01FF ) ;
# endif
2017-03-21 03:31:41 +01:00
ret = unzGoToNextFile ( pkg ) ;
fc + + ;
}
2017-12-17 19:47:50 +01:00
if ( p ) {
memdelete ( p ) ;
}
2017-03-21 03:31:41 +01:00
unzClose ( pkg ) ;
_update_template_list ( ) ;
2018-08-13 12:46:02 +02:00
return true ;
2017-03-21 03:31:41 +01:00
}
void ExportTemplateManager : : popup_manager ( ) {
_update_template_list ( ) ;
2020-03-06 18:00:16 +01:00
popup_centered ( Size2 ( 400 , 400 ) * EDSCALE ) ;
2017-03-21 03:31:41 +01:00
}
void ExportTemplateManager : : ok_pressed ( ) {
template_open - > popup_centered_ratio ( ) ;
}
2020-02-17 22:06:54 +01:00
void ExportTemplateManager : : _http_download_mirror_completed ( int p_status , int p_code , const PackedStringArray & headers , const PackedByteArray & p_data ) {
2017-11-04 19:08:13 +01:00
if ( p_status ! = HTTPRequest : : RESULT_SUCCESS | | p_code ! = 200 ) {
2019-12-27 03:31:55 +01:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error getting the list of mirrors. " ) ) ;
2017-11-04 19:08:13 +01:00
return ;
}
String mirror_str ;
{
2020-02-17 22:06:54 +01:00
const uint8_t * r = p_data . ptr ( ) ;
mirror_str . parse_utf8 ( ( const char * ) r , p_data . size ( ) ) ;
2017-11-04 19:08:13 +01:00
}
2017-11-02 03:12:28 +01:00
template_list_state - > hide ( ) ;
template_download_progress - > hide ( ) ;
Variant r ;
String errs ;
int errline ;
Error err = JSON : : parse ( mirror_str , r , errs , errline ) ;
if ( err ! = OK ) {
2019-12-27 03:31:55 +01:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error parsing JSON of mirror list. Please report this issue! " ) ) ;
2017-11-02 03:12:28 +01:00
return ;
}
bool mirrors_found = false ;
Dictionary d = r ;
if ( d . has ( " mirrors " ) ) {
Array mirrors = d [ " mirrors " ] ;
for ( int i = 0 ; i < mirrors . size ( ) ; i + + ) {
Dictionary m = mirrors [ i ] ;
ERR_CONTINUE ( ! m . has ( " url " ) | | ! m . has ( " name " ) ) ;
LinkButton * lb = memnew ( LinkButton ) ;
lb - > set_text ( m [ " name " ] ) ;
2020-02-21 18:28:45 +01:00
lb - > connect ( " pressed " , callable_mp ( this , & ExportTemplateManager : : _begin_template_download ) , varray ( m [ " url " ] ) ) ;
2017-11-02 03:12:28 +01:00
template_list - > add_child ( lb ) ;
mirrors_found = true ;
}
}
if ( ! mirrors_found ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " No download links found for this version. Direct download is only available for official releases. " ) ) ;
return ;
}
}
2020-05-14 14:29:06 +02:00
2020-02-17 22:06:54 +01:00
void ExportTemplateManager : : _http_download_templates_completed ( int p_status , int p_code , const PackedStringArray & headers , const PackedByteArray & p_data ) {
2017-11-02 03:12:28 +01:00
switch ( p_status ) {
case HTTPRequest : : RESULT_CANT_RESOLVE : {
template_list_state - > set_text ( TTR ( " Can't resolve. " ) ) ;
} break ;
case HTTPRequest : : RESULT_BODY_SIZE_LIMIT_EXCEEDED :
case HTTPRequest : : RESULT_CONNECTION_ERROR :
2019-06-26 15:08:25 +02:00
case HTTPRequest : : RESULT_CHUNKED_BODY_SIZE_MISMATCH :
2017-11-02 03:12:28 +01:00
case HTTPRequest : : RESULT_SSL_HANDSHAKE_ERROR :
case HTTPRequest : : RESULT_CANT_CONNECT : {
template_list_state - > set_text ( TTR ( " Can't connect. " ) ) ;
} break ;
case HTTPRequest : : RESULT_NO_RESPONSE : {
template_list_state - > set_text ( TTR ( " No response. " ) ) ;
} break ;
case HTTPRequest : : RESULT_REQUEST_FAILED : {
2017-12-12 16:57:17 +01:00
template_list_state - > set_text ( TTR ( " Request Failed. " ) ) ;
2017-11-02 03:12:28 +01:00
} break ;
case HTTPRequest : : RESULT_REDIRECT_LIMIT_REACHED : {
template_list_state - > set_text ( TTR ( " Redirect Loop. " ) ) ;
} break ;
default : {
if ( p_code ! = 200 ) {
template_list_state - > set_text ( TTR ( " Failed: " ) + " " + itos ( p_code ) ) ;
} else {
2018-02-07 16:18:14 +01:00
String path = download_templates - > get_download_file ( ) ;
template_list_state - > set_text ( TTR ( " Download Complete. " ) ) ;
template_downloader - > hide ( ) ;
2019-08-09 13:45:30 +02:00
bool ret = _install_from_file ( path , false ) ;
2018-08-13 12:46:02 +02:00
if ( ret ) {
2019-08-09 13:45:30 +02:00
// Clean up downloaded file.
DirAccessRef da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
Error err = da - > remove ( path ) ;
2018-08-13 12:46:02 +02:00
if ( err ! = OK ) {
2019-08-09 13:45:30 +02:00
EditorNode : : get_singleton ( ) - > add_io_error ( TTR ( " Cannot remove temporary file: " ) + " \n " + path + " \n " ) ;
2018-08-13 12:46:02 +02:00
}
} else {
2019-08-09 13:45:30 +02:00
EditorNode : : get_singleton ( ) - > add_io_error ( vformat ( TTR ( " Templates installation failed. \n The problematic templates archives can be found at '%s'. " ) , path ) ) ;
2018-08-13 12:46:02 +02:00
}
2017-11-02 03:12:28 +01:00
}
} break ;
}
set_process ( false ) ;
}
void ExportTemplateManager : : _begin_template_download ( const String & p_url ) {
2020-04-28 15:19:37 +02:00
if ( Input : : get_singleton ( ) - > is_key_pressed ( KEY_SHIFT ) ) {
2018-01-26 20:38:08 +01:00
OS : : get_singleton ( ) - > shell_open ( p_url ) ;
return ;
}
2017-11-02 03:12:28 +01:00
for ( int i = 0 ; i < template_list - > get_child_count ( ) ; i + + ) {
BaseButton * b = Object : : cast_to < BaseButton > ( template_list - > get_child ( 0 ) ) ;
if ( b ) {
b - > set_disabled ( true ) ;
}
}
download_data . clear ( ) ;
2018-02-07 16:18:14 +01:00
download_templates - > set_download_file ( EditorSettings : : get_singleton ( ) - > get_cache_dir ( ) . plus_file ( " tmp_templates.tpz " ) ) ;
download_templates - > set_use_threads ( true ) ;
2017-11-02 03:12:28 +01:00
Error err = download_templates - > request ( p_url ) ;
if ( err ! = OK ) {
2019-08-09 13:45:30 +02:00
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Error requesting URL: " ) + " " + p_url ) ;
2017-11-02 03:12:28 +01:00
return ;
}
set_process ( true ) ;
template_list_state - > show ( ) ;
template_download_progress - > set_max ( 100 ) ;
template_download_progress - > set_value ( 0 ) ;
template_download_progress - > show ( ) ;
2018-04-22 19:36:01 +02:00
template_list_state - > set_text ( TTR ( " Connecting to Mirror... " ) ) ;
2017-11-02 03:12:28 +01:00
}
2018-02-01 02:15:06 +01:00
void ExportTemplateManager : : _window_template_downloader_closed ( ) {
download_templates - > cancel_request ( ) ;
}
2017-11-02 03:12:28 +01:00
void ExportTemplateManager : : _notification ( int p_what ) {
2020-03-06 18:00:16 +01:00
if ( p_what = = NOTIFICATION_VISIBILITY_CHANGED ) {
if ( ! is_visible ( ) ) {
set_process ( false ) ;
}
}
2017-11-02 03:12:28 +01:00
if ( p_what = = NOTIFICATION_PROCESS ) {
update_countdown - = get_process_delta_time ( ) ;
if ( update_countdown > 0 ) {
return ;
}
update_countdown = 0.5 ;
String status ;
bool errored = false ;
switch ( download_templates - > get_http_client_status ( ) ) {
case HTTPClient : : STATUS_DISCONNECTED :
status = TTR ( " Disconnected " ) ;
errored = true ;
break ;
2020-05-10 13:00:47 +02:00
case HTTPClient : : STATUS_RESOLVING :
status = TTR ( " Resolving " ) ;
break ;
2017-11-02 03:12:28 +01:00
case HTTPClient : : STATUS_CANT_RESOLVE :
status = TTR ( " Can't Resolve " ) ;
errored = true ;
break ;
2020-05-10 13:00:47 +02:00
case HTTPClient : : STATUS_CONNECTING :
status = TTR ( " Connecting... " ) ;
break ;
2017-11-02 03:12:28 +01:00
case HTTPClient : : STATUS_CANT_CONNECT :
2017-12-12 16:57:17 +01:00
status = TTR ( " Can't Connect " ) ;
2017-11-02 03:12:28 +01:00
errored = true ;
break ;
2020-05-10 13:00:47 +02:00
case HTTPClient : : STATUS_CONNECTED :
status = TTR ( " Connected " ) ;
break ;
case HTTPClient : : STATUS_REQUESTING :
status = TTR ( " Requesting... " ) ;
break ;
2017-11-02 03:12:28 +01:00
case HTTPClient : : STATUS_BODY :
status = TTR ( " Downloading " ) ;
if ( download_templates - > get_body_size ( ) > 0 ) {
status + = " " + String : : humanize_size ( download_templates - > get_downloaded_bytes ( ) ) + " / " + String : : humanize_size ( download_templates - > get_body_size ( ) ) ;
template_download_progress - > set_max ( download_templates - > get_body_size ( ) ) ;
template_download_progress - > set_value ( download_templates - > get_downloaded_bytes ( ) ) ;
} else {
status + = " " + String : : humanize_size ( download_templates - > get_downloaded_bytes ( ) ) ;
}
break ;
case HTTPClient : : STATUS_CONNECTION_ERROR :
status = TTR ( " Connection Error " ) ;
errored = true ;
break ;
case HTTPClient : : STATUS_SSL_HANDSHAKE_ERROR :
status = TTR ( " SSL Handshake Error " ) ;
errored = true ;
break ;
}
template_list_state - > set_text ( status ) ;
if ( errored ) {
set_process ( false ) ;
}
}
}
2019-04-07 20:46:52 +02:00
bool ExportTemplateManager : : can_install_android_template ( ) {
2019-08-27 16:42:15 +02:00
const String templates_dir = EditorSettings : : get_singleton ( ) - > get_templates_dir ( ) . plus_file ( VERSION_FULL_CONFIG ) ;
2020-03-02 16:53:05 +01:00
return FileAccess : : exists ( templates_dir . plus_file ( " android_source.zip " ) ) ;
2019-04-07 20:46:52 +02:00
}
Error ExportTemplateManager : : install_android_template ( ) {
2019-09-03 02:31:51 +02:00
// To support custom Android builds, we install the Java source code and buildsystem
// from android_source.zip to the project's res://android folder.
2019-08-27 16:42:15 +02:00
2019-04-07 20:46:52 +02:00
DirAccessRef da = DirAccess : : open ( " res:// " ) ;
ERR_FAIL_COND_V ( ! da , ERR_CANT_CREATE ) ;
2019-08-27 16:42:15 +02:00
// Make res://android dir (if it does not exist).
2019-04-07 20:46:52 +02:00
da - > make_dir ( " android " ) ;
{
2019-08-27 16:42:15 +02:00
// Add version, to ensure building won't work if template and Godot version don't match.
2019-04-07 20:46:52 +02:00
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 ( ) ;
}
2019-10-18 18:59:04 +02:00
// 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 " ) ;
2019-04-07 20:46:52 +02:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2019-10-18 18:59:04 +02:00
{
// 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 ( ) ;
}
2019-04-07 20:46:52 +02:00
2019-08-27 16:42:15 +02:00
// Uncompress source 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 " ) ;
2019-04-07 20:46:52 +02:00
ERR_FAIL_COND_V ( ! FileAccess : : exists ( source_zip ) , ERR_CANT_OPEN ) ;
2020-04-02 01:20:12 +02:00
FileAccess * src_f = nullptr ;
2019-04-07 20:46:52 +02:00
zlib_filefunc_def io = zipio_create_io_from_file ( & src_f ) ;
unzFile pkg = unzOpen2 ( source_zip . utf8 ( ) . get_data ( ) , & io ) ;
2019-08-21 19:32:58 +02:00
ERR_FAIL_COND_V_MSG ( ! pkg , ERR_CANT_OPEN , " Android sources not in ZIP format. " ) ;
2019-04-07 20:46:52 +02:00
int ret = unzGoToFirstFile ( pkg ) ;
int total_files = 0 ;
2019-08-27 16:42:15 +02:00
// Count files to unzip.
2019-04-07 20:46:52 +02:00
while ( ret = = UNZ_OK ) {
total_files + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ret = unzGoToFirstFile ( pkg ) ;
2019-08-27 16:42:15 +02:00
ProgressDialog : : get_singleton ( ) - > add_task ( " uncompress_src " , TTR ( " Uncompressing Android Build Sources " ) , total_files ) ;
2019-04-07 20:46:52 +02:00
2019-08-27 16:42:15 +02:00
Set < String > dirs_tested ;
2019-04-07 20:46:52 +02:00
int idx = 0 ;
while ( ret = = UNZ_OK ) {
2019-08-27 16:42:15 +02:00
// Get file path.
2019-04-07 20:46:52 +02:00
unz_file_info info ;
2019-08-27 16:42:15 +02:00
char fpath [ 16384 ] ;
2020-04-02 01:20:12 +02:00
ret = unzGetCurrentFileInfo ( pkg , & info , fpath , 16384 , nullptr , 0 , nullptr , 0 ) ;
2019-04-07 20:46:52 +02:00
2019-08-27 16:42:15 +02:00
String path = fpath ;
String base_dir = path . get_base_dir ( ) ;
2019-04-07 20:46:52 +02:00
2019-08-27 16:42:15 +02:00
if ( ! path . ends_with ( " / " ) ) {
2019-04-07 20:46:52 +02:00
Vector < uint8_t > data ;
data . resize ( info . uncompressed_size ) ;
2019-08-27 16:42:15 +02:00
// Read.
2019-04-07 20:46:52 +02:00
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 ) ;
}
2019-08-27 16:42:15 +02:00
String to_write = String ( " res://android/build " ) . plus_file ( path ) ;
2019-04-07 20:46:52 +02:00
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 {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Can't uncompress file: " + to_write ) ;
2019-04-07 20:46:52 +02:00
}
}
2019-08-27 16:42:15 +02:00
ProgressDialog : : get_singleton ( ) - > task_step ( " uncompress_src " , path , idx ) ;
idx + + ;
ret = unzGoToNextFile ( pkg ) ;
}
ProgressDialog : : get_singleton ( ) - > end_task ( " uncompress_src " ) ;
unzClose ( pkg ) ;
2019-04-07 20:46:52 +02:00
return OK ;
}
2017-03-21 03:31:41 +01:00
void ExportTemplateManager : : _bind_methods ( ) {
}
ExportTemplateManager : : ExportTemplateManager ( ) {
VBoxContainer * main_vb = memnew ( VBoxContainer ) ;
add_child ( main_vb ) ;
current_hb = memnew ( HBoxContainer ) ;
main_vb - > add_margin_child ( TTR ( " Current Version: " ) , current_hb , false ) ;
installed_scroll = memnew ( ScrollContainer ) ;
main_vb - > add_margin_child ( TTR ( " Installed Versions: " ) , installed_scroll , true ) ;
installed_vb = memnew ( VBoxContainer ) ;
installed_scroll - > add_child ( installed_vb ) ;
installed_scroll - > set_enable_v_scroll ( true ) ;
installed_scroll - > set_enable_h_scroll ( false ) ;
2020-03-06 18:00:16 +01:00
installed_vb - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2017-03-21 03:31:41 +01:00
get_cancel ( ) - > set_text ( TTR ( " Close " ) ) ;
get_ok ( ) - > set_text ( TTR ( " Install From File " ) ) ;
remove_confirm = memnew ( ConfirmationDialog ) ;
remove_confirm - > set_title ( TTR ( " Remove Template " ) ) ;
add_child ( remove_confirm ) ;
2020-02-21 18:28:45 +01:00
remove_confirm - > connect ( " confirmed " , callable_mp ( this , & ExportTemplateManager : : _uninstall_template_confirm ) ) ;
2017-03-21 03:31:41 +01:00
template_open = memnew ( FileDialog ) ;
2019-03-25 01:54:29 +01:00
template_open - > set_title ( TTR ( " Select Template File " ) ) ;
2019-12-13 09:07:45 +01:00
template_open - > add_filter ( " *.tpz ; " + TTR ( " Godot Export Templates " ) ) ;
2017-03-21 03:31:41 +01:00
template_open - > set_access ( FileDialog : : ACCESS_FILESYSTEM ) ;
2020-03-06 18:00:16 +01:00
template_open - > set_file_mode ( FileDialog : : FILE_MODE_OPEN_FILE ) ;
2020-02-21 18:28:45 +01:00
template_open - > connect ( " file_selected " , callable_mp ( this , & ExportTemplateManager : : _install_from_file ) , varray ( true ) ) ;
2017-03-21 03:31:41 +01:00
add_child ( template_open ) ;
set_title ( TTR ( " Export Template Manager " ) ) ;
set_hide_on_ok ( false ) ;
2017-11-02 03:12:28 +01:00
request_mirror = memnew ( HTTPRequest ) ;
add_child ( request_mirror ) ;
2020-02-21 18:28:45 +01:00
request_mirror - > connect ( " request_completed " , callable_mp ( this , & ExportTemplateManager : : _http_download_mirror_completed ) ) ;
2017-11-02 03:12:28 +01:00
download_templates = memnew ( HTTPRequest ) ;
add_child ( download_templates ) ;
2020-02-21 18:28:45 +01:00
download_templates - > connect ( " request_completed " , callable_mp ( this , & ExportTemplateManager : : _http_download_templates_completed ) ) ;
2017-11-02 03:12:28 +01:00
template_downloader = memnew ( AcceptDialog ) ;
template_downloader - > set_title ( TTR ( " Download Templates " ) ) ;
template_downloader - > get_ok ( ) - > set_text ( TTR ( " Close " ) ) ;
2018-02-01 02:15:06 +01:00
template_downloader - > set_exclusive ( true ) ;
2017-11-02 03:12:28 +01:00
add_child ( template_downloader ) ;
2020-03-06 18:00:16 +01:00
template_downloader - > connect ( " cancelled " , callable_mp ( this , & ExportTemplateManager : : _window_template_downloader_closed ) ) ;
2017-11-02 03:12:28 +01:00
VBoxContainer * vbc = memnew ( VBoxContainer ) ;
template_downloader - > add_child ( vbc ) ;
ScrollContainer * sc = memnew ( ScrollContainer ) ;
sc - > set_custom_minimum_size ( Size2 ( 400 , 200 ) * EDSCALE ) ;
2018-01-26 20:38:08 +01:00
vbc - > add_margin_child ( TTR ( " Select mirror from list: (Shift+Click: Open in Browser) " ) , sc ) ;
2017-11-02 03:12:28 +01:00
template_list = memnew ( VBoxContainer ) ;
sc - > add_child ( template_list ) ;
sc - > set_enable_v_scroll ( true ) ;
sc - > set_enable_h_scroll ( false ) ;
template_list_state = memnew ( Label ) ;
vbc - > add_child ( template_list_state ) ;
template_download_progress = memnew ( ProgressBar ) ;
vbc - > add_child ( template_download_progress ) ;
update_countdown = 0 ;
2017-03-21 03:31:41 +01:00
}