diff --git a/MultiplayerExtensions/Beatmaps/PreviewBeatmapPacket.cs b/MultiplayerExtensions/Beatmaps/PreviewBeatmapPacket.cs index e82e284..32e5110 100644 --- a/MultiplayerExtensions/Beatmaps/PreviewBeatmapPacket.cs +++ b/MultiplayerExtensions/Beatmaps/PreviewBeatmapPacket.cs @@ -72,7 +72,7 @@ static async public Task FromPreview(PreviewBeatmapStub pr packet.beatsPerMinute = preview.beatsPerMinute; packet.songDuration = preview.songDuration; - packet.coverImage = await preview.GetRawCoverAsync(CancellationToken.None); + //packet.coverImage = await preview.GetRawCoverAsync(CancellationToken.None); packet.characteristic = characteristic; packet.difficulty = difficulty; diff --git a/MultiplayerExtensions/Environments/LobbyEnvironmentManager.cs b/MultiplayerExtensions/Environments/LobbyEnvironmentManager.cs index 3b6ee62..2b70f66 100644 --- a/MultiplayerExtensions/Environments/LobbyEnvironmentManager.cs +++ b/MultiplayerExtensions/Environments/LobbyEnvironmentManager.cs @@ -62,7 +62,7 @@ private void HandleLobbyEnvironmentLoaded(object sender, System.EventArgs e) _stageManager.transform.localScale = new Vector3(centerScreenScale, centerScreenScale, centerScreenScale); SetAllPlayerPlaceColors(Color.black); - SetPlayerPlaceColor(_sessionManager.localPlayer, _playerManager.localColor); + SetPlayerPlaceColor(_sessionManager.localPlayer, ExtendedPlayerManager.localColor); foreach (ExtendedPlayer player in _playerManager.players.Values) SetPlayerPlaceColor(player, player.playerColor); } diff --git a/MultiplayerExtensions/HarmonyPatches/InstallerPatches.cs b/MultiplayerExtensions/HarmonyPatches/InstallerPatches.cs index 6ff6861..6a16e8b 100644 --- a/MultiplayerExtensions/HarmonyPatches/InstallerPatches.cs +++ b/MultiplayerExtensions/HarmonyPatches/InstallerPatches.cs @@ -108,18 +108,23 @@ internal static void Prefix(ref GameplayCoreInstaller __instance, ref IConnected newModifiers = exPlayer.lastModifiers; else newModifiers = hostPlayer?.lastModifiers; - - if (newModifiers != null) - ____sceneSetupData = new GameplayCoreSceneSetupData( - ____sceneSetupData.difficultyBeatmap, - ____sceneSetupData.previewBeatmapLevel, - newModifiers, - ____sceneSetupData.playerSpecificSettings, - ____sceneSetupData.practiceSettings, - ____sceneSetupData.useTestNoteCutSoundEffects, - ____sceneSetupData.environmentInfo, - ____sceneSetupData.colorScheme - ); + + if (Plugin.Config.LagReducer && newModifiers != null) + newModifiers = newModifiers.CopyWith(zenMode: true); + + if (newModifiers == null) + newModifiers = ____sceneSetupData.gameplayModifiers; + + ____sceneSetupData = new GameplayCoreSceneSetupData( + ____sceneSetupData.difficultyBeatmap, + ____sceneSetupData.previewBeatmapLevel, + newModifiers.CopyWith(zenMode: true), + ____sceneSetupData.playerSpecificSettings, + ____sceneSetupData.practiceSettings, + ____sceneSetupData.useTestNoteCutSoundEffects, + ____sceneSetupData.environmentInfo, + ____sceneSetupData.colorScheme + ); } } } diff --git a/MultiplayerExtensions/MPEvents.cs b/MultiplayerExtensions/MPEvents.cs index a2aa5e7..913b20d 100644 --- a/MultiplayerExtensions/MPEvents.cs +++ b/MultiplayerExtensions/MPEvents.cs @@ -41,6 +41,11 @@ public static class MPEvents /// public static event EventHandler? FreeModChanged; + /// + /// Raised when the host toggles host pick. + /// + public static event EventHandler? HostPickChanged; + internal static void RaiseMasterServerChanged(object sender, MasterServerInfo info) => MasterServerChanged?.RaiseEventSafe(sender, info, nameof(MasterServerChanged)); internal static void RaiseRoomCodeChanged(object sender, string code) @@ -55,6 +60,8 @@ internal static void RaiseCustomSongsChanged(object sender, bool state) => CustomSongsChanged.RaiseEventSafe(sender, state, nameof(CustomSongsChanged)); internal static void RaiseFreeModChanged(object sender, bool state) => FreeModChanged.RaiseEventSafe(sender, state, nameof(FreeModChanged)); + internal static void RaiseHostPickChanged(object sender, bool state) + => HostPickChanged.RaiseEventSafe(sender, state, nameof(HostPickChanged)); } public class SelectedBeatmapEventArgs : EventArgs diff --git a/MultiplayerExtensions/MPState.cs b/MultiplayerExtensions/MPState.cs index b081e53..3da9e82 100644 --- a/MultiplayerExtensions/MPState.cs +++ b/MultiplayerExtensions/MPState.cs @@ -98,6 +98,22 @@ internal set } } + private static bool _hostPickEnabled; + /// + /// Whether host pick is enabled in the current lobby. + /// + public static bool HostPickEnabled + { + get => _hostPickEnabled; + internal set + { + if (_hostPickEnabled == value) + return; + _hostPickEnabled = value; + Plugin.Log?.Debug($"Updated host pick to '{value}'"); + } + } + private static bool _easterEggsEnabled = true; /// /// Whether easter eggs in multiplayer are enabled. diff --git a/MultiplayerExtensions/MultiplayerExtensions.csproj b/MultiplayerExtensions/MultiplayerExtensions.csproj index 27ec98e..6f202ff 100644 --- a/MultiplayerExtensions/MultiplayerExtensions.csproj +++ b/MultiplayerExtensions/MultiplayerExtensions.csproj @@ -4,7 +4,7 @@ Library Properties MultiplayerExtensions - 0.4.8 + 0.4.9 net472 true portable diff --git a/MultiplayerExtensions/OverrideClasses/GameStateControllerStub.cs b/MultiplayerExtensions/OverrideClasses/GameStateControllerStub.cs index 241da93..9c232f8 100644 --- a/MultiplayerExtensions/OverrideClasses/GameStateControllerStub.cs +++ b/MultiplayerExtensions/OverrideClasses/GameStateControllerStub.cs @@ -1,7 +1,10 @@ using IPA.Utilities; using MultiplayerExtensions.Packets; using MultiplayerExtensions.Sessions; +using MultiplayerExtensions.Utilities; +using Polyglot; using System; +using System.Collections.Generic; using System.Linq; namespace MultiplayerExtensions.OverrideClasses @@ -25,6 +28,8 @@ internal GameStateControllerStub(PacketManager packetManager, ExtendedPlayerMana _lobbyGameState.gameStateDidChangeEvent -= base.HandleGameStateDidChange; _lobbyGameState.gameStateDidChangeEvent += HandleGameStateDidChange; base.Activate(); + + (this as ILobbyGameStateController).levelFinishedEvent += handleLevelFinished; } public new void Deactivate() @@ -35,6 +40,8 @@ internal GameStateControllerStub(PacketManager packetManager, ExtendedPlayerMana _lobbyGameState.gameStateDidChangeEvent -= HandleGameStateDidChange; _lobbyGameState.gameStateDidChangeEvent += base.HandleGameStateDidChange; base.Deactivate(); + + (this as ILobbyGameStateController).levelFinishedEvent -= handleLevelFinished; } public new void StartListeningToGameStart() @@ -86,6 +93,18 @@ private void OnPlayerStateChanged(IConnectedPlayer player) { _multiplayerSessionManager.SetLocalPlayerState("start_primed", false); starting = true; + + if (!Plugin.Config.HostPick) + { + ILobbyPlayerDataModel localPlayerDataModel = _lobbyPlayersDataModel.GetLobbyPlayerDataModel(_lobbyPlayersDataModel.localUserId); + IEnumerable validDataModels = _lobbyPlayersDataModel.playersData.Values.Where(data => data.beatmapLevel != null); + ILobbyPlayerDataModel chosenPlayerDataModel = validDataModels.ElementAt(new Random().Next(0, validDataModels.Count())); + localPlayerDataModel.beatmapLevel = chosenPlayerDataModel.beatmapLevel; + localPlayerDataModel.beatmapCharacteristic = chosenPlayerDataModel.beatmapCharacteristic; + localPlayerDataModel.beatmapDifficulty = chosenPlayerDataModel.beatmapDifficulty; + localPlayerDataModel.gameplayModifiers = chosenPlayerDataModel.gameplayModifiers; + } + base.StartGame(); _multiplayerLevelLoader.countdownFinishedEvent -= base.HandleMultiplayerLevelLoaderCountdownFinished; _multiplayerLevelLoader.countdownFinishedEvent += HandleCountdown; @@ -162,6 +181,13 @@ private void StartLevel() base.HandleMultiplayerLevelLoaderCountdownFinished(previewBeatmapLevel, beatmapDifficulty, beatmapCharacteristic, difficultyBeatmap, gameplayModifiers); } + private void handleLevelFinished(MultiplayerLevelScenesTransitionSetupDataSO sceneSetupData, MultiplayerResultsData resultsData) + { + string? hash = Utilities.Utils.LevelIdToHash(sceneSetupData.previewBeatmapLevel.levelID); + if (hash != null) + _ = Statistics.PlayMap(hash, sceneSetupData.beatmapDifficulty.ToString(), sceneSetupData.beatmapCharacteristic.serializedName, (int)Math.Floor(resultsData.localPlayerResultData.levelCompletionResults.endSongTime), (int)ExtendedPlayerManager.localPlatform, MPState.CurrentMasterServer.hostname); + } + private bool starting; private IPreviewBeatmapLevel? previewBeatmapLevel; diff --git a/MultiplayerExtensions/Plugin.cs b/MultiplayerExtensions/Plugin.cs index 73d96fe..2019cd4 100644 --- a/MultiplayerExtensions/Plugin.cs +++ b/MultiplayerExtensions/Plugin.cs @@ -16,6 +16,8 @@ using Zenject; using MultiplayerExtensions.UI; using BeatSaberMarkupLanguage.Settings; +using System.Net.Http; +using MultiplayerExtensions.Sessions; namespace MultiplayerExtensions { @@ -23,10 +25,15 @@ namespace MultiplayerExtensions public class Plugin { public static readonly string HarmonyId = "com.github.Zingabopp.MultiplayerExtensions"; + internal static Plugin Instance { get; private set; } = null!; internal static PluginMetadata PluginMetadata = null!; - internal static Harmony? _harmony; + internal static IPALogger Log { get; private set; } = null!; + internal static PluginConfig Config = null!; + + internal static HttpClient HttpClient { get; private set; } = null!; internal static BeatSaver BeatSaver = null!; + internal static Harmony? _harmony; internal static Harmony Harmony { get @@ -34,11 +41,19 @@ internal static Harmony Harmony return _harmony ??= new Harmony(HarmonyId); } } - /// - /// Use to send log messages through BSIPA. - /// - internal static IPALogger Log { get; private set; } = null!; - internal static PluginConfig Config = null!; + + public static string UserAgent + { + get + { + var modVersion = PluginMetadata.Version.ToString(); + var bsVersion = IPA.Utilities.UnityGame.GameVersion.ToString(); + return $"MultiplayerExtensions/{modVersion} (BeatSaber/{bsVersion})"; + } + } + + private const int MaxPlayers = 100; + private const int MinPlayers = 10; [Init] public Plugin(IPALogger logger, Config conf, Zenjector zenjector, PluginMetadata pluginMetadata) @@ -47,11 +62,15 @@ public Plugin(IPALogger logger, Config conf, Zenjector zenjector, PluginMetadata PluginMetadata = pluginMetadata; Log = logger; Config = conf.Generated(); + zenjector.OnApp(); zenjector.OnMenu(); zenjector.OnGame(); + HttpOptions options = new HttpOptions("MultiplayerExtensions", new Version(pluginMetadata.Version.ToString())); BeatSaver = new BeatSaver(options); + HttpClient = new HttpClient(); + HttpClient.DefaultRequestHeaders.Add("User-Agent", Plugin.UserAgent); } [OnStart] @@ -60,10 +79,7 @@ public void OnApplicationStart() Plugin.Log?.Info($"MultiplayerExtensions: '{VersionInfo.Description}'"); BSMLSettings.instance.AddSettingsMenu("Multiplayer", "MultiplayerExtensions.UI.settings.bsml", MPSettings.instance); - if (Plugin.Config.MaxPlayers > 100) - Plugin.Config.MaxPlayers = 100; - if (Plugin.Config.MaxPlayers < 10) - Plugin.Config.MaxPlayers = 10; + Plugin.Config.MaxPlayers = Math.Max(Math.Min(Config.MaxPlayers, MaxPlayers), MinPlayers); MPState.FreeModEnabled = false; HarmonyManager.ApplyDefaultPatches(); diff --git a/MultiplayerExtensions/Sessions/ExtendedPlayerManager.cs b/MultiplayerExtensions/Sessions/ExtendedPlayerManager.cs index 8fcb88e..4fc39c9 100644 --- a/MultiplayerExtensions/Sessions/ExtendedPlayerManager.cs +++ b/MultiplayerExtensions/Sessions/ExtendedPlayerManager.cs @@ -14,9 +14,9 @@ public class ExtendedPlayerManager : IInitializable, IDisposable protected readonly IPlatformUserModel _platformUserModel; private Dictionary _players = new Dictionary(); - internal string? localPlatformID; - internal Platform localPlatform; - internal Color localColor; + internal static string? localPlatformID; + internal static Platform localPlatform; + internal static Color localColor; public Dictionary players { get => _players; } public event Action? extendedPlayerConnectedEvent; @@ -44,6 +44,8 @@ public void Initialize() { localPlatformID = r.Result.platformUserId; localPlatform = r.Result.platform.ToPlatform(); + + _ = Statistics.AddUser(localPlatformID, (int)localPlatform); }); } diff --git a/MultiplayerExtensions/Sessions/SessionManager.cs b/MultiplayerExtensions/Sessions/SessionManager.cs index 054d655..0a68371 100644 --- a/MultiplayerExtensions/Sessions/SessionManager.cs +++ b/MultiplayerExtensions/Sessions/SessionManager.cs @@ -1,4 +1,5 @@ -using System; +using MultiplayerExtensions.Utilities; +using System; using Zenject; namespace MultiplayerExtensions.Sessions @@ -18,6 +19,7 @@ public void Initialize() MPState.CustomSongsEnabled = Plugin.Config.CustomSongs; MPState.FreeModEnabled = Plugin.Config.FreeMod; + MPState.HostPickEnabled = Plugin.Config.HostPick; _sessionManager.SetLocalPlayerState("modded", true); _sessionManager.SetLocalPlayerState("customsongs", Plugin.Config.CustomSongs); @@ -34,6 +36,7 @@ public void Dispose() private void HandleConnected() { MPState.LocalPlayerIsHost = _sessionManager.localPlayer.isConnectionOwner; + _ = Statistics.UseMaster(MPState.CurrentMasterServer.hostname, (int)ExtendedPlayerManager.localPlatform, ExtendedPlayerManager.localPlatformID, MPState.LocalPlayerIsHost); } private void HandlePlayerStateChanged(IConnectedPlayer player) diff --git a/MultiplayerExtensions/UI/ClientLobbySetupPanel.bsml b/MultiplayerExtensions/UI/ClientLobbySetupPanel.bsml index 2ec6fa5..5d6805e 100644 --- a/MultiplayerExtensions/UI/ClientLobbySetupPanel.bsml +++ b/MultiplayerExtensions/UI/ClientLobbySetupPanel.bsml @@ -1,13 +1,15 @@ - + - + + + diff --git a/MultiplayerExtensions/UI/ClientLobbySetupPanel.cs b/MultiplayerExtensions/UI/ClientLobbySetupPanel.cs index 877239e..481451f 100644 --- a/MultiplayerExtensions/UI/ClientLobbySetupPanel.cs +++ b/MultiplayerExtensions/UI/ClientLobbySetupPanel.cs @@ -35,6 +35,9 @@ internal void Inject(IMultiplayerSessionManager sessionManager, ClientLobbySetup [UIComponent("FreeModToggle")] public ToggleSetting freeModToggle = null!; + [UIComponent("HostPickToggle")] + public ToggleSetting hostPickToggle = null!; + [UIComponent("VerticalHUDToggle")] public ToggleSetting verticalHUDToggle = null!; @@ -44,6 +47,9 @@ internal void Inject(IMultiplayerSessionManager sessionManager, ClientLobbySetup [UIComponent("HologramToggle")] public ToggleSetting hologramToggle = null!; + [UIComponent("LagReducerToggle")] + public ToggleSetting lagReducerToggle = null!; + [UIComponent("DownloadProgressText")] public FormattableText downloadProgressText = null!; #endregion @@ -75,6 +81,20 @@ public bool FreeMod } } + [UIValue("HostPick")] + public bool HostPick + { + get => MPState.HostPickEnabled; + set + { + if (MPState.HostPickEnabled != value) + { + MPState.HostPickEnabled = value; + MPEvents.RaiseHostPickChanged(this, value); + } + } + } + [UIValue("VerticalHUD")] public bool VerticalHUD { @@ -96,6 +116,13 @@ public bool Hologram set { Plugin.Config.Hologram = value; } } + [UIValue("LagReducer")] + public bool LagReducer + { + get => Plugin.Config.LagReducer; + set { Plugin.Config.LagReducer = value; } + } + [UIValue("DownloadProgress")] public string DownloadProgress { @@ -120,6 +147,13 @@ public void SetFreeMod(bool value) SetModifierText(); } + [UIAction("SetHostPick")] + public void SetHostPick(bool value) + { + HostPick = value; + hostPickToggle.Value = value; + } + [UIAction("SetVerticalHUD")] public void SetVerticalHUD(bool value) { @@ -146,6 +180,13 @@ public void SetHologram(bool value) Hologram = value; hologramToggle.Value = value; } + + [UIAction("SetLagReducer")] + public void SetLagReducer(bool value) + { + LagReducer = value; + lagReducerToggle.Value = value; + } #endregion private void OnActivate(bool firstActivation, bool addedToHierarchy, bool screenSystemEnabling) @@ -168,10 +209,12 @@ private void OnPlayerStateChanged(IConnectedPlayer player) { customSongsToggle.interactable = false; freeModToggle.interactable = false; + hostPickToggle.interactable = false; if (player.userId != sessionManager.localPlayer.userId && player.isConnectionOwner) { SetCustomSongs(player.HasState("customsongs")); SetFreeMod(player.HasState("freemod")); + SetHostPick(player.HasState("hostpick")); } } diff --git a/MultiplayerExtensions/UI/HostLobbySetupPanel.bsml b/MultiplayerExtensions/UI/HostLobbySetupPanel.bsml index 985d615..b53c957 100644 --- a/MultiplayerExtensions/UI/HostLobbySetupPanel.bsml +++ b/MultiplayerExtensions/UI/HostLobbySetupPanel.bsml @@ -1,13 +1,15 @@ - + + - - - + + + + diff --git a/MultiplayerExtensions/UI/HostLobbySetupPanel.cs b/MultiplayerExtensions/UI/HostLobbySetupPanel.cs index b43521b..657a27c 100644 --- a/MultiplayerExtensions/UI/HostLobbySetupPanel.cs +++ b/MultiplayerExtensions/UI/HostLobbySetupPanel.cs @@ -35,6 +35,9 @@ internal void Inject(IMultiplayerSessionManager sessionManager, HostLobbySetupVi [UIComponent("FreeModToggle")] public ToggleSetting freeModToggle = null!; + [UIComponent("HostPickToggle")] + public ToggleSetting hostPickToggle = null!; + [UIComponent("VerticalHUDToggle")] public ToggleSetting verticalHUDToggle = null!; @@ -44,6 +47,9 @@ internal void Inject(IMultiplayerSessionManager sessionManager, HostLobbySetupVi [UIComponent("HologramToggle")] public ToggleSetting hologramToggle = null!; + [UIComponent("LagReducerToggle")] + public ToggleSetting lagReducerToggle = null!; + [UIComponent("DownloadProgressText")] public FormattableText downloadProgressText = null!; #endregion @@ -77,6 +83,21 @@ public bool FreeMod } } + [UIValue("HostPick")] + public bool HostPick + { + get => Plugin.Config.HostPick; + set + { + Plugin.Config.HostPick = value; + if (MPState.HostPickEnabled != value) + { + MPState.HostPickEnabled = value; + MPEvents.RaiseHostPickChanged(this, value); + } + } + } + [UIValue("VerticalHUD")] public bool VerticalHUD { @@ -98,6 +119,13 @@ public bool Hologram set { Plugin.Config.Hologram = value; } } + [UIValue("LagReducer")] + public bool LagReducer + { + get => Plugin.Config.LagReducer; + set { Plugin.Config.LagReducer = value; } + } + [UIValue("DownloadProgress")] public string DownloadProgress { @@ -126,6 +154,15 @@ public void SetFreeMod(bool value) SetModifierText(); } + [UIAction("SetHostPick")] + public void SetHostPick(bool value) + { + HostPick = value; + hostPickToggle.Value = value; + + UpdateStates(); + } + [UIAction("SetVerticalHUD")] public void SetVerticalHUD(bool value) { @@ -152,6 +189,13 @@ public void SetHologram(bool value) Hologram = value; hologramToggle.Value = value; } + + [UIAction("SetLagReducer")] + public void SetLagReducer(bool value) + { + LagReducer = value; + lagReducerToggle.Value = value; + } #endregion private void OnActivate(bool firstActivation, bool addedToHierarchy, bool screenSystemEnabling) @@ -167,6 +211,7 @@ private void UpdateStates() { sessionManager?.SetLocalPlayerState("customsongs", CustomSongs); sessionManager?.SetLocalPlayerState("freemod", FreeMod); + sessionManager?.SetLocalPlayerState("hostpick", HostPick); } private void SetModifierText() diff --git a/MultiplayerExtensions/Utilities/Config.cs b/MultiplayerExtensions/Utilities/Config.cs index f65deb5..9c41ba7 100644 --- a/MultiplayerExtensions/Utilities/Config.cs +++ b/MultiplayerExtensions/Utilities/Config.cs @@ -9,6 +9,8 @@ public class PluginConfig public virtual bool Hologram { get; set; } = true; public virtual bool CustomSongs { get; set; } = true; public virtual bool FreeMod { get; set; } = false; + public virtual bool LagReducer { get; set; } = false; + public virtual bool HostPick { get; set; } = true; public virtual string Color { get; set; } = "#08C0FF"; public virtual int MaxPlayers { get; set; } = 10; diff --git a/MultiplayerExtensions/Utilities/Statistics.cs b/MultiplayerExtensions/Utilities/Statistics.cs new file mode 100644 index 0000000..f40e854 --- /dev/null +++ b/MultiplayerExtensions/Utilities/Statistics.cs @@ -0,0 +1,79 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace MultiplayerExtensions.Utilities +{ + public static class Statistics + { + private const string BASE_URL = "https://mpex.goobwabber.com/api/v1"; + + // From com.github.roydejong.BeatSaberServerBrowser + private static async Task PerformWebRequest(string method, string endpoint, string json = null) + { + var targetUrl = BASE_URL + endpoint; + Plugin.Log?.Debug($"{method} {targetUrl} {json}"); + + try + { + HttpResponseMessage response; + + switch (method) + { + case "GET": + response = await Plugin.HttpClient.GetAsync(targetUrl).ConfigureAwait(false); + break; + case "POST": + if (String.IsNullOrEmpty(json)) + { + response = await Plugin.HttpClient.PostAsync(targetUrl, null).ConfigureAwait(false); + } + else + { + var content = new StringContent(json, Encoding.UTF8, "application/json"); + response = await Plugin.HttpClient.PostAsync(targetUrl, content).ConfigureAwait(false); + } + break; + default: + throw new ArgumentException($"Invalid request method for the Master Server API: {method}"); + } + + if (response.StatusCode != HttpStatusCode.OK) + { + throw new Exception($"Expected HTTP 200 OK, got HTTP {response.StatusCode}"); + } + + Plugin.Log?.Debug($"✔ 200 OK: {method} {targetUrl}"); + return response; + } + catch (TaskCanceledException ex) + { + return null; + } + catch (Exception ex) + { + Plugin.Log?.Error($"⚠ Request error: {method} {targetUrl} → {ex}"); + return null; + } + } + + public static async Task AddUser(string userId, int platform) + { + return await PerformWebRequest("POST", $"/adduser?userId={userId}&platform={platform}") != null; + } + + public static async Task PlayMap(string hash, string difficulty, string characteristic, int timePlayed, int platform, string hostname) + { + return await PerformWebRequest("POST", $"/playmap?hash={hash}&difficulty={difficulty}&characteristic={characteristic}&timePlayed={timePlayed}&platform={platform}&hostname={hostname}") != null; + } + + public static async Task UseMaster(string hostname, int platform, string? userId = null, bool host = false) + { + if (userId != null) + return await PerformWebRequest("POST", $"/usemaster?hostname={hostname}&userId={userId}&platform={platform}&host={host}") != null; + return await PerformWebRequest("POST", $"/usemaster?hostname={hostname}&platform={platform}&host={host}") != null; + } + } +} diff --git a/MultiplayerExtensions/manifest.json b/MultiplayerExtensions/manifest.json index 331bb88..d6e4025 100644 --- a/MultiplayerExtensions/manifest.json +++ b/MultiplayerExtensions/manifest.json @@ -3,9 +3,9 @@ "id": "MultiplayerExtensions", "name": "MultiplayerExtensions", "author": "Zingabopp and Goobwabber", - "version": "0.4.8", + "version": "0.4.9", "description": "Expands the functionality of Beat Saber Multiplayer.", - "gameVersion": "1.13.4", + "gameVersion": "1.16.1", "dependsOn": { "BSIPA": "^4.1.4", "SongCore": "^3.2.0",