@@ -53,11 +53,13 @@ public GameScore SubmitScore(ISerializedScore score, GameUser user, GameLevel le
5353 && currentFirstPlace . Score < score . Score
5454 && currentFirstPlace . PublisherId != user . UserId ;
5555
56- this . Write ( ( ) =>
56+ this . WriteEnsuringStatistics ( level , ( ) =>
5757 {
5858 this . GameScores . Add ( newScore ) ;
59+ level . Statistics ! . CompletionCount ++ ;
5960 } ) ;
6061
62+ this . RecalculateScoreStatistics ( level . LevelId , score . ScoreType ) ;
6163 this . CreateLevelScoreEvent ( user , newScore ) ;
6264
6365 // Notify the last #1 users that they've been overtaken
@@ -101,12 +103,12 @@ public DatabaseScoreList GetTopScoresForLevel(GameLevel level, int count, int sk
101103 scores = scores . Where ( s => s . ScoreType == scoreType ) ;
102104
103105 if ( ! showDuplicates )
104- scores = scores . DistinctBy ( s => s . PublisherId ) ;
106+ scores = scores . Where ( s => s . Rank != 0 ) ;
105107
106108 if ( minAge != null )
107109 scores = scores . Where ( s => s . ScoreSubmitted >= minAge ) ;
108110
109- return new ( scores . ToArray ( ) . Select ( ( s , i ) => new ScoreWithRank ( s , i + 1 ) ) , skip , count , user ) ;
111+ return new ( scores . ToArray ( ) . Select ( s => new ScoreWithRank ( s , s . Rank ) ) , skip , count , user ) ;
110112 }
111113
112114 public DatabaseScoreList GetRankedScoresAroundScore ( GameScore score , int count , GameUser ? user = null )
@@ -118,13 +120,12 @@ public DatabaseScoreList GetRankedScoresAroundScore(GameScore score, int count,
118120 List < GameScore > scores = this . GameScoresIncluded
119121 . Where ( s => s . ScoreType == score . ScoreType && s . LevelId == score . LevelId )
120122 . OrderByDescending ( s => s . Score )
121- . ToArray ( )
122- . DistinctBy ( s => s . PublisherId )
123+ . Where ( s => s . Rank != 0 )
123124 . ToList ( ) ;
124125
125126 return new
126127 (
127- scores . Select ( ( s , i ) => new ScoreWithRank ( s , i + 1 ) ) ,
128+ scores . Select ( s => new ScoreWithRank ( s , s . Rank ) ) ,
128129 Math . Min ( scores . Count , scores . IndexOf ( score ) - count / 2 ) , // center user's score around other scores
129130 count , user
130131 ) ;
@@ -140,18 +141,16 @@ public DatabaseScoreList GetLevelTopScoresByFriends(GameUser user, GameLevel lev
140141 IEnumerable < GameScore > scores = this . GameScoresIncluded
141142 . Where ( s => s . LevelId == level . LevelId )
142143 . OrderByDescending ( s => s . Score )
143- . ToArray ( )
144- . DistinctBy ( s => s . PublisherId )
145- //TODO: THIS CALL IS EXTREMELY INEFFECIENT!!! once we are in postgres land, figure out a way to do this effeciently
146- . Where ( s => s . PlayerIds . Any ( p => mutuals . Contains ( p ) ) ) ;
144+ . Where ( s => s . Rank != 0 )
145+ . Where ( s => mutuals . Contains ( s . PublisherId ) ) ;
147146
148147 if ( scoreType != 0 )
149148 scores = scores . Where ( s => s . ScoreType == scoreType ) ;
150149
151150 if ( minAge != null )
152151 scores = scores . Where ( s => s . ScoreSubmitted >= minAge ) ;
153152
154- return new ( scores . Select ( ( s , i ) => new ScoreWithRank ( s , i + 1 ) ) , skip , count , user ) ;
153+ return new ( scores . ToArray ( ) . Select ( s => new ScoreWithRank ( s , s . Rank ) ) , skip , count , user ) ;
155154 }
156155
157156 [ Pure ]
@@ -178,35 +177,35 @@ public void DeleteScore(GameScore score)
178177 IQueryable < Event > scoreEvents = this . Events
179178 . Where ( e => e . StoredDataType == EventDataType . Score && e . StoredObjectId == score . ScoreId ) ;
180179
181- this . Write ( ( ) =>
182- {
183- this . Events . RemoveRange ( scoreEvents ) ;
184- this . GameScores . Remove ( score ) ;
185- } ) ;
180+ this . Events . RemoveRange ( scoreEvents ) ;
181+ this . GameScores . Remove ( score ) ;
182+ this . SaveChanges ( ) ;
183+
184+ // Only recalculate ranks if the score was a high-score.
185+ // Also, separate write transaction because otherwise recalculation would still include the unwanted scores.
186+ if ( score . Rank != 0 )
187+ this . RecalculateScoreStatistics ( score . LevelId , score . ScoreType , true ) ;
186188 }
187189
188190 public void DeleteScoresSetByUser ( GameUser user )
189191 {
192+ // Find out which leaderboards we will have to recalculate the ranks of.
193+ // Only need high-scores for this, as the other, overtaken scores should not affect ranking at all,
194+ // and additionally, this way we will always have not more than 1 score per level/score type,
195+ // avoiding recalculation of the same leaderboards multiple times.
190196 IEnumerable < GameScore > scores = this . GameScores
191- // FIXME: Realm (ahem, I mean the atlas device sdk *rolls eyes*) is a fucking joke.
192- // Realm doesn't support .Contains on IList<T>. Yes, really.
193- // This means we are forced to iterate over EVERY SCORE.
194- // I can't wait for Postgres.
195- . AsEnumerableIfRealm ( )
196- . Where ( s => s . PlayerIdsRaw . Contains ( user . UserId . ToString ( ) ) )
197- . ToArrayIfPostgres ( ) ;
198-
199- this . Write ( ( ) =>
197+ . Where ( s => s . PublisherId == user . UserId && s . Rank != 0 )
198+ . ToArray ( ) ;
199+
200+ this . GameScores . RemoveRange ( s => s . PublisherId == user . UserId ) ;
201+ this . Events . RemoveRange ( s => s . StoredDataType == EventDataType . Score && s . UserId == user . UserId ) ;
202+ this . SaveChanges ( ) ;
203+
204+ foreach ( GameScore score in scores )
200205 {
201- foreach ( GameScore score in scores )
202- {
203- IQueryable < Event > scoreEvents = this . Events
204- . Where ( e => e . StoredDataType == EventDataType . Score && e . StoredObjectId == score . ScoreId ) ;
205-
206- this . Events . RemoveRange ( scoreEvents ) ;
207- this . GameScores . Remove ( score ) ;
208- }
209- } ) ;
206+ this . RecalculateScoreStatistics ( score . LevelId , score . ScoreType , false ) ;
207+ }
208+ this . SaveChanges ( ) ;
210209 }
211210
212211 public IEnumerable < GameUser > GetPlayersFromScore ( GameScore score )
0 commit comments