From 72a76a0ea59c68ce1446947e43da4fde271c50d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Mon, 20 Mar 2023 12:47:13 +0200 Subject: [PATCH 1/8] Refactor import/export to support multiple ride rating update states --- src/openrct2/park/ParkFile.cpp | 20 +++++++++++++++++++- src/openrct2/rct2/S6Importer.cpp | 5 ++++- src/openrct2/ride/RideRatings.cpp | 23 +++++++++++++++++++++-- src/openrct2/ride/RideRatings.h | 7 ++++++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/openrct2/park/ParkFile.cpp b/src/openrct2/park/ParkFile.cpp index a30fe38b1a..53d6421f68 100644 --- a/src/openrct2/park/ParkFile.cpp +++ b/src/openrct2/park/ParkFile.cpp @@ -536,7 +536,25 @@ namespace OpenRCT2 cs.ReadWrite(gGrassSceneryTileLoopPosition); cs.ReadWrite(gWidePathTileLoopPosition); - ReadWriteRideRatingCalculationData(cs, gRideRatingUpdateState); + auto& rideRatings = RideRatingGetUpdateStates(); + if (os.GetHeader().TargetVersion >= 21) + { + cs.ReadWriteArray(rideRatings, [this, &cs](RideRatingUpdateState& calcData) { + ReadWriteRideRatingCalculationData(cs, calcData); + return true; + }); + } + else + { + // Only single state was stored prior to version 20. + if (os.GetMode() == OrcaStream::Mode::READING) + { + // Since we read only one state ensure the rest is reset. + RideRatingResetUpdateStates(); + } + auto& rideRatingUpdateState = rideRatings[0]; + ReadWriteRideRatingCalculationData(cs, rideRatingUpdateState); + } if (os.GetHeader().TargetVersion >= 14) { diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 76fe31644a..9ee12d3c7c 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1097,7 +1097,10 @@ namespace RCT2 void ImportRideRatingsCalcData() { const auto& src = _s6.RideRatingsCalcData; - auto& dst = gRideRatingUpdateState; + // S6 has only one state, ensure we reset all states before reading the first one. + RideRatingResetUpdateStates(); + auto& rideRatingStates = RideRatingGetUpdateStates(); + auto& dst = rideRatingStates[0]; dst = {}; dst.Proximity = { src.ProximityX, src.ProximityY, src.ProximityZ }; dst.ProximityStart = { src.ProximityStartX, src.ProximityStartY, src.ProximityStartZ }; diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index c6afa920a3..e95be1f15e 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -77,7 +77,7 @@ struct ShelteredEights uint8_t TotalShelteredEighths; }; -RideRatingUpdateState gRideRatingUpdateState; +static RideRatingUpdateStates gRideRatingUpdateStates; static void ride_ratings_update_state(RideRatingUpdateState& state); static void ride_ratings_update_state_0(RideRatingUpdateState& state); @@ -93,6 +93,25 @@ static void ride_ratings_score_close_proximity(RideRatingUpdateState& state, Til static void ride_ratings_add(RatingTuple* rating, int32_t excitement, int32_t intensity, int32_t nausea); +RideRatingUpdateStates& RideRatingGetUpdateStates() +{ + return gRideRatingUpdateStates; +} + +void RideRatingResetUpdateStates() +{ + RideRatingUpdateState nullState{}; + nullState.State = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + + std::fill(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), nullState); +} + +RideRatingUpdateState& RideRatingGetUpdateState(size_t index) +{ + Guard::IndexInRange(index, gRideRatingUpdateStates); + return gRideRatingUpdateStates[index]; +} + /** * This is a small hack function to keep calling the ride rating processor until * the given ride's ratings have been calculated. Whatever is currently being @@ -126,7 +145,7 @@ void RideRatingsUpdateAll() // NOTE: With the new save format more than one ride can be updated at once, but this has not yet been implemented. // The SV6 format could store only a single state. - ride_ratings_update_state(gRideRatingUpdateState); + ride_ratings_update_state(gRideRatingUpdateStates[0]); } static void ride_ratings_update_state(RideRatingUpdateState& state) diff --git a/src/openrct2/ride/RideRatings.h b/src/openrct2/ride/RideRatings.h index 7ca932121d..3ed0481744 100644 --- a/src/openrct2/ride/RideRatings.h +++ b/src/openrct2/ride/RideRatings.h @@ -54,7 +54,12 @@ struct RideRatingUpdateState uint16_t StationFlags; }; -extern RideRatingUpdateState gRideRatingUpdateState; +static constexpr size_t RideRatingMaxUpdateStates = 4; + +using RideRatingUpdateStates = std::array; + +RideRatingUpdateStates& RideRatingGetUpdateStates(); +void RideRatingResetUpdateStates(); void RideRatingsUpdateRide(const Ride& ride); void RideRatingsUpdateAll(); From 8eec6948c2148947aa53a602d3362fb4176c1227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Mon, 20 Mar 2023 15:25:21 +0200 Subject: [PATCH 2/8] Update multiple ride rating states at once --- src/openrct2/ride/RideRatings.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index e95be1f15e..6a1cb24496 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -7,6 +7,8 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ +#pragma optimize("", off) + #include "RideRatings.h" #include "../Cheats.h" @@ -143,9 +145,10 @@ void RideRatingsUpdateAll() if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) return; - // NOTE: With the new save format more than one ride can be updated at once, but this has not yet been implemented. - // The SV6 format could store only a single state. - ride_ratings_update_state(gRideRatingUpdateStates[0]); + for (auto& updateState : gRideRatingUpdateStates) + { + ride_ratings_update_state(updateState); + } } static void ride_ratings_update_state(RideRatingUpdateState& state) @@ -190,6 +193,12 @@ static RideId GetNextRideToUpdate(RideId currentRide) return (*nextRide).id; } +static bool RideRatingIsUpdatingRide(RideId id) +{ + return std::any_of(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), [id](auto& state) { + return state.CurrentRide == id && state.State != RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + }); +} /** * * rct2: 0x006B5A5C @@ -209,7 +218,10 @@ static void ride_ratings_update_state_0(RideRatingUpdateState& state) if (nextRide != nullptr && nextRide->status != RideStatus::Closed && !(nextRide->lifecycle_flags & RIDE_LIFECYCLE_FIXED_RATINGS)) { - state.State = RIDE_RATINGS_STATE_INITIALISE; + if (!RideRatingIsUpdatingRide(nextRideId)) + { + state.State = RIDE_RATINGS_STATE_INITIALISE; + } } state.CurrentRide = nextRideId; } From d3e377e9053919dcbba42ad2b34cc4668c3001d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Mon, 20 Mar 2023 16:50:08 +0200 Subject: [PATCH 3/8] Refactor and optimize ride rating calculation --- src/openrct2/ride/RideRatings.cpp | 102 ++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 25 deletions(-) diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index 6a1cb24496..3027406bc8 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -6,9 +6,6 @@ * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ - -#pragma optimize("", off) - #include "RideRatings.h" #include "../Cheats.h" @@ -81,6 +78,11 @@ struct ShelteredEights static RideRatingUpdateStates gRideRatingUpdateStates; +// Amount of updates allowed per updating state on the current tick. +// The total amount would be MaxRideRatingSubSteps * RideRatingMaxUpdateStates which +// would be currently 80, this is the worst case of sub-steps and may break out earlier. +static constexpr size_t MaxRideRatingUpdateSubSteps = 20; + static void ride_ratings_update_state(RideRatingUpdateState& state); static void ride_ratings_update_state_0(RideRatingUpdateState& state); static void ride_ratings_update_state_1(RideRatingUpdateState& state); @@ -92,7 +94,6 @@ static void ride_ratings_begin_proximity_loop(RideRatingUpdateState& state); static void RideRatingsCalculate(RideRatingUpdateState& state, Ride& ride); static void RideRatingsCalculateValue(Ride& ride); static void ride_ratings_score_close_proximity(RideRatingUpdateState& state, TileElement* inputTileElement); - static void ride_ratings_add(RatingTuple* rating, int32_t excitement, int32_t intensity, int32_t nausea); RideRatingUpdateStates& RideRatingGetUpdateStates() @@ -147,7 +148,14 @@ void RideRatingsUpdateAll() for (auto& updateState : gRideRatingUpdateStates) { - ride_ratings_update_state(updateState); + for (size_t i = 0; i < MaxRideRatingUpdateSubSteps; ++i) + { + ride_ratings_update_state(updateState); + + // We need to abort the loop if the state machine requested to find the next ride. + if (updateState.State == RIDE_RATINGS_STATE_FIND_NEXT_RIDE) + break; + } } } @@ -176,6 +184,42 @@ static void ride_ratings_update_state(RideRatingUpdateState& state) } } +static bool RideRatingIsUpdatingRide(RideId id) +{ + return std::any_of(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), [id](auto& state) { + return state.CurrentRide == id && state.State != RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + }); +} + +static bool ShouldSkipRatingCalculation(const Ride& ride) +{ + // Skip anything that isn't a real ride. + if (ride.GetClassification() != RideClassification::Ride) + { + return true; + } + + // Skip rides that are closed. + if (ride.status == RideStatus::Closed) + { + return true; + } + + // Skip anything that is already updating. + if (RideRatingIsUpdatingRide(ride.id)) + { + return true; + } + + // Skip rides that have a fixed rating. + if (ride.lifecycle_flags & RIDE_LIFECYCLE_FIXED_RATINGS) + { + return true; + } + + return false; +} + static RideId GetNextRideToUpdate(RideId currentRide) { auto rm = GetRideManager(); @@ -183,22 +227,33 @@ static RideId GetNextRideToUpdate(RideId currentRide) { return RideId::GetNull(); } - // Skip all empty ride ids - auto nextRide = std::next(rm.get(currentRide)); - // If at end, loop around - if (nextRide == rm.end()) + + auto it = rm.get(currentRide); + if (it == rm.end()) { - nextRide = rm.begin(); + // Start at the beginning, ride is missing. + it = rm.begin(); } - return (*nextRide).id; + else + { + it = std::next(it); + } + + // Filter out rides to avoid wasting a tick to find the next ride. + while (it != rm.end() && ShouldSkipRatingCalculation(*it)) + { + it++; + } + + // If we reached the end of the list we start over, + // in case the next ride doesn't pass the filter function it will + // look for the next matching ride in the next tick. + if (it == rm.end()) + it = rm.begin(); + + return (*it).id; } -static bool RideRatingIsUpdatingRide(RideId id) -{ - return std::any_of(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), [id](auto& state) { - return state.CurrentRide == id && state.State != RIDE_RATINGS_STATE_FIND_NEXT_RIDE; - }); -} /** * * rct2: 0x006B5A5C @@ -213,15 +268,12 @@ static void ride_ratings_update_state_0(RideRatingUpdateState& state) state.CurrentRide = {}; } - auto nextRideId = GetNextRideToUpdate(state.CurrentRide); - auto nextRide = GetRide(nextRideId); - if (nextRide != nullptr && nextRide->status != RideStatus::Closed - && !(nextRide->lifecycle_flags & RIDE_LIFECYCLE_FIXED_RATINGS)) + const auto nextRideId = GetNextRideToUpdate(state.CurrentRide); + const auto* nextRide = GetRide(nextRideId); + if (nextRide != nullptr && !ShouldSkipRatingCalculation(*nextRide)) { - if (!RideRatingIsUpdatingRide(nextRideId)) - { - state.State = RIDE_RATINGS_STATE_INITIALISE; - } + Guard::Assert(!RideRatingIsUpdatingRide(nextRideId)); + state.State = RIDE_RATINGS_STATE_INITIALISE; } state.CurrentRide = nextRideId; } From 1480495d2f35c4c57245136186b5db0df4bae865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Mon, 20 Mar 2023 17:03:36 +0200 Subject: [PATCH 4/8] Bump up park version --- src/openrct2/park/ParkFile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openrct2/park/ParkFile.h b/src/openrct2/park/ParkFile.h index 4c7e1bc042..8f8ea15236 100644 --- a/src/openrct2/park/ParkFile.h +++ b/src/openrct2/park/ParkFile.h @@ -9,7 +9,7 @@ struct ObjectRepositoryItem; namespace OpenRCT2 { // Current version that is saved. - constexpr uint32_t PARK_FILE_CURRENT_VERSION = 20; + constexpr uint32_t PARK_FILE_CURRENT_VERSION = 21; // The minimum version that is forwards compatible with the current version. constexpr uint32_t PARK_FILE_MIN_VERSION = 19; From 9610578621da416d31c6d80c9a16949e008336b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Mon, 20 Mar 2023 17:05:38 +0200 Subject: [PATCH 5/8] Bump up network version --- src/openrct2/network/NetworkBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 35a13838b1..3bf494f91b 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -43,7 +43,7 @@ // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "12" +#define NETWORK_STREAM_VERSION "13" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION From 1ae20ca3f2fef4be8c960a5113ea6acf70921d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:07:42 +0200 Subject: [PATCH 6/8] Apply review suggestions --- src/openrct2/park/ParkFile.h | 2 +- src/openrct2/ride/RideRatings.cpp | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/openrct2/park/ParkFile.h b/src/openrct2/park/ParkFile.h index 8f8ea15236..69fe23f2b5 100644 --- a/src/openrct2/park/ParkFile.h +++ b/src/openrct2/park/ParkFile.h @@ -12,7 +12,7 @@ namespace OpenRCT2 constexpr uint32_t PARK_FILE_CURRENT_VERSION = 21; // The minimum version that is forwards compatible with the current version. - constexpr uint32_t PARK_FILE_MIN_VERSION = 19; + constexpr uint32_t PARK_FILE_MIN_VERSION = 21; // The minimum version that is backwards compatible with the current version. // If this is increased beyond 0, uncomment the checks in ParkFile.cpp and Context.cpp! diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index 3027406bc8..4ccdeb90f3 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -6,6 +6,7 @@ * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ + #include "RideRatings.h" #include "../Cheats.h" @@ -109,12 +110,6 @@ void RideRatingResetUpdateStates() std::fill(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), nullState); } -RideRatingUpdateState& RideRatingGetUpdateState(size_t index) -{ - Guard::IndexInRange(index, gRideRatingUpdateStates); - return gRideRatingUpdateStates[index]; -} - /** * This is a small hack function to keep calling the ride rating processor until * the given ride's ratings have been calculated. Whatever is currently being From fe74395cf806e8b62e9bf106de3058428a86e0d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:24:40 +0200 Subject: [PATCH 7/8] Update changelog.txt --- distribution/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 343512f178..cb62155f52 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -30,6 +30,7 @@ - Improved: [#19463] Added ‘W’ and ‘Y’ with circumflex to sprite font (for Welsh). - Improved: [#19549] Enable large address awareness for 32 bit Windows builds allowing to use 4 GiB of virtual memory. - Improved: [#19668] Decreased the minimum map size from 13 to 3. +- Improved: [#19683] The delays for ride ratings to appear has been reduced drastically. - Change: [#19018] Renamed actions to fit the naming scheme. - Change: [#19091] [Plugin] Add game action information to callback arguments of custom actions. - Change: [#19233] Reduce lift speed minimum and maximum values for “Classic Wooden Coaster”. From 8a0b563316c819eeac037be3c3cd96a57904d4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 21 Mar 2023 18:55:20 +0200 Subject: [PATCH 8/8] Update replays --- CMakeLists.txt | 4 ++-- openrct2.proj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb9253ef5a..dc3fad1b08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,9 +76,9 @@ set(OPENMSX_VERSION "1.1.0") set(OPENMSX_URL "https://github.com/OpenRCT2/OpenMusic/releases/download/v${OPENMSX_VERSION}/openmusic.zip") set(OPENMSX_SHA1 "8f0cf6b2fd4727e91b0d4062e7f199a43d15e777") -set(REPLAYS_VERSION "0.0.75") +set(REPLAYS_VERSION "0.0.76") set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip") -set(REPLAYS_SHA1 "D1701450AE0FE84B144236243A925801B67D92ED") +set(REPLAYS_SHA1 "AE5808DE726D27F5311731677A20C96A8FF9101F") option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.") option(WITH_TESTS "Build tests") diff --git a/openrct2.proj b/openrct2.proj index b3202b4ded..b6515e9a5c 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -51,8 +51,8 @@ 64EF7E0B7785602C91AEC66F005C035B05A2133B https://github.com/OpenRCT2/OpenMusic/releases/download/v1.1.0/openmusic.zip 8f0cf6b2fd4727e91b0d4062e7f199a43d15e777 - https://github.com/OpenRCT2/replays/releases/download/v0.0.75/replays.zip - D1701450AE0FE84B144236243A925801B67D92ED + https://github.com/OpenRCT2/replays/releases/download/v0.0.76/replays.zip + AE5808DE726D27F5311731677A20C96A8FF9101F