예제 #1
0
        //To fix the bug explained in CustomLevelStaticData.cs
        private void OnDidSelectSongEvent(SongListViewController songListViewController)
        {
            var song = CustomLevelStaticDatas.FirstOrDefault(x => x.levelId == songListViewController.levelId);

            if (song == null)
            {
                return;
            }
            if (song.difficultyLevels.All(x => x.difficulty != _songSelectionView.difficulty))
            {
                var isDiffSelected =
                    ReflectionUtil.GetPrivateField <bool>(_difficultyView, "_difficultySelected");
                if (!isDiffSelected)
                {
                    return;
                }
                //The new selected song does not have the current difficulty selected
                var firstDiff = song.difficultyLevels.FirstOrDefault();
                if (firstDiff == null)
                {
                    return;
                }
                ReflectionUtil.SetPrivateField(_songSelectionView, "_difficulty", firstDiff.difficulty);
            }
        }
예제 #2
0
        public void OpenSongsList(string songToSelectWhenLoaded = null)
        {
            if (songListViewController == null)
            {
                songListViewController = BeatSaberUI.CreateViewController <SongListViewController>();
            }
            if (_bottomViewController == null)
            {
                _bottomViewController = BeatSaberUI.CreateViewController <BottomViewController>();
            }
            if (_resultsViewController == null)
            {
                _resultsViewController = Resources.FindObjectsOfTypeAll <ResultsViewController>().First();
            }
            if (_playerDataModel == null)
            {
                _playerDataModel = Resources.FindObjectsOfTypeAll <PlayerDataModel>().First();
            }
            if (_menuLightsManager == null)
            {
                _menuLightsManager = Resources.FindObjectsOfTypeAll <MenuLightsManager>().First();
            }
            if (_soloFreePlayFlowCoordinator == null)
            {
                _soloFreePlayFlowCoordinator = Resources.FindObjectsOfTypeAll <SoloFreePlayFlowCoordinator>().First();
            }
            if (_campaignFlowCoordinator == null)
            {
                _campaignFlowCoordinator = Resources.FindObjectsOfTypeAll <CampaignFlowCoordinator>().First();
            }
            if (_alwaysOwnedContent == null)
            {
                _alwaysOwnedContent = Resources.FindObjectsOfTypeAll <AlwaysOwnedContentSO>().First();
            }
            if (_primaryLevelCollection == null)
            {
                _primaryLevelCollection = _alwaysOwnedContent.alwaysOwnedPacks.First(x => x.packID == OstHelper.packs[0].PackID).beatmapLevelCollection as BeatmapLevelCollectionSO;
            }
            if (_secondaryLevelCollection == null)
            {
                _secondaryLevelCollection = _alwaysOwnedContent.alwaysOwnedPacks.First(x => x.packID == OstHelper.packs[1].PackID).beatmapLevelCollection as BeatmapLevelCollectionSO;
            }
            if (_tertiaryLevelCollection == null)
            {
                _tertiaryLevelCollection = _alwaysOwnedContent.alwaysOwnedPacks.First(x => x.packID == OstHelper.packs[2].PackID).beatmapLevelCollection as BeatmapLevelCollectionSO;
            }
            if (_extrasLevelCollection == null)
            {
                _extrasLevelCollection = _alwaysOwnedContent.alwaysOwnedPacks.First(x => x.packID == OstHelper.packs[3].PackID).beatmapLevelCollection as BeatmapLevelCollectionSO;
            }
            if (!songListViewController.isInViewControllerHierarchy || !songListViewController.isActiveAndEnabled)
            {
                SetViewControllersToNavigationController(_mainModNavigationController, new ViewController[] { songListViewController });

                songListViewController.SelectWhenLoaded(songToSelectWhenLoaded);
                songListViewController.SongListRowSelected += SongListRowSelected;
                songListViewController.ReloadPressed       += () => ReloadServerData();
                ReloadServerData();
            }
        }
예제 #3
0
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            View.Identifier = "SearchResultsXibView";
            View.TranslatesAutoresizingMaskIntoConstraints = false;

            songListViewController = new SongListViewController();

            var itemIndex   = tabView.IndexOf(new NSString("songs"));
            var tabViewItem = tabView.Items [itemIndex];

            tabViewItem.View.AddSubview(songListViewController.View);

            tabViewItem.View.AddConstraints(FillHorizontal(songListViewController.View, false));
            tabViewItem.View.AddConstraints(FillVertical(songListViewController.View, false));

            songListViewController.Tracks             = viewModel.Search.Tracks;
            songListViewController.SongDoubleClicked += DidSongDoubleClicked;

            tabGroup.AddTab("songs", "Playlist", "Tracks");
            tabGroup.AddTab("videos", "Video", "Videos");
            tabGroup.AddTab("artists", "Artist", "Artists");

            tabGroup.ActiveTabChanged += OnActiveTabChanged;

            tabGroup.ActivateTab("songs");

            artistsCollectionView.Delegate   = this;
            artistsCollectionView.Selectable = true;
            artistsCollectionView.RegisterNib(new NSNib("ArtistItemView", NSBundle.MainBundle), "ArtistItemView");
            artistsCollectionView.RegisterClassForItem(typeof(ArtistItemView), "ArtistItemView");

            artistsCollectionView.ReloadData();
        }
예제 #4
0
        public override void ViewDidLoad()
        {
            headerViewController   = new HeaderViewController();
            songListViewController = new SongListViewController();

            albumListViewController = new AlbumListViewController();
            albumListViewController.SongDoubleClicked += PlaySong;
            albumListViewPlaceholder.PresentView(albumListViewController.View);

            playlistsViewController = new AlbumListViewController();
            playlistsViewController.SongDoubleClicked += PlaySong;
            playlistViewPlaceholder.PresentView(playlistsViewController.View);

            songListViewController.SongDoubleClicked += PlaySong;
            songListViewController.DisableScroll();

            songListViewPlaceholder.PresentView(songListViewController.TableView);
            songListViewPlaceholder.InvalidateIntrinsicContentSize();

            headerViewPlaceholder.PresentView(headerViewController.View);
            headerViewPlaceholder.InvalidateIntrinsicContentSize();

            headerViewController.TabGroup.AddTab("popularSongs", "Playlist", "Popular Songs");
            headerViewController.TabGroup.AddTab("albums", "Star", "Albums");
            headerViewController.TabGroup.AddTab("playlists", "Playlist", "Playlists");

            headerViewController.TabGroup.ActivateTab("popularSongs");
            headerViewController.TabGroup.ActiveTabChanged += DidActiveTabChanged;

            this.WhenAnyValue(val => val.ViewModel)
            .Where(val => val != null)
            .DistinctUntilChanged()
            .Subscribe(vm => BindViewModel());
        }
예제 #5
0
 //Starts the necessary coroutine chain to make the mod functional
 public static void GetData(
     SongListViewController slvc,
     string userId,
     Action <Player> userDataGottenCallback    = null,
     Action <List <Team> > teamsGottenCallback = null,
     Action <List <Song> > songsGottenCallback = null
     )
 {
     SharedCoroutineStarter.instance.StartCoroutine(GetAllData(slvc, userId, userDataGottenCallback, teamsGottenCallback, songsGottenCallback));
 }
예제 #6
0
        private void SceneManagerOnActiveSceneChanged(Scene arg0, Scene scene)
        {
            StartCoroutine(WaitRemoveScores());

            SongListViewController songListController = Resources.FindObjectsOfTypeAll <SongListViewController>()
                                                        .FirstOrDefault();

            if (songListController == null)
            {
                return;
            }
            songListController.didSelectSongEvent += OnDidSelectSongEvent;

            _songSelectionView = Resources.FindObjectsOfTypeAll <SongSelectionMasterViewController>().FirstOrDefault();
            _difficultyView    = Resources.FindObjectsOfTypeAll <DifficultyViewController>().FirstOrDefault();
        }
예제 #7
0
        //GET the teams from the server
        private static IEnumerator GetTeams(SongListViewController slvc, Action <List <Team> > teamsGottenCallback = null)
        {
            UnityWebRequest www = UnityWebRequest.Get($"{discordCommunityApi}/teams/");

            Logger.Debug($"REQUESTING TEAMS: {discordCommunityApi}/teams/");
            www.timeout = 30;
            yield return(www.SendWebRequest());

            if (www.isNetworkError || www.isHttpError)
            {
                Logger.Error($"Error getting teams: {www.error}");
                slvc.ErrorHappened($"Error getting teams: {www.error}");
            }
            else
            {
                try
                {
                    //Clear out existing teams
                    Team.allTeams.Clear();

                    //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 team in node)
                    {
                        var teamObject =
                            new Team(
                                team.Key,
                                team.Value["teamName"],
                                team.Value["captainId"],
                                team.Value["color"],
                                team.Value["requiredTokens"],
                                team.Value["nextPromotion"]
                                );
                        Team.allTeams.Add(teamObject);
                    }

                    teamsGottenCallback?.Invoke(Team.allTeams);
                }
                catch (Exception e)
                {
                    Logger.Error($"Error parsing teams data: {e}");
                    slvc.ErrorHappened($"Error parsing teams data: {e}");
                    yield break;
                }
            }
        }
예제 #8
0
        //GET the user's profile data from the server
        private static IEnumerator GetUserData(SongListViewController slvc, string userId, Action <Player> userDataGottenCallback = null)
        {
            UnityWebRequest www = UnityWebRequest.Get($"{discordCommunityApi}/playerstats/{userId}");

            Logger.Debug($"GETTING PLAYER DATA: {discordCommunityApi}/playerstats/{userId}");
            www.timeout = 30;
            yield return(www.SendWebRequest());

            if (www.isNetworkError || www.isHttpError)
            {
                Logger.Error($"Error getting player stats: {www.error}");
                slvc.ErrorHappened($"Error getting player stats: {www.error}");
            }
            else
            {
                try
                {
                    var node = JSON.Parse(www.downloadHandler.text);

                    //If there is a message from the server, display it
                    if (node["message"] != null && node["message"].ToString().Length > 1)
                    {
                        slvc.ErrorHappened(node["message"]);
                        yield break;
                    }

                    //If the client is out of date, show update message
                    if (VersionCode < Convert.ToInt32(node["version"].Value))
                    {
                        slvc.ErrorHappened($"Version {SharedConstructs.Version} is now out of date. Please download the newest one from the Discord.");
                    }

                    Player.Instance.Team          = node["team"];
                    Player.Instance.Tokens        = Convert.ToInt32(node["tokens"].Value);
                    Player.Instance.ServerOptions = (ServerFlags)Convert.ToInt32(node["serverSettings"].Value);

                    userDataGottenCallback?.Invoke(Player.Instance);
                }
                catch (Exception e)
                {
                    Logger.Error($"Error parsing playerstats data: {e}");
                    slvc.ErrorHappened($"Error parsing playerstats data: {e}");
                    yield break;
                }
            }
        }
예제 #9
0
        //Gets all relevant data for the mod to work
        private static IEnumerator GetAllData(
            SongListViewController slvc,
            string userId,
            Action <Player> userDataGottenCallback    = null,
            Action <List <Team> > teamsGottenCallback = null,
            Action <List <Song> > songsGottenCallback = null
            )
        {
            yield return(SharedCoroutineStarter.instance.StartCoroutine(GetUserData(slvc, userId, userDataGottenCallback)));

            yield return(SharedCoroutineStarter.instance.StartCoroutine(GetTeams(slvc, teamsGottenCallback)));

            if (!slvc.errorHappened && !slvc.HasSongs())
            {
                yield return(SharedCoroutineStarter.instance.StartCoroutine(GetSongs(slvc, userId, songsGottenCallback)));
            }
        }
예제 #10
0
        //To fix the bug explained in CustomLevelStaticData.cs
        private void OnDidSelectSongEvent(SongListViewController songListViewController)
        {
            try
            {
                CustomLevelStaticData song =
                    CustomLevelStaticDatas.FirstOrDefault(x => x.levelId == songListViewController.levelId);
                if (song == null)
                {
                    return;
                }

                if (!LoadIfNotLoaded(song))
                {
                    Logger.Log("Song was modified, updated leaderboard ID to " + song.levelId);
                    songListViewController.SelectSong(_levels.FirstIndexWhere(data => data.levelId == song.levelId));
                    return;
                }

                if (song.difficultyLevels.All(x => x.difficulty != _songSelectionView.difficulty))
                {
                    bool isDiffSelected =
                        ReflectionUtil.GetPrivateField <bool>(_difficultyView, "_difficultySelected");
                    if (!isDiffSelected)
                    {
                        return;
                    }
                    //The new selected song does not have the current difficulty selected
                    LevelStaticData.DifficultyLevel firstDiff = song.difficultyLevels.FirstOrDefault();
                    if (firstDiff == null)
                    {
                        return;
                    }
                    ReflectionUtil.SetPrivateField(_songSelectionView, "_difficulty", firstDiff.difficulty);
                }
            }
            catch (Exception e)
            {
                Logger.Log(e.ToString());
            }
        }
예제 #11
0
        //Download songs. Taken from BeatSaberMultiplayer
        //availableSongs: List of IBeatmapLevel which may hold levels already approved for display
        //downloadQueue: List of beatsaver ids representing songs left to download
        //completedDownloads: List of beatsaver ids representing songs that have successfully downloaded
        //songId: The song this instance of the Coroutine is supposed to download
        //slvc: The song list view controller to display the downloaded songs to
        private static IEnumerator DownloadSongs(string songHash, SongListViewController slvc)
        {
            UnityWebRequest www = UnityWebRequest.Get($"{beatSaverDownloadUrl}{songHash}");

#if BETA
            Logger.Info($"DOWNLOADING: {beatSaverDownloadUrl}{songHash}");
#endif
            bool  timeout = false;
            float time    = 0f;

            www.SetRequestHeader("user-agent", @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36");
            UnityWebRequestAsyncOperation asyncRequest = www.SendWebRequest();

            while (!asyncRequest.isDone || asyncRequest.progress < 1f)
            {
                yield return(null);

                time += Time.deltaTime;

                if (time >= 15f && asyncRequest.progress == 0f)
                {
                    www.Abort();
                    timeout = true;
                }
            }

            if (www.isNetworkError || www.isHttpError || timeout)
            {
                Logger.Error($"Error downloading song {songHash}: {www.error}");
                slvc.ErrorHappened($"Error downloading song {songHash}: {www.error}");
            }
            else
            {
                //Logger.Info("Received response from BeatSaver.com...");

                string zipPath         = "";
                string customSongsPath = CustomLevelPathHelper.customLevelsDirectoryPath;
                string customSongPath  = "";

                byte[] data = www.downloadHandler.data;

                try
                {
                    customSongPath = customSongsPath + "/" + songHash + "/";
                    zipPath        = customSongPath + songHash + ".zip";
                    if (!Directory.Exists(customSongPath))
                    {
                        Directory.CreateDirectory(customSongPath);
                    }
                    File.WriteAllBytes(zipPath, data);
                    //Logger.Info("Downloaded zip file!");
                }
                catch (Exception e)
                {
                    Logger.Error($"Error writing zip: {e}");
                    slvc.ErrorHappened($"Error writing zip: {e}");
                    yield break;
                }

                //Logger.Info("Extracting...");

                try
                {
                    ZipFile.ExtractToDirectory(zipPath, customSongPath);
                }
                catch (Exception e)
                {
                    Logger.Error($"Unable to extract ZIP! Exception: {e}");
                    slvc.ErrorHappened($"Unable to extract ZIP! Exception: {e}");
                    yield break;
                }

                try
                {
                    File.Delete(zipPath);
                }
                catch (IOException e)
                {
                    Logger.Warning($"Unable to delete zip! Exception: {e}");
                    slvc.ErrorHappened($"Unable to delete zip! Exception: {e}");
                    yield break;
                }

                Logger.Success($"Downloaded!");
            }
        }
예제 #12
0
        //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);
                }
            }
        }