* fix: lobby music volume will be changed on options change without restart (also lobby music not looped anymore)
* refactor: now lobby music is part of ContentAudioSystem. Lobby playlist is used instead of single track. Client now selects next lobby soundtrack after previous finished.
* refactor: incapsulated info on current lobby track in simple record
* refactor: fixed inconsistent naming between song and soundtrack for lobbymusic
* refactor: xml-doc for LobbyPlaylistChangedEvent
* fix: inverted invalid _audio.PlayGlobal check to return only if lobby soundtrack play call failed
---------
Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru>
+++ /dev/null
-using Content.Client.GameTicking.Managers;
-using Content.Client.Lobby;
-using Content.Shared.CCVar;
-using Content.Shared.GameTicking;
-using JetBrains.Annotations;
-using Robust.Client;
-using Robust.Client.State;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Configuration;
-using Robust.Shared.Player;
-
-namespace Content.Client.Audio;
-
-[UsedImplicitly]
-public sealed class BackgroundAudioSystem : EntitySystem
-{
- /*
- * TODO: Nuke this system and merge into contentaudiosystem
- */
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly IBaseClient _client = default!;
- [Dependency] private readonly IConfigurationManager _configManager = default!;
- [Dependency] private readonly ClientGameTicker _gameTicker = default!;
- [Dependency] private readonly IStateManager _stateManager = default!;
-
- private readonly AudioParams _lobbyParams = new(-5f, 1, "Master", 0, 0, 0, true, 0f);
- private readonly AudioParams _roundEndParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
-
- public EntityUid? LobbyMusicStream;
- public EntityUid? LobbyRoundRestartAudioStream;
-
- public override void Initialize()
- {
- base.Initialize();
-
- Subs.CVar(_configManager, CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged);
- Subs.CVar(_configManager, CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged);
-
- _stateManager.OnStateChanged += StateManagerOnStateChanged;
-
- _client.PlayerLeaveServer += OnLeave;
-
- _gameTicker.LobbySongUpdated += LobbySongUpdated;
-
- SubscribeNetworkEvent<RoundRestartCleanupEvent>(PlayRestartSound);
- }
-
- public override void Shutdown()
- {
- base.Shutdown();
-
- _stateManager.OnStateChanged -= StateManagerOnStateChanged;
-
- _client.PlayerLeaveServer -= OnLeave;
-
- _gameTicker.LobbySongUpdated -= LobbySongUpdated;
-
- EndLobbyMusic();
- }
-
- private void StateManagerOnStateChanged(StateChangedEventArgs args)
- {
- switch (args.NewState)
- {
- case LobbyState:
- StartLobbyMusic();
- break;
- default:
- EndLobbyMusic();
- break;
- }
- }
-
- private void OnLeave(object? sender, PlayerEventArgs args)
- {
- EndLobbyMusic();
- }
-
- private void LobbyMusicVolumeCVarChanged(float volume)
- {
- if (_stateManager.CurrentState is LobbyState)
- {
- RestartLobbyMusic();
- }
- }
-
- private void LobbyMusicCVarChanged(bool musicEnabled)
- {
- if (!musicEnabled)
- {
- EndLobbyMusic();
- }
- else if (_stateManager.CurrentState is LobbyState)
- {
- StartLobbyMusic();
- }
- else
- {
- EndLobbyMusic();
- }
- }
-
- private void LobbySongUpdated()
- {
- RestartLobbyMusic();
- }
-
- public void RestartLobbyMusic()
- {
- EndLobbyMusic();
- StartLobbyMusic();
- }
-
- public void StartLobbyMusic()
- {
- if (LobbyMusicStream != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled))
- return;
-
- var file = _gameTicker.LobbySong;
- if (file == null) // We have not received the lobby song yet.
- {
- return;
- }
-
- LobbyMusicStream = _audio.PlayGlobal(
- file,
- Filter.Local(),
- false,
- _lobbyParams.WithVolume(_lobbyParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume))))?.Entity;
- }
-
- private void EndLobbyMusic()
- {
- LobbyMusicStream = _audio.Stop(LobbyMusicStream);
- }
-
- private void PlayRestartSound(RoundRestartCleanupEvent ev)
- {
- if (!_configManager.GetCVar(CCVars.RestartSoundsEnabled))
- return;
-
- var file = _gameTicker.RestartSound;
- if (string.IsNullOrEmpty(file))
- {
- return;
- }
-
- LobbyRoundRestartAudioStream = _audio.PlayGlobal(
- file,
- Filter.Local(),
- false,
- _roundEndParams.WithVolume(_roundEndParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume)))
- )?.Entity;
- }
-}
--- /dev/null
+using System.Linq;
+using Content.Client.GameTicking.Managers;
+using Content.Client.Lobby;
+using Content.Shared.Audio.Events;
+using Content.Shared.CCVar;
+using Content.Shared.GameTicking;
+using Robust.Client;
+using Robust.Client.ResourceManagement;
+using Robust.Client.State;
+using Robust.Shared.Audio;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Player;
+using Robust.Shared.Timing;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Audio;
+
+// Part of ContentAudioSystem that is responsible for lobby music playing/stopping and round-end sound-effect.
+public sealed partial class ContentAudioSystem
+{
+ [Dependency] private readonly IBaseClient _client = default!;
+ [Dependency] private readonly ClientGameTicker _gameTicker = default!;
+ [Dependency] private readonly IStateManager _stateManager = default!;
+ [Dependency] private readonly IResourceCache _resourceCache = default!;
+
+ private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
+ private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
+
+ /// <summary>
+ /// EntityUid of lobby restart sound component.
+ /// </summary>
+ private EntityUid? _lobbyRoundRestartAudioStream;
+
+ /// <summary>
+ /// Shuffled list of soundtrack file-names.
+ /// </summary>
+ private string[]? _lobbyPlaylist;
+
+ /// <summary>
+ /// Short info about lobby soundtrack currently playing. Is null if soundtrack is not playing.
+ /// </summary>
+ private LobbySoundtrackInfo? _lobbySoundtrackInfo;
+
+ private Action<LobbySoundtrackChangedEvent>? _lobbySoundtrackChanged;
+
+ /// <summary>
+ /// Event for subscription on lobby soundtrack changes.
+ /// </summary>
+ public event Action<LobbySoundtrackChangedEvent>? LobbySoundtrackChanged
+ {
+ add
+ {
+ if (value != null)
+ {
+ if (_lobbySoundtrackInfo != null)
+ {
+ value(new LobbySoundtrackChangedEvent(_lobbySoundtrackInfo.Filename));
+ }
+
+ _lobbySoundtrackChanged += value;
+ }
+ }
+ remove => _lobbySoundtrackChanged -= value;
+ }
+
+ /// <summary>
+ /// Initializes subscriptions that are related to lobby music.
+ /// </summary>
+ private void InitializeLobbyMusic()
+ {
+ Subs.CVar(_configManager, CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged);
+ Subs.CVar(_configManager, CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged);
+
+ _stateManager.OnStateChanged += StateManagerOnStateChanged;
+
+ _client.PlayerLeaveServer += OnLeave;
+
+ SubscribeNetworkEvent<LobbyMusicStopEvent>(OnLobbySongStopped);
+ SubscribeNetworkEvent<LobbyPlaylistChangedEvent>(OnLobbySongChanged);
+ }
+
+ private void OnLobbySongStopped(LobbyMusicStopEvent ev)
+ {
+ EndLobbyMusic();
+ }
+
+ private void StateManagerOnStateChanged(StateChangedEventArgs args)
+ {
+ switch (args.NewState)
+ {
+ case LobbyState:
+ StartLobbyMusic();
+ break;
+ default:
+ EndLobbyMusic();
+ break;
+ }
+ }
+
+ private void OnLeave(object? sender, PlayerEventArgs args)
+ {
+ EndLobbyMusic();
+ }
+
+ private void LobbyMusicVolumeCVarChanged(float volume)
+ {
+ if (_lobbySoundtrackInfo != null)
+ {
+ _audio.SetVolume(
+ _lobbySoundtrackInfo.MusicStreamEntityUid,
+ _lobbySoundtrackParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume))
+ );
+ }
+ }
+
+ private void LobbyMusicCVarChanged(bool musicEnabled)
+ {
+ if (musicEnabled && _stateManager.CurrentState is LobbyState)
+ {
+ StartLobbyMusic();
+ }
+ else
+ {
+ EndLobbyMusic();
+ }
+ }
+
+ private void OnLobbySongChanged(LobbyPlaylistChangedEvent playlistChangedEvent)
+ {
+ var playlist = playlistChangedEvent.Playlist;
+ //playlist is already playing, no need to restart it
+ if (_lobbySoundtrackInfo != null
+ && _lobbyPlaylist != null
+ && _lobbyPlaylist.SequenceEqual(playlist)
+ )
+ {
+ return;
+ }
+
+ EndLobbyMusic();
+ StartLobbyMusic(playlistChangedEvent.Playlist);
+ }
+
+ /// <summary>
+ /// Re-starts playing lobby music from playlist, last sent from server. if there is currently none - does nothing.
+ /// </summary>
+ private void StartLobbyMusic()
+ {
+ if (_lobbyPlaylist == null || _lobbyPlaylist.Length == 0)
+ {
+ return;
+ }
+
+ StartLobbyMusic(_lobbyPlaylist);
+ }
+
+ /// <summary>
+ /// Starts playing lobby music from playlist. If playlist is empty, or lobby music setting is turned off - does nothing.
+ /// </summary>
+ /// <param name="playlist">Array of soundtrack filenames for lobby playlist.</param>
+ private void StartLobbyMusic(string[] playlist)
+ {
+ if (_lobbySoundtrackInfo != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled))
+ return;
+
+ _lobbyPlaylist = playlist;
+ if (_lobbyPlaylist.Length == 0)
+ {
+ return;
+ }
+
+ PlaySoundtrack(playlist[0]);
+ }
+
+ private void PlaySoundtrack(string soundtrackFilename)
+ {
+ if (!_resourceCache.TryGetResource(new ResPath(soundtrackFilename), out AudioResource? audio))
+ {
+ return;
+ }
+
+ var playResult = _audio.PlayGlobal(
+ soundtrackFilename,
+ Filter.Local(),
+ false,
+ _lobbySoundtrackParams.WithVolume(_lobbySoundtrackParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume)))
+ );
+ if (playResult.Value.Entity == default)
+ {
+ _sawmill.Warning(
+ $"Tried to play lobby soundtrack '{{Filename}}' using {nameof(SharedAudioSystem)}.{nameof(SharedAudioSystem.PlayGlobal)} but it returned default value of EntityUid!",
+ soundtrackFilename);
+ return;
+ }
+
+ var nextTrackOn = _timing.CurTime + audio.AudioStream.Length;
+ _lobbySoundtrackInfo = new LobbySoundtrackInfo(soundtrackFilename, nextTrackOn, playResult.Value.Entity);
+
+ var lobbySongChangedEvent = new LobbySoundtrackChangedEvent(soundtrackFilename);
+ _lobbySoundtrackChanged?.Invoke(lobbySongChangedEvent);
+ }
+
+ private void EndLobbyMusic()
+ {
+ if (_lobbySoundtrackInfo == null)
+ {
+ return;
+ }
+
+ _audio.Stop(_lobbySoundtrackInfo.MusicStreamEntityUid);
+ _lobbySoundtrackInfo = null;
+ var lobbySongChangedEvent = new LobbySoundtrackChangedEvent();
+ _lobbySoundtrackChanged?.Invoke(lobbySongChangedEvent);
+ }
+
+ private void PlayRestartSound(RoundRestartCleanupEvent ev)
+ {
+ if (!_configManager.GetCVar(CCVars.RestartSoundsEnabled))
+ return;
+
+ var file = _gameTicker.RestartSound;
+ if (string.IsNullOrEmpty(file))
+ {
+ return;
+ }
+
+ _lobbyRoundRestartAudioStream = _audio.PlayGlobal(
+ file,
+ Filter.Local(),
+ false,
+ _roundEndSoundEffectParams.WithVolume(_roundEndSoundEffectParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume)))
+ )?.Entity;
+ }
+
+ private void ShutdownLobbyMusic()
+ {
+ _stateManager.OnStateChanged -= StateManagerOnStateChanged;
+
+ _client.PlayerLeaveServer -= OnLeave;
+
+ EndLobbyMusic();
+ }
+
+ private void UpdateLobbyMusic()
+ {
+ if (
+ _lobbySoundtrackInfo != null
+ && _timing.CurTime >= _lobbySoundtrackInfo.NextTrackOn
+ && _lobbyPlaylist?.Length > 0
+ )
+ {
+ var nextSoundtrackFilename = GetNextSoundtrackFromPlaylist(_lobbySoundtrackInfo.Filename, _lobbyPlaylist);
+ PlaySoundtrack(nextSoundtrackFilename);
+ }
+ }
+
+ private static string GetNextSoundtrackFromPlaylist(string currentSoundtrackFilename, string[] playlist)
+ {
+ var indexOfCurrent = Array.IndexOf(playlist, currentSoundtrackFilename);
+ var nextTrackIndex = indexOfCurrent + 1;
+ if (nextTrackIndex > playlist.Length - 1)
+ {
+ nextTrackIndex = 0;
+ }
+
+ return playlist[nextTrackIndex];
+ }
+
+ /// <summary> Container for lobby soundtrack information. </summary>
+ /// <param name="Filename">Soundtrack filename.</param>
+ /// <param name="NextTrackOn">Time (based on <see cref="IGameTiming.CurTime"/>) when this track is going to finish playing and next track have to be started.</param>
+ /// <param name="MusicStreamEntityUid">
+ /// EntityUid of launched soundtrack (from <see cref="SharedAudioSystem.PlayGlobal(string,Robust.Shared.Player.Filter,bool,System.Nullable{Robust.Shared.Audio.AudioParams})"/>).
+ /// </param>
+ private sealed record LobbySoundtrackInfo(string Filename, TimeSpan NextTrackOn, EntityUid MusicStreamEntityUid);
+}
+
+/// <summary>
+/// Event of changing lobby soundtrack (or stopping lobby music - will pass null for <paramref name="SoundtrackFilename"/> in that case).
+/// Is used by <see cref="ContentAudioSystem.LobbySoundtrackChanged"/> and <see cref="LobbyState.UpdateLobbySoundtrackInfo"/>.
+/// </summary>
+/// <param name="SoundtrackFilename">Filename of newly set soundtrack, or null if soundtrack playback is stopped.</param>
+public sealed record LobbySoundtrackChangedEvent(string? SoundtrackFilename = null);
public const float AmbientMusicMultiplier = 3f;
public const float LobbyMultiplier = 3f;
public const float InterfaceMultiplier = 2f;
-
+
public override void Initialize()
{
base.Initialize();
+
UpdatesOutsidePrediction = true;
InitializeAmbientMusic();
+ InitializeLobbyMusic();
SubscribeNetworkEvent<RoundRestartCleanupEvent>(OnRoundCleanup);
}
_fadingOut.Clear();
// Preserve lobby music but everything else should get dumped.
- var lobbyMusic = EntityManager.System<BackgroundAudioSystem>().LobbyMusicStream;
+ var lobbyMusic = _lobbySoundtrackInfo?.MusicStreamEntityUid;
TryComp(lobbyMusic, out AudioComponent? lobbyMusicComp);
var oldMusicGain = lobbyMusicComp?.Gain;
- var restartAudio = EntityManager.System<BackgroundAudioSystem>().LobbyRoundRestartAudioStream;
+ var restartAudio = _lobbyRoundRestartAudioStream;
TryComp(restartAudio, out AudioComponent? restartComp);
var oldAudioGain = restartComp?.Gain;
{
Audio.SetGain(restartAudio, oldAudioGain.Value, restartComp);
}
+ PlayRestartSound(ev);
}
public override void Shutdown()
{
base.Shutdown();
ShutdownAmbientMusic();
+ ShutdownLobbyMusic();
}
public override void Update(float frameTime)
return;
UpdateAmbientMusic();
+ UpdateLobbyMusic();
UpdateFades(frameTime);
}
using Content.Client.Gameplay;
using Content.Client.Lobby;
using Content.Client.RoundEnd;
-using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.GameWindow;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.State;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Configuration;
-using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Content.Client.GameTicking.Managers
[ViewVariables] public bool AreWeReady { get; private set; }
[ViewVariables] public bool IsGameStarted { get; private set; }
- [ViewVariables] public string? LobbySong { get; private set; }
[ViewVariables] public string? RestartSound { get; private set; }
[ViewVariables] public string? LobbyBackground { get; private set; }
[ViewVariables] public bool DisallowedLateJoin { get; private set; }
public event Action? InfoBlobUpdated;
public event Action? LobbyStatusUpdated;
- public event Action? LobbySongUpdated;
public event Action? LobbyLateJoinStatusUpdated;
public event Action<IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>>>? LobbyJobsAvailableUpdated;
_initialized = true;
}
- public void SetLobbySong(string? song, bool forceUpdate = false)
- {
- var updated = song != LobbySong;
-
- LobbySong = song;
-
- if (updated || forceUpdate)
- LobbySongUpdated?.Invoke();
- }
-
private void LateJoinStatus(TickerLateJoinStatusEvent message)
{
DisallowedLateJoin = message.Disallowed;
RoundStartTimeSpan = message.RoundStartTimeSpan;
IsGameStarted = message.IsRoundStarted;
AreWeReady = message.YouAreReady;
- SetLobbySong(message.LobbySong);
LobbyBackground = message.LobbyBackground;
Paused = message.Paused;
private void RoundEnd(RoundEndMessageEvent message)
{
// Force an update in the event of this song being the same as the last.
- SetLobbySong(message.LobbySong, true);
RestartSound = message.RestartSound;
// Don't open duplicate windows (mainly for replays).
+using Content.Client.Audio;
using Content.Client.GameTicking.Managers;
using Content.Client.LateJoin;
using Content.Client.Lobby.UI;
[ViewVariables] private CharacterSetupGui? _characterSetup;
private ClientGameTicker _gameTicker = default!;
+ private ContentAudioSystem _contentAudioSystem = default!;
protected override Type? LinkedScreenType { get; } = typeof(LobbyGui);
private LobbyGui? _lobby;
var chatController = _userInterfaceManager.GetUIController<ChatUIController>();
_gameTicker = _entityManager.System<ClientGameTicker>();
+ _contentAudioSystem = _entityManager.System<ContentAudioSystem>();
+ _contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo;
_characterSetup = new CharacterSetupGui(_entityManager, _resourceCache, _preferencesManager,
_prototypeManager, _configurationManager);
LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide);
_gameTicker.InfoBlobUpdated -= UpdateLobbyUi;
_gameTicker.LobbyStatusUpdated -= LobbyStatusUpdated;
_gameTicker.LobbyLateJoinStatusUpdated -= LobbyLateJoinStatusUpdated;
+ _contentAudioSystem.LobbySoundtrackChanged -= UpdateLobbySoundtrackInfo;
_voteManager.ClearPopupContainer();
{
_lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob);
}
+ }
- if (_gameTicker.LobbySong == null)
+ private void UpdateLobbySoundtrackInfo(LobbySoundtrackChangedEvent ev)
+ {
+ if (ev.SoundtrackFilename == null)
{
_lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
}
- else if (_resourceCache.TryGetResource<AudioResource>(_gameTicker.LobbySong, out var lobbySongResource))
+ else if (
+ ev.SoundtrackFilename != null
+ && _resourceCache.TryGetResource<AudioResource>(ev.SoundtrackFilename, out var lobbySongResource)
+ )
{
var lobbyStream = lobbySongResource.AudioStream;
- var title = string.IsNullOrEmpty(lobbyStream.Title) ?
- Loc.GetString("lobby-state-song-unknown-title") :
- lobbyStream.Title;
+ var title = string.IsNullOrEmpty(lobbyStream.Title)
+ ? Loc.GetString("lobby-state-song-unknown-title")
+ : lobbyStream.Title;
- var artist = string.IsNullOrEmpty(lobbyStream.Artist) ?
- Loc.GetString("lobby-state-song-unknown-artist") :
- lobbyStream.Artist;
+ var artist = string.IsNullOrEmpty(lobbyStream.Artist)
+ ? Loc.GetString("lobby-state-song-unknown-artist")
+ : lobbyStream.Artist;
var markup = Loc.GetString("lobby-state-song-text",
("songTitle", title),
using Content.Client.Chat.UI;
using Content.Client.Info;
+using Content.Client.Message;
using Content.Client.Preferences;
using Content.Client.Preferences.UI;
using Content.Client.UserInterface.Screens;
SetAnchorPreset(MainContainer, LayoutPreset.Wide);
SetAnchorPreset(Background, LayoutPreset.Wide);
+ LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
+
LeaveButton.OnPressed += _ => _consoleHost.ExecuteCommand("disconnect");
OptionsButton.OnPressed += _ => _userInterfaceManager.GetUIController<OptionsUIController>().ToggleWindow();
}
+using System.Linq;
+using Content.Server.GameTicking;
using Content.Server.GameTicking.Events;
using Content.Shared.Audio;
+using Content.Shared.Audio.Events;
using Content.Shared.GameTicking;
using Robust.Server.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
namespace Content.Server.Audio;
public sealed class ContentAudioSystem : SharedContentAudioSystem
{
+ [ValidatePrototypeId<SoundCollectionPrototype>]
+ private const string LobbyMusicCollection = "LobbyMusic";
+
[Dependency] private readonly AudioSystem _serverAudio = default!;
+ [Dependency] private readonly IRobustRandom _robustRandom = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ private SoundCollectionPrototype _lobbyMusicCollection = default!;
+ private string[]? _lobbyPlaylist;
public override void Initialize()
{
base.Initialize();
+
+ _lobbyMusicCollection = _prototypeManager.Index<SoundCollectionPrototype>(LobbyMusicCollection);
+ _lobbyPlaylist = ShuffleLobbyPlaylist();
+
+ SubscribeLocalEvent<RoundEndMessageEvent>(OnRoundEnd);
+ SubscribeLocalEvent<PlayerJoinedLobbyEvent>(OnPlayerJoinedLobby);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundCleanup);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
// yeah it's whacky af.
_serverAudio.ReloadPresets();
}
+
+ private void OnPlayerJoinedLobby(PlayerJoinedLobbyEvent ev)
+ {
+ if (_lobbyPlaylist != null)
+ {
+ var session = ev.PlayerSession;
+ RaiseNetworkEvent(new LobbyPlaylistChangedEvent(_lobbyPlaylist), session);
+ }
+ }
+
+ private void OnRoundEnd(RoundEndMessageEvent ev)
+ {
+ // The lobby song is set here instead of in RestartRound,
+ // because ShowRoundEndScoreboard triggers the start of the music playing
+ // at the end of a round, and this needs to be set before RestartRound
+ // in order for the lobby song status display to be accurate.
+ _lobbyPlaylist = ShuffleLobbyPlaylist();
+ RaiseNetworkEvent(new LobbyPlaylistChangedEvent(_lobbyPlaylist));
+ }
+
+ private string[] ShuffleLobbyPlaylist()
+ {
+ var playlist = _lobbyMusicCollection.PickFiles
+ .Select(x => x.ToString())
+ .ToArray();
+ _robustRandom.Shuffle(playlist);
+
+ return playlist;
+ }
}
private TickerLobbyStatusEvent GetStatusMsg(ICommonSession session)
{
_playerGameStatuses.TryGetValue(session.UserId, out var status);
- return new TickerLobbyStatusEvent(RunLevel != GameRunLevel.PreRoundLobby, LobbySong, LobbyBackground, status == PlayerGameStatus.ReadyToPlay, _roundStartTime, RoundPreloadTime, RoundStartTimeSpan, Paused);
+ return new TickerLobbyStatusEvent(RunLevel != GameRunLevel.PreRoundLobby, LobbyBackground, status == PlayerGameStatus.ReadyToPlay, _roundStartTime, RoundPreloadTime, RoundStartTimeSpan, Paused);
}
private void SendStatusToAll()
+++ /dev/null
-using Robust.Shared.Audio;
-using Robust.Shared.Random;
-using Robust.Shared.Utility;
-
-namespace Content.Server.GameTicking
-{
- public sealed partial class GameTicker
- {
- [ValidatePrototypeId<SoundCollectionPrototype>]
- private const string LobbyMusicCollection = "LobbyMusic";
-
- [ViewVariables]
- private bool _lobbyMusicInitialized = false;
-
- [ViewVariables]
- private SoundCollectionPrototype _lobbyMusicCollection = default!;
-
- [ViewVariables]
- public string? LobbySong { get; private set; }
-
- private void InitializeLobbyMusic()
- {
- DebugTools.Assert(!_lobbyMusicInitialized);
- _lobbyMusicCollection = _prototypeManager.Index<SoundCollectionPrototype>(LobbyMusicCollection);
-
- // Now that the collection is set, the lobby music has been initialized and we can choose a random song.
- _lobbyMusicInitialized = true;
-
- ChooseRandomLobbySong();
- }
-
- /// <summary>
- /// Sets the current lobby song, or stops it if null.
- /// </summary>
- /// <param name="song">The lobby song to play, or null to stop any lobby songs.</param>
- public void SetLobbySong(string? song)
- {
- DebugTools.Assert(_lobbyMusicInitialized);
-
- if (song == null)
- {
- LobbySong = null;
- return;
- // TODO GAMETICKER send song stop event
- }
-
- LobbySong = song;
- // TODO GAMETICKER send song change event
- }
-
- /// <summary>
- /// Plays a random song from the LobbyMusic sound collection.
- /// </summary>
- public void ChooseRandomLobbySong()
- {
- DebugTools.Assert(_lobbyMusicInitialized);
- SetLobbySong(_robustRandom.Pick(_lobbyMusicCollection.PickFiles).ToString());
- }
-
- /// <summary>
- /// Stops the current lobby song being played.
- /// </summary>
- public void StopLobbySong()
- {
- SetLobbySong(null);
- }
- }
-}
RunLevel = GameRunLevel.PostRound;
- // The lobby song is set here instead of in RestartRound,
- // because ShowRoundEndScoreboard triggers the start of the music playing
- // at the end of a round, and this needs to be set before RestartRound
- // in order for the lobby song status display to be accurate.
- LobbySong = _robustRandom.Pick(_lobbyMusicCollection.PickFiles).ToString();
-
ShowRoundEndScoreboard(text);
SendRoundEndDiscordMessage();
}
var listOfPlayerInfoFinal = listOfPlayerInfo.OrderBy(pi => pi.PlayerOOCName).ToArray();
var sound = RoundEndSoundCollection == null ? null : _audio.GetSound(new SoundCollectionSpecifier(RoundEndSoundCollection));
- RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, RoundId,
- listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal, LobbySong, sound));
+ var roundEndMessageEvent = new RoundEndMessageEvent(
+ gamemodeTitle,
+ roundEndText,
+ roundDuration,
+ RoundId,
+ listOfPlayerInfoFinal.Length,
+ listOfPlayerInfoFinal,
+ sound
+ );
+ RaiseNetworkEvent(roundEndMessageEvent);
+ RaiseLocalEvent(roundEndMessageEvent);
_replayRoundPlayerInfo = listOfPlayerInfoFinal;
_replayRoundText = roundEndText;
InitializeStatusShell();
InitializeCVars();
InitializePlayer();
- InitializeLobbyMusic();
InitializeLobbyBackground();
InitializeGamePreset();
DebugTools.Assert(_prototypeManager.Index<JobPrototype>(FallbackOverflowJob).Name == FallbackOverflowJobName,
--- /dev/null
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Audio.Events;
+
+/// <summary>
+/// Event of changing lobby music playlist (on server).
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class LobbyPlaylistChangedEvent : EntityEventArgs
+{
+ /// <inheritdoc />
+ public LobbyPlaylistChangedEvent(string[] playlist)
+ {
+ Playlist = playlist;
+ }
+
+ /// <summary>
+ /// List of soundtrack filenames for lobby playlist.
+ /// </summary>
+ public string[] Playlist;
+}
+
+/// <summary>
+/// Event of stopping lobby music.
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class LobbyMusicStopEvent : EntityEventArgs
+{
+}
public sealed class TickerLobbyStatusEvent : EntityEventArgs
{
public bool IsRoundStarted { get; }
- public string? LobbySong { get; }
public string? LobbyBackground { get; }
public bool YouAreReady { get; }
// UTC.
public TimeSpan RoundStartTimeSpan { get; }
public bool Paused { get; }
- public TickerLobbyStatusEvent(bool isRoundStarted, string? lobbySong, string? lobbyBackground, bool youAreReady, TimeSpan startTime, TimeSpan preloadTime, TimeSpan roundStartTimeSpan, bool paused)
+ public TickerLobbyStatusEvent(bool isRoundStarted, string? lobbyBackground, bool youAreReady, TimeSpan startTime, TimeSpan preloadTime, TimeSpan roundStartTimeSpan, bool paused)
{
IsRoundStarted = isRoundStarted;
- LobbySong = lobbySong;
LobbyBackground = lobbyBackground;
YouAreReady = youAreReady;
StartTime = startTime;
public int RoundId { get; }
public int PlayerCount { get; }
public RoundEndPlayerInfo[] AllPlayersEndInfo { get; }
- public string? LobbySong;
/// <summary>
/// Sound gets networked due to how entity lifecycle works between client / server and to avoid clipping.
int roundId,
int playerCount,
RoundEndPlayerInfo[] allPlayersEndInfo,
- string? lobbySong,
string? restartSound)
{
GamemodeTitle = gamemodeTitle;
RoundId = roundId;
PlayerCount = playerCount;
AllPlayersEndInfo = allPlayersEndInfo;
- LobbySong = lobbySong;
RestartSound = restartSound;
}
}