diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index e86099d637..400188f5b9 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -73,6 +73,9 @@ INSTANTIATE_POOL_METHODS(TraceRestrictSlot) TraceRestrictCounterPool _tracerestrictcounter_pool("TraceRestrictCounter"); INSTANTIATE_POOL_METHODS(TraceRestrictCounter) +std::vector TraceRestrictSlot::veh_temporarily_added; +std::vector TraceRestrictSlot::veh_temporarily_removed; + /** * TraceRestrictRefId --> TraceRestrictProgramID (Pool ID) mapping * The indirection is mainly to enable shared programs @@ -667,16 +670,28 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp if (input.permitted_slot_operations & TRPISP_PBS_RES_END_ACQUIRE) { if (!slot->Occupy(v->index)) out.flags |= TRPRF_PBS_RES_END_WAIT; } else if (input.permitted_slot_operations & TRPISP_PBS_RES_END_ACQ_DRY) { - if (!slot->OccupyDryRun(v->index)) out.flags |= TRPRF_PBS_RES_END_WAIT; + if (this->actions_used_flags & TRPAUF_PBS_RES_END_SIMULATE) { + if (!slot->OccupyDryRunUsingTemporaryState(v->index)) out.flags |= TRPRF_PBS_RES_END_WAIT; + } else { + if (!slot->OccupyDryRun(v->index)) out.flags |= TRPRF_PBS_RES_END_WAIT; + } } break; case TRSCOF_PBS_RES_END_ACQ_TRY: - if (input.permitted_slot_operations & TRPISP_PBS_RES_END_ACQUIRE) slot->Occupy(v->index); + if (input.permitted_slot_operations & TRPISP_PBS_RES_END_ACQUIRE) { + slot->Occupy(v->index); + } else if ((input.permitted_slot_operations & TRPISP_PBS_RES_END_ACQ_DRY) && (this->actions_used_flags & TRPAUF_PBS_RES_END_SIMULATE)) { + slot->OccupyDryRunUsingTemporaryState(v->index); + } break; case TRSCOF_PBS_RES_END_RELEASE: - if (input.permitted_slot_operations & TRPISP_PBS_RES_END_RELEASE) slot->Vacate(v->index); + if (input.permitted_slot_operations & TRPISP_PBS_RES_END_RELEASE) { + slot->Vacate(v->index); + } else if ((input.permitted_slot_operations & TRPISP_PBS_RES_END_ACQ_DRY) && (this->actions_used_flags & TRPAUF_PBS_RES_END_SIMULATE)) { + slot->VacateUsingTemporaryState(v->index); + } break; case TRSCOF_ACQUIRE_TRY_ON_RESERVE: @@ -797,6 +812,9 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp } } } + if ((input.permitted_slot_operations & TRPISP_PBS_RES_END_ACQ_DRY) && (this->actions_used_flags & TRPAUF_PBS_RES_END_SIMULATE)) { + TraceRestrictSlot::RevertTemporaryChanges(v->index); + } assert(condstack.empty()); } @@ -823,6 +841,11 @@ CommandCost TraceRestrictProgram::Validate(const std::vector condstack.clear(); actions_used_flags = static_cast(0); + static std::vector pbs_res_end_released_slots; + pbs_res_end_released_slots.clear(); + static std::vector pbs_res_end_acquired_slots; + pbs_res_end_acquired_slots.clear(); + size_t size = items.size(); for (size_t i = 0; i < size; i++) { TraceRestrictItem item = items[i]; @@ -878,8 +901,6 @@ CommandCost TraceRestrictProgram::Validate(const std::vector case TRIT_COND_ENTRY_DIRECTION: case TRIT_COND_PBS_ENTRY_SIGNAL: case TRIT_COND_TRAIN_GROUP: - case TRIT_COND_TRAIN_IN_SLOT: - case TRIT_COND_SLOT_OCCUPANCY: case TRIT_COND_PHYS_PROP: case TRIT_COND_PHYS_RATIO: case TRIT_COND_TRAIN_OWNER: @@ -891,6 +912,13 @@ CommandCost TraceRestrictProgram::Validate(const std::vector case TRIT_COND_CATEGORY: break; + case TRIT_COND_TRAIN_IN_SLOT: + case TRIT_COND_SLOT_OCCUPANCY: + if (find_index(pbs_res_end_released_slots, GetTraceRestrictValue(item)) >= 0 || find_index(pbs_res_end_acquired_slots, GetTraceRestrictValue(item)) >= 0) { + actions_used_flags |= TRPAUF_PBS_RES_END_SIMULATE; + } + break; + default: return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION); } @@ -947,11 +975,19 @@ CommandCost TraceRestrictProgram::Validate(const std::vector case TRSCOF_PBS_RES_END_ACQ_WAIT: actions_used_flags |= TRPAUF_PBS_RES_END_SLOT | TRPAUF_PBS_RES_END_WAIT; + if (find_index(pbs_res_end_released_slots, GetTraceRestrictValue(item)) >= 0) actions_used_flags |= TRPAUF_PBS_RES_END_SIMULATE; + include(pbs_res_end_acquired_slots, GetTraceRestrictValue(item)); break; case TRSCOF_PBS_RES_END_ACQ_TRY: + actions_used_flags |= TRPAUF_PBS_RES_END_SLOT; + if (find_index(pbs_res_end_released_slots, GetTraceRestrictValue(item)) >= 0) actions_used_flags |= TRPAUF_PBS_RES_END_SIMULATE; + include(pbs_res_end_acquired_slots, GetTraceRestrictValue(item)); + break; + case TRSCOF_PBS_RES_END_RELEASE: actions_used_flags |= TRPAUF_PBS_RES_END_SLOT; + include(pbs_res_end_released_slots, GetTraceRestrictValue(item)); break; case TRSCOF_ACQUIRE_TRY_ON_RESERVE: @@ -1932,7 +1968,7 @@ bool TraceRestrictSlot::Occupy(VehicleID id, bool force) /** * Dry-run adding vehicle ID to occupants if possible and not already an occupant * @param id Vehicle ID - * @return whether vehicle IDwould be an occupant + * @return whether vehicle ID would be an occupant */ bool TraceRestrictSlot::OccupyDryRun(VehicleID id) { @@ -1941,6 +1977,25 @@ bool TraceRestrictSlot::OccupyDryRun(VehicleID id) return true; } +/** + * Dry-run adding vehicle ID to occupants if possible and not already an occupant, record any changes in the temporary state to be reverted later + * @param id Vehicle ID + * @return whether vehicle ID is now an occupant + */ +bool TraceRestrictSlot::OccupyDryRunUsingTemporaryState(VehicleID id) +{ + if (this->IsOccupant(id)) return true; + if (this->occupants.size() >= this->max_occupancy) return false; + + this->occupants.push_back(id); + + if (find_index(veh_temporarily_removed, this->index) < 0) { + include(veh_temporarily_added, this->index); + } + + return true; +} + /** * Remove vehicle ID from occupants * @param id Vehicle ID @@ -1953,6 +2008,19 @@ void TraceRestrictSlot::Vacate(VehicleID id) } } +/** + * Remove vehicle ID from occupants, record any changes in the temporary state to be reverted later + * @param id Vehicle ID + */ +void TraceRestrictSlot::VacateUsingTemporaryState(VehicleID id) +{ + if (container_unordered_remove(this->occupants, id)) { + if (find_index(veh_temporarily_added, this->index) < 0) { + include(veh_temporarily_removed, this->index); + } + } +} + /** Remove all occupants */ void TraceRestrictSlot::Clear() { @@ -2039,6 +2107,21 @@ void TraceRestrictSlot::PreCleanPool() slot_vehicle_index.clear(); } +/** Revert any temporary changes */ +void TraceRestrictSlot::RevertTemporaryChanges(VehicleID veh) +{ + for (TraceRestrictSlotID id : veh_temporarily_added) { + TraceRestrictSlot *slot = TraceRestrictSlot::Get(id); + container_unordered_remove(slot->occupants, veh); + } + for (TraceRestrictSlotID id : veh_temporarily_removed) { + TraceRestrictSlot *slot = TraceRestrictSlot::Get(id); + include(slot->occupants, veh); + } + veh_temporarily_added.clear(); + veh_temporarily_removed.clear(); +} + /** Remove vehicle ID from all slot occupants */ void TraceRestrictRemoveVehicleFromAllSlots(VehicleID vehicle_id) { diff --git a/src/tracerestrict.h b/src/tracerestrict.h index dbdfe96a09..c874722749 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -421,6 +421,7 @@ enum TraceRestrictProgramActionsUsedFlags { TRPAUF_NO_PBS_BACK_PENALTY = 1 << 13, ///< No PBS back penalty is present TRPAUF_SLOT_ACQUIRE_ON_RES = 1 << 14, ///< Slot acquire (on reserve) action is present TRPAUF_SPEED_ADAPTATION = 1 << 15, ///< Speed adaptation control + TRPAUF_PBS_RES_END_SIMULATE = 1 << 16, ///< PBS reservations ending at this signal slot changes must be fully simulated in dry run mode }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramActionsUsedFlags) @@ -994,10 +995,14 @@ struct TraceRestrictSlot : TraceRestrictSlotPool::PoolItem<&_tracerestrictslot_p std::vector progsig_dependants; + static std::vector veh_temporarily_added; + static std::vector veh_temporarily_removed; + static void RebuildVehicleIndex(); static bool ValidateVehicleIndex(); static void ValidateSlotOccupants(std::function log); static void PreCleanPool(); + static void RevertTemporaryChanges(VehicleID veh); TraceRestrictSlot(CompanyID owner = INVALID_COMPANY, VehicleType type = VEH_TRAIN) { @@ -1020,7 +1025,9 @@ struct TraceRestrictSlot : TraceRestrictSlotPool::PoolItem<&_tracerestrictslot_p bool Occupy(VehicleID id, bool force = false); bool OccupyDryRun(VehicleID ids); + bool OccupyDryRunUsingTemporaryState(VehicleID id); void Vacate(VehicleID id); + void VacateUsingTemporaryState(VehicleID id); void Clear(); void UpdateSignals();