1
1
#nullable enable
2
- using System . Diagnostics . CodeAnalysis ;
3
2
using LBPUnion . ProjectLighthouse . Database ;
4
3
using LBPUnion . ProjectLighthouse . Extensions ;
5
4
using LBPUnion . ProjectLighthouse . Helpers ;
@@ -30,26 +29,14 @@ public ScoreController(DatabaseContext database)
30
29
this . database = database ;
31
30
}
32
31
33
- private string [ ] getFriendUsernames ( int userId , string username )
32
+ private static int [ ] GetFriendIds ( int userId )
34
33
{
35
34
UserFriendData ? store = UserFriendStore . GetUserFriendData ( userId ) ;
36
- if ( store == null ) return new [ ] { username , } ;
35
+ List < int > ? friendIds = store ? . FriendIds ;
36
+ friendIds ??= new List < int > ( ) ;
37
+ friendIds . Add ( userId ) ;
37
38
38
- List < string > friendNames = new ( )
39
- {
40
- username ,
41
- } ;
42
-
43
- // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
44
- foreach ( int friendId in store . FriendIds )
45
- {
46
- string ? friendUsername = this . database . Users . Where ( u => u . UserId == friendId )
47
- . Select ( u => u . Username )
48
- . FirstOrDefault ( ) ;
49
- if ( friendUsername != null ) friendNames . Add ( friendUsername ) ;
50
- }
51
-
52
- return friendNames . ToArray ( ) ;
39
+ return friendIds . Distinct ( ) . ToArray ( ) ;
53
40
}
54
41
55
42
[ HttpPost ( "scoreboard/{slotType}/{id:int}" ) ]
@@ -144,41 +131,41 @@ public async Task<IActionResult> SubmitScore(string slotType, int id, int childI
144
131
145
132
await this . database . SaveChangesAsync ( ) ;
146
133
147
- string playerIdCollection = string . Join ( ',' , score . PlayerIds ) ;
148
-
149
134
ScoreEntity ? existingScore = await this . database . Scores . Where ( s => s . SlotId == slot . SlotId )
150
135
. Where ( s => s . ChildSlotId == 0 || s . ChildSlotId == childId )
151
- . Where ( s => s . PlayerIdCollection == playerIdCollection )
136
+ . Where ( s => s . UserId == token . UserId )
152
137
. Where ( s => s . Type == score . Type )
153
138
. FirstOrDefaultAsync ( ) ;
154
139
if ( existingScore != null )
155
140
{
156
141
existingScore . Points = Math . Max ( existingScore . Points , score . Points ) ;
142
+ existingScore . Timestamp = TimeHelper . TimestampMillis ;
157
143
}
158
144
else
159
145
{
160
146
ScoreEntity playerScore = new ( )
161
147
{
162
- PlayerIdCollection = playerIdCollection ,
148
+ UserId = token . UserId ,
163
149
Type = score . Type ,
164
150
Points = score . Points ,
165
151
SlotId = slotId ,
166
152
ChildSlotId = childId ,
153
+ Timestamp = TimeHelper . TimestampMillis ,
167
154
} ;
168
155
this . database . Scores . Add ( playerScore ) ;
169
156
}
170
157
171
158
await this . database . SaveChangesAsync ( ) ;
172
159
173
- return this . Ok ( this . getScores ( new LeaderboardOptions
160
+ return this . Ok ( await this . GetScores ( new LeaderboardOptions
174
161
{
175
162
RootName = "scoreboardSegment" ,
176
163
PageSize = 5 ,
177
164
PageStart = - 1 ,
178
165
SlotId = slotId ,
179
166
ChildSlotId = childId ,
180
167
ScoreType = score . Type ,
181
- TargetUsername = username ,
168
+ TargetUser = token . UserId ,
182
169
TargetPlayerIds = null ,
183
170
} ) ) ;
184
171
}
@@ -189,8 +176,6 @@ public async Task<IActionResult> Lbp1Leaderboards(string slotType, int id)
189
176
{
190
177
GameTokenEntity token = this . GetToken ( ) ;
191
178
192
- string username = await this . database . UsernameFromGameToken ( token ) ;
193
-
194
179
if ( slotType == "developer" ) id = await SlotHelper . GetPlaceholderSlotId ( this . database , id , SlotType . Developer ) ;
195
180
196
181
LeaderboardOptions options = new ( )
@@ -199,7 +184,7 @@ public async Task<IActionResult> Lbp1Leaderboards(string slotType, int id)
199
184
PageStart = 1 ,
200
185
ScoreType = - 1 ,
201
186
SlotId = id ,
202
- TargetUsername = username ,
187
+ TargetUser = token . UserId ,
203
188
RootName = "scoreboardSegment" ,
204
189
} ;
205
190
if ( ! HttpMethods . IsPost ( this . Request . Method ) )
@@ -208,7 +193,7 @@ public async Task<IActionResult> Lbp1Leaderboards(string slotType, int id)
208
193
for ( int i = 1 ; i <= 4 ; i ++ )
209
194
{
210
195
options . ScoreType = i ;
211
- ScoreboardResponse response = this . getScores ( options ) ;
196
+ ScoreboardResponse response = await this . GetScores ( options ) ;
212
197
scoreboardResponses . Add ( new PlayerScoreboardResponse ( response . Scores , i ) ) ;
213
198
}
214
199
return this . Ok ( new MultiScoreboardResponse ( scoreboardResponses ) ) ;
@@ -217,9 +202,9 @@ public async Task<IActionResult> Lbp1Leaderboards(string slotType, int id)
217
202
GameScore ? score = await this . DeserializeBody < GameScore > ( ) ;
218
203
if ( score == null ) return this . BadRequest ( ) ;
219
204
options . ScoreType = score . Type ;
220
- options . TargetPlayerIds = this . getFriendUsernames ( token . UserId , username ) ;
205
+ options . TargetPlayerIds = GetFriendIds ( token . UserId ) ;
221
206
222
- return this . Ok ( this . getScores ( options ) ) ;
207
+ return this . Ok ( await this . GetScores ( options ) ) ;
223
208
}
224
209
225
210
[ HttpGet ( "friendscores/{slotType}/{slotId:int}/{type:int}" ) ]
@@ -230,96 +215,95 @@ public async Task<IActionResult> FriendScores(string slotType, int slotId, int?
230
215
231
216
if ( pageSize <= 0 ) return this . BadRequest ( ) ;
232
217
233
- string username = await this . database . UsernameFromGameToken ( token ) ;
234
-
235
218
if ( SlotHelper . IsTypeInvalid ( slotType ) ) return this . BadRequest ( ) ;
236
219
237
220
if ( slotType == "developer" ) slotId = await SlotHelper . GetPlaceholderSlotId ( this . database , slotId , SlotType . Developer ) ;
238
221
239
- string [ ] friendIds = this . getFriendUsernames ( token . UserId , username ) ;
222
+ int [ ] friendIds = GetFriendIds ( token . UserId ) ;
240
223
241
- return this . Ok ( this . getScores ( new LeaderboardOptions
224
+ return this . Ok ( await this . GetScores ( new LeaderboardOptions
242
225
{
243
226
RootName = "scores" ,
244
227
PageSize = pageSize ,
245
228
PageStart = pageStart ,
246
229
SlotId = slotId ,
247
230
ChildSlotId = childId ,
248
231
ScoreType = type ,
249
- TargetUsername = username ,
232
+ TargetUser = token . UserId ,
250
233
TargetPlayerIds = friendIds ,
251
234
} ) ) ;
252
235
}
253
236
254
237
[ HttpGet ( "topscores/{slotType}/{slotId:int}/{type:int}" ) ]
255
238
[ HttpGet ( "topscores/{slotType}/{slotId:int}/{childId:int}/{type:int}" ) ]
256
- [ SuppressMessage ( "ReSharper" , "PossibleMultipleEnumeration" ) ]
257
239
public async Task < IActionResult > TopScores ( string slotType , int slotId , int ? childId , int type , [ FromQuery ] int pageStart = - 1 , [ FromQuery ] int pageSize = 5 )
258
240
{
259
241
GameTokenEntity token = this . GetToken ( ) ;
260
242
261
243
if ( pageSize <= 0 ) return this . BadRequest ( ) ;
262
244
263
- string username = await this . database . UsernameFromGameToken ( token ) ;
264
-
265
245
if ( SlotHelper . IsTypeInvalid ( slotType ) ) return this . BadRequest ( ) ;
266
246
267
247
if ( slotType == "developer" ) slotId = await SlotHelper . GetPlaceholderSlotId ( this . database , slotId , SlotType . Developer ) ;
268
248
269
- return this . Ok ( this . getScores ( new LeaderboardOptions
249
+ return this . Ok ( await this . GetScores ( new LeaderboardOptions
270
250
{
271
251
RootName = "scores" ,
272
252
PageSize = pageSize ,
273
253
PageStart = pageStart ,
274
254
SlotId = slotId ,
275
255
ChildSlotId = childId ,
276
256
ScoreType = type ,
277
- TargetUsername = username ,
257
+ TargetUser = token . UserId ,
278
258
TargetPlayerIds = null ,
279
259
} ) ) ;
280
260
}
281
261
282
262
private class LeaderboardOptions
283
263
{
284
- public int SlotId { get ; set ; }
264
+ public int SlotId { get ; init ; }
285
265
public int ScoreType { get ; set ; }
286
- public string TargetUsername { get ; set ; } = "" ;
287
- public int PageStart { get ; set ; } = - 1 ;
288
- public int PageSize { get ; set ; } = 5 ;
289
- public string RootName { get ; set ; } = "scores" ;
290
- public string [ ] ? TargetPlayerIds ;
266
+ public int TargetUser { get ; init ; }
267
+ public int PageStart { get ; init ; } = - 1 ;
268
+ public int PageSize { get ; init ; } = 5 ;
269
+ public string RootName { get ; init ; } = "scores" ;
270
+ public int [ ] ? TargetPlayerIds ;
291
271
public int ? ChildSlotId ;
292
272
}
293
273
294
- private ScoreboardResponse getScores ( LeaderboardOptions options )
274
+ private async Task < ScoreboardResponse > GetScores ( LeaderboardOptions options )
295
275
{
296
- // This is hella ugly but it technically assigns the proper rank to a score
297
- // var needed for Anonymous type returned from SELECT
298
- var rankedScores = this . database . Scores . Where ( s => s . SlotId == options . SlotId )
276
+ IQueryable < ScoreEntity > scoreQuery = this . database . Scores . Where ( s => s . SlotId == options . SlotId )
299
277
. Where ( s => options . ScoreType == - 1 || s . Type == options . ScoreType )
300
278
. Where ( s => s . ChildSlotId == 0 || s . ChildSlotId == options . ChildSlotId )
301
- . AsEnumerable ( )
302
- . Where ( s => options . TargetPlayerIds == null ||
303
- options . TargetPlayerIds . Any ( id => s . PlayerIdCollection . Split ( "," ) . Contains ( id ) ) )
304
- . OrderByDescending ( s => s . Points )
305
- . ThenBy ( s => s . ScoreId )
306
- . ToList ( )
307
- . Select ( ( s , rank ) => new
279
+ . Where ( s => options . TargetPlayerIds == null || options . TargetPlayerIds . Contains ( s . UserId ) ) ;
280
+
281
+ // First find if you have a score on a level to find scores around it
282
+ var myScore = await scoreQuery . Where ( s => s . UserId == options . TargetUser )
283
+ . Select ( s => new
308
284
{
309
285
Score = s ,
310
- Rank = rank + 1 ,
311
- } )
312
- . ToList ( ) ;
286
+ Rank = scoreQuery . Count ( s2 => s2 . Points > s . Points ) + 1 ,
287
+ } ) . FirstOrDefaultAsync ( ) ;
313
288
289
+ int skipAmt = options . PageStart != - 1 || myScore == null ? options . PageStart - 1 : myScore . Rank - 3 ;
314
290
315
- // Find your score, since even if you aren't in the top list your score is pinned
316
- var myScore = rankedScores . Where ( rs => rs . Score . PlayerIdCollection . Split ( "," ) . Contains ( options . TargetUsername ) ) . MaxBy ( rs => rs . Score . Points ) ;
291
+ var rankedScores = scoreQuery . OrderByDescending ( s => s . Points )
292
+ . ThenBy ( s => s . Timestamp )
293
+ . ThenBy ( s => s . ScoreId )
294
+ . Skip ( Math . Max ( 0 , skipAmt ) )
295
+ . Take ( Math . Min ( options . PageSize , 30 ) )
296
+ . Select ( s => new
297
+ {
298
+ Score = s ,
299
+ Rank = scoreQuery . Count ( s2 => s2 . Points > s . Points ) + 1 ,
300
+ } )
301
+ . ToList ( ) ;
317
302
318
- // Paginated viewing: if not requesting pageStart, get results around user
319
- var pagedScores = rankedScores . Skip ( options . PageStart != - 1 || myScore == null ? options . PageStart - 1 : myScore . Rank - 3 ) . Take ( Math . Min ( options . PageSize , 30 ) ) ;
303
+ int totalScores = scoreQuery . Count ( ) ;
320
304
321
- List < GameScore > gameScores = pagedScores . ToSerializableList ( ps => GameScore . CreateFromEntity ( ps . Score , ps . Rank ) ) ;
305
+ List < GameScore > gameScores = rankedScores . ToSerializableList ( ps => GameScore . CreateFromEntity ( ps . Score , ps . Rank ) ) ;
322
306
323
- return new ScoreboardResponse ( options . RootName , gameScores , rankedScores . Count , myScore ? . Score . Points ?? 0 , myScore ? . Rank ?? 0 ) ;
307
+ return new ScoreboardResponse ( options . RootName , gameScores , totalScores , myScore ? . Score . Points ?? 0 , myScore ? . Rank ?? 0 ) ;
324
308
}
325
309
}
0 commit comments