public void Tick(OsuStatus status, OsuMemoryStatus rawStatus, StructuredOsuMemoryReader reader) { _notUpdatingTokens.WaitOne(); _notUpdatingMemoryValues.Reset(); lock (_lockingObject) { if (!ReferenceEquals(OsuMemoryData, reader.OsuMemoryAddresses)) { OsuMemoryData = reader.OsuMemoryAddresses; } if ((OsuStatus)_liveTokens["status"].Token.Value != status) { _liveTokens["status"].Token.Value = status; } _liveTokens["rawStatus"].Token.Value = rawStatus; int playTime = OsuMemoryData.GeneralData.AudioTime; switch (status) { case OsuStatus.Playing: case OsuStatus.Watching: if (_lastStatus != OsuStatus.Playing && _lastStatus != OsuStatus.Watching) { _newPlayStarted.Set(); Thread.Sleep(500); //Initial play delay } reader.TryRead(OsuMemoryData.Player); if (!ReferenceEquals(_rawData.Play, OsuMemoryData.Player)) { _rawData.Play = OsuMemoryData.Player; } //TODO: support for live multiplayer leaderboard if (!ReadLeaderboard) { //Read whole leaderboard once if (reader.TryRead(OsuMemoryData.LeaderBoard)) { ReadLeaderboard = true; if (!ReferenceEquals(_rawData.LeaderBoard, OsuMemoryData.LeaderBoard)) { _rawData.LeaderBoard = OsuMemoryData.LeaderBoard; } } } else { //Throttle whole leaderboard reads - Temporary solution until multiplayer detection is implemented, this should be only done in multiplayer if (_nextLeaderBoardUpdate < DateTime.UtcNow) { reader.TryRead(OsuMemoryData.LeaderBoard); _nextLeaderBoardUpdate = DateTime.UtcNow.AddMilliseconds(_settings.Get <int>(MultiplayerLeaderBoardUpdateRate)); } else { //...then update main player data if (OsuMemoryData.LeaderBoard.HasLeaderBoard) { reader.TryRead(OsuMemoryData.LeaderBoard.MainPlayer); } } } break; case OsuStatus.ResultsScreen: ReadLeaderboard = false; reader.TryRead(OsuMemoryData.ResultsScreen); if (!ReferenceEquals(_rawData.Play, OsuMemoryData.ResultsScreen)) { _rawData.Play = OsuMemoryData.ResultsScreen; } playTime = Convert.ToInt32(_rawData.PpCalculator?.BeatmapLength ?? 0); break; default: ReadLeaderboard = false; _rawData.LeaderBoard = new LeaderBoard(); reader.TryRead(OsuMemoryData.Skin); _lastStatus = status; break; } _rawData.PlayTime = playTime; _liveTokens["time"].Update(); _lastStatus = status; } _notUpdatingMemoryValues.Set(); }
private async void OnShown(object sender, EventArgs eventArgs) { if (!string.IsNullOrEmpty(_osuWindowTitleHint)) { Text += $": {_osuWindowTitleHint}"; } Text += $" ({(Environment.Is64BitProcess ? "x64" : "x86")})"; _sreader.InvalidRead += SreaderOnInvalidRead; await Task.Run(async() => { Stopwatch stopwatch; double readTimeMs, readTimeMsMin, readTimeMsMax; _sreader.WithTimes = true; var readUsingProperty = false; var baseAddresses = new OsuBaseAddresses(); while (true) { if (cts.IsCancellationRequested) { return; } if (!_sreader.CanRead) { Invoke((MethodInvoker)(() => { textBox_Data.Text = "osu! process not found"; })); await Task.Delay(_readDelay); continue; } stopwatch = Stopwatch.StartNew(); if (readUsingProperty) { baseAddresses.Beatmap.Id = ReadInt(baseAddresses.Beatmap, nameof(CurrentBeatmap.Id)); baseAddresses.Beatmap.SetId = ReadInt(baseAddresses.Beatmap, nameof(CurrentBeatmap.SetId)); baseAddresses.Beatmap.MapString = ReadString(baseAddresses.Beatmap, nameof(CurrentBeatmap.MapString)); baseAddresses.Beatmap.FolderName = ReadString(baseAddresses.Beatmap, nameof(CurrentBeatmap.FolderName)); baseAddresses.Beatmap.OsuFileName = ReadString(baseAddresses.Beatmap, nameof(CurrentBeatmap.OsuFileName)); baseAddresses.Beatmap.Md5 = ReadString(baseAddresses.Beatmap, nameof(CurrentBeatmap.Md5)); baseAddresses.Beatmap.Ar = ReadFloat(baseAddresses.Beatmap, nameof(CurrentBeatmap.Ar)); baseAddresses.Beatmap.Cs = ReadFloat(baseAddresses.Beatmap, nameof(CurrentBeatmap.Cs)); baseAddresses.Beatmap.Hp = ReadFloat(baseAddresses.Beatmap, nameof(CurrentBeatmap.Hp)); baseAddresses.Beatmap.Od = ReadFloat(baseAddresses.Beatmap, nameof(CurrentBeatmap.Od)); baseAddresses.Beatmap.Status = ReadShort(baseAddresses.Beatmap, nameof(CurrentBeatmap.Status)); baseAddresses.Skin.Folder = ReadString(baseAddresses.Skin, nameof(Skin.Folder)); baseAddresses.GeneralData.RawStatus = ReadInt(baseAddresses.GeneralData, nameof(GeneralData.RawStatus)); baseAddresses.GeneralData.GameMode = ReadInt(baseAddresses.GeneralData, nameof(GeneralData.GameMode)); baseAddresses.GeneralData.Retries = ReadInt(baseAddresses.GeneralData, nameof(GeneralData.Retries)); baseAddresses.GeneralData.AudioTime = ReadInt(baseAddresses.GeneralData, nameof(GeneralData.AudioTime)); baseAddresses.GeneralData.Mods = ReadInt(baseAddresses.GeneralData, nameof(GeneralData.Mods)); } else { _sreader.TryRead(baseAddresses.Beatmap); _sreader.TryRead(baseAddresses.Skin); _sreader.TryRead(baseAddresses.GeneralData); } if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.SongSelect) { _sreader.TryRead(baseAddresses.SongSelectionScores); } else { baseAddresses.SongSelectionScores.Scores.Clear(); } if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.ResultsScreen) { _sreader.TryRead(baseAddresses.ResultsScreen); } if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.Playing) { _sreader.TryRead(baseAddresses.Player); //TODO: flag needed for single/multi player detection (should be read once per play in singleplayer) _sreader.TryRead(baseAddresses.LeaderBoard); _sreader.TryRead(baseAddresses.KeyOverlay); if (readUsingProperty) { //Testing reading of reference types(other than string) _sreader.TryReadProperty(baseAddresses.Player, nameof(Player.Mods), out var dummyResult); } } else { baseAddresses.LeaderBoard.Players.Clear(); } var hitErrors = baseAddresses.Player?.HitErrors; if (hitErrors != null) { var hitErrorsCount = hitErrors.Count; hitErrors.Clear(); hitErrors.Add(hitErrorsCount); } stopwatch.Stop(); readTimeMs = stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond; lock (_minMaxLock) { if (readTimeMs < _memoryReadTimeMin) { _memoryReadTimeMin = readTimeMs; } if (readTimeMs > _memoryReadTimeMax) { _memoryReadTimeMax = readTimeMs; } // copy value since we're inside lock readTimeMsMin = _memoryReadTimeMin; readTimeMsMax = _memoryReadTimeMax; } try { Invoke((MethodInvoker)(() => { textBox_readTime.Text = $" ReadTimeMS: {readTimeMs}{Environment.NewLine}" + $" Min ReadTimeMS: {readTimeMsMin}{Environment.NewLine}" + $"Max ReadTimeMS: {readTimeMsMax}{Environment.NewLine}"; textBox_Data.Text = JsonConvert.SerializeObject(baseAddresses, Formatting.Indented); textBox_ReadTimes.Text = JsonConvert.SerializeObject(_sreader.ReadTimes, Formatting.Indented); })); } catch (ObjectDisposedException) { return; } _sreader.ReadTimes.Clear(); await Task.Delay(_readDelay); } }, cts.Token); }