From a06b7dcf4c41fc11886120d62d2090dbd0626223 Mon Sep 17 00:00:00 2001 From: thomeval Date: Mon, 21 Oct 2024 16:52:25 +0000 Subject: [PATCH] Prevent invalid high scores from being saved (#898) * Prevent saving band high scores when there are bots present, or when Song Speed is less than 100% * Refactor score recording in GameManager. Fix wrong condition preventing solo scores from saving. * Added "Band score not saved" message to ScoreScreen. * Rename ShouldRecordSoloScore to IsSoloScoreValid Refactor IsBandScoreValid to use IsSoloScoreValid * Fixed several issues identified from PR feedback --- Assets/Scenes/ScoreScene.unity | 151 ++++++++++++++++++ Assets/Script/Gameplay/GameManager.cs | 57 ++++--- .../Menu/ScoreScreen/ScoreScreenMenu.cs | 15 +- Assets/Script/Scores/ScoreContainer.cs | 28 +++- Assets/StreamingAssets/lang/en-US.json | 3 + 5 files changed, 227 insertions(+), 27 deletions(-) diff --git a/Assets/Scenes/ScoreScene.unity b/Assets/Scenes/ScoreScene.unity index 2371b679e..bd0444fd0 100644 --- a/Assets/Scenes/ScoreScene.unity +++ b/Assets/Scenes/ScoreScene.unity @@ -125,6 +125,156 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &126113574 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 126113577} + - component: {fileID: 126113576} + - component: {fileID: 126113575} + - component: {fileID: 126113578} + m_Layer: 5 + m_Name: Band Score Not Saved Message + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &126113575 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 126113574} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: (Band Score not saved) + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: c45bdba5d22921a4a961427fb9eac360, type: 2} + m_sharedMaterial: {fileID: 241437652069387127, guid: c45bdba5d22921a4a961427fb9eac360, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4286940159 + m_fontColor: {r: 1, g: 0.514151, b: 0.5232028, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 24 + m_fontSizeBase: 24 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 4 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: -2.75, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &126113576 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 126113574} + m_CullTransparentMesh: 1 +--- !u!224 &126113577 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 126113574} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1025282833} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -250, y: 35} + m_SizeDelta: {x: 400, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &126113578 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 126113574} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7666ce39360a4330a733067d9e66b929, type: 3} + m_Name: + m_EditorClassIdentifier: + _localizationKey: Menu.ScoreScreen.BandScoreNotSaved --- !u!1 &213421497 GameObject: m_ObjectHideFlags: 0 @@ -1714,6 +1864,7 @@ MonoBehaviour: _artistName: {fileID: 924495893} _bandStarView: {fileID: 1284876700} _bandScore: {fileID: 1305557732} + _bandScoreNotSavedMessage: {fileID: 126113575} _horizontalScrollBar: {fileID: 1490960382} _guitarCardPrefab: {fileID: 6705571759713988567, guid: 462dbfc1d4280484ebd8081e78f8f4ca, type: 3} diff --git a/Assets/Script/Gameplay/GameManager.cs b/Assets/Script/Gameplay/GameManager.cs index 27c296a2b..79bc4dd1a 100644 --- a/Assets/Script/Gameplay/GameManager.cs +++ b/Assets/Script/Gameplay/GameManager.cs @@ -379,7 +379,10 @@ public bool OverrideResume() { bool resumed = _songRunner.OverrideResume(); if (resumed) + { ResumeCore(); + } + return resumed; } @@ -434,15 +437,32 @@ private bool EndSong() ReplayInfo = replayInfo, }; + RecordScores(replayInfo); + + // Go to the score screen + GlobalVariables.Instance.LoadScene(SceneIndex.Score); + return true; + } + + private void RecordScores(ReplayInfo replayInfo) + { + if (!ScoreContainer.IsBandScoreValid(SongSpeed)) + { + return; + } // Get all of the individual player score entries var playerEntries = new List(); + foreach (var player in _players) { var profile = player.Player.Profile; - // Skip bots - if (player.Player.Profile.IsBot) continue; + // Skip bots and anyone that's obviously cheating. + if (!ScoreContainer.IsSoloScoreValid(SongSpeed, player.Player)) + { + continue; + } playerEntries.Add(new PlayerScoreRecord { @@ -464,31 +484,24 @@ private bool EndSong() }); } - // Record the score into the database (if there's at least 1 non-bot player) - if (playerEntries.Count > 0) + // Record the score into the database (but only if there are no bots, and Song Speed is at least 100%) + ScoreContainer.RecordScore(new GameRecord { - ScoreContainer.RecordScore(new GameRecord - { - Date = DateTime.Now, + Date = DateTime.Now, - SongChecksum = Song.Hash.HashBytes, - SongName = Song.Name, - SongArtist = Song.Artist, - SongCharter = Song.Charter, + SongChecksum = Song.Hash.HashBytes, + SongName = Song.Name, + SongArtist = Song.Artist, + SongCharter = Song.Charter, - ReplayFileName = replayInfo?.ReplayName, - ReplayChecksum = replayInfo?.ReplayChecksum.HashBytes, + ReplayFileName = replayInfo?.ReplayName, + ReplayChecksum = replayInfo?.ReplayChecksum.HashBytes, - BandScore = BandScore, - BandStars = StarAmountHelper.GetStarsFromInt((int) BandStars), - - SongSpeed = SongSpeed - }, playerEntries); - } + BandScore = BandScore, + BandStars = StarAmountHelper.GetStarsFromInt((int) BandStars), - // Go to the score screen - GlobalVariables.Instance.LoadScene(SceneIndex.Score); - return true; + SongSpeed = SongSpeed + }, playerEntries); } public void ForceQuitSong() diff --git a/Assets/Script/Menu/ScoreScreen/ScoreScreenMenu.cs b/Assets/Script/Menu/ScoreScreen/ScoreScreenMenu.cs index 76769e46d..90fda0014 100644 --- a/Assets/Script/Menu/ScoreScreen/ScoreScreenMenu.cs +++ b/Assets/Script/Menu/ScoreScreen/ScoreScreenMenu.cs @@ -1,4 +1,5 @@ -using TMPro; +using System.Linq; +using TMPro; using UnityEngine; using UnityEngine.UI; using YARG.Core; @@ -14,7 +15,7 @@ using YARG.Localization; using YARG.Menu.Navigation; using YARG.Menu.Persistent; -using YARG.Replays; +using YARG.Scores; using YARG.Song; namespace YARG.Menu.ScoreScreen @@ -34,6 +35,8 @@ public class ScoreScreenMenu : MonoBehaviour [SerializeField] private TextMeshProUGUI _bandScore; [SerializeField] + private TextMeshProUGUI _bandScoreNotSavedMessage; + [SerializeField] private Scrollbar _horizontalScrollBar; [Space] @@ -85,6 +88,7 @@ private void OnEnable() // Set text _songTitle.text = song.Name; _artistName.text = song.Artist; + _bandScoreNotSavedMessage.gameObject.SetActive(!ScoreContainer.IsBandScoreValid(PersistentState.Default.SongSpeed)); // Set speed text (if not at 100% speed) if (!Mathf.Approximately(GlobalVariables.State.SongSpeed, 1f)) @@ -172,7 +176,12 @@ private bool AnalyzeReplay(SongEntry songEntry, ReplayInfo? replayEntry) _analyzingReplay = false; return true; } - + if (GlobalVariables.State.ScoreScreenStats.Value.PlayerScores.All(e => e.Player.Profile.IsBot)) + { + YargLogger.LogInfo("No human players in ReplayEntry."); + _analyzingReplay = false; + return true; + } if (replayEntry == null) { YargLogger.LogError("ReplayEntry is null"); diff --git a/Assets/Script/Scores/ScoreContainer.cs b/Assets/Script/Scores/ScoreContainer.cs index 490badf61..02b86399f 100644 --- a/Assets/Script/Scores/ScoreContainer.cs +++ b/Assets/Script/Scores/ScoreContainer.cs @@ -2,9 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Cysharp.Threading.Tasks; using SQLite; -using UnityEngine; using YARG.Core; using YARG.Core.Logging; using YARG.Core.Song; @@ -58,6 +56,32 @@ private static void InitDatabase() _db.CreateTable(); } + public static bool IsBandScoreValid(float songSpeed) + { + if (!PlayerContainer.Players.Any()) + { + return false; + } + + // If any player is disqualified from a valid Solo Score, this should disqualify the Band Score as well. + if (PlayerContainer.Players.Any(e => !e.SittingOut && !IsSoloScoreValid(songSpeed, e))) + { + return false; + } + + return true; + } + + public static bool IsSoloScoreValid(float songSpeed, YargPlayer player) + { + if (songSpeed < 1.0f || player.Profile.IsBot) + { + return false; + } + + return true; + } + public static void RecordScore(GameRecord gameRecord, List playerEntries) { try diff --git a/Assets/StreamingAssets/lang/en-US.json b/Assets/StreamingAssets/lang/en-US.json index 849593a56..54f1c2c4e 100644 --- a/Assets/StreamingAssets/lang/en-US.json +++ b/Assets/StreamingAssets/lang/en-US.json @@ -287,6 +287,9 @@ "Bots" : "Bots", "Players" : "Players" }, + "ScoreScreen" : { + "BandScoreNotSaved" : "(Band score not saved)" + }, "Settings": { "NoFolder": "No Folder", "SearchHeader": {