Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor autosave #1554

Merged
merged 3 commits into from
Nov 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 57 additions & 18 deletions src/editor/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
#include "sdk/integration.hpp"
#include "sprite/sprite_manager.hpp"
#include "supertux/game_manager.hpp"
#include "supertux/gameconfig.hpp"
#include "supertux/globals.hpp"
#include "supertux/level.hpp"
#include "supertux/level_parser.hpp"
#include "supertux/menu/menu_storage.hpp"
Expand All @@ -54,7 +56,6 @@
#include "supertux/world.hpp"
#include "util/file_system.hpp"
#include "util/reader_mapping.hpp"
#include "util/string_util.hpp"
#include "video/compositor.hpp"
#include "video/drawing_context.hpp"
#include "video/surface.hpp"
Expand All @@ -78,7 +79,7 @@ Editor::Editor() :
m_level(),
m_world(),
m_levelfile(),
m_test_levelfile(),
m_autosave_levelfile(),
m_quit_request(false),
m_newlevel_request(false),
m_reload_request(false),
Expand All @@ -100,7 +101,8 @@ Editor::Editor() :
m_bgr_surface(Surface::from_file("images/background/antarctic/arctis2.png")),
m_undo_manager(new UndoManager),
m_ignore_sector_change(false),
m_level_first_loaded(false)
m_level_first_loaded(false),
m_time_since_last_save(0.f)
{
auto toolbox_widget = std::make_unique<EditorToolboxWidget>(*this);
auto layers_widget = std::make_unique<EditorLayersWidget>(*this);
Expand Down Expand Up @@ -152,6 +154,25 @@ Editor::draw(Compositor& compositor)
void
Editor::update(float dt_sec, const Controller& controller)
{
// Auto-save (interval)
if (m_level) {
m_time_since_last_save += dt_sec;
if (m_time_since_last_save >= static_cast<float>(std::max(
g_config->editor_autosave_frequency, 1)) * 60.f) {
m_time_since_last_save = 0.f;
std::string backup_filename = get_autosave_from_levelname(m_levelfile);
std::string directory = get_level_directory();

// Set the test level file even though we're not testing, so that
// if the user quits the editor without ever testing, it'll delete
// the autosave file anyways
m_autosave_levelfile = FileSystem::join(directory, backup_filename);
m_level->save(m_autosave_levelfile);
}
} else {
m_time_since_last_save = 0.f;
}

// Pass all requests
if (m_reload_request) {
reload_level();
Expand Down Expand Up @@ -209,12 +230,31 @@ Editor::update(float dt_sec, const Controller& controller)
}
}

void
Editor::remove_autosave_file()
{
// Clear the auto-save file
if (!m_autosave_levelfile.empty())
{
// Try to remove the test level using the PhysFS file system
if (physfsutil::remove(m_autosave_levelfile) != 0)
{
// This file is not inside any PhysFS mounts,
// try to remove this using normal file system
// methods.
FileSystem::remove(m_autosave_levelfile);
}
}
}

void
Editor::save_level()
{
m_undo_manager->reset_index();
m_level->save(m_world ? FileSystem::join(m_world->get_basedir(), m_levelfile) :
m_levelfile);
m_time_since_last_save = 0.f;
remove_autosave_file();
}

std::string
Expand Down Expand Up @@ -242,7 +282,7 @@ Editor::test_level(const boost::optional<std::pair<std::string, Vector>>& test_p

Tile::draw_editor_images = false;
Compositor::s_render_lighting = true;
std::string backup_filename = m_levelfile + "~";
std::string backup_filename = get_autosave_from_levelname(m_levelfile);
std::string directory = get_level_directory();

// This is jank to get an owned World pointer, GameManager/World
Expand All @@ -254,8 +294,10 @@ Editor::test_level(const boost::optional<std::pair<std::string, Vector>>& test_p
current_world = owned_world.get();
}

m_test_levelfile = FileSystem::join(directory, backup_filename);
m_level->save(m_test_levelfile);
m_autosave_levelfile = FileSystem::join(directory, backup_filename);
m_level->save(m_autosave_levelfile);
m_time_since_last_save = 0.f;

if (!m_level->is_worldmap())
{
Integration::set_level(m_level->get_name().c_str());
Expand All @@ -266,7 +308,7 @@ Editor::test_level(const boost::optional<std::pair<std::string, Vector>>& test_p
{
Integration::set_worldmap(m_level->get_name().c_str());
Integration::set_status(TESTING_WORLDMAP);
GameManager::current()->start_worldmap(*current_world, "", m_test_levelfile);
GameManager::current()->start_worldmap(*current_world, "", m_autosave_levelfile);
}

m_leveltested = true;
Expand Down Expand Up @@ -472,6 +514,12 @@ Editor::reload_level()
StringUtil::has_suffix(m_levelfile, ".stwm"),
true));
ReaderMapping::s_translations_enabled = true;

// Autosave files : Once the level is loaded, make sure
// to use the regular file
m_levelfile = get_levelname_from_autosave(m_levelfile);
m_autosave_levelfile = FileSystem::join(get_level_directory(),
get_autosave_from_levelname(m_levelfile));
}

void
Expand All @@ -481,6 +529,8 @@ Editor::quit_editor()

auto quit = [this] ()
{
remove_autosave_file();

//Quit level editor
m_world = nullptr;
m_levelfile = "";
Expand Down Expand Up @@ -573,17 +623,6 @@ Editor::setup()

// Reactivate the editor after level test
if (m_leveltested) {
if (!m_test_levelfile.empty())
{
// Try to remove the test level using the PhysFS file system
if (physfsutil::remove(m_test_levelfile) != 0)
{
// This file is not inside any PhysFS mounts,
// try to remove this using normal file system
// methods.
FileSystem::remove(m_test_levelfile);
}
}
m_leveltested = false;
Tile::draw_editor_images = true;
m_level->reactivate();
Expand Down
18 changes: 17 additions & 1 deletion src/editor/editor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "util/currenton.hpp"
#include "util/file_system.hpp"
#include "util/log.hpp"
#include "util/string_util.hpp"
#include "video/surface_ptr.hpp"

class GameObject;
Expand All @@ -48,6 +49,17 @@ class Editor final : public Screen,
public:
static bool is_active();

private:
static bool is_autosave_file(const std::string& filename) {
return StringUtil::has_suffix(filename, "~");
}
static std::string get_levelname_from_autosave(const std::string& filename) {
return is_autosave_file(filename) ? filename.substr(0, filename.size() - 1) : filename;
}
static std::string get_autosave_from_levelname(const std::string& filename) {
return is_autosave_file(filename) ? filename : filename + "~";
}

public:
static bool s_resaving_in_progress;

Expand Down Expand Up @@ -93,6 +105,8 @@ class Editor final : public Screen,

bool is_testing_level() const { return m_leveltested; }

void remove_autosave_file();

/** Checks whether the level can be saved and does not contain
obvious issues (currently: check if main sector and a spawn point
named "main" is present) */
Expand Down Expand Up @@ -145,7 +159,7 @@ class Editor final : public Screen,
std::unique_ptr<World> m_world;

std::string m_levelfile;
std::string m_test_levelfile;
std::string m_autosave_levelfile;

public:
bool m_quit_request;
Expand Down Expand Up @@ -179,6 +193,8 @@ class Editor final : public Screen,
bool m_ignore_sector_change;

bool m_level_first_loaded;

float m_time_since_last_save;

private:
Editor(const Editor&) = delete;
Expand Down
8 changes: 6 additions & 2 deletions src/gui/dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@
#include "video/video_system.hpp"
#include "video/viewport.hpp"

Dialog::Dialog(bool passive) :
Dialog::Dialog(bool passive, bool auto_clear_dialogs) :
m_text(),
m_buttons(),
m_selected_button(),
m_cancel_button(-1),
m_passive(passive),
m_clear_diags(auto_clear_dialogs),
m_text_size()
{
}
Expand Down Expand Up @@ -258,7 +259,10 @@ Dialog::on_button_click(int button) const
{
m_buttons[button].callback();
}
MenuManager::instance().set_dialog({});
if (m_clear_diags || button == m_cancel_button)
{
MenuManager::instance().set_dialog({});
}
}

/* EOF */
3 changes: 2 additions & 1 deletion src/gui/dialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ class Dialog
int m_selected_button;
int m_cancel_button;
bool m_passive;
bool m_clear_diags;

Sizef m_text_size;

public:
Dialog(bool passive = false);
Dialog(bool passive = false, bool auto_clear_dialogs = true);
virtual ~Dialog();

void set_text(const std::string& text);
Expand Down
7 changes: 6 additions & 1 deletion src/supertux/gameconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Config::Config() :
enable_discord(false),
discord_hide_editor(false),
#endif
editor_autosave_frequency(5),
repository_url()
{
}
Expand Down Expand Up @@ -93,6 +94,8 @@ Config::load()
#endif
}

config_mapping.get("editor_autosave_frequency", editor_autosave_frequency);

EditorOverlayWidget::autotile_help = !developer_mode;

if (is_christmas()) {
Expand Down Expand Up @@ -209,7 +212,9 @@ Config::save()
#endif
}
writer.end_list("integrations");


writer.write("editor_autosave_frequency", editor_autosave_frequency);

if (is_christmas()) {
writer.write("christmas", christmas_mode);
}
Expand Down
2 changes: 2 additions & 0 deletions src/supertux/gameconfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ class Config final
bool discord_hide_editor;
#endif

int editor_autosave_frequency;

std::string repository_url;

bool is_christmas() const {
Expand Down
6 changes: 5 additions & 1 deletion src/supertux/level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <physfs.h>
#include <numeric>

#include <boost/algorithm/string/predicate.hpp>

Level* Level::s_current = nullptr;

Level::Level(bool worldmap) :
Expand Down Expand Up @@ -89,7 +91,9 @@ Level::save(const std::string& filepath, bool retry)

Writer writer(filepath);
save(writer);
log_warning << "Level saved as " << filepath << "." << std::endl;
log_warning << "Level saved as " << filepath << "."
<< (boost::algorithm::ends_with(filepath, "~") ? " [Autosave]" : "")
<< std::endl;
} catch(std::exception& e) {
if (retry) {
std::stringstream msg;
Expand Down
32 changes: 30 additions & 2 deletions src/supertux/menu/editor_level_select_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,44 @@ EditorLevelSelectMenu::create_item(bool worldmap)
}
}

void
EditorLevelSelectMenu::open_level(const std::string& filename)
{
auto editor = Editor::current();
editor->set_level(filename);
MenuManager::instance().clear_menu_stack();
}

void
EditorLevelSelectMenu::menu_action(MenuItem& item)
{
auto editor = Editor::current();
World* world = editor->get_world();
if (item.get_id() >= 0)
{
editor->set_level(m_levelset->get_level_filename(item.get_id()));

MenuManager::instance().clear_menu_stack();
std::string file_name = m_levelset->get_level_filename(item.get_id());
std::string file_name_full = FileSystem::join(editor->get_level_directory(), file_name);

if (PHYSFS_exists((file_name_full + "~").c_str())) {
auto dialog = std::make_unique<Dialog>(/* passive = */ false, /* auto_clear_dialogs = */ false);
dialog->set_text(_("An auto-save recovery file was found. Would you like to restore the recovery\nfile and resume where you were before the editor crashed?"));
dialog->clear_buttons();
dialog->add_default_button(_("Yes"), [this, file_name] {
open_level(file_name + "~");
MenuManager::instance().set_dialog({});
});
dialog->add_button(_("No"), [this, file_name] {
Dialog::show_confirmation(_("This will delete the auto-save file. Are you sure?"), [this, file_name] {
open_level(file_name);
});
});
dialog->add_cancel_button(_("Cancel"));
MenuManager::instance().set_dialog(std::move(dialog));
} else {
open_level(file_name);
}

} else {
switch (item.get_id()) {
case -1:
Expand Down
2 changes: 2 additions & 0 deletions src/supertux/menu/editor_level_select_menu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class EditorLevelSelectMenu final : public Menu

void menu_action(MenuItem& item) override;

void open_level(const std::string& filename);

private:
void initialize();
void create_level();
Expand Down
2 changes: 2 additions & 0 deletions src/supertux/menu/editor_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "gui/menu_manager.hpp"
#include "supertux/level.hpp"
#include "supertux/gameconfig.hpp"
#include "supertux/globals.hpp"
#include "supertux/menu/menu_storage.hpp"
#include "util/gettext.hpp"
#include "video/compositor.hpp"
Expand Down Expand Up @@ -68,6 +69,7 @@ EditorMenu::EditorMenu()
add_toggle(-1, _("Render Light"), &Compositor::s_render_lighting);
add_toggle(-1, _("Autotile Mode"), &EditorOverlayWidget::autotile_mode);
add_toggle(-1, _("Enable Autotile Help"), &EditorOverlayWidget::autotile_help);
add_intfield(_("Autosave Frequency"), &(g_config->editor_autosave_frequency));

add_submenu(worldmap ? _("Worldmap Settings") : _("Level Settings"),
MenuStorage::EDITOR_LEVEL_MENU);
Expand Down