diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj
index 3ac45239996..257999b8c13 100644
--- a/Content.Client/Content.Client.csproj
+++ b/Content.Client/Content.Client.csproj
@@ -22,8 +22,6 @@
-
-
diff --git a/Content.Client/Corvax/DiscordAuth/DiscordAuthGui.xaml b/Content.Client/Corvax/DiscordAuth/DiscordAuthGui.xaml
new file mode 100644
index 00000000000..389854ed529
--- /dev/null
+++ b/Content.Client/Corvax/DiscordAuth/DiscordAuthGui.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Corvax/DiscordAuth/DiscordAuthGui.xaml.cs b/Content.Client/Corvax/DiscordAuth/DiscordAuthGui.xaml.cs
new file mode 100644
index 00000000000..3427f5ff7f4
--- /dev/null
+++ b/Content.Client/Corvax/DiscordAuth/DiscordAuthGui.xaml.cs
@@ -0,0 +1,35 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.Console;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Corvax.DiscordAuth;
+
+[GenerateTypedNameReferences]
+public sealed partial class DiscordAuthGui : Control
+{
+ [Dependency] private readonly DiscordAuthManager _discordAuthManager = default!;
+ [Dependency] private readonly IClientConsoleHost _consoleHost = default!;
+
+ public DiscordAuthGui()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
+
+ QuitButton.OnPressed += (_) =>
+ {
+ _consoleHost.ExecuteCommand("quit");
+ };
+
+ UrlEdit.Text = _discordAuthManager.AuthUrl;
+ OpenUrlButton.OnPressed += (_) =>
+ {
+ if (_discordAuthManager.AuthUrl != string.Empty)
+ {
+ IoCManager.Resolve().OpenUri(_discordAuthManager.AuthUrl);
+ }
+ };
+ }
+}
diff --git a/Content.Client/Corvax/DiscordAuth/DiscordAuthManager.cs b/Content.Client/Corvax/DiscordAuth/DiscordAuthManager.cs
new file mode 100644
index 00000000000..ea3944c8532
--- /dev/null
+++ b/Content.Client/Corvax/DiscordAuth/DiscordAuthManager.cs
@@ -0,0 +1,30 @@
+using System.Threading;
+using Content.Shared.Corvax.DiscordAuth;
+using Robust.Client.State;
+using Robust.Shared.Network;
+using Timer = Robust.Shared.Timing.Timer;
+
+namespace Content.Client.Corvax.DiscordAuth;
+
+public sealed class DiscordAuthManager
+{
+ [Dependency] private readonly IClientNetManager _netManager = default!;
+ [Dependency] private readonly IStateManager _stateManager = default!;
+
+ public string AuthUrl { get; private set; } = string.Empty;
+
+ public void Initialize()
+ {
+ _netManager.RegisterNetMessage();
+ _netManager.RegisterNetMessage(OnDiscordAuthRequired);
+ }
+
+ private void OnDiscordAuthRequired(MsgDiscordAuthRequired message)
+ {
+ if (_stateManager.CurrentState is not DiscordAuthState)
+ {
+ AuthUrl = message.AuthUrl;
+ _stateManager.RequestStateChange();
+ }
+ }
+}
diff --git a/Content.Client/Corvax/DiscordAuth/DiscordAuthState.cs b/Content.Client/Corvax/DiscordAuth/DiscordAuthState.cs
new file mode 100644
index 00000000000..b7512ec7fcd
--- /dev/null
+++ b/Content.Client/Corvax/DiscordAuth/DiscordAuthState.cs
@@ -0,0 +1,34 @@
+using System.Threading;
+using Content.Shared.Corvax.DiscordAuth;
+using Robust.Client.State;
+using Robust.Client.UserInterface;
+using Robust.Shared.Network;
+using Timer = Robust.Shared.Timing.Timer;
+
+namespace Content.Client.Corvax.DiscordAuth;
+
+public sealed class DiscordAuthState : State
+{
+ [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
+ [Dependency] private readonly IClientNetManager _netManager = default!;
+
+ private DiscordAuthGui? _gui;
+ private readonly CancellationTokenSource _checkTimerCancel = new();
+
+ protected override void Startup()
+ {
+ _gui = new DiscordAuthGui();
+ _userInterfaceManager.StateRoot.AddChild(_gui);
+
+ Timer.SpawnRepeating(TimeSpan.FromSeconds(5), () =>
+ {
+ _netManager.ClientSendMessage(new MsgDiscordAuthCheck());
+ }, _checkTimerCancel.Token);
+ }
+
+ protected override void Shutdown()
+ {
+ _checkTimerCancel.Cancel();
+ _gui!.Dispose();
+ }
+}
diff --git a/Content.Client/Corvax/JoinQueue/JoinQueueManager.cs b/Content.Client/Corvax/JoinQueue/JoinQueueManager.cs
new file mode 100644
index 00000000000..78a5ed3de70
--- /dev/null
+++ b/Content.Client/Corvax/JoinQueue/JoinQueueManager.cs
@@ -0,0 +1,26 @@
+using Content.Shared.Corvax.JoinQueue;
+using Robust.Client.State;
+using Robust.Shared.Network;
+
+namespace Content.Client.Corvax.JoinQueue;
+
+public sealed class JoinQueueManager
+{
+ [Dependency] private readonly IClientNetManager _netManager = default!;
+ [Dependency] private readonly IStateManager _stateManager = default!;
+
+ public void Initialize()
+ {
+ _netManager.RegisterNetMessage(OnQueueUpdate);
+ }
+
+ private void OnQueueUpdate(MsgQueueUpdate msg)
+ {
+ if (_stateManager.CurrentState is not QueueState)
+ {
+ _stateManager.RequestStateChange();
+ }
+
+ ((QueueState) _stateManager.CurrentState).OnQueueUpdate(msg);
+ }
+}
\ No newline at end of file
diff --git a/Content.Client/Corvax/JoinQueue/QueueGui.xaml b/Content.Client/Corvax/JoinQueue/QueueGui.xaml
new file mode 100644
index 00000000000..5ad739b4f9f
--- /dev/null
+++ b/Content.Client/Corvax/JoinQueue/QueueGui.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Corvax/JoinQueue/QueueGui.xaml.cs b/Content.Client/Corvax/JoinQueue/QueueGui.xaml.cs
new file mode 100644
index 00000000000..391d464667a
--- /dev/null
+++ b/Content.Client/Corvax/JoinQueue/QueueGui.xaml.cs
@@ -0,0 +1,41 @@
+using Content.Shared.CCVar;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Configuration;
+
+namespace Content.Client.Corvax.JoinQueue;
+
+[GenerateTypedNameReferences]
+public sealed partial class QueueGui : Control
+{
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+
+ public event Action? QuitPressed;
+
+ public QueueGui()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
+
+ QuitButton.OnPressed += (_) => QuitPressed?.Invoke();
+
+ // Disable "priority join" button on Steam builds
+ // since it violates Valve's rules about alternative storefronts.
+ PriorityJoinButton.Visible = !_cfg.GetCVar(CCVars.BrandingSteam);
+
+ PriorityJoinButton.OnPressed += (_) =>
+ {
+ var linkPatreon = _cfg.GetCVar(CCVars.InfoLinksPatreon);
+ IoCManager.Resolve().OpenUri(linkPatreon);
+ };
+ }
+
+ public void UpdateInfo(int total, int position)
+ {
+ QueueTotal.Text = total.ToString();
+ QueuePosition.Text = position.ToString();
+ }
+}
diff --git a/Content.Client/Corvax/JoinQueue/QueueState.cs b/Content.Client/Corvax/JoinQueue/QueueState.cs
new file mode 100644
index 00000000000..4307a1ca2d7
--- /dev/null
+++ b/Content.Client/Corvax/JoinQueue/QueueState.cs
@@ -0,0 +1,52 @@
+using Content.Shared.Corvax.JoinQueue;
+using Robust.Client.Console;
+using Robust.Client.GameObjects;
+using Robust.Client.State;
+using Robust.Client.UserInterface;
+using Robust.Shared.Player;
+
+namespace Content.Client.Corvax.JoinQueue;
+
+public sealed class QueueState : State
+{
+ [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
+ [Dependency] private readonly IClientConsoleHost _consoleHost = default!;
+
+ private const string JoinSoundPath = "/Audio/Effects/voteding.ogg";
+
+ private QueueGui? _gui;
+
+ protected override void Startup()
+ {
+ _gui = new QueueGui();
+ _userInterfaceManager.StateRoot.AddChild(_gui);
+
+ _gui.QuitPressed += OnQuitPressed;
+ }
+
+ protected override void Shutdown()
+ {
+ _gui!.QuitPressed -= OnQuitPressed;
+ _gui.Dispose();
+
+ Ding();
+ }
+
+ private void Ding()
+ {
+ if (IoCManager.Resolve().TrySystem(out var audio))
+ {
+ audio.PlayGlobal(JoinSoundPath, Filter.Local(), false);
+ }
+ }
+
+ public void OnQueueUpdate(MsgQueueUpdate msg)
+ {
+ _gui?.UpdateInfo(msg.Total, msg.Position);
+ }
+
+ private void OnQuitPressed()
+ {
+ _consoleHost.ExecuteCommand("quit");
+ }
+}
\ No newline at end of file
diff --git a/Content.Client/Corvax/Sponsors/SponsorsManager.cs b/Content.Client/Corvax/Sponsors/SponsorsManager.cs
new file mode 100644
index 00000000000..b5e300d42aa
--- /dev/null
+++ b/Content.Client/Corvax/Sponsors/SponsorsManager.cs
@@ -0,0 +1,23 @@
+using System.Diagnostics.CodeAnalysis;
+using Content.Shared.Corvax.Sponsors;
+using Robust.Shared.Network;
+
+namespace Content.Client.Corvax.Sponsors;
+
+public sealed class SponsorsManager
+{
+ [Dependency] private readonly IClientNetManager _netMgr = default!;
+
+ private SponsorInfo? _info;
+
+ public void Initialize()
+ {
+ _netMgr.RegisterNetMessage(msg => _info = msg.Info);
+ }
+
+ public bool TryGetInfo([NotNullWhen(true)] out SponsorInfo? sponsor)
+ {
+ sponsor = _info;
+ return _info != null;
+ }
+}
diff --git a/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs b/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs
index da6dd7147d5..87f29e0a54a 100644
--- a/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs
+++ b/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs
@@ -1,9 +1,9 @@
using System.Linq;
+using Content.Client.Corvax.Sponsors;
using Content.Client.Corvax.TTS;
using Content.Shared.Corvax.TTS;
using Content.Shared.Preferences;
using Robust.Shared.Random;
-using Content.Corvax.Interfaces.Client;
namespace Content.Client.Preferences.UI;
@@ -11,7 +11,6 @@ public sealed partial class HumanoidProfileEditor
{
private IRobustRandom _random = default!;
private TTSSystem _ttsSys = default!;
- private IClientSponsorsManager? _sponsorsMgr;
private List _voiceList = default!;
private readonly List _sampleText = new()
{
@@ -23,9 +22,6 @@ public sealed partial class HumanoidProfileEditor
private void InitializeVoice()
{
- if (!IoCManager.Instance!.TryResolveType(out _sponsorsMgr))
- return;
-
_random = IoCManager.Resolve();
_ttsSys = _entMan.System();
_voiceList = _prototypeManager
@@ -45,8 +41,7 @@ private void InitializeVoice()
private void UpdateTTSVoicesControls()
{
- if (Profile is null ||
- _sponsorsMgr is null)
+ if (Profile is null)
return;
_voiceButton.Clear();
@@ -65,7 +60,7 @@ private void UpdateTTSVoicesControls()
firstVoiceChoiceId = i;
if (voice.SponsorOnly &&
- _sponsorsMgr.TryGetInfo(out var sponsor) &&
+ IoCManager.Resolve().TryGetInfo(out var sponsor) &&
!sponsor.AllowedMarkings.Contains(voice.ID))
{
_voiceButton.SetItemDisabled(_voiceButton.GetIdx(i), true);
diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs
index dcc79d7b3aa..c59e4b9d8d6 100644
--- a/Content.Client/Entry/EntryPoint.cs
+++ b/Content.Client/Entry/EntryPoint.cs
@@ -1,6 +1,9 @@
using Content.Client.Administration.Managers;
using Content.Client.Changelog;
using Content.Client.Chat.Managers;
+using Content.Client.Corvax.DiscordAuth;
+using Content.Client.Corvax.JoinQueue;
+using Content.Client.Corvax.Sponsors;
using Content.Client.Corvax.TTS;
using Content.Client.Options;
using Content.Client.Eui;
@@ -66,6 +69,9 @@ public sealed class EntryPoint : GameClient
[Dependency] private readonly ExtendedDisconnectInformationManager _extendedDisconnectInformation = default!;
[Dependency] private readonly JobRequirementsManager _jobRequirements = default!;
[Dependency] private readonly ContentLocalizationManager _contentLoc = default!;
+ [Dependency] private readonly SponsorsManager _sponsorsManager = default!; // Corvax-Sponsors
+ [Dependency] private readonly JoinQueueManager _queueManager = default!; // Corvax-Queue
+ [Dependency] private readonly DiscordAuthManager _discordAuthManager = default!; // Corvax-DiscordAuth
[Dependency] private readonly ContentReplayPlaybackManager _playbackMan = default!;
[Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly IReplayLoadManager _replayLoad = default!;
@@ -163,6 +169,9 @@ public override void PostInit()
_voteManager.Initialize();
_userInterfaceManager.SetDefaultTheme("SS14DefaultTheme");
_userInterfaceManager.SetActiveTheme(_configManager.GetCVar(CVars.InterfaceTheme));
+ _sponsorsManager.Initialize(); // Corvax-Sponsors
+ _queueManager.Initialize(); // Corvax-Queue
+ _discordAuthManager.Initialize(); // Corvax-DiscordAuth
_documentParsingManager.Initialize();
_baseClient.RunLevelChanged += (_, args) =>
diff --git a/Content.Client/Humanoid/MarkingPicker.xaml.cs b/Content.Client/Humanoid/MarkingPicker.xaml.cs
index c8935d46574..eb366f761ac 100644
--- a/Content.Client/Humanoid/MarkingPicker.xaml.cs
+++ b/Content.Client/Humanoid/MarkingPicker.xaml.cs
@@ -1,5 +1,5 @@
using System.Linq;
-using Content.Corvax.Interfaces.Client;
+using Content.Client.Corvax.Sponsors;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
@@ -19,7 +19,7 @@ public sealed partial class MarkingPicker : Control
{
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- private IClientSponsorsManager? _sponsorsManager; // Corvax-Sponsors
+ [Dependency] private readonly SponsorsManager _sponsorsManager = default!; // Corvax-Sponsors
public Action? OnMarkingAdded;
public Action? OnMarkingRemoved;
@@ -125,7 +125,6 @@ public MarkingPicker()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
SetupCategoryButtons();
CMarkingCategoryButton.OnItemSelected += OnCategoryChange;
@@ -209,8 +208,7 @@ public void Populate(string filter)
if (marking.SponsorOnly)
{
item.Disabled = true;
- if (_sponsorsManager != null &&
- _sponsorsManager.TryGetInfo(out var sponsor))
+ if (_sponsorsManager.TryGetInfo(out var sponsor))
{
item.Disabled = !sponsor.AllowedMarkings.Contains(marking.ID);
}
diff --git a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs
index 7cd241c5bc3..c6f9f0b0481 100644
--- a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs
+++ b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs
@@ -1,5 +1,5 @@
using System.Linq;
-using Content.Corvax.Interfaces.Client;
+using Content.Client.Corvax.Sponsors;
using Content.Shared.Humanoid.Markings;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
@@ -12,7 +12,7 @@ namespace Content.Client.Humanoid;
public sealed partial class SingleMarkingPicker : BoxContainer
{
[Dependency] private readonly MarkingManager _markingManager = default!;
- private IClientSponsorsManager? _sponsorsManager; // Corvax-Sponsors
+ [Dependency] private readonly SponsorsManager _sponsorsManager = default!; // Corvax-Sponsors
///
/// What happens if a marking is selected.
@@ -124,7 +124,6 @@ public SingleMarkingPicker()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
MarkingList.OnItemSelected += SelectMarking;
AddButton.OnPressed += _ =>
@@ -197,8 +196,7 @@ public void PopulateList(string filter)
if (marking.SponsorOnly)
{
item.Disabled = true;
- if (_sponsorsManager != null &&
- _sponsorsManager.TryGetInfo(out var sponsor))
+ if (_sponsorsManager.TryGetInfo(out var sponsor))
{
item.Disabled = !sponsor.AllowedMarkings.Contains(marking.ID);
}
diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs
index d52352493b6..ba60740b0e0 100644
--- a/Content.Client/IoC/ClientContentIoC.cs
+++ b/Content.Client/IoC/ClientContentIoC.cs
@@ -2,6 +2,9 @@
using Content.Client.Changelog;
using Content.Client.Chat.Managers;
using Content.Client.Clickable;
+using Content.Client.Corvax.DiscordAuth;
+using Content.Client.Corvax.JoinQueue;
+using Content.Client.Corvax.Sponsors;
using Content.Client.Corvax.TTS;
using Content.Client.Options;
using Content.Client.Eui;
@@ -45,6 +48,9 @@ public static void Register()
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
+ IoCManager.Register(); // Corvax-Sponsors
+ IoCManager.Register(); // Corvax-Queue
+ IoCManager.Register(); // Corvax-DiscordAuth
IoCManager.Register();
IoCManager.Register();
}
diff --git a/Content.Client/Preferences/ClientPreferencesManager.cs b/Content.Client/Preferences/ClientPreferencesManager.cs
index f4519cb9cd7..f3d77b3154b 100644
--- a/Content.Client/Preferences/ClientPreferencesManager.cs
+++ b/Content.Client/Preferences/ClientPreferencesManager.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Content.Corvax.Interfaces.Client;
+using Content.Client.Corvax.Sponsors;
using Content.Shared.Preferences;
using Robust.Client;
using Robust.Shared.IoC;
@@ -19,7 +19,7 @@ public sealed class ClientPreferencesManager : IClientPreferencesManager
{
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly IBaseClient _baseClient = default!;
- private IClientSponsorsManager? _sponsorsManager; // Corvax-Sponsors
+ [Dependency] private readonly SponsorsManager _sponsorsManager = default!; // Corvax-Sponsors
public event Action? OnServerDataLoaded;
@@ -28,7 +28,6 @@ public sealed class ClientPreferencesManager : IClientPreferencesManager
public void Initialize()
{
- IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
_netManager.RegisterNetMessage(HandlePreferencesAndSettings);
_netManager.RegisterNetMessage();
_netManager.RegisterNetMessage();
@@ -64,9 +63,7 @@ public void SelectCharacter(int slot)
public void UpdateCharacter(ICharacterProfile profile, int slot)
{
// Corvax-Sponsors-Start
- var allowedMarkings = _sponsorsManager != null && _sponsorsManager.TryGetInfo(out var sponsor)
- ? sponsor.AllowedMarkings
- : new string[]{};
+ var allowedMarkings = _sponsorsManager.TryGetInfo(out var sponsor) ? sponsor.AllowedMarkings : new string[]{};
profile.EnsureValid(allowedMarkings);
// Corvax-Sponsors-End
var characters = new Dictionary(Preferences.Characters) {[slot] = profile};
diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs
index 40a910e345b..1f3045cec8d 100644
--- a/Content.Server/Chat/Managers/ChatManager.cs
+++ b/Content.Server/Chat/Managers/ChatManager.cs
@@ -1,8 +1,8 @@
using System.Linq;
-using Content.Corvax.Interfaces.Server;
using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;
using Content.Server.Administration.Systems;
+using Content.Server.Corvax.Sponsors;
using Content.Server.MoMMI;
using Content.Server.Preferences.Managers;
using Content.Shared.Administration;
@@ -40,9 +40,9 @@ internal sealed class ChatManager : IChatManager
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
+ [Dependency] private readonly SponsorsManager _sponsorsManager = default!; // Corvax-Sponsors
[Dependency] private readonly INetConfigurationManager _netConfigManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
- private IServerSponsorsManager? _sponsorsManager; // Corvax-Sponsors
///
/// The maximum length a player-sent message can be sent
@@ -54,7 +54,6 @@ internal sealed class ChatManager : IChatManager
public void Initialize()
{
- IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
_netManager.RegisterNetMessage();
_configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true);
@@ -207,9 +206,7 @@ private void SendOOC(IPlayerSession player, string message)
}
// Corvax-Sponsors-Start
- if (_sponsorsManager != null &&
- _sponsorsManager.TryGetInfo(player.UserId, out var sponsorData) &&
- sponsorData.OOCColor != null)
+ if (_sponsorsManager.TryGetInfo(player.UserId, out var sponsorData) && sponsorData.OOCColor != null)
{
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", sponsorData.OOCColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
}
diff --git a/Content.Server/Connection/ConnectionManager.cs b/Content.Server/Connection/ConnectionManager.cs
index c176acb8629..543c996bc96 100644
--- a/Content.Server/Connection/ConnectionManager.cs
+++ b/Content.Server/Connection/ConnectionManager.cs
@@ -1,6 +1,6 @@
using System.Collections.Immutable;
using System.Threading.Tasks;
-using Content.Corvax.Interfaces.Server;
+using Content.Server.Corvax.Sponsors;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Server.Preferences.Managers;
@@ -31,12 +31,11 @@ public sealed class ConnectionManager : IConnectionManager
[Dependency] private readonly IServerNetManager _netMgr = default!;
[Dependency] private readonly IServerDbManager _db = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly SponsorsManager _sponsorsManager = default!; // Corvax-Sponsors
[Dependency] private readonly ILocalizationManager _loc = default!;
- private IServerSponsorsManager? _sponsorsMgr; // Corvax-Sponsors
public void Initialize()
{
- IoCManager.Instance!.TryResolveType(out _sponsorsMgr); // Corvax-Sponsors
_netMgr.Connecting += NetMgrOnConnecting;
_netMgr.AssignUserIdCallback = AssignUserIdCallback;
// Approval-based IP bans disabled because they don't play well with Happy Eyeballs.
@@ -202,7 +201,7 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e)
public async Task HavePrivilegedJoin(NetUserId userId)
{
var isAdmin = await _dbManager.GetAdminDataForAsync(userId) != null;
- var havePriorityJoin = _sponsorsMgr != null && _sponsorsMgr.TryGetInfo(userId, out var sponsor) && sponsor.HavePriorityJoin; // Corvax-Sponsors
+ var havePriorityJoin = _sponsorsManager.TryGetInfo(userId, out var sponsor) && sponsor.HavePriorityJoin; // Corvax-Sponsors
var wasInGame = EntitySystem.TryGet(out var ticker) &&
ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
status == PlayerGameStatus.JoinedGame;
diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj
index 778b4d0281b..3aba36c788d 100644
--- a/Content.Server/Content.Server.csproj
+++ b/Content.Server/Content.Server.csproj
@@ -24,7 +24,6 @@
-
diff --git a/Content.Server/Corvax/DiscordAuth/DiscordAuthManager.cs b/Content.Server/Corvax/DiscordAuth/DiscordAuthManager.cs
new file mode 100644
index 00000000000..627720cd163
--- /dev/null
+++ b/Content.Server/Corvax/DiscordAuth/DiscordAuthManager.cs
@@ -0,0 +1,125 @@
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Content.Shared.Corvax.CCCVars;
+using Content.Shared.Corvax.DiscordAuth;
+using JetBrains.Annotations;
+using Robust.Server.Player;
+using Robust.Shared.Configuration;
+using Robust.Shared.Enums;
+using Robust.Shared.Network;
+
+namespace Content.Server.Corvax.DiscordAuth;
+
+// TODO: Add minimal Discord account age check for panic bunker by extracting timestamp from snowflake received from API secured with key
+
+///
+/// Manage Discord linking with SS14 account through external API
+///
+public sealed class DiscordAuthManager
+{
+ [Dependency] private readonly IServerNetManager _netMgr = default!;
+ [Dependency] private readonly IPlayerManager _playerMgr = default!;
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+
+ private ISawmill _sawmill = default!;
+ private readonly HttpClient _httpClient = new();
+ private bool _isEnabled = false;
+ private string _apiUrl = string.Empty;
+ private string _apiKey = string.Empty;
+
+ ///
+ /// Raised when player passed verification or if feature disabled
+ ///
+ public event EventHandler? PlayerVerified;
+
+ public void Initialize()
+ {
+ _sawmill = Logger.GetSawmill("discord_auth");
+
+ _cfg.OnValueChanged(CCCVars.DiscordAuthEnabled, v => _isEnabled = v, true);
+ _cfg.OnValueChanged(CCCVars.DiscordAuthApiUrl, v => _apiUrl = v, true);
+ _cfg.OnValueChanged(CCCVars.DiscordAuthApiKey, v => _apiKey = v, true);
+
+ _netMgr.RegisterNetMessage();
+ _netMgr.RegisterNetMessage(OnAuthCheck);
+
+ _playerMgr.PlayerStatusChanged += OnPlayerStatusChanged;
+ }
+
+ private async void OnAuthCheck(MsgDiscordAuthCheck message)
+ {
+ var isVerified = await IsVerified(message.MsgChannel.UserId);
+ if (isVerified)
+ {
+ var session = _playerMgr.GetSessionByUserId(message.MsgChannel.UserId);
+
+ PlayerVerified?.Invoke(this, session);
+ }
+ }
+
+ private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
+ {
+ if (e.NewStatus != SessionStatus.Connected)
+ return;
+
+ if (!_isEnabled)
+ {
+ PlayerVerified?.Invoke(this, e.Session);
+ return;
+ }
+
+ if (e.NewStatus == SessionStatus.Connected)
+ {
+ var isVerified = await IsVerified(e.Session.UserId);
+ if (isVerified)
+ {
+ PlayerVerified?.Invoke(this, e.Session);
+ return;
+ }
+
+ var authUrl = await GenerateAuthLink(e.Session.UserId);
+ var msg = new MsgDiscordAuthRequired() { AuthUrl = authUrl };
+ e.Session.ConnectedClient.SendMessage(msg);
+ }
+ }
+
+ public async Task GenerateAuthLink(NetUserId userId, CancellationToken cancel = default)
+ {
+ _sawmill.Info($"Player {userId} requested generation Discord verification link");
+
+ var requestUrl = $"{_apiUrl}/{WebUtility.UrlEncode(userId.ToString())}?key={_apiKey}";
+ var response = await _httpClient.PostAsync(requestUrl, null, cancel);
+ if (!response.IsSuccessStatusCode)
+ {
+ var content = await response.Content.ReadAsStringAsync();
+ throw new Exception($"Verification API returned bad status code: {response.StatusCode}\nResponse: {content}");
+ }
+
+ var data = await response.Content.ReadFromJsonAsync(cancellationToken: cancel);
+ return data!.Url;
+ }
+
+ public async Task IsVerified(NetUserId userId, CancellationToken cancel = default)
+ {
+ _sawmill.Debug($"Player {userId} check Discord verification");
+
+ var requestUrl = $"{_apiUrl}/{WebUtility.UrlEncode(userId.ToString())}";
+ var response = await _httpClient.GetAsync(requestUrl, cancel);
+ if (!response.IsSuccessStatusCode)
+ {
+ var content = await response.Content.ReadAsStringAsync();
+ throw new Exception($"Verification API returned bad status code: {response.StatusCode}\nResponse: {content}");
+ }
+
+ var data = await response.Content.ReadFromJsonAsync(cancellationToken: cancel);
+ return data!.IsLinked;
+ }
+
+ [UsedImplicitly]
+ private sealed record DiscordGenerateLinkResponse(string Url);
+ [UsedImplicitly]
+ private sealed record DiscordAuthInfoResponse(bool IsLinked);
+}
diff --git a/Content.Server/Corvax/JoinQueue/JoinQueueManager.cs b/Content.Server/Corvax/JoinQueue/JoinQueueManager.cs
new file mode 100644
index 00000000000..87b8d00eaaf
--- /dev/null
+++ b/Content.Server/Corvax/JoinQueue/JoinQueueManager.cs
@@ -0,0 +1,167 @@
+using System.Linq;
+using Content.Server.Connection;
+using Content.Server.Corvax.DiscordAuth;
+using Content.Shared.CCVar;
+using Content.Shared.Corvax.CCCVars;
+using Content.Shared.Corvax.JoinQueue;
+using Prometheus;
+using Robust.Server.Player;
+using Robust.Shared.Configuration;
+using Robust.Shared.Enums;
+using Robust.Shared.Network;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Corvax.JoinQueue;
+
+///
+/// Manages new player connections when the server is full and queues them up, granting access when a slot becomes free
+///
+public sealed class JoinQueueManager
+{
+ private static readonly Gauge QueueCount = Metrics.CreateGauge(
+ "join_queue_count",
+ "Amount of players in queue.");
+
+ private static readonly Counter QueueBypassCount = Metrics.CreateCounter(
+ "join_queue_bypass_count",
+ "Amount of players who bypassed queue by privileges.");
+
+ private static readonly Histogram QueueTimings = Metrics.CreateHistogram(
+ "join_queue_timings",
+ "Timings of players in queue",
+ new HistogramConfiguration()
+ {
+ LabelNames = new[] {"type"},
+ Buckets = Histogram.ExponentialBuckets(1, 2, 14),
+ });
+
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IConnectionManager _connectionManager = default!;
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly IServerNetManager _netManager = default!;
+ [Dependency] private readonly DiscordAuthManager _discordAuthManager = default!;
+
+ ///
+ /// Queue of active player sessions
+ ///
+ private readonly List _queue = new(); // Real Queue class can't delete disconnected users
+
+ private bool _isEnabled = false;
+
+ public int PlayerInQueueCount => _queue.Count;
+ public int ActualPlayersCount => _playerManager.PlayerCount - PlayerInQueueCount; // Now it's only real value with actual players count that in game
+
+ public void Initialize()
+ {
+ _netManager.RegisterNetMessage();
+
+ _cfg.OnValueChanged(CCCVars.QueueEnabled, OnQueueCVarChanged, true);
+ _playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
+ _discordAuthManager.PlayerVerified += OnPlayerVerified;
+ }
+
+ private void OnQueueCVarChanged(bool value)
+ {
+ _isEnabled = value;
+
+ if (!value)
+ {
+ foreach (var session in _queue)
+ {
+ session.ConnectedClient.Disconnect("Queue was disabled");
+ }
+ }
+ }
+
+ private async void OnPlayerVerified(object? sender, IPlayerSession session)
+ {
+ if (!_isEnabled)
+ {
+ SendToGame(session);
+ return;
+ }
+
+ var isPrivileged = await _connectionManager.HavePrivilegedJoin(session.UserId);
+ var currentOnline = _playerManager.PlayerCount - 1; // Do not count current session in general online, because we are still deciding her fate
+ var haveFreeSlot = currentOnline < _cfg.GetCVar(CCVars.SoftMaxPlayers);
+ if (isPrivileged || haveFreeSlot)
+ {
+ SendToGame(session);
+
+ if (isPrivileged && !haveFreeSlot)
+ QueueBypassCount.Inc();
+
+ return;
+ }
+
+ _queue.Add(session);
+ ProcessQueue(false, session.ConnectedTime);
+ }
+
+ private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
+ {
+ if (e.NewStatus == SessionStatus.Disconnected)
+ {
+ var wasInQueue = _queue.Remove(e.Session);
+
+ if (!wasInQueue && e.OldStatus != SessionStatus.InGame) // Process queue only if player disconnected from InGame or from queue
+ return;
+
+ ProcessQueue(true, e.Session.ConnectedTime);
+
+ if (wasInQueue)
+ QueueTimings.WithLabels("Unwaited").Observe((DateTime.UtcNow - e.Session.ConnectedTime).TotalSeconds);
+ }
+ }
+
+ ///
+ /// If possible, takes the first player in the queue and sends him into the game
+ ///
+ /// Is method called on disconnect event
+ /// Session connected time for histogram metrics
+ private void ProcessQueue(bool isDisconnect, DateTime connectedTime)
+ {
+ var players = ActualPlayersCount;
+ if (isDisconnect)
+ players--; // Decrease currently disconnected session but that has not yet been deleted
+
+ var haveFreeSlot = players < _cfg.GetCVar(CCVars.SoftMaxPlayers);
+ var queueContains = _queue.Count > 0;
+ if (haveFreeSlot && queueContains)
+ {
+ var session = _queue.First();
+ _queue.Remove(session);
+
+ SendToGame(session);
+
+ QueueTimings.WithLabels("Waited").Observe((DateTime.UtcNow - connectedTime).TotalSeconds);
+ }
+
+ SendUpdateMessages();
+ QueueCount.Set(_queue.Count);
+ }
+
+ ///
+ /// Sends messages to all players in the queue with the current state of the queue
+ ///
+ private void SendUpdateMessages()
+ {
+ for (var i = 0; i < _queue.Count; i++)
+ {
+ _queue[i].ConnectedClient.SendMessage(new MsgQueueUpdate
+ {
+ Total = _queue.Count,
+ Position = i + 1,
+ });
+ }
+ }
+
+ ///
+ /// Letting player's session into game, change player state
+ ///
+ /// Player session that will be sent to game
+ private void SendToGame(IPlayerSession s)
+ {
+ Timer.Spawn(0, s.JoinGame);
+ }
+}
diff --git a/Content.Server/Corvax/Loadout/LoadoutSystem.cs b/Content.Server/Corvax/Loadout/LoadoutSystem.cs
index e58e8a7002a..eecb54d054d 100644
--- a/Content.Server/Corvax/Loadout/LoadoutSystem.cs
+++ b/Content.Server/Corvax/Loadout/LoadoutSystem.cs
@@ -1,5 +1,5 @@
using System.Linq;
-using Content.Corvax.Interfaces.Server;
+using Content.Server.Corvax.Sponsors;
using Content.Server.GameTicking;
using Content.Server.Hands.Systems;
using Content.Server.Storage.EntitySystems;
@@ -18,19 +18,15 @@ public sealed class LoadoutSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly HandsSystem _handsSystem = default!;
[Dependency] private readonly StorageSystem _storageSystem = default!;
- private IServerSponsorsManager? _sponsorsManager;
+ [Dependency] private readonly SponsorsManager _sponsorsManager = default!;
public override void Initialize()
{
- IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
SubscribeLocalEvent(OnPlayerSpawned);
}
private void OnPlayerSpawned(PlayerSpawnCompleteEvent ev)
{
- if (_sponsorsManager == null)
- return;
-
if (_sponsorsManager.TryGetInfo(ev.Player.UserId, out var sponsor))
{
foreach (var loadoutId in sponsor.AllowedLoadouts)
diff --git a/Content.Server/Corvax/Sponsors/SponsorsManager.cs b/Content.Server/Corvax/Sponsors/SponsorsManager.cs
index 706f74cacea..4085562ec41 100644
--- a/Content.Server/Corvax/Sponsors/SponsorsManager.cs
+++ b/Content.Server/Corvax/Sponsors/SponsorsManager.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs
index 8f03bf836d0..acd97c5b642 100644
--- a/Content.Server/Entry/EntryPoint.cs
+++ b/Content.Server/Entry/EntryPoint.cs
@@ -5,6 +5,9 @@
using Content.Server.Afk;
using Content.Server.Chat.Managers;
using Content.Server.Connection;
+using Content.Server.Corvax.DiscordAuth;
+using Content.Server.Corvax.JoinQueue;
+using Content.Server.Corvax.Sponsors;
using Content.Server.Corvax.TTS;
using Content.Server.Database;
using Content.Server.EUI;
@@ -105,6 +108,9 @@ public override void Init()
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
+ IoCManager.Resolve().Initialize(); // Corvax-DiscordAuth
+ IoCManager.Resolve().Initialize(); // Corvax-Sponsors
+ IoCManager.Resolve().Initialize(); // Corvax-Queue
IoCManager.Resolve().Initialize(); // Corvax-TTS
IoCManager.Resolve().Initialize();
diff --git a/Content.Server/GameTicking/GameTicker.Player.cs b/Content.Server/GameTicking/GameTicker.Player.cs
index f1c52ab4df7..7d30fecea00 100644
--- a/Content.Server/GameTicking/GameTicker.Player.cs
+++ b/Content.Server/GameTicking/GameTicker.Player.cs
@@ -1,4 +1,3 @@
-using Content.Corvax.Interfaces.Server;
using Content.Server.Database;
using Content.Server.Players;
using Content.Shared.GameTicking;
@@ -55,7 +54,7 @@ private async void PlayerStatusChanged(object? sender, SessionStatusEventArgs ar
// Make the player actually join the game.
// timer time must be > tick length
- Timer.Spawn(0, args.Session.JoinGame);
+ // Timer.Spawn(0, args.Session.JoinGame); // Corvax-Queue: Moved to `JoinQueueManager`
var record = await _dbManager.GetPlayerRecordByUserId(args.Session.UserId);
var firstConnection = record != null &&
diff --git a/Content.Server/GameTicking/GameTicker.StatusShell.cs b/Content.Server/GameTicking/GameTicker.StatusShell.cs
index ff1e7e98bf7..bbc082ad44b 100644
--- a/Content.Server/GameTicking/GameTicker.StatusShell.cs
+++ b/Content.Server/GameTicking/GameTicker.StatusShell.cs
@@ -1,5 +1,5 @@
using System.Text.Json.Nodes;
-using Content.Corvax.Interfaces.Server;
+using Content.Server.Corvax.JoinQueue;
using Content.Shared.CCVar;
using Robust.Server.ServerStatus;
using Robust.Shared.Configuration;
@@ -23,6 +23,7 @@ public sealed partial class GameTicker
/// For access to CVars in status responses.
///
[Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly JoinQueueManager _queueManager = default!; // Corvax-Queue
private void InitializeStatusShell()
{
@@ -34,14 +35,8 @@ private void GetStatusResponse(JsonNode jObject)
// This method is raised from another thread, so this better be thread safe!
lock (_statusShellLock)
{
- // Corvax-Queue-Start
- var players = IoCManager.Instance!.TryResolveType(out var joinQueueManager)
- ? joinQueueManager.ActualPlayersCount
- : _playerManager.PlayerCount;
- // Corvax-Queue-End
-
jObject["name"] = _baseServer.ServerName;
- jObject["players"] = players; // Corvax-Queue
+ jObject["players"] = _queueManager.ActualPlayersCount; // Corvax-Queue
jObject["soft_max_players"] = _cfg.GetCVar(CCVars.SoftMaxPlayers);
jObject["run_level"] = (int) _runLevel;
if (_runLevel >= GameRunLevel.InRound)
diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs
index 10542ce83b2..b3d173f033d 100644
--- a/Content.Server/IoC/ServerContentIoC.cs
+++ b/Content.Server/IoC/ServerContentIoC.cs
@@ -6,6 +6,9 @@
using Content.Server.Andromeda;
using Content.Server.Chat.Managers;
using Content.Server.Connection;
+using Content.Server.Corvax.DiscordAuth;
+using Content.Server.Corvax.JoinQueue;
+using Content.Server.Corvax.Sponsors;
using Content.Server.Corvax.TTS;
using Content.Server.Database;
using Content.Server.Discord;
@@ -56,7 +59,10 @@ public static void Register()
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
+ IoCManager.Register(); // Corvax-Sponsors
+ IoCManager.Register(); // Corvax-Queue
IoCManager.Register(); // Corvax-TTS
+ IoCManager.Register(); // Corvax-DiscordAuth
IoCManager.Register();
//IoCManager.Register(); // Andromeda BanNotification
IoCManager.Register();
diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs
index 77af46ab25a..90ce83a72c9 100644
--- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs
+++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs
@@ -2,7 +2,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Content.Corvax.Interfaces.Server;
+using Content.Server.Corvax.Sponsors;
using Content.Server.Database;
using Content.Server.Humanoid;
using Content.Shared.CCVar;
@@ -27,7 +27,7 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IServerDbManager _db = default!;
[Dependency] private readonly IPrototypeManager _protos = default!;
- private IServerSponsorsManager? _sponsors;
+ [Dependency] private readonly SponsorsManager _sponsors = default!;
// Cache player prefs on the server so we don't need as much async hell related to them.
private readonly Dictionary _cachedPlayerPrefs =
@@ -37,7 +37,6 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager
public void Init()
{
- IoCManager.Instance!.TryResolveType(out _sponsors); // Corvax-Sponsors
_netManager.RegisterNetMessage();
_netManager.RegisterNetMessage(HandleSelectCharacterMessage);
_netManager.RegisterNetMessage(HandleUpdateCharacterMessage);
@@ -104,9 +103,7 @@ private async void HandleUpdateCharacterMessage(MsgUpdateCharacter message)
// Corvax-Sponsors-Start: Ensure removing sponsor markings if client somehow bypassed client filtering
// WARN! It's not removing markings from DB!
- var allowedMarkings = _sponsors != null && _sponsors.TryGetInfo(message.MsgChannel.UserId, out var sponsor)
- ? sponsor.AllowedMarkings
- : new string[]{};
+ var allowedMarkings = _sponsors.TryGetInfo(message.MsgChannel.UserId, out var sponsor) ? sponsor.AllowedMarkings : new string[]{};
profile.EnsureValid(allowedMarkings);
// Corvax-Sponsors-End
var profiles = new Dictionary(curPrefs.Characters)
@@ -204,9 +201,7 @@ async Task LoadPrefs()
// Corvax-Sponsors-Start: Remove sponsor markings from expired sponsors
foreach (var (_, profile) in prefs.Characters)
{
- var allowedMarkings = _sponsors != null && _sponsors.TryGetInfo(session.UserId, out var sponsor)
- ? sponsor.AllowedMarkings
- : new string[]{};
+ var allowedMarkings = _sponsors.TryGetInfo(session.UserId, out var sponsor) ? sponsor.AllowedMarkings : new string[]{};
profile.EnsureValid(allowedMarkings);
}
// Corvax-Sponsors-End
@@ -233,13 +228,11 @@ public void OnClientDisconnected(IPlayerSession session)
private int GetMaxUserCharacterSlots(NetUserId userId)
{
var maxSlots = _cfg.GetCVar(CCVars.GameMaxCharacterSlots);
- var extraSlots = _sponsors != null && _sponsors.TryGetInfo(userId, out var sponsor)
- ? sponsor.ExtraSlots
- : 0;
+ var extraSlots = _sponsors.TryGetInfo(userId, out var sponsor) ? sponsor.ExtraSlots : 0;
return maxSlots + extraSlots;
}
// Corvax-Sponsors-End
-
+
public bool HavePreferencesLoaded(IPlayerSession session)
{
return _cachedPlayerPrefs.ContainsKey(session.UserId);
diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj
index 793611efd44..e67223ae3cc 100644
--- a/Content.Shared/Content.Shared.csproj
+++ b/Content.Shared/Content.Shared.csproj
@@ -22,7 +22,6 @@
false
-
diff --git a/Content.Shared/Corvax/CCCVars/CCCVars.cs b/Content.Shared/Corvax/CCCVars/CCCVars.cs
index e09bb6f164b..d3d8e2a1c64 100644
--- a/Content.Shared/Corvax/CCCVars/CCCVars.cs
+++ b/Content.Shared/Corvax/CCCVars/CCCVars.cs
@@ -9,6 +9,16 @@ namespace Content.Shared.Corvax.CCCVars;
// ReSharper disable once InconsistentNaming
public sealed class CCCVars
{
+ /**
+ * Sponsors
+ */
+
+ ///
+ /// URL of the sponsors server API.
+ ///
+ public static readonly CVarDef SponsorsApiUrl =
+ CVarDef.Create("sponsor.api_url", "", CVar.SERVERONLY);
+
/*
* Queue
*/
@@ -68,4 +78,26 @@ public static readonly CVarDef
///
public static readonly CVarDef PeacefulRoundEnd =
CVarDef.Create("game.peaceful_end", true, CVar.SERVERONLY);
+
+ /*
+ * Discord Auth
+ */
+
+ ///
+ /// Enabled Discord linking, show linking button and modal window
+ ///
+ public static readonly CVarDef DiscordAuthEnabled =
+ CVarDef.Create("discord_auth.enabled", false, CVar.SERVERONLY);
+
+ ///
+ /// URL of the Discord auth server API
+ ///
+ public static readonly CVarDef DiscordAuthApiUrl =
+ CVarDef.Create("discord_auth.api_url", "", CVar.SERVERONLY);
+
+ ///
+ /// Secret key of the Discord auth server API
+ ///
+ public static readonly CVarDef DiscordAuthApiKey =
+ CVarDef.Create("discord_auth.api_key", "", CVar.SERVERONLY | CVar.CONFIDENTIAL);
}
diff --git a/Content.Shared/Corvax/DiscordAuth/MsgDiscordAuthCheck.cs b/Content.Shared/Corvax/DiscordAuth/MsgDiscordAuthCheck.cs
new file mode 100644
index 00000000000..c1019c57410
--- /dev/null
+++ b/Content.Shared/Corvax/DiscordAuth/MsgDiscordAuthCheck.cs
@@ -0,0 +1,22 @@
+using Lidgren.Network;
+using Robust.Shared.Network;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Corvax.DiscordAuth;
+
+///
+/// Client sends this event to force server check player Discord verification state
+///
+public sealed class MsgDiscordAuthCheck : NetMessage
+{
+ public override MsgGroups MsgGroup => MsgGroups.Command;
+
+ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
+ {
+ }
+
+ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
+ {
+ }
+}
+
diff --git a/Content.Shared/Corvax/DiscordAuth/MsgDiscordAuthRequired.cs b/Content.Shared/Corvax/DiscordAuth/MsgDiscordAuthRequired.cs
new file mode 100644
index 00000000000..292b3cd373f
--- /dev/null
+++ b/Content.Shared/Corvax/DiscordAuth/MsgDiscordAuthRequired.cs
@@ -0,0 +1,25 @@
+using Lidgren.Network;
+using Robust.Shared.Network;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Corvax.DiscordAuth;
+
+///
+/// Server sends this event to client on connect if Discord auth is required
+///
+public sealed class MsgDiscordAuthRequired : NetMessage
+{
+ public override MsgGroups MsgGroup => MsgGroups.Command;
+
+ public string AuthUrl = string.Empty;
+
+ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
+ {
+ AuthUrl = buffer.ReadString();
+ }
+
+ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
+ {
+ buffer.Write(AuthUrl);
+ }
+}
diff --git a/Content.Shared/Corvax/JoinQueue/MsgQueueUpdate.cs b/Content.Shared/Corvax/JoinQueue/MsgQueueUpdate.cs
new file mode 100644
index 00000000000..6f44fb8e918
--- /dev/null
+++ b/Content.Shared/Corvax/JoinQueue/MsgQueueUpdate.cs
@@ -0,0 +1,36 @@
+using Lidgren.Network;
+using Robust.Shared.Network;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Corvax.JoinQueue;
+
+///
+/// Sent from server to client with queue state for player
+/// Also initiates queue state on client
+///
+public sealed class MsgQueueUpdate : NetMessage
+{
+ public override MsgGroups MsgGroup => MsgGroups.Command;
+
+ ///
+ /// Total players in queue
+ ///
+ public int Total { get; set; }
+
+ ///
+ /// Player current position in queue (starts from 1)
+ ///
+ public int Position { get; set; }
+
+ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
+ {
+ Total = buffer.ReadInt32();
+ Position = buffer.ReadInt32();
+ }
+
+ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
+ {
+ buffer.Write(Total);
+ buffer.Write(Position);
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Corvax/Sponsors/MsgSponsorInfo.cs b/Content.Shared/Corvax/Sponsors/MsgSponsorInfo.cs
index bd5a77b1af9..40acc9d35e4 100644
--- a/Content.Shared/Corvax/Sponsors/MsgSponsorInfo.cs
+++ b/Content.Shared/Corvax/Sponsors/MsgSponsorInfo.cs
@@ -1,4 +1,4 @@
-using System.IO;
+using System.IO;
using System.Text.Json.Serialization;
using Lidgren.Network;
using Robust.Shared.Network;
diff --git a/Corvax/Content.Corvax.Interfaces.Client/Content.Corvax.Interfaces.Client.csproj b/Corvax/Content.Corvax.Interfaces.Client/Content.Corvax.Interfaces.Client.csproj
deleted file mode 100644
index a3707b18478..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Client/Content.Corvax.Interfaces.Client.csproj
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- net7.0
- enable
- enable
-
-
-
- false
-
-
-
-
diff --git a/Corvax/Content.Corvax.Interfaces.Client/IClientDiscordAuthManager.cs b/Corvax/Content.Corvax.Interfaces.Client/IClientDiscordAuthManager.cs
deleted file mode 100644
index aa78731ee36..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Client/IClientDiscordAuthManager.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Content.Corvax.Interfaces.Shared;
-
-namespace Content.Corvax.Interfaces.Client;
-
-public interface IClientDiscordAuthManager : ISharedDiscordAuthManager
-{
- public string AuthUrl { get; }
-}
diff --git a/Corvax/Content.Corvax.Interfaces.Client/IClientJoinQueueManager.cs b/Corvax/Content.Corvax.Interfaces.Client/IClientJoinQueueManager.cs
deleted file mode 100644
index 04a7eaa2782..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Client/IClientJoinQueueManager.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Content.Corvax.Interfaces.Client;
-
-public interface IClientJoinQueueManager
-{
- public void Initialize();
-}
diff --git a/Corvax/Content.Corvax.Interfaces.Client/IClientSponsorsManager.cs b/Corvax/Content.Corvax.Interfaces.Client/IClientSponsorsManager.cs
deleted file mode 100644
index 2a9b4236765..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Client/IClientSponsorsManager.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using Content.Corvax.Interfaces.Shared;
-
-namespace Content.Corvax.Interfaces.Client;
-
-public interface IClientSponsorsManager : ISharedSponsorsManager
-{
- public bool TryGetInfo([NotNullWhen(true)] out ISponsorInfo? sponsor);
-}
diff --git a/Corvax/Content.Corvax.Interfaces.Server/Content.Corvax.Interfaces.Server.csproj b/Corvax/Content.Corvax.Interfaces.Server/Content.Corvax.Interfaces.Server.csproj
deleted file mode 100644
index eb80a9f04e8..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Server/Content.Corvax.Interfaces.Server.csproj
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- net7.0
- enable
- enable
-
-
-
- false
-
-
-
-
diff --git a/Corvax/Content.Corvax.Interfaces.Server/IServerDiscordAuthManager.cs b/Corvax/Content.Corvax.Interfaces.Server/IServerDiscordAuthManager.cs
deleted file mode 100644
index 1ec680877fb..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Server/IServerDiscordAuthManager.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Content.Corvax.Interfaces.Shared;
-using Robust.Server.Player;
-using Robust.Shared.Network;
-
-namespace Content.Corvax.Interfaces.Server;
-
-public interface IServerDiscordAuthManager : ISharedDiscordAuthManager
-{
- public event EventHandler? PlayerVerified;
- public Task GenerateAuthLink(NetUserId userId, CancellationToken cancel);
- public Task IsVerified(NetUserId userId, CancellationToken cancel);
-}
diff --git a/Corvax/Content.Corvax.Interfaces.Server/IServerJoinQueueManager.cs b/Corvax/Content.Corvax.Interfaces.Server/IServerJoinQueueManager.cs
deleted file mode 100644
index 266faaa1b55..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Server/IServerJoinQueueManager.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Content.Corvax.Interfaces.Server;
-
-public interface IServerJoinQueueManager
-{
- public int PlayerInQueueCount { get; }
- public int ActualPlayersCount { get; }
- public void Initialize();
-}
diff --git a/Corvax/Content.Corvax.Interfaces.Server/IServerSponsorsManager.cs b/Corvax/Content.Corvax.Interfaces.Server/IServerSponsorsManager.cs
deleted file mode 100644
index e8823f9af18..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Server/IServerSponsorsManager.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using Content.Corvax.Interfaces.Shared;
-using Robust.Shared.Network;
-
-namespace Content.Corvax.Interfaces.Server;
-
-public interface IServerSponsorsManager : ISharedSponsorsManager
-{
- public bool TryGetInfo(NetUserId userId, [NotNullWhen(true)] out ISponsorInfo? sponsor);
-}
diff --git a/Corvax/Content.Corvax.Interfaces.Shared/Content.Corvax.Interfaces.Shared.csproj b/Corvax/Content.Corvax.Interfaces.Shared/Content.Corvax.Interfaces.Shared.csproj
deleted file mode 100644
index 901790f4d84..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Shared/Content.Corvax.Interfaces.Shared.csproj
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- net7.0
- enable
- enable
-
-
-
-
-
diff --git a/Corvax/Content.Corvax.Interfaces.Shared/ISharedDiscordAuthManager.cs b/Corvax/Content.Corvax.Interfaces.Shared/ISharedDiscordAuthManager.cs
deleted file mode 100644
index 3b6770108f8..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Shared/ISharedDiscordAuthManager.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Content.Corvax.Interfaces.Shared;
-
-public interface ISharedDiscordAuthManager
-{
- public void Initialize();
-}
diff --git a/Corvax/Content.Corvax.Interfaces.Shared/ISharedSponsorsManager.cs b/Corvax/Content.Corvax.Interfaces.Shared/ISharedSponsorsManager.cs
deleted file mode 100644
index a35121a3d75..00000000000
--- a/Corvax/Content.Corvax.Interfaces.Shared/ISharedSponsorsManager.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Content.Corvax.Interfaces.Shared;
-
-public interface ISharedSponsorsManager
-{
- public void Initialize();
-}
-
-public interface ISponsorInfo
-{
- public int? Tier { get; set; }
- public string? OOCColor { get; set; }
- public bool HavePriorityJoin { get; set; }
- public int ExtraSlots { get; set; }
- public string[] AllowedMarkings { get; set; }
-}
diff --git a/Secrets b/Secrets
index a6fc2e52b0a..5b32e405743 160000
--- a/Secrets
+++ b/Secrets
@@ -1 +1 @@
-Subproject commit a6fc2e52b0a227cde743070cf42df1de7090836a
+Subproject commit 5b32e405743c1463b79dcf34a5606a6d103d82dd
diff --git a/SpaceStation14.sln b/SpaceStation14.sln
index d5961116297..2dc4c95508d 100644
--- a/SpaceStation14.sln
+++ b/SpaceStation14.sln
@@ -127,14 +127,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Shared.CompNetworkGe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Serialization.Generator", "RobustToolbox\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj", "{6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Corvax Interfaces", "Corvax Interfaces", "{DA6E23AF-5AC1-43C8-831D-812A2A758731}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.Corvax.Interfaces.Shared", "Corvax\Content.Corvax.Interfaces.Shared\Content.Corvax.Interfaces.Shared.csproj", "{F9F78EE1-1FBE-43F9-8E86-78327FE330DB}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.Corvax.Interfaces.Server", "Corvax\Content.Corvax.Interfaces.Server\Content.Corvax.Interfaces.Server.csproj", "{21D609DE-FD15-4A7E-8541-B003DB6FFC62}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.Corvax.Interfaces.Client", "Corvax\Content.Corvax.Interfaces.Client\Content.Corvax.Interfaces.Client.csproj", "{B63ADCCD-FA56-4114-9E14-E0793802557E}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -438,30 +430,6 @@ Global
{6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU
{6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}.Tools|Any CPU.ActiveCfg = Debug|Any CPU
{6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}.Tools|Any CPU.Build.0 = Debug|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.Release|Any CPU.Build.0 = Release|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.DebugOpt|Any CPU.ActiveCfg = Debug|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.Tools|Any CPU.ActiveCfg = Debug|Any CPU
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB}.Tools|Any CPU.Build.0 = Debug|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.Release|Any CPU.Build.0 = Release|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.DebugOpt|Any CPU.ActiveCfg = Debug|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.Tools|Any CPU.ActiveCfg = Debug|Any CPU
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62}.Tools|Any CPU.Build.0 = Debug|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.Release|Any CPU.Build.0 = Release|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.DebugOpt|Any CPU.ActiveCfg = Debug|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.Tools|Any CPU.ActiveCfg = Debug|Any CPU
- {B63ADCCD-FA56-4114-9E14-E0793802557E}.Tools|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -493,9 +461,6 @@ Global
{A965CB3B-FD31-44AF-8872-85ABA436098D} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE}
{07CA34A1-1D37-4771-A2E3-495A1044AE0B} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE}
{6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE}
- {F9F78EE1-1FBE-43F9-8E86-78327FE330DB} = {DA6E23AF-5AC1-43C8-831D-812A2A758731}
- {21D609DE-FD15-4A7E-8541-B003DB6FFC62} = {DA6E23AF-5AC1-43C8-831D-812A2A758731}
- {B63ADCCD-FA56-4114-9E14-E0793802557E} = {DA6E23AF-5AC1-43C8-831D-812A2A758731}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AA37ED9F-F8D6-468E-A101-658AD605B09A}
diff --git a/Tools/package_client_build.py b/Tools/package_client_build.py
index ab42e0084ea..7ab59526f70 100755
--- a/Tools/package_client_build.py
+++ b/Tools/package_client_build.py
@@ -46,12 +46,6 @@ def __getattr__(self, name):
CLIENT_CONTENT_ASSEMBLIES = [
# IF YOU ADD SOMETHING HERE, ADD IT TO MANIFEST.YML TOO.
- # Corvax-Secrets-Start
- "Content.Corvax.Interfaces.Shared",
- "Content.Corvax.Interfaces.Client",
- "Content.Corvax.Shared",
- "Content.Corvax.Client",
- # Corvax-Secrets-End
"Content.Client",
"Content.Shared",
"Content.Shared.Database"
@@ -106,21 +100,6 @@ def build(skip_build: bool) -> None:
"/p:FullRelease=True",
"/m"
], check=True)
- # Corvax-Secrets-Start
- if os.path.exists(p("Secrets", "Content.Corvax.Client")):
- print(Fore.GREEN + f"Secrets found. Building secret project..." + Style.RESET_ALL)
- subprocess.run([
- "dotnet",
- "build",
- p("Secrets","Content.Corvax.Client", "Content.Corvax.Client.csproj"),
- "-c", "Release",
- "--nologo",
- "/v:m",
- "/t:Rebuild",
- "/p:FullRelease=True",
- "/m"
- ], check=True)
- # Corvax-Secrets-End
print(Fore.GREEN + "Packaging client..." + Style.RESET_ALL)
@@ -196,8 +175,7 @@ def copy_content_assemblies(target, zipf):
# Include content assemblies.
for asm in base_assemblies:
- if os.path.exists(p(source_dir, asm + ".dll")): # Corvax-Secrets: Allow optional assemblies
- files.append(asm + ".dll")
+ files.append(asm + ".dll")
# If PDB available, include it aswell.
pdb_path = asm + ".pdb"
if os.path.exists(p(source_dir, pdb_path)):
diff --git a/Tools/package_server_build.py b/Tools/package_server_build.py
index e8504ed630f..8824fbff9b6 100755
--- a/Tools/package_server_build.py
+++ b/Tools/package_server_build.py
@@ -61,12 +61,7 @@ def __init__(self, rid: str, target_os: str, build_by_default: bool):
# Assembly names to copy from content.
# PDBs are included if available, .dll/.pdb appended automatically.
SERVER_CONTENT_ASSEMBLIES = [
- # Corvax-Secrets-Start
- "Content.Corvax.Interfaces.Shared",
- "Content.Corvax.Interfaces.Server",
- "Content.Corvax.Shared",
- "Content.Corvax.Server",
- # Corvax-Secrets-End
+ "Content.CorvaxServer", # Corvax-Secrets
"Content.Server.Database",
"Content.Server",
"Content.Shared",
@@ -175,12 +170,12 @@ def build_platform(platform: PlatformReg, skip_build: bool, hybrid_acz: bool) ->
"/m"
], check=True)
# Corvax-Secrets-Start
- if os.path.exists(p("Secrets", "Content.Corvax.Server")):
+ if os.path.exists(p("Secrets", "Content.CorvaxServer")):
print(Fore.GREEN + f"Secrets found. Building secret project for {platform.rid}..." + Style.RESET_ALL)
subprocess.run([
"dotnet",
"build",
- p("Secrets","Content.Corvax.Server", "Content.Corvax.Server.csproj"),
+ p("Secrets","Content.CorvaxServer", "Content.CorvaxServer.csproj"),
"-c", "Release",
"--nologo",
"/v:m",