@@ -478,8 +478,20 @@ class ProjectDialog : public ConfirmationDialog {
478
478
cd->grab_focus ();
479
479
return ;
480
480
}
481
+ PackedStringArray project_features = ProjectSettings::get_required_features ();
481
482
ProjectSettings::CustomMap initial_settings;
482
- initial_settings[" rendering/vulkan/rendering/back_end" ] = rasterizer_button_group->get_pressed_button ()->get_meta (SNAME (" driver_name" ));
483
+ // Be sure to change this code if/when renderers are changed.
484
+ int renderer_type = rasterizer_button_group->get_pressed_button ()->get_meta (SNAME (" driver_name" ));
485
+ initial_settings[" rendering/vulkan/rendering/back_end" ] = renderer_type;
486
+ if (renderer_type == 0 ) {
487
+ project_features.push_back (" Vulkan Clustered" );
488
+ } else if (renderer_type == 1 ) {
489
+ project_features.push_back (" Vulkan Mobile" );
490
+ } else {
491
+ WARN_PRINT (" Unknown renderer type. Please report this as a bug on GitHub." );
492
+ }
493
+ project_features.sort ();
494
+ initial_settings[" application/config/features" ] = project_features;
483
495
initial_settings[" application/config/name" ] = project_name->get_text ().strip_edges ();
484
496
initial_settings[" application/config/icon" ] = " res://icon.png" ;
485
497
@@ -1019,6 +1031,7 @@ class ProjectList : public ScrollContainer {
1019
1031
String path;
1020
1032
String icon;
1021
1033
String main_scene;
1034
+ PackedStringArray unsupported_features;
1022
1035
uint64_t last_edited = 0 ;
1023
1036
bool favorite = false ;
1024
1037
bool grayed = false ;
@@ -1035,6 +1048,7 @@ class ProjectList : public ScrollContainer {
1035
1048
const String &p_path,
1036
1049
const String &p_icon,
1037
1050
const String &p_main_scene,
1051
+ const PackedStringArray &p_unsupported_features,
1038
1052
uint64_t p_last_edited,
1039
1053
bool p_favorite,
1040
1054
bool p_grayed,
@@ -1046,6 +1060,7 @@ class ProjectList : public ScrollContainer {
1046
1060
path = p_path;
1047
1061
icon = p_icon;
1048
1062
main_scene = p_main_scene;
1063
+ unsupported_features = p_unsupported_features;
1049
1064
last_edited = p_last_edited;
1050
1065
favorite = p_favorite;
1051
1066
grayed = p_grayed;
@@ -1097,8 +1112,7 @@ class ProjectList : public ScrollContainer {
1097
1112
void remove_project (int p_index, bool p_update_settings);
1098
1113
void update_icons_async ();
1099
1114
void load_project_icon (int p_index);
1100
-
1101
- static void load_project_data (const String &p_property_key, Item &p_item, bool p_favorite);
1115
+ static Item load_project_data (const String &p_property_key, bool p_favorite);
1102
1116
1103
1117
String _search_term;
1104
1118
FilterOption _order_option;
@@ -1189,7 +1203,8 @@ void ProjectList::load_project_icon(int p_index) {
1189
1203
item.control ->icon_needs_reload = false ;
1190
1204
}
1191
1205
1192
- void ProjectList::load_project_data (const String &p_property_key, Item &p_item, bool p_favorite) {
1206
+ // Load project data from p_property_key and return it in a ProjectList::Item. p_favorite is passed directly into the Item.
1207
+ ProjectList::Item ProjectList::load_project_data (const String &p_property_key, bool p_favorite) {
1193
1208
String path = EditorSettings::get_singleton ()->get (p_property_key);
1194
1209
String conf = path.plus_file (" project.godot" );
1195
1210
bool grayed = false ;
@@ -1209,13 +1224,56 @@ void ProjectList::load_project_data(const String &p_property_key, Item &p_item,
1209
1224
}
1210
1225
1211
1226
if (config_version > ProjectSettings::CONFIG_VERSION) {
1212
- // Comes from an incompatible (more recent) Godot version, grey it out
1227
+ // Comes from an incompatible (more recent) Godot version, gray it out.
1213
1228
grayed = true ;
1214
1229
}
1215
1230
1216
- String description = cf->get_value (" application" , " config/description" , " " );
1217
- String icon = cf->get_value (" application" , " config/icon" , " " );
1218
- String main_scene = cf->get_value (" application" , " run/main_scene" , " " );
1231
+ const String description = cf->get_value (" application" , " config/description" , " " );
1232
+ const String icon = cf->get_value (" application" , " config/icon" , " " );
1233
+ const String main_scene = cf->get_value (" application" , " run/main_scene" , " " );
1234
+
1235
+ PackedStringArray project_features = cf->get_value (" application" , " config/features" , PackedStringArray ());
1236
+ bool project_features_dirty = false ;
1237
+ // If there is no feature list currently present, force one to generate.
1238
+ if (project_features.is_empty ()) {
1239
+ project_features = ProjectSettings::get_required_features ();
1240
+ project_features_dirty = true ;
1241
+ }
1242
+ // Check the rendering API.
1243
+ const String rendering_api = cf->get_value (" rendering" , " quality/driver/driver_name" , " " );
1244
+ if (rendering_api != " " ) {
1245
+ // Add the rendering API as a project feature if it doesn't already exist.
1246
+ if (!project_features.has (rendering_api)) {
1247
+ project_features.append (rendering_api);
1248
+ project_features_dirty = true ;
1249
+ }
1250
+ }
1251
+ // Check for the existence of a csproj file.
1252
+ if (FileAccess::exists (path.plus_file (project_name + " .csproj" ))) {
1253
+ // If there is a csproj file, add the C# feature if it doesn't already exist.
1254
+ if (!project_features.has (" C#" )) {
1255
+ project_features.append (" C#" );
1256
+ project_features_dirty = true ;
1257
+ }
1258
+ } else {
1259
+ // If there isn't a csproj file, remove the C# feature if it exists.
1260
+ if (project_features.has (" C#" )) {
1261
+ project_features.remove_at (project_features.find (" C#" ));
1262
+ project_features_dirty = true ;
1263
+ }
1264
+ }
1265
+ if (project_features_dirty) {
1266
+ project_features.sort ();
1267
+ // Write the updated feature list, but only if the project config version is the same.
1268
+ // Never write to project files with a different config version!
1269
+ if (config_version == ProjectSettings::CONFIG_VERSION) {
1270
+ ProjectSettings *ps = ProjectSettings::get_singleton ();
1271
+ ps->load_custom (conf);
1272
+ ps->set (" application/config/features" , project_features);
1273
+ ps->save_custom (conf);
1274
+ }
1275
+ }
1276
+ PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features (project_features);
1219
1277
1220
1278
uint64_t last_edited = 0 ;
1221
1279
if (FileAccess::exists (conf)) {
@@ -1237,9 +1295,9 @@ void ProjectList::load_project_data(const String &p_property_key, Item &p_item,
1237
1295
print_line (" Project is missing: " + conf);
1238
1296
}
1239
1297
1240
- String project_key = p_property_key.get_slice (" /" , 1 );
1298
+ const String project_key = p_property_key.get_slice (" /" , 1 );
1241
1299
1242
- p_item = Item (project_key, project_name, description, path, icon, main_scene, last_edited, p_favorite, grayed, missing, config_version);
1300
+ return Item (project_key, project_name, description, path, icon, main_scene, unsupported_features , last_edited, p_favorite, grayed, missing, config_version);
1243
1301
}
1244
1302
1245
1303
void ProjectList::load_projects () {
@@ -1282,8 +1340,7 @@ void ProjectList::load_projects() {
1282
1340
String project_key = property_key.get_slice (" /" , 1 );
1283
1341
bool favorite = favorites.has (" favorite_projects/" + project_key);
1284
1342
1285
- Item item;
1286
- load_project_data (property_key, item, favorite);
1343
+ Item item = load_project_data (property_key, favorite);
1287
1344
1288
1345
_projects.push_back (item);
1289
1346
}
@@ -1366,7 +1423,7 @@ void ProjectList::create_project_item_control(int p_index) {
1366
1423
TextureButton *favorite = memnew (TextureButton);
1367
1424
favorite->set_name (" FavoriteButton" );
1368
1425
favorite->set_normal_texture (favorite_icon);
1369
- // This makes the project's "hover" style display correctly when hovering the favorite icon
1426
+ // This makes the project's "hover" style display correctly when hovering the favorite icon.
1370
1427
favorite->set_mouse_filter (MOUSE_FILTER_PASS);
1371
1428
favorite->connect (" pressed" , callable_mp (this , &ProjectList::_favorite_pressed), varray (hb));
1372
1429
favorite_box->add_child (favorite);
@@ -1396,40 +1453,65 @@ void ProjectList::create_project_item_control(int p_index) {
1396
1453
ec->set_custom_minimum_size (Size2 (0 , 1 ));
1397
1454
ec->set_mouse_filter (MOUSE_FILTER_PASS);
1398
1455
vb->add_child (ec);
1399
- Label *title = memnew (Label (!item.missing ? item.project_name : TTR (" Missing Project" )));
1400
- title->add_theme_font_override (" font" , get_theme_font (SNAME (" title" ), SNAME (" EditorFonts" )));
1401
- title->add_theme_font_size_override (" font_size" , get_theme_font_size (SNAME (" title_size" ), SNAME (" EditorFonts" )));
1402
- title->add_theme_color_override (" font_color" , font_color);
1403
- title->set_clip_text (true );
1404
- vb->add_child (title);
1405
-
1406
- HBoxContainer *path_hb = memnew (HBoxContainer);
1407
- path_hb->set_h_size_flags (Control::SIZE_EXPAND_FILL);
1408
- vb->add_child (path_hb);
1409
-
1410
- Button *show = memnew (Button );
1411
- // Display a folder icon if the project directory can be opened, or a "broken file" icon if it can't.
1412
- show->set_icon (get_theme_icon (!item.missing ? " Load" : " FileBroken" , " EditorIcons" ));
1413
- if (!item.grayed ) {
1414
- // Don't make the icon less prominent if the parent is already grayed out.
1415
- show->set_modulate (Color (1 , 1 , 1 , 0.5 ));
1416
- }
1417
- path_hb->add_child (show);
1418
-
1419
- if (!item.missing ) {
1420
- show->connect (" pressed" , callable_mp (this , &ProjectList::_show_project), varray (item.path ));
1421
- show->set_tooltip (TTR (" Show in File Manager" ));
1422
- } else {
1423
- show->set_tooltip (TTR (" Error: Project is missing on the filesystem." ));
1424
- }
1425
1456
1426
- Label *fpath = memnew (Label (item.path ));
1427
- fpath->set_structured_text_bidi_override (Control::STRUCTURED_TEXT_FILE);
1428
- path_hb->add_child (fpath);
1429
- fpath->set_h_size_flags (Control::SIZE_EXPAND_FILL);
1430
- fpath->set_modulate (Color (1 , 1 , 1 , 0.5 ));
1431
- fpath->add_theme_color_override (" font_color" , font_color);
1432
- fpath->set_clip_text (true );
1457
+ { // Top half, title and unsupported features labels.
1458
+ HBoxContainer *title_hb = memnew (HBoxContainer);
1459
+ vb->add_child (title_hb);
1460
+
1461
+ Label *title = memnew (Label (!item.missing ? item.project_name : TTR (" Missing Project" )));
1462
+ title->set_h_size_flags (Control::SIZE_EXPAND_FILL);
1463
+ title->add_theme_font_override (" font" , get_theme_font (SNAME (" title" ), SNAME (" EditorFonts" )));
1464
+ title->add_theme_font_size_override (" font_size" , get_theme_font_size (SNAME (" title_size" ), SNAME (" EditorFonts" )));
1465
+ title->add_theme_color_override (" font_color" , font_color);
1466
+ title->set_clip_text (true );
1467
+ title_hb->add_child (title);
1468
+
1469
+ String unsupported_features_str = Variant (item.unsupported_features ).operator String ().trim_prefix (" [" ).trim_suffix (" ]" );
1470
+ int length = unsupported_features_str.length ();
1471
+ if (length > 0 ) {
1472
+ Label *unsupported_label = memnew (Label (unsupported_features_str));
1473
+ unsupported_label->set_custom_minimum_size (Size2 (length * 15 , 10 ));
1474
+ unsupported_label->add_theme_font_override (" font" , get_theme_font (SNAME (" title" ), SNAME (" EditorFonts" )));
1475
+ unsupported_label->add_theme_color_override (" font_color" , get_theme_color (SNAME (" warning_color" ), SNAME (" Editor" )));
1476
+ unsupported_label->set_clip_text (true );
1477
+ unsupported_label->set_align (Label::ALIGN_RIGHT);
1478
+ title_hb->add_child (unsupported_label);
1479
+ Control *spacer = memnew (Control ());
1480
+ spacer->set_custom_minimum_size (Size2 (10 , 10 ));
1481
+ title_hb->add_child (spacer);
1482
+ }
1483
+ }
1484
+
1485
+ { // Bottom half, containing the path and view folder button.
1486
+ HBoxContainer *path_hb = memnew (HBoxContainer);
1487
+ path_hb->set_h_size_flags (Control::SIZE_EXPAND_FILL);
1488
+ vb->add_child (path_hb);
1489
+
1490
+ Button *show = memnew (Button );
1491
+ // Display a folder icon if the project directory can be opened, or a "broken file" icon if it can't.
1492
+ show->set_icon (get_theme_icon (!item.missing ? " Load" : " FileBroken" , " EditorIcons" ));
1493
+ show->set_flat (true );
1494
+ if (!item.grayed ) {
1495
+ // Don't make the icon less prominent if the parent is already grayed out.
1496
+ show->set_modulate (Color (1 , 1 , 1 , 0.5 ));
1497
+ }
1498
+ path_hb->add_child (show);
1499
+
1500
+ if (!item.missing ) {
1501
+ show->connect (" pressed" , callable_mp (this , &ProjectList::_show_project), varray (item.path ));
1502
+ show->set_tooltip (TTR (" Show in File Manager" ));
1503
+ } else {
1504
+ show->set_tooltip (TTR (" Error: Project is missing on the filesystem." ));
1505
+ }
1506
+
1507
+ Label *fpath = memnew (Label (item.path ));
1508
+ fpath->set_structured_text_bidi_override (Control::STRUCTURED_TEXT_FILE);
1509
+ path_hb->add_child (fpath);
1510
+ fpath->set_h_size_flags (Control::SIZE_EXPAND_FILL);
1511
+ fpath->set_modulate (Color (1 , 1 , 1 , 0.5 ));
1512
+ fpath->add_theme_color_override (" font_color" , font_color);
1513
+ fpath->set_clip_text (true );
1514
+ }
1433
1515
1434
1516
_scroll_children->add_child (hb);
1435
1517
item.control = hb;
@@ -1634,8 +1716,7 @@ int ProjectList::refresh_project(const String &dir_path) {
1634
1716
if (should_be_in_list) {
1635
1717
// Recreate it with updated info
1636
1718
1637
- Item item;
1638
- load_project_data (property_key, item, is_favourite);
1719
+ Item item = load_project_data (property_key, is_favourite);
1639
1720
1640
1721
_projects.push_back (item);
1641
1722
create_project_item_control (_projects.size () - 1 );
@@ -2114,8 +2195,12 @@ void ProjectManager::_open_selected_projects_ask() {
2114
2195
}
2115
2196
2116
2197
// Update the project settings or don't open
2117
- String conf = project.path .plus_file (" project.godot" );
2118
- int config_version = project.version ;
2198
+ const String conf = project.path .plus_file (" project.godot" );
2199
+ const int config_version = project.version ;
2200
+ PackedStringArray unsupported_features = project.unsupported_features ;
2201
+
2202
+ Label *ask_update_label = ask_update_settings->get_label ();
2203
+ ask_update_label->set_align (Label::ALIGN_LEFT); // Reset in case of previous center align.
2119
2204
2120
2205
// Check if the config_version property was empty or 0
2121
2206
if (config_version == 0 ) {
@@ -2135,6 +2220,35 @@ void ProjectManager::_open_selected_projects_ask() {
2135
2220
dialog_error->popup_centered ();
2136
2221
return ;
2137
2222
}
2223
+ // Check if the project is using features not supported by this build of Godot.
2224
+ if (!unsupported_features.is_empty ()) {
2225
+ String warning_message = " " ;
2226
+ for (int i = 0 ; i < unsupported_features.size (); i++) {
2227
+ String feature = unsupported_features[i];
2228
+ if (feature == " Double Precision" ) {
2229
+ warning_message += TTR (" Warning: This project uses double precision floats, but this version of\n Godot uses single precision floats. Opening this project may cause data loss.\n\n " );
2230
+ unsupported_features.remove_at (i);
2231
+ i--;
2232
+ } else if (feature == " C#" ) {
2233
+ warning_message += TTR (" Warning: This project uses C#, but this build of Godot does not have\n the Mono module. If you proceed you will not be able to use any C# scripts.\n\n " );
2234
+ unsupported_features.remove_at (i);
2235
+ i--;
2236
+ } else if (feature.substr (0 , 3 ).is_numeric ()) {
2237
+ warning_message += vformat (TTR (" Warning: This project was built in Godot %s.\n Opening will upgrade or downgrade the project to Godot %s.\n\n " ), Variant (feature), Variant (VERSION_BRANCH));
2238
+ unsupported_features.remove_at (i);
2239
+ i--;
2240
+ }
2241
+ }
2242
+ if (!unsupported_features.is_empty ()) {
2243
+ String unsupported_features_str = Variant (unsupported_features).operator String ().trim_prefix (" [" ).trim_suffix (" ]" );
2244
+ warning_message += vformat (TTR (" Warning: This project uses the following features not supported by this build of Godot:\n\n %s\n\n " ), unsupported_features_str);
2245
+ }
2246
+ warning_message += TTR (" Open anyway? Project will be modified." );
2247
+ ask_update_label->set_align (Label::ALIGN_CENTER);
2248
+ ask_update_settings->set_text (warning_message);
2249
+ ask_update_settings->popup_centered ();
2250
+ return ;
2251
+ }
2138
2252
2139
2253
// Open if the project is up-to-date
2140
2254
_open_selected_projects ();
0 commit comments