diff --git a/osu.Desktop/LegacyIpc/LegacyIpcDifficultyCalculationRequest.cs b/osu.Desktop/LegacyIpc/LegacyIpcDifficultyCalculationRequest.cs new file mode 100644 index 0000000000..d6ef390a8f --- /dev/null +++ b/osu.Desktop/LegacyIpc/LegacyIpcDifficultyCalculationRequest.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Desktop.LegacyIpc +{ + /// + /// A difficulty calculation request from the legacy client. + /// + /// + /// Synchronise any changes with osu!stable. + /// + public class LegacyIpcDifficultyCalculationRequest + { + public string BeatmapFile { get; set; } + public int RulesetId { get; set; } + public int Mods { get; set; } + } +} diff --git a/osu.Desktop/LegacyIpc/LegacyIpcDifficultyCalculationResponse.cs b/osu.Desktop/LegacyIpc/LegacyIpcDifficultyCalculationResponse.cs new file mode 100644 index 0000000000..7b9fae5797 --- /dev/null +++ b/osu.Desktop/LegacyIpc/LegacyIpcDifficultyCalculationResponse.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Desktop.LegacyIpc +{ + /// + /// A difficulty calculation response returned to the legacy client. + /// + /// + /// Synchronise any changes with osu!stable. + /// + public class LegacyIpcDifficultyCalculationResponse + { + public double StarRating { get; set; } + } +} diff --git a/osu.Desktop/LegacyIpc/LegacyIpcMessage.cs b/osu.Desktop/LegacyIpc/LegacyIpcMessage.cs new file mode 100644 index 0000000000..6fefae4509 --- /dev/null +++ b/osu.Desktop/LegacyIpc/LegacyIpcMessage.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Platform; + +namespace osu.Desktop.LegacyIpc +{ + /// + /// An that can be used to communicate to and from legacy clients. + /// + /// + /// Synchronise any changes with osu-stable. + /// + public class LegacyIpcMessage : IpcMessage + { + public LegacyIpcMessage() + { + // Types/assemblies are not inter-compatible, so always serialise/deserialise into objects. + base.Type = typeof(object).FullName; + } + + public new string Type => base.Type; // Hide setter. + + public new object Value + { + get => base.Value; + set => base.Value = new Data + { + MessageType = value.GetType().Name, + MessageData = value + }; + } + + public class Data + { + public string MessageType { get; set; } + public object MessageData { get; set; } + } + } +} diff --git a/osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs b/osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs new file mode 100644 index 0000000000..4fca40f22f --- /dev/null +++ b/osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using osu.Framework.Platform; + +namespace osu.Desktop.LegacyIpc +{ + public class LegacyTcpIpcProvider : TcpIpcProvider + { + public new Func MessageReceived; + + public LegacyTcpIpcProvider() + { + base.MessageReceived += msg => + { + try + { + var legacyData = ((JObject)msg.Value).ToObject(); + object value = parseObject((JObject)legacyData.MessageData, legacyData.MessageType); + + object result = MessageReceived?.Invoke(value); + return result != null ? new LegacyIpcMessage { Value = result } : null; + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return null; + }; + } + + public Task SendMessageAsync(object message) => base.SendMessageAsync(new LegacyIpcMessage { Value = message }); + + public async Task SendMessageWithResponseAsync(object message) + { + var result = await base.SendMessageWithResponseAsync(new LegacyIpcMessage { Value = message }).ConfigureAwait(false); + + var legacyData = ((JObject)result.Value).ToObject(); + return (T)parseObject((JObject)legacyData.MessageData, legacyData.MessageType); + } + + public new Task SendMessageAsync(IpcMessage message) => throw new InvalidOperationException("Use typed overloads."); + + public new Task SendMessageWithResponseAsync(IpcMessage message) => throw new InvalidOperationException("Use typed overloads."); + + private object parseObject(JObject value, string type) + { + switch (type) + { + case nameof(LegacyIpcDifficultyCalculationRequest): + return value.ToObject(); + + case nameof(LegacyIpcDifficultyCalculationResponse): + return value.ToObject(); + + default: + throw new ArgumentException($"Unknown type: {type}"); + } + } + } +} diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 6dd6849d78..cb9204f518 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using osu.Desktop.LegacyIpc; using osu.Framework; using osu.Framework.Development; using osu.Framework.Logging; @@ -27,6 +28,8 @@ namespace osu.Desktop { private const string base_game_name = @"osu"; + private static LegacyTcpIpcProvider legacyIpcProvider; + [STAThread] public static void Main(string[] args) { @@ -59,33 +62,26 @@ namespace osu.Desktop gameName = $"{base_game_name}-{clientID}"; break; - case "--osu-stable-difficulty-stream": - while (true) + case "--legacy-ipc-server": + using (legacyIpcProvider = new LegacyTcpIpcProvider()) { - try - { - string beatmapFile = Console.ReadLine() ?? string.Empty; - int rulesetId = int.Parse(Console.ReadLine() ?? string.Empty); - LegacyMods legacyMods = (LegacyMods)int.Parse(Console.ReadLine() ?? string.Empty); - - Ruleset ruleset = rulesetId switch - { - 0 => new OsuRuleset(), - 1 => new TaikoRuleset(), - 2 => new CatchRuleset(), - 3 => new ManiaRuleset(), - _ => throw new ArgumentException("Invalid ruleset id") - }; - - Mod[] mods = ruleset.ConvertFromLegacyMods(legacyMods).ToArray(); - WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(beatmapFile, _ => ruleset); - Console.WriteLine(ruleset.CreateDifficultyCalculator(beatmap).Calculate(mods).StarRating); - } - catch - { - Console.WriteLine(0); - } + legacyIpcProvider.MessageReceived += onLegacyIpcMessageReceived; + legacyIpcProvider.Bind(); + legacyIpcProvider.StartAsync().Wait(); } + + return; + + case "--legacy-ipc-client": + using (legacyIpcProvider = new LegacyTcpIpcProvider()) + { + Console.WriteLine(legacyIpcProvider.SendMessageWithResponseAsync(new LegacyIpcDifficultyCalculationRequest + { + BeatmapFile = "/home/smgi/Downloads/osu_files/129891.osu", + }).Result.StarRating); + } + + return; } } @@ -141,13 +137,39 @@ namespace osu.Desktop return continueExecution; } - } - // Note: Keep in osu.Desktop namespace, or update osu!stable also. - public class DifficultyCalculationMessage - { - public string BeatmapFile { get; set; } - public int RulesetId { get; set; } - public int Mods { get; set; } + private static object onLegacyIpcMessageReceived(object message) + { + switch (message) + { + case LegacyIpcDifficultyCalculationRequest req: + try + { + Ruleset ruleset = req.RulesetId switch + { + 0 => new OsuRuleset(), + 1 => new TaikoRuleset(), + 2 => new CatchRuleset(), + 3 => new ManiaRuleset(), + _ => throw new ArgumentException("Invalid ruleset id") + }; + + Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray(); + WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(req.BeatmapFile, _ => ruleset); + + return new LegacyIpcDifficultyCalculationResponse + { + StarRating = ruleset.CreateDifficultyCalculator(beatmap).Calculate(mods).StarRating + }; + } + catch + { + return new LegacyIpcDifficultyCalculationResponse(); + } + } + + Console.WriteLine("Type not matched."); + return null; + } } }