diff --git a/src/economy.cpp b/src/economy.cpp index 354f5addcf..ef35e289fd 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -314,6 +314,8 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) assert(old_owner != _local_company); } + ClearOrderDestinationRefcountMap(); + Town *t; assert(old_owner != new_owner); @@ -589,6 +591,8 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) /* Change owner of deferred cargo payments */ ChangeOwnershipOfCargoPacketDeferredPayments(old_owner, new_owner); + IntialiseOrderDestinationRefcountMap(); + cur_company.Restore(); MarkWholeScreenDirty(); diff --git a/src/misc.cpp b/src/misc.cpp index 5cee807ffc..9fd08a2d0f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -102,6 +102,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin FreeSignalDependencies(); ClearZoningCaches(); + IntialiseOrderDestinationRefcountMap(); ResetPersistentNewGRFData(); diff --git a/src/openttd.cpp b/src/openttd.cpp index 1e6b2c1d9c..3089c77c76 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -362,6 +362,7 @@ static void ShutdownGame() FreeSignalDependencies(); ClearZoningCaches(); + ClearOrderDestinationRefcountMap(); /* No NewGRFs were loaded when it was still bootstrapping. */ if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData(); diff --git a/src/order_base.h b/src/order_base.h index f3d93726f2..0ca811f382 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -24,11 +24,32 @@ #include #include +#include "3rdparty/cpp-btree/btree_map.h" typedef Pool OrderPool; typedef Pool OrderListPool; extern OrderPool _order_pool; extern OrderListPool _orderlist_pool; +extern btree::btree_map _order_destination_refcount_map; +extern bool _order_destination_refcount_map_valid; + +inline uint32 OrderDestinationRefcountMapKey(DestinationID dest, CompanyID cid, OrderType order_type, VehicleType veh_type) +{ + static_assert(sizeof(dest) == 2); + static_assert(OT_END <= 16); + return (((uint32) dest) << 16) | (((uint32) cid) << 8) | (((uint32) order_type) << 4) | ((uint32) veh_type); +} + +template void IterateOrderRefcountMapForDestinationID(DestinationID dest, F handler) +{ + for (auto lb = _order_destination_refcount_map.lower_bound(OrderDestinationRefcountMapKey(dest, (CompanyID) 0, (OrderType) 0, (VehicleType) 0)); lb != _order_destination_refcount_map.end(); ++lb) { + if (GB(lb->first, 16, 16) != dest) return; + if (lb->second && !handler((CompanyID) GB(lb->first, 8, 8), (OrderType) GB(lb->first, 4, 4), (VehicleType) GB(lb->first, 0, 4), lb->second)) return; + } +} + +void IntialiseOrderDestinationRefcountMap(); +void ClearOrderDestinationRefcountMap(); struct OrderExtraInfo { uint8 cargo_type_flags[NUM_CARGO] = {}; ///< Load/unload types for each cargo type. diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 43f53a3c0c..a0e0804210 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -55,6 +55,47 @@ INSTANTIATE_POOL_METHODS(Order) OrderListPool _orderlist_pool("OrderList"); INSTANTIATE_POOL_METHODS(OrderList) +btree::btree_map _order_destination_refcount_map; +bool _order_destination_refcount_map_valid = false; + +void IntialiseOrderDestinationRefcountMap() +{ + ClearOrderDestinationRefcountMap(); + const Vehicle *v; + FOR_ALL_VEHICLES(v) { + const Order *order; + FOR_VEHICLE_ORDERS(v, order) { + if (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT) || order->IsType(OT_IMPLICIT)) { + _order_destination_refcount_map[OrderDestinationRefcountMapKey(order->GetDestination(), v->owner, order->GetType(), v->type)]++; + } + } + } + _order_destination_refcount_map_valid = true; +} + +void ClearOrderDestinationRefcountMap() +{ + _order_destination_refcount_map.clear(); + _order_destination_refcount_map_valid = false; +} + +static void UpdateOrderDestinationRefcount(const Order *order, VehicleType type, Owner owner, int delta) +{ + if (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT) || order->IsType(OT_IMPLICIT)) { + _order_destination_refcount_map[OrderDestinationRefcountMapKey(order->GetDestination(), owner, order->GetType(), type)] += delta; + } +} + +inline void RegisterOrderDestination(const Order *order, VehicleType type, Owner owner) +{ + if (_order_destination_refcount_map_valid) UpdateOrderDestinationRefcount(order, type, owner, 1); +} + +inline void UnregisterOrderDestination(const Order *order, VehicleType type, Owner owner) +{ + if (_order_destination_refcount_map_valid) UpdateOrderDestinationRefcount(order, type, owner, -1); +} + /** Clean everything up. */ Order::~Order() { @@ -397,6 +438,9 @@ void OrderList::Initialize(Order *chain, Vehicle *v) this->total_duration = 0; this->order_index.clear(); + VehicleType type = v->type; + Owner owner = v->owner; + for (Order *o = this->first; o != nullptr; o = o->next) { if (!o->IsType(OT_IMPLICIT)) ++this->num_manual_orders; if (!o->IsType(OT_CONDITIONAL)) { @@ -404,6 +448,7 @@ void OrderList::Initialize(Order *chain, Vehicle *v) this->total_duration += o->GetWaitTime() + o->GetTravelTime(); } this->order_index.push_back(o); + RegisterOrderDestination(o, type, owner); } for (Vehicle *u = this->first_shared->PreviousShared(); u != nullptr; u = u->PreviousShared()) { @@ -422,7 +467,10 @@ void OrderList::Initialize(Order *chain, Vehicle *v) void OrderList::FreeChain(bool keep_orderlist) { Order *next; + VehicleType type = this->GetFirstSharedVehicle()->type; + Owner owner = this->GetFirstSharedVehicle()->owner; for (Order *o = this->first; o != nullptr; o = next) { + UnregisterOrderDestination(o, type, owner); next = o->next; delete o; } @@ -629,6 +677,7 @@ void OrderList::InsertOrderAt(Order *new_order, int index) this->timetable_duration += new_order->GetTimetabledWait() + new_order->GetTimetabledTravel(); this->total_duration += new_order->GetWaitTime() + new_order->GetTravelTime(); } + RegisterOrderDestination(new_order, this->GetFirstSharedVehicle()->type, this->GetFirstSharedVehicle()->owner); this->ReindexOrderList(); /* We can visit oil rigs and buoys that are not our own. They will be shown in @@ -664,6 +713,7 @@ void OrderList::DeleteOrderAt(int index) this->timetable_duration -= (to_remove->GetTimetabledWait() + to_remove->GetTimetabledTravel()); this->total_duration -= (to_remove->GetWaitTime() + to_remove->GetTravelTime()); } + UnregisterOrderDestination(to_remove, this->GetFirstSharedVehicle()->type, this->GetFirstSharedVehicle()->owner); delete to_remove; this->ReindexOrderList(); }