2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file order_backup.cpp Handling of order backups. */
11 #include "command_func.h"
12 #include "core/pool_func.hpp"
13 #include "network/network.h"
14 #include "network/network_func.h"
15 #include "order_backup.h"
16 #include "vehicle_base.h"
17 #include "window_func.h"
18 #include "station_map.h"
19 #include "order_cmd.h"
20 #include "group_cmd.h"
22 #include "safeguards.h"
24 OrderBackupPool
_order_backup_pool("BackupOrder");
25 INSTANTIATE_POOL_METHODS(OrderBackup
)
27 /** Free everything that is allocated. */
28 OrderBackup::~OrderBackup()
30 if (CleaningPool()) return;
32 Order
*o
= this->orders
;
33 while (o
!= nullptr) {
34 Order
*next
= o
->next
;
41 * Create an order backup for the given vehicle.
42 * @param v The vehicle to make a backup of.
43 * @param user The user that is requesting the backup.
45 OrderBackup::OrderBackup(const Vehicle
*v
, uint32 user
)
49 this->group
= v
->group_id
;
51 this->CopyConsistPropertiesFrom(v
);
53 /* If we have shared orders, store the vehicle we share the order with. */
54 if (v
->IsOrderListShared()) {
55 this->clone
= (v
->FirstShared() == v
) ? v
->NextShared() : v
->FirstShared();
57 /* Else copy the orders */
58 Order
**tail
= &this->orders
;
60 /* Count the number of orders */
61 for (const Order
*order
: v
->Orders()) {
62 Order
*copy
= new Order();
63 copy
->AssignOrder(*order
);
71 * Restore the data of this order to the given vehicle.
72 * @param v The vehicle to restore to.
74 void OrderBackup::DoRestore(Vehicle
*v
)
76 /* If we had shared orders, recover that */
77 if (this->clone
!= nullptr) {
78 Command
<CMD_CLONE_ORDER
>::Do(DC_EXEC
, CO_SHARE
, v
->index
, this->clone
->index
);
79 } else if (this->orders
!= nullptr && OrderList::CanAllocateItem()) {
80 v
->orders
= new OrderList(this->orders
, v
);
81 this->orders
= nullptr;
82 /* Make sure buoys/oil rigs are updated in the station list. */
83 InvalidateWindowClassesData(WC_STATION_LIST
, 0);
86 v
->CopyConsistPropertiesFrom(this);
88 /* Make sure orders are in range */
89 v
->UpdateRealOrderIndex();
90 if (v
->cur_implicit_order_index
>= v
->GetNumOrders()) v
->cur_implicit_order_index
= v
->cur_real_order_index
;
92 /* Restore vehicle group */
93 Command
<CMD_ADD_VEHICLE_GROUP
>::Do(DC_EXEC
, this->group
, v
->index
, false);
97 * Create an order backup for the given vehicle.
98 * @param v The vehicle to make a backup of.
99 * @param user The user that is requesting the backup.
100 * @note Will automatically remove any previous backups of this user.
102 /* static */ void OrderBackup::Backup(const Vehicle
*v
, uint32 user
)
104 /* Don't use reset as that broadcasts over the network to reset the variable,
105 * which is what we are doing at the moment. */
106 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
107 if (ob
->user
== user
) delete ob
;
109 if (OrderBackup::CanAllocateItem()) {
110 new OrderBackup(v
, user
);
115 * Restore the data of this order to the given vehicle.
116 * @param v The vehicle to restore to.
117 * @param user The user that built the vehicle, thus wants to restore.
118 * @note After restoration the backup will automatically be removed.
120 /* static */ void OrderBackup::Restore(Vehicle
*v
, uint32 user
)
122 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
123 if (v
->tile
!= ob
->tile
|| ob
->user
!= user
) continue;
131 * Reset an OrderBackup given a tile and user.
132 * @param tile The tile associated with the OrderBackup.
133 * @param user The user associated with the OrderBackup.
134 * @note Must not be used from the GUI!
136 /* static */ void OrderBackup::ResetOfUser(TileIndex tile
, uint32 user
)
138 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
139 if (ob
->user
== user
&& (ob
->tile
== tile
|| tile
== INVALID_TILE
)) delete ob
;
144 * Clear an OrderBackup
145 * @param flags For command.
146 * @param tile Tile related to the to-be-cleared OrderBackup.
147 * @param user_id User that had the OrderBackup.
148 * @return The cost of this operation or an error.
150 CommandCost
CmdClearOrderBackup(DoCommandFlag flags
, TileIndex tile
, ClientID user_id
)
152 /* No need to check anything. If the tile or user don't exist we just ignore it. */
153 if (flags
& DC_EXEC
) OrderBackup::ResetOfUser(tile
== 0 ? INVALID_TILE
: tile
, user_id
);
155 return CommandCost();
159 * Reset an user's OrderBackup if needed.
160 * @param user The user associated with the OrderBackup.
161 * @pre _network_server.
162 * @note Must not be used from a command.
164 /* static */ void OrderBackup::ResetUser(uint32 user
)
166 assert(_network_server
);
168 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
169 /* If it's not a backup of us, ignore it. */
170 if (ob
->user
!= user
) continue;
172 Command
<CMD_CLEAR_ORDER_BACKUP
>::Post(0, static_cast<ClientID
>(user
));
178 * Reset the OrderBackups from GUI/game logic.
179 * @param t The tile of the order backup.
180 * @param from_gui Whether the call came from the GUI, i.e. whether
181 * it must be synced over the network.
183 /* static */ void OrderBackup::Reset(TileIndex t
, bool from_gui
)
185 /* The user has CLIENT_ID_SERVER as default when network play is not active,
186 * but compiled it. A network client has its own variable for the unique
187 * client/user identifier. Finally if networking isn't compiled in the
188 * default is just plain and simple: 0. */
189 uint32 user
= _networking
&& !_network_server
? _network_own_client_id
: CLIENT_ID_SERVER
;
191 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
192 /* If this is a GUI action, and it's not a backup of us, ignore it. */
193 if (from_gui
&& ob
->user
!= user
) continue;
194 /* If it's not for our chosen tile either, ignore it. */
195 if (t
!= INVALID_TILE
&& t
!= ob
->tile
) continue;
198 /* We need to circumvent the "prevention" from this command being executed
199 * while the game is paused, so use the internal method. Nor do we want
200 * this command to get its cost estimated when shift is pressed. */
201 Command
<CMD_CLEAR_ORDER_BACKUP
>::Unsafe
<CommandCallback
>(STR_NULL
, nullptr, true, false, ob
->tile
, CommandTraits
<CMD_CLEAR_ORDER_BACKUP
>::Args
{ ob
->tile
, static_cast<ClientID
>(user
) });
203 /* The command came from the game logic, i.e. the clearing of a tile.
204 * In that case we have no need to actually sync this, just do it. */
211 * Clear the group of all backups having this group ID.
212 * @param group The group to clear.
214 /* static */ void OrderBackup::ClearGroup(GroupID group
)
216 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
217 if (ob
->group
== group
) ob
->group
= DEFAULT_GROUP
;
222 * Clear/update the (clone) vehicle from an order backup.
223 * @param v The vehicle to clear.
225 * @note If it is not possible to set another vehicle as clone
226 * "example", then this backed up order will be removed.
228 /* static */ void OrderBackup::ClearVehicle(const Vehicle
*v
)
230 assert(v
!= nullptr);
231 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
232 if (ob
->clone
== v
) {
233 /* Get another item in the shared list. */
234 ob
->clone
= (v
->FirstShared() == v
) ? v
->NextShared() : v
->FirstShared();
235 /* But if that isn't there, remove it. */
236 if (ob
->clone
== nullptr) delete ob
;
242 * Removes an order from all vehicles. Triggers when, say, a station is removed.
243 * @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
244 * @param destination The destination. Can be a StationID, DepotID or WaypointID.
245 * @param hangar Only used for airports in the destination.
246 * When false, remove airport and hangar orders.
247 * When true, remove either airport or hangar order.
249 /* static */ void OrderBackup::RemoveOrder(OrderType type
, DestinationID destination
, bool hangar
)
251 for (OrderBackup
*ob
: OrderBackup::Iterate()) {
252 for (Order
*order
= ob
->orders
; order
!= nullptr; order
= order
->next
) {
253 OrderType ot
= order
->GetType();
254 if (ot
== OT_GOTO_DEPOT
&& (order
->GetDepotActionType() & ODATFB_NEAREST_DEPOT
) != 0) continue;
255 if (ot
== OT_GOTO_DEPOT
&& hangar
&& !IsHangarTile(ob
->tile
)) continue; // Not an aircraft? Can't have a hangar order.
256 if (ot
== OT_IMPLICIT
|| (IsHangarTile(ob
->tile
) && ot
== OT_GOTO_DEPOT
&& !hangar
)) ot
= OT_GOTO_STATION
;
257 if (ot
== type
&& order
->GetDestination() == destination
) {
258 /* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */