2010-08-18 15:15:59 +00:00
|
|
|
/*
|
|
|
|
* This file is part of OpenTTD.
|
|
|
|
* 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.
|
|
|
|
* 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.
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @file order_backup.cpp Handling of order backups. */
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "command_func.h"
|
|
|
|
#include "core/pool_func.hpp"
|
2010-08-18 20:48:38 +00:00
|
|
|
#include "network/network.h"
|
|
|
|
#include "network/network_func.h"
|
2010-08-18 15:15:59 +00:00
|
|
|
#include "order_backup.h"
|
|
|
|
#include "vehicle_base.h"
|
2011-11-12 18:06:34 +00:00
|
|
|
#include "window_func.h"
|
2011-12-09 20:48:13 +00:00
|
|
|
#include "station_map.h"
|
2021-10-05 20:02:27 +00:00
|
|
|
#include "order_cmd.h"
|
2021-10-29 23:31:46 +00:00
|
|
|
#include "group_cmd.h"
|
2010-08-18 15:15:59 +00:00
|
|
|
|
2014-04-23 20:13:33 +00:00
|
|
|
#include "safeguards.h"
|
|
|
|
|
2010-08-18 15:15:59 +00:00
|
|
|
OrderBackupPool _order_backup_pool("BackupOrder");
|
|
|
|
INSTANTIATE_POOL_METHODS(OrderBackup)
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/** Free everything that is allocated. */
|
2010-08-18 15:15:59 +00:00
|
|
|
OrderBackup::~OrderBackup()
|
|
|
|
{
|
2010-08-18 20:48:38 +00:00
|
|
|
if (CleaningPool()) return;
|
|
|
|
|
|
|
|
Order *o = this->orders;
|
2019-04-10 21:07:06 +00:00
|
|
|
while (o != nullptr) {
|
2010-08-18 20:48:38 +00:00
|
|
|
Order *next = o->next;
|
|
|
|
delete o;
|
|
|
|
o = next;
|
|
|
|
}
|
2010-08-18 15:15:59 +00:00
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* Create an order backup for the given vehicle.
|
|
|
|
* @param v The vehicle to make a backup of.
|
|
|
|
* @param user The user that is requesting the backup.
|
|
|
|
*/
|
2010-08-18 20:48:38 +00:00
|
|
|
OrderBackup::OrderBackup(const Vehicle *v, uint32 user)
|
2010-08-18 15:15:59 +00:00
|
|
|
{
|
2010-08-18 20:48:38 +00:00
|
|
|
this->user = user;
|
2010-08-18 15:15:59 +00:00
|
|
|
this->tile = v->tile;
|
|
|
|
this->group = v->group_id;
|
|
|
|
|
2012-07-29 16:45:34 +00:00
|
|
|
this->CopyConsistPropertiesFrom(v);
|
2010-08-18 15:15:59 +00:00
|
|
|
|
|
|
|
/* If we have shared orders, store the vehicle we share the order with. */
|
|
|
|
if (v->IsOrderListShared()) {
|
2010-08-18 15:58:30 +00:00
|
|
|
this->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
|
2010-08-18 15:15:59 +00:00
|
|
|
} else {
|
|
|
|
/* Else copy the orders */
|
2010-08-18 20:48:38 +00:00
|
|
|
Order **tail = &this->orders;
|
2010-08-18 15:15:59 +00:00
|
|
|
|
|
|
|
/* Count the number of orders */
|
2020-12-26 23:13:56 +00:00
|
|
|
for (const Order *order : v->Orders()) {
|
2010-08-18 20:48:38 +00:00
|
|
|
Order *copy = new Order();
|
|
|
|
copy->AssignOrder(*order);
|
|
|
|
*tail = copy;
|
|
|
|
tail = ©->next;
|
2010-08-18 15:15:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* Restore the data of this order to the given vehicle.
|
|
|
|
* @param v The vehicle to restore to.
|
|
|
|
*/
|
2010-08-18 20:48:38 +00:00
|
|
|
void OrderBackup::DoRestore(Vehicle *v)
|
2010-08-18 15:15:59 +00:00
|
|
|
{
|
|
|
|
/* If we had shared orders, recover that */
|
2019-04-10 21:07:06 +00:00
|
|
|
if (this->clone != nullptr) {
|
2021-11-02 21:58:40 +00:00
|
|
|
Command<CMD_CLONE_ORDER>::Do(DC_EXEC, CO_SHARE, v->index, this->clone->index);
|
2019-04-10 21:07:06 +00:00
|
|
|
} else if (this->orders != nullptr && OrderList::CanAllocateItem()) {
|
2022-02-12 18:28:34 +00:00
|
|
|
v->orders = new OrderList(this->orders, v);
|
2019-04-10 21:07:06 +00:00
|
|
|
this->orders = nullptr;
|
2011-11-12 18:06:34 +00:00
|
|
|
/* Make sure buoys/oil rigs are updated in the station list. */
|
|
|
|
InvalidateWindowClassesData(WC_STATION_LIST, 0);
|
2010-08-18 15:15:59 +00:00
|
|
|
}
|
|
|
|
|
2012-07-29 16:45:34 +00:00
|
|
|
v->CopyConsistPropertiesFrom(this);
|
|
|
|
|
|
|
|
/* Make sure orders are in range */
|
|
|
|
v->UpdateRealOrderIndex();
|
2012-07-29 16:48:00 +00:00
|
|
|
if (v->cur_implicit_order_index >= v->GetNumOrders()) v->cur_implicit_order_index = v->cur_real_order_index;
|
2010-08-18 15:15:59 +00:00
|
|
|
|
|
|
|
/* Restore vehicle group */
|
2021-11-16 23:40:06 +00:00
|
|
|
Command<CMD_ADD_VEHICLE_GROUP>::Do(DC_EXEC, this->group, v->index, false);
|
2010-08-18 18:52:16 +00:00
|
|
|
}
|
2010-08-18 15:15:59 +00:00
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* Create an order backup for the given vehicle.
|
|
|
|
* @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.
|
|
|
|
*/
|
2010-08-18 20:48:38 +00:00
|
|
|
/* static */ void OrderBackup::Backup(const Vehicle *v, uint32 user)
|
2010-08-18 18:52:16 +00:00
|
|
|
{
|
2010-08-18 20:48:38 +00:00
|
|
|
/* Don't use reset as that broadcasts over the network to reset the variable,
|
|
|
|
* which is what we are doing at the moment. */
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
2010-08-18 20:48:38 +00:00
|
|
|
if (ob->user == user) delete ob;
|
|
|
|
}
|
2011-02-10 13:17:24 +00:00
|
|
|
if (OrderBackup::CanAllocateItem()) {
|
|
|
|
new OrderBackup(v, user);
|
|
|
|
}
|
2010-08-18 15:15:59 +00:00
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* Restore the data of this order to the given vehicle.
|
|
|
|
* @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.
|
|
|
|
*/
|
2010-08-18 20:48:38 +00:00
|
|
|
/* static */ void OrderBackup::Restore(Vehicle *v, uint32 user)
|
2010-08-18 15:15:59 +00:00
|
|
|
{
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
2010-08-18 20:48:38 +00:00
|
|
|
if (v->tile != ob->tile || ob->user != user) continue;
|
2010-08-18 18:52:16 +00:00
|
|
|
|
|
|
|
ob->DoRestore(v);
|
|
|
|
delete ob;
|
2010-08-18 15:15:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* 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!
|
|
|
|
*/
|
2010-08-18 20:48:38 +00:00
|
|
|
/* static */ void OrderBackup::ResetOfUser(TileIndex tile, uint32 user)
|
|
|
|
{
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
2010-08-18 20:48:38 +00:00
|
|
|
if (ob->user == user && (ob->tile == tile || tile == INVALID_TILE)) delete ob;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear an OrderBackup
|
|
|
|
* @param flags For command.
|
2021-10-10 00:08:52 +00:00
|
|
|
* @param tile Tile related to the to-be-cleared OrderBackup.
|
2021-11-02 20:34:39 +00:00
|
|
|
* @param user_id User that had the OrderBackup.
|
2010-08-18 20:48:38 +00:00
|
|
|
* @return The cost of this operation or an error.
|
|
|
|
*/
|
2021-11-02 20:34:39 +00:00
|
|
|
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id)
|
2010-08-18 15:15:59 +00:00
|
|
|
{
|
2010-08-18 20:48:38 +00:00
|
|
|
/* No need to check anything. If the tile or user don't exist we just ignore it. */
|
2021-11-02 20:34:39 +00:00
|
|
|
if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, user_id);
|
2010-08-18 20:48:38 +00:00
|
|
|
|
|
|
|
return CommandCost();
|
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2010-08-18 20:48:38 +00:00
|
|
|
/* static */ void OrderBackup::ResetUser(uint32 user)
|
|
|
|
{
|
|
|
|
assert(_network_server);
|
|
|
|
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
|
|
|
/* If it's not a backup of us, ignore it. */
|
2010-08-18 20:48:38 +00:00
|
|
|
if (ob->user != user) continue;
|
|
|
|
|
2021-11-02 20:34:39 +00:00
|
|
|
Command<CMD_CLEAR_ORDER_BACKUP>::Post(0, static_cast<ClientID>(user));
|
2010-08-18 20:48:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* Reset the OrderBackups from GUI/game logic.
|
2011-05-02 16:14:23 +00:00
|
|
|
* @param t The tile of the order backup.
|
2011-01-18 23:09:43 +00:00
|
|
|
* @param from_gui Whether the call came from the GUI, i.e. whether
|
|
|
|
* it must be synced over the network.
|
|
|
|
*/
|
2010-08-18 20:48:38 +00:00
|
|
|
/* 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. */
|
|
|
|
uint32 user = _networking && !_network_server ? _network_own_client_id : CLIENT_ID_SERVER;
|
|
|
|
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
2021-12-06 18:30:46 +00:00
|
|
|
/* If this is a GUI action, and it's not a backup of us, ignore it. */
|
|
|
|
if (from_gui && ob->user != user) continue;
|
2010-08-18 20:48:38 +00:00
|
|
|
/* If it's not for our chosen tile either, ignore it. */
|
|
|
|
if (t != INVALID_TILE && t != ob->tile) continue;
|
|
|
|
|
|
|
|
if (from_gui) {
|
2010-12-07 20:45:11 +00:00
|
|
|
/* We need to circumvent the "prevention" from this command being executed
|
|
|
|
* while the game is paused, so use the internal method. Nor do we want
|
|
|
|
* this command to get its cost estimated when shift is pressed. */
|
2021-11-28 16:37:04 +00:00
|
|
|
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) });
|
2010-08-18 20:48:38 +00:00
|
|
|
} 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;
|
|
|
|
}
|
2010-08-18 18:52:16 +00:00
|
|
|
}
|
2010-08-18 15:15:59 +00:00
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* Clear the group of all backups having this group ID.
|
|
|
|
* @param group The group to clear.
|
|
|
|
*/
|
2010-08-18 15:15:59 +00:00
|
|
|
/* static */ void OrderBackup::ClearGroup(GroupID group)
|
|
|
|
{
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
2010-08-18 15:15:59 +00:00
|
|
|
if (ob->group == group) ob->group = DEFAULT_GROUP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-18 23:09:43 +00:00
|
|
|
/**
|
|
|
|
* Clear/update the (clone) vehicle from an order backup.
|
|
|
|
* @param v The vehicle to clear.
|
2019-04-10 21:07:06 +00:00
|
|
|
* @pre v != nullptr
|
2011-01-18 23:09:43 +00:00
|
|
|
* @note If it is not possible to set another vehicle as clone
|
|
|
|
* "example", then this backed up order will be removed.
|
|
|
|
*/
|
2010-08-18 15:58:30 +00:00
|
|
|
/* static */ void OrderBackup::ClearVehicle(const Vehicle *v)
|
|
|
|
{
|
2019-04-10 21:07:06 +00:00
|
|
|
assert(v != nullptr);
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
2010-08-18 15:58:30 +00:00
|
|
|
if (ob->clone == v) {
|
|
|
|
/* Get another item in the shared list. */
|
|
|
|
ob->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
|
|
|
|
/* But if that isn't there, remove it. */
|
2019-04-10 21:07:06 +00:00
|
|
|
if (ob->clone == nullptr) delete ob;
|
2010-08-18 15:58:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-12-09 20:48:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes an order from all vehicles. Triggers when, say, a station is removed.
|
|
|
|
* @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
|
|
|
|
* @param destination The destination. Can be a StationID, DepotID or WaypointID.
|
2018-10-01 15:01:28 +00:00
|
|
|
* @param hangar Only used for airports in the destination.
|
|
|
|
* When false, remove airport and hangar orders.
|
|
|
|
* When true, remove either airport or hangar order.
|
2011-12-09 20:48:13 +00:00
|
|
|
*/
|
2018-10-01 15:01:28 +00:00
|
|
|
/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination, bool hangar)
|
2011-12-09 20:48:13 +00:00
|
|
|
{
|
2019-12-17 00:35:29 +00:00
|
|
|
for (OrderBackup *ob : OrderBackup::Iterate()) {
|
2019-04-10 21:07:06 +00:00
|
|
|
for (Order *order = ob->orders; order != nullptr; order = order->next) {
|
2011-12-09 20:48:13 +00:00
|
|
|
OrderType ot = order->GetType();
|
|
|
|
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
|
2019-03-20 21:46:02 +00:00
|
|
|
if (ot == OT_GOTO_DEPOT && hangar && !IsHangarTile(ob->tile)) continue; // Not an aircraft? Can't have a hangar order.
|
2018-10-01 15:01:28 +00:00
|
|
|
if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
|
2011-12-09 20:48:13 +00:00
|
|
|
if (ot == type && order->GetDestination() == destination) {
|
|
|
|
/* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */
|
|
|
|
delete ob;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|