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
1=== modified file 'src/logic/map.cc'
2--- src/logic/map.cc 2018-04-07 16:59:00 +0000
3+++ src/logic/map.cc 2018-04-11 07:16:58 +0000
4@@ -70,7 +70,8 @@
5 scenario_types_(NO_SCENARIO),
6 width_(0),
7 height_(0),
8- pathfieldmgr_(new PathfieldManager) {
9+ pathfieldmgr_(new PathfieldManager),
10+ allows_seafaring_(false) {
11 }
12
13 Map::~Map() {
14@@ -157,6 +158,7 @@
15 f = get_fcoords(Coords(x, y));
16 recalc_nodecaps_pass2(world, f);
17 }
18+ recalculate_allows_seafaring();
19 }
20
21 void Map::recalc_default_resources(const World& world) {
22@@ -278,6 +280,7 @@
23 objectives_.clear();
24
25 port_spaces_.clear();
26+ allows_seafaring_ = false;
27
28 // TODO(meitis): should be done here ... but WidelandsMapLoader::preload_map calls
29 // this cleanup AFTER assigning filesystem_ in WidelandsMapLoader::WidelandsMapLoader
30@@ -1316,7 +1319,7 @@
31 return port_spaces_.count(c);
32 }
33
34-bool Map::set_port_space(const World& world, const Coords& c, bool set, bool force) {
35+bool Map::set_port_space(const World& world, const Coords& c, bool set, bool force, bool recalculate_seafaring) {
36 bool success = false;
37 if (set) {
38 success = force || is_port_space_allowed(world, get_fcoords(c));
39@@ -1327,6 +1330,9 @@
40 port_spaces_.erase(c);
41 success = true;
42 }
43+ if (recalculate_seafaring) {
44+ recalculate_allows_seafaring();
45+ }
46 return success;
47 }
48
49@@ -1973,10 +1979,16 @@
50 }
51
52 bool Map::allows_seafaring() const {
53+ return allows_seafaring_;
54+}
55+
56+// This check can become very expensive, so we only recalculate this on relevant map changes.
57+void Map::recalculate_allows_seafaring() {
58
59 // There need to be at least 2 port spaces for seafaring to make sense
60 if (get_port_spaces().size() < 2) {
61- return false;
62+ allows_seafaring_ = false;
63+ return;
64 }
65
66 std::set<Coords> reachable_from_previous_ports;
67@@ -2000,7 +2012,8 @@
68
69 // Found one
70 if (reachable_from_previous_ports.count(current_position) > 0) {
71- return true;
72+ allows_seafaring_ = true;
73+ return;
74 }
75
76 // Adding the neighbors to the list
77@@ -2021,7 +2034,7 @@
78 reachable_from_previous_ports.insert(reachable_coord);
79 }
80 }
81- return false;
82+ allows_seafaring_ = false;
83 }
84
85 void Map::cleanup_port_spaces(const World& world) {
86@@ -2031,6 +2044,7 @@
87 continue;
88 }
89 }
90+ recalculate_allows_seafaring();
91 }
92
93 bool Map::has_artifacts() {
94
95=== modified file 'src/logic/map.h'
96--- src/logic/map.h 2018-04-07 16:59:00 +0000
97+++ src/logic/map.h 2018-04-11 07:16:58 +0000
98@@ -408,7 +408,8 @@
99 /***
100 * Changes the given triangle's terrain. This happens in the editor and might
101 * happen in the game too if some kind of land increasement is implemented (like
102- * drying swamps). The nodecaps need to be recalculated
103+ * drying swamps). The nodecaps need to be recalculated. If terrain was changed from
104+ * or to water, we need to recalculate_allows_seafaring too, depending on the situation.
105 *
106 * @return the radius of changes.
107 */
108@@ -448,14 +449,16 @@
109 /// 'force' sets the port space even if it isn't viable, and is to be used for map loading only.
110 /// Returns whether the port space was set/unset successfully.
111 bool
112- set_port_space(const World& world, const Widelands::Coords& c, bool set, bool force = false);
113+ set_port_space(const World& world, const Widelands::Coords& c, bool set, bool force = false, bool recalculate_seafaring = false);
114 const PortSpacesSet& get_port_spaces() const {
115 return port_spaces_;
116 }
117 std::vector<Coords> find_portdock(const Widelands::Coords& c) const;
118
119- /// Check whether there are at least 2 port spaces that can be reached from each other by water
120+ /// Return true if there are at least 2 port spaces that can be reached from each other by water
121 bool allows_seafaring() const;
122+ /// Calculate whether there are at least 2 port spaces that can be reached from each other by water and set the allows_seafaring property
123+ void recalculate_allows_seafaring();
124 /// Remove all port spaces that are not valid (Buildcap < big or not enough space for a
125 /// portdock).
126 void cleanup_port_spaces(const World& world);
127@@ -518,6 +521,8 @@
128 std::unique_ptr<FileSystem> filesystem_;
129
130 PortSpacesSet port_spaces_;
131+ bool allows_seafaring_;
132+
133 Objectives objectives_;
134
135 MapVersion map_version_;
136
137=== modified file 'src/map_io/s2map.cc'
138--- src/map_io/s2map.cc 2018-04-07 16:59:00 +0000
139+++ src/map_io/s2map.cc 2018-04-11 07:16:58 +0000
140@@ -1065,4 +1065,5 @@
141 log("SUCCESS! Port buildspace set for (%i, %i) \n", fc.x, fc.y);
142 }
143 }
144+ map_.recalculate_allows_seafaring();
145 }
146
147=== modified file 'src/scripting/lua_map.cc'
148--- src/scripting/lua_map.cc 2018-04-07 16:59:00 +0000
149+++ src/scripting/lua_map.cc 2018-04-11 07:16:58 +0000
150@@ -1145,6 +1145,7 @@
151 const char LuaMap::className[] = "Map";
152 const MethodType<LuaMap> LuaMap::Methods[] = {
153 METHOD(LuaMap, place_immovable), METHOD(LuaMap, get_field), METHOD(LuaMap, recalculate),
154+ METHOD(LuaMap, recalculate_seafaring),
155 METHOD(LuaMap, set_port_space), {nullptr, nullptr},
156 };
157 const PropertyType<LuaMap> LuaMap::Properties[] = {
158@@ -1170,8 +1171,7 @@
159 /* RST
160 .. attribute:: allows_seafaring
161
162- (RO) Whether the map currently allows seafaring. This will calculate a path between port spaces,
163- so it's more accurate but less efficient than :any:`number_of_port_spaces`.
164+ (RO) Whether the map currently allows seafaring.
165
166 :returns: True if there are at least two port spaces that can be reached from each other.
167 */
168@@ -1182,9 +1182,7 @@
169 /* RST
170 .. attribute:: number_of_port_spaces
171
172- (RO) The amount of port spaces on the map. If this is >= 2, one can assume that the map
173- allows seafaring. This is checked very quickly and is more efficient than :any:`allows_seafaring`,
174- but it won't detect whether the port spaces can be reached from each other, so it's less accurate.
175+ (RO) The amount of port spaces on the map.
176
177 :returns: An integer with the number of port spaces.
178 */
179@@ -1312,9 +1310,9 @@
180 /* RST
181 .. method:: recalculate()
182
183- This map recalculates the whole map state: height of fields, buildcaps
184- and so on. You only need to call this function if you changed
185- Field.raw_height in any way.
186+ This map recalculates the whole map state: height of fields, buildcaps,
187+ whether the map allows seafaring and so on. You only need to call this
188+ function if you changed :any:`raw_height` in any way.
189 */
190 // TODO(unknown): do we really want this function?
191 int LuaMap::recalculate(lua_State* L) {
192@@ -1324,6 +1322,18 @@
193 }
194
195 /* RST
196+ .. method:: recalculate_seafaring()
197+
198+ This method recalculates whether the map allows seafaring.
199+ You only need to call this function if you have been changing terrains to/from
200+ water and wanted to defer recalculating whether the map allows seafaring.
201+*/
202+int LuaMap::recalculate_seafaring(lua_State* L) {
203+ get_egbase(L).mutable_map()->recalculate_allows_seafaring();
204+ return 0;
205+}
206+
207+/* RST
208 .. method:: set_port_space(x, y, allowed)
209
210 Sets whether a port space is allowed at the coordinates (x, y).
211@@ -1344,7 +1354,7 @@
212 const int y = luaL_checkint32(L, 3);
213 const bool allowed = luaL_checkboolean(L, 4);
214 const bool success = get_egbase(L).mutable_map()->set_port_space(
215- get_egbase(L).world(), Widelands::Coords(x, y), allowed);
216+ get_egbase(L).world(), Widelands::Coords(x, y), allowed, false, true);
217 lua_pushboolean(L, success);
218 return 1;
219 }
220@@ -5932,7 +5942,9 @@
221 (RW) The height of this field. The default height is 10, you can increase
222 or decrease this value to build mountains. Note though that if you change
223 this value too much, all surrounding fields will also change their
224- heights because the slope is constrained.
225+ heights because the slope is constrained. If you are changing the height
226+ of many terrains at once, use :attr:`raw_height` instead and then call
227+ :any:`recalculate` afterwards.
228 */
229 int LuaField::get_height(lua_State* L) {
230 lua_pushuint32(L, fcoords(L).field->get_height());
231@@ -5957,10 +5969,10 @@
232 /* RST
233 .. attribute:: raw_height
234
235- (RW The same as :attr:`height`, but setting this will not trigger a
236+ (RW) The same as :attr:`height`, but setting this will not trigger a
237 recalculation of the surrounding fields. You can use this field to
238 change the height of many fields on a map quickly, then use
239- :func:`wl.map.recalculate()` to make sure that everything is in order.
240+ :any:`recalculate` to make sure that everything is in order.
241 */
242 // UNTESTED
243 int LuaField::get_raw_height(lua_State* L) {
244@@ -6119,7 +6131,11 @@
245 (RW) The terrain of the right/down triangle. This is a string value
246 containing the name of the terrain as it is defined in the world
247 configuration. You can change the terrain by simply assigning another
248- valid name to these variables.
249+ valid name to these variables. If you are changing the terrain from or to
250+ water, the map will not recalculate whether it allows seafaring, because
251+ this recalculation can take up a lot of performance. If you need this
252+ recalculated, you can do so by calling :any:`recalculate_seafaring` after
253+ you're done changing terrains.
254 */
255 int LuaField::get_terr(lua_State* L) {
256 TerrainDescription& td = get_egbase(L).world().terrain_descr(fcoords(L).field->terrain_r());
257@@ -6131,7 +6147,7 @@
258 EditorGameBase& egbase = get_egbase(L);
259 const World& world = egbase.world();
260 const DescriptionIndex td = world.terrains().get_index(name);
261- if (td == static_cast<DescriptionIndex>(-1))
262+ if (td == static_cast<DescriptionIndex>(Widelands::INVALID_INDEX))
263 report_error(L, "Unknown terrain '%s'", name);
264
265 egbase.mutable_map()->change_terrain(world, TCoords<FCoords>(fcoords(L), TriangleIndex::R), td);
266
267=== modified file 'src/scripting/lua_map.h'
268--- src/scripting/lua_map.h 2018-04-07 16:59:00 +0000
269+++ src/scripting/lua_map.h 2018-04-11 07:16:58 +0000
270@@ -98,6 +98,7 @@
271 int place_immovable(lua_State*);
272 int get_field(lua_State*);
273 int recalculate(lua_State*);
274+ int recalculate_seafaring(lua_State*);
275 int set_port_space(lua_State*);
276
277 /*
278
279=== modified file 'test/maps/two_ponds.wmf/scripting/test_seafaring.lua'
280--- test/maps/two_ponds.wmf/scripting/test_seafaring.lua 2017-11-08 15:53:57 +0000
281+++ test/maps/two_ponds.wmf/scripting/test_seafaring.lua 2018-04-11 07:16:58 +0000
282@@ -3,30 +3,42 @@
283 -- not allow seafaring. One of the port spaces has trees on top of it.
284 assert_equal(2, map.number_of_port_spaces)
285 assert_equal(false, map.allows_seafaring)
286+ map:recalculate_seafaring()
287+ assert_equal(false, map.allows_seafaring)
288
289 -- Now try to add a port space on a medium buildcap, it should fail
290 assert_equal(false, map:set_port_space(11, 9, true))
291 assert_equal(2, map.number_of_port_spaces)
292 assert_equal(false, map.allows_seafaring)
293+ map:recalculate_seafaring()
294+ assert_equal(false, map.allows_seafaring)
295
296 -- Now try to add a port space away from water, it should fail
297 assert_equal(false, map:set_port_space(18, 9, true))
298 assert_equal(2, map.number_of_port_spaces)
299 assert_equal(false, map.allows_seafaring)
300+ map:recalculate_seafaring()
301+ assert_equal(false, map.allows_seafaring)
302
303 -- Now add a connecting port space - it should succeed and we should have seafaring then
304 assert_equal(true, map:set_port_space(0, 2, true))
305 assert_equal(3, map.number_of_port_spaces)
306 assert_equal(true, map.allows_seafaring)
307+ map:recalculate_seafaring()
308+ assert_equal(true, map.allows_seafaring)
309
310 stable_save(game, "port_spaces")
311 assert_equal(3, map.number_of_port_spaces)
312 assert_equal(true, map.allows_seafaring)
313+ map:recalculate_seafaring()
314+ assert_equal(true, map.allows_seafaring)
315
316 -- Remove the port space again
317 assert_equal(true, map:set_port_space(0, 2, false))
318 assert_equal(2, map.number_of_port_spaces)
319 assert_equal(false, map.allows_seafaring)
320+ map:recalculate_seafaring()
321+ assert_equal(false, map.allows_seafaring)
322
323 print("# All Tests passed.")
324 wl.ui.MapView():close()

Subscribers

People subscribed via source and target branches

to status/vote changes: