@@ -73,8 +73,31 @@ static size_t CurlWriteFunc(void *ptr, size_t size, size_t nmemb, std::string* d
73
73
return size * nmemb;
74
74
}
75
75
76
- void GetJson ()
76
+ void FetchAiPresetsThreadFunc ()
77
77
{
78
+ // If local file 'savegames/waypoints.json' exists, load it; otherwise download from GitHub.
79
+ // -----------------------------------------------------------------------------------------
80
+
81
+ if (FileExists (PathCombine (App::sys_savegames_dir->getStr (), " waypoints.json" )))
82
+ {
83
+ try
84
+ {
85
+ Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (" waypoints.json" , RGN_SAVEGAMES);
86
+ Message m (MSG_NET_FETCH_AI_PRESETS_SUCCESS);
87
+ m.description = stream->getAsString ();
88
+ App::GetGameContext ()->PushMessage (m);
89
+ }
90
+ catch (...)
91
+ {
92
+ RoR::HandleGenericException (" Top menubar / AI presets" );
93
+ Message m (MSG_NET_FETCH_AI_PRESETS_FAILURE);
94
+ m.description = " Failed to load local AI presets." ;
95
+ App::GetGameContext ()->PushMessage (m);
96
+ }
97
+
98
+ return ; // DONE
99
+ }
100
+
78
101
std::string url = " https://raw.githubusercontent.com/RigsOfRods-Community/ai-waypoints/main/waypoints.json" ;
79
102
std::string response_payload;
80
103
long response_code = 0 ;
@@ -99,10 +122,13 @@ void GetJson()
99
122
Ogre::LogManager::getSingleton ().stream ()
100
123
<< " [RoR|Repository] Failed to download AI presets;"
101
124
<< " Error: '" << curl_easy_strerror (curl_result) << " '; HTTP status code: " << response_code;
125
+ Message m (MSG_NET_FETCH_AI_PRESETS_FAILURE);
126
+ m.description = " Failed to download AI presets." ;
127
+ App::GetGameContext ()->PushMessage (m);
102
128
}
103
129
else
104
130
{
105
- Message m (MSG_NET_REFRESH_AI_PRESETS );
131
+ Message m (MSG_NET_FETCH_AI_PRESETS_SUCCESS );
106
132
m.description = response_payload;
107
133
App::GetGameContext ()->PushMessage (m);
108
134
}
@@ -1266,41 +1292,18 @@ void TopMenubar::Draw(float dt)
1266
1292
ImGui::PopStyleColor ();
1267
1293
ImGui::Separator ();
1268
1294
1269
- bool is_open = ImGui::CollapsingHeader (_LC (" TopMenubar" , " Presets" ));
1270
- if (ImGui::IsItemActivated () && !is_open && ai_presets_all.Empty ()) // Rebuild the preset list if blank
1271
- {
1272
- if (ai_presets_extern.Empty ()) // Fetch once
1273
- {
1274
- if (FileExists (PathCombine (App::sys_savegames_dir->getStr (), " waypoints.json" )))
1275
- {
1276
- App::GetContentManager ()->LoadAndParseJson (" waypoints.json" , RGN_SAVEGAMES, ai_presets_extern);
1277
- this ->RefreshAiPresets ();
1278
- }
1279
- else
1280
- {
1281
- this ->DownloadAiPresets ();
1282
- }
1283
- }
1284
- }
1285
-
1286
- if (is_open && ai_presets_all.Empty ())
1287
- {
1288
- float spinner_size = 8 .f ;
1289
- ImGui::SetCursorPosX ((ImGui::GetWindowSize ().x / 2 .f ) - spinner_size);
1290
- LoadingIndicatorCircle (" spinner" , spinner_size, theme.value_blue_text_color , theme.value_blue_text_color , 10 , 10 );
1291
- }
1292
-
1293
- if (is_open && !ai_presets_all.Empty ())
1295
+ if (ImGui::CollapsingHeader (_LC (" TopMenubar" , " Presets" )))
1294
1296
{
1297
+ // Draw whatever we already have (i.e. presets bundled with terrain, see '[AI Presets]' in terrn2 format).
1295
1298
size_t num_rows = ai_presets_all.GetArray ().Size ();
1296
- int count = 0 ;
1299
+ int display_count = 0 ;
1297
1300
for (size_t i = 0 ; i < num_rows; i++)
1298
1301
{
1299
1302
rapidjson::Value& j_row = ai_presets_all[static_cast <rapidjson::SizeType>(i)];
1300
1303
1301
1304
if (j_row.HasMember (" terrain" ) && App::sim_terrain_name->getStr () == j_row[" terrain" ].GetString ())
1302
1305
{
1303
- count ++;
1306
+ display_count ++;
1304
1307
if (ImGui::Button (j_row[" preset" ].GetString (), ImVec2 (250 , 0 )))
1305
1308
{
1306
1309
ai_waypoints.clear ();
@@ -1329,7 +1332,32 @@ void TopMenubar::Draw(float dt)
1329
1332
}
1330
1333
}
1331
1334
}
1332
- if (count == 0 )
1335
+
1336
+ // Fetch additional presets, or display error if failed
1337
+ if (ai_presets_extern.Empty ())
1338
+ {
1339
+ if (ai_presets_extern_fetching)
1340
+ {
1341
+ float spinner_size = 8 .f ;
1342
+ ImGui::SetCursorPosX ((ImGui::GetWindowSize ().x / 2 .f ) - spinner_size);
1343
+ LoadingIndicatorCircle (" spinner" , spinner_size, theme.value_blue_text_color , theme.value_blue_text_color , 10 , 10 );
1344
+ }
1345
+ else if (ai_presets_extern_error != " " )
1346
+ {
1347
+ ImGui::TextColored (RED_TEXT, " %s" , _LC (" TopMenubar" , " Failed to fetch external presets." ));
1348
+ if (ImGui::Button (_LC (" TopMenubar" , " Retry" )))
1349
+ {
1350
+ this ->FetchExternAiPresetsOnBackground (); // Will post `MSG_NET_REFRESH_AI_PRESETS` when done.
1351
+ }
1352
+ }
1353
+ else
1354
+ {
1355
+ this ->FetchExternAiPresetsOnBackground (); // Will post `MSG_NET_REFRESH_AI_PRESETS` when done.
1356
+ }
1357
+ }
1358
+
1359
+ // If no presets found, display message
1360
+ if (display_count == 0 && !ai_presets_extern_fetching && ai_presets_extern_error == " " )
1333
1361
{
1334
1362
ImGui::Text (" %s" , _LC (" TopMenubar" , " No presets found for this terrain :(" ));
1335
1363
ImGui::Text (" %s" , _LC (" TopMenubar" , " Supported terrains:" ));
@@ -2391,11 +2419,51 @@ void TopMenubar::DrawSpecialStateBox(float top_offset)
2391
2419
}
2392
2420
}
2393
2421
2394
- void TopMenubar::DownloadAiPresets ()
2422
+ void TopMenubar::LoadBundledAiPresets (TerrainPtr terrain)
2423
+ {
2424
+ // Load 'bundled' AI presets - see section `[AI Presets]` in terrn2 file format
2425
+ // ----------------------------------------------------------------------------
2426
+
2427
+ App::GetGuiManager ()->TopMenubar .ai_presets_bundled .SetArray ();
2428
+
2429
+ for (const std::string& filename: terrain->GetDef ().ai_presets_files )
2430
+ {
2431
+ rapidjson::Document j_doc;
2432
+ if (Ogre::ResourceGroupManager::getSingleton ().resourceExists (terrain->getTerrainFileResourceGroup (), filename))
2433
+ {
2434
+ App::GetContentManager ()->LoadAndParseJson (filename, terrain->getTerrainFileResourceGroup (), j_doc);
2435
+ }
2436
+ else
2437
+ {
2438
+ LOG (fmt::format (" [RoR|Terrain] AI presets file '{}' declared in '{}' not found!" , filename, terrain->getTerrainFileName ()));
2439
+ }
2440
+
2441
+ // Ensure the format is about right
2442
+ if (!j_doc.IsArray ())
2443
+ {
2444
+ LOG (fmt::format (" [RoR|Terrain] AI presets file '{}' declared in '{}' has wrong format - the root element is not an array!" ,
2445
+ filename, terrain->getTerrainFileName ()));
2446
+ }
2447
+ else
2448
+ {
2449
+ // Finally add the presets to the list
2450
+ for (const rapidjson::Value& j_bundled_preset: j_doc.GetArray ())
2451
+ {
2452
+ rapidjson::Value preset_copy (j_bundled_preset, App::GetGuiManager ()->TopMenubar .ai_presets_bundled .GetAllocator ());
2453
+ App::GetGuiManager ()->TopMenubar .ai_presets_bundled .PushBack (preset_copy, App::GetGuiManager ()->TopMenubar .ai_presets_bundled .GetAllocator ());
2454
+ }
2455
+ }
2456
+ }
2457
+
2458
+ App::GetGuiManager ()->TopMenubar .RefreshAiPresets ();
2459
+ }
2460
+
2461
+ void TopMenubar::FetchExternAiPresetsOnBackground ()
2395
2462
{
2396
2463
#if defined(USE_CURL)
2397
- std::packaged_task<void ()> task (GetJson );
2464
+ std::packaged_task<void ()> task (FetchAiPresetsThreadFunc );
2398
2465
std::thread (std::move (task)).detach ();
2466
+ ai_presets_extern_fetching = true ;
2399
2467
#endif // defined(USE_CURL)
2400
2468
}
2401
2469
0 commit comments