From 5c6cf6b3182ac7c664391c9b9fab150ebf6f53e8 Mon Sep 17 00:00:00 2001 From: kristijanWolff Date: Thu, 28 May 2026 21:39:42 +0200 Subject: [PATCH 1/3] Update ModsMenu.gd fixes/updates the mods from documents to downloads so android 16 users can acess it. --- UI/ModsMenu/ModsMenu.gd | 79 +++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/UI/ModsMenu/ModsMenu.gd b/UI/ModsMenu/ModsMenu.gd index 758a5c1d0..d3ca0aa0c 100644 --- a/UI/ModsMenu/ModsMenu.gd +++ b/UI/ModsMenu/ModsMenu.gd @@ -13,12 +13,15 @@ func _ready(): if(OS.get_name() == "Android"): $VBoxContainer/GridContainer/ModsFolderButton.setIsDisabled(true) - $VBoxContainer/GridContainer/RemoveModsButton.setIsDisabled(true) + # Keep this enabled so Android users can delete their imported mods! + $VBoxContainer/GridContainer/RemoveModsButton.setIsDisabled(false) + ensure_external_mods_folder_exists() if OS.get_name() == "HTML5" and OS.has_feature("JavaScript"): _define_js() - var text = "[b][url=https://github.com/Alexofp/BDCC/wiki/How-to-install-BDCC-mods]How to install mods (click me)[/url][/b]\n\nTo install a mod drag it into the mods folder and restart the game. Mods are loaded in alphabetical order.\n\n" + # UPDATED: Changed the text instruction to match your new custom Download folder system! + var text = "[b][url=https://github.com/Alexofp/BDCC/wiki/How-to-install-BDCC-mods]How to install mods (click me)[/url][/b]\n\nTo install a mod, drag it into your phone's public folder: [color=yellow]Download/BDCCMods/[/color] and press the Import Mod button. Mods downloaded from the in-game browser are saved here automatically.\n" if(!GlobalRegistry.hasModSupport()): text += "! Mods aren't supported when running the game from the editor, this is godot issue. Export the game and run it standalone to get mod support !\n\n" # read more here: https://github.com/godotengine/godot/issues/19815 @@ -45,6 +48,15 @@ func _ready(): modsLabel.bbcode_text = text +func ensure_external_mods_folder_exists(): + if OS.get_name() == "Android": + var download_path = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + "/" + var target_folder = download_path + "BDCCMods/" + + var dir = Directory.new() + if not dir.dir_exists(target_folder): + dir.make_dir_recursive(target_folder) + func _on_CloseButton_pressed(): emit_signal("onClosePressed") @@ -142,31 +154,44 @@ func _on_AddModButton_pressed(): if(!showedModDialog): showedModDialog = true $ModAcceptDialog.visible = true - else: - if(OS.get_name() == "Android"): -# var has_permissions: bool = false -# -# while not has_permissions: -# #var permissions = OS.get_granted_permissions() -# var permissions: Array = OS.get_granted_permissions() #for Godot 3 branch -# -# if not permissions.has("android.permission.READ_EXTERNAL_STORAGE") \ -# or not permissions.has("android.permission.WRITE_EXTERNAL_STORAGE"): -# var _ok = OS.request_permissions() -# #await get_tree().create_timer(1).timeout -# yield(get_tree().create_timer(1), "timeout") #for Godot 3 branch -# else: -# has_permissions = true - - var d = Directory.new() - var externalDir:String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) - var finalDir = externalDir.plus_file("BDCCMods") - d.make_dir_recursive(finalDir) - #$ImportModDialog.current_dir = finalDir - $AndroidPathAlert.dialog_text = "Mods on android are loaded from:\n"+finalDir+"\nPlace them there and restart the game." - $AndroidPathAlert.popup_centered() - return + elif OS.get_name() == "Android": + var external_folder = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + "/BDCCMods/" + var internal_mod_path = "user://mods/" + var dir = Directory.new() + + # Make sure internal sandbox mod folder exists + if not dir.dir_exists(internal_mod_path): + dir.make_dir_recursive(internal_mod_path) + + if dir.open(external_folder) == OK: + dir.list_dir_begin(true) + var file_name = dir.get_next() + + var success_count = 0 + + while file_name != "": + if not dir.current_is_dir() and (file_name.ends_with(".zip") or file_name.ends_with(".pck")): + var source = external_folder + file_name + var destination = internal_mod_path + file_name + + if dir.copy(source, destination) == OK: + success_count += 1 + + file_name = dir.get_next() + dir.list_dir_end() + + if success_count > 0: + $AndroidPathAlert.dialog_text = "Successfully imported " + str(success_count) + " mod files!\nRestart the game to apply them." + $AndroidPathAlert.window_title = "Import Complete" + $AndroidPathAlert.popup_centered() + else: + $AndroidPathAlert.dialog_text = "No files ending in .zip or .pck were found in Download/BDCCMods/" + $AndroidPathAlert.window_title = "Import Empty" + $AndroidPathAlert.popup_centered() + else: + print("Error: Could not scan external mods directory.") + else: $ImportModDialog.popup_centered() @@ -204,7 +229,7 @@ func _on_ModsLabel_meta_clicked(meta): func _on_SkinsFolderButton_pressed(): if(OS.get_name() == "Android"): - $AndroidPathAlert.dialog_text = "Custom skins on android are loaded from \"Documents/BDCCMods/custom_skins\"\nCreate that folder if it doesn't exist." + $AndroidPathAlert.dialog_text = "Custom skins on android are loaded from \"Download/BDCCMods/custom_skins\"\nCreate that folder if it doesn't exist." $AndroidPathAlert.popup_centered() else: var _ok = Util.fixed_shell_open(ProjectSettings.globalize_path("user://custom_skins")) From d8ee1dd4b95d4db6f7fb80a4817dccc9f76c8889 Mon Sep 17 00:00:00 2001 From: kristijanWolff Date: Thu, 28 May 2026 21:42:31 +0200 Subject: [PATCH 2/3] Update LoadGameScreen.gd changes the path for save files from Documens to downloads so android 16 users can acess it. --- UI/MainMenu/LoadGameScreen.gd | 91 +++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/UI/MainMenu/LoadGameScreen.gd b/UI/MainMenu/LoadGameScreen.gd index 255ff8752..037f3baee 100644 --- a/UI/MainMenu/LoadGameScreen.gd +++ b/UI/MainMenu/LoadGameScreen.gd @@ -8,6 +8,8 @@ var currentExportedPath = "" signal in_focus func _ready(): + # Automatically setup our public folder structure on launch + ensure_external_save_folder_exists() updateSaves() if(OS.get_name() == "Android"): @@ -18,7 +20,16 @@ func _ready(): if OS.get_name() == "HTML5" and OS.has_feature("JavaScript"): _define_js() - + +func ensure_external_save_folder_exists(): + if OS.get_name() == "Android": + var download_path = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + "/" + var target_folder = download_path + "BDCCSaves/" + + var dir = Directory.new() + if not dir.dir_exists(target_folder): + dir.make_dir_recursive(target_folder) + func updateSaves(): Util.delete_children(savesContainer) @@ -68,21 +79,8 @@ func onExportButtonClicked(savePath: String): return if(OS.get_name() == "Android"): -# var has_permissions: bool = false -# while not has_permissions: -# #var permissions = OS.get_granted_permissions() -# var permissions: Array = OS.get_granted_permissions() #for Godot 3 branch -# -# if not permissions.has("android.permission.READ_EXTERNAL_STORAGE") \ -# or not permissions.has("android.permission.WRITE_EXTERNAL_STORAGE"): -# var _ok = OS.request_permissions() -# #await get_tree().create_timer(1).timeout -# yield(get_tree().create_timer(1), "timeout") #for Godot 3 branch -# else: -# has_permissions = true - var d = Directory.new() - var externalDir:String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) + var externalDir:String = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) var finalDir = externalDir.plus_file("BDCCSaves/") d.make_dir_recursive(finalDir) var finalPath = finalDir.plus_file(savePath.get_file()) @@ -168,7 +166,6 @@ func readSaveFileHTML5(): return [file_name, file_data] - func _on_ImportButton_pressed(): if OS.get_name() == "HTML5": var saveDataAndFileName = yield(readSaveFileHTML5(), "completed") @@ -176,27 +173,49 @@ func _on_ImportButton_pressed(): return SAVE.saveGameFromText(saveDataAndFileName[0], saveDataAndFileName[1]) updateSaves() - else: - if(OS.get_name() == "Android"): -# var has_permissions: bool = false -# -# while not has_permissions: -# #var permissions = OS.get_granted_permissions() -# var permissions: Array = OS.get_granted_permissions() #for Godot 3 branch -# -# if not permissions.has("android.permission.READ_EXTERNAL_STORAGE") \ -# or not permissions.has("android.permission.WRITE_EXTERNAL_STORAGE"): -# var _ok = OS.request_permissions() -# #await get_tree().create_timer(1).timeout -# yield(get_tree().create_timer(1), "timeout") #for Godot 3 branch -# else: -# has_permissions = true + elif OS.get_name() == "Android": + var external_folder = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + "/BDCCSaves/" + var internal_save_path = "user://saves/" - var d = Directory.new() - var externalDir:String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) - var finalDir = externalDir.plus_file("BDCCSaves") - d.make_dir_recursive(finalDir) - $ImportSaveDialog.current_dir = finalDir + var dir = Directory.new() + + # Ensure internal path is fully prepared + if not dir.dir_exists(internal_save_path): + dir.make_dir_recursive(internal_save_path) + + if dir.open(external_folder) == OK: + dir.list_dir_begin(true) + var file_name = dir.get_next() + + var success_count = 0 + + while file_name != "": + if not dir.current_is_dir() and file_name.ends_with(".save"): + var source = external_folder + file_name + var destination = internal_save_path + file_name + + if dir.copy(source, destination) == OK: + success_count += 1 + + file_name = dir.get_next() + dir.list_dir_end() + + # Force visual update so user instantly sees new options + updateSaves() + + # Give a nice system pop up confirming success + if success_count > 0: + $SaveExportedAlert.dialog_text = "Successfully imported " + str(success_count) + " save files!" + $SaveExportedAlert.window_title = "Import Complete" + $SaveExportedAlert.popup_centered() + else: + $SaveExportedAlert.dialog_text = "No custom files ending in .save were found in Download/BDCCSaves/" + $SaveExportedAlert.window_title = "Import Empty" + $SaveExportedAlert.popup_centered() + else: + print("Error: Could not scan target directory.") + else: + # Keep original file fallback behavior active for desktop machines $ImportSaveDialog.popup_centered() func _on_ImportSaveDialog_file_selected(path: String): From bdf5b474f8942e688fce975bcee97ae7643692ec Mon Sep 17 00:00:00 2001 From: kristijanWolff Date: Thu, 28 May 2026 21:47:46 +0200 Subject: [PATCH 3/3] Update GlobalRegistry.gd This is the main fix for the Android 16 modded launcher getting stuck on loading. --- GlobalRegistry.gd | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/GlobalRegistry.gd b/GlobalRegistry.gd index 6a83519e3..6adebf9de 100644 --- a/GlobalRegistry.gd +++ b/GlobalRegistry.gd @@ -327,8 +327,8 @@ func getModsFolder() -> String: if(OS.get_name() == "Android"): #var permissions: Array = OS.get_granted_permissions() #for Godot 3 branch #if permissions.has("android.permission.READ_EXTERNAL_STORAGE"): - var externalDir:String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) - var finalDir = externalDir.plus_file("BDCCMods") + var externalDir:String = "user://" + var finalDir = externalDir.plus_file("mods") modsFolder = finalDir var _ok = Directory.new().make_dir(modsFolder) return modsFolder @@ -338,8 +338,8 @@ func getDatapacksFolder() -> String: if(OS.get_name() == "Android"): #var permissions: Array = OS.get_granted_permissions() #for Godot 3 branch #if permissions.has("android.permission.READ_EXTERNAL_STORAGE"): - var externalDir:String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) - var finalDir = externalDir.plus_file("BDCCMods/Datapacks") + var externalDir:String = "user://" + var finalDir = externalDir.plus_file("datapacks") modsFolder = finalDir var _ok = Directory.new().make_dir(modsFolder) return modsFolder @@ -2248,8 +2248,8 @@ func getSkinsAllKeys(): func findCustomSkins(): var skinsFolder = "user://custom_skins" if(OS.get_name() == "Android"): - var externalDir:String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS) - var finalDir = externalDir.plus_file("BDCCMods/custom_skins") + var externalDir:String = "user://" + var finalDir = externalDir.plus_file("custom_skins") skinsFolder = finalDir for skinPath in Util.getFilesInFolder(skinsFolder): @@ -2260,8 +2260,6 @@ func findCustomSkins(): customSkin.setTexturePath(skinPath) skins[customSkin.id] = customSkin - - func registerPartSkin(path: String, authorOverride = ""): var loadedClass = load(path) var object = loadedClass.new()