Merge lp:~widelands-dev/widelands/bug-1753230-working-with-tempfiles into lp:widelands
- bug-1753230-working-with-tempfiles
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 8917 | ||||
Proposed branch: | lp:~widelands-dev/widelands/bug-1753230-working-with-tempfiles | ||||
Merge into: | lp:widelands | ||||
Diff against target: |
365 lines (+164/-54) 8 files modified
src/editor/editorinteractive.cc (+1/-0) src/editor/ui_menus/main_menu_save_map.cc (+34/-52) src/io/filesystem/zip_filesystem.cc (+3/-0) src/logic/editor_game_base.cc (+92/-2) src/logic/editor_game_base.h (+9/-0) src/logic/filesystem_constants.h (+7/-0) src/wlapplication.cc (+16/-0) src/wlapplication.h (+2/-0) |
||||
To merge this branch: | bzr merge lp:~widelands-dev/widelands/bug-1753230-working-with-tempfiles | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email: mp+357656@code.launchpad.net |
Commit message
Fix bugs with missing files/folders in savegames
- Whenever a map is fully loaded (editor/
immediately saved to a temp file and the map's filesystem is
reassigned to that temp file in a special directory.
So, when map/save files are handled anywhere, there won't be any
filesystem conflicts with or even accidentally deletion of the map
filesystem (which would lead to corrupt save files later).
- Temp files are automatically deleted when not needed any more.
- Upon application start, any found temp files that are older than a week
are considered accidental leftovers (e.g. from crashes) and deleted.
Description of the change
GunChleoc (gunchleoc) wrote : | # |
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4161. State: errored. Details: https:/
Appveyor build 3959. State: success. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
I have tested this by allowing only 1 autosave file and sitting on wl_autosave_00.wgf with 7zip on Windows. Since wl_autosave_01.wgf - wl_autosave_05.wgf already existed, the autosaves that I got were wl_autosave_06.wgf - wl_autosave_09.wgf
I think this branch is good to go in after the other nits have been fixed, because it already improves the situation greatly, but maybe we could have some form of "is_writable" test rather than "file_exists"?
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4183. State: failed. Details: https:/
Appveyor build 3981. State: success. Details: https:/
Arty (artydent) wrote : | # |
1. Regarding the autosave stuff:
This wasn't anything this branch tried to solve. It only solved the the conflicts that arose from trying to modify files where the map fs pointed to, resulting in either some crash or error (if the file was currently locked) or resulting in corrupt savefiles.
Those other file conflicts (like trying to modify a savefile that is locked because you have it open with 7zip, or having a file write-protected or some such) are solved with the latest branch: https:/
As for trying to specifically test "is_writable" instead of "file_exists" (or other potentially problematic file states) beforehand, I don't think it's worth it. For one thing, we might have to deal with the specifics of the various access control models on the different platforms. (Just looking at this stuff makes me nauseaous.) There is also the more general problem with any filesystem stuff that you can never be sure that the file state you just checked is still the same when you try your file operation next. Sure, it's very likely, but there can always be race conditions, and some other process might just have snatched the file away between your state check and your attempt to operate on a file.
So aside from a few minor exist checks I favour a simple, practical approach: just try the file operation, but catch and handle any errors. The error codes give a hint about what the actual problem was, but usually that doesn't matter much anyway: either it worked and we continue or it didn't and we abort.
Anyway, the latest branch https:/
2. Those travis build errors
I hadn't actually seen this before. (I'm still a noob on Launchpad and don't know my way around very well, but I am trying to catch up. If there are any important features I might not but should know about, feel free to point something out.)
Anyway, those errors seem like minor nitpicks. I'll fix them.
Btw, are those builds made automatically here for any new branches? Or do I have to initiate them manually? (I haven't checked out the online building possibilities yet. Still on my todo list.)
Arty (artydent) wrote : | # |
Btw, this autosave-thing you observed was a (I think even relatively recently introduced and somewhat dirty) error handling attempt by someone. When the wl_autosave_00.wgf couldn't be renamed (for backup purposes) then there was some "let's just try other filenames" approach, and the game would just find the next autosave filename (up to 09) where there didn't exist any file. At 09 it would just stop and not check any further, but use this as the target name, so from this point on wl_autosave_09.wgf would just have been overwritten with every autosave. (Which worked in your case, because this one wasn't file-locked.)
My latest branch is stricter in this regard. If some relevant renaming or deletion fails, then the autosaving is simply aborted, and there is a little message. I don't think the game needs to handle this in some special way. The player gets a message and can try to fix a problem (if there's a file lock for example) or has to save manually.
GunChleoc (gunchleoc) wrote : | # |
The automatic builds are triggered by every push to a branch that has an active merge request.
For our testing environment, including our codecheck, see:
https:/
If you scroll up to my first comment, you'll see a green "Show diff comments" link. Click on it and you'll see my comments in the diff. For longer diffs, I use the browser's search function and search for the reviewer's username to skip through the comments.
I agree with your reasoning about an "is writable" check, let's keep this branch as is. I'd like my diff comments addressed though.
GunChleoc (gunchleoc) wrote : | # |
You have introduces a "rnrnrn " string in your last commit in a license header ;)
Arty (artydent) wrote : | # |
No, I didn't. :-)
The string is already in the last trunk. I just noticed it and removed it together with the other small fix I commited. (It just didn't feel important enough to mention it in the log.)
GunChleoc (gunchleoc) wrote : | # |
You're right, I read the diff the wrong way around. Thanks for fixing this too :)
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4188. State: passed. Details: https:/
Appveyor build 3985. State: success. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
I have now fixed the nits myself, so that we can start reviewing your other branches
@bunnybot merge
Arty (artydent) wrote : | # |
Huh? I had already fixed them. Did I miss something?
Arty (artydent) wrote : | # |
Oh, I see you had some other nits that you fixed. Fair enough. I should have checked more thoroughly. Or did you mention some specifics before somewhere and I had missed them?
As for the naming of the temp dir, I also had it named "temp" first but felt that might encourage players to just delete it (possibly while the game is up, thus messing up their next save). I guess I was overreacting because a friend of mine notoriously does this kind of thing, always complaining that stupid programmers never clean up their temp stuff. But honestly, "temp" is perfectly fine.
Arty (artydent) wrote : | # |
Oh, now I see where your original comments are. Don't know why I didn't notice them before. Sorry about that.
GunChleoc (gunchleoc) wrote : | # |
Don't worry about it - I know you're still finding your way around Launchpad and I'm happy to help.
If somebody decides to mess with the temp while playing, on their own head be it. At least everybody else will know it's temp files if they want to manually clean it out at a sensible point in time ;)
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4203. State: errored. Details: https:/
Appveyor build 3999. State: failed. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.
Travis build 4203. State: errored. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
Travis error is a timeout
@bunnybot merge force
Preview Diff
1 | === modified file 'src/editor/editorinteractive.cc' | |||
2 | --- src/editor/editorinteractive.cc 2018-09-25 06:32:35 +0000 | |||
3 | +++ src/editor/editorinteractive.cc 2018-11-09 08:01:52 +0000 | |||
4 | @@ -187,6 +187,7 @@ | |||
5 | 187 | } | 187 | } |
6 | 188 | 188 | ||
7 | 189 | ml->load_map_complete(egbase(), Widelands::MapLoader::LoadType::kEditor); | 189 | ml->load_map_complete(egbase(), Widelands::MapLoader::LoadType::kEditor); |
8 | 190 | egbase().postload(); | ||
9 | 190 | egbase().load_graphics(loader_ui); | 191 | egbase().load_graphics(loader_ui); |
10 | 191 | map_changed(MapWas::kReplaced); | 192 | map_changed(MapWas::kReplaced); |
11 | 192 | } | 193 | } |
12 | 193 | 194 | ||
13 | === modified file 'src/editor/ui_menus/main_menu_save_map.cc' | |||
14 | --- src/editor/ui_menus/main_menu_save_map.cc 2018-06-01 08:50:29 +0000 | |||
15 | +++ src/editor/ui_menus/main_menu_save_map.cc 2018-11-09 08:01:52 +0000 | |||
16 | @@ -273,15 +273,14 @@ | |||
17 | 273 | return false; | 273 | return false; |
18 | 274 | } | 274 | } |
19 | 275 | 275 | ||
24 | 276 | // save to a tmp file/dir first, rename later | 276 | // Try deleting file (if it exists). If it fails, give a message and let the player choose a new name. |
25 | 277 | // (important to keep script files in the script directory) | 277 | try { |
26 | 278 | const std::string tmp_name = complete_filename + ".tmp"; | 278 | g_fs->fs_unlink(complete_filename); |
27 | 279 | if (g_fs->file_exists(tmp_name)) { | 279 | } catch (const std::exception& e) { |
28 | 280 | log("Unable to delete old map file %s while saving map: %s\n", complete_filename.c_str(), e.what()); | ||
29 | 280 | const std::string s = | 281 | const std::string s = |
34 | 281 | (boost::format( | 282 | (boost::format(_("File ‘%’ could not be deleted.")) % FileSystem::fs_filename(filename.c_str())).str() |
35 | 282 | _("A file with the name ‘%s.tmp’ already exists. You have to remove it manually.")) % | 283 | + " " + _("Try saving under a different name!"); |
32 | 283 | FileSystem::fs_filename(filename.c_str())) | ||
33 | 284 | .str(); | ||
36 | 285 | UI::WLMessageBox mbox(&eia(), _("Error Saving Map!"), s, UI::WLMessageBox::MBoxType::kOk); | 284 | UI::WLMessageBox mbox(&eia(), _("Error Saving Map!"), s, UI::WLMessageBox::MBoxType::kOk); |
37 | 286 | mbox.run<UI::Panel::Returncodes>(); | 285 | mbox.run<UI::Panel::Returncodes>(); |
38 | 287 | return false; | 286 | return false; |
39 | @@ -290,51 +289,34 @@ | |||
40 | 290 | Widelands::EditorGameBase& egbase = eia().egbase(); | 289 | Widelands::EditorGameBase& egbase = eia().egbase(); |
41 | 291 | Widelands::Map* map = egbase.mutable_map(); | 290 | Widelands::Map* map = egbase.mutable_map(); |
42 | 292 | 291 | ||
44 | 293 | { // fs scope | 292 | // Recompute seafaring tag |
45 | 293 | map->cleanup_port_spaces(egbase.world()); | ||
46 | 294 | if (map->allows_seafaring()) { | ||
47 | 295 | map->add_tag("seafaring"); | ||
48 | 296 | } else { | ||
49 | 297 | map->delete_tag("seafaring"); | ||
50 | 298 | } | ||
51 | 299 | |||
52 | 300 | if (map->has_artifacts()) { | ||
53 | 301 | map->add_tag("artifacts"); | ||
54 | 302 | } else { | ||
55 | 303 | map->delete_tag("artifacts"); | ||
56 | 304 | } | ||
57 | 305 | |||
58 | 306 | // Try saving. | ||
59 | 307 | try { | ||
60 | 294 | std::unique_ptr<FileSystem> fs( | 308 | std::unique_ptr<FileSystem> fs( |
104 | 295 | g_fs->create_sub_file_system(tmp_name, binary ? FileSystem::ZIP : FileSystem::DIR)); | 309 | g_fs->create_sub_file_system(complete_filename, binary ? FileSystem::ZIP : FileSystem::DIR)); |
105 | 296 | 310 | std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*fs, egbase)); | |
106 | 297 | // Recompute seafaring tag | 311 | wms->save(); |
107 | 298 | map->cleanup_port_spaces(egbase.world()); | 312 | fs.reset(); |
108 | 299 | if (map->allows_seafaring()) { | 313 | } catch (const std::exception& e) { |
109 | 300 | map->add_tag("seafaring"); | 314 | std::string s = _("Error Saving Map!\nSaved map file may be corrupt!\n\nReason " |
110 | 301 | } else { | 315 | "given:\n"); |
111 | 302 | map->delete_tag("seafaring"); | 316 | s += e.what(); |
112 | 303 | } | 317 | UI::WLMessageBox mbox(&eia(), _("Error Saving Map!"), s, UI::WLMessageBox::MBoxType::kOk); |
113 | 304 | 318 | mbox.run<UI::Panel::Returncodes>(); | |
114 | 305 | if (map->has_artifacts()) { | 319 | } |
72 | 306 | map->add_tag("artifacts"); | ||
73 | 307 | } else { | ||
74 | 308 | map->delete_tag("artifacts"); | ||
75 | 309 | } | ||
76 | 310 | |||
77 | 311 | try { | ||
78 | 312 | Widelands::MapSaver* wms = new Widelands::MapSaver(*fs, egbase); | ||
79 | 313 | wms->save(); | ||
80 | 314 | delete wms; | ||
81 | 315 | // Reset filesystem to avoid file locks on saves | ||
82 | 316 | fs.reset(); | ||
83 | 317 | map->reset_filesystem(); | ||
84 | 318 | eia().set_need_save(false); | ||
85 | 319 | g_fs->fs_unlink(complete_filename); | ||
86 | 320 | g_fs->fs_rename(tmp_name, complete_filename); | ||
87 | 321 | // Also change fs, as we assign it to the map below | ||
88 | 322 | fs.reset(g_fs->make_sub_file_system(complete_filename)); | ||
89 | 323 | // Set the filesystem of the map to the current save file / directory | ||
90 | 324 | map->swap_filesystem(fs); | ||
91 | 325 | // DONT use fs as of here, its garbage now! | ||
92 | 326 | |||
93 | 327 | } catch (const std::exception& e) { | ||
94 | 328 | std::string s = _("Error Saving Map!\nSaved map file may be corrupt!\n\nReason " | ||
95 | 329 | "given:\n"); | ||
96 | 330 | s += e.what(); | ||
97 | 331 | UI::WLMessageBox mbox(&eia(), _("Error Saving Map!"), s, UI::WLMessageBox::MBoxType::kOk); | ||
98 | 332 | mbox.run<UI::Panel::Returncodes>(); | ||
99 | 333 | |||
100 | 334 | // cleanup tmp file if it was created | ||
101 | 335 | g_fs->fs_unlink(tmp_name); | ||
102 | 336 | } | ||
103 | 337 | } // end fs scope, dont use it | ||
115 | 338 | 320 | ||
116 | 339 | die(); | 321 | die(); |
117 | 340 | return true; | 322 | return true; |
118 | 341 | 323 | ||
119 | === modified file 'src/io/filesystem/zip_filesystem.cc' | |||
120 | --- src/io/filesystem/zip_filesystem.cc 2018-04-07 16:59:00 +0000 | |||
121 | +++ src/io/filesystem/zip_filesystem.cc 2018-11-09 08:01:52 +0000 | |||
122 | @@ -262,6 +262,9 @@ | |||
123 | 262 | * Create a sub filesystem out of this filesystem | 262 | * Create a sub filesystem out of this filesystem |
124 | 263 | */ | 263 | */ |
125 | 264 | FileSystem* ZipFilesystem::make_sub_file_system(const std::string& path) { | 264 | FileSystem* ZipFilesystem::make_sub_file_system(const std::string& path) { |
126 | 265 | if (path == ".") { | ||
127 | 266 | return new ZipFilesystem(zip_file_, basedir_in_zip_file_); | ||
128 | 267 | } | ||
129 | 265 | if (!file_exists(path)) { | 268 | if (!file_exists(path)) { |
130 | 266 | throw wexception( | 269 | throw wexception( |
131 | 267 | "ZipFilesystem::make_sub_file_system: The path '%s' does not exist in zip file '%s'.", | 270 | "ZipFilesystem::make_sub_file_system: The path '%s' does not exist in zip file '%s'.", |
132 | 268 | 271 | ||
133 | === modified file 'src/logic/editor_game_base.cc' | |||
134 | --- src/logic/editor_game_base.cc 2018-09-28 05:41:33 +0000 | |||
135 | +++ src/logic/editor_game_base.cc 2018-11-09 08:01:52 +0000 | |||
136 | @@ -8,7 +8,7 @@ | |||
137 | 8 | * | 8 | * |
138 | 9 | * This program is distributed in the hope that it will be useful, | 9 | * This program is distributed in the hope that it will be useful, |
139 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
141 | 11 | rnrnrn * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
142 | 12 | * GNU General Public License for more details. | 12 | * GNU General Public License for more details. |
143 | 13 | * | 13 | * |
144 | 14 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License |
145 | @@ -26,10 +26,12 @@ | |||
146 | 26 | #include "base/i18n.h" | 26 | #include "base/i18n.h" |
147 | 27 | #include "base/macros.h" | 27 | #include "base/macros.h" |
148 | 28 | #include "base/scoped_timer.h" | 28 | #include "base/scoped_timer.h" |
149 | 29 | #include "base/time_string.h" | ||
150 | 29 | #include "base/wexception.h" | 30 | #include "base/wexception.h" |
151 | 30 | #include "economy/flag.h" | 31 | #include "economy/flag.h" |
152 | 31 | #include "economy/road.h" | 32 | #include "economy/road.h" |
153 | 32 | #include "graphic/color.h" | 33 | #include "graphic/color.h" |
154 | 34 | #include "logic/filesystem_constants.h" | ||
155 | 33 | #include "logic/findimmovable.h" | 35 | #include "logic/findimmovable.h" |
156 | 34 | #include "logic/game.h" | 36 | #include "logic/game.h" |
157 | 35 | #include "logic/game_data_error.h" | 37 | #include "logic/game_data_error.h" |
158 | @@ -48,6 +50,7 @@ | |||
159 | 48 | #include "logic/player.h" | 50 | #include "logic/player.h" |
160 | 49 | #include "logic/playersmanager.h" | 51 | #include "logic/playersmanager.h" |
161 | 50 | #include "logic/roadtype.h" | 52 | #include "logic/roadtype.h" |
162 | 53 | #include "map_io/map_saver.h" | ||
163 | 51 | #include "scripting/logic.h" | 54 | #include "scripting/logic.h" |
164 | 52 | #include "scripting/lua_table.h" | 55 | #include "scripting/lua_table.h" |
165 | 53 | #include "ui_basic/progresswindow.h" | 56 | #include "ui_basic/progresswindow.h" |
166 | @@ -67,12 +70,87 @@ | |||
167 | 67 | : gametime_(0), | 70 | : gametime_(0), |
168 | 68 | lua_(lua_interface), | 71 | lua_(lua_interface), |
169 | 69 | player_manager_(new PlayersManager(*this)), | 72 | player_manager_(new PlayersManager(*this)), |
171 | 70 | ibase_(nullptr) { | 73 | ibase_(nullptr), |
172 | 74 | tmp_fs_(nullptr) { | ||
173 | 71 | if (!lua_) // TODO(SirVer): this is sooo ugly, I can't say | 75 | if (!lua_) // TODO(SirVer): this is sooo ugly, I can't say |
174 | 72 | lua_.reset(new LuaEditorInterface(this)); | 76 | lua_.reset(new LuaEditorInterface(this)); |
175 | 73 | } | 77 | } |
176 | 74 | 78 | ||
177 | 75 | EditorGameBase::~EditorGameBase() { | 79 | EditorGameBase::~EditorGameBase() { |
178 | 80 | delete_tempfile(); | ||
179 | 81 | } | ||
180 | 82 | |||
181 | 83 | /** | ||
182 | 84 | * deletes the temporary file/dir | ||
183 | 85 | * also resets the map filesystem if it points to the temporary file | ||
184 | 86 | */ | ||
185 | 87 | void EditorGameBase::delete_tempfile() { | ||
186 | 88 | if (!tmp_fs_) { | ||
187 | 89 | return; | ||
188 | 90 | } | ||
189 | 91 | |||
190 | 92 | std::string fs_filename = tmp_fs_->get_basename(); | ||
191 | 93 | std::string mapfs_filename = map_.filesystem()->get_basename(); | ||
192 | 94 | if (mapfs_filename == fs_filename) | ||
193 | 95 | map_.reset_filesystem(); | ||
194 | 96 | tmp_fs_.reset(); | ||
195 | 97 | try { | ||
196 | 98 | g_fs->fs_unlink(fs_filename); | ||
197 | 99 | } catch (const std::exception& e) { | ||
198 | 100 | // if file deletion fails then we have an abandoned file lying around, but otherwise that's unproblematic | ||
199 | 101 | log("EditorGameBase::delete_tempfile: deleting temporary file/dir failed: %s\n", e.what()); | ||
200 | 102 | } | ||
201 | 103 | } | ||
202 | 104 | |||
203 | 105 | /** | ||
204 | 106 | * creates a new file/dir, saves the map data, and reassigns the map filesystem | ||
205 | 107 | * does not delete the former temp file if one exists | ||
206 | 108 | * throws an exception if something goes wrong | ||
207 | 109 | */ | ||
208 | 110 | void EditorGameBase::create_tempfile_and_save_mapdata(FileSystem::Type const type) { | ||
209 | 111 | // should only be called when a map was already loaded | ||
210 | 112 | assert(map_.filesystem()); | ||
211 | 113 | |||
212 | 114 | g_fs->ensure_directory_exists(kTempFileDir); | ||
213 | 115 | |||
214 | 116 | std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata"; | ||
215 | 117 | std::string complete_filename = filename + kTempFileExtension; | ||
216 | 118 | |||
217 | 119 | // if a file with that name already exists, then try a few name modifications | ||
218 | 120 | if (g_fs->file_exists(complete_filename)) | ||
219 | 121 | { | ||
220 | 122 | int suffix; | ||
221 | 123 | for (suffix = 0; suffix <= 9; suffix++) | ||
222 | 124 | { | ||
223 | 125 | complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension; | ||
224 | 126 | if (!g_fs->file_exists(complete_filename)) | ||
225 | 127 | break; | ||
226 | 128 | } | ||
227 | 129 | if (suffix > 9) { | ||
228 | 130 | throw wexception("EditorGameBase::create_tempfile_and_save_mapdata(): for all considered filenames a file already existed"); | ||
229 | 131 | } | ||
230 | 132 | } | ||
231 | 133 | |||
232 | 134 | // create tmp_fs_ | ||
233 | 135 | tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type)); | ||
234 | 136 | |||
235 | 137 | // save necessary map data (we actually save the whole map) | ||
236 | 138 | std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this)); | ||
237 | 139 | wms->save(); | ||
238 | 140 | |||
239 | 141 | // swap map fs | ||
240 | 142 | std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system(".")); | ||
241 | 143 | map_.swap_filesystem(mapfs); | ||
242 | 144 | mapfs.reset(); | ||
243 | 145 | |||
244 | 146 | // This is just a convenience hack: | ||
245 | 147 | // If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently implemented - | ||
246 | 148 | // the file is still in zip mode right now, which means that the file isn't finalized yet, i.e., | ||
247 | 149 | // not even a valid zip file until zip mode ends. To force ending the zip mode (thus finalizing the file) | ||
248 | 150 | // we simply perform a (otherwise useless) filesystem request. | ||
249 | 151 | // It's not strictly necessary, but this way we get a valid zip file immediately istead of | ||
250 | 152 | // at some unkown later point (when an unzip operation happens or a filesystem object destructs). | ||
251 | 153 | tmp_fs_->file_exists("binary"); | ||
252 | 76 | } | 154 | } |
253 | 77 | 155 | ||
254 | 78 | void EditorGameBase::think() { | 156 | void EditorGameBase::think() { |
255 | @@ -196,6 +274,16 @@ | |||
256 | 196 | * graphics are loaded. | 274 | * graphics are loaded. |
257 | 197 | */ | 275 | */ |
258 | 198 | void EditorGameBase::postload() { | 276 | void EditorGameBase::postload() { |
259 | 277 | if (map_.filesystem()) { | ||
260 | 278 | // save map data to temporary file and reassign map fs | ||
261 | 279 | try { | ||
262 | 280 | create_tempfile_and_save_mapdata(FileSystem::ZIP); | ||
263 | 281 | } catch (const WException& e) { | ||
264 | 282 | log("EditorGameBase::postload: saving map to temporary file failed: %s", e.what()); | ||
265 | 283 | throw; | ||
266 | 284 | } | ||
267 | 285 | } | ||
268 | 286 | |||
269 | 199 | // Postload tribes | 287 | // Postload tribes |
270 | 200 | assert(tribes_); | 288 | assert(tribes_); |
271 | 201 | tribes_->postload(); | 289 | tribes_->postload(); |
272 | @@ -411,6 +499,8 @@ | |||
273 | 411 | player_manager_->cleanup(); | 499 | player_manager_->cleanup(); |
274 | 412 | 500 | ||
275 | 413 | map_.cleanup(); | 501 | map_.cleanup(); |
276 | 502 | |||
277 | 503 | delete_tempfile(); | ||
278 | 414 | } | 504 | } |
279 | 415 | 505 | ||
280 | 416 | void EditorGameBase::set_road(const FCoords& f, uint8_t const direction, uint8_t const roadtype) { | 506 | void EditorGameBase::set_road(const FCoords& f, uint8_t const direction, uint8_t const roadtype) { |
281 | 417 | 507 | ||
282 | === modified file 'src/logic/editor_game_base.h' | |||
283 | --- src/logic/editor_game_base.h 2018-04-07 16:59:00 +0000 | |||
284 | +++ src/logic/editor_game_base.h 2018-11-09 08:01:52 +0000 | |||
285 | @@ -255,6 +255,15 @@ | |||
286 | 255 | std::unique_ptr<InteractiveBase> ibase_; | 255 | std::unique_ptr<InteractiveBase> ibase_; |
287 | 256 | Map map_; | 256 | Map map_; |
288 | 257 | 257 | ||
289 | 258 | /// Even after a map is fully loaded, some static data (images, scripts) | ||
290 | 259 | /// will still be read from a filesystem whenever a map/game is saved. | ||
291 | 260 | /// To avoid potential filesystem conflicts when (pre)loading/saving/deleting | ||
292 | 261 | /// map/game files (and to avoid having to deal with this in many different places) | ||
293 | 262 | /// a temporary file (in a special dir) is created for such data. | ||
294 | 263 | std::unique_ptr<FileSystem> tmp_fs_; | ||
295 | 264 | void delete_tempfile(); | ||
296 | 265 | void create_tempfile_and_save_mapdata(FileSystem::Type type); | ||
297 | 266 | |||
298 | 258 | DISALLOW_COPY_AND_ASSIGN(EditorGameBase); | 267 | DISALLOW_COPY_AND_ASSIGN(EditorGameBase); |
299 | 259 | }; | 268 | }; |
300 | 260 | 269 | ||
301 | 261 | 270 | ||
302 | === modified file 'src/logic/filesystem_constants.h' | |||
303 | --- src/logic/filesystem_constants.h 2018-04-22 16:01:32 +0000 | |||
304 | +++ src/logic/filesystem_constants.h 2018-11-09 08:01:52 +0000 | |||
305 | @@ -38,6 +38,13 @@ | |||
306 | 38 | const std::string kS2MapExtension1 = ".swd"; | 38 | const std::string kS2MapExtension1 = ".swd"; |
307 | 39 | const std::string kS2MapExtension2 = ".wld"; | 39 | const std::string kS2MapExtension2 = ".wld"; |
308 | 40 | 40 | ||
309 | 41 | /// Filesystem names for temp files holding static data that needs to be accessible via filesystem | ||
310 | 42 | /// Kept in a separate dir to avoid filesystem conflicts | ||
311 | 43 | const std::string kTempFileDir = "temp"; | ||
312 | 44 | const std::string kTempFileExtension = ".tmp"; | ||
313 | 45 | // We delete (accidentally remaining) temp files older than a week | ||
314 | 46 | constexpr double kTempFilesKeepAroundTime = 7 * 24 * 60 * 60; | ||
315 | 47 | |||
316 | 41 | /// Filesystem names and timeouts for replays | 48 | /// Filesystem names and timeouts for replays |
317 | 42 | const std::string kReplayDir = "replays"; | 49 | const std::string kReplayDir = "replays"; |
318 | 43 | const std::string kReplayExtension = ".wrpl"; | 50 | const std::string kReplayExtension = ".wrpl"; |
319 | 44 | 51 | ||
320 | === modified file 'src/wlapplication.cc' | |||
321 | --- src/wlapplication.cc 2018-10-09 17:09:34 +0000 | |||
322 | +++ src/wlapplication.cc 2018-11-09 08:01:52 +0000 | |||
323 | @@ -339,6 +339,7 @@ | |||
324 | 339 | changedir_on_mac(); | 339 | changedir_on_mac(); |
325 | 340 | cleanup_replays(); | 340 | cleanup_replays(); |
326 | 341 | cleanup_ai_files(); | 341 | cleanup_ai_files(); |
327 | 342 | cleanup_temp_files(); | ||
328 | 342 | 343 | ||
329 | 343 | Section& config = g_options.pull_section("global"); | 344 | Section& config = g_options.pull_section("global"); |
330 | 344 | 345 | ||
331 | @@ -1498,6 +1499,21 @@ | |||
332 | 1498 | } | 1499 | } |
333 | 1499 | } | 1500 | } |
334 | 1500 | 1501 | ||
335 | 1502 | /** | ||
336 | 1503 | * Delete old temp files that might still lurk around (game crashes etc.) | ||
337 | 1504 | */ | ||
338 | 1505 | void WLApplication::cleanup_temp_files() { | ||
339 | 1506 | for (const std::string& filename : | ||
340 | 1507 | filter(g_fs->list_directory(kTempFileDir), [](const std::string& fn) { | ||
341 | 1508 | return boost::ends_with(fn, kTempFileExtension); | ||
342 | 1509 | })) { | ||
343 | 1510 | if (is_autogenerated_and_expired(filename, kTempFilesKeepAroundTime)) { | ||
344 | 1511 | log("Deleting old temp file: %s\n", filename.c_str()); | ||
345 | 1512 | g_fs->fs_unlink(filename); | ||
346 | 1513 | } | ||
347 | 1514 | } | ||
348 | 1515 | } | ||
349 | 1516 | |||
350 | 1501 | bool WLApplication::redirect_output(std::string path) { | 1517 | bool WLApplication::redirect_output(std::string path) { |
351 | 1502 | if (path.empty()) { | 1518 | if (path.empty()) { |
352 | 1503 | #ifdef _WIN32 | 1519 | #ifdef _WIN32 |
353 | 1504 | 1520 | ||
354 | === modified file 'src/wlapplication.h' | |||
355 | --- src/wlapplication.h 2018-04-07 16:59:00 +0000 | |||
356 | +++ src/wlapplication.h 2018-11-09 08:01:52 +0000 | |||
357 | @@ -216,6 +216,8 @@ | |||
358 | 216 | 216 | ||
359 | 217 | void cleanup_ai_files(); | 217 | void cleanup_ai_files(); |
360 | 218 | 218 | ||
361 | 219 | void cleanup_temp_files(); | ||
362 | 220 | |||
363 | 219 | bool redirect_output(std::string path = ""); | 221 | bool redirect_output(std::string path = ""); |
364 | 220 | 222 | ||
365 | 221 | // Handle the given pressed key. Returns true when key was | 223 | // Handle the given pressed key. Returns true when key was |
Code LGTM - just some minor nits.