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

Proposed by GunChleoc
Status: Merged
Merged at revision: 8653
Proposed branch: lp:~widelands-dev/widelands/allows_seafaring_performance
Merge into: lp:widelands
Diff against target: 324 lines (+71/-22)
6 files modified
src/logic/map.cc (+19/-5)
src/logic/map.h (+8/-3)
src/map_io/s2map.cc (+1/-0)
src/scripting/lua_map.cc (+30/-14)
src/scripting/lua_map.h (+1/-0)
test/maps/two_ponds.wmf/scripting/test_seafaring.lua (+12/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/allows_seafaring_performance
Reviewer Review Type Date Requested Status
Klaus Halfmann review, compile, automated test Approve
Review via email: mp+342987@code.launchpad.net

Commit message

Only recalculate whether a map allows seafaring when something has changed

- New function Map::recalculate_allows_seafaring()
- Map::allows_seafaring() is recalculated:
  - On map load
  - On editor save
  - When a port space is changed via Lua scripting
  - When it is triggered by LuaMap::recalculate_seafaring() or recalculate()
- Added test
- Some documentation tweaks to lua_map.cc.
- Fixed the check for invalid terrains in LuaField::get_terr.

Description of the change

Only recalculate whether a map allows seafaring when something has changed. This new design is a bit more fragile, but I think the performance gain is worth it, because the AI needs this function.

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

Continuous integration builds have changed state:

Travis build 3369. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/364989186.
Appveyor build 3175. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_allows_seafaring_performance-3175.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

This is a prerequisite for the smaller_building_statistics branch.
checking this out now ...

Code is reasonable.

As this change carries its own testcase it can go in
once the test works for me.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks for the review :)

Can you trigger the merge once your testing is done?

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

allows_seafaring_performance$ ./regression_test.py -b ./widelands
...
test/maps/two_ponds.wmf/scripting/test_seafaring.lua ...
  Running Widelands ... done.
  Loading savegame: port_spaces ... done.
ok
...
Ran 41 tests in 1204.108s

@bunnybot merge

review: Approve (review, compile, automated test)
Revision history for this message
kaputtnik (franku) wrote :

wow, finally i can play debug builds again without stuttering, even on big maps.

Thanks :-)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/logic/map.cc'
--- src/logic/map.cc 2018-04-07 16:59:00 +0000
+++ src/logic/map.cc 2018-04-11 07:16:58 +0000
@@ -70,7 +70,8 @@
70 scenario_types_(NO_SCENARIO),70 scenario_types_(NO_SCENARIO),
71 width_(0),71 width_(0),
72 height_(0),72 height_(0),
73 pathfieldmgr_(new PathfieldManager) {73 pathfieldmgr_(new PathfieldManager),
74 allows_seafaring_(false) {
74}75}
7576
76Map::~Map() {77Map::~Map() {
@@ -157,6 +158,7 @@
157 f = get_fcoords(Coords(x, y));158 f = get_fcoords(Coords(x, y));
158 recalc_nodecaps_pass2(world, f);159 recalc_nodecaps_pass2(world, f);
159 }160 }
161 recalculate_allows_seafaring();
160}162}
161163
162void Map::recalc_default_resources(const World& world) {164void Map::recalc_default_resources(const World& world) {
@@ -278,6 +280,7 @@
278 objectives_.clear();280 objectives_.clear();
279281
280 port_spaces_.clear();282 port_spaces_.clear();
283 allows_seafaring_ = false;
281284
282 // TODO(meitis): should be done here ... but WidelandsMapLoader::preload_map calls285 // TODO(meitis): should be done here ... but WidelandsMapLoader::preload_map calls
283 // this cleanup AFTER assigning filesystem_ in WidelandsMapLoader::WidelandsMapLoader286 // this cleanup AFTER assigning filesystem_ in WidelandsMapLoader::WidelandsMapLoader
@@ -1316,7 +1319,7 @@
1316 return port_spaces_.count(c);1319 return port_spaces_.count(c);
1317}1320}
13181321
1319bool Map::set_port_space(const World& world, const Coords& c, bool set, bool force) {1322bool Map::set_port_space(const World& world, const Coords& c, bool set, bool force, bool recalculate_seafaring) {
1320 bool success = false;1323 bool success = false;
1321 if (set) {1324 if (set) {
1322 success = force || is_port_space_allowed(world, get_fcoords(c));1325 success = force || is_port_space_allowed(world, get_fcoords(c));
@@ -1327,6 +1330,9 @@
1327 port_spaces_.erase(c);1330 port_spaces_.erase(c);
1328 success = true;1331 success = true;
1329 }1332 }
1333 if (recalculate_seafaring) {
1334 recalculate_allows_seafaring();
1335 }
1330 return success;1336 return success;
1331}1337}
13321338
@@ -1973,10 +1979,16 @@
1973}1979}
19741980
1975bool Map::allows_seafaring() const {1981bool Map::allows_seafaring() const {
1982 return allows_seafaring_;
1983}
1984
1985// This check can become very expensive, so we only recalculate this on relevant map changes.
1986void Map::recalculate_allows_seafaring() {
19761987
1977 // There need to be at least 2 port spaces for seafaring to make sense1988 // There need to be at least 2 port spaces for seafaring to make sense
1978 if (get_port_spaces().size() < 2) {1989 if (get_port_spaces().size() < 2) {
1979 return false;1990 allows_seafaring_ = false;
1991 return;
1980 }1992 }
19811993
1982 std::set<Coords> reachable_from_previous_ports;1994 std::set<Coords> reachable_from_previous_ports;
@@ -2000,7 +2012,8 @@
20002012
2001 // Found one2013 // Found one
2002 if (reachable_from_previous_ports.count(current_position) > 0) {2014 if (reachable_from_previous_ports.count(current_position) > 0) {
2003 return true;2015 allows_seafaring_ = true;
2016 return;
2004 }2017 }
20052018
2006 // Adding the neighbors to the list2019 // Adding the neighbors to the list
@@ -2021,7 +2034,7 @@
2021 reachable_from_previous_ports.insert(reachable_coord);2034 reachable_from_previous_ports.insert(reachable_coord);
2022 }2035 }
2023 }2036 }
2024 return false;2037 allows_seafaring_ = false;
2025}2038}
20262039
2027void Map::cleanup_port_spaces(const World& world) {2040void Map::cleanup_port_spaces(const World& world) {
@@ -2031,6 +2044,7 @@
2031 continue;2044 continue;
2032 }2045 }
2033 }2046 }
2047 recalculate_allows_seafaring();
2034}2048}
20352049
2036bool Map::has_artifacts() {2050bool Map::has_artifacts() {
20372051
=== modified file 'src/logic/map.h'
--- src/logic/map.h 2018-04-07 16:59:00 +0000
+++ src/logic/map.h 2018-04-11 07:16:58 +0000
@@ -408,7 +408,8 @@
408 /***408 /***
409 * Changes the given triangle's terrain. This happens in the editor and might409 * Changes the given triangle's terrain. This happens in the editor and might
410 * happen in the game too if some kind of land increasement is implemented (like410 * happen in the game too if some kind of land increasement is implemented (like
411 * drying swamps). The nodecaps need to be recalculated411 * drying swamps). The nodecaps need to be recalculated. If terrain was changed from
412 * or to water, we need to recalculate_allows_seafaring too, depending on the situation.
412 *413 *
413 * @return the radius of changes.414 * @return the radius of changes.
414 */415 */
@@ -448,14 +449,16 @@
448 /// 'force' sets the port space even if it isn't viable, and is to be used for map loading only.449 /// 'force' sets the port space even if it isn't viable, and is to be used for map loading only.
449 /// Returns whether the port space was set/unset successfully.450 /// Returns whether the port space was set/unset successfully.
450 bool451 bool
451 set_port_space(const World& world, const Widelands::Coords& c, bool set, bool force = false);452 set_port_space(const World& world, const Widelands::Coords& c, bool set, bool force = false, bool recalculate_seafaring = false);
452 const PortSpacesSet& get_port_spaces() const {453 const PortSpacesSet& get_port_spaces() const {
453 return port_spaces_;454 return port_spaces_;
454 }455 }
455 std::vector<Coords> find_portdock(const Widelands::Coords& c) const;456 std::vector<Coords> find_portdock(const Widelands::Coords& c) const;
456457
457 /// Check whether there are at least 2 port spaces that can be reached from each other by water458 /// Return true if there are at least 2 port spaces that can be reached from each other by water
458 bool allows_seafaring() const;459 bool allows_seafaring() const;
460 /// Calculate whether there are at least 2 port spaces that can be reached from each other by water and set the allows_seafaring property
461 void recalculate_allows_seafaring();
459 /// Remove all port spaces that are not valid (Buildcap < big or not enough space for a462 /// Remove all port spaces that are not valid (Buildcap < big or not enough space for a
460 /// portdock).463 /// portdock).
461 void cleanup_port_spaces(const World& world);464 void cleanup_port_spaces(const World& world);
@@ -518,6 +521,8 @@
518 std::unique_ptr<FileSystem> filesystem_;521 std::unique_ptr<FileSystem> filesystem_;
519522
520 PortSpacesSet port_spaces_;523 PortSpacesSet port_spaces_;
524 bool allows_seafaring_;
525
521 Objectives objectives_;526 Objectives objectives_;
522527
523 MapVersion map_version_;528 MapVersion map_version_;
524529
=== modified file 'src/map_io/s2map.cc'
--- src/map_io/s2map.cc 2018-04-07 16:59:00 +0000
+++ src/map_io/s2map.cc 2018-04-11 07:16:58 +0000
@@ -1065,4 +1065,5 @@
1065 log("SUCCESS! Port buildspace set for (%i, %i) \n", fc.x, fc.y);1065 log("SUCCESS! Port buildspace set for (%i, %i) \n", fc.x, fc.y);
1066 }1066 }
1067 }1067 }
1068 map_.recalculate_allows_seafaring();
1068}1069}
10691070
=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc 2018-04-07 16:59:00 +0000
+++ src/scripting/lua_map.cc 2018-04-11 07:16:58 +0000
@@ -1145,6 +1145,7 @@
1145const char LuaMap::className[] = "Map";1145const char LuaMap::className[] = "Map";
1146const MethodType<LuaMap> LuaMap::Methods[] = {1146const MethodType<LuaMap> LuaMap::Methods[] = {
1147 METHOD(LuaMap, place_immovable), METHOD(LuaMap, get_field), METHOD(LuaMap, recalculate),1147 METHOD(LuaMap, place_immovable), METHOD(LuaMap, get_field), METHOD(LuaMap, recalculate),
1148 METHOD(LuaMap, recalculate_seafaring),
1148 METHOD(LuaMap, set_port_space), {nullptr, nullptr},1149 METHOD(LuaMap, set_port_space), {nullptr, nullptr},
1149};1150};
1150const PropertyType<LuaMap> LuaMap::Properties[] = {1151const PropertyType<LuaMap> LuaMap::Properties[] = {
@@ -1170,8 +1171,7 @@
1170/* RST1171/* RST
1171 .. attribute:: allows_seafaring1172 .. attribute:: allows_seafaring
11721173
1173 (RO) Whether the map currently allows seafaring. This will calculate a path between port spaces,1174 (RO) Whether the map currently allows seafaring.
1174 so it's more accurate but less efficient than :any:`number_of_port_spaces`.
11751175
1176 :returns: True if there are at least two port spaces that can be reached from each other.1176 :returns: True if there are at least two port spaces that can be reached from each other.
1177*/1177*/
@@ -1182,9 +1182,7 @@
1182/* RST1182/* RST
1183 .. attribute:: number_of_port_spaces1183 .. attribute:: number_of_port_spaces
11841184
1185 (RO) The amount of port spaces on the map. If this is >= 2, one can assume that the map1185 (RO) The amount of port spaces on the map.
1186 allows seafaring. This is checked very quickly and is more efficient than :any:`allows_seafaring`,
1187 but it won't detect whether the port spaces can be reached from each other, so it's less accurate.
11881186
1189 :returns: An integer with the number of port spaces.1187 :returns: An integer with the number of port spaces.
1190*/1188*/
@@ -1312,9 +1310,9 @@
1312/* RST1310/* RST
1313 .. method:: recalculate()1311 .. method:: recalculate()
13141312
1315 This map recalculates the whole map state: height of fields, buildcaps1313 This map recalculates the whole map state: height of fields, buildcaps,
1316 and so on. You only need to call this function if you changed1314 whether the map allows seafaring and so on. You only need to call this
1317 Field.raw_height in any way.1315 function if you changed :any:`raw_height` in any way.
1318*/1316*/
1319// TODO(unknown): do we really want this function?1317// TODO(unknown): do we really want this function?
1320int LuaMap::recalculate(lua_State* L) {1318int LuaMap::recalculate(lua_State* L) {
@@ -1324,6 +1322,18 @@
1324}1322}
13251323
1326/* RST1324/* RST
1325 .. method:: recalculate_seafaring()
1326
1327 This method recalculates whether the map allows seafaring.
1328 You only need to call this function if you have been changing terrains to/from
1329 water and wanted to defer recalculating whether the map allows seafaring.
1330*/
1331int LuaMap::recalculate_seafaring(lua_State* L) {
1332 get_egbase(L).mutable_map()->recalculate_allows_seafaring();
1333 return 0;
1334}
1335
1336/* RST
1327 .. method:: set_port_space(x, y, allowed)1337 .. method:: set_port_space(x, y, allowed)
13281338
1329 Sets whether a port space is allowed at the coordinates (x, y).1339 Sets whether a port space is allowed at the coordinates (x, y).
@@ -1344,7 +1354,7 @@
1344 const int y = luaL_checkint32(L, 3);1354 const int y = luaL_checkint32(L, 3);
1345 const bool allowed = luaL_checkboolean(L, 4);1355 const bool allowed = luaL_checkboolean(L, 4);
1346 const bool success = get_egbase(L).mutable_map()->set_port_space(1356 const bool success = get_egbase(L).mutable_map()->set_port_space(
1347 get_egbase(L).world(), Widelands::Coords(x, y), allowed);1357 get_egbase(L).world(), Widelands::Coords(x, y), allowed, false, true);
1348 lua_pushboolean(L, success);1358 lua_pushboolean(L, success);
1349 return 1;1359 return 1;
1350}1360}
@@ -5932,7 +5942,9 @@
5932 (RW) The height of this field. The default height is 10, you can increase5942 (RW) The height of this field. The default height is 10, you can increase
5933 or decrease this value to build mountains. Note though that if you change5943 or decrease this value to build mountains. Note though that if you change
5934 this value too much, all surrounding fields will also change their5944 this value too much, all surrounding fields will also change their
5935 heights because the slope is constrained.5945 heights because the slope is constrained. If you are changing the height
5946 of many terrains at once, use :attr:`raw_height` instead and then call
5947 :any:`recalculate` afterwards.
5936*/5948*/
5937int LuaField::get_height(lua_State* L) {5949int LuaField::get_height(lua_State* L) {
5938 lua_pushuint32(L, fcoords(L).field->get_height());5950 lua_pushuint32(L, fcoords(L).field->get_height());
@@ -5957,10 +5969,10 @@
5957/* RST5969/* RST
5958 .. attribute:: raw_height5970 .. attribute:: raw_height
59595971
5960 (RW The same as :attr:`height`, but setting this will not trigger a5972 (RW) The same as :attr:`height`, but setting this will not trigger a
5961 recalculation of the surrounding fields. You can use this field to5973 recalculation of the surrounding fields. You can use this field to
5962 change the height of many fields on a map quickly, then use5974 change the height of many fields on a map quickly, then use
5963 :func:`wl.map.recalculate()` to make sure that everything is in order.5975 :any:`recalculate` to make sure that everything is in order.
5964*/5976*/
5965// UNTESTED5977// UNTESTED
5966int LuaField::get_raw_height(lua_State* L) {5978int LuaField::get_raw_height(lua_State* L) {
@@ -6119,7 +6131,11 @@
6119 (RW) The terrain of the right/down triangle. This is a string value6131 (RW) The terrain of the right/down triangle. This is a string value
6120 containing the name of the terrain as it is defined in the world6132 containing the name of the terrain as it is defined in the world
6121 configuration. You can change the terrain by simply assigning another6133 configuration. You can change the terrain by simply assigning another
6122 valid name to these variables.6134 valid name to these variables. If you are changing the terrain from or to
6135 water, the map will not recalculate whether it allows seafaring, because
6136 this recalculation can take up a lot of performance. If you need this
6137 recalculated, you can do so by calling :any:`recalculate_seafaring` after
6138 you're done changing terrains.
6123*/6139*/
6124int LuaField::get_terr(lua_State* L) {6140int LuaField::get_terr(lua_State* L) {
6125 TerrainDescription& td = get_egbase(L).world().terrain_descr(fcoords(L).field->terrain_r());6141 TerrainDescription& td = get_egbase(L).world().terrain_descr(fcoords(L).field->terrain_r());
@@ -6131,7 +6147,7 @@
6131 EditorGameBase& egbase = get_egbase(L);6147 EditorGameBase& egbase = get_egbase(L);
6132 const World& world = egbase.world();6148 const World& world = egbase.world();
6133 const DescriptionIndex td = world.terrains().get_index(name);6149 const DescriptionIndex td = world.terrains().get_index(name);
6134 if (td == static_cast<DescriptionIndex>(-1))6150 if (td == static_cast<DescriptionIndex>(Widelands::INVALID_INDEX))
6135 report_error(L, "Unknown terrain '%s'", name);6151 report_error(L, "Unknown terrain '%s'", name);
61366152
6137 egbase.mutable_map()->change_terrain(world, TCoords<FCoords>(fcoords(L), TriangleIndex::R), td);6153 egbase.mutable_map()->change_terrain(world, TCoords<FCoords>(fcoords(L), TriangleIndex::R), td);
61386154
=== modified file 'src/scripting/lua_map.h'
--- src/scripting/lua_map.h 2018-04-07 16:59:00 +0000
+++ src/scripting/lua_map.h 2018-04-11 07:16:58 +0000
@@ -98,6 +98,7 @@
98 int place_immovable(lua_State*);98 int place_immovable(lua_State*);
99 int get_field(lua_State*);99 int get_field(lua_State*);
100 int recalculate(lua_State*);100 int recalculate(lua_State*);
101 int recalculate_seafaring(lua_State*);
101 int set_port_space(lua_State*);102 int set_port_space(lua_State*);
102103
103 /*104 /*
104105
=== modified file 'test/maps/two_ponds.wmf/scripting/test_seafaring.lua'
--- test/maps/two_ponds.wmf/scripting/test_seafaring.lua 2017-11-08 15:53:57 +0000
+++ test/maps/two_ponds.wmf/scripting/test_seafaring.lua 2018-04-11 07:16:58 +0000
@@ -3,30 +3,42 @@
3 -- not allow seafaring. One of the port spaces has trees on top of it.3 -- not allow seafaring. One of the port spaces has trees on top of it.
4 assert_equal(2, map.number_of_port_spaces)4 assert_equal(2, map.number_of_port_spaces)
5 assert_equal(false, map.allows_seafaring)5 assert_equal(false, map.allows_seafaring)
6 map:recalculate_seafaring()
7 assert_equal(false, map.allows_seafaring)
68
7 -- Now try to add a port space on a medium buildcap, it should fail9 -- Now try to add a port space on a medium buildcap, it should fail
8 assert_equal(false, map:set_port_space(11, 9, true))10 assert_equal(false, map:set_port_space(11, 9, true))
9 assert_equal(2, map.number_of_port_spaces)11 assert_equal(2, map.number_of_port_spaces)
10 assert_equal(false, map.allows_seafaring)12 assert_equal(false, map.allows_seafaring)
13 map:recalculate_seafaring()
14 assert_equal(false, map.allows_seafaring)
1115
12 -- Now try to add a port space away from water, it should fail16 -- Now try to add a port space away from water, it should fail
13 assert_equal(false, map:set_port_space(18, 9, true))17 assert_equal(false, map:set_port_space(18, 9, true))
14 assert_equal(2, map.number_of_port_spaces)18 assert_equal(2, map.number_of_port_spaces)
15 assert_equal(false, map.allows_seafaring)19 assert_equal(false, map.allows_seafaring)
20 map:recalculate_seafaring()
21 assert_equal(false, map.allows_seafaring)
1622
17 -- Now add a connecting port space - it should succeed and we should have seafaring then23 -- Now add a connecting port space - it should succeed and we should have seafaring then
18 assert_equal(true, map:set_port_space(0, 2, true))24 assert_equal(true, map:set_port_space(0, 2, true))
19 assert_equal(3, map.number_of_port_spaces)25 assert_equal(3, map.number_of_port_spaces)
20 assert_equal(true, map.allows_seafaring)26 assert_equal(true, map.allows_seafaring)
27 map:recalculate_seafaring()
28 assert_equal(true, map.allows_seafaring)
2129
22 stable_save(game, "port_spaces")30 stable_save(game, "port_spaces")
23 assert_equal(3, map.number_of_port_spaces)31 assert_equal(3, map.number_of_port_spaces)
24 assert_equal(true, map.allows_seafaring)32 assert_equal(true, map.allows_seafaring)
33 map:recalculate_seafaring()
34 assert_equal(true, map.allows_seafaring)
2535
26 -- Remove the port space again36 -- Remove the port space again
27 assert_equal(true, map:set_port_space(0, 2, false))37 assert_equal(true, map:set_port_space(0, 2, false))
28 assert_equal(2, map.number_of_port_spaces)38 assert_equal(2, map.number_of_port_spaces)
29 assert_equal(false, map.allows_seafaring)39 assert_equal(false, map.allows_seafaring)
40 map:recalculate_seafaring()
41 assert_equal(false, map.allows_seafaring)
3042
31 print("# All Tests passed.")43 print("# All Tests passed.")
32 wl.ui.MapView():close()44 wl.ui.MapView():close()

Subscribers

People subscribed via source and target branches

to status/vote changes: