17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-10 16:03:16 +01:00

Merge branch 'master' into feat/renderers-cleanup

This commit is contained in:
2026-11-07 16:44:51 +01:00
177 changed files with 11657 additions and 833 deletions

View File

@@ -295,7 +295,7 @@ void eu07_application::DiscordRPCService()
if (currentMode != mode::driver)
{
// the editor and the other modes: nothing to tell, without spinning a core on it
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::this_thread::sleep_for(std::chrono::milliseconds(250));
continue;
}
@@ -715,19 +715,29 @@ void eu07_application::release_python_lock()
void eu07_application::exit()
{
Global.applicationQuitOrder = true;
// each step of the closing in the log, to tell from it where a closing that hangs stops
auto const closing{std::chrono::steady_clock::now()};
auto const step = [closing](char const *What) {
WriteLog(std::format("Closing: {} at {} ms", What, std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - closing).count()));
};
step("the modes");
for (auto &mode : m_modes)
mode.reset();
step("the renderer");
GfxRenderer->Shutdown();
m_network.reset();
// SafeDelete( simulation::Train );
step("the scenery");
SafeDelete(simulation::Region);
step("the ui and the python workers");
ui_layer::shutdown();
// the python workers detach their GL context on the way out, so they have to be done before the windows they use are destroyed
m_taskqueue.exit();
step("the windows");
for (auto *window : m_windows)
{
glfwDestroyWindow(window);
@@ -738,6 +748,8 @@ void eu07_application::exit()
if (!Global.exec_on_exit.empty())
system(Global.exec_on_exit.c_str());
step("done");
StopLogService();
auto it = Global.threads.find("LogService");
if (it != Global.threads.end())
{

View File

@@ -74,9 +74,9 @@ void driver_ui::render_menu_contents()
if (ImGui::BeginMenu(STR_C("Mode windows")))
{
ImGui::MenuItem(m_aidpanel.title.c_str(), "F1", &m_aidpanel.is_open);
ImGui::MenuItem(m_scenariopanel.title.c_str(), "F1", &m_aidpanel.is_open);
ImGui::MenuItem(STR_C("Timetable"), "F2", &m_timetablepanel.is_open);
ImGui::MenuItem(m_debugpanel.name().c_str(), "F12", &m_debugpanel.is_open);
ImGui::MenuItem(m_scenariopanel.title.c_str(), "F3", &m_scenariopanel.is_open);
ImGui::MenuItem(m_debugpanel.name().c_str(), "Shift+F12", &m_debugpanel.is_open);
ImGui::MenuItem(m_mappanel.name().c_str(), "Tab", &m_mappanel.is_open);
ImGui::MenuItem(m_vehiclelist.name().c_str(), nullptr, &m_vehiclelist.is_open);
ImGui::MenuItem(m_trainingcardpanel.name().c_str(), nullptr, &m_trainingcardpanel.is_open);

View File

@@ -1067,6 +1067,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri
void track_gizmo_length(TTrack &Track, double Length);
void track_gizmo_end(TTrack &Track);
bool m_track_dirty{false};
bool m_track_drag_on_the_way{false}; // committed, but not as the end of the drag
std::chrono::steady_clock::time_point m_track_last_commit;
editor_track::state m_track_field_before;
// the event to add to the path, typed

View File

@@ -126,8 +126,8 @@ bool is_curved(segment_data const &Path)
void copy_text(char *Buffer, std::size_t const Size, std::string const &Text)
{
std::strncpy(Buffer, Text.c_str(), Size - 1);
Buffer[Size - 1] = 0;
// as much of the text as fits, always terminated
Buffer[Text.copy(Buffer, Size - 1)] = 0;
}
std::string lower_case(std::string Text)

View File

@@ -263,12 +263,14 @@ void editor_mode::draw_track_overlay() const
void editor_mode::commit_track_drag(bool const Force)
{
if (false == m_track_dirty || m_track_drag.empty())
// on letting go the rest of what follows the dragged tracks is yet to come, even with all of the drag committed
if ((false == m_track_dirty && false == (Force && m_track_drag_on_the_way)) || m_track_drag.empty())
return;
auto const now = std::chrono::steady_clock::now();
if (false == Force && now - m_track_last_commit < std::chrono::milliseconds(100))
return;
editor_track::commit(m_track_drag);
editor_track::commit(m_track_drag, false == Force);
m_track_drag_on_the_way = false == Force;
m_track_dirty = false;
m_track_last_commit = now;
}

View File

@@ -21,6 +21,7 @@ http://mozilla.org/MPL/2.0/.
#include "scene/scenelayers.h"
#include "simulation/simulation.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include "utilities/translation.h"
#include "utilities/utilities.h"
#include "world/Track.h"
@@ -174,6 +175,20 @@ void label(screen_projection const &Projection, glm::dvec3 const &Point, std::st
drawlist->AddText(ImVec2(at.x + 12.f, at.y), IM_COL32(255, 255, 255, 235), Text.c_str());
}
// the first line of the file of the turntable controls, which tells it from the files of the scenery made by hand
char const *const kControlsHeader{"// made by the scenery editor on each save: T turns each turntable to its next position, Shift+T to the previous one"};
// true if the file is one of the turntable controls the editor writes
bool controls_file(std::string const &Path)
{
std::ifstream input{Path, std::ios_base::binary};
std::string first;
std::getline(input, first);
if (false == first.empty() && first.back() == '\r')
first.pop_back();
return first == kControlsHeader;
}
} // namespace
bool editor_mode::scan_turntables(bool const Wait)
@@ -1215,7 +1230,7 @@ bool editor_mode::turntable_controls(std::vector<std::string> &Rootstatements)
auto const exists{std::filesystem::exists(path, error)};
if (tables == 0 && false == exists)
return false;
auto const content{"// made by the scenery editor on each save: T turns each turntable to its next position, Shift+T to the previous one\r\n" + text.str()};
auto const content{std::string{kControlsHeader} + "\r\n" + text.str()};
std::string current;
{
std::ifstream input{path, std::ios_base::binary};
@@ -1226,6 +1241,21 @@ bool editor_mode::turntable_controls(std::vector<std::string> &Rootstatements)
std::ofstream output{path, std::ios_base::binary | std::ios_base::trunc};
output << content;
}
// the controls written for this scenery under another name, the file it was copied from, give way to these: with both
// included each turntable would have its events twice. their files stay on the disk, for the scenery they were made for
for (std::size_t handle = 1; handle <= scene::Layers.size(); ++handle)
{
auto const layer{static_cast<scene::layer_handle>(handle)};
if (false == scene::Layers.listed(layer) || scene::Layers.layer(layer).removed)
continue;
auto name{scene::Layers.layer(layer).name};
std::replace(name.begin(), name.end(), '\\', '/');
auto const lower{ToLower(name)};
if (lower == ToLower(file) || lower.ends_with("/" + ToLower(file)) || false == lower.ends_with("_obrotnice.scm"))
continue;
if (controls_file(Global.asCurrentSceneryPath + scene::Layers.layer(layer).name) && scene::Layers.remove(layer))
WriteLog("Editor: " + name + " left out of the scenery, the turntable controls are in " + file + " now");
}
auto included{m_turntable_included};
for (std::size_t handle = 1; handle <= scene::Layers.size() && false == included; ++handle)
{

View File

@@ -21,6 +21,8 @@ http://mozilla.org/MPL/2.0/.
#include "utilities/translation.h"
#include "audio/audiorenderer.h"
#include <format>
scenarioloader_mode::scenarioloader_mode() {
m_userinterface = std::make_shared<scenarioloader_ui>();
}
@@ -52,8 +54,9 @@ bool scenarioloader_mode::update() {
try {
state = simulation::State.deserialize_begin(Global.SceneryFile);
}
catch (std::exception const &e) {
ErrorLog("Bad init: scenario \"" + Global.SceneryFile + "\" couldn't be opened (" + e.what() + ")");
// the file it can't read, as the parser and the scenery opening it report it
catch (std::runtime_error const &e) {
ErrorLog(std::format("Bad init: scenario \"{}\" couldn't be opened ({})", Global.SceneryFile, e.what()));
state.reset();
Global.SceneryFile.clear();
Application.pop_mode();
@@ -67,8 +70,13 @@ bool scenarioloader_mode::update() {
return true;
}
catch (invalid_scenery_exception &e) {
// back to the launcher as when the scenario can't be opened, not on to the driver mode with the scenery half loaded
ErrorLog( "Bad init: scenario loading failed" );
state.reset();
Global.SceneryFile.clear();
Application.pop_mode();
Application.push_mode(eu07_application::mode::launcher);
return true;
}
// the loading state holds the scenario file open, which on some systems keeps the file from being replaced
// when the scenery is saved in the editor. it'd also get in the way of another load

View File

@@ -407,14 +407,21 @@ bool ui_layer::init(GLFWwindow *Window)
}
// panels can be docked together, and dragged out of the simulator window into windows of their own (e.g. on another monitor)
// when both backends can do it. glfw can't place windows under wayland. both flags have to be set before the first frame
// when both backends can do it. glfw can't place windows under wayland. gamescope does not work well with popup windows.
// both flags have to be set before the first frame
m_imguiio->ConfigFlags |= ImGuiConfigFlags_DockingEnable;
const char* desktop = std::getenv("XDG_CURRENT_DESKTOP");
const char* gamepadui = std::getenv("SteamGamepadUI");
if (!desktop) desktop = "";
if (!gamepadui) gamepadui = "";
auto const viewportsupport{(m_imguiio->BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (m_imguiio->BackendFlags & ImGuiBackendFlags_RendererHasViewports) &&
glfwGetPlatform() != GLFW_PLATFORM_WAYLAND};
glfwGetPlatform() != GLFW_PLATFORM_WAYLAND && std::string_view{desktop} != "gamescope" && std::string_view{gamepadui} != "1"};
if (Global.ui_viewports && viewportsupport)
{
m_imguiio->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
m_imguiio->ConfigViewportsNoTaskBarIcon = true;
// only dragging a panel with the cursor past the main window takes it out, not resizing it or placing it at the edge
m_imguiio->ConfigViewportsNoAutoDetach = true;
auto &platformio{ImGui::GetPlatformIO()};
imgui_create_window = platformio.Platform_CreateWindow;
platformio.Platform_CreateWindow = create_viewport_window;