Merge pull request #19683 from ZehMatt/parallel-ride-ratings

Improve ride rating calculation
This commit is contained in:
Matthias Moninger 2023-03-22 22:25:06 +02:00 committed by GitHub
commit e8ec7b6c89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 25 deletions

View File

@ -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")

View File

@ -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”.

View File

@ -51,8 +51,8 @@
<OpenSFXSha1>64EF7E0B7785602C91AEC66F005C035B05A2133B</OpenSFXSha1>
<OpenMSXUrl>https://github.com/OpenRCT2/OpenMusic/releases/download/v1.1.0/openmusic.zip</OpenMSXUrl>
<OpenMSXSha1>8f0cf6b2fd4727e91b0d4062e7f199a43d15e777</OpenMSXSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.75/replays.zip</ReplaysUrl>
<ReplaysSha1>D1701450AE0FE84B144236243A925801B67D92ED</ReplaysSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.76/replays.zip</ReplaysUrl>
<ReplaysSha1>AE5808DE726D27F5311731677A20C96A8FF9101F</ReplaysSha1>
</PropertyGroup>
<ItemGroup>

View File

@ -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

View File

@ -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)
{

View File

@ -9,10 +9,10 @@ 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;
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!

View File

@ -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 };

View File

@ -77,7 +77,12 @@ struct ShelteredEights
uint8_t TotalShelteredEighths;
};
RideRatingUpdateState gRideRatingUpdateState;
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);
@ -90,9 +95,21 @@ 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()
{
return gRideRatingUpdateStates;
}
void RideRatingResetUpdateStates()
{
RideRatingUpdateState nullState{};
nullState.State = RIDE_RATINGS_STATE_FIND_NEXT_RIDE;
std::fill(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), nullState);
}
/**
* 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
@ -124,9 +141,17 @@ 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(gRideRatingUpdateState);
for (auto& updateState : gRideRatingUpdateStates)
{
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;
}
}
}
static void ride_ratings_update_state(RideRatingUpdateState& state)
@ -154,6 +179,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();
@ -161,14 +222,31 @@ 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;
}
/**
@ -185,11 +263,11 @@ 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))
{
Guard::Assert(!RideRatingIsUpdatingRide(nextRideId));
state.State = RIDE_RATINGS_STATE_INITIALISE;
}
state.CurrentRide = nextRideId;

View File

@ -54,7 +54,12 @@ struct RideRatingUpdateState
uint16_t StationFlags;
};
extern RideRatingUpdateState gRideRatingUpdateState;
static constexpr size_t RideRatingMaxUpdateStates = 4;
using RideRatingUpdateStates = std::array<RideRatingUpdateState, RideRatingMaxUpdateStates>;
RideRatingUpdateStates& RideRatingGetUpdateStates();
void RideRatingResetUpdateStates();
void RideRatingsUpdateRide(const Ride& ride);
void RideRatingsUpdateAll();