order_backup.cpp

Go to the documentation of this file.
00001 /* $Id: order_backup.cpp 23795 2012-01-13 21:54:59Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "command_func.h"
00014 #include "core/pool_func.hpp"
00015 #include "network/network.h"
00016 #include "network/network_func.h"
00017 #include "order_backup.h"
00018 #include "vehicle_base.h"
00019 #include "window_func.h"
00020 #include "station_map.h"
00021 
00022 OrderBackupPool _order_backup_pool("BackupOrder");
00023 INSTANTIATE_POOL_METHODS(OrderBackup)
00024 
00025 
00026 OrderBackup::~OrderBackup()
00027 {
00028   free(this->name);
00029 
00030   if (CleaningPool()) return;
00031 
00032   Order *o = this->orders;
00033   while (o != NULL) {
00034     Order *next = o->next;
00035     delete o;
00036     o = next;
00037   }
00038 }
00039 
00045 OrderBackup::OrderBackup(const Vehicle *v, uint32 user)
00046 {
00047   this->user             = user;
00048   this->tile             = v->tile;
00049   this->orderindex       = v->cur_implicit_order_index;
00050   this->group            = v->group_id;
00051   this->service_interval = v->service_interval;
00052 
00053   if (v->name != NULL) this->name = strdup(v->name);
00054 
00055   /* If we have shared orders, store the vehicle we share the order with. */
00056   if (v->IsOrderListShared()) {
00057     this->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
00058   } else {
00059     /* Else copy the orders */
00060     Order **tail = &this->orders;
00061 
00062     /* Count the number of orders */
00063     const Order *order;
00064     FOR_VEHICLE_ORDERS(v, order) {
00065       Order *copy = new Order();
00066       copy->AssignOrder(*order);
00067       *tail = copy;
00068       tail = &copy->next;
00069     }
00070   }
00071 }
00072 
00077 void OrderBackup::DoRestore(Vehicle *v)
00078 {
00079   /* If we have a custom name, process that */
00080   v->name = this->name;
00081   this->name = NULL;
00082 
00083   /* If we had shared orders, recover that */
00084   if (this->clone != NULL) {
00085     DoCommand(0, v->index | CO_SHARE << 30, this->clone->index, DC_EXEC, CMD_CLONE_ORDER);
00086   } else if (this->orders != NULL && OrderList::CanAllocateItem()) {
00087     v->orders.list = new OrderList(this->orders, v);
00088     this->orders = NULL;
00089     /* Make sure buoys/oil rigs are updated in the station list. */
00090     InvalidateWindowClassesData(WC_STATION_LIST, 0);
00091   }
00092 
00093   uint num_orders = v->GetNumOrders();
00094   if (num_orders != 0) {
00095     v->cur_real_order_index = v->cur_implicit_order_index = this->orderindex % num_orders;
00096     v->UpdateRealOrderIndex();
00097   }
00098   v->service_interval = this->service_interval;
00099 
00100   /* Restore vehicle group */
00101   DoCommand(0, this->group, v->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP);
00102 }
00103 
00110 /* static */ void OrderBackup::Backup(const Vehicle *v, uint32 user)
00111 {
00112   /* Don't use reset as that broadcasts over the network to reset the variable,
00113    * which is what we are doing at the moment. */
00114   OrderBackup *ob;
00115   FOR_ALL_ORDER_BACKUPS(ob) {
00116     if (ob->user == user) delete ob;
00117   }
00118   if (OrderBackup::CanAllocateItem()) {
00119     new OrderBackup(v, user);
00120   }
00121 }
00122 
00129 /* static */ void OrderBackup::Restore(Vehicle *v, uint32 user)
00130 {
00131   OrderBackup *ob;
00132   FOR_ALL_ORDER_BACKUPS(ob) {
00133     if (v->tile != ob->tile || ob->user != user) continue;
00134 
00135     ob->DoRestore(v);
00136     delete ob;
00137   }
00138 }
00139 
00146 /* static */ void OrderBackup::ResetOfUser(TileIndex tile, uint32 user)
00147 {
00148   OrderBackup *ob;
00149   FOR_ALL_ORDER_BACKUPS(ob) {
00150     if (ob->user == user && (ob->tile == tile || tile == INVALID_TILE)) delete ob;
00151   }
00152 }
00153 
00163 CommandCost CmdClearOrderBackup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00164 {
00165   /* No need to check anything. If the tile or user don't exist we just ignore it. */
00166   if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, p2);
00167 
00168   return CommandCost();
00169 }
00170 
00177 /* static */ void OrderBackup::ResetUser(uint32 user)
00178 {
00179   assert(_network_server);
00180 
00181   OrderBackup *ob;
00182   FOR_ALL_ORDER_BACKUPS(ob) {
00183     /* If it's not an backup of us, so ignore it. */
00184     if (ob->user != user) continue;
00185 
00186     DoCommandP(0, 0, user, CMD_CLEAR_ORDER_BACKUP);
00187     return;
00188   }
00189 }
00190 
00197 /* static */ void OrderBackup::Reset(TileIndex t, bool from_gui)
00198 {
00199   /* The user has CLIENT_ID_SERVER as default when network play is not active,
00200    * but compiled it. A network client has its own variable for the unique
00201    * client/user identifier. Finally if networking isn't compiled in the
00202    * default is just plain and simple: 0. */
00203 #ifdef ENABLE_NETWORK
00204   uint32 user = _networking && !_network_server ? _network_own_client_id : CLIENT_ID_SERVER;
00205 #else
00206   uint32 user = 0;
00207 #endif
00208 
00209   OrderBackup *ob;
00210   FOR_ALL_ORDER_BACKUPS(ob) {
00211     /* If it's not an backup of us, so ignore it. */
00212     if (ob->user != user) continue;
00213     /* If it's not for our chosen tile either, ignore it. */
00214     if (t != INVALID_TILE && t != ob->tile) continue;
00215 
00216     if (from_gui) {
00217       /* We need to circumvent the "prevention" from this command being executed
00218        * while the game is paused, so use the internal method. Nor do we want
00219        * this command to get its cost estimated when shift is pressed. */
00220       DoCommandPInternal(ob->tile, 0, user, CMD_CLEAR_ORDER_BACKUP, NULL, NULL, true, false);
00221     } else {
00222       /* The command came from the game logic, i.e. the clearing of a tile.
00223        * In that case we have no need to actually sync this, just do it. */
00224       delete ob;
00225     }
00226   }
00227 }
00228 
00233 /* static */ void OrderBackup::ClearGroup(GroupID group)
00234 {
00235   OrderBackup *ob;
00236   FOR_ALL_ORDER_BACKUPS(ob) {
00237     if (ob->group == group) ob->group = DEFAULT_GROUP;
00238   }
00239 }
00240 
00248 /* static */ void OrderBackup::ClearVehicle(const Vehicle *v)
00249 {
00250   assert(v != NULL);
00251   OrderBackup *ob;
00252   FOR_ALL_ORDER_BACKUPS(ob) {
00253     if (ob->clone == v) {
00254       /* Get another item in the shared list. */
00255       ob->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
00256       /* But if that isn't there, remove it. */
00257       if (ob->clone == NULL) delete ob;
00258     }
00259   }
00260 }
00261 
00262 void InitializeOrderBackups()
00263 {
00264   _order_backup_pool.CleanPool();
00265 }
00266 
00272 /* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination)
00273 {
00274   OrderBackup *ob;
00275   FOR_ALL_ORDER_BACKUPS(ob) {
00276     for (Order *order = ob->orders; order != NULL; order = order->next) {
00277       OrderType ot = order->GetType();
00278       if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
00279       if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT)) ot = OT_GOTO_STATION;
00280       if (ot == type && order->GetDestination() == destination) {
00281         /* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */
00282         delete ob;
00283         break;
00284       }
00285     }
00286   }
00287 }