From a3b8169fd61f3ba2b8c08b36af134927bfc4c987 Mon Sep 17 00:00:00 2001 From: pleb Date: Sun, 19 Jul 2026 20:25:22 -0700 Subject: [PATCH] Update Setlist for Beat Saber 1.44.1 startup scan --- README.md | 4 +- Setlist/BeatLeaderPlaylistOwnership.cs | 78 ++++++++------------------ Setlist/Plugin.cs | 61 ++++++++++++-------- Setlist/Properties/AssemblyInfo.cs | 4 +- Setlist/SetlistSyncHost.cs | 5 ++ Setlist/manifest.json | 6 +- docs/notes.md | 8 +-- 7 files changed, 79 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 0ec5c7d..b6d4af0 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ device that consumes them, without alt-tabbing or running a sync script. ## Requirements -- Beat Saber `1.40.8` (PC), via BSIPA. -- [BeatLeader](https://github.com/BeatLeader/beatleader-mod) `^0.9.0` — must be +- Beat Saber `1.44.1` (PC), via BSIPA. +- [BeatLeader](https://github.com/BeatLeader/beatleader-mod) `>=0.9.0` — must be installed **and signed in** for the sync to authenticate. - [PlaylistManager](https://github.com/rithik-b/PlaylistManager) `^1.7.0`. - [BeatSaberPlaylistsLib](https://github.com/Zingabopp/BeatSaberPlaylistsLib) diff --git a/Setlist/BeatLeaderPlaylistOwnership.cs b/Setlist/BeatLeaderPlaylistOwnership.cs index 331ad5b..970e0e9 100644 --- a/Setlist/BeatLeaderPlaylistOwnership.cs +++ b/Setlist/BeatLeaderPlaylistOwnership.cs @@ -3,8 +3,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Threading; -using System.Threading.Tasks; using BeatSaberPlaylistsLib.Types; using UnityEngine; using IPALogger = IPA.Logging.Logger; @@ -20,7 +18,6 @@ namespace Setlist private const float PlatformUserPollStepSeconds = 0.5f; /// How long to wait for to appear and populate playerId (plugin runs before menu init). private const float PlatformUserWaitTimeoutSeconds = 30f; - private const float PlatformUserGetUserInfoRetrySeconds = 3f; internal static bool TryExtractBeatLeaderPlaylistGuid(string syncUrl, out string guid) { @@ -111,55 +108,18 @@ namespace Setlist internal static IEnumerator FetchPlatformUserIdCoroutine(Action onDone) { var waited = 0f; - var lastGetUserInfoAttempt = -PlatformUserGetUserInfoRetrySeconds; while (waited < PlatformUserWaitTimeoutSeconds) { foreach (var plm in EnumerateLeaderboardModels()) { - var pid = plm.playerId; + var pid = ReadPlatformUserId(plm); if (!string.IsNullOrEmpty(pid)) { - onDone(pid.Trim()); + onDone(pid); yield break; } } - if (waited - lastGetUserInfoAttempt >= PlatformUserGetUserInfoRetrySeconds) - { - lastGetUserInfoAttempt = waited; - var model = ResolvePlatformUserModel(); - if (model != null) - { - Task task; - try - { - task = model.GetUserInfo(CancellationToken.None); - } - catch (Exception) - { - task = null; - } - - if (task != null) - { - while (!task.IsCompleted) - { - yield return null; - } - - if (!task.IsFaulted) - { - var uid = task.Result?.platformUserId; - if (!string.IsNullOrEmpty(uid)) - { - onDone(uid.Trim()); - yield break; - } - } - } - } - } - waited += PlatformUserPollStepSeconds; yield return new WaitForSeconds(PlatformUserPollStepSeconds); } @@ -188,26 +148,36 @@ namespace Setlist } } - private static IPlatformUserModel ResolvePlatformUserModel() + private static string ReadPlatformUserId(PlatformLeaderboardsModel model) { - var field = typeof(PlatformLeaderboardsModel).GetField( - "_platformUserModel", - BindingFlags.Instance | BindingFlags.NonPublic); - if (field == null) + if (model == null) { return null; } - IPlatformUserModel last = null; - foreach (var plm in EnumerateLeaderboardModels()) + var id = NormalizeUserId(model.playerId); + if (!string.IsNullOrEmpty(id)) { - if (field.GetValue(plm) is IPlatformUserModel m) - { - last = m; - } + return id; } - return last; + var platformField = typeof(PlatformLeaderboardsModel).GetField( + "_platform", + BindingFlags.Instance | BindingFlags.NonPublic); + var platform = platformField?.GetValue(model); + var user = platform?.GetType().GetProperty("user")?.GetValue(platform, null); + return NormalizeUserId(user?.GetType().GetProperty("userId")?.GetValue(user, null)); + } + + private static string NormalizeUserId(object raw) + { + if (raw == null) + { + return null; + } + + var value = raw.ToString()?.Trim(); + return string.IsNullOrEmpty(value) || value == "0" ? null : value; } /// diff --git a/Setlist/Plugin.cs b/Setlist/Plugin.cs index baf5cf0..7c0c411 100644 --- a/Setlist/Plugin.cs +++ b/Setlist/Plugin.cs @@ -1,8 +1,10 @@ using System; +using System.Collections; using System.Collections.Generic; using BeatSaberPlaylistsLib.Types; using IPA; using PlaylistManager.Utilities; +using UnityEngine; using IPALogger = IPA.Logging.Logger; namespace Setlist @@ -20,6 +22,9 @@ namespace Setlist /// Set after the ownership scan resolves the platform user id; used when syncing after PlaylistManager adds a song. internal static string CachedPlatformUserId { get; set; } + private const float PlaylistScanPollStepSeconds = 0.5f; + private const float PlaylistScanWaitTimeoutSeconds = 10f; + [Init] public Plugin(IPALogger logger) { @@ -36,43 +41,55 @@ namespace Setlist Events.playlistSongAdded += OnPlaylistSongAdded; + SetlistSyncHost.Instance.StartPlaylistOwnershipScan(Log); + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + } + + internal static IEnumerator CoScanAndLogPlaylistOwnership(IPALogger log) + { + var waited = 0f; + while (waited <= PlaylistScanWaitTimeoutSeconds) + { var playlists = BeatSaberPlaylistsLib.PlaylistManager.DefaultManager.GetAllPlaylists( includeChildren: true, out AggregateException loadErrors); if (loadErrors != null) { - Log.Error(loadErrors.Message); + log.Error(loadErrors.Message); foreach (var inner in loadErrors.InnerExceptions) { - Log.Error(inner.ToString()); + log.Error(inner.ToString()); } } - if (playlists == null || playlists.Length == 0) + if (playlists != null && playlists.Length > 0) { - Log.Info("No playlists loaded (or playlist library not initialized yet)."); - return; + var entries = new List<(IPlaylist Playlist, string Title, bool HasSyncUrl, string BeatLeaderGuid, string OwnerId)>(); + foreach (var playlist in playlists) + { + BeatLeaderPlaylistOwnership.TryReadBeatLeaderMetadata( + playlist, + out var hasSyncUrl, + out var blGuid, + out var ownerId); + + entries.Add((playlist, playlist.Title, hasSyncUrl, blGuid, ownerId)); + } + + BeatLeaderPlaylistOwnership.ScheduleVerifyAndLog(entries, log); + yield break; } - var entries = new List<(IPlaylist Playlist, string Title, bool HasSyncUrl, string BeatLeaderGuid, string OwnerId)>(); - foreach (var playlist in playlists) - { - BeatLeaderPlaylistOwnership.TryReadBeatLeaderMetadata( - playlist, - out var hasSyncUrl, - out var blGuid, - out var ownerId); - - entries.Add((playlist, playlist.Title, hasSyncUrl, blGuid, ownerId)); - } - - BeatLeaderPlaylistOwnership.ScheduleVerifyAndLog(entries, Log); - } - catch (Exception ex) - { - Log.Error(ex.ToString()); + waited += PlaylistScanPollStepSeconds; + yield return new WaitForSeconds(PlaylistScanPollStepSeconds); } + + log.Info("No playlists loaded after startup wait."); } [OnExit] diff --git a/Setlist/Properties/AssemblyInfo.cs b/Setlist/Properties/AssemblyInfo.cs index e38d79a..51454d5 100644 --- a/Setlist/Properties/AssemblyInfo.cs +++ b/Setlist/Properties/AssemblyInfo.cs @@ -11,5 +11,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("50F53E6E-21D5-4780-8E67-273877DAA28C")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("0.1.1.0")] +[assembly: AssemblyFileVersion("0.1.1.0")] diff --git a/Setlist/SetlistSyncHost.cs b/Setlist/SetlistSyncHost.cs index 2a0d866..fb24f8c 100644 --- a/Setlist/SetlistSyncHost.cs +++ b/Setlist/SetlistSyncHost.cs @@ -28,5 +28,10 @@ namespace Setlist { StartCoroutine(BeatLeaderPlaylistSync.CoPostOwnedPlaylistToBeatLeader(playlist, log)); } + + internal void StartPlaylistOwnershipScan(IPALogger log) + { + StartCoroutine(Plugin.CoScanAndLogPlaylistOwnership(log)); + } } } diff --git a/Setlist/manifest.json b/Setlist/manifest.json index 0e5ed9a..8b2c090 100644 --- a/Setlist/manifest.json +++ b/Setlist/manifest.json @@ -3,13 +3,13 @@ "id": "Setlist", "name": "Setlist", "author": "", - "version": "0.1.0", + "version": "0.1.1", "description": "Syncs playlists with external sources.", - "gameVersion": "1.40.8", + "gameVersion": "1.44.1", "dependsOn": { "BSIPA": "^4.3.0", "BeatSaberPlaylistsLib": "^1.7.0", - "BeatLeader": "^0.9.0", + "BeatLeader": ">=0.9.0", "PlaylistManager": "^1.7.0" } } diff --git a/docs/notes.md b/docs/notes.md index 5a48c8e..25c0819 100644 --- a/docs/notes.md +++ b/docs/notes.md @@ -3,13 +3,13 @@ Gamedir ```sh -cd ~/.local/share/BSManager/BSInstances/1.40.8 +cd ~/.local/share/BSManager/BSInstances/1.44.1 ``` ## Logs ```sh -tail -f Logs/_latest.log +tail -F Logs/_latest.log ``` ## FPFC Mode @@ -23,7 +23,7 @@ Mouse input is broken because Wayland is not yet supported. To run beat saber, you can mimic the launch execution that bs-manager performs with the following: ```sh -cd "/home/pleb/.local/share/BSManager/BSInstances/1.40.8" +cd "/home/pleb/.local/share/BSManager/BSInstances/1.44.1" export SteamAppId=620980 SteamOverlayGameId=620980 SteamGameId=620980 export WINEDLLOVERRIDES='winhttp=n,b' @@ -40,7 +40,7 @@ steam-run "/home/pleb/.local/share/Steam/steamapps/common/Proton - Experimental/ Then, after a few seconds you can read the log messages after BS starts up ``` -cat ~/.local/share/BSManager/BSInstances/1.40.8/Logs/_latest.log | grep Setlist +cat ~/.local/share/BSManager/BSInstances/1.44.1/Logs/_latest.log | grep Setlist ``` Thereafter, kill the process.