← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/remove-savegame-compatibility-after-economy-change into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/remove-savegame-compatibility-after-economy-change into lp:widelands.

Commit message:
Removed savegame compatibility code

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/remove-savegame-compatibility-after-economy-change/+merge/349390

Since we just broke savegame compatibility, we can get rid of the compatibility code.

Old maps should still be loadable.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/remove-savegame-compatibility-after-economy-change into lp:widelands.
=== modified file 'src/economy/expedition_bootstrap.cc'
--- src/economy/expedition_bootstrap.cc	2018-04-07 16:59:00 +0000
+++ src/economy/expedition_bootstrap.cc	2018-07-12 08:22:25 +0000
@@ -206,19 +206,11 @@
    Warehouse& warehouse, FileRead& fr, Game& game, MapObjectLoader& mol, uint16_t packet_version) {
 
 	static const uint16_t kCurrentPacketVersion = 7;
-
 	assert(queues_.empty());
 	// Load worker queues
 	std::vector<WorkersQueue*> wqs;
 	try {
-		if (packet_version <= 6) {
-			// This code is actually quite broken/inflexible but it should work
-			// If we are here, use the old loader for build 19 packets
-			const uint8_t num_workers = fr.unsigned_8();
-			WorkersQueue* wq = new WorkersQueue(warehouse, warehouse.owner().tribe().builder(), 1);
-			wq->load_for_expedition(fr, game, mol, num_workers);
-			wqs.push_back(wq);
-		} else if (packet_version >= kCurrentPacketVersion) {
+		if (packet_version == kCurrentPacketVersion) {
 			uint8_t num_queues = fr.unsigned_8();
 			for (uint8_t i = 0; i < num_queues; ++i) {
 				WorkersQueue* wq = new WorkersQueue(warehouse, INVALID_INDEX, 0);

=== modified file 'src/economy/workers_queue.cc'
--- src/economy/workers_queue.cc	2018-04-07 16:59:00 +0000
+++ src/economy/workers_queue.cc	2018-07-12 08:22:25 +0000
@@ -176,23 +176,6 @@
 	return w;
 }
 
-void WorkersQueue::load_for_expedition(FileRead& fr,
-                                       Game& game,
-                                       MapObjectLoader& mol,
-                                       uint8_t num_workers) {
-	assert(type_ == wwWORKER);
-	assert(index_ != INVALID_INDEX);
-	for (uint8_t i = 0; i < num_workers; ++i) {
-		if (fr.unsigned_8() == 1) {
-			request_.reset(new Request(owner_, 0, InputQueue::request_callback, wwWORKER));
-			request_->read(fr, game, mol);
-		} else {
-			workers_.push_back(&mol.get<Worker>(fr.unsigned_32()));
-		}
-	}
-	// All other values have hopefully be set by the constructor or the caller
-}
-
 /**
  * Read and write
  */

=== modified file 'src/economy/workers_queue.h'
--- src/economy/workers_queue.h	2018-04-07 16:59:00 +0000
+++ src/economy/workers_queue.h	2018-07-12 08:22:25 +0000
@@ -70,14 +70,6 @@
 	 */
 	Worker* extract_worker();
 
-	/**
-	 * Loads the state of this WorkersQueue.
-	 * This method should only be used by ExpeditionBootstrap as compatibility helper when loading
-	 * save games for build 19.
-	 * If we ever drop support for them, remove this method and rework ExpeditionBootstrap::load().
-	 */
-	void load_for_expedition(FileRead&, Game&, MapObjectLoader&, uint8_t);
-
 protected:
 	void read_child(FileRead&, Game&, MapObjectLoader&) override;
 	void write_child(FileWrite&, Game&, MapObjectSaver&) override;

=== modified file 'src/game_io/game_interactive_player_packet.cc'
--- src/game_io/game_interactive_player_packet.cc	2018-04-07 16:59:00 +0000
+++ src/game_io/game_interactive_player_packet.cc	2018-07-12 08:22:25 +0000
@@ -34,17 +34,6 @@
 
 constexpr uint16_t kCurrentPacketVersion = 4;
 
-void load_landmarks_pre_zoom(FileRead* fr, InteractiveBase* ibase) {
-	size_t no_of_landmarks = fr->unsigned_8();
-	for (size_t i = 0; i < no_of_landmarks; ++i) {
-		uint8_t set = fr->unsigned_8();
-		MapView::View view = {Vector2f(fr->signed_32(), fr->signed_32()), 1.f};
-		if (set > 0) {
-			ibase->set_landmark(i, view);
-		}
-	}
-}
-
 }  // namespace
 
 void GameInteractivePlayerPacket::read(FileSystem& fs, Game& game, MapObjectLoader*) {
@@ -52,7 +41,7 @@
 		FileRead fr;
 		fr.open(fs, "binary/interactive_player");
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version >= 2 && packet_version <= kCurrentPacketVersion) {
+		if (packet_version == kCurrentPacketVersion) {
 			PlayerNumber player_number = fr.unsigned_8();
 			if (!(0 < player_number && player_number <= game.map().get_nrplayers())) {
 				throw GameDataError("Invalid player number: %i.", player_number);
@@ -70,15 +59,7 @@
 				if (player_number > max)
 					throw GameDataError("The game has no players!");
 			}
-			Vector2f center_map_pixel = Vector2f::zero();
-			if (packet_version <= 3) {
-				center_map_pixel.x = fr.unsigned_16();
-				center_map_pixel.y = fr.unsigned_16();
-			} else {
-				center_map_pixel.x = fr.float_32();
-				center_map_pixel.y = fr.float_32();
-			}
-
+			Vector2f center_map_pixel(fr.float_32(), fr.float_32());
 			uint32_t const display_flags = fr.unsigned_32();
 
 			if (InteractiveBase* const ibase = game.get_ibase()) {
@@ -96,19 +77,15 @@
 
 			// Map landmarks
 			if (InteractiveBase* const ibase = game.get_ibase()) {
-				if (packet_version == 3) {
-					load_landmarks_pre_zoom(&fr, ibase);
-				} else if (packet_version >= 4) {
-					size_t no_of_landmarks = fr.unsigned_8();
-					for (size_t i = 0; i < no_of_landmarks; ++i) {
-						uint8_t set = fr.unsigned_8();
-						const float x = fr.float_32();
-						const float y = fr.float_32();
-						const float zoom = fr.float_32();
-						MapView::View view = {Vector2f(x, y), zoom};
-						if (set > 0) {
-							ibase->set_landmark(i, view);
-						}
+				size_t no_of_landmarks = fr.unsigned_8();
+				for (size_t i = 0; i < no_of_landmarks; ++i) {
+					uint8_t set = fr.unsigned_8();
+					const float x = fr.float_32();
+					const float y = fr.float_32();
+					const float zoom = fr.float_32();
+					MapView::View view = {Vector2f(x, y), zoom};
+					if (set > 0) {
+						ibase->set_landmark(i, view);
 					}
 				}
 			}

=== modified file 'src/game_io/game_player_ai_persistent_packet.cc'
--- src/game_io/game_player_ai_persistent_packet.cc	2018-04-07 16:59:00 +0000
+++ src/game_io/game_player_ai_persistent_packet.cc	2018-07-12 08:22:25 +0000
@@ -28,14 +28,7 @@
 
 namespace Widelands {
 
-// Introduction of genetic algorithm with all structures that are needed for it
 constexpr uint16_t kCurrentPacketVersion = 5;
-// Last version with 150 magic numbers
-constexpr uint16_t kOldMagicNumbers = 4;
-// First version with genetics
-constexpr uint16_t kPacketVersion3 = 3;
-// Old Version before using genetics
-constexpr uint16_t kPacketVersion2 = 2;
 
 void GamePlayerAiPersistentPacket::read(FileSystem& fs, Game& game, MapObjectLoader*) {
 	try {
@@ -44,129 +37,84 @@
 		FileRead fr;
 		fr.open(fs, "binary/player_ai");
 		uint16_t const packet_version = fr.unsigned_16();
-		// TODO(GunChleoc): Savegame compatibility, remove after Build20
-		if (packet_version >= kPacketVersion2 && packet_version <= kCurrentPacketVersion) {
+		if (packet_version == kCurrentPacketVersion) {
 			iterate_players_existing(p, nr_players, game, player) try {
 				// Make sure that all containers are reset properly etc.
 				player->ai_data.initialize();
-
-				if (packet_version == kPacketVersion2) {
-					// Packet is not compatible. Consume without using the data.
-					fr.unsigned_8();
-					fr.unsigned_32();
-					fr.unsigned_32();
-					fr.unsigned_32();
-					fr.unsigned_16();
-					fr.unsigned_8();
-					fr.signed_16();
-					fr.unsigned_32();
-					fr.unsigned_32();
-					fr.signed_16();
-					fr.signed_32();
-					fr.unsigned_32();
-					fr.signed_32();
-					fr.unsigned_32();
-					fr.unsigned_32();
-					// Make the AI initialize itself
-					player->ai_data.initialized = 0;
-				} else {
-					// Contains Genetic algorithm data
-					player->ai_data.initialized = (fr.unsigned_8() == 1) ? true : false;
-					player->ai_data.colony_scan_area = fr.unsigned_32();
-					player->ai_data.trees_around_cutters = fr.unsigned_32();
-					player->ai_data.expedition_start_time = fr.unsigned_32();
-					player->ai_data.ships_utilization = fr.unsigned_16();
-					player->ai_data.no_more_expeditions = (fr.unsigned_8() == 1) ? true : false;
-					player->ai_data.last_attacked_player = fr.signed_16();
-					player->ai_data.least_military_score = fr.unsigned_32();
-					player->ai_data.target_military_score = fr.unsigned_32();
-					player->ai_data.ai_productionsites_ratio = fr.unsigned_32();
-					player->ai_data.ai_personality_mil_upper_limit = fr.signed_32();
-
-					// Magic numbers
-					size_t magic_numbers_size = fr.unsigned_32();
-
-					// Here we deal with old savegames that contains 150 magic numbers only
-					if (packet_version <= kOldMagicNumbers) {
-						// The savegame contains less then expected number of magic numbers
-						assert(magic_numbers_size <
-						       Widelands::Player::AiPersistentState::kMagicNumbersSize);
-						assert(player->ai_data.magic_numbers.size() ==
-						       Widelands::Player::AiPersistentState::kMagicNumbersSize);
-						for (size_t i = 0; i < magic_numbers_size; ++i) {
-							player->ai_data.magic_numbers.at(i) = fr.signed_16();
-						}
-						// Adding '50' to missing possitions
-						for (size_t i = magic_numbers_size;
-						     i < Widelands::Player::AiPersistentState::kMagicNumbersSize; ++i) {
-							player->ai_data.magic_numbers.at(i) = 50;
-						}
-					} else {
-						if (magic_numbers_size >
-						    Widelands::Player::AiPersistentState::kMagicNumbersSize) {
-							throw GameDataError("Too many magic numbers: We have %" PRIuS
-							                    " but only %" PRIuS "are allowed",
-							                    magic_numbers_size,
-							                    Widelands::Player::AiPersistentState::kMagicNumbersSize);
-						}
-						assert(player->ai_data.magic_numbers.size() ==
-						       Widelands::Player::AiPersistentState::kMagicNumbersSize);
-						for (size_t i = 0; i < magic_numbers_size; ++i) {
-							player->ai_data.magic_numbers.at(i) = fr.signed_16();
-						}
-					}
-
-					// Neurons
-					const size_t neuron_pool_size = fr.unsigned_32();
-					if (neuron_pool_size > Widelands::Player::AiPersistentState::kNeuronPoolSize) {
-						throw GameDataError(
-						   "Too many neurons: We have %" PRIuS " but only %" PRIuS "are allowed",
-						   neuron_pool_size, Widelands::Player::AiPersistentState::kNeuronPoolSize);
-					}
-					assert(player->ai_data.neuron_weights.size() ==
-					       Widelands::Player::AiPersistentState::kNeuronPoolSize);
-					for (size_t i = 0; i < neuron_pool_size; ++i) {
-						player->ai_data.neuron_weights.at(i) = fr.signed_8();
-					}
-					assert(player->ai_data.neuron_functs.size() ==
-					       Widelands::Player::AiPersistentState::kNeuronPoolSize);
-					for (size_t i = 0; i < neuron_pool_size; ++i) {
-						player->ai_data.neuron_functs.at(i) = fr.signed_8();
-					}
-
-					// F-neurons
-					const size_t f_neuron_pool_size = fr.unsigned_32();
-					if (f_neuron_pool_size > Widelands::Player::AiPersistentState::kFNeuronPoolSize) {
-						throw GameDataError(
-						   "Too many f neurons: We have %" PRIuS " but only %" PRIuS "are allowed",
-						   f_neuron_pool_size, Widelands::Player::AiPersistentState::kFNeuronPoolSize);
-					}
-					assert(player->ai_data.f_neurons.size() ==
-					       Widelands::Player::AiPersistentState::kFNeuronPoolSize);
-					for (size_t i = 0; i < f_neuron_pool_size; ++i) {
-						player->ai_data.f_neurons.at(i) = fr.unsigned_32();
-					}
-
-					// Remaining buildings for basic economy
-					assert(player->ai_data.remaining_basic_buildings.empty());
-
-					size_t remaining_basic_buildings_size = fr.unsigned_32();
-					for (uint16_t i = 0; i < remaining_basic_buildings_size; ++i) {
-						if (packet_version == kPacketVersion3) {  // Old genetics (buildings saved as idx)
-							player->ai_data.remaining_basic_buildings.emplace(
-							   static_cast<Widelands::DescriptionIndex>(fr.unsigned_32()),
-							   fr.unsigned_32());
-						} else {  // New genetics (buildings saved as strigs)
-							const std::string building_string = fr.string();
-							const Widelands::DescriptionIndex bld_idx =
-							   player->tribe().building_index(building_string);
-							player->ai_data.remaining_basic_buildings.emplace(bld_idx, fr.unsigned_32());
-						}
-					}
-					// Basic sanity check for remaining basic buildings
-					assert(player->ai_data.remaining_basic_buildings.size() <
-					       player->tribe().buildings().size());
-				}
+				// Contains Genetic algorithm data
+				player->ai_data.initialized = (fr.unsigned_8() == 1) ? true : false;
+				player->ai_data.colony_scan_area = fr.unsigned_32();
+				player->ai_data.trees_around_cutters = fr.unsigned_32();
+				player->ai_data.expedition_start_time = fr.unsigned_32();
+				player->ai_data.ships_utilization = fr.unsigned_16();
+				player->ai_data.no_more_expeditions = (fr.unsigned_8() == 1) ? true : false;
+				player->ai_data.last_attacked_player = fr.signed_16();
+				player->ai_data.least_military_score = fr.unsigned_32();
+				player->ai_data.target_military_score = fr.unsigned_32();
+				player->ai_data.ai_productionsites_ratio = fr.unsigned_32();
+				player->ai_data.ai_personality_mil_upper_limit = fr.signed_32();
+
+				// Magic numbers
+				const size_t magic_numbers_size = fr.unsigned_32();
+				if (magic_numbers_size >
+					Widelands::Player::AiPersistentState::kMagicNumbersSize) {
+					throw GameDataError("Too many magic numbers: We have %" PRIuS
+										" but only %" PRIuS "are allowed",
+										magic_numbers_size,
+										Widelands::Player::AiPersistentState::kMagicNumbersSize);
+				}
+				assert(player->ai_data.magic_numbers.size() ==
+					   Widelands::Player::AiPersistentState::kMagicNumbersSize);
+				for (size_t i = 0; i < magic_numbers_size; ++i) {
+					player->ai_data.magic_numbers.at(i) = fr.signed_16();
+				}
+
+				// Neurons
+				const size_t neuron_pool_size = fr.unsigned_32();
+				if (neuron_pool_size > Widelands::Player::AiPersistentState::kNeuronPoolSize) {
+					throw GameDataError(
+					   "Too many neurons: We have %" PRIuS " but only %" PRIuS "are allowed",
+					   neuron_pool_size, Widelands::Player::AiPersistentState::kNeuronPoolSize);
+				}
+				assert(player->ai_data.neuron_weights.size() ==
+					   Widelands::Player::AiPersistentState::kNeuronPoolSize);
+				for (size_t i = 0; i < neuron_pool_size; ++i) {
+					player->ai_data.neuron_weights.at(i) = fr.signed_8();
+				}
+				assert(player->ai_data.neuron_functs.size() ==
+					   Widelands::Player::AiPersistentState::kNeuronPoolSize);
+				for (size_t i = 0; i < neuron_pool_size; ++i) {
+					player->ai_data.neuron_functs.at(i) = fr.signed_8();
+				}
+
+				// F-neurons
+				const size_t f_neuron_pool_size = fr.unsigned_32();
+				if (f_neuron_pool_size > Widelands::Player::AiPersistentState::kFNeuronPoolSize) {
+					throw GameDataError(
+					   "Too many f neurons: We have %" PRIuS " but only %" PRIuS "are allowed",
+					   f_neuron_pool_size, Widelands::Player::AiPersistentState::kFNeuronPoolSize);
+				}
+				assert(player->ai_data.f_neurons.size() ==
+					   Widelands::Player::AiPersistentState::kFNeuronPoolSize);
+				for (size_t i = 0; i < f_neuron_pool_size; ++i) {
+					player->ai_data.f_neurons.at(i) = fr.unsigned_32();
+				}
+
+				// Remaining buildings for basic economy
+				assert(player->ai_data.remaining_basic_buildings.empty());
+
+				size_t remaining_basic_buildings_size = fr.unsigned_32();
+				for (uint16_t i = 0; i < remaining_basic_buildings_size; ++i) {
+					// Buildings saved as strings
+					const std::string building_string = fr.string();
+					const Widelands::DescriptionIndex bld_idx =
+					   player->tribe().building_index(building_string);
+					player->ai_data.remaining_basic_buildings.emplace(bld_idx, fr.unsigned_32());
+				}
+				// Basic sanity check for remaining basic buildings
+				assert(player->ai_data.remaining_basic_buildings.size() <
+					   player->tribe().buildings().size());
+
 			} catch (const WException& e) {
 				throw GameDataError("player %u: %s", p, e.what());
 			}

=== modified file 'src/game_io/game_player_info_packet.cc'
--- src/game_io/game_player_info_packet.cc	2018-04-28 07:02:42 +0000
+++ src/game_io/game_player_info_packet.cc	2018-07-12 08:22:25 +0000
@@ -37,7 +37,7 @@
 		FileRead fr;
 		fr.open(fs, "binary/player_info");
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version >= 19 && packet_version <= kCurrentPacketVersion) {
+		if (packet_version == kCurrentPacketVersion) {
 			uint32_t const max_players = fr.unsigned_16();
 			for (uint32_t i = 1; i < max_players + 1; ++i) {
 				game.remove_player(i);
@@ -60,7 +60,7 @@
 
 					player->set_ai(fr.c_string());
 					player->read_statistics(fr);
-					player->read_remaining_shipnames(fr, packet_version);
+					player->read_remaining_shipnames(fr);
 
 					player->casualties_ = fr.unsigned_32();
 					player->kills_ = fr.unsigned_32();
@@ -72,17 +72,15 @@
 			}
 
 			// Result screen
-			if (packet_version > 19) {
-				PlayersManager* manager = game.player_manager();
-				const uint8_t no_endstatus = fr.unsigned_8();
-				for (uint8_t i = 0; i < no_endstatus; ++i) {
-					PlayerEndStatus status;
-					status.player = fr.unsigned_8();
-					status.result = static_cast<PlayerEndResult>(fr.unsigned_8());
-					status.time = fr.unsigned_32();
-					status.info = fr.c_string();
-					manager->set_player_end_status(status);
-				}
+			PlayersManager* manager = game.player_manager();
+			const uint8_t no_endstatus = fr.unsigned_8();
+			for (uint8_t i = 0; i < no_endstatus; ++i) {
+				PlayerEndStatus status;
+				status.player = fr.unsigned_8();
+				status.result = static_cast<PlayerEndResult>(fr.unsigned_8());
+				status.time = fr.unsigned_32();
+				status.info = fr.c_string();
+				manager->set_player_end_status(status);
 			}
 
 			game.read_statistics(fr);

=== modified file 'src/logic/map_objects/immovable.cc'
--- src/logic/map_objects/immovable.cc	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/immovable.cc	2018-07-12 08:22:25 +0000
@@ -550,6 +550,7 @@
 
 	Immovable& imm = dynamic_cast<Immovable&>(*get_object());
 
+	// Supporting older versions for map loading
 	if (packet_version >= 5) {
 		PlayerNumber pn = fr.unsigned_8();
 		if (pn && pn <= kMaxPlayers) {

=== modified file 'src/logic/map_objects/map_object.cc'
--- src/logic/map_objects/map_object.cc	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/map_object.cc	2018-07-12 08:22:25 +0000
@@ -592,6 +592,7 @@
 			throw wexception("header is %u, expected %u", header, HeaderMapObject);
 
 		uint8_t const packet_version = fr.unsigned_8();
+		// Supporting older versions for map loading
 		if (packet_version < 1 || packet_version > kCurrentPacketVersionMapObject) {
 			throw UnhandledVersionError("MapObject", packet_version, kCurrentPacketVersionMapObject);
 		}

=== modified file 'src/logic/map_objects/tribes/militarysite.h'
--- src/logic/map_objects/tribes/militarysite.h	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/militarysite.h	2018-07-12 08:22:25 +0000
@@ -36,7 +36,6 @@
 
 // I assume elsewhere, that enum SoldierPreference fits to uint8_t.
 enum class SoldierPreference : uint8_t {
-	kNotSet,  // For savegame compatibility only.
 	kRookies,
 	kHeroes,
 };

=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc	2018-05-27 06:02:18 +0000
+++ src/logic/map_objects/tribes/ship.cc	2018-07-12 08:22:25 +0000
@@ -1110,7 +1110,7 @@
 ==============================
 */
 
-constexpr uint8_t kCurrentPacketVersion = 7;
+constexpr uint8_t kCurrentPacketVersion = 8;
 
 const Bob::Task* Ship::Loader::get_task(const std::string& name) {
 	if (name == "shipidle" || name == "ship")

=== modified file 'src/logic/map_objects/tribes/ship.h'
--- src/logic/map_objects/tribes/ship.h	2018-07-12 04:41:20 +0000
+++ src/logic/map_objects/tribes/ship.h	2018-07-12 08:22:25 +0000
@@ -35,10 +35,11 @@
 class PortDock;
 
 // This can't be part of the Ship class because of forward declaration in game.h
+// Keep the order of entries for savegame compatibility.
 enum class IslandExploreDirection {
-	kCounterClockwise = 0,  // This comes first for savegame compatibility (used to be = 0)
-	kClockwise = 1,
-	kNotSet
+	kNotSet,
+	kCounterClockwise,
+	kClockwise,
 };
 
 struct NoteShip {

=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/soldier.cc	2018-07-12 08:22:25 +0000
@@ -1574,9 +1574,6 @@
 */
 
 constexpr uint8_t kCurrentPacketVersion = 3;
-// TODO(TiborB): This is only for map compatibility in regression tests, we should get rid of this
-// ASAP
-constexpr uint8_t kOldPacketVersion = 2;
 
 Soldier::Loader::Loader() : battle_(0) {
 }
@@ -1586,17 +1583,10 @@
 
 	try {
 		uint8_t packet_version = fr.unsigned_8();
-		if (packet_version == kCurrentPacketVersion || packet_version == kOldPacketVersion) {
-
+		if (packet_version == kCurrentPacketVersion) {
 			Soldier& soldier = get<Soldier>();
 			soldier.current_health_ = fr.unsigned_32();
-			if (packet_version == kCurrentPacketVersion) {
-				soldier.retreat_health_ = fr.unsigned_32();
-			} else {
-				// not ideal but will be used only for regression tests
-				soldier.retreat_health_ = 0;
-			}
-
+			soldier.retreat_health_ = fr.unsigned_32();
 			soldier.health_level_ = std::min(fr.unsigned_32(), soldier.descr().get_max_health_level());
 			soldier.attack_level_ = std::min(fr.unsigned_32(), soldier.descr().get_max_attack_level());
 			soldier.defense_level_ =
@@ -1614,7 +1604,6 @@
 				soldier.combat_walkstart_ = fr.unsigned_32();
 				soldier.combat_walkend_ = fr.unsigned_32();
 			}
-
 			battle_ = fr.unsigned_32();
 		} else {
 			throw UnhandledVersionError("Soldier", packet_version, kCurrentPacketVersion);

=== modified file 'src/logic/map_objects/tribes/worker.cc'
--- src/logic/map_objects/tribes/worker.cc	2018-05-05 17:10:37 +0000
+++ src/logic/map_objects/tribes/worker.cc	2018-07-12 08:22:25 +0000
@@ -2959,21 +2959,6 @@
 	}
 
 	const Map& map = game.map();
-
-	if (scouts_worklist.empty()) {
-		// This routine assumes that scouts_worklist is not empty. There is one exception:
-		// First call to this routine after loading an old savegame. The least-invasive
-		// way to acquire old savegame compatibility was to simply ask the scout to go home early,
-		// under this special situation. Anybody reading this,
-		// TODO(kxq): Please remove this code block (and compatibility_2017 code from load routine)
-		// once Build 20 is out. Thanks.
-		log("Warning: sending scout home. Assuming the game was just started, from savegame, in "
-		    "compatibility mode.\n");
-		pop_task(game);
-		schedule_act(game, 10);
-		return;
-	}
-
 	bool do_run = static_cast<int32_t>(state.ivar2 - game.get_gametime()) > 0;
 
 	// do not pop; this function is called many times per run.
@@ -3044,12 +3029,7 @@
 	Bob::Loader::load(fr);
 	try {
 		uint8_t packet_version = fr.unsigned_8();
-		// TODO(kxq): Remove the compatibility_2017 code (and similars, dozen lines below) after B20
-		// TODO(kxq): Also remove the code fragment from Worker::scout_update with compatibility_2017
-		// in comment.
-		bool compatibility_2017 = 2 == packet_version;
-		if (packet_version == kCurrentPacketVersion || compatibility_2017) {
-
+		if (packet_version == kCurrentPacketVersion) {
 			Worker& worker = get<Worker>();
 			location_ = fr.unsigned_32();
 			carried_ware_ = fr.unsigned_32();
@@ -3059,14 +3039,7 @@
 				worker.transfer_ = new Transfer(dynamic_cast<Game&>(egbase()), worker);
 				worker.transfer_->read(fr, transfer_);
 			}
-			unsigned veclen;
-			// TODO(kxq): Remove compatibility_2017 associated code from here and above,
-			// after build 20 has been released.
-			if (compatibility_2017) {
-				veclen = 0;
-			} else {
-				veclen = fr.unsigned_8();
-			}
+			const unsigned veclen = fr.unsigned_8();
 			for (unsigned q = 0; q < veclen; q++) {
 				if (fr.unsigned_8()) {
 					const PlaceToScout gsw;
@@ -3079,7 +3052,6 @@
 					worker.scouts_worklist.push_back(gtt);
 				}
 			}
-
 		} else {
 			throw UnhandledVersionError("Worker", packet_version, kCurrentPacketVersion);
 		}

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2018-07-12 04:41:20 +0000
+++ src/logic/player.cc	2018-07-12 08:22:25 +0000
@@ -1337,19 +1337,14 @@
  *
  * \param fr source stream
  */
-void Player::read_remaining_shipnames(FileRead& fr, uint16_t packet_version) {
+void Player::read_remaining_shipnames(FileRead& fr) {
 	// First get rid of default shipnames
 	remaining_shipnames_.clear();
 	const uint16_t count = fr.unsigned_16();
 	for (uint16_t i = 0; i < count; ++i) {
 		remaining_shipnames_.insert(fr.string());
 	}
-	// TODO(GunChleoc): Savegame compatibility. Remove after Build 20.
-	if (packet_version >= 21) {
-		ship_name_counter_ = fr.unsigned_32();
-	} else {
-		ship_name_counter_ = ships_.size();
-	}
+	ship_name_counter_ = fr.unsigned_32();
 }
 
 /**

=== modified file 'src/logic/player.h'
--- src/logic/player.h	2018-07-12 04:41:20 +0000
+++ src/logic/player.h	2018-07-12 08:22:25 +0000
@@ -581,7 +581,7 @@
 
 	void read_statistics(FileRead&);
 	void write_statistics(FileWrite&) const;
-	void read_remaining_shipnames(FileRead&, uint16_t packet_version);
+	void read_remaining_shipnames(FileRead&);
 	void write_remaining_shipnames(FileWrite&) const;
 	void sample_statistics();
 	void ware_produced(DescriptionIndex);

=== modified file 'src/logic/playercommand.cc'
--- src/logic/playercommand.cc	2018-07-12 04:41:20 +0000
+++ src/logic/playercommand.cc	2018-07-12 08:22:25 +0000
@@ -199,7 +199,7 @@
 void PlayerCommand::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
 	try {
 		const uint16_t packet_version = fr.unsigned_16();
-		if (packet_version >= 2 && packet_version <= kCurrentPacketVersionPlayerCommand) {
+		if (packet_version == kCurrentPacketVersionPlayerCommand) {
 			GameLogicCommand::read(fr, egbase, mol);
 			sender_ = fr.unsigned_8();
 			if (!egbase.get_player(sender_))
@@ -1140,16 +1140,14 @@
 void CmdSetInputMaxFill::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
 	try {
 		const uint16_t packet_version = fr.unsigned_16();
-		if (packet_version >= 1 && packet_version <= kCurrentPacketVersionCmdSetInputMaxFill) {
+		if (packet_version == kCurrentPacketVersionCmdSetInputMaxFill) {
 			PlayerCommand::read(fr, egbase, mol);
 			serial_ = get_object_serial_or_zero<Building>(fr.unsigned_32(), mol);
 			index_ = fr.signed_32();
-			if (packet_version > 1) {
-				if (fr.unsigned_8() == 0) {
-					type_ = wwWARE;
-				} else {
-					type_ = wwWORKER;
-				}
+			if (fr.unsigned_8() == 0) {
+				type_ = wwWARE;
+			} else {
+				type_ = wwWORKER;
 			}
 			max_fill_ = fr.unsigned_32();
 		} else {

=== modified file 'src/map_io/map_allowed_building_types_packet.cc'
--- src/map_io/map_allowed_building_types_packet.cc	2018-04-07 16:59:00 +0000
+++ src/map_io/map_allowed_building_types_packet.cc	2018-07-12 08:22:25 +0000
@@ -85,23 +85,6 @@
 				} catch (const WException& e) {
 					throw GameDataError("player %u (%s): %s", p, tribe.name().c_str(), e.what());
 				}
-
-				// Savegame compatibility: If all buildings except for the barracks are allowed, allow
-				// it too. This will make games playable again except for scenarios that restrict the
-				// number of buildings and need soldier creation.
-				// TODO(Notabilis): Remove this when we break save game compatibility anyway
-				if (!player->is_building_type_allowed(player->tribe().barracks())) {
-					size_t allowed_buildings = 0;
-					for (const Widelands::DescriptionIndex& index : player->tribe().buildings()) {
-						if (player->is_building_type_allowed(index)) {
-							++allowed_buildings;
-						}
-					}
-					if (player->tribe().buildings().size() - 1 == allowed_buildings) {
-						log("WARNING: Enabling barracks for player %u.\n", player->player_number());
-						player->allow_building_type(player->tribe().barracks(), true);
-					}
-				}
 			}
 		} else {
 			throw UnhandledVersionError(

=== modified file 'src/map_io/map_buildingdata_packet.cc'
--- src/map_io/map_buildingdata_packet.cc	2018-04-07 16:59:00 +0000
+++ src/map_io/map_buildingdata_packet.cc	2018-07-12 08:22:25 +0000
@@ -508,10 +508,6 @@
 			militarysite.soldier_upgrade_requirements_ =
 			   RequireAttribute(TrainingAttribute::kTotal, reqmin, reqmax);
 			militarysite.soldier_preference_ = static_cast<SoldierPreference>(fr.unsigned_8());
-			// TODO(GunChleoc): Savegame compatibility, remove kNotSet after Build 20.
-			if (militarysite.soldier_preference_ == SoldierPreference::kNotSet) {
-				militarysite.soldier_preference_ = SoldierPreference::kRookies;
-			}
 			militarysite.next_swap_soldiers_time_ = fr.signed_32();
 			militarysite.soldier_upgrade_try_ = 0 != fr.unsigned_8() ? true : false;
 			militarysite.doing_upgrade_request_ = 0 != fr.unsigned_8() ? true : false;

=== modified file 'src/wui/soldierlist.cc'
--- src/wui/soldierlist.cc	2018-04-07 16:59:00 +0000
+++ src/wui/soldierlist.cc	2018-07-12 08:22:25 +0000
@@ -436,8 +436,6 @@
 	if (upcast(Widelands::MilitarySite, ms, &building_)) {
 		switch (ms->get_soldier_preference()) {
 		case Widelands::SoldierPreference::kRookies:
-			FALLS_THROUGH;
-		case Widelands::SoldierPreference::kNotSet:
 			soldier_preference_.set_state(0);
 			break;
 		case Widelands::SoldierPreference::kHeroes:

=== modified file 'test/maps/lua_testsuite.wmf/binary/mapobjects'
Binary files test/maps/lua_testsuite.wmf/binary/mapobjects	2018-04-17 03:17:15 +0000 and test/maps/lua_testsuite.wmf/binary/mapobjects	2018-07-12 08:22:25 +0000 differ

Follow ups