1 /*
2 * Copyright (C) 2007-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "map_io/map_players_view_packet.h"
21
22 #include "base/log.h"
23 #include "base/macros.h"
24 #include "base/wexception.h"
25 #include "economy/flag.h"
26 #include "economy/road.h"
27 #include "economy/waterway.h"
28 #include "io/fileread.h"
29 #include "io/filesystem/filesystem_exceptions.h"
30 #include "io/filewrite.h"
31 #include "logic/editor_game_base.h"
32 #include "logic/field.h"
33 #include "logic/game_data_error.h"
34 #include "logic/map_objects/tribes/tribe_descr.h"
35 #include "logic/map_objects/world/world.h"
36 #include "logic/player.h"
37
38 namespace Widelands {
39
40 #define PLAYERDIRNAME_TEMPLATE "player/%u"
41 #define DIRNAME_TEMPLATE PLAYERDIRNAME_TEMPLATE "/view"
42
43 constexpr uint8_t kCurrentPacketVersionUnseenTimes = 1;
44 #define UNSEEN_TIMES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/unseen_times_%u"
45
46 constexpr uint8_t kCurrentPacketVersionImmovableKinds = 2;
47 #define NODE_IMMOVABLE_KINDS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/node_immovable_kinds_%u"
48 #define TRIANGLE_IMMOVABLE_KINDS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/triangle_immovable_kinds_%u"
49
50 constexpr uint8_t kCurrentPacketVersionImmovables = 2;
51 #define NODE_IMMOVABLES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/node_immovables_%u"
52 #define TRIANGLE_IMMOVABLES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/triangle_immovables_%u"
53
54 constexpr uint8_t kCurrentPacketVersionRoads = 2;
55 #define ROADS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/roads_%u"
56
57 constexpr uint8_t kCurrentPacketVersionTerrains = 2;
58 #define TERRAINS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/terrains_%u"
59
60 constexpr uint8_t kCurrentPacketVersionOwners = 0;
61 #define OWNERS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/owners_%u"
62
63 constexpr uint8_t kCurrentPacketVersionSurveys = 2;
64 #define SURVEYS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/surveys_%u"
65
66 constexpr uint8_t kCurrentPacketVersionSurveyAmounts = 2;
67 #define SURVEY_AMOUNTS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/survey_amounts_%u"
68
69 constexpr uint8_t kCurrentPacketVersionSurveyTimes = 1;
70 #define SURVEY_TIMES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/survey_times_%u"
71
72 constexpr uint8_t kCurrentPacketVersionHidden = 1;
73 #define HIDDEN_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/hidden_%u"
74
75 constexpr uint8_t kCurrentPacketVersionVision = 1;
76 #define VISION_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/vision_%u"
77
78 constexpr uint8_t kCurrentPacketVersionBorder = 1;
79 #define BORDER_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/border_%u"
80
81 #define FILENAME_SIZE 64
82
83 enum {
84 UNSEEN_NONE = 0,
85 UNSEEN_TRIBEORWORLD = 1,
86 UNSEEN_FLAG = 2,
87 UNSEEN_BUILDING = 3,
88 UNSEEN_PORTDOCK = 4
89 };
90
91 // The map is traversed by row and column. In each step we process of one map
92 // field (which is 1 node, 2 triangles and 3 edges that are stored together).
93 // For this processing we need to keep track of 4 nodes:
94 // * f: the node of the processed field
95 // * bl: the bottom left neighbour of f
96 // * br: the bottom right neighbour of f
97 // * r: the right neighbour of f
98 //
99 // The layout of the processing region is as follows:
100 //
101 // f------ r
102 // / \ /
103 // / \ /
104 // / D \ R /
105 // / \ /
106 // / \ /
107 // bl------br
108
109 struct MapObjectData {
MapObjectDataWidelands::MapObjectData110 MapObjectData() : map_object_descr(nullptr) {
111 }
112 const MapObjectDescr* map_object_descr;
113 ConstructionsiteInformation csi;
114 };
115
116 namespace {
117 #define OPEN_INPUT_FILE(filetype, file, filename, filename_template, version) \
118 char(filename)[FILENAME_SIZE]; \
119 snprintf(filename, sizeof(filename), filename_template, plnum, version); \
120 filetype file; \
121 try { \
122 (file).open(fs, filename); \
123 } catch (const FileError&) { \
124 throw GameDataError("MapPlayersViewPacket::read: player %u:Could not open " \
125 "\"%s\" for reading. This file should exist when \"%s\" exists", \
126 static_cast<unsigned int>(plnum), filename, unseen_times_filename); \
127 }
128
129 // Try to find the file with newest fitting version number
130 #define OPEN_INPUT_FILE_NEW_VERSION(filetype, file, filename, fileversion, filename_template, \
131 version) \
132 uint8_t fileversion = version; \
133 filetype file; \
134 char(filename)[FILENAME_SIZE]; \
135 for (;; --fileversion) { \
136 snprintf(filename, sizeof(filename), filename_template, plnum, fileversion); \
137 try { \
138 (file).open(fs, filename); \
139 break; \
140 } catch (...) { \
141 if (fileversion == 0) \
142 throw GameDataError("MapPlayersViewPacket::read: player %u:Could not open " \
143 "\"%s\" for reading. This file should exist when \"%s\" exists", \
144 static_cast<unsigned int>(plnum), filename, \
145 unseen_times_filename); \
146 } \
147 }
148
149 // Using this macro, if no file exists, fileversion will be set to -1
150 #define OPEN_INPUT_FILE_NEW_VERSION_SILENT(filetype, file, filename, fileversion, file_templ, v) \
151 int8_t fileversion = v; \
152 filetype file; \
153 char(filename)[FILENAME_SIZE]; \
154 for (; fileversion >= -1; --fileversion) { \
155 snprintf(filename, sizeof(filename), file_templ, plnum, fileversion); \
156 try { \
157 (file).open(fs, filename); \
158 break; \
159 } catch (...) { \
160 } \
161 }
162
163 #define CHECK_TRAILING_BYTES(file, filename) \
164 if (!(file).end_of_file()) \
165 throw GameDataError("MapPlayersViewPacket::read: player %u:" \
166 "Found %" PRIuS " trailing bytes in \"%s\"", \
167 static_cast<unsigned int>(plnum), \
168 static_cast<size_t>((file).get_size() - (file).get_pos()), filename);
169
170 // Errors for the Read* functions.
171 struct TribeImmovableNonexistent : public FileRead::DataError {
TribeImmovableNonexistentWidelands::__anon3db69d170211::TribeImmovableNonexistent172 explicit TribeImmovableNonexistent(const std::string& Name)
173 : DataError("immovable type \"%s\" does not seem to be a tribe immovable", Name.c_str()),
174 name(Name) {
175 }
176
177 std::string name;
178 };
179 struct WorldImmovableNonexistent : public FileRead::DataError {
WorldImmovableNonexistentWidelands::__anon3db69d170211::WorldImmovableNonexistent180 explicit WorldImmovableNonexistent(char const* const Name)
181 : DataError("world does not define immovable type \"%s\"", Name), name(Name) {
182 }
183 char const* const name;
184 };
185 struct BuildingNonexistent : public FileRead::DataError {
BuildingNonexistentWidelands::__anon3db69d170211::BuildingNonexistent186 explicit BuildingNonexistent(char const* const Name)
187 : DataError("tribes do not define building type \"%s\"", Name), name(Name) {
188 }
189 char const* const name;
190 };
191
192 // reads an immovable depending on whether it is a tribe or world immovable
read_immovable_type(StreamRead * fr,const EditorGameBase & egbase,const TribesLegacyLookupTable & tribes_lookup_table,const WorldLegacyLookupTable & world_lookup_table)193 const ImmovableDescr& read_immovable_type(StreamRead* fr,
194 const EditorGameBase& egbase,
195 const TribesLegacyLookupTable& tribes_lookup_table,
196 const WorldLegacyLookupTable& world_lookup_table) {
197 uint8_t owner = fr->unsigned_8();
198 char const* const name = fr->c_string();
199 if (owner == static_cast<uint8_t>(MapObjectDescr::OwnerType::kWorld)) {
200 DescriptionIndex const index =
201 egbase.world().get_immovable_index(world_lookup_table.lookup_immovable(name));
202 if (index == Widelands::INVALID_INDEX) {
203 throw WorldImmovableNonexistent(name);
204 }
205 return *egbase.world().get_immovable_descr(index);
206 } else {
207 assert(owner == static_cast<uint8_t>(MapObjectDescr::OwnerType::kTribe));
208 DescriptionIndex const index =
209 egbase.tribes().immovable_index(tribes_lookup_table.lookup_immovable(name));
210 if (index == Widelands::INVALID_INDEX) {
211 throw TribeImmovableNonexistent(name);
212 }
213 return *egbase.tribes().get_immovable_descr(index);
214 }
215 }
216
217 // Reads a c_string and interprets it as the name of an immovable type.
218 //
219 // \returns a reference to the building type description.
220 //
221 // \throws Building_Nonexistent if there is no building type with that name
read_building_type(StreamRead * fr,const EditorGameBase & egbase)222 const BuildingDescr& read_building_type(StreamRead* fr, const EditorGameBase& egbase) {
223 char const* const name = fr->c_string();
224 DescriptionIndex const index = egbase.tribes().building_index(name);
225 if (!egbase.tribes().building_exists(index)) {
226 throw BuildingNonexistent(name);
227 }
228 return *egbase.tribes().get_building_descr(index);
229 }
230
231 // Encode a Immovable_Type into 'wr'.
write_immovable_type(StreamWrite * wr,const ImmovableDescr & immovable)232 void write_immovable_type(StreamWrite* wr, const ImmovableDescr& immovable) {
233 wr->unsigned_8(static_cast<uint8_t>(immovable.owner_type()));
234 wr->string(immovable.name());
235 }
236
237 // Encode a Building_Type into 'wr'.
write_building_type(StreamWrite * wr,const BuildingDescr & building)238 void write_building_type(StreamWrite* wr, const BuildingDescr& building) {
239 wr->string(building.name());
240 }
241
242 } // namespace
243
244 inline static MapObjectData
read_unseen_immovable(const EditorGameBase & egbase,uint8_t & immovable_kind,FileRead & immovables_file,const TribesLegacyLookupTable & tribes_lookup_table,const WorldLegacyLookupTable & world_lookup_table,uint8_t & version)245 read_unseen_immovable(const EditorGameBase& egbase,
246 uint8_t& immovable_kind,
247 FileRead& immovables_file,
248 const TribesLegacyLookupTable& tribes_lookup_table,
249 const WorldLegacyLookupTable& world_lookup_table,
250 uint8_t& version) {
251 MapObjectData m;
252 try {
253 switch (immovable_kind) {
254 case UNSEEN_NONE: // The player sees no immovable.
255 m.map_object_descr = nullptr;
256 break;
257 case UNSEEN_TRIBEORWORLD: // The player sees a tribe or world immovable.
258 m.map_object_descr =
259 &read_immovable_type(&immovables_file, egbase, tribes_lookup_table, world_lookup_table);
260 break;
261 case UNSEEN_FLAG: // The player sees a flag.
262 m.map_object_descr = &g_flag_descr;
263 break;
264 case UNSEEN_BUILDING: // The player sees a building.
265 m.map_object_descr = &read_building_type(&immovables_file, egbase);
266 if (version == kCurrentPacketVersionImmovables) {
267 // Read data from immovables file
268 if (immovables_file.unsigned_8() == 1) { // the building is a constructionsite
269 m.csi.becomes = &read_building_type(&immovables_file, egbase);
270 if (immovables_file.unsigned_8() == 1) {
271 m.csi.was = &read_building_type(&immovables_file, egbase);
272 }
273 m.csi.totaltime = immovables_file.unsigned_32();
274 m.csi.completedtime = immovables_file.unsigned_32();
275 }
276 } else {
277 throw UnhandledVersionError(
278 "MapPlayersViewPacket", version, kCurrentPacketVersionImmovables);
279 }
280 break;
281 case UNSEEN_PORTDOCK: // The player sees a port dock
282 m.map_object_descr = &g_portdock_descr;
283 break;
284 default:
285 throw GameDataError("Unknown immovable-kind type %d", immovable_kind);
286 }
287 } catch (const WException& e) {
288 throw GameDataError("unseen immovable: %s", e.what());
289 }
290 return m;
291 }
292
read(FileSystem & fs,EditorGameBase & egbase,bool const skip,MapObjectLoader &,const TribesLegacyLookupTable & tribes_lookup_table,const WorldLegacyLookupTable & world_lookup_table)293 void MapPlayersViewPacket::read(FileSystem& fs,
294 EditorGameBase& egbase,
295 bool const skip,
296 MapObjectLoader&,
297 const TribesLegacyLookupTable& tribes_lookup_table,
298 const WorldLegacyLookupTable& world_lookup_table)
299
300 {
301 if (skip) {
302 return;
303 }
304
305 const Map& map = egbase.map();
306 const uint16_t mapwidth = map.get_width();
307 const uint16_t mapheight = map.get_height();
308 Field& first_field = map[0];
309 const PlayerNumber nr_players = map.get_nrplayers();
310 iterate_players_existing(plnum, nr_players, egbase, player) {
311 Player::Field* const player_fields = player->fields_.get();
312 uint32_t const gametime = egbase.get_gametime();
313
314 char unseen_times_filename[FILENAME_SIZE];
315 snprintf(unseen_times_filename, sizeof(unseen_times_filename), UNSEEN_TIMES_FILENAME_TEMPLATE,
316 static_cast<unsigned int>(plnum), kCurrentPacketVersionUnseenTimes);
317 FileRead unseen_times_file;
318 struct NotFound {};
319
320 if (!unseen_times_file.try_open(fs, unseen_times_filename)) {
321 log("MapPlayersViewPacket::read: WARNING: Could not open "
322 "\"%s\" for reading. Assuming that the game is from an old "
323 "version without player point of view. Will give player %u "
324 "knowledge of unseen nodes, edges and triangles (but not "
325 "resources).",
326 unseen_times_filename, static_cast<unsigned int>(plnum));
327
328 for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
329 ++first_in_row.y, first_in_row.field += mapwidth) {
330 FCoords r = first_in_row, br = map.bl_n(r);
331 MapIndex r_index = r.field - &first_field;
332 MapIndex br_index = br.field - &first_field;
333 Player::Field* r_player_field = player_fields + r_index;
334 Player::Field* br_player_field = player_fields + br_index;
335 Vision r_vision = r_player_field->vision;
336 Vision br_vision = br_player_field->vision;
337 do {
338 const FCoords f = r;
339 Player::Field& f_player_field = *r_player_field;
340 const Vision f_vision = r_vision, bl_vision = br_vision;
341 move_r(mapwidth, r, r_index);
342 move_r(mapwidth, br, br_index);
343 r_player_field = player_fields + r_index;
344 br_player_field = player_fields + br_index;
345 r_vision = r_player_field->vision;
346 br_vision = br_player_field->vision;
347
348 f_player_field.time_node_last_unseen = gametime;
349
350 if (f_vision) { // node
351 // owner
352 f_player_field.owner = f.field->get_owned_by();
353 assert(f_player_field.owner < 0x20);
354
355 // map_object_descr
356 const MapObjectDescr* map_object_descr;
357 if (const BaseImmovable* base_immovable = f.field->get_immovable()) {
358 map_object_descr = &base_immovable->descr();
359 if (Road::is_road_descr(map_object_descr) ||
360 Waterway::is_waterway_descr(map_object_descr)) {
361 map_object_descr = nullptr;
362 } else if (upcast(Building const, building, base_immovable)) {
363 if (building->get_position() != f) {
364 // TODO(unknown): This is not the building's main position
365 // so we can not see it. But it should be
366 // possible to see it from a distance somehow.
367 map_object_descr = nullptr;
368 }
369 }
370 } else {
371 map_object_descr = nullptr;
372 }
373 f_player_field.map_object_descr = map_object_descr;
374 }
375
376 { // triangles
377 // Must be initialized because the rendering code is
378 // accessing it even for triangles that the player does not
379 // see (it is the darkening that actually hides the ground
380 // from the player).
381 Field::Terrains terrains;
382 terrains.d = terrains.r = 0;
383
384 if (f_vision | bl_vision | br_vision) {
385 terrains.d = f.field->terrain_d();
386 }
387 if (f_vision | br_vision | r_vision) {
388 terrains.r = f.field->terrain_r();
389 }
390 f_player_field.terrains = terrains;
391 }
392
393 { // edges
394 if (f_vision | bl_vision) {
395 f_player_field.r_sw = f.field->get_road(WALK_SW);
396 }
397 if (f_vision | br_vision) {
398 f_player_field.r_se = f.field->get_road(WALK_SE);
399 }
400 if (f_vision | r_vision) {
401 f_player_field.r_e = f.field->get_road(WALK_E);
402 }
403 }
404
405 // The player is not given information about resources that he
406 // has discovered, because there is no such information in old
407 // savegames, except for the resource indicators that are
408 // merely immovables with limited lifetime.
409 } while (r.x);
410 }
411 return;
412 }
413
414 // Verify the vision values
415 FileRead vision_file;
416 bool have_vision = false;
417
418 try {
419 char fname[FILENAME_SIZE];
420 snprintf(fname, sizeof(fname), VISION_FILENAME_TEMPLATE, static_cast<unsigned int>(plnum),
421 kCurrentPacketVersionVision);
422 vision_file.open(fs, fname);
423 have_vision = true;
424 } catch (...) {
425 }
426
427 if (have_vision) {
428 for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
429 ++first_in_row.y, first_in_row.field += mapwidth) {
430 FCoords r = first_in_row;
431 MapIndex r_index = r.field - &first_field;
432 Player::Field* r_player_field = player_fields + r_index;
433 do {
434 Player::Field& f_player_field = *r_player_field;
435 move_r(mapwidth, r, r_index);
436 r_player_field = player_fields + r_index;
437
438 uint32_t file_vision = vision_file.unsigned_32();
439
440 // There used to be a check here that the calculated, and the
441 // loaded vision were the same. I removed this check, because
442 // scripting could have given the player a permanent view of
443 // this field. That's why we save this stuff in the first place!
444 if (file_vision != f_player_field.vision) {
445 f_player_field.vision = file_vision;
446 }
447 } while (r.x);
448 }
449
450 log("Vision check successful for player %u\n", static_cast<unsigned int>(plnum));
451 }
452
453 // Read the player's knowledge about all fields
454 OPEN_INPUT_FILE_NEW_VERSION(FileRead, node_immovable_kinds_file,
455 node_immovable_kinds_filename, node_immovable_kinds_file_version,
456 NODE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
457 kCurrentPacketVersionImmovableKinds)
458
459 OPEN_INPUT_FILE_NEW_VERSION(FileRead, node_immovables_file, node_immovables_filename,
460 node_immovables_file_version, NODE_IMMOVABLES_FILENAME_TEMPLATE,
461 kCurrentPacketVersionImmovables)
462
463 OPEN_INPUT_FILE_NEW_VERSION(FileRead, roads_file, roads_filename, road_file_version,
464 ROADS_FILENAME_TEMPLATE, kCurrentPacketVersionRoads)
465
466 OPEN_INPUT_FILE_NEW_VERSION(FileRead, terrains_file, terrains_filename, terrains_file_version,
467 TERRAINS_FILENAME_TEMPLATE, kCurrentPacketVersionTerrains)
468
469 OPEN_INPUT_FILE_NEW_VERSION(
470 FileRead, triangle_immovable_kinds_file, triangle_immovable_kinds_filename,
471 triangle_immovable_kinds_file_version, TRIANGLE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
472 kCurrentPacketVersionImmovableKinds)
473
474 OPEN_INPUT_FILE_NEW_VERSION(FileRead, triangle_immovables_file, triangle_immovables_filename,
475 triangle_immovables_file_version,
476 TRIANGLE_IMMOVABLES_FILENAME_TEMPLATE,
477 kCurrentPacketVersionImmovables)
478
479 OPEN_INPUT_FILE(FileRead, owners_file, owners_filename, OWNERS_FILENAME_TEMPLATE,
480 kCurrentPacketVersionOwners)
481
482 OPEN_INPUT_FILE_NEW_VERSION(FileRead, surveys_file, surveys_filename, surveys_file_version,
483 SURVEYS_FILENAME_TEMPLATE, kCurrentPacketVersionSurveys)
484
485 OPEN_INPUT_FILE_NEW_VERSION(FileRead, survey_amounts_file, survey_amounts_filename,
486 survey_amounts_file_version, SURVEY_AMOUNTS_FILENAME_TEMPLATE,
487 kCurrentPacketVersionSurveyAmounts)
488
489 OPEN_INPUT_FILE(FileRead, survey_times_file, survey_times_filename,
490 SURVEY_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyTimes)
491
492 OPEN_INPUT_FILE_NEW_VERSION(FileRead, border_file, border_filename, border_file_version,
493 BORDER_FILENAME_TEMPLATE, kCurrentPacketVersionBorder)
494
495 OPEN_INPUT_FILE_NEW_VERSION_SILENT(FileRead, hidden_file, hidden_filename,
496 hidden_file_version, HIDDEN_FILENAME_TEMPLATE,
497 kCurrentPacketVersionHidden)
498
499 for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
500 ++first_in_row.y, first_in_row.field += mapwidth) {
501 FCoords r = first_in_row, br = map.bl_n(r);
502 MapIndex r_index = r.field - &first_field;
503 MapIndex br_index = br.field - &first_field;
504 Player::Field* r_player_field = player_fields + r_index;
505 Player::Field* br_player_field = player_fields + br_index;
506 Vision r_vision = r_player_field->vision;
507 Vision br_vision = br_player_field->vision;
508 bool r_everseen = r_vision, r_seen = 1 < r_vision;
509 bool br_everseen = br_vision, br_seen = 1 < br_vision;
510 do {
511 const FCoords f = r;
512 Player::Field& f_player_field = *r_player_field;
513 const Vision f_vision = r_vision;
514 const bool f_everseen = r_everseen;
515 const bool bl_everseen = br_everseen;
516 const bool f_seen = r_seen;
517 const bool bl_seen = br_seen;
518 move_r(mapwidth, r, r_index);
519 move_r(mapwidth, br, br_index);
520 r_player_field = player_fields + r_index;
521 br_player_field = player_fields + br_index;
522 r_vision = r_player_field->vision;
523 br_vision = br_player_field->vision;
524 r_everseen = r_vision;
525 r_seen = 1 < r_vision;
526 br_everseen = br_vision;
527 br_seen = 1 < br_vision;
528
529 // Store the player's view of ownership in these
530 // temporary variables and save it in the player when set.
531 PlayerNumber owner = 0;
532
533 switch (f_vision) { // owner and map_object_descr
534 case 0:
535 // The player has never seen this node, so he has no
536 // information about it. Neither should he be informed about
537 // it now.
538 break;
539 case 1: {
540 // The player has seen the node but does not see it now. Load
541 // his information about the node from file.
542 try {
543 f_player_field.time_node_last_unseen = unseen_times_file.unsigned_32();
544 } catch (const FileRead::FileBoundaryExceeded&) {
545 throw GameDataError("MapPlayersViewPacket::read: player %u: in "
546 "\"%s\":%" PRIuS ": node (%i, %i): unexpected end of file "
547 "while reading time_node_last_unseen",
548 static_cast<unsigned int>(plnum), unseen_times_filename,
549 static_cast<size_t>(unseen_times_file.get_pos() - 4), f.x,
550 f.y);
551 }
552
553 try {
554 owner = owners_file.unsigned_8();
555 } catch (const FileRead::FileBoundaryExceeded&) {
556 throw GameDataError("MapPlayersViewPacket::read: player %u: in "
557 "\"%s\":%" PRIuS ": node (%i, %i): unexpected end of file "
558 "while reading owner",
559 static_cast<unsigned int>(plnum), unseen_times_filename,
560 static_cast<size_t>(unseen_times_file.get_pos() - 1), f.x,
561 f.y);
562 }
563 if (nr_players < owner) {
564 throw GameDataError("MapPlayersViewPacket::read: player %u: in "
565 "\"%s\":%" PRIuS " & 0xf: node (%i, %i): Player thinks that "
566 "this node is owned by player %u, but there are only %u "
567 "players",
568 static_cast<unsigned int>(plnum), owners_filename,
569 static_cast<size_t>(owners_file.get_pos() - 1), f.x, f.y,
570 owner, nr_players);
571 }
572 uint8_t imm_kind = 0;
573 if (node_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
574 imm_kind = node_immovable_kinds_file.unsigned_8();
575 } else {
576 throw UnhandledVersionError("MapPlayersViewPacket - Node Immovable kinds",
577 node_immovable_kinds_file_version,
578 kCurrentPacketVersionImmovableKinds);
579 }
580 MapObjectData mod =
581 read_unseen_immovable(egbase, imm_kind, node_immovables_file, tribes_lookup_table,
582 world_lookup_table, node_immovables_file_version);
583 f_player_field.map_object_descr = mod.map_object_descr;
584 f_player_field.constructionsite = mod.csi;
585
586 // Read in whether this field had a border the last time it was seen
587 if (border_file_version == kCurrentPacketVersionBorder) {
588 uint8_t borders = border_file.unsigned_8();
589 f_player_field.border = borders & 1;
590 f_player_field.border_r = borders & 2;
591 f_player_field.border_br = borders & 4;
592 f_player_field.border_bl = borders & 8;
593 } else {
594 throw UnhandledVersionError("MapPlayersViewPacket - Border file",
595 border_file_version, kCurrentPacketVersionBorder);
596 }
597 break;
598 }
599 default:
600 // The player currently sees the node. Therefore his
601 // information about the node has not been saved. Fill in the
602 // information from the game state.
603
604 // owner
605 owner = f.field->get_owned_by();
606 assert(owner <= nr_players);
607
608 // map_object_descr
609 const MapObjectDescr* map_object_descr;
610 if (const BaseImmovable* base_immovable = f.field->get_immovable()) {
611 map_object_descr = &base_immovable->descr();
612 if (Road::is_road_descr(map_object_descr) ||
613 Waterway::is_waterway_descr(map_object_descr)) {
614 map_object_descr = nullptr;
615 } else if (upcast(Building const, building, base_immovable)) {
616 if (building->get_position() != f) {
617 // TODO(unknown): This is not the building's main position so
618 // we can not see it. But it should be possible
619 // to see it from a distance somehow.
620 map_object_descr = nullptr;
621 }
622 }
623 } else {
624 map_object_descr = nullptr;
625 }
626 f_player_field.map_object_descr = map_object_descr;
627 break;
628 }
629
630 // triangles
631 if (f_seen | bl_seen | br_seen) {
632 // The player currently sees the D triangle. Therefore his
633 // information about the triangle has not been saved. Fill in
634 // the information from the game state.
635 f_player_field.terrains.d = f.field->terrain_d();
636 } else if (f_everseen | bl_everseen | br_everseen) {
637 // The player has seen the D triangle but does not see it now.
638 // Load his information about the triangle from file.
639 if (terrains_file_version == kCurrentPacketVersionTerrains) {
640 f_player_field.terrains.d = terrains_file.unsigned_8();
641 } else {
642 throw UnhandledVersionError("MapPlayersViewPacket - Terrains",
643 terrains_file_version, kCurrentPacketVersionTerrains);
644 }
645 uint8_t im_kind = 0;
646 if (triangle_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
647 im_kind = triangle_immovable_kinds_file.unsigned_8();
648 } else {
649 throw UnhandledVersionError("MapPlayersViewPacket - Triangle Immovable kinds",
650 triangle_immovable_kinds_file_version,
651 kCurrentPacketVersionImmovableKinds);
652 }
653 // We read and ignore the immovable information on the D
654 // triangle. This was done because there were vague plans of
655 // suporting immovables on the triangles instead as on the
656 // nodes.
657 // TODO(sirver): Remove this logic the next time we break
658 // savegame compatibility.
659 read_unseen_immovable(egbase, im_kind, triangle_immovables_file, tribes_lookup_table,
660 world_lookup_table, triangle_immovables_file_version);
661 }
662 if (f_seen | br_seen | r_seen) {
663 // The player currently sees the R triangle. Therefore his
664 // information about the triangle has not been saved. Fill in
665 // the information from the game state.
666 f_player_field.terrains.r = f.field->terrain_r();
667 } else if (f_everseen | br_everseen | r_everseen) {
668 // The player has seen the R triangle but does not see it now.
669 // Load his information about the triangle from file.
670 if (terrains_file_version == kCurrentPacketVersionTerrains) {
671 f_player_field.terrains.r = terrains_file.unsigned_8();
672 } else {
673 throw UnhandledVersionError("MapPlayersViewPacket - Terrains",
674 terrains_file_version, kCurrentPacketVersionTerrains);
675 }
676 uint8_t im_kind = 0;
677 if (triangle_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
678 im_kind = triangle_immovable_kinds_file.unsigned_8();
679 } else {
680 throw UnhandledVersionError("MapPlayersViewPacket - Triangle Immovable kinds",
681 triangle_immovable_kinds_file_version,
682 kCurrentPacketVersionImmovableKinds);
683 }
684 // We read and ignore the immovable information on the D
685 // triangle. This was done because there were vague plans of
686 // suporting immovables on the triangles instead as on the
687 // nodes.
688 read_unseen_immovable(egbase, im_kind, triangle_immovables_file, tribes_lookup_table,
689 world_lookup_table, triangle_immovables_file_version);
690 }
691
692 { // edges
693 if (f_seen | bl_seen) {
694 f_player_field.r_sw = f.field->get_road(WALK_SW);
695 } else if (f_everseen | bl_everseen) {
696 // The player has seen the SouthWest edge but does not see
697 // it now. Load his information about this edge from file.
698 if (road_file_version == kCurrentPacketVersionRoads) {
699 f_player_field.r_sw = static_cast<RoadSegment>(roads_file.unsigned_8());
700 } else {
701 throw UnhandledVersionError("MapPlayersViewPacket - Road file",
702 road_file_version, kCurrentPacketVersionRoads);
703 }
704 }
705 if (f_seen | br_seen) {
706 f_player_field.r_se = f.field->get_road(WALK_SE);
707 } else if (f_everseen | br_everseen) {
708 // The player has seen the SouthEast edge but does not see
709 // it now. Load his information about this edge from file.
710 if (road_file_version == kCurrentPacketVersionRoads) {
711 f_player_field.r_se = static_cast<RoadSegment>(roads_file.unsigned_8());
712 } else {
713 throw UnhandledVersionError("MapPlayersViewPacket - Road file",
714 road_file_version, kCurrentPacketVersionRoads);
715 }
716 }
717 if (f_seen | r_seen) {
718 f_player_field.r_e = f.field->get_road(WALK_E);
719 } else if (f_everseen | r_everseen) {
720 // The player has seen the East edge but does not see
721 // it now. Load his information about this edge from file.
722 if (road_file_version == kCurrentPacketVersionRoads) {
723 f_player_field.r_e = static_cast<RoadSegment>(roads_file.unsigned_8());
724 } else {
725 throw UnhandledVersionError("MapPlayersViewPacket - Road file",
726 road_file_version, kCurrentPacketVersionRoads);
727 }
728 }
729 }
730
731 // Now save this information in the player field.
732 f_player_field.owner = owner;
733
734 // geologic survey
735 try {
736 bool survey = false;
737 if (surveys_file_version == kCurrentPacketVersionSurveys) {
738 survey = (f_everseen & bl_everseen & br_everseen) && surveys_file.unsigned_8();
739 } else {
740 throw UnhandledVersionError("MapPlayersViewPacket - Surveys file",
741 surveys_file_version, kCurrentPacketVersionSurveys);
742 }
743 if (survey) {
744 if (survey_amounts_file_version == kCurrentPacketVersionSurveyAmounts) {
745 f_player_field.resource_amounts.d = survey_amounts_file.unsigned_8();
746 } else {
747 throw UnhandledVersionError("MapPlayersViewPacket - Survey amounts",
748 survey_amounts_file_version,
749 kCurrentPacketVersionSurveyAmounts);
750 }
751 try {
752 f_player_field
753 .time_triangle_last_surveyed[static_cast<int>(TriangleIndex::D)] =
754 survey_times_file.unsigned_32();
755 } catch (const FileRead::FileBoundaryExceeded&) {
756 throw GameDataError(
757 "MapPlayersViewPacket::read: player %u: in "
758 "\"%s\":%" PRIuS ": node (%i, %i) t = D: unexpected end of "
759 "file while reading time_triangle_last_surveyed",
760 static_cast<unsigned int>(plnum), survey_times_filename,
761 static_cast<size_t>(survey_times_file.get_pos() - 4), f.x, f.y);
762 }
763 }
764 } catch (const FileRead::FileBoundaryExceeded&) {
765 throw GameDataError("MapPlayersViewPacket::read: player %u: in \"%s\": "
766 "node (%i, %i) t = D: unexpected end of file while reading "
767 "survey bit",
768 static_cast<unsigned int>(plnum), surveys_filename, f.x, f.y);
769 }
770 try {
771 bool survey = false;
772 if (surveys_file_version == kCurrentPacketVersionSurveys) {
773 survey = (f_everseen & br_everseen & r_everseen) && surveys_file.unsigned_8();
774 } else {
775 throw UnhandledVersionError("MapPlayersViewPacket - Surveys file",
776 surveys_file_version, kCurrentPacketVersionSurveys);
777 }
778 if (survey) {
779 if (survey_amounts_file_version == kCurrentPacketVersionSurveyAmounts) {
780 f_player_field.resource_amounts.r = survey_amounts_file.unsigned_8();
781 } else {
782 throw UnhandledVersionError("MapPlayersViewPacket - Survey amounts",
783 survey_amounts_file_version,
784 kCurrentPacketVersionSurveyAmounts);
785 }
786 try {
787 f_player_field
788 .time_triangle_last_surveyed[static_cast<int>(TriangleIndex::R)] =
789 survey_times_file.unsigned_32();
790 } catch (const FileRead::FileBoundaryExceeded&) {
791 throw GameDataError(
792 "MapPlayersViewPacket::read: player %u: in "
793 "\"%s\":%" PRIuS ": node (%i, %i) t = R: unexpected end of "
794 "file while reading time_triangle_last_surveyed",
795 static_cast<unsigned int>(plnum), survey_times_filename,
796 static_cast<size_t>(survey_times_file.get_pos() - 4), f.x, f.y);
797 }
798 }
799 } catch (const FileRead::FileBoundaryExceeded&) {
800 throw GameDataError("MapPlayersViewPacket::read: player %u: in \"%s\": "
801 "node (%i, %i) t = R: unexpected end of file while reading "
802 "survey bit",
803 static_cast<unsigned int>(plnum), surveys_filename, f.x, f.y);
804 }
805 } while (r.x);
806 }
807
808 // Read the number of explicitly hidden fields and then loop through them
809 if (hidden_file_version == kCurrentPacketVersionHidden) {
810 const uint32_t no_of_hidden_fields = hidden_file.unsigned_32();
811 for (uint32_t i = 0; i < no_of_hidden_fields; ++i) {
812 player->hidden_fields_.insert(
813 std::make_pair(hidden_file.unsigned_32(), hidden_file.unsigned_16()));
814 }
815 } else if (hidden_file_version < 0) {
816 // TODO(GunChleoc): Savegame compatibility - remove after Build 20
817 log("MapPlayersViewPacket - No hidden fields to read for Player %d - probably an old save "
818 "file\n",
819 static_cast<unsigned int>(plnum));
820 } else {
821 throw UnhandledVersionError("MapPlayersViewPacket - Hidden fields file",
822 hidden_file_version, kCurrentPacketVersionHidden);
823 }
824
825 CHECK_TRAILING_BYTES(unseen_times_file, unseen_times_filename)
826 CHECK_TRAILING_BYTES(node_immovable_kinds_file, node_immovable_kinds_filename)
827 CHECK_TRAILING_BYTES(node_immovables_file, node_immovables_filename)
828 CHECK_TRAILING_BYTES(roads_file, roads_filename)
829 CHECK_TRAILING_BYTES(terrains_file, terrains_filename)
830 CHECK_TRAILING_BYTES(triangle_immovable_kinds_file, triangle_immovable_kinds_filename)
831 CHECK_TRAILING_BYTES(triangle_immovables_file, triangle_immovables_filename)
832 CHECK_TRAILING_BYTES(owners_file, owners_filename)
833 CHECK_TRAILING_BYTES(surveys_file, surveys_filename)
834 CHECK_TRAILING_BYTES(survey_amounts_file, survey_amounts_filename)
835 CHECK_TRAILING_BYTES(survey_times_file, survey_times_filename)
836 CHECK_TRAILING_BYTES(border_file, border_filename)
837 CHECK_TRAILING_BYTES(hidden_file, hidden_filename)
838 }
839 }
840
write_unseen_immovable(MapObjectData const * map_object_data,FileWrite & immovable_kinds_file,FileWrite & immovables_file)841 inline static void write_unseen_immovable(MapObjectData const* map_object_data,
842 FileWrite& immovable_kinds_file,
843 FileWrite& immovables_file) {
844 MapObjectDescr const* const map_object_descr = map_object_data->map_object_descr;
845 const ConstructionsiteInformation& csi = map_object_data->csi;
846 assert(!Road::is_road_descr(map_object_descr) && !Waterway::is_waterway_descr(map_object_descr));
847 uint8_t immovable_kind = 255;
848
849 if (!map_object_descr) {
850 immovable_kind = UNSEEN_NONE;
851 } else if (upcast(ImmovableDescr const, immovable_descr, map_object_descr)) {
852 immovable_kind = UNSEEN_TRIBEORWORLD;
853 write_immovable_type(&immovables_file, *immovable_descr);
854 } else if (map_object_descr->type() == MapObjectType::FLAG) {
855 immovable_kind = UNSEEN_FLAG;
856 } else if (upcast(BuildingDescr const, building_descr, map_object_descr)) {
857 immovable_kind = UNSEEN_BUILDING;
858 write_building_type(&immovables_file, *building_descr);
859 if (!csi.becomes) {
860 immovables_file.unsigned_8(0);
861 } else {
862 // the building is a constructionsite
863 immovables_file.unsigned_8(1);
864 write_building_type(&immovables_file, *csi.becomes);
865 if (!csi.was) {
866 immovables_file.unsigned_8(0);
867 } else {
868 // constructionsite is an enhancement, therefor we write down the enhancement
869 immovables_file.unsigned_8(1);
870 write_building_type(&immovables_file, *csi.was);
871 }
872 immovables_file.unsigned_32(csi.totaltime);
873 immovables_file.unsigned_32(csi.completedtime);
874 }
875 } else if (map_object_descr->type() == MapObjectType::PORTDOCK) {
876 immovable_kind = UNSEEN_PORTDOCK;
877 } else {
878 // We should never get here.. output some information about the situation.
879 log("\nwidelands_map_players_view_data_packet.cc::write_unseen_immovable(): ");
880 log("%s %s was not expected.\n", typeid(*map_object_descr).name(),
881 map_object_descr->name().c_str());
882 NEVER_HERE();
883 }
884 immovable_kinds_file.unsigned_8(immovable_kind);
885 }
886
887 #define WRITE(file, filename_template, version) \
888 snprintf(filename, sizeof(filename), filename_template, plnum, version); \
889 (file).write(fs, filename);
890
write(FileSystem & fs,EditorGameBase & egbase,MapObjectSaver &)891 void MapPlayersViewPacket::write(FileSystem& fs, EditorGameBase& egbase, MapObjectSaver&) {
892 fs.ensure_directory_exists("player");
893 const Map& map = egbase.map();
894 const uint16_t mapwidth = map.get_width();
895 const uint16_t mapheight = map.get_height();
896 // TODO(unknown): make first_field const when FCoords has been templatized so it can
897 // have "const Field * field;"
898 Field& first_field = map[0];
899 const PlayerNumber nr_players = map.get_nrplayers();
900 iterate_players_existing_const(
901 plnum, nr_players, egbase,
902 player) if (const Player::Field* const player_fields = player->fields_.get()) {
903 FileWrite unseen_times_file;
904 FileWrite node_immovable_kinds_file;
905 FileWrite node_immovables_file;
906 FileWrite roads_file;
907 FileWrite terrains_file;
908 FileWrite triangle_immovable_kinds_file;
909 FileWrite triangle_immovables_file;
910 FileWrite owners_file;
911 FileWrite surveys_file;
912 FileWrite survey_amounts_file;
913 FileWrite survey_times_file;
914 FileWrite hidden_file;
915 FileWrite vision_file;
916 FileWrite border_file;
917 for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
918 ++first_in_row.y, first_in_row.field += mapwidth) {
919 FCoords r = first_in_row, br = map.bl_n(r);
920 MapIndex r_index = r.field - &first_field;
921 MapIndex br_index = br.field - &first_field;
922 const Player::Field* r_player_field = player_fields + r_index;
923 const Player::Field* br_player_field = player_fields + br_index;
924 Vision r_vision = r_player_field->vision;
925 Vision br_vision = br_player_field->vision;
926 bool r_everseen = r_vision, r_seen = 1 < r_vision;
927 bool br_everseen = br_vision, br_seen = 1 < br_vision;
928 do {
929 const Player::Field& f_player_field = *r_player_field;
930 const bool f_everseen = r_everseen;
931 const bool bl_everseen = br_everseen;
932 const bool f_seen = r_seen;
933 const bool bl_seen = br_seen;
934 move_r(mapwidth, r, r_index);
935 move_r(mapwidth, br, br_index);
936 r_player_field = player_fields + r_index;
937 br_player_field = player_fields + br_index;
938 r_vision = r_player_field->vision;
939 br_vision = br_player_field->vision;
940 r_everseen = r_vision;
941 r_seen = 1 < r_vision;
942 br_everseen = br_vision;
943 br_seen = 1 < br_vision;
944
945 vision_file.unsigned_32(f_player_field.vision);
946
947 if (!f_seen) {
948
949 if (f_everseen) { // node
950 unseen_times_file.unsigned_32(f_player_field.time_node_last_unseen);
951 assert(f_player_field.owner < 0x20);
952 owners_file.unsigned_8(f_player_field.owner);
953 MapObjectData mod;
954 mod.map_object_descr = f_player_field.map_object_descr;
955 mod.csi = f_player_field.constructionsite;
956 write_unseen_immovable(&mod, node_immovable_kinds_file, node_immovables_file);
957
958 // write whether this field had a border the last time it was seen
959 uint8_t borders = 0;
960 borders |= f_player_field.border;
961 borders |= f_player_field.border_r << 1;
962 borders |= f_player_field.border_br << 2;
963 borders |= f_player_field.border_bl << 3;
964 border_file.unsigned_8(borders);
965 }
966
967 // triangles
968 if
969 // the player does not see the D triangle now but has
970 // seen it
971 ((!bl_seen) & (!br_seen) & (f_everseen | bl_everseen | br_everseen)) {
972 terrains_file.unsigned_8(f_player_field.terrains.d);
973 MapObjectData mod;
974 mod.map_object_descr = nullptr;
975 write_unseen_immovable(
976 &mod, triangle_immovable_kinds_file, triangle_immovables_file);
977 }
978 if
979 // the player does not see the R triangle now but has
980 // seen it
981 ((!br_seen) & (!r_seen) & (f_everseen | br_everseen | r_everseen)) {
982 terrains_file.unsigned_8(f_player_field.terrains.r);
983 MapObjectData mod;
984 mod.map_object_descr = nullptr;
985 write_unseen_immovable(
986 &mod, triangle_immovable_kinds_file, triangle_immovables_file);
987 }
988
989 // edges
990 if ((!bl_seen) && (f_everseen || bl_everseen)) {
991 roads_file.unsigned_8(f_player_field.road_sw());
992 }
993 if ((!br_seen) && (f_everseen || br_everseen)) {
994 roads_file.unsigned_8(f_player_field.road_se());
995 }
996 if ((!r_seen) && (f_everseen || r_everseen)) {
997 roads_file.unsigned_8(f_player_field.road_e());
998 }
999 }
1000
1001 // geologic survey
1002 if (f_everseen & bl_everseen & br_everseen) {
1003 const uint32_t time_last_surveyed =
1004 f_player_field.time_triangle_last_surveyed[static_cast<int>(TriangleIndex::D)];
1005 const uint8_t has_info = time_last_surveyed != 0xffffffff;
1006 surveys_file.unsigned_8(has_info);
1007 if (has_info) {
1008 survey_amounts_file.unsigned_8(f_player_field.resource_amounts.d);
1009 survey_times_file.unsigned_32(time_last_surveyed);
1010 }
1011 }
1012 if (f_everseen & br_everseen & r_everseen) {
1013 const uint32_t time_last_surveyed =
1014 f_player_field.time_triangle_last_surveyed[static_cast<int>(TriangleIndex::R)];
1015 const uint8_t has_info = time_last_surveyed != 0xffffffff;
1016 surveys_file.unsigned_8(has_info);
1017 if (has_info) {
1018 survey_amounts_file.unsigned_8(f_player_field.resource_amounts.r);
1019 survey_times_file.unsigned_32(time_last_surveyed);
1020 }
1021 }
1022 } while (r.x);
1023 }
1024 // Write the number of explicitly hidden fields and then loop through them
1025 hidden_file.unsigned_32(player->hidden_fields_.size());
1026 for (const auto& hidden : player->hidden_fields_) {
1027 hidden_file.unsigned_32(hidden.first);
1028 hidden_file.unsigned_16(hidden.second);
1029 }
1030
1031 char filename[FILENAME_SIZE];
1032
1033 fs.ensure_directory_exists(
1034 (boost::format(PLAYERDIRNAME_TEMPLATE) % static_cast<unsigned int>(plnum)).str());
1035 fs.ensure_directory_exists(
1036 (boost::format(DIRNAME_TEMPLATE) % static_cast<unsigned int>(plnum)).str());
1037
1038 WRITE(unseen_times_file, UNSEEN_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionUnseenTimes)
1039
1040 WRITE(node_immovable_kinds_file, NODE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
1041 kCurrentPacketVersionImmovableKinds)
1042
1043 WRITE(
1044 node_immovables_file, NODE_IMMOVABLES_FILENAME_TEMPLATE, kCurrentPacketVersionImmovables)
1045
1046 WRITE(roads_file, ROADS_FILENAME_TEMPLATE, kCurrentPacketVersionRoads)
1047
1048 WRITE(terrains_file, TERRAINS_FILENAME_TEMPLATE, kCurrentPacketVersionTerrains)
1049
1050 WRITE(triangle_immovable_kinds_file, TRIANGLE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
1051 kCurrentPacketVersionImmovableKinds)
1052
1053 WRITE(triangle_immovables_file, TRIANGLE_IMMOVABLES_FILENAME_TEMPLATE,
1054 kCurrentPacketVersionImmovables)
1055
1056 WRITE(owners_file, OWNERS_FILENAME_TEMPLATE, kCurrentPacketVersionOwners)
1057
1058 WRITE(surveys_file, SURVEYS_FILENAME_TEMPLATE, kCurrentPacketVersionSurveys)
1059
1060 WRITE(
1061 survey_amounts_file, SURVEY_AMOUNTS_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyAmounts)
1062
1063 WRITE(survey_times_file, SURVEY_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyTimes)
1064
1065 WRITE(hidden_file, HIDDEN_FILENAME_TEMPLATE, kCurrentPacketVersionHidden)
1066
1067 WRITE(vision_file, VISION_FILENAME_TEMPLATE, kCurrentPacketVersionVision)
1068
1069 WRITE(border_file, BORDER_FILENAME_TEMPLATE, kCurrentPacketVersionBorder)
1070 }
1071 }
1072 } // namespace Widelands
1073