diff --git a/osu.Game.Rulesets.IGPlayer.Tests/osu.Game.Rulesets.IGPlayer.Tests.csproj b/osu.Game.Rulesets.IGPlayer.Tests/osu.Game.Rulesets.IGPlayer.Tests.csproj index 207ce60..8f0c051 100644 --- a/osu.Game.Rulesets.IGPlayer.Tests/osu.Game.Rulesets.IGPlayer.Tests.csproj +++ b/osu.Game.Rulesets.IGPlayer.Tests/osu.Game.Rulesets.IGPlayer.Tests.csproj @@ -13,6 +13,7 @@ + diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs index 73b9d81..a4e48d7 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/BMData.cs @@ -1,3 +1,4 @@ +using System; using Newtonsoft.Json; using osu.Framework.Graphics.Audio; using osu.Game.Beatmaps; @@ -53,6 +54,7 @@ public void UpdateBeatmap(WorkingBeatmap beatmap) this.MetaData.Mapper = metadata.Author.Username; this.MetaData.DiffName = beatmap.BeatmapInfo.DifficultyName; + //todo: 适应Mod变更 var diffInf = beatmap.BeatmapInfo.Difficulty; this.Stats.AR = diffInf.ApproachRate; this.Stats.CS = diffInf.CircleSize; @@ -60,6 +62,17 @@ public void UpdateBeatmap(WorkingBeatmap beatmap) this.Stats.OD = diffInf.OverallDifficulty; this.Stats.SR = (float)beatmap.BeatmapInfo.StarRating; + this.Stats.BPM.Max = (int)Math.Round(beatmap.Beatmap.ControlPointInfo.BPMMaximum); + this.Stats.BPM.Max = (int)Math.Round(beatmap.Beatmap.ControlPointInfo.BPMMinimum); + + this.Path.AudioPath = "~ NOT IMPLEMENTED ~"; + this.Path.BackgroundPath = "~ NOT IMPLEMENTED ~"; + this.Path.BeatmapFile = "~ NOT IMPLEMENTED ~"; + this.Path.BgPath = "~ NOT IMPLEMENTED ~"; + this.Path.BeatmapFolder = "~ NOT IMPLEMENTED ~"; + + this.Stats.MaxCombo = -1; + short rankingStatus; switch (beatmap.BeatmapInfo.Status) diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/MenuModsData.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/MenuModsData.cs index 1cff6d9..2301d0f 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/MenuModsData.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Menu/MenuModsData.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Menu { @@ -12,18 +9,5 @@ public struct MenuModsData [JsonProperty("str")] public string Acronyms; - - public void UpdateFrom(IReadOnlyList mods) - { - this.AppliedMods = mods.Count; - - if (mods.Count >= 1) - { - string str = mods.Aggregate("", (current, mod) => current + $"{mod.Acronym}"); - this.Acronyms = str; - } - else - this.Acronyms = "NM"; - } } } diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Root.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Root.cs index 2cb860e..dd7c502 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Root.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Data/Root.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.Graphics.Audio; using osu.Game.Beatmaps; @@ -7,6 +8,7 @@ using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Results; using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Settings; using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Tourney; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data { @@ -28,6 +30,10 @@ public class DataRoot : ISelfUpdatableFromBeatmap, ISelfUpdatableFromAudio [JsonProperty("tourney")] public TourneyValues TourneyValues = new TourneyValues(); + public void ApplyMods(IList mods) + { + } + public void UpdateBeatmap(WorkingBeatmap workingBeatmap) { MenuValues.UpdateBeatmap(workingBeatmap); diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/GosuCompatInjector.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/GosuCompatInjector.cs index 0722553..9d8a413 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/GosuCompatInjector.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/GosuCompatInjector.cs @@ -1,6 +1,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Web; using osuTK; @@ -8,7 +9,7 @@ namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory { public partial class GosuCompatInjector : Component { - public static Updater? Updater { get; private set; } + public TrackerHub? Updater { get; private set; } private Container getContainerFromGame(string containerName, OsuGame game) { @@ -33,38 +34,46 @@ private Container getContainerFromGame(string containerName, OsuGame game) private void initializeUpdater() { + if (game == null) + { + Logger.Log("OsuGame is null, returning..."); + return; + } + var children = new Drawable[] { - handler = new WsLoader + handler = new WebSocketLoader { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - Updater = new Updater(handler) + Updater = new TrackerHub(handler) }; - if (game != null) - { - var container = this.getContainerFromGame("mfosu Gosumemory compat container", game); + var container = this.getContainerFromGame("mfosu Gosumemory compat container", game); + container.AddRange(children); - if (container.Children.Count == 0) - container.AddRange(children); - } + Logger.Log("Added gosu compat!"); } [BackgroundDependencyLoader] private void load(OsuGame? game) { + Logger.Log("Gosu compat injector!"); + + AlwaysPresent = true; + this.Anchor = Anchor.Centre; this.Origin = Anchor.Centre; this.RelativeSizeAxes = Axes.Both; this.Size = new Vector2(0.6f); + Logger.Log($"Updater is {Updater}"); if (Updater == null) initializeUpdater(); } - private WsLoader? handler; + private WebSocketLoader? handler; } } diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/AbstractTracker.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/AbstractTracker.cs new file mode 100644 index 0000000..8a6f3ff --- /dev/null +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/AbstractTracker.cs @@ -0,0 +1,34 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Tracker; + +public partial class AbstractTracker : CompositeDrawable +{ + protected TrackerHub Hub { get; private set; } + + public AbstractTracker(TrackerHub hub) + { + this.Hub = hub; + AlwaysPresent = true; + + InternalChild = new OsuSpriteText + { + Text = $"{this}", + Margin = new MarginPadding(30) + }; + } + + [BackgroundDependencyLoader] + private void load() + { + this.Clock = new FramedClock(null, false); + } + + public virtual void UpdateValues() + { + } +} diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/BeatmapStrainTracker.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/BeatmapStrainTracker.cs new file mode 100644 index 0000000..83687c7 --- /dev/null +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/BeatmapStrainTracker.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Framework.Logging; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Tracker; + +/// +/// 计算pp/密度表 +/// +public partial class BeatmapStrainTracker : AbstractTracker +{ + public BeatmapStrainTracker(TrackerHub hub) + : base(hub) + { + } + + private double invokeTime = 0d; + private WorkingBeatmap? beatmapOnInvoke; + + private CancellationTokenSource? ppStrainCancellationTokenSource; + + private bool scheduleStrainComputes; + + private readonly Bindable working = new Bindable(); + + [BackgroundDependencyLoader] + private void load(Bindable globalWorking) + { + this.working.BindTo(globalWorking); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + working.BindValueChanged(e => + { + this.UpdateStrain(e.NewValue); + }); + } + + protected override void Update() + { + if (scheduleStrainComputes && beatmapOnInvoke == working.Value) + UpdateStrain(this.working.Value); + } + + public void UpdateStrain(WorkingBeatmap workingBeatmap) + { + this.invokeTime = Clock.CurrentTime; + beatmapOnInvoke = workingBeatmap; + scheduleStrainComputes = false; + + ppStrainCancellationTokenSource?.Cancel(); + ppStrainCancellationTokenSource = new CancellationTokenSource(); + + Task.Run(async () => await updateStrain(workingBeatmap), ppStrainCancellationTokenSource.Token) + .ContinueWith(task => + { + if (!task.IsCompleted) return; + + if (task.Exception != null) + { + Logging.LogError(task.Exception, "Error occurred while updating strain"); + return; + } + + this.Schedule(() => + { + float[] result = task.GetResultSafely(); + Hub.GetDataRoot().MenuValues.pp.Strains = result; + }); + }); + } + + private Task updateStrain(WorkingBeatmap workingBeatmap) + { + try + { + double length = workingBeatmap.Track.Length; + + //WorkingBeatmap.TrackLoaded: true + WorkingBeatmap.Track.IsLoaded: false -> Track Length: 0 + if (length <= 0) + { + //持续5秒都没有音频,可能已经损坏,清空分布 + //todo: 没有音频的时候使用谱面长度来计算并更新分布和进度 + if (Clock.CurrentTime - invokeTime >= 10 * 1000) + { + Hub.GetDataRoot().MenuValues.pp.Strains = new[] { 0f }; + + Logger.Log("谱面音频在10秒内都没有加载,将放弃计算物件分布...", level: LogLevel.Important); + return Task.FromResult(new[] { 0f }); + } + + scheduleStrainComputes = true; + return Task.FromResult(new [] { 0f }); + } + + scheduleStrainComputes = false; + + // 最大分段数和密度缩放 + int maximumSegments = 512; + double segmentScale = 1; + + // 根据歌曲长度分段,总共分为 (歌曲总时间(秒) * segScale) 段 + int targetSegments = (int)(TimeSpan.FromMilliseconds(length).TotalSeconds * segmentScale); + + // 限制最大分段数量 + targetSegments = Math.Min(maximumSegments, targetSegments); + if (targetSegments <= 0) targetSegments = 1; + + // 尝试自动转谱 + var converter = workingBeatmap.BeatmapInfo.Ruleset.CreateInstance().CreateBeatmapConverter(workingBeatmap.Beatmap); + IBeatmap? beatmap = null; + + //Logger.Log($"Track length: {length} ~ Segments {targetSegments} ~ Conv? {converter.CanConvert()} ~ Loaded? {workingBeatmap.Track.IsLoaded} ~ Track? {workingBeatmap.Track}"); + if (converter.CanConvert()) beatmap = converter.Convert(); + var hitObjects = beatmap?.HitObjects ?? new HitObject[] { }; + + //获取每段的音频跨度 + double audioStep = length / targetSegments; + + //Segment -> Count + var segments = new Dictionary(); + + for (int i = 0; i < targetSegments; i++) + { + //此段的音频跨度 + double startTime = i * audioStep; + double endTime = (1 + i) * audioStep; + + //将跨度内的所有物件添加进来 + //o -> [startTime, endTime) + int count = hitObjects.Count(o => o.StartTime < endTime && o.StartTime >= startTime); + + segments.TryAdd(i, count); + } + + //最后将其返回 + return Task.FromResult(segments.Values.ToArray()); + } + catch (Exception e) + { + return Task.FromException(e); + } + } +} diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/BeatmapTracker.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/BeatmapTracker.cs new file mode 100644 index 0000000..d3fe4b9 --- /dev/null +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/BeatmapTracker.cs @@ -0,0 +1,38 @@ +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Logging; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Tracker; + +public partial class BeatmapTracker : AbstractTracker +{ + public BeatmapTracker(TrackerHub hub) + : base(hub) + { + } + + private readonly Bindable beatmap = new Bindable(); + + [BackgroundDependencyLoader] + private void load(Bindable globalBeatmap) + { + this.beatmap.BindTo(globalBeatmap); + } + + protected override void LoadComplete() + { + Logger.Log("DDDLOADCOMPLETE"); + base.LoadComplete(); + + this.beatmap.BindValueChanged(e => + { + this.onBeatmapChanged(e.NewValue); + }, true); + } + + private void onBeatmapChanged(WorkingBeatmap newBeatmap) + { + Hub.GetDataRoot().UpdateBeatmap(newBeatmap); + } +} diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/PPRulesetTracker.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/PPRulesetTracker.cs new file mode 100644 index 0000000..4e0e66b --- /dev/null +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/PPRulesetTracker.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Consts; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.PP; +using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; + +namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Tracker; + +/// +/// 用于更新一些游戏外的PP/游戏模式信息 +/// +public partial class PPRulesetTracker : AbstractTracker +{ + public PPRulesetTracker(TrackerHub hub) + : base(hub) + { + } + + [Resolved] + private Bindable ruleset { get; set; } = null!; + + [Resolved] + private IBindable> mods { get; set; } = null!; + + private Ruleset? rsInstance; + private PerformanceCalculator? performanceCalculator; + + private readonly Bindable working = new Bindable(); + + private CancellationTokenSource? ppCalcTokenSource; + + [BackgroundDependencyLoader] + private void load(Bindable globalWorking) + { + this.working.BindTo(globalWorking); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // Mods产生变动时视为谱面更新并重新计算PP + mods.BindValueChanged(e => + { + var newMods = e.NewValue; + var dataRoot = Hub.GetDataRoot(); + + dataRoot.MenuValues.Mods.AppliedMods = newMods.Count; + + if (newMods.Count >= 1) + { + string str = newMods.Aggregate("", (current, mod) => current + $"{mod.Acronym}"); + dataRoot.MenuValues.Mods.Acronyms = str; + } + else + { + dataRoot.MenuValues.Mods.Acronyms = "NM"; + } + + working.TriggerChange(); + }, true); + + // 游戏模式产生变动时更新相关信息并视为谱面更新重新计算PP + ruleset.BindValueChanged(v => + { + this.rsInstance = v.NewValue.CreateInstance(); + this.performanceCalculator = rsInstance.CreatePerformanceCalculator(); + Hub.GetDataRoot().GameplayValues.Gamemode = LegacyGamemodes.FromRulesetInfo(v.NewValue); + + working.TriggerChange(); + }, true); + + // 谱面产生变动时更新PP信息 + working.BindValueChanged(e => + { + var modsCopy = mods.Value.Where(m => m.Acronym != "CL").Select(m => m.DeepClone()).ToArray(); + + runCalculateMaxPP(e.NewValue, modsCopy) + .ContinueWith(task => + { + if (!task.IsCompleted) return; + + this.Schedule(() => + { + var result = task.GetResultSafely(); + + var dataRoot = Hub.GetDataRoot(); + dataRoot.GameplayValues.pp.MaxThisPlay = dataRoot.GameplayValues.pp.PPIfFc = result.MaxPP; + dataRoot.MenuValues.pp.PPPerfect = result.MaxPP; + }); + }); + }, true); + } + + private struct PerformanceInfo + { + public int MaxPP; + } + + private Task runCalculateMaxPP(WorkingBeatmap workingBeatmap, Mod[] modsCopy) + { + ppCalcTokenSource?.Cancel(); + ppCalcTokenSource = new CancellationTokenSource(); + + return Task.Run(async () => await calculateMaxmiumPerformancePoints(workingBeatmap, modsCopy).ConfigureAwait(false), ppCalcTokenSource.Token); + } + + private Task calculateMaxmiumPerformancePoints(WorkingBeatmap workingBeatmap, Mod[] modsCopy) + { + int maxpp = 0; + + try + { + var score = new ScoreInfo(workingBeatmap.BeatmapInfo, ruleset.Value) + { + Mods = modsCopy + }; + + PerformanceAttributes performanceAttribute; + + if (!rsInstance?.CreateBeatmapConverter(workingBeatmap.Beatmap).CanConvert() ?? true) + performanceAttribute = new PerformanceAttributes(); + else + { + // 使用此rsInstance的performanceCalc + performanceAttribute = this.performanceCalculator != null + ? new Calculator(rsInstance, this.performanceCalculator).CalculatePerfectPerformance(score, workingBeatmap) + : new PerformanceAttributes(); + } + + maxpp = (int)performanceAttribute.Total; + } + catch (Exception e) + { + Logging.LogError(e, "Error occurred while calculating performance point!"); + } + + var info = new PerformanceInfo + { + MaxPP = maxpp + }; + + return Task.FromResult(info); + } +} diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/ScreenTracker.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/ScreenTracker.cs new file mode 100644 index 0000000..a574e32 --- /dev/null +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Tracker/ScreenTracker.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Consts; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Gameplay; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Extensions; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens; +using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Ranking; + +namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Tracker; + +public partial class ScreenTracker : AbstractTracker +{ + public ScreenTracker(TrackerHub hub) + : base(hub) + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + LocateScreenStack(); + + if (screenStack == null && !warningPrinted) + { + Logger.Log("无法定位到OsuScreenStack, 一些功能可能不会正常运作", level: LogLevel.Important); + warningPrinted = true; + } + } + + [Resolved] + private OsuGame game { get; set; } = null!; + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + private static bool warningPrinted; + private static OsuScreenStack? screenStack; + + protected OsuScreenStack? getScreenStack() + { + return screenStack; + } + + public void LocateScreenStack() + { + if (screenStack != null) return; + + const BindingFlags flag = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic; + var screenStackField = game.GetType().GetFields(flag).FirstOrDefault(f => f.FieldType == typeof(OsuScreenStack)); + + if (screenStackField == null) return; + + object? val = screenStackField.GetValue(game); + + if (val is not OsuScreenStack osuScreenStack) return; + + screenStack = osuScreenStack; + + screenStack.ScreenExited += onScreenSwitch; + screenStack.ScreenPushed += onScreenSwitch; + } + + private Screens.Play.Player? playerScreen; + + private ResultsScreen? resultsScreen; + + private CancellationTokenSource? scorePPCalcTokenSource; + + private string playerName = "???"; + + private HealthProcessorAccessor? healthProcessorAccessor; + + private bool isInGame() + { + return playerScreen != null; + } + + private CounterContainer? inGamePPCounter; + + private int performanceThisRun = 0; + + public override void UpdateValues() + { + base.UpdateValues(); + + if (!isInGame()) + { + if (inGamePPCounter == null) return; + + inGamePPCounter.Expire(); + inGamePPCounter = null; + + return; + } + + var scoreInfo = playerScreen!.Score?.ScoreInfo.DeepClone(); + + //scoreInfo in EndlessPlayer would be null for somehow + if (scoreInfo == null) + return; + + playerScreen!.GameplayState.ScoreProcessor.PopulateScore(scoreInfo); + + var dataRoot = Hub.GetDataRoot(); + dataRoot.GameplayValues.FromScore(scoreInfo); + + if (inGamePPCounter == null) + AddInternal(inGamePPCounter = new CounterContainer(playerScreen.GameplayState, playerScreen.GameplayState.ScoreProcessor)); + + double health = 200 * (healthProcessorAccessor?.HealthProcessor.Health.Value ?? 0d); + dataRoot.GameplayValues.Hp.Smooth = dataRoot.GameplayValues.Hp.Normal; + dataRoot.GameplayValues.Hp.Normal = (float)health; + + performanceThisRun = inGamePPCounter.Current.Value; + dataRoot.GameplayValues.pp.Current = this.performanceThisRun; + } + + private void onScreenSwitch(IScreen prevScreen, IScreen nextScreen) + { + scorePPCalcTokenSource?.Cancel(); + + Logger.Log($"🦢 Screen Switch! {prevScreen} -> {nextScreen}"); + + this.playerScreen = null; + this.resultsScreen = null; + + //updateCancellationTokenSource?.Cancel(); + + var dataRoot = Hub.GetDataRoot(); + + //从结算切换到其他页面:重置游玩数据 + if (prevScreen is ResultsScreen || nextScreen is PlayerLoader) + { + performanceThisRun = 0; + dataRoot.GameplayValues.Reset(); + } + + healthProcessorAccessor?.Expire(); + healthProcessorAccessor = null; + + switch (nextScreen) + { + case ResultsScreen results: + this.onResultsScreen(results); + break; + + case Screens.Play.Player player: + this.onPlayerScreen(player); + break; + + default: + dataRoot.MenuValues.OsuState = OsuStates.IDLE; + break; + } + + dataRoot.GameplayValues.Name = playerName = (playerScreen != null) + ? (playerScreen.Score?.ScoreInfo?.User.Username ?? "???") + : (resultsScreen != null ? (resultsScreen.SelectedScore.Value?.User.Username ?? "???") : api.LocalUser.Value.Username); + } + + [Resolved] + private BeatmapDifficultyCache beatmapDifficultyCache { get; set; } = null!; + + private void onResultsScreen(ResultsScreen results) + { + var score = results.SelectedScore.Value; + var dataRoot = Hub.GetDataRoot(); + + dataRoot.MenuValues.OsuState = OsuStates.IDLE; + this.resultsScreen = results; + + if (score == null) return; + + dataRoot.GameplayValues.FromScore(score); + dataRoot.GameplayValues.pp.Current = (int?)score.PP ?? 0; + + if (score.PP.HasValue) + { + dataRoot.GameplayValues.pp.Current = (int)score.PP; + } + else + { + scorePPCalcTokenSource?.Cancel(); + scorePPCalcTokenSource = new CancellationTokenSource(); + + if (score.BeatmapInfo != null) + { + // 参考了 osu.Game.Screens.Ranking.Expanded.Statistics.PerformanceStatistic + Task.Run(async () => + { + double pp = await calculatePPFromScore(score, scorePPCalcTokenSource.Token).ConfigureAwait(false); + this.Schedule(() => dataRoot.GameplayValues.pp.Current = (int)Math.Floor(pp)); + }, scorePPCalcTokenSource.Token); + } + else + { + Logger.Log("score.BeatmapInfo is null?! Not updating pp to gosu..."); + } + } + } + + private async Task calculatePPFromScore(ScoreInfo score, CancellationToken cancellationToken) + { + if (score.BeatmapInfo == null) return 0d; + + var scorePPCalculator = score.Ruleset.CreateInstance().CreatePerformanceCalculator(); + var starDiff = await beatmapDifficultyCache.GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods, cancellationToken).ConfigureAwait(false); + + if (starDiff?.Attributes == null || scorePPCalculator == null) return 0d; + + var result = await scorePPCalculator.CalculateAsync(score, starDiff.Value.Attributes, cancellationToken).ConfigureAwait(false); + return result.Total; + } + + private void onPlayerScreen(Screens.Play.Player player) + { + this.playerScreen = player; + Hub.GetDataRoot().MenuValues.OsuState = OsuStates.PLAYING; + + Logger.Log("PLAYER!"); + + player.DimmableStoryboard?.Add(healthProcessorAccessor = new HealthProcessorAccessor()); + } + + private void updateLeaderboard(IList scoreInfos) + { + var list = new List(); + + int index = 0; + LeaderboardPlayer? ourPlayer = null; + + foreach (var scoreInfo in scoreInfos) + { + var lbp = new LeaderboardPlayer + { + Name = scoreInfo.RealmUser.Username, + Score = (int)scoreInfo.TotalScore, + Combo = scoreInfo.Combo, + MaxCombo = scoreInfo.MaxCombo, + Mods = "NM", + Hit300 = scoreInfo.GetResultsPerfect(), + Hit100 = scoreInfo.GetResultsGreat(), + Hit50 = scoreInfo.Statistics.GetValueOrDefault(HitResult.Meh, 0), + HitMiss = scoreInfo.Statistics.GetValueOrDefault(HitResult.Miss, 0), + Team = 0, + Position = index + }; + + index++; + + if (lbp.Name == playerName) + ourPlayer = lbp; + else + list.Add(lbp); + } + + var dataRoot = Hub.GetDataRoot(); + dataRoot.GameplayValues.Leaderboard.Slots = list.ToArray(); + dataRoot.GameplayValues.Leaderboard.OurPlayer = ourPlayer; + } + + private partial class HealthProcessorAccessor : CompositeDrawable + { + [Resolved] + private HealthProcessor healthProcessor { get; set; } = null!; + + public HealthProcessor HealthProcessor => healthProcessor; + } + + private partial class CounterContainer : CompositeDrawable + { + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + => dependencyContainer = new DependencyContainer(base.CreateChildDependencies(parent)); + + private DependencyContainer dependencyContainer = null!; + private readonly GameplayState gameplayState; + private readonly ScoreProcessor scoreProcessor; + + public CounterContainer(GameplayState gameplayState, ScoreProcessor scoreProcessor) + { + this.gameplayState = gameplayState; + this.scoreProcessor = scoreProcessor; + } + + private readonly PerformancePointsCounter counter = new PerformancePointsCounter + { + AlwaysPresent = true + }; + + [BackgroundDependencyLoader] + private void load(OsuGame game) + { + dependencyContainer.CacheAs(typeof(GameplayState), gameplayState); + dependencyContainer.CacheAs(typeof(ScoreProcessor), scoreProcessor); + + this.AlwaysPresent = true; + this.Alpha = 0; + + this.AddInternal(counter); + } + + public Bindable Current => counter.Current; + } +} diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/TrackerHub.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/TrackerHub.cs new file mode 100644 index 0000000..92f15a1 --- /dev/null +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/TrackerHub.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Tracker; +using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Web; +using osu.Game.Scoring; + +namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory +{ + public partial class TrackerHub : CompositeDrawable + { + private readonly WebSocketLoader wsLoader; + + public TrackerHub(WebSocketLoader game) + { + this.wsLoader = game; + } + + public DataRoot GetDataRoot() + { + return wsLoader.DataRoot; + } + + private readonly List trackers = new List(); + + [BackgroundDependencyLoader] + private void load() + { + Logger.Log("Gosumemoty Compat!"); + + this.Clock = new FramedClock(null, false); + + this.addTrackerRange(new AbstractTracker[] + { + new BeatmapStrainTracker(this), + new PPRulesetTracker(this), + new BeatmapTracker(this), + new ScreenTracker(this) + }); + +#if DEBUG + AddInternal(new RoundedButton + { + Height = 60f, + Width = 60f, + Text = "Dump JSON", + Action = () => + { + UpdateValues(); + + string str = JsonConvert.SerializeObject(wsLoader.DataRoot, Formatting.None, new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Include + }); + + Logger.Log(str); + } + }); +#endif + } + + private void addTracker(AbstractTracker tracker) + { + if (trackers.Contains(tracker)) return; + + trackers.Add(tracker); + this.AddInternal(tracker); + } + + private void addTrackerRange(AbstractTracker[] trackers) + { + foreach (var abstractTracker in trackers) + this.addTracker(abstractTracker); + } + + private void removeTracker(AbstractTracker tracker) + { + trackers.Remove(tracker); + if (this.InternalChildren.Contains(tracker)) + this.RemoveInternal(tracker, true); + } + + [Resolved] + private MusicController musicController { get; set; } = null!; + + [Resolved] + private Bindable workingBeatmap { get; set; } = null!; + + [Resolved] + private OsuGame osuGame { get; set; } = null!; + + private double lastUpdate; + + protected override void Update() + { + base.Update(); + + //更新太快容易卡住网页 + if (Clock.CurrentTime - lastUpdate < 200) return; + + lastUpdate = Clock.CurrentTime; + UpdateValues(); + } + + //region InGame and PP + + [Resolved] + private ScoreManager scoreManager { get; set; } = null!; + + //endregion + + //private CancellationTokenSource? updateCancellationTokenSource; + + public void UpdateValues() + { + this.AlwaysPresent = true; + + var obj = wsLoader.DataRoot; + obj.UpdateTrack(musicController.CurrentTrack); + + foreach (var abstractTracker in trackers) + { + try + { + abstractTracker.UpdateValues(); + } + catch (Exception e) + { + Logging.LogError(e, $"Error occurred while updating tracker {abstractTracker}, disabling this..."); + } + } + + try + { + string str = JsonConvert.SerializeObject(obj, Formatting.None, new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Include + }); + + this.wsLoader.Boardcast(str); + } + catch (Exception e) + { + Logger.Log($"Unable to update osu status to WebSocket: {e.Message}", level: LogLevel.Important); + Logger.Log(e.ToString()); + } + } + } +} diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Updater.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Updater.cs deleted file mode 100644 index a7ed150..0000000 --- a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Updater.cs +++ /dev/null @@ -1,528 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; -using osu.Framework.Screens; -using osu.Framework.Timing; -using osu.Game.Beatmaps; -using osu.Game.Online.API; -using osu.Game.Overlays; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Consts; -using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Data.Gameplay; -using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Extensions; -using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.PP; -using osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Web; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using osu.Game.Screens; -using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Ranking; - -namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory -{ - public partial class Updater : CompositeDrawable - { - private readonly WsLoader wsLoader; - - public Updater(WsLoader game) - { - this.wsLoader = game; - } - - [Resolved] - private OsuGame game { get; set; } = null!; - - public void LocateScreenStack() - { - if (screenStack != null) return; - - const BindingFlags flag = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic; - var screenStackField = game.GetType().GetFields(flag).FirstOrDefault(f => f.FieldType == typeof(OsuScreenStack)); - - if (screenStackField == null) return; - - object? val = screenStackField.GetValue(game); - - if (val is not OsuScreenStack osuScreenStack) return; - - this.screenStack = osuScreenStack; - - screenStack.ScreenExited += onScreenSwitch; - screenStack.ScreenPushed += onScreenSwitch; - } - - [BackgroundDependencyLoader] - private void load() - { - if (screenStack == null) - LocateScreenStack(); - - if (this.screenStack == null) - Logger.Log("无法定位到OsuScreenStack, 一些功能可能不会正常运作", level: LogLevel.Important); - - Logger.Log("Gosumemoty Compat!"); - - this.Clock = new FramedClock(null, false); - - var obj = wsLoader.DataRoot; - - workingBeatmap.BindValueChanged(v => - { - beatmapChangedTime = Clock.CurrentTime; - - if (!isInGame()) updateOverallPerformancePoints(workingBeatmap.Value, v.OldValue != v.NewValue); - obj.UpdateBeatmap(v.NewValue); - }); - - ruleset.BindValueChanged(v => - { - this.rsInstance = v.NewValue.CreateInstance(); - this.performanceCalculator = rsInstance.CreatePerformanceCalculator(); - wsLoader.DataRoot.GameplayValues.Gamemode = LegacyGamemodes.FromRulesetInfo(v.NewValue); - - workingBeatmap.TriggerChange(); - }, true); - - mods.BindValueChanged(v => - { - obj.MenuValues.Mods.UpdateFrom(v.NewValue); - workingBeatmap.TriggerChange(); - }); - - updateOverallPerformancePoints(workingBeatmap.Value); - } - - private OsuScreenStack? screenStack; - - [Resolved] - private IBindable> mods { get; set; } = null!; - - [Resolved] - private MusicController musicController { get; set; } = null!; - - [Resolved] - private Bindable workingBeatmap { get; set; } = null!; - - [Resolved] - private OsuGame osuGame { get; set; } = null!; - - [Resolved] - private Bindable ruleset { get; set; } = null!; - - private double lastUpdate; - - protected override void Update() - { - base.Update(); - - //更新太快容易卡住网页 - if (Clock.CurrentTime - lastUpdate < 200) return; - - lastUpdate = Clock.CurrentTime; - UpdateValues(); - if (scheduleStrainComputes) updatePPStrains(workingBeatmap.Value); - } - - //region InGame and PP - - private CounterContainer? inGamePPCounter; - - private int maxPP = 0; - private int performanceThisRun = 0; - - [Resolved] - private ScoreManager scoreManager { get; set; } = null!; - - [Resolved] - private BeatmapDifficultyCache beatmapDifficultyCache { get; set; } = null!; - - private CancellationTokenSource? overallPPCancellationTokenSource; - - private void updateOverallPerformancePoints(WorkingBeatmap workingBeatmap, bool recalculateStrains = true) - { - // 排除Classic模组(会导致最大pp不准确) - var modsCopy = mods.Value.Where(m => m.Acronym != "CL") - .Select(m => m.DeepClone()).ToArray(); - - overallPPCancellationTokenSource?.Cancel(); - overallPPCancellationTokenSource = new CancellationTokenSource(); - - scheduleStrainComputes = true; - - Task.Run(async () => await updateOverallPPTask(workingBeatmap, modsCopy), overallPPCancellationTokenSource.Token); - } - - private Task updateOverallPPTask(WorkingBeatmap workingBeatmap, Mod[] modsCopy) - { - int maxpp = 0; - var obj = wsLoader.DataRoot; - - try - { - var score = new ScoreInfo(workingBeatmap.BeatmapInfo, ruleset.Value) - { - Mods = modsCopy - }; - - PerformanceAttributes performanceAttribute; - - if (!rsInstance?.CreateBeatmapConverter(workingBeatmap.Beatmap).CanConvert() ?? true) - performanceAttribute = new PerformanceAttributes(); - else - { - // 使用此rsInstance的performanceCalc - performanceAttribute = this.performanceCalculator != null - ? new Calculator(rsInstance, this.performanceCalculator).CalculatePerfectPerformance(score, workingBeatmap) - : new PerformanceAttributes(); - } - - maxpp = (int)performanceAttribute.Total; - } - catch (Exception) - { - } - - this.maxPP = maxpp; - obj.GameplayValues.pp.MaxThisPlay = obj.GameplayValues.pp.PPIfFc = maxpp; - obj.MenuValues.pp.PPPerfect = maxpp; - - return Task.CompletedTask; - } - - private Ruleset? rsInstance; - - private CancellationTokenSource? ppStrainCancellationTokenSource; - - private bool scheduleStrainComputes; - - private double beatmapChangedTime; - - private void updatePPStrains(WorkingBeatmap workingBeatmap) - { - ppStrainCancellationTokenSource?.Cancel(); - ppStrainCancellationTokenSource = new CancellationTokenSource(); - - Task.Run(() => - { - try - { - double length = workingBeatmap.Track.Length; - - //WorkingBeatmap.TrackLoaded: true + WorkingBeatmap.Track.IsLoaded: false -> Track Length: 0 - if (length <= 0) - { - //持续5秒都没有音频,可能已经损坏,清空分布 - //todo: 没有音频的时候使用谱面长度来计算并更新分布和进度 - if (Clock.CurrentTime - beatmapChangedTime >= 5 * 1000) - { - wsLoader.DataRoot.MenuValues.pp.Strains = new[] { 0f }; - return; - } - - scheduleStrainComputes = true; - return; - } - - scheduleStrainComputes = false; - - // 最大分段数和密度缩放 - int maximumSegments = 512; - double segmentScale = 1; - - // 根据歌曲长度分段,每 (1 * segScale) 秒分一段 - int targetSegments = (int)(TimeSpan.FromMilliseconds(length).TotalSeconds * segmentScale); - targetSegments = Math.Min(maximumSegments, targetSegments); - if (targetSegments <= 0) targetSegments = 1; - - // 尝试自动转谱 - var converter = workingBeatmap.BeatmapInfo.Ruleset.CreateInstance().CreateBeatmapConverter(workingBeatmap.Beatmap); - IBeatmap? beatmap = null; - - //Logger.Log($"Track length: {length} ~ Segments {targetSegments} ~ Conv? {converter.CanConvert()} ~ Loaded? {workingBeatmap.Track.IsLoaded} ~ Track? {workingBeatmap.Track}"); - if (converter.CanConvert()) beatmap = converter.Convert(); - var hitObjects = beatmap?.HitObjects ?? new HitObject[] { }; - - //获取每段的音频跨度 - double audioStep = length / targetSegments; - - //Segment -> Count - var segments = new Dictionary(); - - for (int i = 0; i < targetSegments; i++) - { - //此段的音频跨度 - double startTime = i * audioStep; - double endTime = (1 + i) * audioStep; - - //将跨度内的所有物件添加进来 - //o -> [startTime, endTime) - int count = hitObjects.Count(o => o.StartTime < endTime && o.StartTime >= startTime); - - segments.TryAdd(i, count); - } - - //最后添加到DataRoot里 - wsLoader.DataRoot.MenuValues.pp.Strains = segments.Values.ToArray(); - } - catch (Exception e) - { - } - }, ppStrainCancellationTokenSource.Token); - } - - private PerformanceCalculator? performanceCalculator; - - //endregion - - private bool isInGame() - { - return playerScreen != null; - } - - private Screens.Play.Player? playerScreen; - - private ResultsScreen? resultsScreen; - - private CancellationTokenSource? scorePPCalcTokenSource; - - private string playerName = "???"; - - private void onScreenSwitch(IScreen prevScreen, IScreen nextScreen) - { - scorePPCalcTokenSource?.Cancel(); - - this.playerScreen = null; - this.resultsScreen = null; - - //updateCancellationTokenSource?.Cancel(); - - var dataRoot = wsLoader.DataRoot; - - //从结算切换到其他页面:重置游玩数据 - if (prevScreen is ResultsScreen || nextScreen is PlayerLoader) - { - performanceThisRun = 0; - dataRoot.GameplayValues.Reset(); - } - - healthProcessorAccessor?.Expire(); - healthProcessorAccessor = null; - - switch (nextScreen) - { - case ResultsScreen results: - var score = results.SelectedScore.Value; - - if (score != null) - { - dataRoot.GameplayValues.FromScore(score); - dataRoot.GameplayValues.pp.Current = (int?)score.PP ?? 0; - - if (score.PP.HasValue) - { - dataRoot.GameplayValues.pp.Current = (int)score.PP; - } - else - { - scorePPCalcTokenSource?.Cancel(); - scorePPCalcTokenSource = new CancellationTokenSource(); - - if (score.BeatmapInfo == null) - { - Logger.Log("score.BeatmapInfo is null?! Not updating pp to gosu..."); - } - else - { - // 参考了 osu.Game.Screens.Ranking.Expanded.Statistics.PerformanceStatistic - Task.Run(async () => - { - var scorePPCalculator = score.Ruleset.CreateInstance().CreatePerformanceCalculator(); - var starDiff = await beatmapDifficultyCache.GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods).ConfigureAwait(false); - - double pp = 0d; - - if (starDiff?.Attributes != null && scorePPCalculator != null) - { - var result = await scorePPCalculator.CalculateAsync(score, starDiff.Value.Attributes, scorePPCalcTokenSource.Token).ConfigureAwait(false); - pp = result.Total; - } - - this.Schedule(() => dataRoot.GameplayValues.pp.Current = (int)Math.Floor(pp)); - }, scorePPCalcTokenSource.Token); - } - } - - dataRoot.MenuValues.Mods.UpdateFrom(score.Mods.Where(m => m.Acronym != "CL").ToArray()); - } - - dataRoot.MenuValues.OsuState = OsuStates.PLAYING; - this.resultsScreen = results; - break; - - case Screens.Play.Player player: - this.playerScreen = player; - dataRoot.MenuValues.OsuState = OsuStates.PLAYING; - - player.DimmableStoryboard?.Add(healthProcessorAccessor = new HealthProcessorAccessor()); - break; - - default: - dataRoot.MenuValues.OsuState = OsuStates.IDLE; - break; - } - - dataRoot.GameplayValues.Name = playerName = (playerScreen != null) - ? (playerScreen.Score?.ScoreInfo?.User.Username ?? "???") - : (resultsScreen != null ? (resultsScreen.SelectedScore.Value?.User.Username ?? "???") : api.LocalUser.Value.Username); - } - - private void updateLeaderboard(IList scoreInfos) - { - var list = new List(); - - int index = 0; - LeaderboardPlayer? ourPlayer = null; - - foreach (var scoreInfo in scoreInfos) - { - var lbp = new LeaderboardPlayer - { - Name = scoreInfo.RealmUser.Username, - Score = (int)scoreInfo.TotalScore, - Combo = scoreInfo.Combo, - MaxCombo = scoreInfo.MaxCombo, - Mods = "NM", - Hit300 = scoreInfo.GetResultsPerfect(), - Hit100 = scoreInfo.GetResultsGreat(), - Hit50 = scoreInfo.Statistics.GetValueOrDefault(HitResult.Meh, 0), - HitMiss = scoreInfo.Statistics.GetValueOrDefault(HitResult.Miss, 0), - Team = 0, - Position = index - }; - - index++; - - if (lbp.Name == playerName) - ourPlayer = lbp; - else - list.Add(lbp); - } - - wsLoader.DataRoot.GameplayValues.Leaderboard.Slots = list.ToArray(); - wsLoader.DataRoot.GameplayValues.Leaderboard.OurPlayer = ourPlayer; - } - - private HealthProcessorAccessor? healthProcessorAccessor; - - [Resolved] - private IAPIProvider api { get; set; } = null!; - - //private CancellationTokenSource? updateCancellationTokenSource; - - public void UpdateValues() - { - this.AlwaysPresent = true; - - var obj = wsLoader.DataRoot; - obj.UpdateTrack(musicController.CurrentTrack); - - if (isInGame()) - { - var scoreInfo = playerScreen!.Score?.ScoreInfo.DeepClone(); - - //scoreInfo in EndlessPlayer would be null for somehow - if (scoreInfo == null) - return; - - playerScreen!.GameplayState.ScoreProcessor.PopulateScore(scoreInfo); - - obj.GameplayValues.FromScore(scoreInfo); - - if (inGamePPCounter == null) - AddInternal(inGamePPCounter = new CounterContainer(playerScreen.GameplayState, playerScreen.GameplayState.ScoreProcessor)); - - double health = 200 * (healthProcessorAccessor?.HealthProcessor.Health.Value ?? 0d); - obj.GameplayValues.Hp.Smooth = obj.GameplayValues.Hp.Normal; - obj.GameplayValues.Hp.Normal = (float)health; - - performanceThisRun = inGamePPCounter.Current.Value; - obj.GameplayValues.pp.Current = this.performanceThisRun; - } - - if (inGamePPCounter != null && !isInGame()) - { - inGamePPCounter.Expire(); - inGamePPCounter = null; - } - - try - { - string str = JsonConvert.SerializeObject(obj, Formatting.None, new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Include - }); - - this.wsLoader.Boardcast(str); - } - catch (Exception e) - { - Logger.Log($"Unable to update osu status to WebSocket: {e.Message}", level: LogLevel.Important); - Logger.Log(e.ToString()); - } - } - - private partial class HealthProcessorAccessor : CompositeDrawable - { - [Resolved] - private HealthProcessor healthProcessor { get; set; } = null!; - - public HealthProcessor HealthProcessor => healthProcessor; - } - - private partial class CounterContainer : CompositeDrawable - { - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - => dependencyContainer = new DependencyContainer(base.CreateChildDependencies(parent)); - - private DependencyContainer dependencyContainer = null!; - private readonly GameplayState gameplayState; - private readonly ScoreProcessor scoreProcessor; - - public CounterContainer(GameplayState gameplayState, ScoreProcessor scoreProcessor) - { - this.gameplayState = gameplayState; - this.scoreProcessor = scoreProcessor; - } - - private readonly PerformancePointsCounter counter = new PerformancePointsCounter - { - AlwaysPresent = true - }; - - [BackgroundDependencyLoader] - private void load(OsuGame game) - { - dependencyContainer.CacheAs(typeof(GameplayState), gameplayState); - dependencyContainer.CacheAs(typeof(ScoreProcessor), scoreProcessor); - - this.AlwaysPresent = true; - this.Alpha = 0; - - this.AddInternal(counter); - } - - public Bindable Current => counter.Current; - } - } -} diff --git a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Web/WsServer.cs b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Web/WebSocketLoader.cs similarity index 91% rename from osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Web/WsServer.cs rename to osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Web/WebSocketLoader.cs index 474b7d8..bf3a836 100644 --- a/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Web/WsServer.cs +++ b/osu.Game.Rulesets.IGPlayer/Feature/Gosumemory/Web/WebSocketLoader.cs @@ -10,14 +10,20 @@ namespace osu.Game.Rulesets.IGPlayer.Feature.Gosumemory.Web { - public partial class WsLoader : CompositeDrawable + public partial class WebSocketLoader : CompositeDrawable { public readonly DataRoot DataRoot = new DataRoot(); + public WebSocketLoader() + { + AlwaysPresent = true; + } + [BackgroundDependencyLoader] private void load() { - Schedule(() => startServer()); + Logger.Log("WS LOAD!"); + Schedule(startServer); } public void Restart() diff --git a/osu.Game.Rulesets.IGPlayer/Helper/Injectors/OsuGameInjector.cs b/osu.Game.Rulesets.IGPlayer/Helper/Injectors/OsuGameInjector.cs index d673198..a0e7bad 100644 --- a/osu.Game.Rulesets.IGPlayer/Helper/Injectors/OsuGameInjector.cs +++ b/osu.Game.Rulesets.IGPlayer/Helper/Injectors/OsuGameInjector.cs @@ -32,7 +32,7 @@ public static int GetRegisteredSessionHash() public static bool InjectDependencies(Storage storage, OsuGame gameInstance, Scheduler scheduler) { - int sessionHashCode = gameInstance.GetHashCode(); + int sessionHashCode = gameInstance.Toolbar.GetHashCode(); if (currentSessionHash == sessionHashCode) { diff --git a/osu.Game.Rulesets.IGPlayer/IGPlayerRuleset.cs b/osu.Game.Rulesets.IGPlayer/IGPlayerRuleset.cs index 3e736a4..a26f3b3 100644 --- a/osu.Game.Rulesets.IGPlayer/IGPlayerRuleset.cs +++ b/osu.Game.Rulesets.IGPlayer/IGPlayerRuleset.cs @@ -129,7 +129,7 @@ private void load(OsuGame game, Storage storage, IModelImporter } }; - Logger.Log("[IGPlayer] Injecting dependencies..."); + Logger.Log("[IGPlayer] Injecting dependencies.d.."); Logger.Log($"Deps: Game = '{game}' :: Storage = '{storage}' :: Importer = '{beatmapImporter}' :: IAPIProvider = '{api}'"); if (OsuGameInjector.InjectDependencies(storage, game, this.Scheduler)) return;