| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Content.Shared.Voting;
- using Robust.Client;
- using Robust.Client.Audio;
- using Robust.Client.Console;
- using Robust.Client.GameObjects;
- using Robust.Client.ResourceManagement;
- using Robust.Client.UserInterface;
- using Robust.Shared.IoC;
- using Robust.Shared.Network;
- using Robust.Shared.Timing;
- using Robust.Shared.Player;
- using Robust.Shared.Audio;
- using Robust.Shared.Audio.Sources;
- using Robust.Shared.ContentPack;
- namespace Content.Client.Voting
- {
- public interface IVoteManager
- {
- void Initialize();
- void SendCastVote(int voteId, int option);
- void ClearPopupContainer();
- void SetPopupContainer(Control container);
- bool CanCallVote { get; }
- bool CanCallStandardVote(StandardVoteType type, out TimeSpan whenCan);
- event Action<bool> CanCallVoteChanged;
- event Action CanCallStandardVotesChanged;
- }
- public sealed class VoteManager : IVoteManager
- {
- [Dependency] private readonly IAudioManager _audio = default!;
- [Dependency] private readonly IBaseClient _client = default!;
- [Dependency] private readonly IClientConsoleHost _console = default!;
- [Dependency] private readonly IClientNetManager _netManager = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly IResourceCache _res = default!;
- private readonly Dictionary<StandardVoteType, TimeSpan> _standardVoteTimeouts = new();
- private readonly Dictionary<int, ActiveVote> _votes = new();
- private readonly Dictionary<int, UI.VotePopup> _votePopups = new();
- private Control? _popupContainer;
- private IAudioSource? _voteSource;
- public bool CanCallVote { get; private set; }
- public event Action<bool>? CanCallVoteChanged;
- public event Action? CanCallStandardVotesChanged;
- public void Initialize()
- {
- const string sound = "/Audio/Effects/voteding.ogg";
- _voteSource = _audio.CreateAudioSource(_res.GetResource<AudioResource>(sound));
- if (_voteSource != null)
- {
- _voteSource.Global = true;
- }
- _netManager.RegisterNetMessage<MsgVoteData>(ReceiveVoteData);
- _netManager.RegisterNetMessage<MsgVoteCanCall>(ReceiveVoteCanCall);
- _client.RunLevelChanged += ClientOnRunLevelChanged;
- }
- private void ClientOnRunLevelChanged(object? sender, RunLevelChangedEventArgs e)
- {
- // Clear votes on disconnect.
- if (e.NewLevel == ClientRunLevel.Initialize)
- {
- ClearPopupContainer();
- _votes.Clear();
- }
- }
- public bool CanCallStandardVote(StandardVoteType type, out TimeSpan whenCan)
- {
- return !_standardVoteTimeouts.TryGetValue(type, out whenCan);
- }
- public void ClearPopupContainer()
- {
- if (_popupContainer == null)
- return;
- if (!_popupContainer.Disposed)
- {
- foreach (var popup in _votePopups.Values)
- {
- popup.Orphan();
- }
- }
- _votePopups.Clear();
- _popupContainer = null;
- }
- public void SetPopupContainer(Control container)
- {
- if (_popupContainer != null)
- {
- ClearPopupContainer();
- }
- _popupContainer = container;
- SetVoteData();
- }
- private void SetVoteData()
- {
- if (_popupContainer == null)
- return;
- foreach (var (vId, vote) in _votes)
- {
- var popup = new UI.VotePopup(vote);
- _votePopups.Add(vId, popup);
- _popupContainer.AddChild(popup);
- popup.UpdateData();
- }
- }
- private void ReceiveVoteData(MsgVoteData message)
- {
- var @new = false;
- var voteId = message.VoteId;
- if (!_votes.TryGetValue(voteId, out var existingVote))
- {
- if (!message.VoteActive)
- {
- // Got "vote inactive" for nonexistent vote???
- return;
- }
- _voteSource?.Restart();
- @new = true;
- // Refresh
- var container = _popupContainer;
- ClearPopupContainer();
- if (container != null)
- SetPopupContainer(container);
- // New vote from the server.
- var vote = new ActiveVote(voteId)
- {
- Entries = message.Options
- .Select(c => new VoteEntry(c.name))
- .ToArray()
- };
- existingVote = vote;
- _votes.Add(voteId, vote);
- }
- else if (!message.VoteActive)
- {
- // Remove gone vote.
- _votes.Remove(voteId);
- if (_votePopups.TryGetValue(voteId, out var toRemove))
- {
- toRemove.Orphan();
- _votePopups.Remove(voteId);
- }
- return;
- }
- // Update vote data from incoming.
- if (message.IsYourVoteDirty)
- existingVote.OurVote = message.YourVote;
- // On the server, most of these params can't change.
- // It can't hurt to just re-set this stuff since I'm lazy and the server is sending it anyways, so...
- existingVote.Initiator = message.VoteInitiator;
- existingVote.Title = message.VoteTitle;
- existingVote.StartTime = _gameTiming.RealServerToLocal(message.StartTime);
- existingVote.EndTime = _gameTiming.RealServerToLocal(message.EndTime);
- existingVote.DisplayVotes = message.DisplayVotes;
- existingVote.TargetEntity = message.TargetEntity;
- // Logger.Debug($"{existingVote.StartTime}, {existingVote.EndTime}, {_gameTiming.RealTime}");
- for (var i = 0; i < message.Options.Length; i++)
- {
- existingVote.Entries[i].Votes = message.Options[i].votes;
- }
- if (@new && _popupContainer != null)
- {
- var popup = new UI.VotePopup(existingVote);
- _votePopups.Add(voteId, popup);
- _popupContainer.AddChild(popup);
- }
- if (_votePopups.TryGetValue(voteId, out var ePopup))
- {
- ePopup.UpdateData();
- }
- }
- private void ReceiveVoteCanCall(MsgVoteCanCall message)
- {
- if (CanCallVote != message.CanCall)
- {
- // TODO: actually use the "when can call vote" time for UI display or something.
- CanCallVote = message.CanCall;
- CanCallVoteChanged?.Invoke(CanCallVote);
- }
- _standardVoteTimeouts.Clear();
- foreach (var (type, time) in message.VotesUnavailable)
- {
- var fixedTime = (time == TimeSpan.Zero) ? time : _gameTiming.RealServerToLocal(time);
- _standardVoteTimeouts.Add(type, fixedTime);
- }
- CanCallStandardVotesChanged?.Invoke();
- }
- public void SendCastVote(int voteId, int option)
- {
- var data = _votes[voteId];
- // Update immediately to avoid any funny reconciliation bugs.
- // See also code in server side to avoid bulldozing this.
- data.OurVote = option;
- _console.LocalShell.RemoteExecuteCommand($"vote {voteId} {option}");
- }
- public sealed class ActiveVote
- {
- public VoteEntry[] Entries = default!;
- // Both of these are local RealTime (converted at NetMsg receive).
- public TimeSpan StartTime;
- public TimeSpan EndTime;
- public string Title = "";
- public string Initiator = "";
- public int? OurVote;
- public int Id;
- public bool DisplayVotes;
- public int? TargetEntity; // NetEntity
- public ActiveVote(int voteId)
- {
- Id = voteId;
- }
- }
- public sealed class VoteEntry
- {
- public string Text { get; }
- public int Votes { get; set; }
- public VoteEntry(string text)
- {
- Text = text;
- }
- }
- }
- }
|