Skip to content

Commit 2508c4f

Browse files
committed
Fixed TopMenubar/VehicleAi glitches with AI presets.
*Problem:* After returning to menu and loading another map, the presets would never display (the spinner would spin indefinitely). *Cause:* broken by adding the [AI Presets] section to terrn2 format - these required the list of presets to be refreshed on each new terrain, but it only happened once. *Updates:* * I changed it so that the local waypoints file also loads on background with spinner, so it doesn't lag the game * I also made the budndled presets display immediately with spinner below it, instead of displaying everything only after download finished * `MSG_NET_REFRESH_AI_PRESETS` is dead, long live `MSG_NET_FETCH_AI_PRESETS_{SUCCESS/FAILURE}`
1 parent 017800f commit 2508c4f

File tree

9 files changed

+139
-61
lines changed

9 files changed

+139
-61
lines changed

doc/angelscript/Script2Game/globals.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,8 @@ enum MsgType
629629
MSG_NET_REFRESH_REPOLIST_SUCCESS, //!< Background task notification, DO NOT PUSH MANUALLY.
630630
MSG_NET_OPEN_RESOURCE_SUCCESS, //!< Background task notification, DO NOT PUSH MANUALLY.
631631
MSG_NET_REFRESH_REPOLIST_FAILURE, //!< Background task notification, DO NOT PUSH MANUALLY.
632-
MSG_NET_REFRESH_AI_PRESETS, //!< Request refresh of AI presets menu in top menubar. No params.
632+
MSG_NET_FETCH_AI_PRESETS_SUCCESS, //!< Background task notification, DO NOT PUSH MANUALLY.
633+
MSG_NET_FETCH_AI_PRESETS_FAILURE, //!< Background task notification, DO NOT PUSH MANUALLY.
633634
// Simulation
634635
MSG_SIM_PAUSE_REQUESTED, //!< Pause game. No params.
635636
MSG_SIM_UNPAUSE_REQUESTED, //!< Unpause game. No params.

source/main/Application.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,8 @@ const char* MsgTypeToString(MsgType type)
577577
case MSG_NET_REFRESH_REPOLIST_SUCCESS : return "MSG_NET_REFRESH_REPOLIST_SUCCESS";
578578
case MSG_NET_OPEN_RESOURCE_SUCCESS : return "MSG_NET_OPEN_RESOURCE_SUCCESS";
579579
case MSG_NET_REFRESH_REPOLIST_FAILURE : return "MSG_NET_REFRESH_REPOLIST_FAILURE";
580-
case MSG_NET_REFRESH_AI_PRESETS : return "MSG_NET_REFRESH_AI_PRESETS";
580+
case MSG_NET_FETCH_AI_PRESETS_SUCCESS : return "MSG_NET_FETCH_AI_PRESETS_SUCCESS";
581+
case MSG_NET_FETCH_AI_PRESETS_FAILURE : return "MSG_NET_FETCH_AI_PRESETS_FAILURE";
581582

582583
case MSG_SIM_PAUSE_REQUESTED : return "MSG_SIM_PAUSE_REQUESTED";
583584
case MSG_SIM_UNPAUSE_REQUESTED : return "MSG_SIM_UNPAUSE_REQUESTED";

source/main/Application.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ namespace RoR {
7373
/// Global gameplay message loop, see `struct Message` in GameContext.h
7474
enum MsgType
7575
{
76+
// CHECKLIST for adding new message types:
77+
// * Provide name as string constant - see `MsgTypeToString()` in Application.cpp
78+
// * Register it with AngelScript - see `RegisterMessageQueue()` in 'scripting/bindings/MsgQueueAngelscript.cpp'
79+
// * Allow/Deny pushing from AngelScript - see `pushMessage()` in 'scripting/GameScript.cpp'
80+
// * Document the AngelScript usage - see 'doc/angelscript/Script2Game/globals.h'.
81+
// * Make it do something useful in 'main.cpp' ;)
82+
7683
MSG_INVALID,
7784
// Application
7885
MSG_APP_SHUTDOWN_REQUESTED,
@@ -100,7 +107,8 @@ enum MsgType
100107
MSG_NET_REFRESH_REPOLIST_SUCCESS, //!< Payload = GUI::ResourcesCollection* (owner)
101108
MSG_NET_OPEN_RESOURCE_SUCCESS, //!< Payload = GUI::ResourcesCollection* (owner)
102109
MSG_NET_REFRESH_REPOLIST_FAILURE, //!< Payload = RoR::CurlFailInfo* (owner)
103-
MSG_NET_REFRESH_AI_PRESETS,
110+
MSG_NET_FETCH_AI_PRESETS_SUCCESS, //!< Description = JSON string
111+
MSG_NET_FETCH_AI_PRESETS_FAILURE, //!< Description = message
104112
// Simulation
105113
MSG_SIM_PAUSE_REQUESTED,
106114
MSG_SIM_UNPAUSE_REQUESTED,

source/main/gui/panels/GUI_TopMenubar.cpp

+100-32
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,31 @@ static size_t CurlWriteFunc(void *ptr, size_t size, size_t nmemb, std::string* d
7373
return size * nmemb;
7474
}
7575

76-
void GetJson()
76+
void FetchAiPresetsThreadFunc()
7777
{
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+
78101
std::string url = "https://raw.githubusercontent.com/RigsOfRods-Community/ai-waypoints/main/waypoints.json";
79102
std::string response_payload;
80103
long response_code = 0;
@@ -99,10 +122,13 @@ void GetJson()
99122
Ogre::LogManager::getSingleton().stream()
100123
<< "[RoR|Repository] Failed to download AI presets;"
101124
<< " 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);
102128
}
103129
else
104130
{
105-
Message m(MSG_NET_REFRESH_AI_PRESETS);
131+
Message m(MSG_NET_FETCH_AI_PRESETS_SUCCESS);
106132
m.description = response_payload;
107133
App::GetGameContext()->PushMessage(m);
108134
}
@@ -1266,41 +1292,18 @@ void TopMenubar::Draw(float dt)
12661292
ImGui::PopStyleColor();
12671293
ImGui::Separator();
12681294

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")))
12941296
{
1297+
// Draw whatever we already have (i.e. presets bundled with terrain, see '[AI Presets]' in terrn2 format).
12951298
size_t num_rows = ai_presets_all.GetArray().Size();
1296-
int count = 0;
1299+
int display_count = 0;
12971300
for (size_t i = 0; i < num_rows; i++)
12981301
{
12991302
rapidjson::Value& j_row = ai_presets_all[static_cast<rapidjson::SizeType>(i)];
13001303

13011304
if (j_row.HasMember("terrain") && App::sim_terrain_name->getStr() == j_row["terrain"].GetString())
13021305
{
1303-
count++;
1306+
display_count++;
13041307
if (ImGui::Button(j_row["preset"].GetString(), ImVec2(250, 0)))
13051308
{
13061309
ai_waypoints.clear();
@@ -1329,7 +1332,32 @@ void TopMenubar::Draw(float dt)
13291332
}
13301333
}
13311334
}
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 == "")
13331361
{
13341362
ImGui::Text("%s", _LC("TopMenubar", "No presets found for this terrain :("));
13351363
ImGui::Text("%s", _LC("TopMenubar", "Supported terrains:"));
@@ -2391,11 +2419,51 @@ void TopMenubar::DrawSpecialStateBox(float top_offset)
23912419
}
23922420
}
23932421

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()
23952462
{
23962463
#if defined(USE_CURL)
2397-
std::packaged_task<void()> task(GetJson);
2464+
std::packaged_task<void()> task(FetchAiPresetsThreadFunc);
23982465
std::thread(std::move(task)).detach();
2466+
ai_presets_extern_fetching = true;
23992467
#endif // defined(USE_CURL)
24002468
}
24012469

source/main/gui/panels/GUI_TopMenubar.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,13 @@ class TopMenubar
9898
int ai_mode_prev = 0;
9999

100100
// AI waypoint presets
101-
void DownloadAiPresets(); //!< Initiate threaded download of 'extern' waypoints from GitHub repo.
101+
void FetchExternAiPresetsOnBackground(); //!< Initiate threaded (down)load of 'extern' waypoints from GitHub repo.
102+
void LoadBundledAiPresets(TerrainPtr terrain); //!< Loads JSON files from `[AI Presets]` section in .terrn2 file format.
102103
void RefreshAiPresets(); //!< Refresh the list of presets, used for display. Needs to be called when terrain is loaded.
103104
rapidjson::Document ai_presets_all; //!< The full list of presets, used for display. Needs to be refreshed when terrain is loaded.
104105
rapidjson::Document ai_presets_extern; //!< Externally provided presets (GitHub repo or local 'savegames/waypoints.json' file).
106+
bool ai_presets_extern_fetching = false; //!< True if the (down)load of 'extern' waypoints is in progress.
107+
std::string ai_presets_extern_error; //!< Error message from the (down)load of 'extern' waypoints.
105108
rapidjson::Document ai_presets_bundled; //!< Presets bundled with the terrain, see `[AI Presets]` section in .terrn2 file format.
106109

107110
// Tuning menu

source/main/main.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -749,10 +749,11 @@ int main(int argc, char *argv[])
749749
break;
750750
}
751751

752-
case MSG_NET_REFRESH_AI_PRESETS:
752+
case MSG_NET_FETCH_AI_PRESETS_SUCCESS:
753753
{
754754
try
755755
{
756+
App::GetGuiManager()->TopMenubar.ai_presets_extern_fetching = false;
756757
App::GetGuiManager()->TopMenubar.ai_presets_extern.Parse(m.description.c_str());
757758
App::GetGuiManager()->TopMenubar.RefreshAiPresets();
758759
}
@@ -763,6 +764,21 @@ int main(int argc, char *argv[])
763764
break;
764765
}
765766

767+
case MSG_NET_FETCH_AI_PRESETS_FAILURE:
768+
{
769+
try
770+
{
771+
App::GetGuiManager()->TopMenubar.ai_presets_extern_fetching = false;
772+
App::GetGuiManager()->TopMenubar.ai_presets_extern_error = m.description;
773+
App::GetGuiManager()->TopMenubar.RefreshAiPresets();
774+
}
775+
catch (...)
776+
{
777+
HandleMsgQueueException(m.type);
778+
}
779+
break;
780+
}
781+
766782
// -- Gameplay events --
767783

768784
case MSG_SIM_PAUSE_REQUESTED:

source/main/scripting/GameScript.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,8 @@ bool GameScript::pushMessage(MsgType type, AngelScript::CScriptDictionary* dict)
13941394
case MSG_NET_REFRESH_REPOLIST_SUCCESS:
13951395
case MSG_NET_OPEN_RESOURCE_SUCCESS:
13961396
case MSG_NET_REFRESH_REPOLIST_FAILURE:
1397+
case MSG_NET_FETCH_AI_PRESETS_SUCCESS:
1398+
case MSG_NET_FETCH_AI_PRESETS_FAILURE:
13971399
// GUI
13981400
case MSG_GUI_SHOW_MESSAGE_BOX_REQUESTED:
13991401
case MSG_GUI_DOWNLOAD_PROGRESS:

source/main/scripting/bindings/MsgQueueAngelscript.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ void RoR::RegisterMessageQueue(asIScriptEngine* engine)
6262
result = engine->RegisterEnumValue("MsgType", "MSG_NET_REFRESH_REPOLIST_SUCCESS", MSG_NET_REFRESH_REPOLIST_SUCCESS); ROR_ASSERT(result >= 0);
6363
result = engine->RegisterEnumValue("MsgType", "MSG_NET_OPEN_RESOURCE_SUCCESS", MSG_NET_OPEN_RESOURCE_SUCCESS); ROR_ASSERT(result >= 0);
6464
result = engine->RegisterEnumValue("MsgType", "MSG_NET_REFRESH_REPOLIST_FAILURE", MSG_NET_REFRESH_REPOLIST_FAILURE); ROR_ASSERT(result >= 0);
65-
result = engine->RegisterEnumValue("MsgType", "MSG_NET_REFRESH_AI_PRESETS", MSG_NET_REFRESH_AI_PRESETS); ROR_ASSERT(result >= 0);
65+
result = engine->RegisterEnumValue("MsgType", "MSG_NET_FETCH_AI_PRESETS_SUCCESS", MSG_NET_FETCH_AI_PRESETS_SUCCESS); ROR_ASSERT(result >= 0);
66+
result = engine->RegisterEnumValue("MsgType", "MSG_NET_FETCH_AI_PRESETS_FAILURE", MSG_NET_FETCH_AI_PRESETS_FAILURE); ROR_ASSERT(result >= 0);
6667
// Simulation
6768
result = engine->RegisterEnumValue("MsgType", "MSG_SIM_PAUSE_REQUESTED", MSG_SIM_PAUSE_REQUESTED); ROR_ASSERT(result >= 0);
6869
result = engine->RegisterEnumValue("MsgType", "MSG_SIM_UNPAUSE_REQUESTED", MSG_SIM_UNPAUSE_REQUESTED); ROR_ASSERT(result >= 0);

source/main/terrain/Terrain.cpp

+1-23
Original file line numberDiff line numberDiff line change
@@ -475,29 +475,7 @@ void RoR::Terrain::initAiPresets()
475475
// Load 'bundled' AI presets - see section `[AI Presets]` in terrn2 file format
476476
// ----------------------------------------------------------------------------
477477

478-
for (const std::string& filename: m_def.ai_presets_files)
479-
{
480-
// assume there's just one file - load directly into the `ai_presets_bundled` variable
481-
if (Ogre::ResourceGroupManager::getSingleton().resourceExists(this->getTerrainFileResourceGroup(), filename))
482-
{
483-
App::GetContentManager()->LoadAndParseJson(filename, this->getTerrainFileResourceGroup(), App::GetGuiManager()->TopMenubar.ai_presets_bundled);
484-
}
485-
else
486-
{
487-
LOG(fmt::format("[RoR|Terrain] AI presets file '{}' declared in '{}' not found!", filename, this->getTerrainFileName()));
488-
}
489-
490-
// Ensure the format is about right
491-
if (!App::GetGuiManager()->TopMenubar.ai_presets_bundled.IsArray())
492-
{
493-
LOG(fmt::format("[RoR|Terrain] AI presets file '{}' declared in '{}' has wrong format - the root element is not an array!",
494-
filename, this->getTerrainFileName()));
495-
App::GetGuiManager()->TopMenubar.ai_presets_bundled.Clear();
496-
}
497-
}
498-
499-
// Force refresh - this is needed because the 'extern' AI presets are loaded later.
500-
App::GetGuiManager()->TopMenubar.ai_presets_all.Clear();
478+
App::GetGuiManager()->TopMenubar.LoadBundledAiPresets(this);
501479
}
502480

503481
void RoR::Terrain::setGravity(float value)

0 commit comments

Comments
 (0)