private void showBeatmapPanel(SpectatorState state) { if (state?.BeatmapID == null) { beatmapPanelContainer.Clear(); onlineBeatmap = null; return; } var req = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId); req.Success += res => Schedule(() => { if (state != this.state) { return; } onlineBeatmap = res.ToBeatmapSet(rulesets); beatmapPanelContainer.Child = new GridBeatmapPanel(onlineBeatmap); checkForAutomaticDownload(); }); api.Queue(req); }
private void updateLocalRoomSettings(MultiplayerRoomSettings settings) { if (Room == null) { return; } Schedule(() => { if (Room == null) { return; } Debug.Assert(apiRoom != null); // Update a few properties of the room instantaneously. Room.Settings = settings; apiRoom.Name.Value = Room.Settings.Name; // The playlist update is delayed until an online beatmap lookup (below) succeeds. // In-order for the client to not display an outdated beatmap, the playlist is forcefully cleared here. apiRoom.Playlist.Clear(); RoomChanged?.Invoke(); var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId); req.Success += res => updatePlaylist(settings, res); api.Queue(req); }); }
private void loadNewPanel(BeatmapInfo beatmap) { loadCancellation?.Cancel(); request?.Cancel(); panel?.FadeOut(200); panel?.Expire(); panel = null; if (beatmap?.OnlineBeatmapID == null) { return; } loadCancellation = new CancellationTokenSource(); request = new GetBeatmapSetRequest(beatmap.OnlineBeatmapID.Value, BeatmapSetLookupType.BeatmapId); request.Success += res => Schedule(() => { panel = new DirectGridPanel(res.ToBeatmapSet(rulesets)); LoadComponentAsync(panel, AddInternal, loadCancellation.Token); }); api.Queue(request); }
private Task updateLocalRoomSettings(MultiplayerRoomSettings settings, CancellationToken cancellationToken = default) => scheduleAsync(() => { if (Room == null) { return; } Debug.Assert(apiRoom != null); // Update a few properties of the room instantaneously. Room.Settings = settings; apiRoom.Name.Value = Room.Settings.Name; // The current item update is delayed until an online beatmap lookup (below) succeeds. // In-order for the client to not display an outdated beatmap, the current item is forcefully cleared here. CurrentMatchPlayingItem.Value = null; RoomUpdated?.Invoke(); var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId); req.Success += res => { if (cancellationToken.IsCancellationRequested) { return; } updatePlaylist(settings, res); }; api.Queue(req); }, cancellationToken);
private void load(OsuGameBase osu, APIAccess api, RulesetStore rulesets) { Bindable <BeatmapInfo> beatmapBindable = new Bindable <BeatmapInfo>(); var imported = ImportBeatmapTest.LoadOszIntoOsu(osu); Child = backgroundSprite = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }; backgroundSprite.Beatmap.BindTo(beatmapBindable); var req = new GetBeatmapSetRequest(1); api.Queue(req); AddStep("null", () => beatmapBindable.Value = null); AddStep("imported", () => beatmapBindable.Value = imported.Beatmaps.First()); if (api.IsLoggedIn) { AddUntilStep(() => req.Result != null, "wait for api response"); AddStep("online", () => beatmapBindable.Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) }); } else { AddStep("online (login first)", () => { }); } }
public void TestOnlineBeatmap() { if (api.IsLoggedIn) { var req = new GetBeatmapSetRequest(1); api.Queue(req); AddUntilStep("wait for api response", () => req.Result != null); TestUpdateableBeatmapBackgroundSprite background = null; AddStep("load online beatmap", () => { Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both, Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } } }; }); AddUntilStep("wait for load", () => background.ContentLoaded); } else { AddStep("online (login first)", () => { }); } }
public void ShowBeatmapSet(int beatmapSetId) { // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. var req = new GetBeatmapSetRequest(beatmapSetId); req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets)); api.Queue(req); }
public void FetchAndShowBeatmapSet(int beatmapSetId) { beatmapSet.Value = null; var req = new GetBeatmapSetRequest(beatmapSetId); req.Success += res => beatmapSet.Value = res.ToBeatmapSet(rulesets); API.Queue(req); Show(); }
private void showBeatmapPanel(int beatmapId) { var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); req.Success += res => Schedule(() => { beatmapPanelContainer.Child = new GridBeatmapPanel(res.ToBeatmapSet(rulesets)); }); api.Queue(req); }
public void FetchAndShowBeatmap(int beatmapId) { beatmapSet.Value = null; var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); req.Success += res => { beatmapSet.Value = res.ToBeatmapSet(rulesets); Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); }; API.Queue(req); Show(); }
private void showBeatmapPanel(SpectatorState state) { Debug.Assert(state.BeatmapID != null); onlineBeatmapRequest = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId); onlineBeatmapRequest.Success += beatmapSet => Schedule(() => { this.beatmapSet = beatmapSet; beatmapPanelContainer.Child = new BeatmapCardNormal(this.beatmapSet, allowExpansion: false); checkForAutomaticDownload(); }); api.Queue(onlineBeatmapRequest); }
private void showBeatmapPanel(SpectatorState state) { Debug.Assert(state.BeatmapID != null); onlineBeatmapRequest = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId); onlineBeatmapRequest.Success += res => Schedule(() => { onlineBeatmap = res.ToBeatmapSet(rulesets); beatmapPanelContainer.Child = new GridBeatmapPanel(onlineBeatmap); checkForAutomaticDownload(); }); api.Queue(onlineBeatmapRequest); }
protected override Task <APIBeatmapSet> GetOnlineBeatmapSet(int beatmapId, CancellationToken cancellationToken = default) { var tcs = new TaskCompletionSource <APIBeatmapSet>(); var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); req.Success += res => { if (cancellationToken.IsCancellationRequested) { tcs.SetCanceled(); return; } tcs.SetResult(res); }; req.Failure += e => tcs.SetException(e); API.Queue(req); return(tcs.Task); }
public bool Crawl(int id, PisstaubeDbContext _context) { try { while (!_apiAccess.IsLoggedIn) { Thread.Sleep(1000); } var setRequest = new GetBeatmapSetRequest(id); lock (_lock) _request_count++; _rl.Limit(); setRequest.Perform(_apiAccess); lock (_lock) if (_request_count > int.Parse(Environment.GetEnvironmentVariable("CRAWLER_REQUESTS_PER_MINUTE"))) { Thread.Sleep(TimeSpan.FromMinutes(1)); } var apiSet = setRequest.Result; var localSet = apiSet?.ToBeatmapSet(_store); if (localSet == null) { return(false); } var dbSet = BeatmapSet.FromBeatmapSetInfo(localSet); if (dbSet == null) { return(false); } foreach (var map in dbSet.ChildrenBeatmaps) { var fileInfo = _downloader.Download(map); map.FileMd5 = _cache.Get() .CacheBeatmaps .Where(cmap => cmap.Hash == fileInfo.Hash) .Select(cmap => cmap.FileMd5) .FirstOrDefault(); } lock (_lock) { _context.BeatmapSet.Add(dbSet); } _search.IndexBeatmap(dbSet); } catch (Exception ex) { Logger.Error(ex, $"Unknown Error occured while crawling Id {id}!"); lock (_lock) Thread.Sleep(TimeSpan.FromMinutes(1)); return(false); } return(true); }
private void load() { int currentScoreId = 0; // Handling here is pretending to be a server, while also updating the local state to match // how the server would eventually respond and update the RoomManager. ((DummyAPIAccess)api).HandleRequest = req => { switch (req) { case CreateRoomRequest createRoomRequest: var apiRoom = new Room(); apiRoom.CopyFrom(createRoomRequest.Room); // Passwords are explicitly not copied between rooms. apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); apiRoom.Password.Value = createRoomRequest.Room.Password.Value; AddRoom(apiRoom); var responseRoom = new APICreatedRoom(); responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); createRoomRequest.TriggerSuccess(responseRoom); return(true); case JoinRoomRequest joinRoomRequest: { var room = Rooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); if (joinRoomRequest.Password != room.Password.Value) { joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password.")); return(true); } joinRoomRequest.TriggerSuccess(); return(true); } case PartRoomRequest partRoomRequest: partRoomRequest.TriggerSuccess(); return(true); case GetRoomsRequest getRoomsRequest: var roomsWithoutParticipants = new List <Room>(); foreach (var r in Rooms) { roomsWithoutParticipants.Add(createResponseRoom(r, false)); } getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); return(true); case GetRoomRequest getRoomRequest: getRoomRequest.TriggerSuccess(createResponseRoom(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); return(true); case GetBeatmapSetRequest getBeatmapSetRequest: var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type); onlineReq.Success += res => getBeatmapSetRequest.TriggerSuccess(res); onlineReq.Failure += e => getBeatmapSetRequest.TriggerFailure(e); // Get the online API from the game's dependencies. game.Dependencies.Get <IAPIProvider>().Queue(onlineReq); return(true); case CreateRoomScoreRequest createRoomScoreRequest: createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); return(true); case SubmitRoomScoreRequest submitRoomScoreRequest: submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore { ID = currentScoreId++, Accuracy = 1, EndedAt = DateTimeOffset.Now, Passed = true, Rank = ScoreRank.S, MaxCombo = 1000, TotalScore = 1000000, User = api.LocalUser.Value, Statistics = new Dictionary <HitResult, int>() }); return(true); } return(false); }; }
private void load() { int currentScoreId = 0; int currentRoomId = 0; int currentPlaylistItemId = 0; ((DummyAPIAccess)api).HandleRequest = req => { switch (req) { case CreateRoomRequest createRoomRequest: var createdRoom = new APICreatedRoom(); createdRoom.CopyFrom(createRoomRequest.Room); createdRoom.RoomID.Value ??= currentRoomId++; for (int i = 0; i < createdRoom.Playlist.Count; i++) { createdRoom.Playlist[i].ID = currentPlaylistItemId++; } Rooms.Add(createdRoom); createRoomRequest.TriggerSuccess(createdRoom); return(true); case JoinRoomRequest joinRoomRequest: joinRoomRequest.TriggerSuccess(); return(true); case PartRoomRequest partRoomRequest: partRoomRequest.TriggerSuccess(); return(true); case GetRoomsRequest getRoomsRequest: var roomsWithoutParticipants = new List <Room>(); foreach (var r in Rooms) { var newRoom = new Room(); newRoom.CopyFrom(r); newRoom.RecentParticipants.Clear(); roomsWithoutParticipants.Add(newRoom); } getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); return(true); case GetRoomRequest getRoomRequest: getRoomRequest.TriggerSuccess(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId)); return(true); case GetBeatmapSetRequest getBeatmapSetRequest: var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type); onlineReq.Success += res => getBeatmapSetRequest.TriggerSuccess(res); onlineReq.Failure += e => getBeatmapSetRequest.TriggerFailure(e); // Get the online API from the game's dependencies. game.Dependencies.Get <IAPIProvider>().Queue(onlineReq); return(true); case CreateRoomScoreRequest createRoomScoreRequest: createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); return(true); case SubmitRoomScoreRequest submitRoomScoreRequest: submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore { ID = currentScoreId++, Accuracy = 1, EndedAt = DateTimeOffset.Now, Passed = true, Rank = ScoreRank.S, MaxCombo = 1000, TotalScore = 1000000, User = api.LocalUser.Value, Statistics = new Dictionary <HitResult, int>() }); return(true); } return(false); }; }
public void BeginUpdaterSync() { while (true) { var beatmapSets = _factory.Get().BeatmapSet.Where(x => !x.Disabled) .Where( x => x.LastChecked != null && (( ( x.RankedStatus == BeatmapSetOnlineStatus.None || x.RankedStatus == BeatmapSetOnlineStatus.Graveyard || x.RankedStatus == BeatmapSetOnlineStatus.Pending || x.RankedStatus == BeatmapSetOnlineStatus.Ranked || x.RankedStatus == BeatmapSetOnlineStatus.Loved ) && (x.LastChecked.Value + TimeSpan.FromDays(30)) .Subtract(DateTime.Now).TotalMilliseconds < 0 ) || ( ( x.RankedStatus == BeatmapSetOnlineStatus.Qualified || x.RankedStatus == BeatmapSetOnlineStatus.WIP ) && (x.LastChecked.Value + TimeSpan.FromDays(1)) .Subtract(DateTime.Now).TotalMilliseconds < 0 ) || ( ( x.RankedStatus == BeatmapSetOnlineStatus.Approved ) && (x.LastChecked.Value + TimeSpan.FromDays(90)) .Subtract(DateTime.Now).TotalMilliseconds < 0 )) ); foreach (var bmSet in beatmapSets.ToList()) { bmSet.ChildrenBeatmaps = _factory.Get().Beatmaps.Where(x => x.ParentSetId == bmSet.SetId).ToList(); var setRequest = new GetBeatmapSetRequest(bmSet.SetId); _rl.Limit(); setRequest.Perform(_apiAccess); Logger.LogPrint("Updating BeatmapSetId " + bmSet.SetId); var setInfo = setRequest.Result.ToBeatmapSet(_store); var newBm = BeatmapSet.FromBeatmapSetInfo(setInfo); using var db = _factory.GetForWrite(); var hasChanged = false; foreach (var cb in newBm.ChildrenBeatmaps) { var fInfo = _bmDl.Download(cb); var ha = _cFactory.Get().CacheBeatmaps.Where(b => b.Hash == fInfo.Hash).Select(f => f.FileMd5).FirstOrDefault(); cb.FileMd5 = ha; db.Context.Entry(cb).State = Microsoft.EntityFrameworkCore.EntityState.Modified; db.Context.Beatmaps.Update(cb); if (bmSet.ChildrenBeatmaps.Any(x => x.FileMd5 == ha)) { continue; } hasChanged = true; } if (newBm.ChildrenBeatmaps.Count > bmSet.ChildrenBeatmaps.Count) { hasChanged = true; } var bmFileId = newBm.SetId.ToString("x8"); var bmFileIdNoVid = newBm.SetId.ToString("x8") + "_novid"; if (hasChanged) { _storage.GetStorageForDirectory("cache").Delete(bmFileId); _storage.GetStorageForDirectory("cache").Delete(bmFileIdNoVid); _search.DeleteBeatmap(newBm.SetId); _search.IndexBeatmap(newBm); } db.Context.Entry(newBm).State = Microsoft.EntityFrameworkCore.EntityState.Modified; db.Context.BeatmapSet.Update(newBm); FileSafety.DeleteCleanupDirectory(); } } }
public virtual async Task <bool> Crawl(int id) { var dbContext = _dbContextPool.Rent(); Logger.LogPrint($"Crawling BeatmapId {id}...", LoggingTarget.Network, LogLevel.Debug); try { var beatmapSetRequest = new GetBeatmapSetRequest(id); beatmapSetRequest.Perform(ApiProvider); var beatmapSetInfo = beatmapSetRequest.Result; if (beatmapSetInfo == null) { _errorCount++; return(false); } var beatmapSet = BeatmapSet.FromBeatmapSetInfo(beatmapSetInfo.ToBeatmapSet()); if (beatmapSet == null) { _errorCount++; return(false); } var cacheStorage = _storage.GetStorageForDirectory("cache"); if (cacheStorage.Exists(beatmapSet.SetId.ToString("x8") + "_novid")) { cacheStorage.Delete(beatmapSet.SetId.ToString("x8") + "_novid"); } if (cacheStorage.Exists(beatmapSet.SetId.ToString("x8"))) { cacheStorage.Delete(beatmapSet.SetId.ToString("x8")); } foreach (var childrenBeatmap in beatmapSet.ChildrenBeatmaps) { var fileInfo = _beatmapDownloader.Download(childrenBeatmap); childrenBeatmap.FileMd5 = fileInfo.Item2; } dbContext.BeatmapSet.AddOrUpdate(beatmapSet); await dbContext.SaveChangesAsync(); SearchEngine.Index(new [] { beatmapSet }); _errorCount = 0; return(true); } catch (WebException) // Don't worry about WebException exceptions, we can safely ignore those. { _errorCount++; return(false); } catch (Exception e) // Everything else, redo the Crawl. { Logger.Error(e, "Unknown error during crawling occured!"); SentrySdk.CaptureException(e); if (_errorCount > 1024) { Logger.LogPrint("Error count too high! canceling crawl...", LoggingTarget.Network, LogLevel.Important); return(false); } _errorCount++; return(await Crawl(id)); } finally { _dbContextPool.Return(dbContext); } }
/// <summary> /// Handles an API request, while also updating the local state to match /// how the server would eventually respond and update an <see cref="RoomManager"/>. /// </summary> /// <param name="request">The API request to handle.</param> /// <param name="localUser">The local user to store in responses where required.</param> /// <param name="game">The game base for cases where actual online requests need to be sent.</param> /// <returns>Whether the request was successfully handled.</returns> public bool HandleRequest(APIRequest request, User localUser, OsuGameBase game) { switch (request) { case CreateRoomRequest createRoomRequest: var apiRoom = new Room(); apiRoom.CopyFrom(createRoomRequest.Room); // Passwords are explicitly not copied between rooms. apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); apiRoom.Password.Value = createRoomRequest.Room.Password.Value; AddServerSideRoom(apiRoom); var responseRoom = new APICreatedRoom(); responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); createRoomRequest.TriggerSuccess(responseRoom); return(true); case JoinRoomRequest joinRoomRequest: { var room = ServerSideRooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); if (joinRoomRequest.Password != room.Password.Value) { joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password.")); return(true); } joinRoomRequest.TriggerSuccess(); return(true); } case PartRoomRequest partRoomRequest: partRoomRequest.TriggerSuccess(); return(true); case GetRoomsRequest getRoomsRequest: var roomsWithoutParticipants = new List <Room>(); foreach (var r in ServerSideRooms) { roomsWithoutParticipants.Add(createResponseRoom(r, false)); } getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); return(true); case GetRoomRequest getRoomRequest: getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); return(true); case GetBeatmapSetRequest getBeatmapSetRequest: var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type); onlineReq.Success += res => getBeatmapSetRequest.TriggerSuccess(res); onlineReq.Failure += e => getBeatmapSetRequest.TriggerFailure(e); // Get the online API from the game's dependencies. game.Dependencies.Get <IAPIProvider>().Queue(onlineReq); return(true); case CreateRoomScoreRequest createRoomScoreRequest: createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); return(true); case SubmitRoomScoreRequest submitRoomScoreRequest: submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore { ID = currentScoreId++, Accuracy = 1, EndedAt = DateTimeOffset.Now, Passed = true, Rank = ScoreRank.S, MaxCombo = 1000, TotalScore = 1000000, User = localUser, Statistics = new Dictionary <HitResult, int>() }); return(true); } return(false); }