public static string GetHashFromID(string id) { if (OstHelper.IsOst(id)) { return(id); } Logger.Debug($"Getting hash for {id} from {beatSaverUrl}"); HttpClientHandler httpClientHandler = new HttpClientHandler(); httpClientHandler.AllowAutoRedirect = false; using (var client = new HttpClient(httpClientHandler)) { client.DefaultRequestHeaders.Add("user-agent", SharedConstructs.Name); var response = client.GetAsync($"{beatSaverDownloadByKeyUrl}{id}"); response.Wait(); var result = response.Result.Headers.Location.ToString(); var startIndex = result.LastIndexOf("/") + 1; var length = result.LastIndexOf(".") - startIndex; return(result.Substring(startIndex, length)); } }
public static string GetHashFromLevelId(string levelId) { if (OstHelper.IsOst(levelId)) { return(levelId); } return(Collections.hashForLevelID(levelId).ToLower()); }
private void MockClient_PlaySong(Beatmap map) { if (OstHelper.IsOst(map.LevelId)) { return; } var match = State.Matches.First(x => x.Players.Contains(Self)); otherPlayersInMatch = match.Players.Select(x => x.Id).Union(new Guid[] { match.Leader.Id }).ToArray(); currentlyPlayingMap = map; currentlyPlayingSong = new DownloadedSong(HashFromLevelId(map.LevelId)); currentMaxScore = currentlyPlayingSong.GetMaxScore(currentlyPlayingMap.Characteristic.SerializedName, currentlyPlayingMap.Difficulty); /*using (var libVLC = new LibVLC()) * { * var media = new Media(libVLC, currentlyPlayingSong.GetAudioPath(), FromType.FromPath); * await media.Parse(); * * songTimer = new Timer(); * songTimer.AutoReset = false; * songTimer.Interval = media.Duration; * songTimer.Elapsed += SongTimer_Elapsed; * * noteTimer = new Timer(); * noteTimer.AutoReset = false; * noteTimer.Interval = 500; * noteTimer.Elapsed += NoteTimer_Elapsed; * * noteTimer.Start(); * songTimer.Start(); * }*/ songTimer = new Timer(); songTimer.AutoReset = false; songTimer.Interval = 60 * 3 * 1000; songTimer.Elapsed += SongTimer_Elapsed; noteTimer = new Timer(); noteTimer.AutoReset = false; noteTimer.Interval = 500; noteTimer.Elapsed += NoteTimer_Elapsed; noteTimer.Start(); songTimer.Start(); (Self as Player).PlayState = Player.PlayStates.InGame; var playerUpdated = new Event(); playerUpdated.Type = Event.EventType.PlayerUpdated; playerUpdated.ChangedObject = Self; Send(new Packet(playerUpdated)); }
public SongListItem(GameplayParameters parameters) { this.parameters = parameters; cancellationToken = new CancellationTokenSource(); if (OstHelper.IsOst(parameters.Beatmap.LevelId) || SongUtils.masterLevelList.Any(x => x.levelID == parameters.Beatmap.LevelId)) { downloadState = DownloadState.Complete; level = SongUtils.masterLevelList.First(x => x.levelID == parameters.Beatmap.LevelId); } else { SongDownloader.DownloadSong(parameters.Beatmap.LevelId, true, OnSongDownloaded, OnDownloadProgress); } }
public DownloadedSong(string songHash) { Hash = songHash; if (!OstHelper.IsOst(Hash)) { _infoPath = GetInfoPath(); Characteristics = GetBeatmapCharacteristics(); Name = GetSongName(); } else { Name = OstHelper.GetOstSongNameFromLevelId(Hash); Characteristics = new string[] { "Standard", "OneSaber", "NoArrows", "90Degree", "360Degree" }; } }
protected override void Client_PacketReceived(Packet packet) { base.Client_PacketReceived(packet); if (packet.Type == PacketType.PlaySong) { PlaySong playSong = packet.SpecificPacket as PlaySong; var desiredLevel = SongUtils.masterLevelList.First(x => x.levelID == playSong.GameplayParameters.Beatmap.LevelId); var desiredCharacteristic = desiredLevel.previewDifficultyBeatmapSets.FirstOrDefault(x => x.beatmapCharacteristic.serializedName == playSong.GameplayParameters.Beatmap.Characteristic.SerializedName).beatmapCharacteristic ?? desiredLevel.previewDifficultyBeatmapSets.First().beatmapCharacteristic; var desiredDifficulty = (BeatmapDifficulty)playSong.GameplayParameters.Beatmap.Difficulty; var playerData = Resources.FindObjectsOfTypeAll <PlayerDataModel>().First().playerData; var playerSettings = playerData.playerSpecificSettings; //Override defaults if we have forced options enabled if (playSong.GameplayParameters.PlayerSettings.Options != PlayerOptions.None) { playerSettings = new PlayerSpecificSettings( playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.StaticLights), playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.LeftHanded), playSong.GameplayParameters.PlayerSettings.PlayerHeight, playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.AutoPlayerHeight), playSong.GameplayParameters.PlayerSettings.SfxVolume, playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.ReduceDebris), playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.NoHud), playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.NoFailEffects), playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.AdvancedHud), playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.AutoRestart), playSong.GameplayParameters.PlayerSettings.SaberTrailIntensity, playSong.GameplayParameters.PlayerSettings.NoteJumpStartBeatOffset, playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.HideNoteSpawnEffect), playSong.GameplayParameters.PlayerSettings.Options.HasFlag(PlayerOptions.AdaptiveSfx) ); } var songSpeed = GameplayModifiers.SongSpeed.Normal; if (playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.SlowSong)) { songSpeed = GameplayModifiers.SongSpeed.Slower; } if (playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.FastSong)) { songSpeed = GameplayModifiers.SongSpeed.Faster; } var gameplayModifiers = new GameplayModifiers( playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.DemoNoFail), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.DemoNoObstacles), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.BatteryEnergy) ? GameplayModifiers.EnergyType.Battery : GameplayModifiers.EnergyType.Bar, playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.NoFail), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.InstaFail), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.FailOnClash), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.NoObstacles) ? GameplayModifiers.EnabledObstacleType.NoObstacles : GameplayModifiers.EnabledObstacleType.All, playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.NoBombs), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.FastNotes), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.StrictAngles), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.DisappearingArrows), songSpeed, playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.NoArrows), playSong.GameplayParameters.GameplayModifiers.Options.HasFlag(GameOptions.GhostNotes) ); var colorScheme = playerData.colorSchemesSettings.overrideDefaultColors ? playerData.colorSchemesSettings.GetSelectedColorScheme() : null; //Disable score submission if nofail is on. This is specifically for Hidden Sabers, though it may stay longer if (playSong.DisableScoresaberSubmission) { BS_Utils.Gameplay.ScoreSubmission.DisableSubmission(SharedConstructs.Name); } if (playSong.ShowNormalNotesOnStream) { var customNotes = IPA.Loader.PluginManager.GetPluginFromId("CustomNotes"); if (customNotes != null) { EnableHMDOnly(); } } PlaySong?.Invoke(desiredLevel, desiredCharacteristic, desiredDifficulty, gameplayModifiers, playerSettings, playerData.overrideEnvironmentSettings, colorScheme, playSong.FloatingScoreboard, playSong.StreamSync, playSong.DisablePause, playSong.DisableFail); } else if (packet.Type == PacketType.Command) { Command command = packet.SpecificPacket as Command; if (command.CommandType == Command.CommandTypes.ReturnToMenu) { if (SyncHandler.Instance != null) { ScreenOverlay.Instance.Clear(); } if ((Self as Player).PlayState == Player.PlayStates.InGame) { PlayerUtils.ReturnToMenu(); } } else if (command.CommandType == Command.CommandTypes.ScreenOverlay_ShowPng) { ScreenOverlay.Instance.ShowPng(); } else if (command.CommandType == Command.CommandTypes.DelayTest_Finish) { UnityMainThreadDispatcher.Instance().Enqueue(() => { ScreenOverlay.Instance.Clear(); SyncHandler.Instance.Resume(); SyncHandler.Destroy(); }); } } else if (packet.Type == PacketType.LoadSong) { LoadSong loadSong = packet.SpecificPacket as LoadSong; Action <IBeatmapLevel> SongLoaded = (loadedLevel) => { //Send updated download status (Self as Player).DownloadState = Player.DownloadStates.Downloaded; var playerUpdate = new Event(); playerUpdate.Type = Event.EventType.PlayerUpdated; playerUpdate.ChangedObject = Self; Send(new Packet(playerUpdate)); //Notify any listeners of the client that a song has been loaded LoadedSong?.Invoke(loadedLevel); }; if (OstHelper.IsOst(loadSong.LevelId)) { SongLoaded?.Invoke(SongUtils.masterLevelList.First(x => x.levelID == loadSong.LevelId) as BeatmapLevelSO); } else { if (SongUtils.masterLevelList.Any(x => x.levelID == loadSong.LevelId)) { SongUtils.LoadSong(loadSong.LevelId, SongLoaded); } else { Action <string, bool> loadSongAction = (hash, succeeded) => { if (succeeded) { SongUtils.LoadSong(loadSong.LevelId, SongLoaded); } else { (Self as Player).DownloadState = Player.DownloadStates.DownloadError; var playerUpdated = new Event(); playerUpdated.Type = Event.EventType.PlayerUpdated; playerUpdated.ChangedObject = Self; Send(new Packet(playerUpdated)); } }; (Self as Player).DownloadState = Player.DownloadStates.Downloading; var playerUpdate = new Event(); playerUpdate.Type = Event.EventType.PlayerUpdated; playerUpdate.ChangedObject = Self; Send(new Packet(playerUpdate)); SongDownloader.DownloadSong(loadSong.LevelId, songDownloaded: loadSongAction, downloadProgressChanged: (hash, progress) => Logger.Debug($"DOWNLOAD PROGRESS ({hash}): {progress}"), customHostUrl: loadSong.CustomHostUrl); } } } else if (packet.Type == PacketType.File) { File file = packet.SpecificPacket as File; if (file.Intent == File.Intentions.SetPngToShowWhenTriggered) { var pngBytes = file.Compressed ? CompressionUtils.Decompress(file.Data) : file.Data; ScreenOverlay.Instance.SetPngBytes(pngBytes); } else if (file.Intent == File.Intentions.ShowPngImmediately) { var pngBytes = file.Compressed ? CompressionUtils.Decompress(file.Data) : file.Data; ScreenOverlay.Instance.SetPngBytes(pngBytes); ScreenOverlay.Instance.ShowPng(); } Send(packet.From, new Packet(new Acknowledgement() { PacketId = packet.Id, Type = Acknowledgement.AcknowledgementType.FileDownloaded })); } }
public async Task AddSongAsync([Remainder] string paramString = null) { if (IsAdmin()) { var eventId = paramString.ParseArgs("eventId"); var songId = paramString.ParseArgs("song"); if (string.IsNullOrEmpty(eventId) || string.IsNullOrEmpty(songId)) { await ReplyAsync(embed : ("Usage: `addSong -eventId \"[event id]\" -song [song link]`\n" + "To find event ids, please run `listEvents`\n" + "Optional parameters: `-difficulty [difficulty]`, `-characteristic [characteristic]` (example: `-characteristic onesaber`), `-[modifier]` (example: `-nofail`)").ErrorEmbed()); return; } //Parse the difficulty input, either as an int or a string BeatmapDifficulty difficulty = BeatmapDifficulty.ExpertPlus; string difficultyArg = paramString.ParseArgs("difficulty"); if (difficultyArg != null) { //If the enum conversion doesn't succeed, try it as an int if (!Enum.TryParse(difficultyArg, true, out difficulty)) { await ReplyAsync(embed : "Could not parse difficulty parameter".ErrorEmbed()); return; } } string characteristic = paramString.ParseArgs("characteristic"); characteristic = characteristic ?? "Standard"; GameOptions gameOptions = GameOptions.None; PlayerOptions playerOptions = PlayerOptions.None; //Load up the GameOptions and PlayerOptions foreach (GameOptions o in Enum.GetValues(typeof(GameOptions))) { if (paramString.ParseArgs(o.ToString()) == "true") { gameOptions = (gameOptions | o); } } foreach (PlayerOptions o in Enum.GetValues(typeof(PlayerOptions))) { if (paramString.ParseArgs(o.ToString()) == "true") { playerOptions = (playerOptions | o); } } //Sanitize input if (songId.StartsWith("https://beatsaver.com/") || songId.StartsWith("https://bsaber.com/")) { //Strip off the trailing slash if there is one if (songId.EndsWith("/")) { songId = songId.Substring(0, songId.Length - 1); } //Strip off the beginning of the url to leave the id songId = songId.Substring(songId.LastIndexOf("/") + 1); } if (songId.Contains("&")) { songId = songId.Substring(0, songId.IndexOf("&")); } var server = ServerService.GetServer(); if (server == null) { await ReplyAsync(embed : "The Server is not running, so we can't can't add songs to it".ErrorEmbed()); } else { //Get the hash for the song var hash = BeatSaverDownloader.GetHashFromID(songId); var knownPairs = await HostScraper.ScrapeHosts(server.State.KnownHosts, $"{server.CoreServer.Address}:{server.CoreServer.Port}", 0); var targetPair = knownPairs.FirstOrDefault(x => x.Value.Events.Any(y => y.EventId.ToString() == eventId)); var targetEvent = targetPair.Value.Events.FirstOrDefault(x => x.EventId.ToString() == eventId); var songPool = targetEvent.QualifierMaps.ToList(); if (OstHelper.IsOst(hash)) { if (!SongExists(songPool, hash, characteristic, (int)difficulty, (int)gameOptions, (int)playerOptions)) { GameplayParameters parameters = new GameplayParameters { Beatmap = new Beatmap { Name = OstHelper.GetOstSongNameFromLevelId(hash), LevelId = hash, Characteristic = new Characteristic { SerializedName = characteristic }, Difficulty = difficulty }, GameplayModifiers = new GameplayModifiers { Options = gameOptions }, PlayerSettings = new PlayerSpecificSettings { Options = playerOptions } }; songPool.Add(parameters); targetEvent.QualifierMaps = songPool.ToArray(); var response = await server.SendUpdateQualifierEvent(targetPair.Key, targetEvent); if (response.Type == Response.ResponseType.Success) { await ReplyAsync(embed : ($"Added: {parameters.Beatmap.Name} ({difficulty}) ({characteristic})" + $"{(gameOptions != GameOptions.None ? $" with game options: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" with player options: ({playerOptions})" : "!")}").SuccessEmbed()); } else if (response.Type == Response.ResponseType.Fail) { await ReplyAsync(embed : response.Message.ErrorEmbed()); } } else { await ReplyAsync(embed : "Song is already active in the database".ErrorEmbed()); } } else { BeatSaverDownloader.DownloadSong(hash, async(songPath) => { if (songPath != null) { DownloadedSong song = new DownloadedSong(hash); string songName = song.Name; if (!song.GetBeatmapDifficulties(characteristic).Contains(difficulty)) { BeatmapDifficulty nextBestDifficulty = song.GetClosestDifficultyPreferLower(difficulty); if (SongExists(songPool, hash, characteristic, (int)nextBestDifficulty, (int)gameOptions, (int)playerOptions)) { await ReplyAsync(embed: $"{songName} doesn't have {difficulty}, and {nextBestDifficulty} is already in the event".ErrorEmbed()); } else { GameplayParameters parameters = new GameplayParameters { Beatmap = new Beatmap { Name = songName, LevelId = $"custom_level_{hash.ToUpper()}", Characteristic = new Characteristic { SerializedName = characteristic }, Difficulty = nextBestDifficulty }, GameplayModifiers = new GameplayModifiers { Options = gameOptions }, PlayerSettings = new PlayerSpecificSettings { Options = playerOptions } }; songPool.Add(parameters); targetEvent.QualifierMaps = songPool.ToArray(); var response = await server.SendUpdateQualifierEvent(targetPair.Key, targetEvent); if (response.Type == Response.ResponseType.Success) { await ReplyAsync(embed: ($"{songName} doesn't have {difficulty}, using {nextBestDifficulty} instead.\n" + $"Added to the song list" + $"{(gameOptions != GameOptions.None ? $" with game options: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" with player options: ({playerOptions})" : "!")}").SuccessEmbed()); } else if (response.Type == Response.ResponseType.Fail) { await ReplyAsync(embed: response.Message.ErrorEmbed()); } } } else { GameplayParameters parameters = new GameplayParameters { Beatmap = new Beatmap { Name = songName, LevelId = $"custom_level_{hash.ToUpper()}", Characteristic = new Characteristic { SerializedName = characteristic }, Difficulty = difficulty }, GameplayModifiers = new GameplayModifiers { Options = gameOptions }, PlayerSettings = new PlayerSpecificSettings { Options = playerOptions } }; songPool.Add(parameters); targetEvent.QualifierMaps = songPool.ToArray(); var response = await server.SendUpdateQualifierEvent(targetPair.Key, targetEvent); if (response.Type == Response.ResponseType.Success) { await ReplyAsync(embed: ($"{songName} ({difficulty}) ({characteristic}) downloaded and added to song list" + $"{(gameOptions != GameOptions.None ? $" with game options: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" with player options: ({playerOptions})" : "!")}").SuccessEmbed()); } else if (response.Type == Response.ResponseType.Fail) { await ReplyAsync(embed: response.Message.ErrorEmbed()); } } } else { await ReplyAsync(embed: "Could not download song.".ErrorEmbed()); } }); } } } else { await ReplyAsync(embed : "You do not have sufficient permissions to use this command".ErrorEmbed()); } }
public async Task AddSongAsync([Remainder] string paramString = null) { if (IsAdmin()) { var eventId = paramString.ParseArgs("赛事"); var songId = paramString.ParseArgs("歌曲"); if (string.IsNullOrEmpty(eventId) || string.IsNullOrEmpty(songId)) { await ReplyAsync(embed : ("用法: `添加歌曲 -赛事 \"[赛事ID]\" -歌曲 [链接/key]`\n" + "赛事ID可以通过`赛事列表`命令查找\n" + "可选参数: `-难度 [Easy/Normal/Hard/Expert/ExpertPlus]`, `-谱型 [例如: onesaber]`, `-[修改项]` (例如: 不死模式为 `-nofail`)").ErrorEmbed()); return; } //Parse the difficulty input, either as an int or a string BeatmapDifficulty difficulty = BeatmapDifficulty.ExpertPlus; string difficultyArg = paramString.ParseArgs("难度"); if (difficultyArg != null) { //If the enum conversion doesn't succeed, try it as an int if (!Enum.TryParse(difficultyArg, true, out difficulty)) { await ReplyAsync(embed : "请检查难度参数".ErrorEmbed()); return; } } string characteristic = paramString.ParseArgs("谱型"); characteristic = characteristic ?? "Standard"; GameOptions gameOptions = GameOptions.None; PlayerOptions playerOptions = PlayerOptions.None; //Load up the GameOptions and PlayerOptions foreach (GameOptions o in Enum.GetValues(typeof(GameOptions))) { if (paramString.ParseArgs(o.ToString()) == "true") { gameOptions = (gameOptions | o); } } foreach (PlayerOptions o in Enum.GetValues(typeof(PlayerOptions))) { if (paramString.ParseArgs(o.ToString()) == "true") { playerOptions = (playerOptions | o); } } //Sanitize input if (songId.StartsWith("https://beatsaver.com/") || songId.StartsWith("https://bsaber.com/")) { //Strip off the trailing slash if there is one if (songId.EndsWith("/")) { songId = songId.Substring(0, songId.Length - 1); } //Strip off the beginning of the url to leave the id songId = songId.Substring(songId.LastIndexOf("/") + 1); } if (songId.Contains("&")) { songId = songId.Substring(0, songId.IndexOf("&")); } var server = ServerService.GetServer(); if (server == null) { await ReplyAsync(embed : "服务器不在线,所以不能添加歌曲".ErrorEmbed()); } else { //Get the hash for the song var hash = BeatSaverDownloader.GetHashFromID(songId); var knownPairs = await HostScraper.ScrapeHosts(server.State.KnownHosts, $"{server.CoreServer.Address}:{server.CoreServer.Port}", 0); var targetPair = knownPairs.FirstOrDefault(x => x.Value.Events.Any(y => y.EventId.ToString() == eventId)); var targetEvent = targetPair.Value.Events.FirstOrDefault(x => x.EventId.ToString() == eventId); var songPool = targetEvent.QualifierMaps.ToList(); if (OstHelper.IsOst(hash)) { if (!SongExists(songPool, hash, characteristic, (int)difficulty, (int)gameOptions, (int)playerOptions)) { GameplayParameters parameters = new GameplayParameters { Beatmap = new Beatmap { Name = OstHelper.GetOstSongNameFromLevelId(hash), LevelId = hash, Characteristic = new Characteristic { SerializedName = characteristic }, Difficulty = difficulty }, GameplayModifiers = new GameplayModifiers { Options = gameOptions }, PlayerSettings = new PlayerSpecificSettings { Options = playerOptions } }; songPool.Add(parameters); targetEvent.QualifierMaps = songPool.ToArray(); var response = await server.SendUpdateQualifierEvent(targetPair.Key, targetEvent); if (response.Type == Response.ResponseType.Success) { await ReplyAsync(embed : ($"已添加: {parameters.Beatmap.Name} ({difficulty}) ({characteristic})" + $"{(gameOptions != GameOptions.None ? $" 附加游戏参数: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" 附加玩家参数: ({playerOptions})" : "!")}").SuccessEmbed()); } else if (response.Type == Response.ResponseType.Fail) { await ReplyAsync(embed : response.Message.ErrorEmbed()); } } else { await ReplyAsync(embed : "歌曲已存在于数据库".ErrorEmbed()); } } else { var songInfo = await BeatSaverDownloader.GetSongInfo(songId); string songName = songInfo.Name; if (!songInfo.HasDifficulty(characteristic, difficulty)) { BeatmapDifficulty nextBestDifficulty = songInfo.GetClosestDifficultyPreferLower(characteristic, difficulty); if (SongExists(songPool, hash, characteristic, (int)nextBestDifficulty, (int)gameOptions, (int)playerOptions)) { await ReplyAsync(embed : $"{songName} 不存在 {difficulty} 难度, 而且 {nextBestDifficulty} 已存在于赛事".ErrorEmbed()); } else { GameplayParameters parameters = new GameplayParameters { Beatmap = new Beatmap { Name = songName, LevelId = $"custom_level_{hash.ToUpper()}", Characteristic = new Characteristic { SerializedName = characteristic }, Difficulty = nextBestDifficulty }, GameplayModifiers = new GameplayModifiers { Options = gameOptions }, PlayerSettings = new PlayerSpecificSettings { Options = playerOptions } }; songPool.Add(parameters); targetEvent.QualifierMaps = songPool.ToArray(); var response = await server.SendUpdateQualifierEvent(targetPair.Key, targetEvent); if (response.Type == Response.ResponseType.Success) { await ReplyAsync(embed : ($"{songName} 不存在 {difficulty} 难度, 使用 {nextBestDifficulty} 难度代替。\n" + $"已添加至歌曲列表" + $"{(gameOptions != GameOptions.None ? $" 附加游戏参数: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" 附加玩家参数: ({playerOptions})" : "!")}").SuccessEmbed()); } else if (response.Type == Response.ResponseType.Fail) { await ReplyAsync(embed : response.Message.ErrorEmbed()); } } } else { GameplayParameters parameters = new GameplayParameters { Beatmap = new Beatmap { Name = songName, LevelId = $"custom_level_{hash.ToUpper()}", Characteristic = new Characteristic { SerializedName = characteristic }, Difficulty = difficulty }, GameplayModifiers = new GameplayModifiers { Options = gameOptions }, PlayerSettings = new PlayerSpecificSettings { Options = playerOptions } }; songPool.Add(parameters); targetEvent.QualifierMaps = songPool.ToArray(); var response = await server.SendUpdateQualifierEvent(targetPair.Key, targetEvent); if (response.Type == Response.ResponseType.Success) { await ReplyAsync(embed : ($"{songName} ({difficulty}) ({characteristic}) 已下载并添加至歌曲列表" + $"{(gameOptions != GameOptions.None ? $" 附加游戏参数: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" 附加玩家参数: ({playerOptions})" : "!")}").SuccessEmbed()); } else if (response.Type == Response.ResponseType.Fail) { await ReplyAsync(embed : response.Message.ErrorEmbed()); } } } } } else { await ReplyAsync(embed : "你没有足够的权限使用该命令".ErrorEmbed()); } }
public static void StartHttpServer() { var route_config = new List <Route>() { new Route { Name = "Score Receiver", UrlRegex = @"^/submit/$", Method = "POST", Callable = (HttpRequest request) => { try { //Get JSON object from request content JSONNode node = JSON.Parse(WebUtility.UrlDecode(request.Content)); //Get Score object from JSON Score s = Score.FromString(node["pb"]); if (RSA.SignScore(Convert.ToUInt64(s.UserId), s.SongHash, s.Difficulty, s.Characteristic, s.FullCombo, s.Score_, s.PlayerOptions, s.GameOptions) == s.Signed && Song.Exists(s.SongHash, (LevelDifficulty)s.Difficulty, s.Characteristic, true) && !new Song(s.SongHash, (LevelDifficulty)s.Difficulty, s.Characteristic).Old&& Player.Exists(s.UserId) && Player.IsRegistered(s.UserId)) { Logger.Info($"RECEIVED VALID SCORE: {s.Score_} FOR {new Player(s.UserId).DiscordName} {s.SongHash} {s.Difficulty} {s.Characteristic}"); } else { Logger.Error($"RECEIVED INVALID SCORE {s.Score_} FROM {new Player(s.UserId).DiscordName} FOR {s.UserId} {s.SongHash} {s.Difficulty} {s.Characteristic}"); return(new HttpResponse() { ReasonPhrase = "Bad Request", StatusCode = "400" }); } #if BEATKHANA PostToBeatKhana(s.UserId, s.SongHash, s.Score_.ToString(), new EventServer.BeatSaver.Song(s.SongHash).GetMaxScore(s.Characteristic, (LevelDifficulty)s.Difficulty).ToString()); #endif Database.Score oldScore = null; if (Database.Score.Exists(s.SongHash, s.UserId, (LevelDifficulty)s.Difficulty, s.Characteristic)) { oldScore = new Database.Score(s.SongHash, s.UserId, (LevelDifficulty)s.Difficulty, s.Characteristic); } if (oldScore == null ^ (oldScore != null && oldScore.GetScore() < s.Score_)) { Player player = new Player(s.UserId); long oldScoreNumber = oldScore == null ? 0 : oldScore.GetScore(); oldScore?.SetOld(); //Player stats if (oldScoreNumber > 0) { player.IncrementPersonalBestsBeaten(); } else { player.IncrementSongsPlayed(); } player.IncrementSongsPlayed(); player.TotalScore += s.Score_ - oldScoreNumber; //Increment total score only by the amount the score has increased Database.Score newScore = new Database.Score(s.SongHash, s.UserId, (LevelDifficulty)s.Difficulty, s.Characteristic); newScore.SetScore(s.Score_, s.FullCombo); //Only send message if player is registered if (Player.Exists(s.UserId)) { CommunityBot.SendToScoreChannel($"User \"{player.DiscordMention}\" has scored {s.Score_} on {new Song(s.SongHash, (LevelDifficulty)s.Difficulty, s.Characteristic).SongName} ({(LevelDifficulty)s.Difficulty}) ({s.Characteristic})!"); } } return(new HttpResponse() { ReasonPhrase = "OK", StatusCode = "200" }); } catch (Exception e) { Logger.Error($"{e}"); } return(new HttpResponse() { ReasonPhrase = "Bad Request", StatusCode = "400" }); } }, new Route { Name = "Rank Receiver", UrlRegex = @"^/requestrank/$", Method = "POST", Callable = (HttpRequest request) => { try { //Get JSON object from request content JSONNode node = JSON.Parse(WebUtility.UrlDecode(request.Content)); //Get Score object from JSON RankRequest r = RankRequest.FromString(node["pb"]); if (RSA.SignRankRequest(Convert.ToUInt64(r.UserId), r.RequestedTeamId, r.InitialAssignment) == r.Signed && Player.Exists(r.UserId) && Player.IsRegistered(r.UserId) && Team.Exists(r.RequestedTeamId)) { Logger.Info($"RECEIVED VALID RANK REQUEST: {r.RequestedTeamId} FOR {r.UserId} {r.RequestedTeamId} {r.InitialAssignment}"); var player = new Player(r.UserId); var team = new Team(r.RequestedTeamId); //The rank up system will ignore requets where the player doesn't have the required tokens, //or is requesting a rank higher than the one above their current rank (if it's not an inital rank assignment) if (r.InitialAssignment && player.Team == "-1") { CommunityBot.ChangeTeam(player, team); } else if (player.Team != "gold") { var oldTeam = new Team(player.Team); var nextTeam = new Team(oldTeam.NextPromotion); if (player.Tokens >= nextTeam.RequiredTokens) { CommunityBot.ChangeTeam(player, nextTeam); } } else if (player.Team == "gold" && player.Tokens >= new Team("blue").RequiredTokens) //Player is submitting for Blue { new Vote(player.UserId, r.OstScoreInfo); } else { return(new HttpResponse() { ReasonPhrase = "Bad Request", StatusCode = "400" }); } return(new HttpResponse() { ReasonPhrase = "OK", StatusCode = "200" }); } else { Logger.Warning($"RECEIVED INVALID RANK REQUEST {r.RequestedTeamId} FROM {r.UserId}"); } } catch (Exception e) { Logger.Error($"{e}"); } return(new HttpResponse() { ReasonPhrase = "Bad Request", StatusCode = "400" }); } }, new Route { Name = "Song Getter", UrlRegex = @"^/songs/", Method = "GET", Callable = (HttpRequest request) => { string[] requestData = request.Path.Substring(1).Split('/'); string userId = requestData[1]; userId = Regex.Replace(userId, "[^a-zA-Z0-9- ]", ""); //If the userid is null, we'll give them e+ everything. It's likely the leaderboard site if (!string.IsNullOrEmpty(userId) && (!Player.Exists(userId) || !Player.IsRegistered(userId))) { return(new HttpResponse() { ReasonPhrase = "Bad Request", StatusCode = "400" }); } JSONNode json = new JSONObject(); List <SongConstruct> songs = GetActiveSongs(); songs.ForEach(x => { //If the request doesn't include a player id, we can't be sure which difficulty the player is supposed to play, so we'll just leave //the difficulty set to Auto because it's probably the website asking if (x.Difficulty == LevelDifficulty.Auto && !string.IsNullOrEmpty(userId)) { if (OstHelper.IsOst(x.SongHash)) { x.Difficulty = new Player(userId).GetPreferredDifficulty(OstHelper.IsOst(x.SongHash)); } else { var preferredDifficulty = new Player(userId).GetPreferredDifficulty(OstHelper.IsOst(x.SongHash)); x.Difficulty = new BeatSaver.Song(x.SongHash).GetClosestDifficultyPreferLower(preferredDifficulty); } } var item = new JSONObject(); item["songName"] = x.Name; item["songHash"] = x.SongHash; item["difficulty"] = (int)x.Difficulty; item["gameOptions"] = (int)x.GameOptions; item["playerOptions"] = (int)x.PlayerOptions; item["characteristic"] = x.Characteristic; json.Add(x.SongHash + (int)x.Difficulty, item); }); return(new HttpResponse() { ContentAsUTF8 = json.ToString(), ReasonPhrase = "OK", StatusCode = "200" }); } }, new Route { Name = "Team Getter", UrlRegex = @"^/teams/", Method = "GET", Callable = (HttpRequest request) => { string source = request.Path.Substring(request.Path.LastIndexOf("/") + 1); if (!string.IsNullOrEmpty(source)) { Logger.Success("Team Request from Duo's Site"); } JSONNode json = new JSONObject(); List <Team> teams = GetAllTeams(); foreach (var team in teams) { var item = new JSONObject(); item["captainId"] = team.Captain; item["teamName"] = team.TeamName; item["color"] = team.Color; item["requiredTokens"] = team.RequiredTokens; item["nextPromotion"] = team.NextPromotion; json.Add(team.TeamId, item); } return(new HttpResponse() { ContentAsUTF8 = json.ToString(), ReasonPhrase = "OK", StatusCode = "200" }); } }, new Route { Name = "Player Stats Getter", UrlRegex = @"^/playerstats/", Method = "GET", Callable = (HttpRequest request) => { string userId = request.Path.Substring(request.Path.LastIndexOf("/") + 1); userId = Regex.Replace(userId, "[^a-zA-Z0-9]", ""); if (!(userId.Length == 17 || userId.Length == 16 || userId.Length == 15)) //17 = vive, 16 = oculus, 15 = sfkwww { Logger.Error($"Invalid id size: {userId.Length}"); return(new HttpResponse() { ReasonPhrase = "Bad Request", StatusCode = "400" }); } JSONNode json = new JSONObject(); if (Player.Exists(userId) && Player.IsRegistered(userId)) { #if QUALIFIER var isRegistered = true; if (userId != "76561198063268251") { var registrationCheck = IsPlayerRegisteredInBSWC(userId); registrationCheck.Wait(); isRegistered = registrationCheck.Result; } #else var isRegistered = true; #endif if ((Config.ServerFlags.HasFlag(ServerFlags.Teams) && new Player(userId).Team == "-1") || !isRegistered) { json["message"] = "Please be sure you're assigned to a team before playing"; } else { Player player = new Player(userId); json["version"] = VersionCode; json["team"] = player.Team; json["tokens"] = player.Tokens; json["serverSettings"] = (int)Config.ServerFlags; } } else if (Player.Exists(userId) && !Player.IsRegistered(userId)) { json["message"] = "You have been banned from this event."; } else { json["message"] = "Please register with the bot before playing. Check #plugin-information for more info. Sorry for the inconvenience."; } return(new HttpResponse() { ContentAsUTF8 = json.ToString(), ReasonPhrase = "OK", StatusCode = "200" }); } }, new Route { Name = "Song Leaderboard Getter", UrlRegex = @"^/leaderboards/", Method = "GET", Callable = (HttpRequest request) => { string[] requestData = request.Path.Substring(1).Split('/'); string songHash = requestData[1]; JSONNode json = new JSONObject(); #if HIDDENNOTES return(new HttpResponse() { ContentAsUTF8 = json.ToString(), ReasonPhrase = "OK", StatusCode = "200" }); #endif if (songHash == "all") { int take = 10; string teamId = "-1"; if (requestData.Length > 2) { take = Convert.ToInt32(requestData[2]); } if (requestData.Length > 3) { teamId = requestData[3]; } List <SongConstruct> songs = GetAllScores(teamId); songs.ToList().ForEach(x => { JSONNode songNode = new JSONObject(); songNode["levelId"] = x.SongHash; songNode["songName"] = x.Name; songNode["difficulty"] = (int)x.Difficulty; songNode["characteristic"] = x.Characteristic; songNode["scores"] = new JSONObject(); int place = 1; x.Scores.Take(take).ToList().ForEach(y => { JSONNode scoreNode = new JSONObject(); scoreNode["score"] = y.Score; scoreNode["player"] = new Player(y.UserId).DiscordName; scoreNode["place"] = place; scoreNode["fullCombo"] = y.FullCombo ? "true" : "false"; scoreNode["userId"] = y.UserId; scoreNode["team"] = y.TeamId; songNode["scores"].Add(Convert.ToString(place++), scoreNode); }); json.Add(x.SongHash + ":" + (int)x.Difficulty, songNode); }); } else { int take = 10; int difficulty = Convert.ToInt32(requestData[2]); string characteristic = requestData[3]; string teamId = requestData[4]; if (requestData.Length > 5) { take = Convert.ToInt32(requestData[5]); } songHash = Regex.Replace(songHash, "[^a-zA-Z0-9- ]", ""); teamId = Regex.Replace(teamId, "[^a-zA-Z0-9- ]", ""); SongConstruct songConstruct = new SongConstruct() { SongHash = songHash, Difficulty = (LevelDifficulty)difficulty, Characteristic = characteristic }; if (!Song.Exists(songHash, (LevelDifficulty)difficulty, characteristic, true)) { Logger.Error($"Song doesn't exist for leaderboards: {songHash}"); return(new HttpResponse() { ReasonPhrase = "Bad Request", StatusCode = "400" }); } List <ScoreConstruct> scores = GetScoresForSong(songConstruct, teamId); int place = 1; scores.Take(take).ToList().ForEach(x => { JSONNode node = new JSONObject(); node["score"] = x.Score; node["player"] = new Player(x.UserId).DiscordName; node["place"] = place; node["fullCombo"] = x.FullCombo ? "true" : "false"; node["userId"] = x.UserId; node["team"] = x.TeamId; json.Add(Convert.ToString(place++), node); }); } return(new HttpResponse() { ContentAsUTF8 = json.ToString(), ReasonPhrase = "OK", StatusCode = "200" }); } } }; Logger.Info($"HTTP Server listening on {Dns.GetHostName()}"); #if (TEAMSABER) int port = 3707; #elif (DISCORDCOMMUNITY) int port = 3703; #elif (TRUEACCURACY) int port = 3711; #elif (ASIAVR) int port = 3709; #elif (QUALIFIER) int port = 3713; #elif (BTH) int port = 3715; #elif (FENCINGQUALIFIER) int port = 3717; #elif (BEATKHANA) int port = 3719; #elif (HIDDENNOTES) int port = 3721; #elif BETA int port = 3704; //My vhost is set up to direct to 3708 when the /api-beta/ route is followed #endif HttpServer httpServer = new HttpServer(port, route_config); httpServer.Listen(); }
//GET the songs from the server, then start the Download coroutine to download and display them //TODO: Time complexity here is a mess. private static IEnumerator GetSongs(SongListViewController slvc, string userId, Action <List <Song> > songsGottenCallback = null) { UnityWebRequest www = UnityWebRequest.Get($"{discordCommunityApi}/songs/{userId}/"); Logger.Debug($"REQUESTING SONGS: {discordCommunityApi}/songs/{userId}/"); www.timeout = 30; yield return(www.SendWebRequest()); if (www.isNetworkError || www.isHttpError) { Logger.Error($"Error getting songs: {www.error}"); slvc.ErrorHappened($"Error getting songs: {www.error}"); } else { List <Song> songs = new List <Song>(); try { //Get the list of songs to download, and map out the song ids to the corresponding gamemodes var node = JSON.Parse(www.downloadHandler.text); foreach (var id in node) { var newSong = new Song() { Hash = id.Value["songHash"], SongName = id.Value["songName"], GameOptions = (GameOptions)Convert.ToInt32(id.Value["gameOptions"].ToString()), PlayerOptions = (PlayerOptions)Convert.ToInt32(id.Value["playerOptions"].ToString()), Difficulty = (LevelDifficulty)Convert.ToInt32(id.Value["difficulty"].ToString()), Characteristic = id.Value["characteristic"] }; Logger.Debug($"ADDING SONG: {newSong.SongName} {newSong.Difficulty} {newSong.Characteristic}"); songs.Add(newSong); } } catch (Exception e) { Logger.Error($"Error parsing getsong data: {e}"); slvc.ErrorHappened($"Error parsing getsong data: {e}"); yield break; } //If we got songs, filter them as neccessary then download any we don't have List <Song> availableSongs = new List <Song>(); //Filter out songs we already have and OSTS IEnumerable <Song> osts = songs.Where(x => OstHelper.IsOst(x.Hash)); IEnumerable <Song> alreadyHave = songs.Where(x => Collections.songWithHashPresent(x.Hash.ToUpper())); //Loads a level from a song instance, populates the Beatmap property and adds to the available list Action <Song> loadLevel = (song) => { if (Collections.songWithHashPresent(song.Hash.ToUpper())) { var levelId = Collections.levelIDsForHash(song.Hash).First(); var customPreview = Loader.CustomLevelsCollection.beatmapLevels.First(x => x.levelID == levelId) as CustomPreviewBeatmapLevel; song.PreviewBeatmap = customPreview; //TODO: Figure out proper async-ness here /*var beatmapLevelResult = Task.Run(async () => await SongUtils.GetLevelFromPreview(customPreview)); * beatmapLevelResult.Wait(); * * //TODO: add characteristic name field to the song data stored in the server * song.Beatmap = SongUtils.GetClosestDifficultyPreferLower(beatmapLevelResult.Result?.beatmapLevel, (BeatmapDifficulty)song.Difficulty); * availableSongs.Add(song);*/ } else { slvc.ErrorHappened($"Could not load level {song.SongName}"); } }; //Load the preview levels for what we have foreach (Song song in osts) { foreach (IBeatmapLevelPack pack in Loader.BeatmapLevelsModelSO.allLoadedBeatmapLevelPackCollection.beatmapLevelPacks) { var foundLevel = pack.beatmapLevelCollection.beatmapLevels.FirstOrDefault(y => y.levelID.ToLower() == song.Hash.ToLower()); if (foundLevel != null) { song.PreviewBeatmap = foundLevel; } } } foreach (Song song in alreadyHave) { loadLevel(song); } //Of what we already have, add the Levels to the availableSongs list availableSongs.AddRange(alreadyHave); availableSongs.AddRange(osts); //Remove what we already have from the download queue songs.RemoveAll(x => availableSongs.Contains(x)); //Don't redownload //Download the things we don't have, or if we have everything, show the menu if (songs.Count > 0) { List <IEnumerator> downloadCoroutines = new List <IEnumerator>(); songs.ForEach(x => { downloadCoroutines.Add(DownloadSongs(x.Hash, slvc)); }); //Wait for the all downloads to finish yield return(SharedCoroutineStarter.instance.StartCoroutine(new ParallelCoroutine().ExecuteCoroutines(downloadCoroutines.ToArray()))); Action <Loader, Dictionary <string, CustomPreviewBeatmapLevel> > songsLoaded = (_, __) => { //Now that they're refreshed, we can populate their beatmaps and add them to the available list songs.ForEach(x => loadLevel(x)); songsGottenCallback?.Invoke(availableSongs.Union(songs).ToList()); }; Loader.SongsLoadedEvent -= songsLoaded; Loader.SongsLoadedEvent += songsLoaded; Loader.Instance.RefreshSongs(false); } else { songsGottenCallback?.Invoke(availableSongs); } } }
public async Task AddSongAsync(string songId, [Remainder] string paramString = null) { if (IsAdmin()) { //Parse the difficulty input, either as an int or a string BeatmapDifficulty difficulty = BeatmapDifficulty.ExpertPlus; string difficultyArg = ParseArgs(paramString, "difficulty"); if (difficultyArg != null) { //If the enum conversion doesn't succeed, try it as an int if (!Enum.TryParse(difficultyArg, true, out difficulty)) { await ReplyAsync("Could not parse difficulty parameter.\n" + "Usage: addSong [songId] [difficulty]"); return; } } string characteristic = ParseArgs(paramString, "characteristic"); characteristic = characteristic ?? "Standard"; GameOptions gameOptions = GameOptions.None; PlayerOptions playerOptions = PlayerOptions.None; //Load up the GameOptions and PlayerOptions foreach (GameOptions o in Enum.GetValues(typeof(GameOptions))) { if (ParseArgs(paramString, o.ToString()) == "true") { gameOptions = (gameOptions | o); } } foreach (PlayerOptions o in Enum.GetValues(typeof(PlayerOptions))) { if (ParseArgs(paramString, o.ToString()) == "true") { playerOptions = (playerOptions | o); } } //Sanitize input if (songId.StartsWith("https://beatsaver.com/") || songId.StartsWith("https://bsaber.com/")) { //Strip off the trailing slash if there is one if (songId.EndsWith("/")) { songId = songId.Substring(0, songId.Length - 1); } //Strip off the beginning of the url to leave the id songId = songId.Substring(songId.LastIndexOf("/") + 1); } if (songId.Contains("&")) { songId = songId.Substring(0, songId.IndexOf("&")); } //Get the hash for the song var hash = BeatSaverDownloader.GetHashFromID(songId); if (OstHelper.IsOst(hash)) { //if (!Song.Exists(hash, parsedDifficulty, characteristicArg, true)) if (!SongExists(Context.Guild.Id, hash, characteristic, (int)difficulty, (int)gameOptions, (int)playerOptions)) { Song song = new Song { Name = OstHelper.GetOstSongNameFromLevelId(hash), GuildId = Context.Guild.Id, LevelId = hash, Characteristic = characteristic, BeatmapDifficulty = (int)difficulty, GameOptions = (int)gameOptions, PlayerOptions = (int)playerOptions }; await ReplyAsync($"Added: {song.Name} ({difficulty}) ({characteristic})" + $"{(gameOptions != GameOptions.None ? $" with game options: ({gameOptions.ToString()})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" with player options: ({playerOptions.ToString()})" : "!")}"); } else { await ReplyAsync("Song is already active in the database"); } } else { BeatSaverDownloader.DownloadSong(hash, async(songPath) => { if (songPath != null) { DownloadedSong song = new DownloadedSong(hash); string songName = song.Name; if (!song.GetBeatmapDifficulties(characteristic).Contains(difficulty)) { BeatmapDifficulty nextBestDifficulty = song.GetClosestDifficultyPreferLower(difficulty); if (SongExists(Context.Guild.Id, hash, characteristic, (int)nextBestDifficulty, (int)gameOptions, (int)playerOptions)) { await ReplyAsync($"{songName} doesn't have {difficulty}, and {nextBestDifficulty} is already in the database.\n" + $"Song not added."); } else { Song databaseSong = new Song { Name = OstHelper.GetOstSongNameFromLevelId(hash), GuildId = Context.Guild.Id, LevelId = hash, Characteristic = characteristic, BeatmapDifficulty = (int)nextBestDifficulty, GameOptions = (int)gameOptions, PlayerOptions = (int)playerOptions }; await ReplyAsync($"{songName} doesn't have {difficulty}, using {nextBestDifficulty} instead.\n" + $"Added to the song list" + $"{(gameOptions != GameOptions.None ? $" with game options: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" with player options: ({playerOptions})" : "!")}"); } } else { Song databaseSong = new Song { Name = OstHelper.GetOstSongNameFromLevelId(hash), GuildId = Context.Guild.Id, LevelId = hash, Characteristic = characteristic, BeatmapDifficulty = (int)difficulty, GameOptions = (int)gameOptions, PlayerOptions = (int)playerOptions }; await ReplyAsync($"{songName} ({difficulty}) ({characteristic}) downloaded and added to song list" + $"{(gameOptions != GameOptions.None ? $" with game options: ({gameOptions})" : "")}" + $"{(playerOptions != PlayerOptions.None ? $" with player options: ({playerOptions})" : "!")}"); } } else { await ReplyAsync("Could not download song."); } }); } } }
protected override void Client_PacketRecieved(Packet packet) { base.Client_PacketRecieved(packet); if (packet.Type == PacketType.PlaySong) { PlaySong playSong = packet.SpecificPacket as PlaySong; var desiredLevel = SongUtils.masterLevelList.First(x => x.levelID == playSong.Beatmap.LevelId); var desiredCharacteristic = desiredLevel.previewDifficultyBeatmapSets.FirstOrDefault(x => x.beatmapCharacteristic.serializedName == playSong.Beatmap.Characteristic.SerializedName).beatmapCharacteristic ?? desiredLevel.previewDifficultyBeatmapSets.First().beatmapCharacteristic; var desiredDifficulty = (BeatmapDifficulty)playSong.Beatmap.Difficulty; var playerData = Resources.FindObjectsOfTypeAll <PlayerDataModel>().First().playerData; var playerSettings = playerData.playerSpecificSettings; //Override defaults if we have forced options enabled if (playSong.PlayerSettings.Options != PlayerOptions.None) { playerSettings = new PlayerSpecificSettings(); playerSettings.leftHanded = playSong.PlayerSettings.Options.HasFlag(PlayerOptions.LeftHanded); playerSettings.staticLights = playSong.PlayerSettings.Options.HasFlag(PlayerOptions.StaticLights); playerSettings.noTextsAndHuds = playSong.PlayerSettings.Options.HasFlag(PlayerOptions.NoHud); playerSettings.advancedHud = playSong.PlayerSettings.Options.HasFlag(PlayerOptions.AdvancedHud); playerSettings.reduceDebris = playSong.PlayerSettings.Options.HasFlag(PlayerOptions.ReduceDebris); } var gameplayModifiers = new GameplayModifiers(); gameplayModifiers.batteryEnergy = playSong.GameplayModifiers.Options.HasFlag(GameOptions.BatteryEnergy); gameplayModifiers.disappearingArrows = playSong.GameplayModifiers.Options.HasFlag(GameOptions.DisappearingArrows); gameplayModifiers.failOnSaberClash = playSong.GameplayModifiers.Options.HasFlag(GameOptions.FailOnClash); gameplayModifiers.fastNotes = playSong.GameplayModifiers.Options.HasFlag(GameOptions.FastNotes); gameplayModifiers.ghostNotes = playSong.GameplayModifiers.Options.HasFlag(GameOptions.GhostNotes); gameplayModifiers.instaFail = playSong.GameplayModifiers.Options.HasFlag(GameOptions.InstaFail); gameplayModifiers.noBombs = playSong.GameplayModifiers.Options.HasFlag(GameOptions.NoBombs); gameplayModifiers.noFail = playSong.GameplayModifiers.Options.HasFlag(GameOptions.NoFail); gameplayModifiers.noObstacles = playSong.GameplayModifiers.Options.HasFlag(GameOptions.NoObstacles); gameplayModifiers.noArrows = playSong.GameplayModifiers.Options.HasFlag(GameOptions.NoArrows); if (playSong.GameplayModifiers.Options.HasFlag(GameOptions.SlowSong)) { gameplayModifiers.songSpeed = GameplayModifiers.SongSpeed.Slower; } if (playSong.GameplayModifiers.Options.HasFlag(GameOptions.FastSong)) { gameplayModifiers.songSpeed = GameplayModifiers.SongSpeed.Faster; } var colorScheme = playerData.colorSchemesSettings.overrideDefaultColors ? playerData.colorSchemesSettings.GetSelectedColorScheme() : null; PlaySong?.Invoke(desiredLevel, desiredCharacteristic, desiredDifficulty, gameplayModifiers, playerSettings, playerData.overrideEnvironmentSettings, colorScheme, playSong.FloatingScoreboard, playSong.StreamSync, playSong.DisablePause, playSong.DisableFail); } else if (packet.Type == PacketType.Command) { Command command = packet.SpecificPacket as Command; if (command.CommandType == Command.CommandTypes.ReturnToMenu) { if (SyncHandler.Instance != null) { ScreenOverlay.Instance.Clear(); } if ((Self as Player).PlayState == Player.PlayStates.InGame) { PlayerUtils.ReturnToMenu(); } } else if (command.CommandType == Command.CommandTypes.ScreenOverlay_ShowPng) { ScreenOverlay.Instance.ShowPng(); } else if (command.CommandType == Command.CommandTypes.DelayTest_Finish) { UnityMainThreadDispatcher.Instance().Enqueue(() => { ScreenOverlay.Instance.Clear(); SyncHandler.Instance.Resume(); SyncHandler.Destroy(); }); } } else if (packet.Type == PacketType.LoadSong) { LoadSong loadSong = packet.SpecificPacket as LoadSong; Action <IBeatmapLevel> SongLoaded = (loadedLevel) => { //Send updated download status (Self as Player).DownloadState = Player.DownloadStates.Downloaded; var playerUpdate = new Event(); playerUpdate.Type = Event.EventType.PlayerUpdated; playerUpdate.ChangedObject = Self; Send(new Packet(playerUpdate)); //Notify any listeners of the client that a song has been loaded LoadedSong?.Invoke(loadedLevel); Logger.Debug($"SENT DOWNLOADED SIGNAL {(playerUpdate.ChangedObject as Player).DownloadState}"); }; if (OstHelper.IsOst(loadSong.LevelId)) { SongLoaded?.Invoke(SongUtils.masterLevelList.First(x => x.levelID == loadSong.LevelId) as BeatmapLevelSO); } else { if (SongUtils.masterLevelList.Any(x => x.levelID == loadSong.LevelId)) { SongUtils.LoadSong(loadSong.LevelId, SongLoaded); } else { Action <bool> loadSongAction = (succeeded) => { if (succeeded) { SongUtils.LoadSong(loadSong.LevelId, SongLoaded); } else { (Self as Player).DownloadState = Player.DownloadStates.DownloadError; var playerUpdated = new Event(); playerUpdated.Type = Event.EventType.PlayerUpdated; playerUpdated.ChangedObject = Self; Send(new Packet(playerUpdated)); Logger.Debug($"SENT DOWNLOADED SIGNAL {(playerUpdated.ChangedObject as Player).DownloadState}"); } }; (Self as Player).DownloadState = Player.DownloadStates.Downloading; var playerUpdate = new Event(); playerUpdate.Type = Event.EventType.PlayerUpdated; playerUpdate.ChangedObject = Self; Send(new Packet(playerUpdate)); Logger.Debug($"SENT DOWNLOAD SIGNAL {(playerUpdate.ChangedObject as Player).DownloadState}"); SongDownloader.DownloadSong(loadSong.LevelId, songDownloaded: loadSongAction, downloadProgressChanged: (progress) => Logger.Debug($"DOWNLOAD PROGRESS: {progress}")); } } } else if (packet.Type == PacketType.File) { File file = packet.SpecificPacket as File; if (file.Intention == File.Intentions.SetPngToShowWhenTriggered) { var pngBytes = file.Compressed ? CompressionUtils.Decompress(file.Data) : file.Data; ScreenOverlay.Instance.SetPngBytes(pngBytes); } else if (file.Intention == File.Intentions.ShowPngImmediately) { var pngBytes = file.Compressed ? CompressionUtils.Decompress(file.Data) : file.Data; ScreenOverlay.Instance.SetPngBytes(pngBytes); ScreenOverlay.Instance.ShowPng(); } Send(packet.From, new Packet(new Acknowledgement() { PacketId = packet.Id, Type = Acknowledgement.AcknowledgementType.FileDownloaded })); } }
private void MockClient_PlaySong(Beatmap map) { if (OstHelper.IsOst(map.LevelId)) { return; } var match = State.Matches.First(x => x.Players.Any(p => p.Id == Self.Id && p.Name == Self.Name)); otherPlayersInMatch = match.Players.Select(x => x.Id).Union(new string[] { match.LeaderCase == Match.LeaderOneofCase.Coordinator ? match.Coordinator.Id : match.Player.Id }).Select(s => Guid.TryParse(s, out var g) ? g : Guid.Empty).ToArray(); currentlyPlayingMap = map; currentlyPlayingSong = new DownloadedSong(HashFromLevelId(map.LevelId)); currentMaxScore = 0; notesElapsed = 0; /*using (var libVLC = new LibVLC()) * { * var media = new Media(libVLC, currentlyPlayingSong.GetAudioPath(), FromType.FromPath); * await media.Parse(); * * songTimer = new Timer(); * songTimer.AutoReset = false; * songTimer.Interval = media.Duration; * songTimer.Elapsed += SongTimer_Elapsed; * * noteTimer = new Timer(); * noteTimer.AutoReset = false; * noteTimer.Interval = 500; * noteTimer.Elapsed += NoteTimer_Elapsed; * * noteTimer.Start(); * songTimer.Start(); * }*/ songTimer = new Timer(); songTimer.AutoReset = false; songTimer.Interval = 60 * 3 * 1000; songTimer.Elapsed += SongTimer_Elapsed; noteTimer = new Timer(); noteTimer.AutoReset = false; noteTimer.Interval = 500; noteTimer.Elapsed += NoteTimer_Elapsed; noteTimer.Start(); songTimer.Start(); var selfPlayer = SelfObject as Player; selfPlayer.PlayState = Player.Types.PlayStates.InGame; selfPlayer.Score = 0; selfPlayer.Combo = 0; selfPlayer.Accuracy = 0; selfPlayer.SongPosition = 0; multiplier = 1; var playerUpdated = new Event { Type = Event.Types.EventType.PlayerUpdated, ChangedObject = Google.Protobuf.WellKnownTypes.Any.Pack(selfPlayer) }; Send(new Packet(playerUpdated)); }
public async Task LeaderboardsAsync() { if (!IsAdmin()) { return; } string finalMessage = "Leaderboard:\n\n"; if (Config.ServerFlags.HasFlag(ServerFlags.Teams)) { foreach (var team in GetAllTeams()) { finalMessage += $"({team.TeamName})\n\n"; foreach (var song in GetAllScores(teamId: team.TeamId)) { if (song.Scores.Count > 0) //Don't print if no one submitted scores { finalMessage += $"{song.Name} ({song.Scores.First().Difficulty}):\n"; int place = 1; foreach (var score in song.Scores) { //Incredibly inefficient to open a song info file every time, but only the score structure is guaranteed to hold the real difficutly, //seeing as auto difficulty is what would be represented in the songconstruct string percentage = "???%"; if (!OstHelper.IsOst(song.SongHash)) { var maxScore = new BeatSaver.Song(song.SongHash).GetMaxScore(score.Characteristic, score.Difficulty); percentage = ((double)score.Score / maxScore).ToString("P", CultureInfo.InvariantCulture); } finalMessage += place + ": " + new Player(score.UserId).DiscordName + " - " + score.Score + $" ({percentage})" + (score.FullCombo ? " (Full Combo)" : ""); if (Config.ServerFlags.HasFlag(ServerFlags.Tokens)) { if (place == 1) { finalMessage += " (+3 Tokens)"; } else if (place == 2) { finalMessage += " (+2 Tokens)"; } else if (place == 3) { finalMessage += " (+1 Token)"; } } finalMessage += "\n"; place++; } finalMessage += "\n"; } } finalMessage += "\n"; } } else { List <SongConstruct> songs = GetActiveSongs(true); songs.ForEach(x => { string hash = x.SongHash; if (x.Scores.Count > 0) //Don't print if no one submitted scores { var song = new Song(hash, x.Difficulty, x.Characteristic); finalMessage += song.SongName + ":\n"; int place = 1; foreach (ScoreConstruct item in x.Scores) { //Incredibly inefficient to open a song info file every time, but only the score structure is guaranteed to hold the real difficutly, //seeing as auto difficulty is what would be represented in the songconstruct string percentage = "???%"; if (!OstHelper.IsOst(hash)) { var maxScore = new BeatSaver.Song(hash).GetMaxScore(item.Characteristic, item.Difficulty); percentage = ((double)item.Score / maxScore).ToString("P", CultureInfo.InvariantCulture); } finalMessage += place + ": " + new Player(item.UserId).DiscordName + " - " + item.Score + $" ({percentage})" + (item.FullCombo ? " (Full Combo)" : ""); finalMessage += "\n"; place++; } finalMessage += "\n"; } }); } //Deal with long messages if (finalMessage.Length > 2000) { for (int i = 0; finalMessage.Length > 2000; i++) { await ReplyAsync(finalMessage.Substring(0, finalMessage.Length > 2000 ? 2000 : finalMessage.Length)); finalMessage = finalMessage.Substring(2000); } } await ReplyAsync(finalMessage); }