Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multiplayer option to disable score multiplier #23510

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
786cfc4
Add room option to disable score multiplier
timiimit May 10, 2023
0883a1b
Add ScoreProcessor.TotalScoreWithoutScoreMultiplier
timiimit May 10, 2023
84a66ca
Add PlayerConfiguration.ShowScoreWithoutMultiplier
timiimit May 10, 2023
a6f85dd
Make GameplayScoreCounter show desired score
timiimit May 10, 2023
c2777ae
Handle updating correct score in SpectatorScoreProcessor
timiimit May 10, 2023
77fd78a
Make multi results screen display scores correctly
timiimit May 11, 2023
ef6f587
Handle spectating
timiimit May 11, 2023
04c8c02
Remove stupidity
timiimit May 11, 2023
7db9bc7
Undo test change
timiimit May 12, 2023
8563f2b
Remove the need for IsScoreMultiplierApplied
timiimit May 12, 2023
4c4c13a
Rename ScoreInfo.IsScoreDisplayedWithoutScoreMultiplier
timiimit May 12, 2023
14a4592
Fix code style
timiimit May 12, 2023
db882bd
Merge branch 'master' into multi-score-multiplier-option
timiimit May 12, 2023
5e0569d
Handle null check
timiimit May 12, 2023
e714e47
Stick to `NoScoreMultiplier` name
timiimit May 15, 2023
829b631
Add ScoreMultiplierCalculator
timiimit May 15, 2023
e132e2f
Add ScoreInfo.ScoreMultiplierCalculator
timiimit May 15, 2023
339e32f
Fix bug
timiimit May 15, 2023
cd4afae
Merge pull request #1 from timiimit/multi-score-multiplier-option-2
timiimit May 15, 2023
43a8c5f
Rename readonly field to uppercase
timiimit May 15, 2023
b81e206
Fix code style
timiimit May 15, 2023
6e4f445
Fix formatting
timiimit May 15, 2023
182509a
Change checkbox text
timiimit May 16, 2023
4e8b26a
Merge branch 'ppy:master' into multi-score-multiplier-option
timiimit May 16, 2023
bf2a8af
Add `NoScoreMultiplierPill`
timiimit May 16, 2023
8ab2c15
Remove unused namespace
timiimit May 16, 2023
a701238
Merge branch 'ppy:master' into multi-score-multiplier-option
timiimit May 21, 2023
56c38cb
Change checkbox text
timiimit May 21, 2023
c7da8bd
Merge branch 'master' into multi-score-multiplier-option
timiimit May 25, 2023
5f17caf
Use newly merged `OnlinePlayPill`
timiimit May 25, 2023
f28c335
Remove empty line
timiimit May 31, 2023
de6a51d
Merge branch 'master' into multi-score-multiplier-option
timiimit May 31, 2023
661a8b7
Merge branch 'master' into multi-score-multiplier-option-rework
timiimit Jun 9, 2023
2a0f086
Apply score multiplier before import
timiimit Jun 9, 2023
ace0f82
Unapply score multiplier in results screen
timiimit Jun 9, 2023
2b4c302
Merge branch 'master' into multi-score-multiplier-option
timiimit Jun 17, 2023
3b9480b
Fix code quality
timiimit Jun 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions osu.Game/Online/Multiplayer/MultiplayerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,9 @@ public Task LeaveRoom()
/// <param name="queueMode">The new queue mode, if any.</param>
/// <param name="autoStartDuration">The new auto-start countdown duration, if any.</param>
/// <param name="autoSkip">The new auto-skip setting.</param>
/// <param name="noScoreMultiplier">The new no score multiplier setting.</param>
public Task ChangeSettings(Optional<string> name = default, Optional<string> password = default, Optional<MatchType> matchType = default, Optional<QueueMode> queueMode = default,
Optional<TimeSpan> autoStartDuration = default, Optional<bool> autoSkip = default)
Optional<TimeSpan> autoStartDuration = default, Optional<bool> autoSkip = default, Optional<bool> noScoreMultiplier = default)
{
if (Room == null)
throw new InvalidOperationException("Must be joined to a match to change settings.");
Expand All @@ -285,7 +286,8 @@ public Task ChangeSettings(Optional<string> name = default, Optional<string> pas
MatchType = matchType.GetOr(Room.Settings.MatchType),
QueueMode = queueMode.GetOr(Room.Settings.QueueMode),
AutoStartDuration = autoStartDuration.GetOr(Room.Settings.AutoStartDuration),
AutoSkip = autoSkip.GetOr(Room.Settings.AutoSkip)
AutoSkip = autoSkip.GetOr(Room.Settings.AutoSkip),
NoScoreMultiplier = noScoreMultiplier.GetOr(Room.Settings.NoScoreMultiplier)
});
}

Expand Down Expand Up @@ -779,6 +781,7 @@ private void updateLocalRoomSettings(MultiplayerRoomSettings settings)
APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration;
APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
APIRoom.AutoSkip.Value = Room.Settings.AutoSkip;
APIRoom.NoScoreMultiplier.Value = Room.Settings.NoScoreMultiplier;

RoomUpdated?.Invoke();
}
Expand Down
9 changes: 7 additions & 2 deletions osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public class MultiplayerRoomSettings : IEquatable<MultiplayerRoomSettings>
[Key(6)]
public bool AutoSkip { get; set; }

[Key(7)]
public bool NoScoreMultiplier { get; set; }

[IgnoreMember]
public bool AutoStartEnabled => AutoStartDuration != TimeSpan.Zero;

Expand All @@ -46,7 +49,8 @@ public bool Equals(MultiplayerRoomSettings? other)
&& MatchType == other.MatchType
&& QueueMode == other.QueueMode
&& AutoStartDuration == other.AutoStartDuration
&& AutoSkip == other.AutoSkip;
&& AutoSkip == other.AutoSkip
&& NoScoreMultiplier == other.NoScoreMultiplier;
}

public override string ToString() => $"Name:{Name}"
Expand All @@ -55,6 +59,7 @@ public override string ToString() => $"Name:{Name}"
+ $" Item:{PlaylistItemId}"
+ $" Queue:{QueueMode}"
+ $" Start:{AutoStartDuration}"
+ $" AutoSkip:{AutoSkip}";
+ $" AutoSkip:{AutoSkip}"
+ $" NoScoreMultiplier:{NoScoreMultiplier}";
}
}
5 changes: 5 additions & 0 deletions osu.Game/Online/Rooms/Room.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ private int? maxAttempts
[JsonProperty("auto_skip")]
public readonly Bindable<bool> AutoSkip = new Bindable<bool>();

[Cached]
[JsonProperty("no_score_multiplier")]
public readonly Bindable<bool> NoScoreMultiplier = new Bindable<bool>();

public Room()
{
Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
Expand Down Expand Up @@ -200,6 +204,7 @@ public void CopyFrom(Room other)
PlaylistItemStats.Value = other.PlaylistItemStats.Value;
CurrentPlaylistItem.Value = other.CurrentPlaylistItem.Value;
AutoSkip.Value = other.AutoSkip.Value;
NoScoreMultiplier.Value = other.NoScoreMultiplier.Value;

if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
Status.Value = new RoomStatusEnded();
Expand Down
9 changes: 9 additions & 0 deletions osu.Game/Online/Spectator/SpectatorScoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
Expand Down Expand Up @@ -65,6 +66,9 @@ public IClock ReferenceClock
[Resolved]
private SpectatorClient spectatorClient { get; set; } = null!;

[Resolved]
private MultiplayerClient multiplayerClient { get; set; } = null!;

[Resolved]
private RulesetStore rulesetStore { get; set; } = null!;

Expand Down Expand Up @@ -117,6 +121,11 @@ private void onSpectatorStatesChanged(object? sender, NotifyDictionaryChangedEve
Ruleset = rulesetInfo,
Mods = userState.Mods.Select(m => m.ToMod(ruleset)).ToArray()
};

if (multiplayerClient.Room?.Settings.NoScoreMultiplier == true)
{
scoreInfo.ScoreMultiplierCalculator = _ => 1;
}
}

private void onNewFrames(int incomingUserId, FrameDataBundle bundle)
Expand Down
28 changes: 20 additions & 8 deletions osu.Game/Rulesets/Scoring/ScoreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,18 @@ public Dictionary<HitResult, int> MaximumStatistics
private readonly List<HitEvent> hitEvents = new List<HitEvent>();
private HitObject? lastHitObject;

private Func<ScoreInfo, double> scoreMultiplierCalculator;

public Func<ScoreInfo, double> ScoreMultiplierCalculator
{
get => scoreMultiplierCalculator;
set
{
scoreMultiplierCalculator = value;
updateScoreFull();
}
}

public ScoreProcessor(Ruleset ruleset)
{
Ruleset = ruleset;
Expand All @@ -183,15 +195,9 @@ public ScoreProcessor(Ruleset ruleset)
Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
};

Mods.ValueChanged += mods =>
{
scoreMultiplier = 1;

foreach (var m in mods.NewValue)
scoreMultiplier *= m.ScoreMultiplier;
scoreMultiplierCalculator = ScoreInfo.DEFAULT_SCORE_MULTIPLIER_CALCULATOR;

updateScore();
};
Mods.ValueChanged += _ => updateScoreFull();
}

public override void ApplyBeatmap(IBeatmap beatmap)
Expand Down Expand Up @@ -303,6 +309,12 @@ protected virtual void RemoveScoreChange(JudgementResult result)
{
}

private void updateScoreFull()
{
scoreMultiplier = ScoreMultiplierCalculator(new ScoreInfo { Mods = Mods.Value.ToArray() });
updateScore();
}

private void updateScore()
{
Accuracy.Value = currentMaximumBaseScore > 0 ? currentBaseScore / currentMaximumBaseScore : 1;
Expand Down
15 changes: 15 additions & 0 deletions osu.Game/Scoring/ScoreInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ public ScoreInfo DeepClone()
/// </summary>
public bool IsLegacyScore { get; set; }

[Ignored]
public Func<ScoreInfo, double> ScoreMultiplierCalculator { get; set; } = DEFAULT_SCORE_MULTIPLIER_CALCULATOR;

public static readonly Func<ScoreInfo, double> DEFAULT_SCORE_MULTIPLIER_CALCULATOR = s =>
{
double scoreMultiplier = 1;

foreach (var mod in s.Mods)
{
scoreMultiplier *= mod.ScoreMultiplier;
}

return scoreMultiplier;
};

private Dictionary<HitResult, int>? statistics;

[Ignored]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ protected virtual IEnumerable<Drawable> CreateBottomDetails()
{
new MatchTypePill(),
new QueueModePill(),
new NoScoreMultiplierPill()
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable

using osu.Framework.Bindables;

namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public partial class NoScoreMultiplierPill : OnlinePlayPill
{
protected override void LoadComplete()
{
base.LoadComplete();

TextFlow.Text = "No mod multipliers";
NoScoreMultiplier.BindValueChanged(onNoScoreMultiplierChanged, true);
}

private void onNoScoreMultiplierChanged(ValueChangedEvent<bool> mode)
{
if (mode.NewValue)
Show();
else
Hide();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ protected partial class MatchSettings : OnlinePlayComposite
public OsuEnumDropdown<QueueMode> QueueModeDropdown = null!;
public OsuTextBox PasswordTextBox = null!;
public OsuCheckbox AutoSkipCheckbox = null!;
public OsuCheckbox NoScoreMultiplierCheckbox = null!;
public RoundedButton ApplyButton = null!;

public OsuSpriteText ErrorText = null!;
Expand Down Expand Up @@ -247,9 +248,23 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours)
},
new Section("Other")
{
Child = AutoSkipCheckbox = new OsuCheckbox
Child = new FillFlowContainer
{
LabelText = "Automatically skip the beatmap intro"
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(5),
Children = new[]
{
AutoSkipCheckbox = new OsuCheckbox
{
LabelText = "Automatically skip the beatmap intro"
},
NoScoreMultiplierCheckbox = new OsuCheckbox
{
LabelText = "No mod multipliers"
}
}
}
}
}
Expand Down Expand Up @@ -351,6 +366,7 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours)
QueueMode.BindValueChanged(mode => QueueModeDropdown.Current.Value = mode.NewValue, true);
AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true);
AutoSkip.BindValueChanged(autoSkip => AutoSkipCheckbox.Current.Value = autoSkip.NewValue, true);
NoScoreMultiplier.BindValueChanged(noMultiplier => NoScoreMultiplierCheckbox.Current.Value = noMultiplier.NewValue, true);

operationInProgress.BindTo(ongoingOperationTracker.InProgress);
operationInProgress.BindValueChanged(v =>
Expand Down Expand Up @@ -399,7 +415,8 @@ private void apply()
matchType: TypePicker.Current.Value,
queueMode: QueueModeDropdown.Current.Value,
autoStartDuration: autoStartDuration,
autoSkip: AutoSkipCheckbox.Current.Value)
autoSkip: AutoSkipCheckbox.Current.Value,
noScoreMultiplier: NoScoreMultiplierCheckbox.Current.Value)
.ContinueWith(t => Schedule(() =>
{
if (t.IsCompletedSuccessfully)
Expand All @@ -416,6 +433,7 @@ private void apply()
room.QueueMode.Value = QueueModeDropdown.Current.Value;
room.AutoStartDuration.Value = autoStartDuration;
room.AutoSkip.Value = AutoSkipCheckbox.Current.Value;
room.NoScoreMultiplier.Value = NoScoreMultiplierCheckbox.Current.Value;

if (int.TryParse(MaxParticipantsField.Text, out int max))
room.MaxParticipants.Value = max;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public MultiplayerPlayer(Room room, PlaylistItem playlistItem, MultiplayerRoomUs
AllowRestart = false,
AllowSkipping = room.AutoSkip.Value,
AutomaticallySkipIntro = room.AutoSkip.Value,
ScoreMultiplierCalculator = (room.NoScoreMultiplier.Value ? _ => 1 : ScoreInfo.DEFAULT_SCORE_MULTIPLIER_CALCULATOR),
AlwaysShowLeaderboard = true,
})
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ public partial class MultiSpectatorPlayer : SpectatorPlayer
/// <param name="score">The score containing the player's replay.</param>
/// <param name="spectatorPlayerClock">The clock controlling the gameplay running state.</param>
public MultiSpectatorPlayer(Score score, SpectatorPlayerClock spectatorPlayerClock)
: base(score, new PlayerConfiguration { AllowUserInteraction = false })
: base(score, new PlayerConfiguration
{
AllowUserInteraction = false,
ScoreMultiplierCalculator = score.ScoreInfo.ScoreMultiplierCalculator,
})
{
this.spectatorPlayerClock = spectatorPlayerClock;
}
Expand Down
3 changes: 3 additions & 0 deletions osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ public partial class OnlinePlayComposite : CompositeDrawable
[Resolved(typeof(Room))]
protected Bindable<bool> AutoSkip { get; private set; }

[Resolved(typeof(Room))]
protected Bindable<bool> NoScoreMultiplier { get; private set; }

[Resolved(CanBeNull = true)]
private IBindable<PlaylistItem> subScreenSelectedItem { get; set; }

Expand Down
17 changes: 16 additions & 1 deletion osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Online.Multiplayer;

namespace osu.Game.Screens.OnlinePlay.Playlists
{
Expand All @@ -41,6 +42,9 @@ public partial class PlaylistsResultsScreen : ResultsScreen
[Resolved]
private RulesetStore rulesets { get; set; }

[Resolved]
private MultiplayerClient multiplayerClient { get; set; } = null!;

public PlaylistsResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true)
: base(score, allowRetry, allowWatchingReplay)
{
Expand Down Expand Up @@ -182,7 +186,18 @@ private APIRequest createIndexRequest(Action<IEnumerable<ScoreInfo>> scoresCallb
/// <param name="pivot">An optional pivot around which the scores were retrieved.</param>
private void performSuccessCallback([NotNull] Action<IEnumerable<ScoreInfo>> callback, [NotNull] List<MultiplayerScore> scores, [CanBeNull] MultiplayerScores pivot = null) => Schedule(() =>
{
var scoreInfos = scores.Select(s => s.CreateScoreInfo(scoreManager, rulesets, playlistItem, Beatmap.Value.BeatmapInfo)).OrderByTotalScore().ToArray();
var scoreInfos = scores.Select(s => s.CreateScoreInfo(scoreManager, rulesets, playlistItem, Beatmap.Value.BeatmapInfo)).ToArray();

if (multiplayerClient?.Room?.Settings.NoScoreMultiplier == true)
{
foreach (var scoreInfo in scoreInfos)
{
double scoreMultiplier = scoreInfo.ScoreMultiplierCalculator(scoreInfo);
scoreInfo.TotalScore = (long)Math.Round(scoreInfo.TotalScore / scoreMultiplier);
}
}

scoreInfos.OrderByTotalScore();

// Select a score if we don't already have one selected.
// Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll).
Expand Down
1 change: 1 addition & 0 deletions osu.Game/Screens/Play/GameplayState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList<Mod>? mods
};
Mods = mods ?? Array.Empty<Mod>();
ScoreProcessor = scoreProcessor ?? ruleset.CreateScoreProcessor();
ScoreProcessor.ScoreMultiplierCalculator = Score.ScoreInfo.ScoreMultiplierCalculator;
}

/// <summary>
Expand Down
14 changes: 14 additions & 0 deletions osu.Game/Screens/Play/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game,
dependencies.CacheAs(DrawableRuleset);

ScoreProcessor = ruleset.CreateScoreProcessor();
ScoreProcessor.ScoreMultiplierCalculator = Configuration.ScoreMultiplierCalculator;
ScoreProcessor.Mods.Value = gameplayMods;
ScoreProcessor.ApplyBeatmap(playableBeatmap);

Expand All @@ -248,6 +249,7 @@ private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game,
Score.ScoreInfo.BeatmapHash = Beatmap.Value.BeatmapInfo.Hash;
Score.ScoreInfo.Ruleset = ruleset.RulesetInfo;
Score.ScoreInfo.Mods = gameplayMods;
Score.ScoreInfo.ScoreMultiplierCalculator = Configuration.ScoreMultiplierCalculator;

dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score, ScoreProcessor));

Expand Down Expand Up @@ -817,10 +819,20 @@ private Task<ScoreInfo> prepareAndImportScoreAsync(bool forceImport = false)
if (!canShowResults && !forceImport)
return Task.FromResult<ScoreInfo>(null);

// If non-default score multiplier calculator was used we want to recalculate actual score
// with default score multiplier which should always be used when importing or submitting scores.
var gameplayScoreMultiplierCalculator = ScoreProcessor.ScoreMultiplierCalculator;
ScoreProcessor.ScoreMultiplierCalculator = ScoreInfo.DEFAULT_SCORE_MULTIPLIER_CALCULATOR;
long actualTotalScore = ScoreProcessor.TotalScore.Value;
ScoreProcessor.ScoreMultiplierCalculator = gameplayScoreMultiplierCalculator;

return prepareScoreForDisplayTask = Task.Run(async () =>
{
var scoreCopy = Score.DeepClone();

long gameplayScore = scoreCopy.ScoreInfo.TotalScore;
scoreCopy.ScoreInfo.TotalScore = actualTotalScore;

try
{
await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false);
Expand All @@ -839,6 +851,8 @@ private Task<ScoreInfo> prepareAndImportScoreAsync(bool forceImport = false)
Logger.Error(ex, @"Score import failed!");
}

scoreCopy.ScoreInfo.TotalScore = gameplayScore;

return scoreCopy.ScoreInfo;
});
}
Expand Down
Loading