changeset 15865:270f9b0689cc draft

(svn r20547) -Change: the way order backups are performed. Now restoring an order doesn't require up to 765 commands.
author rubidium <rubidium@openttd.org>
date Wed, 18 Aug 2010 20:48:38 +0000
parents 1ce9c097ee89
children 38a12fe3e755
files src/command.cpp src/command_type.h src/depot.cpp src/depot_gui.cpp src/network/core/tcp_game.cpp src/order_backup.cpp src/order_backup.h src/order_cmd.cpp src/saveload/order_sl.cpp src/station_cmd.cpp src/train_cmd.cpp src/vehicle_cmd.cpp src/vehicle_gui.cpp
diffstat 13 files changed, 237 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -88,12 +88,12 @@
 CommandProc CmdForceTrainProceed;
 CommandProc CmdReverseTrainDirection;
 
+CommandProc CmdClearOrderBackup;
 CommandProc CmdModifyOrder;
 CommandProc CmdSkipToOrder;
 CommandProc CmdDeleteOrder;
 CommandProc CmdInsertOrder;
 CommandProc CmdChangeServiceInt;
-CommandProc CmdRestoreOrderIndex;
 
 CommandProc CmdBuildIndustry;
 
@@ -221,8 +221,8 @@
 	DEF_CMD(CmdBuildBuoy,                               CMD_AUTO), // CMD_BUILD_BUOY
 	DEF_CMD(CmdPlantTree,                               CMD_AUTO), // CMD_PLANT_TREE
 
-	DEF_CMD(CmdBuildVehicle,                                   0), // CMD_BUILD_VEHICLE
-	DEF_CMD(CmdSellVehicle,                                    0), // CMD_SELL_VEHICLE
+	DEF_CMD(CmdBuildVehicle,                       CMD_CLIENT_ID), // CMD_BUILD_VEHICLE
+	DEF_CMD(CmdSellVehicle,                        CMD_CLIENT_ID), // CMD_SELL_VEHICLE
 	DEF_CMD(CmdRefitVehicle,                                   0), // CMD_REFIT_VEHICLE
 
 	DEF_CMD(CmdMoveRailVehicle,                                0), // CMD_MOVE_RAIL_VEHICLE
@@ -230,6 +230,7 @@
 	DEF_CMD(CmdForceTrainProceed,                              0), // CMD_FORCE_TRAIN_PROCEED
 	DEF_CMD(CmdReverseTrainDirection,                          0), // CMD_REVERSE_TRAIN_DIRECTION
 
+	DEF_CMD(CmdClearOrderBackup,                   CMD_CLIENT_ID), // CMD_CLEAR_ORDER_BACKUP
 	DEF_CMD(CmdModifyOrder,                                    0), // CMD_MODIFY_ORDER
 	DEF_CMD(CmdSkipToOrder,                                    0), // CMD_SKIP_TO_ORDER
 	DEF_CMD(CmdDeleteOrder,                                    0), // CMD_DELETE_ORDER
@@ -288,7 +289,6 @@
 
 	DEF_CMD(CmdLevelLand, CMD_ALL_TILES | CMD_NO_TEST | CMD_AUTO), // CMD_LEVEL_LAND; test run might clear tiles multiple times, in execution that only happens once
 
-	DEF_CMD(CmdRestoreOrderIndex,                              0), // CMD_RESTORE_ORDER_INDEX
 	DEF_CMD(CmdBuildLock,                               CMD_AUTO), // CMD_BUILD_LOCK
 
 	DEF_CMD(CmdBuildSignalTrack,                        CMD_AUTO), // CMD_BUILD_SIGNAL_TRACK
@@ -502,7 +502,7 @@
 
 #ifdef ENABLE_NETWORK
 	/* Only set p2 when the command does not come from the network. */
-	if (!(cmd & CMD_NETWORK_COMMAND) && GetCommandFlags(cmd) & CMD_CLIENT_ID) p2 = CLIENT_ID_SERVER;
+	if (!(cmd & CMD_NETWORK_COMMAND) && GetCommandFlags(cmd) & CMD_CLIENT_ID && p2 == 0) p2 = CLIENT_ID_SERVER;
 #endif
 
 	CommandCost res = DoCommandPInternal(tile, p1, p2, cmd, callback, text, my_cmd, estimate_only);
--- a/src/command_type.h
+++ b/src/command_type.h
@@ -186,6 +186,7 @@
 	CMD_FORCE_TRAIN_PROCEED,          ///< proceed a train to pass a red signal
 	CMD_REVERSE_TRAIN_DIRECTION,      ///< turn a train around
 
+	CMD_CLEAR_ORDER_BACKUP,           ///< clear the order backup of a given user/tile
 	CMD_MODIFY_ORDER,                 ///< modify an order (like set full-load)
 	CMD_SKIP_TO_ORDER,                ///< skip an order to the next of specific one
 	CMD_DELETE_ORDER,                 ///< delete an order
@@ -242,7 +243,6 @@
 	CMD_COMPANY_CTRL,                 ///< used in multiplayer to create a new companies etc.
 	CMD_LEVEL_LAND,                   ///< level land
 
-	CMD_RESTORE_ORDER_INDEX,          ///< restore vehicle order-index and service interval
 	CMD_BUILD_LOCK,                   ///< build a lock
 
 	CMD_BUILD_SIGNAL_TRACK,           ///< add signals along a track (by dragging)
--- a/src/depot.cpp
+++ b/src/depot.cpp
@@ -27,12 +27,14 @@
 {
 	if (CleaningPool()) return;
 
+	/* Clear the order backup. */
+	OrderBackup::Reset(this->xy, false);
+
 	/* Clear the depot from all order-lists */
 	RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index);
 
 	/* Delete the depot-window */
 	DeleteWindowById(WC_VEHICLE_DEPOT, this->xy);
-	OrderBackup::Reset(this->xy);
 
 	/* Delete the depot list */
 	WindowNumber wno = (this->index << 16) | VLW_DEPOT_LIST | GetTileOwner(this->xy);
--- a/src/depot_gui.cpp
+++ b/src/depot_gui.cpp
@@ -976,12 +976,7 @@
 				this->SetDirty();
 
 				int sell_cmd = (v->type == VEH_TRAIN && (widget == DEPOT_WIDGET_SELL_CHAIN || _ctrl_pressed)) ? 1 : 0;
-
-				bool is_engine = (v->type != VEH_TRAIN || Train::From(v)->IsFrontEngine());
-
-				if (is_engine) OrderBackup::Backup(v);
-
-				if (!DoCommandP(v->tile, v->index | sell_cmd << 16, 0, GetCmdSellVeh(v->type)) && is_engine) OrderBackup::Reset(this->window_number);
+				DoCommandP(v->tile, v->index | sell_cmd << 16 | MAKE_ORDER_BACKUP_FLAG, 0, GetCmdSellVeh(v->type));
 				break;
 			}
 
--- a/src/network/core/tcp_game.cpp
+++ b/src/network/core/tcp_game.cpp
@@ -18,6 +18,7 @@
 #include "../network.h"
 #include "../network_internal.h"
 #include "../../core/pool_func.hpp"
+#include "../../order_backup.h"
 
 #include "table/strings.h"
 
@@ -37,6 +38,7 @@
 NetworkClientSocket::~NetworkClientSocket()
 {
 	if (_redirect_console_to_client == this->client_id) _redirect_console_to_client = INVALID_CLIENT_ID;
+	if (_network_server) OrderBackup::ResetUser(this->client_id);
 	this->client_id = INVALID_CLIENT_ID;
 	this->status = STATUS_INACTIVE;
 }
--- a/src/order_backup.cpp
+++ b/src/order_backup.cpp
@@ -12,6 +12,8 @@
 #include "stdafx.h"
 #include "command_func.h"
 #include "core/pool_func.hpp"
+#include "network/network.h"
+#include "network/network_func.h"
 #include "order_backup.h"
 #include "order_base.h"
 #include "vehicle_base.h"
@@ -23,11 +25,20 @@
 OrderBackup::~OrderBackup()
 {
 	free(this->name);
-	free(this->orders);
+
+	if (CleaningPool()) return;
+
+	Order *o = this->orders;
+	while (o != NULL) {
+		Order *next = o->next;
+		delete o;
+		o = next;
+	}
 }
 
-OrderBackup::OrderBackup(const Vehicle *v)
+OrderBackup::OrderBackup(const Vehicle *v, uint32 user)
 {
+	this->user             = user;
 	this->tile             = v->tile;
 	this->orderindex       = v->cur_order_index;
 	this->group            = v->group_id;
@@ -40,99 +51,128 @@
 		this->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
 	} else {
 		/* Else copy the orders */
+		Order **tail = &this->orders;
 
 		/* Count the number of orders */
-		uint cnt = 0;
 		const Order *order;
-		FOR_VEHICLE_ORDERS(v, order) cnt++;
-
-		/* Allocate memory for the orders plus an end-of-orders marker */
-		this->orders = MallocT<Order>(cnt + 1);
-
-		Order *dest = this->orders;
-
-		/* Copy the orders */
 		FOR_VEHICLE_ORDERS(v, order) {
-			memcpy(dest, order, sizeof(Order));
-			dest++;
+			Order *copy = new Order();
+			copy->AssignOrder(*order);
+			*tail = copy;
+			tail = &copy->next;
 		}
-		/* End the list with an empty order */
-		dest->Free();
 	}
 }
 
-void OrderBackup::DoRestore(const Vehicle *v)
+void OrderBackup::DoRestore(Vehicle *v)
 {
 	/* If we have a custom name, process that */
-	if (this->name != NULL) DoCommandP(0, v->index, 0, CMD_RENAME_VEHICLE, NULL, this->name);
+	v->name = this->name;
+	this->name = NULL;
 
 	/* If we had shared orders, recover that */
 	if (this->clone != NULL) {
-		DoCommandP(0, v->index | (this->clone->index << 16), CO_SHARE, CMD_CLONE_ORDER);
-	} else if (this->orders != NULL) {
-
-		/* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the
-		 *  order number is one more than the current amount of orders, and because
-		 *  in network the commands are queued before send, the second insert always
-		 *  fails in test mode. By bypassing the test-mode, that no longer is a problem. */
-		for (uint i = 0; !this->orders[i].IsType(OT_NOTHING); i++) {
-			Order o = this->orders[i];
-			/* Conditional orders need to have their destination to be valid on insertion. */
-			if (o.IsType(OT_CONDITIONAL)) o.SetConditionSkipToOrder(0);
-
-			if (!DoCommandP(0, v->index + (i << 16), o.Pack(),
-					CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) {
-				break;
-			}
-
-			/* Copy timetable if enabled */
-			if (_settings_game.order.timetabling && !DoCommandP(0, v->index | (i << 16) | (1 << 25),
-					o.wait_time << 16 | o.travel_time,
-					CMD_CHANGE_TIMETABLE | CMD_NO_TEST_IF_IN_NETWORK)) {
-				break;
-			}
-		}
-
-			/* Fix the conditional orders' destination. */
-		for (uint i = 0; !this->orders[i].IsType(OT_NOTHING); i++) {
-			if (!this->orders[i].IsType(OT_CONDITIONAL)) continue;
-
-			if (!DoCommandP(0, v->index + (i << 16), MOF_LOAD | (this->orders[i].GetConditionSkipToOrder() << 4),
-					CMD_MODIFY_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) {
-				break;
-			}
-		}
+		DoCommand(0, v->index | (this->clone->index << 16), CO_SHARE, DC_EXEC, CMD_CLONE_ORDER);
+	} else if (this->orders != NULL && OrderList::CanAllocateItem()) {
+		v->orders.list = new OrderList(this->orders, v);
+		this->orders = NULL;
 	}
 
-	/* Restore vehicle order-index and service interval */
-	DoCommandP(0, v->index, this->orderindex | (this->service_interval << 16), CMD_RESTORE_ORDER_INDEX);
+	uint num_orders = v->GetNumOrders();
+	if (num_orders != 0) v->cur_order_index = this->orderindex % num_orders;
+	v->service_interval = this->service_interval;
 
 	/* Restore vehicle group */
-	DoCommandP(0, this->group, v->index, CMD_ADD_VEHICLE_GROUP);
+	DoCommand(0, this->group, v->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP);
 }
 
-/* static */ void OrderBackup::Backup(const Vehicle *v)
+/* static */ void OrderBackup::Backup(const Vehicle *v, uint32 user)
 {
-	OrderBackup::Reset();
-	new OrderBackup(v);
+	/* Don't use reset as that broadcasts over the network to reset the variable,
+	 * which is what we are doing at the moment. */
+	OrderBackup *ob;
+	FOR_ALL_ORDER_BACKUPS(ob) {
+		if (ob->user == user) delete ob;
+	}
+	new OrderBackup(v, user);
 }
 
-/* static */ void OrderBackup::Restore(const Vehicle *v)
+/* static */ void OrderBackup::Restore(Vehicle *v, uint32 user)
 {
 	OrderBackup *ob;
 	FOR_ALL_ORDER_BACKUPS(ob) {
-		if (v->tile != ob->tile) continue;
+		if (v->tile != ob->tile || ob->user != user) continue;
 
 		ob->DoRestore(v);
 		delete ob;
 	}
 }
 
-/* static */ void OrderBackup::Reset(TileIndex t)
+/* static */ void OrderBackup::ResetOfUser(TileIndex tile, uint32 user)
 {
 	OrderBackup *ob;
 	FOR_ALL_ORDER_BACKUPS(ob) {
-		if (t == INVALID_TILE || t == ob->tile) delete ob;
+		if (ob->user == user && (ob->tile == tile || tile == INVALID_TILE)) delete ob;
+	}
+}
+
+/**
+ * Clear an OrderBackup
+ * @param tile  Tile related to the to-be-cleared OrderBackup.
+ * @param flags For command.
+ * @param p1    Unused.
+ * @param p2    User that had the OrderBackup.
+ * @param text  Unused.
+ * @return The cost of this operation or an error.
+ */
+CommandCost CmdClearOrderBackup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+	/* No need to check anything. If the tile or user don't exist we just ignore it. */
+	if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, p2);
+
+	return CommandCost();
+}
+
+/* static */ void OrderBackup::ResetUser(uint32 user)
+{
+	assert(_network_server);
+
+	OrderBackup *ob;
+	FOR_ALL_ORDER_BACKUPS(ob) {
+		/* If it's not an backup of us, so ignore it. */
+		if (ob->user != user) continue;
+
+		DoCommandP(0, 0, user, CMD_CLEAR_ORDER_BACKUP);
+		return;
+	}
+}
+
+/* static */ void OrderBackup::Reset(TileIndex t, bool from_gui)
+{
+	/* The user has CLIENT_ID_SERVER as default when network play is not active,
+	 * but compiled it. A network client has its own variable for the unique
+	 * client/user identifier. Finally if networking isn't compiled in the
+	 * default is just plain and simple: 0. */
+#ifdef ENABLE_NETWORK
+	uint32 user = _networking && !_network_server ? _network_own_client_id : CLIENT_ID_SERVER;
+#else
+	uint32 user = 0;
+#endif
+
+	OrderBackup *ob;
+	FOR_ALL_ORDER_BACKUPS(ob) {
+		/* If it's not an backup of us, so ignore it. */
+		if (ob->user != user) continue;
+		/* If it's not for our chosen tile either, ignore it. */
+		if (t != INVALID_TILE && t != ob->tile) continue;
+
+		if (from_gui) {
+			DoCommandP(ob->tile, 0, 0, CMD_CLEAR_ORDER_BACKUP);
+		} else {
+			/* The command came from the game logic, i.e. the clearing of a tile.
+			 * In that case we have no need to actually sync this, just do it. */
+			delete ob;
+		}
 	}
 }
 
--- a/src/order_backup.h
+++ b/src/order_backup.h
@@ -28,12 +28,18 @@
 /** The pool with order backups. */
 extern OrderBackupPool _order_backup_pool;
 
+/** Flag to pass to the vehicle construction command when an order should be preserved. */
+static const uint32 MAKE_ORDER_BACKUP_FLAG = 1 << 31;
+
 /**
  * Data for backing up an order of a vehicle so it can be
  * restored after a vehicle is rebuilt in the same depot.
  */
 struct OrderBackup : OrderBackupPool::PoolItem<&_order_backup_pool> {
 private:
+	friend const struct SaveLoad *GetOrderBackupDescription(); ///< Saving and loading of order backups.
+	friend void Load_BKOR();   ///< Creating empty orders upon savegame loading.
+	uint32 user;               ///< The user that requested the backup.
 	TileIndex tile;            ///< Tile of the depot where the order was changed.
 	GroupID group;             ///< The group the vehicle was part of.
 	uint16 service_interval;   ///< The service interval of the vehicle.
@@ -43,17 +49,21 @@
 	VehicleOrderID orderindex; ///< The order-index the vehicle had.
 	Order *orders;             ///< The actual orders if the vehicle was not a clone.
 
+	/** Creation for savegame restoration. */
+	OrderBackup() {}
+
 	/**
 	 * Create an order backup for the given vehicle.
-	 * @param v The vehicle to make a backup of.
+	 * @param v    The vehicle to make a backup of.
+	 * @param user The user that is requesting the backup.
 	 */
-	OrderBackup(const Vehicle *v);
+	OrderBackup(const Vehicle *v, uint32 user);
 
 	/**
 	 * Restore the data of this order to the given vehicle.
 	 * @param v The vehicle to restore to.
 	 */
-	void DoRestore(const Vehicle *v);
+	void DoRestore(Vehicle *v);
 
 public:
 	/** Free everything that is allocated. */
@@ -61,23 +71,43 @@
 
 	/**
 	 * Create an order backup for the given vehicle.
-	 * @param v The vehicle to make a backup of.
+	 * @param v    The vehicle to make a backup of.
+	 * @param user The user that is requesting the backup.
 	 * @note Will automatically remove any previous backups of this user.
 	 */
-	static void Backup(const Vehicle *v);
+	static void Backup(const Vehicle *v, uint32 user);
 
 	/**
 	 * Restore the data of this order to the given vehicle.
-	 * @param v The vehicle to restore to.
+	 * @param v    The vehicle to restore to.
+	 * @param user The user that built the vehicle, thus wants to restore.
 	 * @note After restoration the backup will automatically be removed.
 	 */
-	static void Restore(const Vehicle *v);
+	static void Restore(Vehicle *v, uint32 user);
+
+	/**
+	 * Reset an OrderBackup given a tile and user.
+	 * @param tile The tile associated with the OrderBackup.
+	 * @param user The user associated with the OrderBackup.
+	 * @note Must not be used from the GUI!
+	 */
+	static void ResetOfUser(TileIndex tile, uint32 user);
 
 	/**
-	 * Reset the OrderBackups.
-	 * @param tile The tile of the order backup.
+	 * Reset an user's OrderBackup if needed.
+	 * @param user The user associated with the OrderBackup.
+	 * @pre _network_server.
+	 * @note Must not be used from a command.
 	 */
-	static void Reset(TileIndex tile = INVALID_TILE);
+	static void ResetUser(uint32 user);
+
+	/**
+	 * Reset the OrderBackups from GUI/game logic.
+	 * @param tile     The tile of the order backup.
+	 * @param from_gui Whether the call came from the GUI, i.e. whether
+	 *                 it must be synced over the network.
+	 */
+	static void Reset(TileIndex tile = INVALID_TILE, bool from_gui = true);
 
 	/**
 	 * Clear the group of all backups having this group ID.
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -1328,45 +1328,6 @@
 	return CommandCost();
 }
 
-/**
- * Restore the current order-index of a vehicle and sets service-interval.
- * @param tile unused
- * @param flags operation to perform
- * @param p1 the ID of the vehicle
- * @param p2 various bistuffed elements
- * - p2 = (bit  0-15) - current order-index (p2 & 0xFFFF)
- * - p2 = (bit 16-31) - service interval (p2 >> 16)
- * @param text unused
- * @return the cost of this operation or an error
- * @todo Unfortunately you cannot safely restore the unitnumber or the old vehicle
- * as far as I can see. We can store it in BackuppedOrders, and restore it, but
- * but we have no way of seeing it has been tampered with or not, as we have no
- * legit way of knowing what that ID was.@n
- * If we do want to backup/restore it, just add UnitID uid to BackuppedOrders, and
- * restore it as parameter 'y' (ugly hack I know) for example. "v->unitnumber = y;"
- */
-CommandCost CmdRestoreOrderIndex(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
-{
-	VehicleOrderID cur_ord = GB(p2,  0, 16);
-	uint16 serv_int = GB(p2, 16, 16);
-
-	Vehicle *v = Vehicle::GetIfValid(p1);
-	/* Check the vehicle type and ownership, and if the service interval and order are in range */
-	if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
-
-	CommandCost ret = CheckOwnership(v->owner);
-	if (ret.Failed()) return ret;
-
-	if (serv_int != GetServiceIntervalClamped(serv_int, v->owner) || cur_ord >= v->GetNumOrders()) return CMD_ERROR;
-
-	if (flags & DC_EXEC) {
-		v->cur_order_index = cur_ord;
-		v->service_interval = serv_int;
-	}
-
-	return CommandCost();
-}
-
 
 /**
  *
--- a/src/saveload/order_sl.cpp
+++ b/src/saveload/order_sl.cpp
@@ -11,7 +11,9 @@
 
 #include "../stdafx.h"
 #include "../order_base.h"
+#include "../order_backup.h"
 #include "../settings_type.h"
+#include "../network/network.h"
 
 #include "saveload.h"
 
@@ -235,7 +237,65 @@
 	}
 }
 
+const SaveLoad *GetOrderBackupDescription()
+{
+	static const SaveLoad _order_backup_desc[] = {
+		SLE_VAR(OrderBackup, user,                  SLE_UINT32),
+		SLE_VAR(OrderBackup, tile,                  SLE_UINT32),
+		SLE_VAR(OrderBackup, group,                 SLE_UINT16),
+		SLE_VAR(OrderBackup, service_interval,      SLE_INT32),
+		SLE_STR(OrderBackup, name,                  SLE_STR, 0),
+		SLE_VAR(OrderBackup, clone,                 SLE_UINT16),
+		SLE_VAR(OrderBackup, orderindex,            SLE_UINT8),
+		SLE_REF(OrderBackup, orders,                REF_ORDER),
+		SLE_END()
+	};
+
+	return _order_backup_desc;
+}
+
+static void Save_BKOR()
+{
+	/* We only save this when we're a network server
+	 * as we want this information on our clients. For
+	 * normal games this information isn't needed. */
+	if (!_networking || !_network_server) return;
+
+	OrderBackup *ob;
+	FOR_ALL_ORDER_BACKUPS(ob) {
+		SlSetArrayIndex(ob->index);
+		SlObject(ob, GetOrderBackupDescription());
+	}
+}
+
+void Load_BKOR()
+{
+	int index;
+
+	while ((index = SlIterateArray()) != -1) {
+		/* set num_orders to 0 so it's a valid OrderList */
+		OrderBackup *ob = new (index) OrderBackup();
+		SlObject(ob, GetOrderBackupDescription());
+	}
+
+	/* If we are a network server, then we just loaded
+	 * a previously saved-by-server savegame. There are
+	 * no clients with a backup anymore, so clear it. */
+	if (_networking && _network_server) {
+		_order_backup_pool.CleanPool();
+	}
+}
+
+static void Ptrs_BKOR()
+{
+	OrderBackup *ob;
+	FOR_ALL_ORDER_BACKUPS(ob) {
+		SlObject(ob, GetOrderBackupDescription());
+	}
+}
+
 extern const ChunkHandler _order_chunk_handlers[] = {
+	{ 'BKOR', Save_BKOR, Load_BKOR, Ptrs_BKOR, NULL, CH_ARRAY},
 	{ 'ORDR', Save_ORDR, Load_ORDR, Ptrs_ORDR, NULL, CH_ARRAY},
 	{ 'ORDL', Save_ORDL, Load_ORDL, Ptrs_ORDL, NULL, CH_ARRAY | CH_LAST},
 };
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -2288,7 +2288,7 @@
 		cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]);
 
 		if (flags & DC_EXEC) {
-			if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur);
+			if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false);
 			DeleteAnimatedTile(tile_cur);
 			DoClearSquare(tile_cur);
 			DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur);
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -41,6 +41,7 @@
 #include "engine_base.h"
 #include "engine_func.h"
 #include "newgrf.h"
+#include "order_backup.h"
 
 #include "table/strings.h"
 #include "table/train_cmd.h"
@@ -1325,9 +1326,10 @@
  * - data = 0: only sell the single dragged wagon/engine (and any belonging rear-engines)
  * - data = 1: sell the vehicle and all vehicles following it in the chain
  *             if the wagon is dragged, don't delete the possibly belonging rear-engine to some front
+ * @param user  the user for the order backup.
  * @return the cost of this operation or an error
  */
-CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data)
+CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint32 user)
 {
 	/* Check if we deleted a vehicle window */
 	Window *w = NULL;
@@ -1381,6 +1383,8 @@
 
 			/* If we deleted a window then open a new one for the 'new' train */
 			if (IsLocalCompany() && w != NULL) ShowVehicleViewWindow(new_head);
+		} else if (v->IsPrimaryVehicle() && data & (MAKE_ORDER_BACKUP_FLAG >> 16)) {
+			OrderBackup::Backup(v, user);
 		}
 
 		/* We need to update the information about the train. */
--- a/src/vehicle_cmd.cpp
+++ b/src/vehicle_cmd.cpp
@@ -32,6 +32,7 @@
 #include "articulated_vehicles.h"
 #include "autoreplace_gui.h"
 #include "company_base.h"
+#include "order_backup.h"
 
 #include "table/strings.h"
 
@@ -144,12 +145,14 @@
 		}
 
 		Company::Get(_current_company)->num_engines[eid]++;
+
+		if (v->IsPrimaryVehicle()) OrderBackup::Restore(v, p2);
 	}
 
 	return value;
 }
 
-CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *v, uint16 data);
+CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *v, uint16 data, uint32 user);
 
 /**
  * Sell a vehicle.
@@ -157,7 +160,8 @@
  * @param flags for command.
  * @param p1 various bitstuffed data.
  *  bits  0-15: vehicle ID being sold.
- *  bits 16-31: vehicle type specific bits passed on to the vehicle build functions.
+ *  bits 16-30: vehicle type specific bits passed on to the vehicle build functions.
+ *  bit     31: make a backup of the vehicle's order (if an engine).
  * @param p2 unused.
  * @param text unused.
  * @return the cost of this operation or an error.
@@ -176,12 +180,23 @@
 
 	if (!front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);
 
+	/* Can we actually make the order backup, i.e. are there enough orders? */
+	if (p1 & MAKE_ORDER_BACKUP_FLAG &&
+			front->orders.list != NULL &&
+			!front->orders.list->IsShared() &&
+			!Order::CanAllocateItem(front->orders.list->GetNumOrders())) {
+		/* Only happens in exceptional cases when there aren't enough orders anyhow.
+		 * Thus it should be safe to just drop the orders in that case. */
+		p1 &= ~MAKE_ORDER_BACKUP_FLAG;
+	}
+
 	if (v->type == VEH_TRAIN) {
-		ret = CmdSellRailWagon(flags, v, GB(p1, 16, 16));
+		ret = CmdSellRailWagon(flags, v, GB(p1, 16, 16), p2);
 	} else {
 		ret = CommandCost(EXPENSES_NEW_VEHICLES, -front->value);
 
 		if (flags & DC_EXEC) {
+			if (v->IsPrimaryVehicle() && p1 & MAKE_ORDER_BACKUP_FLAG) OrderBackup::Backup(v, p2);
 			delete front;
 		}
 	}
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -44,7 +44,6 @@
 #include "engine_func.h"
 #include "newgrf.h"
 #include "station_base.h"
-#include "order_backup.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -2373,6 +2372,5 @@
 	if (result.Failed()) return;
 
 	const Vehicle *v = Vehicle::Get(_new_vehicle_id);
-	OrderBackup::Restore(v);
 	ShowVehicleViewWindow(v);
 }