From c1bf236a267b867c95181ebf61ecb78e3da3133f Mon Sep 17 00:00:00 2001 From: Andreas Schmitt Date: Sat, 19 Jun 2021 23:01:12 +0200 Subject: [PATCH] Rewrite the ATC logic Now signals store speed information with a timeout for this data's validity --- src/train.h | 3 +- src/train_cmd.cpp | 158 ++++++++++++++++++++++++---------------------- src/vehicle.cpp | 1 + 3 files changed, 87 insertions(+), 75 deletions(-) diff --git a/src/train.h b/src/train.h index c33f7317e4..b303523cc5 100644 --- a/src/train.h +++ b/src/train.h @@ -147,6 +147,7 @@ struct Train FINAL : public GroundVehicle { uint16 reverse_distance; uint16 tunnel_bridge_signal_num; uint16 speed_restriction; + uint16 signal_speed_restriction; /** We don't want GCC to zero our struct! It already is zeroed and has an index! */ Train() : GroundVehicleBase() {} @@ -190,7 +191,7 @@ struct Train FINAL : public GroundVehicle { }; private: - int GetAtcMaxSpeed(int current_max_speed) const; + int GetAtcMaxSpeed() const; MaxSpeedInfo GetCurrentMaxSpeedInfoInternal(bool update_state) const; public: diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 2c3c0bb4b8..c71ce6916c 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -76,6 +76,40 @@ enum ChooseTrainTrackFlags { }; DECLARE_ENUM_AS_BIT_SET(ChooseTrainTrackFlags) +struct SignalSpeedKey +{ + TileIndex signal_tile; + Track signal_track; + Trackdir last_passing_train_dir; + + bool operator==(const SignalSpeedKey& other) const + { + return signal_tile == other.signal_tile && + signal_track == other.signal_track && + last_passing_train_dir == other.last_passing_train_dir; + } +}; + +struct SignalSpeedKeyHashFunc +{ + std::size_t operator() (const SignalSpeedKey &key) const + { + const std::size_t h1 = std::hash()(key.signal_tile); + const std::size_t h2 = std::hash()(key.last_passing_train_dir); + const std::size_t h3 = std::hash()(key.signal_track); + + return (h1 ^ h2) ^ h3; + } +}; + +struct SignalSpeedValue +{ + uint16 train_speed; + Date time_stamp; +}; + +static std::unordered_map _signal_speeds(1 << 16); + static void TryLongReserveChooseTrainTrackFromReservationEnd(Train *v, bool no_reserve_vehicle_tile = false); static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, ChooseTrainTrackFlags flags, bool *p_got_reservation, ChooseTrainTrackLookAheadState lookahead_state = {}); static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse); @@ -954,75 +988,13 @@ static void AdvanceLookAheadPosition(Train *v) /** * Calculates the maximum speed based on any train in front of this train. */ -int Train::GetAtcMaxSpeed(int current_max_speed) const +int Train::GetAtcMaxSpeed() const { - if (!(this->vehstatus & VS_CRASHED) && _settings_game.vehicle.train_speed_adaption) { - int atc_speed = current_max_speed; - - CFollowTrackRail ft(this); - Trackdir old_td = this->GetVehicleTrackdir(); - - if (ft.Follow(this->tile, this->GetVehicleTrackdir())) { - /* Basic idea: Follow the track for 20 tiles or 3 signals (i.e. at most two signal blocks) looking for other trains. */ - /* If we find one (that meets certain restrictions), we limit the max speed to the speed of that train. */ - int num_tiles = 0; - int num_signals = 0; - - do { - old_td = ft.m_old_td; - - /* If we are on a depot or rail station tile stop searching */ - if (IsDepotTile(ft.m_new_tile) || IsRailStationTile(ft.m_new_tile)) - break; - - /* Increment signal counter if we're on a signal */ - if (IsTileType(ft.m_new_tile, MP_RAILWAY) && ///< Tile has rails - KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE && ///< Tile has exactly *one* track - HasSignalOnTrack(ft.m_new_tile, TrackBitsToTrack(TrackdirBitsToTrackBits(ft.m_new_td_bits)))) { ///< Tile has signal - num_signals++; - } - - /* Check if tile has train/is reserved */ - if (KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE && ///< Tile has exactly *one* track - HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits))) { ///< Tile is reserved - Train* other_train = GetTrainForReservation(ft.m_new_tile, TrackBitsToTrack(TrackdirBitsToTrackBits(ft.m_new_td_bits))); - - - if (other_train != nullptr && - other_train != this && ///< Other train is not this train - other_train->GetAccelerationStatus() != AS_BRAKE) { ///< Other train is not braking - atc_speed = other_train->GetCurrentSpeed(); - break; - } - } - - /* Decide what in direction to continue: reservation, straight or "first/only" direction. */ - /* Abort if there's no reservation even though the tile contains multiple tracks. */ - const TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile)); - - if (reserved != TRACKDIR_BIT_NONE) { - // There is a reservation to follow. - old_td = FindFirstTrackdir(reserved); - } - else if (KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) { - // Tile has more than one track and we have no reservation. Bail out. - break; - } - else { - // There was no reservation but there is only one direction to follow, so follow it. - old_td = FindFirstTrackdir(ft.m_new_td_bits); - } - - num_tiles++; - } while (num_tiles < 20 && num_signals < 4 && ft.Follow(ft.m_new_tile, old_td)); - } - - /* Check that the ATC speed is sufficiently large. - Avoids assertion error in UpdateSpeed(). */ - current_max_speed = std::max(25, std::min(current_max_speed, atc_speed)); + if (!(this->vehstatus & VS_CRASHED) && _settings_game.vehicle.train_speed_adaption && this->signal_speed_restriction != 0) { + return std::max(25, this->signal_speed_restriction); } - return current_max_speed; + return UINT32_MAX; } /** @@ -1035,7 +1007,7 @@ Train::MaxSpeedInfo Train::GetCurrentMaxSpeedInfoInternal(bool update_state) con this->gcache.cached_max_track_speed : std::min(this->tcache.cached_max_curve_speed, this->gcache.cached_max_track_speed); - max_speed = GetAtcMaxSpeed(max_speed); + max_speed = std::min(max_speed, GetAtcMaxSpeed()); if (this->current_order.IsType(OT_LOADING_ADVANCE)) max_speed = std::min(max_speed, 15); @@ -1404,6 +1376,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const v->vehstatus = VS_HIDDEN | VS_DEFPAL; v->reverse_distance = 0; v->speed_restriction = 0; + v->signal_speed_restriction = 0; v->SetWagon(); @@ -4520,6 +4493,7 @@ static void TrainEnterStation(Train *v, StationID station) v->current_order.MakeWaiting(); v->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION); v->cur_speed = 0; + v->signal_speed_restriction = 0; return; } @@ -5576,7 +5550,29 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) } if (update_signals_crossing) { + if (v->IsFrontEngine()) { + if (IsTileType(gp.old_tile, MP_RAILWAY) && HasSignals(gp.old_tile)) { + const TrackdirBits rev_tracks = TrackBitsToTrackdirBits(GetTrackBits(gp.old_tile)) & DiagdirReachesTrackdirs(ReverseDiagDir(enterdir)); + const Trackdir rev_trackdir = FindFirstTrackdir(rev_tracks); + const Track track = TrackdirToTrack(rev_trackdir); + SignalSpeedKey speed_key = { + speed_key.signal_tile = gp.old_tile, + speed_key.signal_track = track, + speed_key.last_passing_train_dir = v->GetVehicleTrackdir() + }; + const auto found_speed_restriction = _signal_speeds.find(speed_key); + + if (found_speed_restriction != _signal_speeds.end()) { + if (_date - found_speed_restriction->second.time_stamp < 6) { + v->signal_speed_restriction = std::max(25, found_speed_restriction->second.train_speed); + } else { + _signal_speeds.erase(speed_key); + v->signal_speed_restriction = 0; + } + } + } + switch (TrainMovedChangeSignal(v, gp.new_tile, enterdir, true)) { case CHANGED_NORMAL_TO_PBS_BLOCK: /* We are entering a block with PBS signals right now, but @@ -5614,17 +5610,31 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) TrainMovedChangeSignal(v, gp.old_tile, ReverseDiagDir(enterdir), false); if (IsLevelCrossingTile(gp.old_tile)) UpdateLevelCrossing(gp.old_tile); - if (IsTileType(gp.old_tile, MP_RAILWAY) && HasSignals(gp.old_tile) && IsRestrictedSignal(gp.old_tile)) { + if (IsTileType(gp.old_tile, MP_RAILWAY) && HasSignals(gp.old_tile)) { const TrackdirBits rev_tracks = TrackBitsToTrackdirBits(GetTrackBits(gp.old_tile)) & DiagdirReachesTrackdirs(ReverseDiagDir(enterdir)); const Trackdir rev_trackdir = FindFirstTrackdir(rev_tracks); const Track track = TrackdirToTrack(rev_trackdir); + if (HasSignalOnTrack(gp.old_tile, track)) { - const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.old_tile, track); - if (prog && prog->actions_used_flags & TRPAUF_SLOT_RELEASE_BACK) { - TraceRestrictProgramResult out; - TraceRestrictProgramInput input(gp.old_tile, ReverseTrackdir(rev_trackdir), nullptr, nullptr); - input.permitted_slot_operations = TRPISP_RELEASE_BACK; - prog->Execute(first, input, out); + SignalSpeedKey speed_key = { + speed_key.signal_tile = gp.old_tile, + speed_key.signal_track = track, + speed_key.last_passing_train_dir = v->GetVehicleTrackdir() + }; + SignalSpeedValue speed_value = { + speed_value.train_speed = v->First()->GetDisplaySpeed(), + speed_value.time_stamp = _date + }; + _signal_speeds[speed_key] = speed_value; + + if (IsRestrictedSignal(gp.old_tile)) { + const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.old_tile, track); + if (prog && prog->actions_used_flags & TRPAUF_SLOT_RELEASE_BACK) { + TraceRestrictProgramResult out; + TraceRestrictProgramInput input(gp.old_tile, ReverseTrackdir(rev_trackdir), nullptr, nullptr); + input.permitted_slot_operations = TRPISP_RELEASE_BACK; + prog->Execute(first, input, out); + } } } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 995a6e0410..cd2d212f8d 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2417,6 +2417,7 @@ void VehicleEnterDepot(Vehicle *v) ClrBit(t->flags, VRF_TOGGLE_REVERSE); t->ConsistChanged(CCF_ARRANGE); t->reverse_distance = 0; + t->signal_speed_restriction = 0; t->lookahead.reset(); if (!(t->vehstatus & VS_CRASHED)) { t->crash_anim_pos = 0;