Просмотр исходного кода

Fix .pck lookup for extensionless binary names with a dot

This was not a problem on Windows as binary names are guaranteed to end
with '.exe', but on Unix systems binary extensions are purely cosmetic
and thus optional, which is a problem when using `get_basename()` to
lookup a potential '.pck' file, as it can fail on e.g. "My Game 2.0" (#15188).

To fix this, ProjectSettings::setup now checks for both basename + '.pck'
and filename + '.pck'.

Fixes #15188, supersedes and closes #22755.

Also took the opportunity to improve documentation on this core method.
Rémi Verschelde 6 лет назад
Родитель
Сommit
9d926b72e1
1 измененных файлов с 57 добавлено и 28 удалено
  1. 57 28
      core/project_settings.cpp

+ 57 - 28
core/project_settings.cpp

@@ -288,9 +288,28 @@ void ProjectSettings::_convert_to_last_version() {
 	}
 }
 
+/*
+ * This method is responsible for loading a project.godot file and/or data file
+ * using the following merit order:
+ *  - If using NetworkClient, try to lookup project file or fail.
+ *  - If --main-pack was passed by the user (`p_main_pack`), load it or fail.
+ *  - Search for .pck file matching binary name. There are two possibilities:
+ *    o exec_path.get_basename() + '.pck' (e.g. 'win_game.exe' -> 'win_game.pck')
+ *    o exec_path + '.pck' (e.g. 'linux_game' -> 'linux_game.pck')
+ *    For each tentative, if the file exists, load it or fail.
+ *  - On relevant platforms (Android/iOS), lookup project file in OS resource path.
+ *    If found, load it or fail.
+ *  - Lookup project file in passed `p_path` (--path passed by the user), i.e. we
+ *    are running from source code.
+ *    If not found and `p_upwards` is true (--upwards passed by the user), look for
+ *    project files in parent folders up to the system root (used to run a game
+ *    from command line while in a subfolder).
+ *    If a project file is found, load it or fail.
+ *    If nothing was found, error out.
+ */
 Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) {
 
-	//If looking for files in network, just use network!
+	// If looking for files in a network client, use it directly
 
 	if (FileAccessNetworkClient::get_singleton()) {
 
@@ -302,9 +321,7 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
 		return err;
 	}
 
-	String exec_path = OS::get_singleton()->get_executable_path();
-
-	//Attempt with a passed main pack first
+	// Attempt with a user-defined main pack first
 
 	if (p_main_pack != "") {
 
@@ -320,25 +337,39 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
 		return err;
 	}
 
-	//Attempt with execname.pck
+	// Attempt with exec_name.pck
+	// (This is the usual case when distributing a Godot game.)
+
+	// Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle)
+	// or the exec path's basename + '.pck' (Windows).
+	// We need to test both possibilities as extensions for Linux binaries are optional
+	// (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck').
+
+	String exec_path = OS::get_singleton()->get_executable_path();
+
 	if (exec_path != "") {
 		bool found = false;
 
-		// get our filename without our path (note, using exec_path.get_file before get_basename anymore because not all file systems have dots in their file names!)
-		String filebase_name = exec_path.get_file().get_basename();
+		String exec_dir = exec_path.get_base_dir();
+		String exec_filename = exec_path.get_file();
+		String exec_basename = exec_filename.get_basename();
+
+		// Try to load data pack at the location of the executable
+		// As mentioned above, we have two potential names to attempt
 
-		// try to open at the location of executable
-		String datapack_name = exec_path.get_base_dir().plus_file(filebase_name) + ".pck";
-		if (_load_resource_pack(datapack_name)) {
+		if (_load_resource_pack(exec_dir.plus_file(exec_basename + ".pck")) ||
+				_load_resource_pack(exec_dir.plus_file(exec_filename + ".pck"))) {
 			found = true;
 		} else {
-			datapack_name = filebase_name + ".pck";
-			if (_load_resource_pack(datapack_name)) {
+			// If we couldn't find them next to the executable, we attempt
+			// the current working directory. Same story, two tests.
+			if (_load_resource_pack(exec_basename + ".pck") ||
+					_load_resource_pack(exec_filename + ".pck")) {
 				found = true;
 			}
 		}
 
-		// if we opened our package, try and load our project...
+		// If we opened our package, try and load our project
 		if (found) {
 			Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 			if (err == OK) {
@@ -350,17 +381,15 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
 		}
 	}
 
-	//Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this)
-	if (OS::get_singleton()->get_resource_dir() != "") {
-		//OS will call Globals->get_resource_path which will be empty if not overridden!
-		//if the OS would rather use somewhere else, then it will not be empty.
+	// Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this)
 
+	if (OS::get_singleton()->get_resource_dir() != "") {
+		// OS will call ProjectSettings->get_resource_path which will be empty if not overridden!
+		// If the OS would rather use a specific location, then it will not be empty.
 		resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/");
-		if (resource_path.length() && resource_path[resource_path.length() - 1] == '/')
+		if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') {
 			resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end
-
-		// data.pck and data.zip are deprecated and no longer supported, apologies.
-		// make sure this is loaded from the resource path
+		}
 
 		Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 		if (err == OK) {
@@ -371,21 +400,19 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
 		return err;
 	}
 
-	//Nothing was found, try to find a project.godot somewhere!
+	// Nothing was found, try to find a project file in provided path (`p_path`)
+	// or, if requested (`p_upwards`) in parent directories.
 
 	DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	ERR_FAIL_COND_V(!d, ERR_CANT_CREATE);
-
 	d->change_dir(p_path);
 
-	String candidate = d->get_current_dir();
 	String current_dir = d->get_current_dir();
-
+	String candidate = current_dir;
 	bool found = false;
 	Error err;
 
 	while (true) {
-
 		err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary"));
 		if (err == OK) {
 			// Optional, we don't mind if it fails
@@ -396,10 +423,10 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
 		}
 
 		if (p_upwards) {
-			// Try to load settings ascending through dirs shape!
+			// Try to load settings ascending through parent directories
 			d->change_dir("..");
 			if (d->get_current_dir() == current_dir)
-				break; //not doing anything useful
+				break; // not doing anything useful
 			current_dir = d->get_current_dir();
 		} else {
 			break;
@@ -416,6 +443,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
 	if (resource_path.length() && resource_path[resource_path.length() - 1] == '/')
 		resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end
 
+	// If we're loading a project.godot from source code, we can operate some
+	// ProjectSettings conversions if need be.
 	_convert_to_last_version();
 
 	return OK;