← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-1732765-economy-refactoring into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1732765-economy-refactoring into lp:widelands.

Commit message:
Economies are now mapped to global serials and kept as unique_ptr in the Player objects.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1631738 in widelands: "Memory leak in economy/economy.cc split"
  https://bugs.launchpad.net/widelands/+bug/1631738
  Bug #1654897 in widelands: "corrupted saved game: "string ended unexpectedly""
  https://bugs.launchpad.net/widelands/+bug/1654897
  Bug #1732765 in widelands: "Configure economy window disappears if only headquarter is available"
  https://bugs.launchpad.net/widelands/+bug/1732765
  Bug #1762260 in widelands: "number of economies on save != number of economies on load"
  https://bugs.launchpad.net/widelands/+bug/1762260

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1732765-economy-refactoring/+merge/345277

Big redesign of economy tracking, hoping that it will fix some bugs.

NOTE: This will break savegame compatibility, so we should take care to deal with all bugs that have interesting savegames attached before merging this. Implementing compatibility would be very complicated.

We used to have economy numbers per player, now we have 1 global serial number.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1732765-economy-refactoring into lp:widelands.
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2018-04-29 09:20:29 +0000
+++ src/ai/defaultai.cc	2018-05-12 04:24:42 +0000
@@ -6423,7 +6423,7 @@
 			assert(new_target > 1);
 
 			game().send_player_command(*new Widelands::CmdSetWareTargetQuantity(
-			   gametime, player_number(), player_->get_economy_number(&observer->economy), id,
+			   gametime, player_number(), observer->economy.serial(), id,
 			   new_target));
 		}
 	}

=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h	2018-04-16 07:03:12 +0000
+++ src/ai/defaultai.h	2018-05-12 04:24:42 +0000
@@ -155,7 +155,7 @@
 	// common for defaultai.cc and defaultai_seafaring.cc
 	static constexpr uint32_t kExpeditionMinDuration = 60 * 60 * 1000;
 	static constexpr uint32_t kExpeditionMaxDuration = 210 * 60 * 1000;
-	static constexpr uint32_t kNoShip = std::numeric_limits<uint32_t>::max();
+	static constexpr Widelands::Serial kNoShip = Widelands::kInvalidSerial;
 	static constexpr int kShipCheckInterval = 5 * 1000;
 
 	// used by defaultai_warfare.cc

=== modified file 'src/ai/defaultai_warfare.cc'
--- src/ai/defaultai_warfare.cc	2018-04-09 01:50:48 +0000
+++ src/ai/defaultai_warfare.cc	2018-05-12 04:24:42 +0000
@@ -85,7 +85,7 @@
 	}
 
 	// now we update some of them
-	uint32_t best_target = std::numeric_limits<uint32_t>::max();
+	Widelands::Serial best_target = Widelands::kInvalidSerial;
 	uint8_t best_score = 0;
 	uint32_t count = 0;
 	// sites that were either conquered or destroyed
@@ -463,7 +463,7 @@
 	}
 
 	// if coordinates hash is not set
-	if (best_target == std::numeric_limits<uint32_t>::max()) {
+	if (best_target == Widelands::kInvalidSerial) {
 		return false;
 	}
 

=== modified file 'src/economy/economy.cc'
--- src/economy/economy.cc	2018-04-07 16:59:00 +0000
+++ src/economy/economy.cc	2018-05-12 04:24:42 +0000
@@ -41,15 +41,19 @@
 
 namespace Widelands {
 
-Economy::Economy(Player& player) : owner_(player), request_timerid_(0), has_window_(false) {
+Serial Economy::last_economy_serial_ = 0;
+
+Economy::Economy(Player& player) : Economy(player, last_economy_serial_++) {
+}
+
+Economy::Economy(Player& player, Serial init_serial) : serial_(init_serial), owner_(player), request_timerid_(0), has_window_(false) {
+	last_economy_serial_ = std::max(last_economy_serial_, serial_ + 1);
 	const TribeDescr& tribe = player.tribe();
 	DescriptionIndex const nr_wares = player.egbase().tribes().nrwares();
 	DescriptionIndex const nr_workers = player.egbase().tribes().nrworkers();
 	wares_.set_nrwares(nr_wares);
 	workers_.set_nrwares(nr_workers);
 
-	player.add_economy(*this);
-
 	ware_target_quantities_ = new TargetQuantity[nr_wares];
 	for (DescriptionIndex i = 0; i < nr_wares; ++i) {
 		TargetQuantity tq;
@@ -69,12 +73,11 @@
 		worker_target_quantities_[i] = tq;
 	}
 
-	router_ = new Router(boost::bind(&Economy::reset_all_pathfinding_cycles, this));
+	router_.reset(new Router(boost::bind(&Economy::reset_all_pathfinding_cycles, this)));
 }
 
 Economy::~Economy() {
-	Notifications::publish(NoteEconomy{this, this, NoteEconomy::Action::kDeleted});
-	owner_.remove_economy(*this);
+	Notifications::publish(NoteEconomy{serial_, serial_, NoteEconomy::Action::kDeleted});
 
 	if (requests_.size())
 		log("Warning: Economy still has requests left on destruction\n");
@@ -85,8 +88,6 @@
 
 	delete[] ware_target_quantities_;
 	delete[] worker_target_quantities_;
-
-	delete router_;
 }
 
 /**
@@ -269,8 +270,9 @@
 	do_remove_flag(flag);
 
 	// automatically delete the economy when it becomes empty.
-	if (flags_.empty())
-		delete this;
+	if (flags_.empty()) {
+		owner_.remove_economy(serial_);
+	}
 }
 
 /**
@@ -528,7 +530,8 @@
 	//  If the options window for e is open, but not the one for this, the user
 	//  should still have an options window after the merge.
 	if (e.has_window() && !has_window()) {
-		Notifications::publish(NoteEconomy{&e, this, NoteEconomy::Action::kMerged});
+		Notifications::publish(
+		   NoteEconomy{e.serial(), serial_, NoteEconomy::Action::kMerged});
 	}
 
 	for (std::vector<Flag*>::size_type i = e.get_nrflags() + 1; --i;) {
@@ -542,9 +545,7 @@
 
 	// Remember that the other economy may not have been connected before the merge
 	split_checks_.insert(split_checks_.end(), e.split_checks_.begin(), e.split_checks_.end());
-
-	// implicitly delete the economy
-	delete &e;
+	owner_.remove_economy(e.serial());
 }
 
 /**
@@ -553,21 +554,21 @@
 void Economy::split(const std::set<OPtr<Flag>>& flags) {
 	assert(!flags.empty());
 
-	Economy& e = *new Economy(owner_);
+	Economy* e = owner_.create_economy();
 
 	for (const DescriptionIndex& ware_index : owner_.tribe().wares()) {
-		e.ware_target_quantities_[ware_index] = ware_target_quantities_[ware_index];
+		e->ware_target_quantities_[ware_index] = ware_target_quantities_[ware_index];
 	}
 
 	for (const DescriptionIndex& worker_index : owner_.tribe().workers()) {
-		e.worker_target_quantities_[worker_index] = worker_target_quantities_[worker_index];
+		e->worker_target_quantities_[worker_index] = worker_target_quantities_[worker_index];
 	}
 
 	for (const OPtr<Flag>& temp_flag : flags) {
 		Flag& flag = *temp_flag.get(owner().egbase());
 		assert(flags_.size() > 1);  // We will not be deleted in remove_flag, right?
 		remove_flag(flag);
-		e.add_flag(flag);
+		e->add_flag(flag);
 	}
 
 	// As long as rebalance commands are tied to specific flags, we

=== modified file 'src/economy/economy.h'
--- src/economy/economy.h	2018-04-07 16:59:00 +0000
+++ src/economy/economy.h	2018-05-12 04:24:42 +0000
@@ -56,8 +56,8 @@
 	// When 2 economies have been merged, this is the economy number that has
 	// been removed, while the other one is the number of the resulting economy.
 	// For all other messages old_economy == new_economy.
-	Economy* old_economy;
-	Economy* new_economy;
+	Widelands::Serial old_economy;
+	Widelands::Serial new_economy;
 
 	enum class Action { kMerged, kDeleted };
 	const Action action;
@@ -108,8 +108,13 @@
 	};
 
 	explicit Economy(Player&);
+	explicit Economy(Player&, Serial serial); // For saveloading
 	~Economy();
 
+	Serial serial() const {
+		return serial_;
+	}
+
 	Player& owner() const {
 		return owner_;
 	}
@@ -209,6 +214,9 @@
 		start_request_timer();
 	}
 
+protected:
+	static Serial last_economy_serial_;
+
 private:
 	// This structs is to store distance from supply to request(or), but to allow unambiguous
 	// sorting if distances are the same, we use also serial number of provider and type of provider
@@ -251,6 +259,8 @@
 	/*************/
 	using RequestList = std::vector<Request*>;
 
+	const Serial serial_;
+
 	Player& owner_;
 
 	using Flags = std::vector<Flag*>;
@@ -264,7 +274,7 @@
 
 	TargetQuantity* ware_target_quantities_;
 	TargetQuantity* worker_target_quantities_;
-	Router* router_;
+	std::unique_ptr<Router> router_;
 
 	using SplitPair = std::pair<OPtr<Flag>, OPtr<Flag>>;
 	std::vector<SplitPair> split_checks_;

=== modified file 'src/economy/economy_data_packet.cc'
--- src/economy/economy_data_packet.cc	2018-04-07 16:59:00 +0000
+++ src/economy/economy_data_packet.cc	2018-05-12 04:24:42 +0000
@@ -27,7 +27,7 @@
 #include "map_io/map_object_loader.h"
 #include "map_io/map_object_saver.h"
 
-constexpr uint16_t kCurrentPacketVersion = 3;
+constexpr uint16_t kCurrentPacketVersion = 4;
 
 namespace Widelands {
 
@@ -35,6 +35,11 @@
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
 		if (packet_version == kCurrentPacketVersion) {
+			const Serial saved_serial = fr.unsigned_32();
+			if (eco_->serial_ != saved_serial) {
+				throw GameDataError("Representative flag/ship has economy serial %d, but the data packet has %d", eco_->serial_, saved_serial);
+			}
+			assert(Economy::last_economy_serial_ >= eco_->serial_);
 			try {
 				const TribeDescr& tribe = eco_->owner().tribe();
 				while (Time const last_modified = fr.unsigned_32()) {
@@ -94,6 +99,11 @@
 
 void EconomyDataPacket::write(FileWrite& fw) {
 	fw.unsigned_16(kCurrentPacketVersion);
+
+	// We save the serial number for sanity checks
+	fw.unsigned_32(eco_->serial());
+
+	// Requests etc.
 	const TribeDescr& tribe = eco_->owner().tribe();
 	for (const DescriptionIndex& ware_index : tribe.wares()) {
 		const Economy::TargetQuantity& tq = eco_->ware_target_quantities_[ware_index];

=== modified file 'src/economy/economy_data_packet.h'
--- src/economy/economy_data_packet.h	2018-04-07 16:59:00 +0000
+++ src/economy/economy_data_packet.h	2018-05-12 04:24:42 +0000
@@ -20,6 +20,8 @@
 #ifndef WL_ECONOMY_ECONOMY_DATA_PACKET_H
 #define WL_ECONOMY_ECONOMY_DATA_PACKET_H
 
+#include <cassert>
+
 class FileRead;
 class FileWrite;
 
@@ -32,6 +34,7 @@
 class EconomyDataPacket {
 public:
 	explicit EconomyDataPacket(Economy* e) : eco_(e) {
+		assert(eco_);
 	}
 
 	void read(FileRead&);

=== modified file 'src/economy/flag.cc'
--- src/economy/flag.cc	2018-04-07 16:59:00 +0000
+++ src/economy/flag.cc	2018-05-12 04:24:42 +0000
@@ -44,7 +44,7 @@
 }
 
 /**
- * Create the flag. Initially, it doesn't have any attachments.
+ * A bare flag, used for testing only.
 */
 Flag::Flag()
    : PlayerImmovable(g_flag_descr),
@@ -106,7 +106,7 @@
 /**
  * Create a flag at the given location
 */
-Flag::Flag(EditorGameBase& egbase, Player* owning_player, const Coords& coords)
+Flag::Flag(EditorGameBase& egbase, Player* owning_player, const Coords& coords, Economy* economy)
    : PlayerImmovable(g_flag_descr),
      building_(nullptr),
      ware_capacity_(8),
@@ -124,17 +124,23 @@
 	upcast(Game, game, &egbase);
 
 	if (game) {
-		//  we split a road, or a new, standalone flag is created
-		(road ? road->get_economy() : new Economy(*owning_player))->add_flag(*this);
-
-		if (road)
-			road->presplit(*game, coords);
+		if (economy) {
+			// We're saveloading
+			economy->add_flag(*this);
+		} else {
+			//  we split a road, or a new, standalone flag is created
+			(road ? road->get_economy() : owning_player->create_economy())->add_flag(*this);
+			if (road) {
+				road->presplit(*game, coords);
+			}
+		}
 	}
 
 	init(egbase);
 
-	if (road && game)
+	if (!economy && road && game) {
 		road->postsplit(*game, *this);
+	}
 }
 
 void Flag::set_flag_position(Coords coords) {

=== modified file 'src/economy/flag.h'
--- src/economy/flag.h	2018-04-07 16:59:00 +0000
+++ src/economy/flag.h	2018-05-12 04:24:42 +0000
@@ -73,8 +73,12 @@
 
 	const FlagDescr& descr() const;
 
-	Flag();                                               /// empty flag for savegame loading
-	Flag(EditorGameBase&, Player* owner, const Coords&);  /// create a new flag
+	/// Empty flag, for unit tests only.
+	Flag();
+
+	/// Create a new flag. Only specify an economy during saveloading.
+	/// Otherwise, a new economy will be created automatically if needed.
+	Flag(EditorGameBase&, Player* owner, const Coords&, Economy* economy = nullptr);
 	~Flag() override;
 
 	void load_finish(EditorGameBase&) override;

=== modified file 'src/game_io/game_player_economies_packet.cc'
--- src/game_io/game_player_economies_packet.cc	2018-04-07 16:59:00 +0000
+++ src/game_io/game_player_economies_packet.cc	2018-05-12 04:24:42 +0000
@@ -34,7 +34,7 @@
 namespace Widelands {
 namespace {
 
-constexpr uint16_t kCurrentPacketVersion = 4;
+constexpr uint16_t kCurrentPacketVersion = 5;
 
 bool write_expedition_ship_economy(Economy* economy, const Map& map, FileWrite* fw) {
 	for (Field const* field = &map[0]; field < &map[map.max_index()]; ++field) {
@@ -67,31 +67,21 @@
 		FileRead fr;
 		fr.open(fs, "binary/player_economies");
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version == 3 || packet_version == kCurrentPacketVersion) {
+		if (packet_version == kCurrentPacketVersion) {
 			iterate_players_existing(p, nr_players, game, player) try {
-				// In packet_version 4 we dump the number of economies a player had at
-				// save time to debug
-				// https://bugs.launchpad.net/widelands/+bug/1654897 which is likely
-				// caused by players having more economies at load than they had at
-				// save.
-				Player::Economies& economies = player->economies_;
-				if (packet_version > 3) {
-					const size_t num_economies = fr.unsigned_16();
-					if (num_economies != economies.size()) {
-						throw GameDataError("Num economies on save (%" PRIuS
-						                    ") != Num economies on load (%" PRIuS ")",
-						                    num_economies, economies.size());
-					}
-				}
-
-				for (uint32_t i = 0; i < economies.size(); ++i) {
+				const size_t num_economies = fr.unsigned_32();
+				for (uint32_t i = 0; i < num_economies; ++i) {
 					uint32_t value = fr.unsigned_32();
 					if (value < 0xffffffff) {
 						if (upcast(Flag const, flag, map[value].get_immovable())) {
-							assert(flag->get_economy()->owner().player_number() ==
-							       player->player_number());
-							EconomyDataPacket d(flag->get_economy());
-							d.read(fr);
+							try {
+								assert(flag->get_economy()->owner().player_number() ==
+										 player->player_number());
+								EconomyDataPacket d(flag->get_economy());
+								d.read(fr);
+							} catch (const GameDataError& e) {
+								throw GameDataError("error reading economy data for flag at map index %d: %s", value, e.what());
+							}
 						} else {
 							throw GameDataError("there is no flag at the specified location");
 						}
@@ -102,13 +92,17 @@
 							if (upcast(Ship const, ship, bob)) {
 								// We are interested only in current player's ships
 								if (ship->get_owner() == player) {
-									assert(ship->get_economy());
-									assert(ship->get_economy()->owner().player_number() ==
-									       player->player_number());
-									EconomyDataPacket d(ship->get_economy());
-									d.read(fr);
-									read_this_economy = true;
-									break;
+									try {
+										assert(ship->get_economy());
+										assert(ship->get_economy()->owner().player_number() ==
+												 player->player_number());
+										EconomyDataPacket d(ship->get_economy());
+										d.read(fr);
+										read_this_economy = true;
+										break;
+									} catch (const GameDataError& e) {
+										throw GameDataError("error reading economy data for ship %s: %s", ship->get_shipname().c_str(), e.what());
+									}
 								}
 							}
 							bob = bob->get_next_bob();
@@ -140,13 +134,13 @@
 	const Map& map = game.map();
 	PlayerNumber const nr_players = map.get_nrplayers();
 	iterate_players_existing_const(p, nr_players, game, player) {
-		const Player::Economies& economies = player->economies_;
-		fw.unsigned_16(economies.size());
-		for (Economy* economy : economies) {
-			Flag* arbitrary_flag = economy->get_arbitrary_flag();
+		const auto& economies = player->economies();
+		fw.unsigned_32(economies.size());
+		for (const auto& economy : economies) {
+			Flag* arbitrary_flag = economy.second->get_arbitrary_flag();
 			if (arbitrary_flag != nullptr) {
 				fw.unsigned_32(map.get_fcoords(arbitrary_flag->get_position()).field - &map[0]);
-				EconomyDataPacket d(economy);
+				EconomyDataPacket d(economy.second.get());
 				d.write(fw);
 				continue;
 			}
@@ -154,8 +148,8 @@
 			// No flag found, let's look for a representative Ship. Expeditions
 			// ships are special and have their own economy (which will not have a
 			// flag), therefore we have to special case them.
-			if (!write_expedition_ship_economy(economy, map, &fw)) {
-				throw GameDataError("economy without representative");
+			if (!write_expedition_ship_economy(economy.second.get(), map, &fw)) {
+				throw GameDataError("Player %d: economy %d has no representative", player->player_number(), economy.first);
 			}
 		}
 	}

=== modified file 'src/logic/game.cc'
--- src/logic/game.cc	2018-04-27 20:12:08 +0000
+++ src/logic/game.cc	2018-05-12 04:24:42 +0000
@@ -912,17 +912,16 @@
 		uint32_t wostock = 0;
 		uint32_t wastock = 0;
 
-		for (uint32_t j = 0; j < plr->get_nr_economies(); ++j) {
-			Economy* const eco = plr->get_economy_by_number(j);
+		for (const auto& economy : plr->economies()) {
 			const TribeDescr& tribe = plr->tribe();
 
 			for (const DescriptionIndex& ware_index : tribe.wares()) {
-				wastock += eco->stock_ware(ware_index);
+				wastock += economy.second->stock_ware(ware_index);
 			}
 
 			for (const DescriptionIndex& worker_index : tribe.workers()) {
 				if (tribe.get_worker_descr(worker_index)->type() != MapObjectType::CARRIER) {
-					wostock += eco->stock_worker(worker_index);
+					wostock += economy.second->stock_worker(worker_index);
 				}
 			}
 		}

=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc	2018-04-29 09:20:29 +0000
+++ src/logic/map_objects/tribes/ship.cc	2018-05-12 04:24:42 +0000
@@ -827,14 +827,14 @@
 	expedition_->scouting_direction = WalkingDir::IDLE;
 	expedition_->exploration_start = Coords(0, 0);
 	expedition_->island_explore_direction = IslandExploreDirection::kClockwise;
-	expedition_->economy.reset(new Economy(*get_owner()));
+	expedition_->economy = get_owner()->create_economy();
 
 	// We are no longer in any other economy, but instead are an economy of our
 	// own.
 	fleet_->remove_ship(game, this);
 	assert(fleet_ == nullptr);
 
-	set_economy(game, expedition_->economy.get());
+	set_economy(game, expedition_->economy);
 
 	for (int i = items_.size() - 1; i >= 0; --i) {
 		WareInstance* ware;
@@ -944,7 +944,7 @@
 	if (!get_fleet() || !get_fleet()->has_ports()) {
 		// We lost our last reachable port, so we reset the expedition's state
 		ship_state_ = ShipStates::kExpeditionWaiting;
-		set_economy(game, expedition_->economy.get());
+		set_economy(game, expedition_->economy);
 
 		worker = nullptr;
 		for (ShippingItem& item : items_) {
@@ -958,7 +958,7 @@
 		Notifications::publish(NoteShip(this, NoteShip::Action::kNoPortLeft));
 		return;
 	}
-	assert(get_economy() && get_economy() != expedition_->economy.get());
+	assert(get_economy() && get_economy() != expedition_->economy);
 
 	send_signal(game, "cancel_expedition");
 
@@ -1097,6 +1097,12 @@
 	                                  heading, rt_description, get_position(), serial_)));
 }
 
+Ship::Expedition::~Expedition() {
+	if (economy) {
+		economy->owner().remove_economy(economy->serial());
+	}
+}
+
 /*
 ==============================
 
@@ -1105,7 +1111,7 @@
 ==============================
 */
 
-constexpr uint8_t kCurrentPacketVersion = 6;
+constexpr uint8_t kCurrentPacketVersion = 7;
 
 const Bob::Task* Ship::Loader::get_task(const std::string& name) {
 	if (name == "shipidle" || name == "ship")
@@ -1116,6 +1122,9 @@
 void Ship::Loader::load(FileRead& fr) {
 	Bob::Loader::load(fr);
 
+	// Economy
+	economy_serial_ = fr.unsigned_32();
+
 	// The state the ship is in
 	ship_state_ = static_cast<ShipStates>(fr.unsigned_8());
 
@@ -1176,6 +1185,14 @@
 
 	Ship& ship = get<Ship>();
 
+	// The economy can sometimes be nullptr (e.g. when there are no ports).
+	if (economy_serial_ != kInvalidSerial) {
+		ship.economy_ = ship.get_owner()->get_economy(economy_serial_);
+		if (!ship.economy_) {
+			ship.economy_ = ship.get_owner()->create_economy(economy_serial_);
+		}
+	}
+
 	// restore the state the ship is in
 	ship.ship_state_ = ship_state_;
 
@@ -1185,8 +1202,7 @@
 	// if the ship is on an expedition, restore the expedition specific data
 	if (expedition_) {
 		ship.expedition_.swap(expedition_);
-		ship.expedition_->economy.reset(new Economy(*ship.get_owner()));
-		ship.economy_ = ship.expedition_->economy.get();
+		ship.expedition_->economy = ship.economy_;
 	} else
 		assert(ship_state_ == ShipStates::kTransport);
 
@@ -1202,28 +1218,16 @@
 
 MapObject::Loader* Ship::load(EditorGameBase& egbase, MapObjectLoader& mol, FileRead& fr) {
 	std::unique_ptr<Loader> loader(new Loader);
-
 	try {
 		// The header has been peeled away by the caller
 		uint8_t const packet_version = fr.unsigned_8();
-		if (1 <= packet_version && packet_version <= kCurrentPacketVersion) {
+		if (packet_version == kCurrentPacketVersion) {
 			try {
 				const ShipDescr* descr = nullptr;
 				// Removing this will break the test suite
-				if (packet_version < 5) {
-					std::string tribe_name = fr.string();
-					fr.c_string();  // This used to be the ship's name, which we don't need any more.
-					if (!Widelands::tribe_exists(tribe_name)) {
-						throw GameDataError("Tribe %s does not exist for ship", tribe_name.c_str());
-					}
-					const DescriptionIndex& tribe_index = egbase.tribes().tribe_index(tribe_name);
-					const TribeDescr& tribe_descr = *egbase.tribes().get_tribe_descr(tribe_index);
-					descr = egbase.tribes().get_ship_descr(tribe_descr.ship());
-				} else {
-					std::string name = fr.c_string();
-					const DescriptionIndex& ship_index = egbase.tribes().safe_ship_index(name);
-					descr = egbase.tribes().get_ship_descr(ship_index);
-				}
+				std::string name = fr.c_string();
+				const DescriptionIndex& ship_index = egbase.tribes().safe_ship_index(name);
+				descr = egbase.tribes().get_ship_descr(ship_index);
 				loader->init(egbase, mol, descr->create_object());
 				loader->load(fr);
 			} catch (const WException& e) {
@@ -1246,6 +1250,9 @@
 
 	Bob::save(egbase, mos, fw);
 
+	// The economy can sometimes be nullptr (e.g. when there are no ports).
+	fw.unsigned_32(economy_ != nullptr ? economy_->serial() : kInvalidSerial);
+
 	// state the ship is in
 	fw.unsigned_8(static_cast<uint8_t>(ship_state_));
 

=== modified file 'src/logic/map_objects/tribes/ship.h'
--- src/logic/map_objects/tribes/ship.h	2018-04-29 09:20:29 +0000
+++ src/logic/map_objects/tribes/ship.h	2018-05-12 04:24:42 +0000
@@ -271,13 +271,15 @@
 	std::string shipname_;
 
 	struct Expedition {
+		~Expedition();
+
 		std::vector<Coords> seen_port_buildspaces;
 		bool swimmable[LAST_DIRECTION];
 		bool island_exploration;
 		WalkingDir scouting_direction;
 		Coords exploration_start;
 		IslandExploreDirection island_explore_direction;
-		std::unique_ptr<Economy> economy;
+		Economy* economy; // Owned by Player
 	};
 	std::unique_ptr<Expedition> expedition_;
 
@@ -295,6 +297,7 @@
 		// Initialize everything to make cppcheck happy.
 		uint32_t lastdock_ = 0U;
 		uint32_t destination_ = 0U;
+		Serial economy_serial_;
 		ShipStates ship_state_ = ShipStates::kTransport;
 		std::string shipname_;
 		std::unique_ptr<Expedition> expedition_;

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2018-04-29 09:20:29 +0000
+++ src/logic/player.cc	2018-05-12 04:24:42 +0000
@@ -798,37 +798,50 @@
 /*
  * Economy stuff below
  */
-void Player::add_economy(Economy& economy) {
-	if (!has_economy(economy))
-		economies_.push_back(&economy);
-}
-
-void Player::remove_economy(Economy& economy) {
-	for (std::vector<Economy*>::iterator economy_iter = economies_.begin();
-	     economy_iter != economies_.end(); ++economy_iter)
-		if (*economy_iter == &economy) {
-			economies_.erase(economy_iter);
-			return;
-		}
-}
-
-bool Player::has_economy(Economy& economy) const {
-	for (Economy* temp_economy : economies_) {
-		if (temp_economy == &economy) {
-			return true;
-		}
+Economy* Player::create_economy() {
+	std::unique_ptr<Economy> eco(new Economy(*this));
+	const Serial serial = eco->serial();
+
+	assert(economies_.count(serial) == 0);
+	economies_.emplace(std::make_pair(serial, std::move(eco)));
+	assert(economies_.at(serial)->serial() == serial);
+	assert(economies_.count(serial) == 1);
+
+	return get_economy(serial);
+}
+
+Economy* Player::create_economy(Serial serial) {
+	std::unique_ptr<Economy> eco(new Economy(*this, serial));
+
+	assert(economies_.count(serial) == 0);
+	economies_.emplace(std::make_pair(serial, std::move(eco)));
+	assert(economies_.at(serial)->serial() == serial);
+	assert(economies_.count(serial) == 1);
+
+	return get_economy(serial);
+}
+
+void Player::remove_economy(Serial serial) {
+	assert(has_economy(serial));
+	economies_.erase(economies_.find(serial));
+	assert(!has_economy(serial));
+}
+
+const std::map<Serial,std::unique_ptr<Economy>>& Player::economies() const {
+	return economies_;
+}
+
+Economy* Player::get_economy(Widelands::Serial serial) const {
+	if (economies_.count(serial) == 0) {
+		return nullptr;
 	}
-	return false;
-}
-
-Player::Economies::size_type Player::get_economy_number(Economy const* const economy) const {
-	Economies::const_iterator const economies_end = economies_.end(),
-	                                economies_begin = economies_.begin();
-	for (Economies::const_iterator it = economies_begin; it != economies_end; ++it)
-		if (*it == economy)
-			return it - economies_begin;
-	NEVER_HERE();
-}
+	return economies_.at(serial).get();
+}
+
+bool Player::has_economy(Widelands::Serial serial) const {
+	return economies_.count(serial) != 0;
+}
+
 
 /************  Military stuff  **********/
 
@@ -1156,11 +1169,8 @@
 	// Calculate stocks
 	std::vector<uint32_t> stocks(egbase().tribes().nrwares());
 
-	const uint32_t nrecos = get_nr_economies();
-	for (uint32_t i = 0; i < nrecos; ++i) {
-		const std::vector<Widelands::Warehouse*>& warehouses = get_economy_by_number(i)->warehouses();
-
-		for (Widelands::Warehouse* warehouse : warehouses) {
+	for (const auto& economy : economies()) {
+		for (Widelands::Warehouse* warehouse : economy.second->warehouses()) {
 			const Widelands::WareList& wares = warehouse->get_wares();
 			for (size_t id = 0; id < stocks.size(); ++id) {
 				stocks[id] += wares.stock(DescriptionIndex(id));

=== modified file 'src/logic/player.h'
--- src/logic/player.h	2018-04-29 09:20:29 +0000
+++ src/logic/player.h	2018-05-12 04:24:42 +0000
@@ -25,6 +25,7 @@
 #include <unordered_map>
 
 #include "base/macros.h"
+#include "economy/economy.h"
 #include "graphic/color.h"
 #include "graphic/playercolor.h"
 #include "logic/editor_game_base.h"
@@ -40,7 +41,6 @@
 class Node;
 namespace Widelands {
 
-class Economy;
 struct Path;
 struct PlayerImmovable;
 class Soldier;
@@ -515,18 +515,12 @@
 	void enhance_building(Building*, DescriptionIndex index_of_new_building);
 	void dismantle_building(Building*);
 
-	// Economy stuff
-	void add_economy(Economy&);
-	void remove_economy(Economy&);
-	bool has_economy(Economy&) const;
-	using Economies = std::vector<Economy*>;
-	Economies::size_type get_economy_number(Economy const*) const;
-	Economy* get_economy_by_number(Economies::size_type const i) const {
-		return economies_[i];
-	}
-	uint32_t get_nr_economies() const {
-		return economies_.size();
-	}
+	Economy* create_economy();
+	Economy* create_economy(Serial serial); // For saveloading only
+	void remove_economy(Serial serial);
+	const std::map<Serial,std::unique_ptr<Economy>>& economies() const;
+	Economy* get_economy(Widelands::Serial serial) const;
+	bool has_economy(Widelands::Serial serial) const;
 
 	uint32_t get_current_produced_statistics(uint8_t);
 
@@ -644,7 +638,7 @@
 	Field* fields_;
 	std::vector<bool> allowed_worker_types_;
 	std::vector<bool> allowed_building_types_;
-	Economies economies_;
+	std::map<Serial,std::unique_ptr<Economy>> economies_;
 	std::set<Serial> ships_;
 	std::string name_;  // Player name
 	std::string ai_;    /**< Name of preferred AI implementation */

=== modified file 'src/logic/playercommand.cc'
--- src/logic/playercommand.cc	2018-04-07 16:59:00 +0000
+++ src/logic/playercommand.cc	2018-05-12 04:24:42 +0000
@@ -1229,8 +1229,8 @@
 
 void CmdSetWareTargetQuantity::execute(Game& game) {
 	Player* player = game.get_player(sender());
-	if (economy() < player->get_nr_economies() && game.tribes().ware_exists(ware_type())) {
-		player->get_economy_by_number(economy())->set_ware_target_quantity(
+	if (player->has_economy(economy()) && game.tribes().ware_exists(ware_type())) {
+		player->get_economy(economy())->set_ware_target_quantity(
 		   ware_type(), permanent_, duetime());
 	}
 }
@@ -1280,9 +1280,9 @@
 void CmdResetWareTargetQuantity::execute(Game& game) {
 	Player* player = game.get_player(sender());
 	const TribeDescr& tribe = player->tribe();
-	if (economy() < player->get_nr_economies() && game.tribes().ware_exists(ware_type())) {
+	if (player->has_economy(economy()) && game.tribes().ware_exists(ware_type())) {
 		const int count = tribe.get_ware_descr(ware_type())->default_target_quantity(tribe.name());
-		player->get_economy_by_number(economy())->set_ware_target_quantity(
+		player->get_economy(economy())->set_ware_target_quantity(
 		   ware_type(), count, duetime());
 	}
 }
@@ -1328,8 +1328,8 @@
 
 void CmdSetWorkerTargetQuantity::execute(Game& game) {
 	Player* player = game.get_player(sender());
-	if (economy() < player->get_nr_economies() && game.tribes().worker_exists(ware_type())) {
-		player->get_economy_by_number(economy())->set_worker_target_quantity(
+	if (player->has_economy(economy()) && game.tribes().worker_exists(ware_type())) {
+		player->get_economy(economy())->set_worker_target_quantity(
 		   ware_type(), permanent_, duetime());
 	}
 }
@@ -1379,9 +1379,9 @@
 void CmdResetWorkerTargetQuantity::execute(Game& game) {
 	Player* player = game.get_player(sender());
 	const TribeDescr& tribe = player->tribe();
-	if (economy() < player->get_nr_economies() && game.tribes().ware_exists(ware_type())) {
+	if (player->has_economy(economy()) && game.tribes().ware_exists(ware_type())) {
 		const int count = tribe.get_ware_descr(ware_type())->default_target_quantity(tribe.name());
-		player->get_economy_by_number(economy())->set_worker_target_quantity(
+		player->get_economy(economy())->set_worker_target_quantity(
 		   ware_type(), count, duetime());
 	}
 }

=== modified file 'src/logic/playercommand.h'
--- src/logic/playercommand.h	2018-04-07 16:59:00 +0000
+++ src/logic/playercommand.h	2018-05-12 04:24:42 +0000
@@ -543,7 +543,7 @@
 	void serialize(StreamWrite&) override;
 
 protected:
-	uint32_t economy() const {
+	Serial economy() const {
 		return economy_;
 	}
 	DescriptionIndex ware_type() const {
@@ -551,7 +551,7 @@
 	}
 
 private:
-	uint32_t economy_;
+	Serial economy_;
 	DescriptionIndex ware_type_;
 };
 

=== modified file 'src/logic/widelands.h'
--- src/logic/widelands.h	2018-04-07 16:59:00 +0000
+++ src/logic/widelands.h	2018-05-12 04:24:42 +0000
@@ -84,6 +84,7 @@
 }
 
 using Serial = uint32_t;  /// Serial number for MapObject.
+constexpr Serial kInvalidSerial = std::numeric_limits<uint32_t>::max();
 
 using Direction = uint8_t;
 

=== modified file 'src/map_io/map_flag_packet.cc'
--- src/map_io/map_flag_packet.cc	2018-04-07 16:59:00 +0000
+++ src/map_io/map_flag_packet.cc	2018-05-12 04:24:42 +0000
@@ -35,7 +35,7 @@
 
 namespace Widelands {
 
-constexpr uint16_t kCurrentPacketVersion = 1;
+constexpr uint16_t kCurrentPacketVersion = 2;
 
 void MapFlagPacket::read(FileSystem& fs,
                          EditorGameBase& egbase,
@@ -63,6 +63,8 @@
 					throw GameDataError("Invalid player number: %i.", owner);
 				}
 
+				const Serial economy_serial = fr.unsigned_32();
+
 				Serial const serial = fr.unsigned_32();
 
 				try {
@@ -94,13 +96,21 @@
 
 					//  No flag lives on more than one place.
 
+					// Get economy from serial
+					Player* player = egbase.get_player(owner);
+					Economy* economy = player->get_economy(economy_serial);
+					if (!economy) {
+						economy = player->create_economy(economy_serial);
+					}
+
 					//  Now, create this Flag. Directly create it, do not call
 					//  the player class since we recreate the data in another
 					//  packet. We always create this, no matter what skip is
 					//  since we have to read the data packets. We delete this
 					//  object later again, if it is not wanted.
-					mol.register_object<Flag>(
-					   serial, *new Flag(dynamic_cast<Game&>(egbase), egbase.get_player(owner), fc));
+					Flag* flag = new Flag(dynamic_cast<Game&>(egbase), player, fc, economy);
+					mol.register_object<Flag>(serial, *flag);
+
 				} catch (const WException& e) {
 					throw GameDataError(
 					   "%u (at (%i, %i), owned by player %u): %s", serial, fc.x, fc.y, owner, e.what());
@@ -133,6 +143,7 @@
 
 			fw.unsigned_8(1);
 			fw.unsigned_8(flag->owner().player_number());
+			fw.unsigned_32(flag->economy().serial());
 			fw.unsigned_32(mos.register_object(*flag));
 		} else  //  no existence, no owner
 			fw.unsigned_8(0);

=== modified file 'src/scripting/lua_bases.cc'
--- src/scripting/lua_bases.cc	2018-04-07 16:59:00 +0000
+++ src/scripting/lua_bases.cc	2018-05-12 04:24:42 +0000
@@ -655,8 +655,8 @@
 	const DescriptionIndex worker = player.tribe().worker_index(workername);
 
 	uint32_t nworkers = 0;
-	for (uint32_t i = 0; i < player.get_nr_economies(); ++i) {
-		nworkers += player.get_economy_by_number(i)->stock_worker(worker);
+	for (const auto& economy : player.economies()) {
+		nworkers += economy.second->stock_worker(worker);
 	}
 	lua_pushuint32(L, nworkers);
 	return 1;
@@ -681,8 +681,8 @@
 	const DescriptionIndex ware = egbase.tribes().ware_index(warename);
 
 	uint32_t nwares = 0;
-	for (uint32_t i = 0; i < player.get_nr_economies(); ++i) {
-		nwares += player.get_economy_by_number(i)->stock_ware(ware);
+	for (const auto& economy : player.economies()) {
+		nwares += economy.second->stock_ware(ware);
 	}
 	lua_pushuint32(L, nwares);
 	return 1;

=== modified file 'src/scripting/lua_game.cc'
--- src/scripting/lua_game.cc	2018-04-16 07:03:12 +0000
+++ src/scripting/lua_game.cc	2018-05-12 04:24:42 +0000
@@ -174,16 +174,16 @@
 */
 int LuaPlayer::get_defeated(lua_State* L) {
 	Player& p = get(L, get_egbase(L));
-	bool have_warehouses = false;
+	bool is_defeated = true;
 
-	for (uint32_t economy_nr = 0; economy_nr < p.get_nr_economies(); economy_nr++) {
-		if (!p.get_economy_by_number(economy_nr)->warehouses().empty()) {
-			have_warehouses = true;
+	for (const auto& economy : p.economies()) {
+		if (!economy.second->warehouses().empty()) {
+			is_defeated = false;
 			break;
 		}
 	}
 
-	lua_pushboolean(L, !have_warehouses);
+	lua_pushboolean(L, is_defeated);
 	return 1;
 }
 
@@ -812,10 +812,8 @@
 					break;
 				}
 			}
-			for (uint32_t j = player.get_nr_economies(); j;) {
-				Economy& economy = *player.get_economy_by_number(--j);
-
-				for (Warehouse* warehouse : economy.warehouses()) {
+			for (const auto& economy: player.economies()) {
+				for (Warehouse* warehouse : economy.second->warehouses()) {
 					warehouse->enable_spawn(game, worker_types_without_cost_index);
 				}
 			}

=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc	2018-04-11 18:42:55 +0000
+++ src/scripting/lua_map.cc	2018-05-12 04:24:42 +0000
@@ -3569,16 +3569,16 @@
 	const Widelands::Economy* economy = get();
 	const Widelands::Player& player = economy->owner();
 	PERS_UINT32("player", player.player_number());
-	PERS_UINT32("economy", player.get_economy_number(economy));
+	PERS_UINT32("economy", economy->serial());
 }
 
 void LuaEconomy::__unpersist(lua_State* L) {
 	Widelands::PlayerNumber player_number;
-	size_t economy_number;
+	Widelands::Serial economy_serial;
 	UNPERS_UINT32("player", player_number);
-	UNPERS_UINT32("economy", economy_number);
+	UNPERS_UINT32("economy", economy_serial);
 	const Widelands::Player& player = get_egbase(L).player(player_number);
-	set_economy_pointer(player.get_economy_by_number(economy_number));
+	set_economy_pointer(player.get_economy(economy_serial));
 }
 
 /* RST

=== modified file 'src/wui/economy_options_window.cc'
--- src/wui/economy_options_window.cc	2018-04-07 16:59:00 +0000
+++ src/wui/economy_options_window.cc	2018-05-12 04:24:42 +0000
@@ -36,10 +36,11 @@
                                            Widelands::Economy* economy,
                                            bool can_act)
    : UI::Window(parent, "economy_options", 0, 0, 0, 0, _("Economy options")),
-     economy_(economy),
+	  serial_(economy->serial()),
+	  player_(&economy->owner()),
      tabpanel_(this, g_gr->images().get("images/ui_basic/but1.png")),
-     ware_panel_(new EconomyOptionsPanel(&tabpanel_, can_act, Widelands::wwWARE, economy)),
-     worker_panel_(new EconomyOptionsPanel(&tabpanel_, can_act, Widelands::wwWORKER, economy)) {
+     ware_panel_(new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWARE)),
+     worker_panel_(new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWORKER)) {
 	set_center_panel(&tabpanel_);
 
 	tabpanel_.add("wares", g_gr->images().get(pic_tab_wares), ware_panel_, _("Wares"));
@@ -50,27 +51,29 @@
 }
 
 EconomyOptionsWindow::~EconomyOptionsWindow() {
-	if (economy_ != nullptr) {
-		economy_->set_has_window(false);
+	Widelands::Economy* economy = player_->get_economy(serial_);
+	if (economy != nullptr) {
+		economy->set_has_window(false);
 	}
 }
 
 void EconomyOptionsWindow::on_economy_note(const Widelands::NoteEconomy& note) {
-	if (note.old_economy == economy_) {
+	if (note.old_economy == serial_) {
 		switch (note.action) {
-		case Widelands::NoteEconomy::Action::kMerged:
-			economy_ = note.new_economy;
+		case Widelands::NoteEconomy::Action::kMerged: {
+			serial_ = note.new_economy;
+			Widelands::Economy* economy = player_->get_economy(serial_);
+			if (economy == nullptr) {
+				die();
+				return;
+			}
+			economy->set_has_window(true);
 			ware_panel_->set_economy(note.new_economy);
 			worker_panel_->set_economy(note.new_economy);
-			economy_->set_has_window(true);
 			move_to_top();
-			break;
+		} break;
 		case Widelands::NoteEconomy::Action::kDeleted:
 			// Make sure that the panels stop thinking first.
-			ware_panel_->die();
-			worker_panel_->die();
-			economy_->set_has_window(false);
-			economy_ = nullptr;
 			die();
 			break;
 		}
@@ -80,12 +83,13 @@
 EconomyOptionsWindow::TargetWaresDisplay::TargetWaresDisplay(UI::Panel* const parent,
                                                              int32_t const x,
                                                              int32_t const y,
+																				 Widelands::Serial serial,
+																				 Widelands::Player* player,
                                                              Widelands::WareWorker type,
-                                                             bool selectable,
-                                                             Widelands::Economy* economy)
-   : AbstractWaresDisplay(parent, x, y, economy->owner().tribe(), type, selectable),
-     economy_(economy) {
-	const Widelands::TribeDescr& owner_tribe = economy->owner().tribe();
+                                                             bool selectable)
+   : AbstractWaresDisplay(parent, x, y, player->tribe(), type, selectable),
+     serial_(serial), player_(player) {
+	const Widelands::TribeDescr& owner_tribe = player->tribe();
 	if (type == Widelands::wwWORKER) {
 		for (const Widelands::DescriptionIndex& worker_index : owner_tribe.workers()) {
 			const Widelands::WorkerDescr* worker_descr = owner_tribe.get_worker_descr(worker_index);
@@ -103,29 +107,36 @@
 	}
 }
 
-void EconomyOptionsWindow::TargetWaresDisplay::set_economy(Widelands::Economy* economy) {
-	economy_ = economy;
+void EconomyOptionsWindow::TargetWaresDisplay::set_economy(Widelands::Serial serial) {
+	serial_ = serial;
 }
 
 std::string
 EconomyOptionsWindow::TargetWaresDisplay::info_for_ware(Widelands::DescriptionIndex const ware) {
+	Widelands::Economy* economy = player_->get_economy(serial_);
+	if (economy == nullptr) {
+		die();
+		return *(new std::string());
+	}
 	return boost::lexical_cast<std::string>(get_type() == Widelands::wwWORKER ?
-	                                           economy_->worker_target_quantity(ware).permanent :
-	                                           economy_->ware_target_quantity(ware).permanent);
+	                                           economy->worker_target_quantity(ware).permanent :
+	                                           economy->ware_target_quantity(ware).permanent);
 }
 
 /**
  * Wraps the wares/workers display together with some buttons
  */
 EconomyOptionsWindow::EconomyOptionsPanel::EconomyOptionsPanel(UI::Panel* parent,
+																					Widelands::Serial serial,
+																					Widelands::Player* player,
                                                                bool can_act,
-                                                               Widelands::WareWorker type,
-                                                               Widelands::Economy* economy)
+                                                               Widelands::WareWorker type)
    : UI::Box(parent, 0, 0, UI::Box::Vertical),
+	  serial_(serial),
+	  player_(player),
      type_(type),
-     economy_(economy),
      can_act_(can_act),
-     display_(this, 0, 0, type_, can_act_, economy) {
+     display_(this, 0, 0, serial_, player_, type_, can_act_) {
 	add(&display_, UI::Box::Resizing::kFullSize);
 
 	if (!can_act_) {
@@ -155,37 +166,34 @@
 	buttons->add(b);
 }
 
-void EconomyOptionsWindow::EconomyOptionsPanel::set_economy(Widelands::Economy* economy) {
-	economy_ = economy;
-	display_.set_economy(economy);
+void EconomyOptionsWindow::EconomyOptionsPanel::set_economy(Widelands::Serial serial) {
+	serial_ = serial;
+	display_.set_economy(serial);
 }
 
 void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int amount) {
-	auto& owner = economy_->owner();
-	Widelands::Game& game = dynamic_cast<Widelands::Game&>(owner.egbase());
+	Widelands::Economy* economy = player_->get_economy(serial_);
+	if (economy == nullptr) {
+		die();
+		return;
+	}
+	Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
 	const bool is_wares = type_ == Widelands::wwWARE;
-	const auto& items = is_wares ? owner.tribe().wares() : owner.tribe().workers();
+	const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
 	for (const Widelands::DescriptionIndex& index : items) {
 		if (display_.ware_selected(index)) {
-			const Widelands::Economy::TargetQuantity& tq = is_wares ?
-			                                                  economy_->ware_target_quantity(index) :
-			                                                  economy_->worker_target_quantity(index);
+			const Widelands::Economy::TargetQuantity& tq =
+			   is_wares ? economy->ware_target_quantity(index) : economy->worker_target_quantity(index);
 			// Don't allow negative new amount.
 			if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) {
 				if (is_wares) {
-					// TODO(sirver): This is crashy. Nobody guarantees that the
-					// economy_number_ is still the same when this command finally
-					// is executed. Player::remove_economy relabels economies on
-					// deletion. Economies require a unique, never changing id, same
-					// as map objects.
 					game.send_player_command(*new Widelands::CmdSetWareTargetQuantity(
-					   game.get_gametime(), owner.player_number(), owner.get_economy_number(economy_),
+					   game.get_gametime(), player_->player_number(), serial_,
 					   index, tq.permanent + amount));
 				} else {
-					// TODO(sirver): Same as above
 					game.send_player_command(*new Widelands::CmdSetWorkerTargetQuantity(
-					   game.get_gametime(), owner.player_number(), owner.get_economy_number(economy_),
-					   index, tq.permanent + amount));
+					   game.get_gametime(), player_->player_number(), serial_, index,
+					   tq.permanent + amount));
 				}
 			}
 		}
@@ -193,19 +201,18 @@
 }
 
 void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() {
-	auto& owner = economy_->owner();
-	Widelands::Game& game = dynamic_cast<Widelands::Game&>(owner.egbase());
+	Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
 	const bool is_wares = type_ == Widelands::wwWARE;
-	const auto& items = is_wares ? owner.tribe().wares() : owner.tribe().workers();
+	const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
 	for (const Widelands::DescriptionIndex& index : items) {
 		if (display_.ware_selected(index)) {
 			if (is_wares) {
 				game.send_player_command(*new Widelands::CmdResetWareTargetQuantity(
-				   game.get_gametime(), owner.player_number(), owner.get_economy_number(economy_),
+				   game.get_gametime(), player_->player_number(), serial_,
 				   index));
 			} else {
 				game.send_player_command(*new Widelands::CmdResetWorkerTargetQuantity(
-				   game.get_gametime(), owner.player_number(), owner.get_economy_number(economy_),
+				   game.get_gametime(), player_->player_number(), serial_,
 				   index));
 			}
 		}

=== modified file 'src/wui/economy_options_window.h'
--- src/wui/economy_options_window.h	2018-04-07 16:59:00 +0000
+++ src/wui/economy_options_window.h	2018-05-12 04:24:42 +0000
@@ -39,17 +39,19 @@
 		TargetWaresDisplay(UI::Panel* const parent,
 		                   int32_t const x,
 		                   int32_t const y,
+								 Widelands::Serial serial,
+							    Widelands::Player* player,
 		                   Widelands::WareWorker type,
-		                   bool selectable,
-		                   Widelands::Economy* economy);
+		                   bool selectable);
 
-		void set_economy(Widelands::Economy*);
+		void set_economy(Widelands::Serial serial);
 
 	protected:
 		std::string info_for_ware(Widelands::DescriptionIndex const ware) override;
 
 	private:
-		Widelands::Economy* economy_;
+		Widelands::Serial serial_;
+		Widelands::Player* player_;
 	};
 
 	/**
@@ -57,17 +59,19 @@
 	 */
 	struct EconomyOptionsPanel : UI::Box {
 		EconomyOptionsPanel(UI::Panel* parent,
+								  Widelands::Serial serial,
+								  Widelands::Player* player,
 		                    bool can_act,
-		                    Widelands::WareWorker type,
-		                    Widelands::Economy* economy);
+		                    Widelands::WareWorker type);
 
-		void set_economy(Widelands::Economy*);
+		void set_economy(Widelands::Serial serial);
 		void change_target(int amount);
 		void reset_target();
 
 	private:
+		Widelands::Serial serial_;
+		Widelands::Player* player_;
 		Widelands::WareWorker type_;
-		Widelands::Economy* economy_;
 		bool can_act_;
 		TargetWaresDisplay display_;
 	};
@@ -75,7 +79,8 @@
 	/// Actions performed when a NoteEconomyWindow is received.
 	void on_economy_note(const Widelands::NoteEconomy& note);
 
-	Widelands::Economy* economy_;
+	Widelands::Serial serial_;
+	Widelands::Player* player_;
 	UI::TabPanel tabpanel_;
 	EconomyOptionsPanel* ware_panel_;
 	EconomyOptionsPanel* worker_panel_;

=== modified file 'src/wui/stock_menu.cc'
--- src/wui/stock_menu.cc	2018-04-07 16:59:00 +0000
+++ src/wui/stock_menu.cc	2018-05-12 04:24:42 +0000
@@ -75,11 +75,9 @@
 void StockMenu::fill_total_waresdisplay(WaresDisplay* waresdisplay, Widelands::WareWorker type) {
 	waresdisplay->remove_all_warelists();
 	const Widelands::Player& player = *player_.get_player();
-	const uint32_t nrecos = player.get_nr_economies();
-	for (uint32_t i = 0; i < nrecos; ++i)
-		waresdisplay->add_warelist(type == Widelands::wwWARE ?
-		                              player.get_economy_by_number(i)->get_wares() :
-		                              player.get_economy_by_number(i)->get_workers());
+	for (const auto& economy : player.economies()) {
+		waresdisplay->add_warelist(type == Widelands::wwWARE ? economy.second->get_wares() : economy.second->get_workers());
+	}
 }
 
 /**
@@ -89,16 +87,10 @@
 void StockMenu::fill_warehouse_waresdisplay(WaresDisplay* waresdisplay,
                                             Widelands::WareWorker type) {
 	waresdisplay->remove_all_warelists();
-	const Widelands::Player& player = *player_.get_player();
-	const uint32_t nrecos = player.get_nr_economies();
-	for (uint32_t i = 0; i < nrecos; ++i) {
-		const std::vector<Widelands::Warehouse*>& warehouses =
-		   player.get_economy_by_number(i)->warehouses();
-
-		for (std::vector<Widelands::Warehouse*>::const_iterator it = warehouses.begin();
-		     it != warehouses.end(); ++it) {
-			waresdisplay->add_warelist(type == Widelands::wwWARE ? (*it)->get_wares() :
-			                                                       (*it)->get_workers());
+	for (const auto& economy : player_.player().economies()) {
+		for (const auto* warehouse: economy.second->warehouses()) {
+			waresdisplay->add_warelist(type == Widelands::wwWARE ? warehouse->get_wares() :
+			                                                       warehouse->get_workers());
 		}
 	}
 }

=== removed file 'test/maps/Aquila.wmf/binary/bob'
Binary files test/maps/Aquila.wmf/binary/bob	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/bob	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/Aquila.wmf/binary/bob_data'
Binary files test/maps/Aquila.wmf/binary/bob_data	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/bob_data	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/Aquila.wmf/binary/building'
Binary files test/maps/Aquila.wmf/binary/building	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/building	1970-01-01 00:00:00 +0000 differ
=== added file 'test/maps/Aquila.wmf/binary/exploration'
Binary files test/maps/Aquila.wmf/binary/exploration	1970-01-01 00:00:00 +0000 and test/maps/Aquila.wmf/binary/exploration	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/Aquila.wmf/binary/exploration'
Binary files test/maps/Aquila.wmf/binary/exploration	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/exploration	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/Aquila.wmf/binary/flag'
Binary files test/maps/Aquila.wmf/binary/flag	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/flag	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/Aquila.wmf/binary/mapobjects'
Binary files test/maps/Aquila.wmf/binary/mapobjects	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/mapobjects	2018-05-12 04:24:42 +0000 differ
=== added file 'test/maps/Aquila.wmf/binary/node_ownership'
Binary files test/maps/Aquila.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 and test/maps/Aquila.wmf/binary/node_ownership	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/Aquila.wmf/binary/node_ownership'
Binary files test/maps/Aquila.wmf/binary/node_ownership	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/Aquila.wmf/binary/resource'
Binary files test/maps/Aquila.wmf/binary/resource	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/resource	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/Aquila.wmf/binary/road'
Binary files test/maps/Aquila.wmf/binary/road	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/road	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/Aquila.wmf/binary/terrain'
Binary files test/maps/Aquila.wmf/binary/terrain	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/terrain	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/Aquila.wmf/binary/ware'
Binary files test/maps/Aquila.wmf/binary/ware	2016-08-13 18:47:51 +0000 and test/maps/Aquila.wmf/binary/ware	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/expedition.wmf/binary/building'
Binary files test/maps/expedition.wmf/binary/building	2013-10-05 07:50:17 +0000 and test/maps/expedition.wmf/binary/building	1970-01-01 00:00:00 +0000 differ
=== added file 'test/maps/expedition.wmf/binary/exploration'
Binary files test/maps/expedition.wmf/binary/exploration	1970-01-01 00:00:00 +0000 and test/maps/expedition.wmf/binary/exploration	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/expedition.wmf/binary/exploration'
Binary files test/maps/expedition.wmf/binary/exploration	2013-10-05 07:50:17 +0000 and test/maps/expedition.wmf/binary/exploration	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/expedition.wmf/binary/flag'
Binary files test/maps/expedition.wmf/binary/flag	2013-10-05 07:50:17 +0000 and test/maps/expedition.wmf/binary/flag	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/expedition.wmf/binary/mapobjects'
Binary files test/maps/expedition.wmf/binary/mapobjects	2013-10-05 07:50:17 +0000 and test/maps/expedition.wmf/binary/mapobjects	2018-05-12 04:24:42 +0000 differ
=== added file 'test/maps/expedition.wmf/binary/node_ownership'
Binary files test/maps/expedition.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 and test/maps/expedition.wmf/binary/node_ownership	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/expedition.wmf/binary/node_ownership'
Binary files test/maps/expedition.wmf/binary/node_ownership	2013-10-05 07:50:17 +0000 and test/maps/expedition.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/expedition.wmf/binary/resource'
Binary files test/maps/expedition.wmf/binary/resource	2015-04-14 19:03:11 +0000 and test/maps/expedition.wmf/binary/resource	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/expedition.wmf/binary/road'
Binary files test/maps/expedition.wmf/binary/road	2013-10-05 07:50:17 +0000 and test/maps/expedition.wmf/binary/road	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/expedition.wmf/binary/terrain'
Binary files test/maps/expedition.wmf/binary/terrain	2015-04-14 19:03:11 +0000 and test/maps/expedition.wmf/binary/terrain	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_persistence.wmf/binary/bob'
Binary files test/maps/lua_persistence.wmf/binary/bob	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/bob	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/lua_persistence.wmf/binary/building'
Binary files test/maps/lua_persistence.wmf/binary/building	2015-04-15 10:06:45 +0000 and test/maps/lua_persistence.wmf/binary/building	1970-01-01 00:00:00 +0000 differ
=== added file 'test/maps/lua_persistence.wmf/binary/exploration'
Binary files test/maps/lua_persistence.wmf/binary/exploration	1970-01-01 00:00:00 +0000 and test/maps/lua_persistence.wmf/binary/exploration	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_persistence.wmf/binary/exploration'
Binary files test/maps/lua_persistence.wmf/binary/exploration	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/exploration	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/lua_persistence.wmf/binary/flag'
Binary files test/maps/lua_persistence.wmf/binary/flag	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/flag	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/lua_persistence.wmf/binary/mapobjects'
Binary files test/maps/lua_persistence.wmf/binary/mapobjects	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/mapobjects	2018-05-12 04:24:42 +0000 differ
=== added file 'test/maps/lua_persistence.wmf/binary/node_ownership'
Binary files test/maps/lua_persistence.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 and test/maps/lua_persistence.wmf/binary/node_ownership	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_persistence.wmf/binary/node_ownership'
Binary files test/maps/lua_persistence.wmf/binary/node_ownership	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/lua_persistence.wmf/binary/resource'
Binary files test/maps/lua_persistence.wmf/binary/resource	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/resource	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_persistence.wmf/binary/road'
Binary files test/maps/lua_persistence.wmf/binary/road	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/road	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/lua_persistence.wmf/binary/terrain'
Binary files test/maps/lua_persistence.wmf/binary/terrain	2012-02-15 21:25:34 +0000 and test/maps/lua_persistence.wmf/binary/terrain	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_testsuite.wmf/binary/bob'
Binary files test/maps/lua_testsuite.wmf/binary/bob	2012-02-15 21:25:34 +0000 and test/maps/lua_testsuite.wmf/binary/bob	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/lua_testsuite.wmf/binary/building'
Binary files test/maps/lua_testsuite.wmf/binary/building	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/building	1970-01-01 00:00:00 +0000 differ
=== added file 'test/maps/lua_testsuite.wmf/binary/exploration'
Binary files test/maps/lua_testsuite.wmf/binary/exploration	1970-01-01 00:00:00 +0000 and test/maps/lua_testsuite.wmf/binary/exploration	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_testsuite.wmf/binary/exploration'
Binary files test/maps/lua_testsuite.wmf/binary/exploration	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/exploration	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/lua_testsuite.wmf/binary/flag'
Binary files test/maps/lua_testsuite.wmf/binary/flag	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/flag	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/lua_testsuite.wmf/binary/mapobjects'
Binary files test/maps/lua_testsuite.wmf/binary/mapobjects	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/mapobjects	2018-05-12 04:24:42 +0000 differ
=== added file 'test/maps/lua_testsuite.wmf/binary/node_ownership'
Binary files test/maps/lua_testsuite.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 and test/maps/lua_testsuite.wmf/binary/node_ownership	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_testsuite.wmf/binary/node_ownership'
Binary files test/maps/lua_testsuite.wmf/binary/node_ownership	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/lua_testsuite.wmf/binary/resource'
Binary files test/maps/lua_testsuite.wmf/binary/resource	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/resource	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/lua_testsuite.wmf/binary/road'
Binary files test/maps/lua_testsuite.wmf/binary/road	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/road	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/lua_testsuite.wmf/binary/terrain'
Binary files test/maps/lua_testsuite.wmf/binary/terrain	2015-04-14 18:34:40 +0000 and test/maps/lua_testsuite.wmf/binary/terrain	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/plain.wmf/binary/building'
Binary files test/maps/plain.wmf/binary/building	2014-06-20 14:38:48 +0000 and test/maps/plain.wmf/binary/building	1970-01-01 00:00:00 +0000 differ
=== added file 'test/maps/plain.wmf/binary/exploration'
Binary files test/maps/plain.wmf/binary/exploration	1970-01-01 00:00:00 +0000 and test/maps/plain.wmf/binary/exploration	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/plain.wmf/binary/exploration'
Binary files test/maps/plain.wmf/binary/exploration	2014-06-20 14:38:48 +0000 and test/maps/plain.wmf/binary/exploration	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/plain.wmf/binary/flag'
Binary files test/maps/plain.wmf/binary/flag	2014-06-20 14:38:48 +0000 and test/maps/plain.wmf/binary/flag	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/plain.wmf/binary/mapobjects'
Binary files test/maps/plain.wmf/binary/mapobjects	2014-06-20 14:38:48 +0000 and test/maps/plain.wmf/binary/mapobjects	2018-05-12 04:24:42 +0000 differ
=== added file 'test/maps/plain.wmf/binary/node_ownership'
Binary files test/maps/plain.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 and test/maps/plain.wmf/binary/node_ownership	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/plain.wmf/binary/node_ownership'
Binary files test/maps/plain.wmf/binary/node_ownership	2014-06-20 14:38:48 +0000 and test/maps/plain.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/plain.wmf/binary/resource'
Binary files test/maps/plain.wmf/binary/resource	2014-06-22 17:09:42 +0000 and test/maps/plain.wmf/binary/resource	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/plain.wmf/binary/road'
Binary files test/maps/plain.wmf/binary/road	2014-06-20 14:38:48 +0000 and test/maps/plain.wmf/binary/road	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/plain.wmf/binary/terrain'
Binary files test/maps/plain.wmf/binary/terrain	2014-06-22 17:09:42 +0000 and test/maps/plain.wmf/binary/terrain	2018-05-12 04:24:42 +0000 differ
=== modified file 'test/maps/port_space.wmf/binary/mapobjects'
Binary files test/maps/port_space.wmf/binary/mapobjects	2016-02-08 20:44:17 +0000 and test/maps/port_space.wmf/binary/mapobjects	2018-05-12 04:24:42 +0000 differ
=== modified file 'test/maps/port_space.wmf/binary/terrain'
Binary files test/maps/port_space.wmf/binary/terrain	2016-02-08 20:44:17 +0000 and test/maps/port_space.wmf/binary/terrain	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/ship_transportation.wmf/binary/building'
Binary files test/maps/ship_transportation.wmf/binary/building	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/building	1970-01-01 00:00:00 +0000 differ
=== added file 'test/maps/ship_transportation.wmf/binary/exploration'
Binary files test/maps/ship_transportation.wmf/binary/exploration	1970-01-01 00:00:00 +0000 and test/maps/ship_transportation.wmf/binary/exploration	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/ship_transportation.wmf/binary/exploration'
Binary files test/maps/ship_transportation.wmf/binary/exploration	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/exploration	1970-01-01 00:00:00 +0000 differ
=== removed file 'test/maps/ship_transportation.wmf/binary/flag'
Binary files test/maps/ship_transportation.wmf/binary/flag	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/flag	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/ship_transportation.wmf/binary/mapobjects'
Binary files test/maps/ship_transportation.wmf/binary/mapobjects	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/mapobjects	2018-05-12 04:24:42 +0000 differ
=== added file 'test/maps/ship_transportation.wmf/binary/node_ownership'
Binary files test/maps/ship_transportation.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 and test/maps/ship_transportation.wmf/binary/node_ownership	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/ship_transportation.wmf/binary/node_ownership'
Binary files test/maps/ship_transportation.wmf/binary/node_ownership	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/node_ownership	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/ship_transportation.wmf/binary/resource'
Binary files test/maps/ship_transportation.wmf/binary/resource	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/resource	2018-05-12 04:24:42 +0000 differ
=== removed file 'test/maps/ship_transportation.wmf/binary/road'
Binary files test/maps/ship_transportation.wmf/binary/road	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/road	1970-01-01 00:00:00 +0000 differ
=== modified file 'test/maps/ship_transportation.wmf/binary/terrain'
Binary files test/maps/ship_transportation.wmf/binary/terrain	2013-10-31 18:52:52 +0000 and test/maps/ship_transportation.wmf/binary/terrain	2018-05-12 04:24:42 +0000 differ

Follow ups