Merge lp:~widelands-dev/widelands/autosave_on_objectives into lp:widelands

Proposed by cghislai
Status: Merged
Merged at revision: 6639
Proposed branch: lp:~widelands-dev/widelands/autosave_on_objectives
Merge into: lp:widelands
Diff against target: 902 lines (+263/-216)
8 files modified
campaigns/tutorial01.wmf/scripting/init.lua (+48/-48)
src/logic/game.h (+0/-4)
src/save_handler.cc (+33/-17)
src/save_handler.h (+12/-4)
src/scripting/lua_game.cc (+19/-4)
src/scripting/lua_root.cc (+27/-27)
src/scripting/lua_root.h (+2/-0)
test/lua/persistence.wmf/scripting/init.lua (+122/-112)
To merge this branch: bzr merge lp:~widelands-dev/widelands/autosave_on_objectives
Reviewer Review Type Date Requested Status
SirVer Approve
Review via email: mp+174546@code.launchpad.net

Description of the change

I just applied the patch provided in the bug report and removed the option checkbox.

Concerning the new lua function, i added it to the const static struct luaL_reg wlgame [] and was able to use it in a newly created win condition, so i guess this was ok?

To post a comment you must log in.
Revision history for this message
SirVer (sirver) wrote :

I pushed my review of this branch in r6613. You can find all my comments by grep-ing for #cghislai. I changed some nits around (see the diff) and added a short comment when I found it appropriate - feel free to just delete them again, they are for your eyes only :). I did not touch anything that would affect the logic because you are more suited to do this as you know your code better.

I think the code looks very reasonable already. The biggest issues is that we now have wl.Game():save and wl.game.save_game - the first one is only used in the persistence.wmf test (see tests/lua/persistence.wmf/scripting/init.lua). I think you should move your logic to the method that is already there and make sure that its new features are exercised in the persistence tests - comments are in the source code as well.

review: Needs Fixing
Revision history for this message
cghislai (charlyghislain) wrote :

I'm a bit lost in the lua tests. It appears I cannot make it run even with trunk code :
"could not load "test/lua/persistence.wmf"

Could you check it? I will try to make a save-in-corountine test afterwards.

I refactored m_allow_autosaving to m_allow_saving, because i guess we have to prevent manual saves as well then, right?

I removed some comments i grasped, and left some that may still be useful

Revision history for this message
SirVer (sirver) wrote :

I looked over the code again and removed more of my comments that you adressed or that I made up my mind about :). One more thing: If you change a Lua method make sure that you update where it is used in the code (i.e. in all *.lua files) and add in a backwards compatible version (I did this for this change). The reason for this is that the Lua interface is a kind of public API that people should rely on and that should not change to quickly so that they have time to adapt - there is no proper deprecation warning for this yet, for now I just simply added a comment.

These methods must be around for at least three builds - the reason is that the lua code is saved into savegames, so if the API changes and a player tries to reload a scenario with older code, the API calls must still be around.

About the test running, the comments in the file where slightly outdated. I updated them to this:
$PATH_TO_WIDELANDS_BINARY --datadir="." --nozip --scenario=test/lua/persistence.wmf && $PATH_TO_WIDELANDS_BINARY --datadir="." --loadgame=~/.widelands/save/lua_persistence.wgf

of course a rm -rf ~/.widelands/save/lua_persistence.wgf should be a good idea as well.

Could you try again if this works for you? You have to be in the root directory of the repository for this to work.

Revision history for this message
cghislai (charlyghislain) wrote :

Running the coroutine test I get a PANIC: unprotected error in call to Lua API (Attempt to persist a C function) which I'm unable to track down...
I'm not very familiar with lua or scripting at that low level (yet).

In the meantime, the former persistence test passed, I think i was trying with the system executable.

Revision history for this message
SirVer (sirver) wrote :

This is the error you get when Lua tries to persist a running coroutine. So this is precisely the error you want to fix :).

In trunk, the save routine must only be called when no coroutine is running, i.e. outside of any coroutine, i.e. when the script is run the first time. This is not very flexible and means that you can essentially not save from inside Lua. I did it because I needed a save routine for the persistence test and this did the job.

Your code now should be able to handle this though, because as soon as you hit the main loop and poll if the game should be saved, there cannot be a Lua coroutine running anymore, so saving should work.

Revision history for this message
cghislai (charlyghislain) wrote :

The autosave seems to work as expected in campaign. Also calling wl.Game():save("test") in a win condition script worked. The coroutine_persistence.wmf test passed, but please check that it tests correctly.

Revision history for this message
SirVer (sirver) wrote :

I did slight modifications to the test and added a bunch of comments - I found the reason why the test did work for you and then didn't and did again: lunit did define some handles to cfunctions, so it needs to be pulled in after loading :).

I will merge this as soon as i get good internet again - seems impossible in this train right now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'campaigns/tutorial01.wmf/scripting/init.lua'
--- campaigns/tutorial01.wmf/scripting/init.lua 2010-11-12 12:42:11 +0000
+++ campaigns/tutorial01.wmf/scripting/init.lua 2013-07-19 21:32:25 +0000
@@ -32,7 +32,7 @@
32use("map", "texts")32use("map", "texts")
3333
34-- =================34-- =================
35-- Helper functions 35-- Helper functions
36-- =================36-- =================
3737
38-- A small helper class to disable/enable autosaving and user interaction38-- A small helper class to disable/enable autosaving and user interaction
@@ -49,14 +49,14 @@
49function UserInputDisabler:establish_blocks()49function UserInputDisabler:establish_blocks()
50 self._ui_state = wl.ui.get_user_input_allowed()50 self._ui_state = wl.ui.get_user_input_allowed()
51 local game = wl.Game()51 local game = wl.Game()
52 self._as_state = game.allow_autosaving52 self._as_state = game.allow_saving
5353
54 wl.ui.set_user_input_allowed(false)54 wl.ui.set_user_input_allowed(false)
55 wl.Game().allow_autosaving = false55 wl.Game().allow_saving = false
56end56end
57function UserInputDisabler:lift_blocks()57function UserInputDisabler:lift_blocks()
58 wl.ui.set_user_input_allowed(self._ui_state)58 wl.ui.set_user_input_allowed(self._ui_state)
59 wl.Game().allow_autosaving= self._as_state59 wl.Game().allow_saving = self._as_state
60end60end
6161
62function _try_add_objective(i)62function _try_add_objective(i)
@@ -108,7 +108,7 @@
108 sleep(130)108 sleep(130)
109 return o109 return o
110end110end
111 111
112function click_on_field(f, g_T, g_sleeptime)112function click_on_field(f, g_T, g_sleeptime)
113 local sleeptime = g_sleeptime or 500113 local sleeptime = g_sleeptime or 500
114114
@@ -125,7 +125,7 @@
125125
126function click_on_panel(panel, g_T, g_sleeptime)126function click_on_panel(panel, g_T, g_sleeptime)
127 local sleeptime = g_sleeptime or 500127 local sleeptime = g_sleeptime or 500
128 128
129 local blocker = UserInputDisabler:new()129 local blocker = UserInputDisabler:new()
130130
131 sleep(sleeptime)131 sleep(sleeptime)
@@ -143,7 +143,7 @@
143function warp_houses(descriptions)143function warp_houses(descriptions)
144 local blocker = UserInputDisabler:new()144 local blocker = UserInputDisabler:new()
145145
146 for idx, d in ipairs(descriptions) do 146 for idx, d in ipairs(descriptions) do
147 local name, x, y = d[1], d[2], d[3]147 local name, x, y = d[1], d[2], d[3]
148 mouse_smoothly_to(wl.Game().map:get_field(x, y))148 mouse_smoothly_to(wl.Game().map:get_field(x, y))
149 sleep(300)149 sleep(300)
@@ -167,7 +167,7 @@
167167
168function build_road(field, ...)168function build_road(field, ...)
169 -- Build a road by clicking the UI. A little faster than before169 -- Build a road by clicking the UI. A little faster than before
170 170
171 -- Make sure that there is room for the road: Rip all immovables171 -- Make sure that there is room for the road: Rip all immovables
172 local cf = field172 local cf = field
173 for idx, d in ipairs{...} do173 for idx, d in ipairs{...} do
@@ -206,11 +206,11 @@
206 end206 end
207 end207 end
208end208end
209 209
210function build_eastern_trainings_area(citadel_field)210function build_eastern_trainings_area(citadel_field)
211 -- Build some infrastructure as another example211 -- Build some infrastructure as another example
212 local blocker = UserInputDisabler:new()212 local blocker = UserInputDisabler:new()
213 213
214 plr:reveal_fields(citadel_field:region(8))214 plr:reveal_fields(citadel_field:region(8))
215 scroll_smoothly_to(wl.Game().map:get_field(21,9))215 scroll_smoothly_to(wl.Game().map:get_field(21,9))
216 scroll_smoothly_to(citadel_field)216 scroll_smoothly_to(citadel_field)
@@ -262,16 +262,16 @@
262 -- add buildwares to the warehouse262 -- add buildwares to the warehouse
263 local ts = map:get_field(31,56).immovable263 local ts = map:get_field(31,56).immovable
264 ts:set_wares(ts.valid_wares)264 ts:set_wares(ts.valid_wares)
265 265
266 scroll_smoothly_to(citadel_field)266 scroll_smoothly_to(citadel_field)
267267
268 blocker:lift_blocks()268 blocker:lift_blocks()
269end269end
270 270
271-- Remove all stones in a given environment. This is done271-- Remove all stones in a given environment. This is done
272-- in a loop for a nice optical effect272-- in a loop for a nice optical effect
273function remove_all_stones(fields, g_sleeptime)273function remove_all_stones(fields, g_sleeptime)
274 local sleeptime = g_sleeptime or 150 274 local sleeptime = g_sleeptime or 150
275 local map = wl.Game().map275 local map = wl.Game().map
276 while #fields > 0 do276 while #fields > 0 do
277 local idx = math.random(#fields)277 local idx = math.random(#fields)
@@ -283,7 +283,7 @@
283 if n then283 if n then
284 n = tonumber(n)284 n = tonumber(n)
285 f.immovable:remove()285 f.immovable:remove()
286 if n > 1 then 286 if n > 1 then
287 remove_field = false287 remove_field = false
288 map:place_immovable("stones" .. n-1, f)288 map:place_immovable("stones" .. n-1, f)
289 end289 end
@@ -298,7 +298,7 @@
298end298end
299299
300-- ==============300-- ==============
301-- Sentry Thread 301-- Sentry Thread
302-- ==============302-- ==============
303-- This thread makes sure that the player does not build stuff where he303-- This thread makes sure that the player does not build stuff where he
304-- is not supposed to. He gets a message box when he tries and what he build304-- is not supposed to. He gets a message box when he tries and what he build
@@ -345,7 +345,7 @@
345 sleep(1000)345 sleep(1000)
346 end346 end
347end347end
348 348
349-- Allows constructionsites for the given buildings, all others are invalid349-- Allows constructionsites for the given buildings, all others are invalid
350-- as is any other immovable build by the player350-- as is any other immovable build by the player
351function allow_constructionsite(i, buildings)351function allow_constructionsite(i, buildings)
@@ -366,7 +366,7 @@
366end366end
367367
368-- ================368-- ================
369-- Message threads 369-- Message threads
370-- ================370-- ================
371function starting_infos()371function starting_infos()
372 sleep(100)372 sleep(100)
@@ -418,7 +418,7 @@
418 click_on_field(wl.Game().map.player_slots[1].starting_field.brn)418 click_on_field(wl.Game().map.player_slots[1].starting_field.brn)
419419
420 msg_box(lumberjack_message_04)420 msg_box(lumberjack_message_04)
421 421
422 register_immovable_as_allowed(first_lumberjack_field.immovable) -- hut + flag422 register_immovable_as_allowed(first_lumberjack_field.immovable) -- hut + flag
423423
424 local f = wl.Game().map:get_field(14,11)424 local f = wl.Game().map:get_field(14,11)
@@ -429,25 +429,25 @@
429 blocker:lift_blocks()429 blocker:lift_blocks()
430430
431 sleep(15000)431 sleep(15000)
432 432
433 local o = msg_box(lumberjack_message_05)433 local o = msg_box(lumberjack_message_05)
434434
435 local blocker = UserInputDisabler:new()435 local blocker = UserInputDisabler:new()
436 436
437 local f = wl.Game().map:get_field(14,11)437 local f = wl.Game().map:get_field(14,11)
438 scroll_smoothly_to(f)438 scroll_smoothly_to(f)
439 mouse_smoothly_to(f)439 mouse_smoothly_to(f)
440 440
441 blocker:lift_blocks()441 blocker:lift_blocks()
442 442
443 -- Wait for flag443 -- Wait for flag
444 while not (f.immovable and f.immovable.type == "flag") do sleep(300) end444 while not (f.immovable and f.immovable.type == "flag") do sleep(300) end
445 o.done = true445 o.done = true
446446
447 sleep(300)447 sleep(300)
448 448
449 msg_box(lumberjack_message_06)449 msg_box(lumberjack_message_06)
450 450
451 while #plr:get_buildings("lumberjacks_hut") < 1 do sleep(300) end451 while #plr:get_buildings("lumberjacks_hut") < 1 do sleep(300) end
452452
453 msg_box(lumberjack_message_07)453 msg_box(lumberjack_message_07)
@@ -458,7 +458,7 @@
458function learn_to_move()458function learn_to_move()
459 -- Teaching the user how to scroll on the map459 -- Teaching the user how to scroll on the map
460 local o = msg_box(inform_about_stones)460 local o = msg_box(inform_about_stones)
461 461
462 function _wait_for_move()462 function _wait_for_move()
463 local cx = wl.ui.MapView().viewpoint_x463 local cx = wl.ui.MapView().viewpoint_x
464 local cy = wl.ui.MapView().viewpoint_y464 local cy = wl.ui.MapView().viewpoint_y
@@ -477,12 +477,12 @@
477 _wait_for_move()477 _wait_for_move()
478 o.done = true478 o.done = true
479 sleep(3000) -- Give the player a chance to try this some more479 sleep(3000) -- Give the player a chance to try this some more
480 480
481 msg_box(congratulate_and_on_to_quarry)481 msg_box(congratulate_and_on_to_quarry)
482482
483 build_a_quarry()483 build_a_quarry()
484end484end
485 485
486function build_a_quarry()486function build_a_quarry()
487 sleep(200)487 sleep(200)
488488
@@ -505,12 +505,12 @@
505505
506 local function _rip_road()506 local function _rip_road()
507 for idx,f in ipairs(cs.fields[1].brn:region(2)) do507 for idx,f in ipairs(cs.fields[1].brn:region(2)) do
508 if f.immovable and f.immovable.type == "road" then 508 if f.immovable and f.immovable.type == "road" then
509 click_on_field(f)509 click_on_field(f)
510 click_on_panel(wl.ui.MapView().windows.510 click_on_panel(wl.ui.MapView().windows.
511 field_action.buttons.destroy_road, 300)511 field_action.buttons.destroy_road, 300)
512 sleep(200)512 sleep(200)
513 return 513 return
514 end514 end
515 end515 end
516 end516 end
@@ -527,17 +527,17 @@
527 click_on_field(map:get_field(11,12))527 click_on_field(map:get_field(11,12))
528 click_on_field(map:get_field(12,12))528 click_on_field(map:get_field(12,12))
529 click_on_field(map:get_field(12,11))529 click_on_field(map:get_field(12,11))
530 530
531 sleep(3000)531 sleep(3000)
532532
533 _rip_road()533 _rip_road()
534 534
535 msg_box(talk_about_roadbuilding_01)535 msg_box(talk_about_roadbuilding_01)
536 -- Showoff direct roadbuilding536 -- Showoff direct roadbuilding
537 click_on_field(cs.fields[1].brn)537 click_on_field(cs.fields[1].brn)
538 click_on_panel(wl.ui.MapView().windows.field_action.buttons.build_road, 300)538 click_on_panel(wl.ui.MapView().windows.field_action.buttons.build_road, 300)
539 click_on_field(map.player_slots[1].starting_field.brn)539 click_on_field(map.player_slots[1].starting_field.brn)
540 540
541 sleep(3000)541 sleep(3000)
542542
543 _rip_road()543 _rip_road()
@@ -545,12 +545,12 @@
545 blocker:lift_blocks()545 blocker:lift_blocks()
546546
547 local o = msg_box(talk_about_roadbuilding_02)547 local o = msg_box(talk_about_roadbuilding_02)
548 548
549 -- From now on, the player can build whatever he wants549 -- From now on, the player can build whatever he wants
550 terminate_bad_boy_sentinel = true550 terminate_bad_boy_sentinel = true
551 551
552 -- Wait a while552 -- Wait a while
553 sleep( 100*1000 ) 553 sleep( 100*1000 )
554554
555 -- Interludium: talk about census and statistics555 -- Interludium: talk about census and statistics
556 census_and_statistics(cs.fields[1])556 census_and_statistics(cs.fields[1])
@@ -560,17 +560,17 @@
560560
561 messages()561 messages()
562end562end
563 563
564function census_and_statistics(field)564function census_and_statistics(field)
565 sleep(15000)565 sleep(15000)
566 566
567 local blocker = UserInputDisabler:new()567 local blocker = UserInputDisabler:new()
568568
569 wl.ui.MapView().census = false569 wl.ui.MapView().census = false
570 wl.ui.MapView().statistics = false570 wl.ui.MapView().statistics = false
571 571
572 wl.ui.MapView():abort_road_building()572 wl.ui.MapView():abort_road_building()
573 573
574 msg_box(census_and_statistics_00)574 msg_box(census_and_statistics_00)
575 -- Pick any empty field575 -- Pick any empty field
576 local function _pick_empty_field()576 local function _pick_empty_field()
@@ -591,7 +591,7 @@
591 click_on_panel(wl.ui.MapView().windows.field_action.buttons.statistics)591 click_on_panel(wl.ui.MapView().windows.field_action.buttons.statistics)
592592
593 msg_box(census_and_statistics_01)593 msg_box(census_and_statistics_01)
594 594
595 blocker:lift_blocks()595 blocker:lift_blocks()
596end596end
597597
@@ -611,7 +611,7 @@
611 -- Wait for messages window to close611 -- Wait for messages window to close
612 while wl.ui.MapView().windows.messages do sleep(300) end612 while wl.ui.MapView().windows.messages do sleep(300) end
613 o.done = true613 o.done = true
614 614
615 msg_box(closing_msg_window_01)615 msg_box(closing_msg_window_01)
616616
617 -- Remove all stones617 -- Remove all stones
@@ -627,10 +627,10 @@
627 expansion()627 expansion()
628end628end
629629
630function expansion() 630function expansion()
631 -- Teach about expanding your territory.631 -- Teach about expanding your territory.
632 sleep(10)632 sleep(10)
633 633
634 -- This is not really needed since the stones are already removed, but if634 -- This is not really needed since the stones are already removed, but if
635 -- we're debugging and we start with this function it is most useful to have635 -- we're debugging and we start with this function it is most useful to have
636 -- the stones away already636 -- the stones away already
@@ -655,7 +655,7 @@
655 while #fields > 0 do655 while #fields > 0 do
656 local idx = math.random(#fields)656 local idx = math.random(#fields)
657 local f = fields[idx]657 local f = fields[idx]
658 658
659 if f.terr:match("berg%d+") and f.terd:match("berg%d+") then659 if f.terr:match("berg%d+") and f.terd:match("berg%d+") then
660 if pcall(function() plr:place_flag(f) end) then660 if pcall(function() plr:place_flag(f) end) then
661 f.immovable:remove()661 f.immovable:remove()
@@ -678,7 +678,7 @@
678 end678 end
679679
680 scroll_smoothly_to(conquer_field)680 scroll_smoothly_to(conquer_field)
681 681
682 local dest = _find_good_flag_position()682 local dest = _find_good_flag_position()
683 local start = _find_nearby_flag()683 local start = _find_nearby_flag()
684684
@@ -699,7 +699,7 @@
699 local function _wait_for_some_resi(wanted)699 local function _wait_for_some_resi(wanted)
700 while 1 do700 while 1 do
701 local cnt = 0701 local cnt = 0
702 for idx, f in ipairs(dest:region(6)) do 702 for idx, f in ipairs(dest:region(6)) do
703 if f.immovable and f.immovable.name:sub(1,4) == "resi" then703 if f.immovable and f.immovable.name:sub(1,4) == "resi" then
704 cnt = cnt + 1704 cnt = cnt + 1
705 if cnt >= wanted then return end705 if cnt >= wanted then return end
@@ -720,7 +720,7 @@
720function training()720function training()
721 -- Teach about trainingsites and soldiers721 -- Teach about trainingsites and soldiers
722 sleep(300)722 sleep(300)
723 723
724 msg_box(warfare_and_training_00)724 msg_box(warfare_and_training_00)
725725
726 local citadel_field = wl.Game().map:get_field(31, 63)726 local citadel_field = wl.Game().map:get_field(31, 63)
@@ -734,7 +734,7 @@
734 scroll_smoothly_to(citadel_field)734 scroll_smoothly_to(citadel_field)
735735
736 local o = msg_box(enhance_fortress)736 local o = msg_box(enhance_fortress)
737 while not (citadel_field.immovable and 737 while not (citadel_field.immovable and
738 citadel_field.immovable.name == "citadel") do sleep(800) end738 citadel_field.immovable.name == "citadel") do sleep(800) end
739 o.done = true739 o.done = true
740740
741741
=== modified file 'src/logic/game.h'
--- src/logic/game.h 2013-07-14 17:05:25 +0000
+++ src/logic/game.h 2013-07-19 21:32:25 +0000
@@ -90,10 +90,6 @@
90 friend struct ::Game_Main_Menu_Load_Game;90 friend struct ::Game_Main_Menu_Load_Game;
91 friend struct ::WLApplication;91 friend struct ::WLApplication;
9292
93 // This friend is for legacy reasons and should probably be removed
94 // at least after summer 2008, maybe even earlier.
95 friend struct Game_Interactive_Player_Data_Packet;
96
97 Game();93 Game();
98 ~Game();94 ~Game();
9995
10096
=== modified file 'src/save_handler.cc'
--- src/save_handler.cc 2013-07-16 13:26:14 +0000
+++ src/save_handler.cc 2013-07-19 21:32:25 +0000
@@ -39,19 +39,35 @@
39 */39 */
40void SaveHandler::think(Widelands::Game & game, int32_t realtime) {40void SaveHandler::think(Widelands::Game & game, int32_t realtime) {
41 initialize(realtime);41 initialize(realtime);
4242 std::string filename = "wl_autosave";
43 if (not m_allow_autosaving) // Is autosaving allowed atm?43
44 return;44 if (!m_allow_saving) {
4545 return;
46 int32_t const autosaveInterval =46 }
47 g_options.pull_section("global").get_int47
48 ("autosave", DEFAULT_AUTOSAVE_INTERVAL * 60);48 if (m_save_requested) {
49 if (autosaveInterval <= 0)49 if (!m_save_filename.empty()) {
50 return; // no autosave requested50 filename = m_save_filename;
5151 }
52 int32_t const elapsed = (realtime - m_last_saved_time) / 1000;52
53 if (elapsed < autosaveInterval)53 log("Autosave: save requested : %s\n", filename.c_str());
54 return;54 m_save_requested = false;
55 m_save_filename = "";
56 } else {
57 const int32_t autosave_interval_in_seconds =
58 g_options.pull_section("global").get_int
59 ("autosave", DEFAULT_AUTOSAVE_INTERVAL * 60);
60 if (autosave_interval_in_seconds <= 0) {
61 return; // no autosave requested
62 }
63
64 const int32_t elapsed = (realtime - m_last_saved_time) / 1000;
65 if (elapsed < autosave_interval_in_seconds) {
66 return;
67 }
68
69 log("Autosave: interval elapsed (%d s), saving\n", elapsed);
70 }
5571
56 // TODO: defer saving to next tick so that this message is shown72 // TODO: defer saving to next tick so that this message is shown
57 // before the actual save, or put the saving logic in another thread73 // before the actual save, or put the saving logic in another thread
@@ -59,20 +75,20 @@
59 (_("Saving game..."));75 (_("Saving game..."));
6076
61 // save the game77 // save the game
62 std::string complete_filename =78 const std::string complete_filename = create_file_name(get_base_dir(), filename);
63 create_file_name (get_base_dir(), "wl_autosave");
64 std::string backup_filename;79 std::string backup_filename;
6580
66 // always overwrite a file81 // always overwrite a file
67 if (g_fs->FileExists(complete_filename)) {82 if (g_fs->FileExists(complete_filename)) {
68 backup_filename = create_file_name (get_base_dir(), "wl_autosave2");83 filename += "2";
84 backup_filename = create_file_name (get_base_dir(), filename);
69 if (g_fs->FileExists(backup_filename)) {85 if (g_fs->FileExists(backup_filename)) {
70 g_fs->Unlink(backup_filename);86 g_fs->Unlink(backup_filename);
71 }87 }
72 g_fs->Rename(complete_filename, backup_filename);88 g_fs->Rename(complete_filename, backup_filename);
73 }89 }
7490
75 static std::string error;91 std::string error;
76 if (!save_game(game, complete_filename, &error)) {92 if (!save_game(game, complete_filename, &error)) {
77 log("Autosave: ERROR! - %s\n", error.c_str());93 log("Autosave: ERROR! - %s\n", error.c_str());
78 game.get_ipl()->get_chat_provider()->send_local94 game.get_ipl()->get_chat_provider()->send_local
7995
=== modified file 'src/save_handler.h'
--- src/save_handler.h 2013-07-14 11:48:13 +0000
+++ src/save_handler.h 2013-07-19 21:32:25 +0000
@@ -32,7 +32,8 @@
3232
33class SaveHandler {33class SaveHandler {
34public:34public:
35 SaveHandler() : m_last_saved_time(0), m_initialized(false), m_allow_autosaving(true) {}35 SaveHandler() : m_last_saved_time(0), m_initialized(false), m_allow_saving(true),
36 m_save_requested(false), m_save_filename("") {}
36 void think(Widelands::Game &, int32_t currenttime);37 void think(Widelands::Game &, int32_t currenttime);
37 std::string create_file_name(std::string dir, std::string filename);38 std::string create_file_name(std::string dir, std::string filename);
38 bool save_game39 bool save_game
@@ -43,13 +44,20 @@
43 static std::string get_base_dir() {return "save";}44 static std::string get_base_dir() {return "save";}
44 const std::string get_cur_filename() {return m_current_filename;}45 const std::string get_cur_filename() {return m_current_filename;}
45 void set_current_filename(std::string filename) {m_current_filename = filename;}46 void set_current_filename(std::string filename) {m_current_filename = filename;}
46 void set_allow_autosaving(bool t) {m_allow_autosaving = t;}47 void set_allow_saving(bool t) {m_allow_saving = t;}
47 bool get_allow_autosaving() {return m_allow_autosaving;}48 bool get_allow_saving() {return m_allow_saving;}
49 void request_save(std::string filename = "")
50 {
51 m_save_requested = true;
52 m_save_filename = filename;
53 }
4854
49private:55private:
50 int32_t m_last_saved_time;56 int32_t m_last_saved_time;
51 bool m_initialized;57 bool m_initialized;
52 bool m_allow_autosaving;58 bool m_allow_saving;
59 bool m_save_requested;
60 std::string m_save_filename;
53 std::string m_current_filename;61 std::string m_current_filename;
5462
55 void initialize(int32_t currenttime);63 void initialize(int32_t currenttime);
5664
=== modified file 'src/scripting/lua_game.cc'
--- src/scripting/lua_game.cc 2013-07-15 05:18:12 +0000
+++ src/scripting/lua_game.cc 2013-07-19 21:32:25 +0000
@@ -470,7 +470,7 @@
470 uint32_t cspeed = game.gameController()->desiredSpeed();470 uint32_t cspeed = game.gameController()->desiredSpeed();
471 game.gameController()->setDesiredSpeed(0);471 game.gameController()->setDesiredSpeed(0);
472472
473 game.save_handler().set_allow_autosaving(false);473 game.save_handler().set_allow_saving(false);
474474
475 Story_Message_Box * mb =475 Story_Message_Box * mb =
476 new Story_Message_Box476 new Story_Message_Box
@@ -486,7 +486,7 @@
486486
487 game.gameController()->setDesiredSpeed(cspeed);487 game.gameController()->setDesiredSpeed(cspeed);
488488
489 game.save_handler().set_allow_autosaving(true);489 game.save_handler().set_allow_saving(true);
490490
491 return 1;491 return 1;
492}492}
@@ -1062,8 +1062,10 @@
1062 .. attribute:: done1062 .. attribute:: done
10631063
1064 (RW) defines if this objective is already fulfilled. If done is1064 (RW) defines if this objective is already fulfilled. If done is
1065 :const`true`, the objective will not be shown to the user, no matter what1065 :const`true`, the objective will not be shown to the user, no matter what.
1066 :attr:`visible` is set to.1066 :attr:`visible` is set to. A savegame will be created when this attribute
1067 is changed to :const`true`.
1068
1067*/1069*/
1068int L_Objective::get_done(lua_State * L) {1070int L_Objective::get_done(lua_State * L) {
1069 Objective & o = get(L, get_game(L));1071 Objective & o = get(L, get_game(L));
@@ -1073,6 +1075,19 @@
1073int L_Objective::set_done(lua_State * L) {1075int L_Objective::set_done(lua_State * L) {
1074 Objective & o = get(L, get_game(L));1076 Objective & o = get(L, get_game(L));
1075 o.set_done(luaL_checkboolean(L, -1));1077 o.set_done(luaL_checkboolean(L, -1));
1078
1079 const int32_t autosave = g_options.pull_section("global").get_int("autosave", 0);
1080 if (autosave <= 0) {
1081 return 0;
1082 }
1083
1084 if (o.done()) {
1085 std::string filename = get_egbase(L).get_map()->get_name();
1086 char buffer[128];
1087 snprintf(buffer, sizeof(buffer), _(" (achieved %s)"), o.descname().c_str());
1088 filename.append(buffer);
1089 get_game(L).save_handler().request_save(filename);
1090 }
1076 return 0;1091 return 0;
1077}1092}
10781093
10791094
=== modified file 'src/scripting/lua_root.cc'
--- src/scripting/lua_root.cc 2012-06-08 22:33:16 +0000
+++ src/scripting/lua_root.cc 2013-07-19 21:32:25 +0000
@@ -82,6 +82,7 @@
82 PROP_RO(L_Game, time),82 PROP_RO(L_Game, time),
83 PROP_RW(L_Game, desired_speed),83 PROP_RW(L_Game, desired_speed),
84 PROP_RW(L_Game, allow_autosaving),84 PROP_RW(L_Game, allow_autosaving),
85 PROP_RW(L_Game, allow_saving),
85 {0, 0, 0},86 {0, 0, 0},
86};87};
8788
@@ -131,22 +132,33 @@
131}132}
132133
133/* RST134/* RST
134 .. attribute:: allow_autosaving135 .. attribute:: allow_saving
135136
136 (RW) Disable or enable auto-saving. When you show off UI features in a137 (RW) Disable or enable saving. When you show off UI features in a
137 tutorial or scenario, you have to disallow auto-saving because UI138 tutorial or scenario, you have to disallow saving because UI
138 elements can not be saved and therefore reloading a game saved in the139 elements can not be saved and therefore reloading a game saved in the
139 meantime would crash the game.140 meantime would crash the game.
140*/141*/
141// UNTESTED142// UNTESTED
143int L_Game::set_allow_saving(lua_State * L) {
144 get_game(L).save_handler().set_allow_saving
145 (luaL_checkboolean(L, -1));
146 return 0;
147}
148// UNTESTED
149int L_Game::get_allow_saving(lua_State * L) {
150 lua_pushboolean(L, get_game(L).save_handler().get_allow_saving());
151 return 1;
152}
142int L_Game::set_allow_autosaving(lua_State * L) {153int L_Game::set_allow_autosaving(lua_State * L) {
143 get_game(L).save_handler().set_allow_autosaving154 // WAS_DEPRECATED_BEFORE(build18), use allow_saving
155 get_game(L).save_handler().set_allow_saving
144 (luaL_checkboolean(L, -1));156 (luaL_checkboolean(L, -1));
145 return 0;157 return 0;
146}158}
147// UNTESTED
148int L_Game::get_allow_autosaving(lua_State * L) {159int L_Game::get_allow_autosaving(lua_State * L) {
149 lua_pushboolean(L, get_game(L).save_handler().get_allow_autosaving());160 // WAS_DEPRECATED_BEFORE(build18), use allow_saving
161 lua_pushboolean(L, get_game(L).save_handler().get_allow_saving());
150 return 1;162 return 1;
151}163}
152164
@@ -192,35 +204,23 @@
192/* RST204/* RST
193 .. method:: save(name)205 .. method:: save(name)
194206
195 Saves the game exactly as if the player had entered the save dialog and207 Requests a savegame. Note that the actual save will be performed
196 entered name as an argument. If some error occurred while saving, this208 later, and that you have no control over any error that may happen
197 will throw an Lua error. Note that this currently doesn't work when209 by then currently.
198 called from inside a Coroutine.
199210
200 :arg name: name of save game. If this game already exists, it will be211 :arg name: name of save game, as if entered in the save dialog.
201 silently overwritten212 If this game already exists, it will be silently overwritten.
213 If empty, the autosave name will be used.
202 :type name: :class:`string`214 :type name: :class:`string`
203 :returns: :const:`nil`215 :returns: :const:`nil`
204*/216*/
205int L_Game::save(lua_State * const L) {217int L_Game::save(lua_State * L) {
206 Widelands::Game & game = get_game(L);218 std::string filename = luaL_checkstring(L, -1);
207219 get_game(L).save_handler().request_save(filename);
208 std::string const complete_filename =
209 game.save_handler().create_file_name
210 (SaveHandler::get_base_dir(), luaL_checkstring(L, -1));
211
212 lua_pop(L, 2); // Make stack empty before persistence starts.
213
214 if (g_fs->FileExists(complete_filename))
215 g_fs->Unlink(complete_filename);
216 std::string error;
217 if (!game.save_handler().save_game(game, complete_filename, &error))
218 return report_error(L, "save error: %s", error.c_str());
219220
220 return 0;221 return 0;
221}222}
222223
223
224/*224/*
225 ==========================================================225 ==========================================================
226 C METHODS226 C METHODS
227227
=== modified file 'src/scripting/lua_root.h'
--- src/scripting/lua_root.h 2012-02-15 21:25:34 +0000
+++ src/scripting/lua_root.h 2013-07-19 21:32:25 +0000
@@ -53,6 +53,8 @@
53 int set_desired_speed(lua_State *);53 int set_desired_speed(lua_State *);
54 int get_allow_autosaving(lua_State *);54 int get_allow_autosaving(lua_State *);
55 int set_allow_autosaving(lua_State *);55 int set_allow_autosaving(lua_State *);
56 int get_allow_saving(lua_State *);
57 int set_allow_saving(lua_State *);
5658
57 /*59 /*
58 * Lua methods60 * Lua methods
5961
=== modified file 'test/lua/persistence.wmf/scripting/init.lua'
--- test/lua/persistence.wmf/scripting/init.lua 2011-02-21 20:36:10 +0000
+++ test/lua/persistence.wmf/scripting/init.lua 2013-07-19 21:32:25 +0000
@@ -1,128 +1,138 @@
1-- =======================================================================1-- =======================================================================
2-- LOADING/SAVING TESTS 2-- LOADING/SAVING TESTS
3-- =======================================================================3-- =======================================================================
4-- This tests saving and loading of various Lua objects in the global 4-- This tests saving and loading of various Lua objects in the global
5-- environment.5-- environment.
6--6--
7-- To run this test use:7-- To run this test, go to the top level directory, set the path to your
8-- ./widelands --nozip --scenario=src/scripting/test/persistence.wmf && 8-- widelands binary and run:
9-- ./widelands --loadgame=~/.widelands/save/lua_persistence.wgf9--
1010-- $PATH_TO_WIDELANDS_BINARY --datadir="." --nozip --scenario=test/lua/persistence.wmf && \
11-- $PATH_TO_WIDELANDS_BINARY --datadir="." --loadgame=~/.widelands/save/lua_persistence.wgf
1112
12-- ====================13-- ====================
13-- Test Data to persist 14-- Test Data to persist
14-- ====================15-- ====================
15use("aux", "set")16use("aux", "set")
16
17my_name = "SirVer"
18pi = 3.1415
19eight = 8
20is_true = true
21is_false = false
22
23game = wl.Game()17game = wl.Game()
24p = game.players[1]18
25map = game.map19function save_coroutine()
26a = { "Hallo", "Welt" }20 game = wl.Game()
27c = { func = function(a) return "I say " .. a .. "!" end }21
28field = map:get_field(32,34)22 my_name = "SirVer"
29tree = map:place_immovable("tree3", field)23 pi = 3.1415
30removed_tree = map:place_immovable("tree4", map:get_field(34,34))24 eight = 8
31removed_tree:remove()25 is_true = true
32corout = coroutine.create(function()26 is_false = false
33 local a = 10027
34 coroutine.yield("What cool is that?")28 game = wl.Game()
35 coroutine.yield(a)29 p = game.players[1]
36end)30 map = game.map
37objective = p:add_objective("lumber", "House", "Ship!")31 a = { "Hallo", "Welt" }
38objective.done = true32 c = { func = function(a) return "I say " .. a .. "!" end }
3933 field = map:get_field(32,34)
40p:send_message("dummy msg1", "dummy msg 1")34 tree = map:place_immovable("tree3", field)
41msg = p:send_message("hello nice", "World", {sender="blah", field = field })35 removed_tree = map:place_immovable("tree4", map:get_field(34,34))
42player_slot = map.player_slots[1]36 removed_tree:remove()
4337
44myset = Set:new{38 corout = coroutine.create(function()
45 map:get_field(10,10), map:get_field(10,10), map:get_field(10,11)39 local a = 100
46}40 coroutine.yield("What cool is that?")
4741 coroutine.yield(a)
48-- ========================42 end)
49-- Test after unpersisting 43 objective = p:add_objective("lumber", "House", "Ship!")
50-- ========================44 objective.done = true
51function check_persistence()45
52coroutine.yield(wl.Game().time + 2000)46 p:send_message("dummy msg1", "dummy msg 1")
5347 msg = p:send_message("hello nice", "World", {sender="blah", field = field })
54use("map", "lunit")48 player_slot = map.player_slots[1]
55lunit.import "assertions"49
5650 myset = Set:new{
57print("###################### CHECKING FOR CORRECT PERSISTENCE")51 map:get_field(10,10), map:get_field(10,10), map:get_field(10,11)
58assert_equal("SirVer", my_name)52 }
59assert_equal(3.1415, pi)53
60assert_equal(8, eight)54 game:save("lua_persistence")
61assert_equal(true, is_true)55 print("Save requested\n");
62assert_equal(false, is_false)56
6357 -- Stick around, so that we are needed to get loaded too.
64assert_equal(1, p.number)58 coroutine.yield(wl.Game().time + 8000)
6559end
66assert_table(a)60
67assert_equal(a[1], "Hallo")61function check_coroutine()
68assert_equal(a[2], "Welt")62 -- Sleep till the save routine has done its job.
6963 coroutine.yield(wl.Game().time + 2000)
70assert_table(c)64
71assert_function(c.func)65 -- Attention, lunit contains code that can not be persisted (c functions),
72assert_equal("I say zonk!", c.func("zonk"))66 -- so it must be imported after reload.
7367 use("map", "lunit")
74assert_equal("tree3", tree.name)68 lunit.import "assertions"
7569
76assert_equal(32, field.x)70 print("###################### CHECKING FOR CORRECT PERSISTENCE")
77assert_equal(34, field.y)71 assert_equal("SirVer", my_name)
78assert_equal(tree, field.immovable)72 assert_equal(3.1415, pi)
7973 assert_equal(8, eight)
80assert_thread(corout)74 assert_equal(true, is_true)
81_,rv = coroutine.resume(corout)75 assert_equal(false, is_false)
82assert_equal("What cool is that?", rv)76
83_,rv = coroutine.resume(corout)77 assert_equal(1, p.number)
84assert_equal(100, rv)78
8579 assert_table(a)
86assert_table(objective)80 assert_equal(a[1], "Hallo")
87assert_equal("lumber", objective.name)81 assert_equal(a[2], "Welt")
88assert_equal("House", objective.title)82
89assert_equal("Ship!", objective.body)83 assert_table(c)
90assert_equal(true, objective.done)84 assert_function(c.func)
9185 assert_equal("I say zonk!", c.func("zonk"))
92assert_table(msg)86
93assert_equal("hello nice", msg.title)87 assert_equal("tree3", tree.name)
94assert_equal("World", msg.body)88
95assert_equal("blah", msg.sender)89 assert_equal(32, field.x)
96assert_equal(field, msg.field)90 assert_equal(34, field.y)
9791 assert_equal(tree, field.immovable)
98assert_table(map)92
99assert_equal(64, map.width)93 assert_thread(corout)
100assert_equal(64, map.height)94 _,rv = coroutine.resume(corout)
10195 assert_equal("What cool is that?", rv)
102assert_table(player_slot)96 _,rv = coroutine.resume(corout)
103assert_equal("barbarians", player_slot.tribe_name)97 assert_equal(100, rv)
104assert_equal("Player 1", player_slot.name)98
105assert_equal(player_slot.name, map.player_slots[1].name)99 assert_table(objective)
106assert_equal(player_slot.tribe_name, map.player_slots[1].tribe_name)100 assert_equal("lumber", objective.name)
107101 assert_equal("House", objective.title)
108assert_equal(2, myset.size)102 assert_equal("Ship!", objective.body)
109assert_true(myset:contains(map:get_field(10,10)))103 assert_equal(true, objective.done)
110assert_true(myset:contains(map:get_field(10,11)))104
111105 assert_table(msg)
112print("################### ALL TEST PASS!")106 assert_equal("hello nice", msg.title)
113107 assert_equal("World", msg.body)
114wl.ui.MapView():close()108 assert_equal("blah", msg.sender)
109 assert_equal(field, msg.field)
110
111 assert_table(map)
112 assert_equal(64, map.width)
113 assert_equal(64, map.height)
114
115 assert_table(player_slot)
116 assert_equal("barbarians", player_slot.tribe_name)
117 assert_equal("Player 1", player_slot.name)
118 assert_equal(player_slot.name, map.player_slots[1].name)
119 assert_equal(player_slot.tribe_name, map.player_slots[1].tribe_name)
120
121 assert_equal(2, myset.size)
122 assert_true(myset:contains(map:get_field(10,10)))
123 assert_true(myset:contains(map:get_field(10,11)))
124
125 print("################### ALL TEST PASS!")
126
127 wl.ui.MapView():close()
115end128end
116129
117130
118-- ==========131-- ==========
119-- Main Code 132-- Main Code
120-- ==========133-- ==========
121-- This starts the test routine, saves the game and exits.134-- This starts the test routine, saves the game and exits.
122-- Loading the saved game will check that all objects are 135-- Loading the saved game will check that all objects are
123-- correctly unpersisted136-- correctly unpersisted
124game = wl.Game()137game:launch_coroutine(coroutine.create(check_coroutine))
125game:launch_coroutine(coroutine.create(check_persistence))138game:launch_coroutine(coroutine.create(save_coroutine))
126game:save("lua_persistence")
127wl.ui.MapView():close()
128

Subscribers

People subscribed via source and target branches

to status/vote changes: