[feat] donate
# Conflicts: # Content.Client/Entry/EntryPoint.cs # Content.Client/IoC/ClientContentIoC.cs # Content.Server/Chat/Managers/ChatManager.cs # Content.Server/Entry/EntryPoint.cs # Content.Server/GameTicking/GameTicker.Player.cs # Content.Server/GameTicking/GameTicker.StatusShell.cs # Content.Server/IoC/ServerContentIoC.cs # Content.Shared/Humanoid/HumanoidCharacterAppearance.cs # Content.Shared/Humanoid/Markings/MarkingPrototype.cs # Content.Shared/Preferences/HumanoidCharacterProfile.cs
This commit is contained in:
@@ -21,6 +21,8 @@ using Content.Client.Singularity;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Client.Voting;
|
||||
using Content.Client.White.JoinQueue;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Shared.Ame;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Localizations;
|
||||
@@ -71,6 +73,11 @@ namespace Content.Client.Entry
|
||||
[Dependency] private readonly IReplayLoadManager _replayLoad = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
[Dependency] private readonly JoinQueueManager _queueManager = default!;
|
||||
//WD-EDIT
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
ClientContentIoC.Register();
|
||||
@@ -163,6 +170,11 @@ namespace Content.Client.Entry
|
||||
_userInterfaceManager.SetActiveTheme(_configManager.GetCVar(CVars.InterfaceTheme));
|
||||
_documentParsingManager.Initialize();
|
||||
|
||||
//WD-EDIT
|
||||
_sponsorsManager.Initialize();
|
||||
_queueManager.Initialize();
|
||||
//WD-EDIT
|
||||
|
||||
_baseClient.RunLevelChanged += (_, args) =>
|
||||
{
|
||||
if (args.NewLevel == ClientRunLevel.Initialize)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
@@ -19,6 +20,10 @@ public sealed partial class MarkingPicker : Control
|
||||
[Dependency] private readonly MarkingManager _markingManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
//WD-EDIT
|
||||
|
||||
public Action<MarkingSet>? OnMarkingAdded;
|
||||
public Action<MarkingSet>? OnMarkingRemoved;
|
||||
public Action<MarkingSet>? OnMarkingColorChange;
|
||||
@@ -202,6 +207,18 @@ public sealed partial class MarkingPicker : Control
|
||||
|
||||
var item = CMarkingsUnused.AddItem($"{GetMarkingName(marking)}", marking.Sprites[0].Frame0());
|
||||
item.Metadata = marking;
|
||||
|
||||
//WD-EDIT
|
||||
if (marking.SponsorOnly)
|
||||
{
|
||||
item.Disabled = true;
|
||||
if (_sponsorsManager.TryGetInfo(out var sponsor))
|
||||
{
|
||||
item.Disabled = !sponsor.AllowedMarkings.Contains(marking.ID);
|
||||
}
|
||||
}
|
||||
//WD-EDIT
|
||||
|
||||
}
|
||||
|
||||
CMarkingPoints.Visible = _currentMarkings.PointsLeft(_selectedMarkingCategory) != -1;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -12,6 +13,10 @@ public sealed partial class SingleMarkingPicker : BoxContainer
|
||||
{
|
||||
[Dependency] private readonly MarkingManager _markingManager = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
//WD-EDIT
|
||||
|
||||
/// <summary>
|
||||
/// What happens if a marking is selected.
|
||||
/// It will send the 'slot' (marking index)
|
||||
@@ -191,6 +196,17 @@ public sealed partial class SingleMarkingPicker : BoxContainer
|
||||
var item = MarkingList.AddItem(Loc.GetString($"marking-{id}"), marking.Sprites[0].Frame0());
|
||||
item.Metadata = marking.ID;
|
||||
|
||||
//WD-EDIT
|
||||
if (marking.SponsorOnly)
|
||||
{
|
||||
item.Disabled = true;
|
||||
if (_sponsorsManager.TryGetInfo(out var sponsor))
|
||||
{
|
||||
item.Disabled = !sponsor.AllowedMarkings.Contains(marking.ID);
|
||||
}
|
||||
}
|
||||
//WD-EDIT
|
||||
|
||||
if (_markings[Slot].MarkingId == id)
|
||||
{
|
||||
_ignoreItemSelected = true;
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Client.Administration.Managers;
|
||||
using Content.Client.Changelog;
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Client.Clickable;
|
||||
using Content.Client.Options;
|
||||
using Content.Client.Eui;
|
||||
using Content.Client.GhostKick;
|
||||
using Content.Client.Info;
|
||||
@@ -15,14 +14,13 @@ using Content.Client.Fullscreen;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Client.Voting;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Module;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Replay;
|
||||
using Content.Client.White.JoinQueue;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Shared.Administration.Managers;
|
||||
|
||||
|
||||
namespace Content.Client.IoC
|
||||
{
|
||||
internal static class ClientContentIoC
|
||||
@@ -49,6 +47,11 @@ namespace Content.Client.IoC
|
||||
IoCManager.Register<JobRequirementsManager>();
|
||||
IoCManager.Register<DocumentParsingManager>();
|
||||
IoCManager.Register<ContentReplayPlaybackManager, ContentReplayPlaybackManager>();
|
||||
|
||||
//WD-EDIT
|
||||
IoCManager.Register<JoinQueueManager>();
|
||||
IoCManager.Register<SponsorsManager>();
|
||||
//WD-EDIT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.White.Sponsors;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -19,6 +20,10 @@ namespace Content.Client.Preferences
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
//WD-EDIT
|
||||
|
||||
public event Action? OnServerDataLoaded;
|
||||
|
||||
public GameSettings Settings { get; private set; } = default!;
|
||||
@@ -60,7 +65,10 @@ namespace Content.Client.Preferences
|
||||
|
||||
public void UpdateCharacter(ICharacterProfile profile, int slot)
|
||||
{
|
||||
profile.EnsureValid();
|
||||
//WD-EDIT
|
||||
var allowedMarkings = _sponsorsManager.TryGetInfo(out var sponsor) ? sponsor.AllowedMarkings : new string[]{};
|
||||
profile.EnsureValid(allowedMarkings);
|
||||
//WD-EDIT
|
||||
var characters = new Dictionary<int, ICharacterProfile>(Preferences.Characters) {[slot] = profile};
|
||||
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex, Preferences.AdminOOCColor);
|
||||
var msg = new MsgUpdateCharacter
|
||||
|
||||
26
Content.Client/White/JoinQueue/JoinQueueManager.cs
Normal file
26
Content.Client/White/JoinQueue/JoinQueueManager.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Content.Shared.White.JoinQueue;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Client.White.JoinQueue;
|
||||
|
||||
public sealed class JoinQueueManager
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgQueueUpdate>(OnQueueUpdate);
|
||||
}
|
||||
|
||||
private void OnQueueUpdate(MsgQueueUpdate msg)
|
||||
{
|
||||
if (_stateManager.CurrentState is not QueueState)
|
||||
{
|
||||
_stateManager.RequestStateChange<QueueState>();
|
||||
}
|
||||
|
||||
((QueueState) _stateManager.CurrentState).OnQueueUpdate(msg);
|
||||
}
|
||||
}
|
||||
34
Content.Client/White/JoinQueue/QueueGui.xaml
Normal file
34
Content.Client/White/JoinQueue/QueueGui.xaml
Normal file
@@ -0,0 +1,34 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:parallax="clr-namespace:Content.Client.Parallax"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<parallax:ParallaxControl />
|
||||
<Control HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<PanelContainer StyleClasses="AngleRect" />
|
||||
<BoxContainer Orientation="Vertical" MinSize="200 200">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Margin="8 0 0 0" Text="{Loc 'queue-title'}"
|
||||
StyleClasses="LabelHeading" VAlign="Center" />
|
||||
<Button Name="QuitButton" Text="{Loc 'queue-quit'}"
|
||||
HorizontalAlignment="Right" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<controls:HighDivider />
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" Margin="0 20 0 0">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||
<Label Text="{Loc 'queue-position'}" Align="Center" />
|
||||
<Label Name="QueuePosition" StyleClasses="LabelHeading" Align="Center" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" Margin="0 10 0 0">
|
||||
<Label Text="{Loc 'queue-total'}" Align="Center" />
|
||||
<Label Name="QueueTotal" StyleClasses="LabelHeading" Align="Center" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" VerticalAlignment="Bottom" Margin="0 20 0 0">
|
||||
<Button Name="PriorityJoinButton" Text="{Loc 'queue-priority-join'}" HorizontalExpand="True" StyleClasses="OpenRight" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
</Control>
|
||||
36
Content.Client/White/JoinQueue/QueueGui.xaml.cs
Normal file
36
Content.Client/White/JoinQueue/QueueGui.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
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.White.JoinQueue;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class QueueGui : Control
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public event Action? QuitPressed;
|
||||
|
||||
public QueueGui()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
|
||||
|
||||
QuitButton.OnPressed += (_) => QuitPressed?.Invoke();
|
||||
PriorityJoinButton.OnPressed += (_) =>
|
||||
{
|
||||
var linkPatreon = _cfg.GetCVar(CCVars.InfoLinksPatreon);
|
||||
IoCManager.Resolve<IUriOpener>().OpenUri(linkPatreon);
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateInfo(int total, int position)
|
||||
{
|
||||
QueueTotal.Text = total.ToString();
|
||||
QueuePosition.Text = position.ToString();
|
||||
}
|
||||
}
|
||||
53
Content.Client/White/JoinQueue/QueueState.cs
Normal file
53
Content.Client/White/JoinQueue/QueueState.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared.White.JoinQueue;
|
||||
using Robust.Client.Audio;
|
||||
|
||||
namespace Content.Client.White.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<IEntityManager>().TrySystem<AudioSystem>(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");
|
||||
}
|
||||
}
|
||||
23
Content.Client/White/Sponsors/SponsorsManager.cs
Normal file
23
Content.Client/White/Sponsors/SponsorsManager.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.White.Sponsors;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Client.White.Sponsors;
|
||||
|
||||
public sealed class SponsorsManager
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _netMgr = default!;
|
||||
|
||||
private SponsorInfo? _info;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netMgr.RegisterNetMessage<MsgSponsorInfo>(msg => _info = msg.Info);
|
||||
}
|
||||
|
||||
public bool TryGetInfo([NotNullWhen(true)] out SponsorInfo? sponsor)
|
||||
{
|
||||
sponsor = _info;
|
||||
return _info != null;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Content.Server.Administration.Managers;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.MoMMI;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.White.Sponsors;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Chat;
|
||||
@@ -46,6 +47,10 @@ namespace Content.Server.Chat.Managers
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
/// WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
/// WD-EDIT
|
||||
|
||||
/// <summary>
|
||||
/// The maximum length a player-sent message can be sent
|
||||
/// </summary>
|
||||
@@ -69,7 +74,8 @@ namespace Content.Server.Chat.Managers
|
||||
|
||||
private void OnOocEnabledChanged(bool val)
|
||||
{
|
||||
if (_oocEnabled == val) return;
|
||||
if (_oocEnabled == val)
|
||||
return;
|
||||
|
||||
_oocEnabled = val;
|
||||
DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-ooc-chat-enabled-message" : "chat-manager-ooc-chat-disabled-message"));
|
||||
@@ -77,7 +83,8 @@ namespace Content.Server.Chat.Managers
|
||||
|
||||
private void OnAdminOocEnabledChanged(bool val)
|
||||
{
|
||||
if (_adminOocEnabled == val) return;
|
||||
if (_adminOocEnabled == val)
|
||||
return;
|
||||
|
||||
_adminOocEnabled = val;
|
||||
DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-admin-ooc-chat-enabled-message" : "chat-manager-admin-ooc-chat-disabled-message"));
|
||||
@@ -119,7 +126,7 @@ namespace Content.Server.Chat.Managers
|
||||
public void DispatchServerMessage(ICommonSession player, string message, bool suppressLog = false)
|
||||
{
|
||||
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(message)));
|
||||
ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, player.ConnectedClient);
|
||||
ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, player.Channel);
|
||||
|
||||
if (!suppressLog)
|
||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server message to {player:Player}: {message}");
|
||||
@@ -127,7 +134,7 @@ namespace Content.Server.Chat.Managers
|
||||
|
||||
public void SendAdminAnnouncement(string message)
|
||||
{
|
||||
var clients = _adminManager.ActiveAdmins.Select(p => p.ConnectedClient);
|
||||
var clients = _adminManager.ActiveAdmins.Select(p => p.Channel);
|
||||
|
||||
var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message",
|
||||
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message)));
|
||||
@@ -138,7 +145,7 @@ namespace Content.Server.Chat.Managers
|
||||
|
||||
public void SendAdminAlert(string message)
|
||||
{
|
||||
var clients = _adminManager.ActiveAdmins.Select(p => p.ConnectedClient);
|
||||
var clients = _adminManager.ActiveAdmins.Select(p => p.Channel);
|
||||
|
||||
var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message",
|
||||
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message)));
|
||||
@@ -149,7 +156,7 @@ namespace Content.Server.Chat.Managers
|
||||
public void SendAdminAlert(EntityUid player, string message)
|
||||
{
|
||||
var mindSystem = _entityManager.System<SharedMindSystem>();
|
||||
if (!mindSystem.TryGetMind(player, out var mindId, out var mind))
|
||||
if (!mindSystem.TryGetMind(player, out _, out var mind))
|
||||
{
|
||||
SendAdminAlert(message);
|
||||
return;
|
||||
@@ -230,12 +237,19 @@ namespace Content.Server.Chat.Managers
|
||||
var prefs = _preferencesManager.GetPreferences(player.UserId);
|
||||
colorOverride = prefs.AdminOOCColor;
|
||||
}
|
||||
if (player.ConnectedClient.UserData.PatronTier is { } patron &&
|
||||
if (player.Channel.UserData.PatronTier is { } patron &&
|
||||
PatronOocColors.TryGetValue(patron, out var patronColor))
|
||||
{
|
||||
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
|
||||
}
|
||||
|
||||
//WD-EDIT
|
||||
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)));
|
||||
}
|
||||
//WD-EDIT
|
||||
|
||||
//TODO: player.Name color, this will need to change the structure of the MsgChatMessage
|
||||
ChatMessageToAll(ChatChannel.OOC, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride: colorOverride, author: player.UserId);
|
||||
_mommiLink.SendOOCMessage(player.Name, message);
|
||||
@@ -250,14 +264,14 @@ namespace Content.Server.Chat.Managers
|
||||
return;
|
||||
}
|
||||
|
||||
var clients = _adminManager.ActiveAdmins.Select(p => p.ConnectedClient);
|
||||
var clients = _adminManager.ActiveAdmins.Select(p => p.Channel);
|
||||
var wrappedMessage = Loc.GetString("chat-manager-send-admin-chat-wrap-message",
|
||||
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
|
||||
("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
|
||||
|
||||
foreach (var client in clients)
|
||||
{
|
||||
var isSource = client != player.ConnectedClient;
|
||||
var isSource = client != player.Channel;
|
||||
ChatMessageToOne(ChatChannel.AdminChat,
|
||||
message,
|
||||
wrappedMessage,
|
||||
@@ -283,7 +297,7 @@ namespace Content.Server.Chat.Managers
|
||||
user?.AddEntity(netSource);
|
||||
|
||||
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
|
||||
_netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client);
|
||||
_netManager.ServerSendMessage(new MsgChatMessage { Message = msg }, client);
|
||||
|
||||
if (!recordReplay)
|
||||
return;
|
||||
@@ -296,7 +310,10 @@ namespace Content.Server.Chat.Managers
|
||||
}
|
||||
|
||||
public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null)
|
||||
=> ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients.ToList(), colorOverride, audioPath, audioVolume, author);
|
||||
{
|
||||
ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients.ToList(),
|
||||
colorOverride, audioPath, audioVolume, author);
|
||||
}
|
||||
|
||||
public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, List<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null)
|
||||
{
|
||||
@@ -305,7 +322,7 @@ namespace Content.Server.Chat.Managers
|
||||
user?.AddEntity(netSource);
|
||||
|
||||
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
|
||||
_netManager.ServerSendToMany(new MsgChatMessage() { Message = msg }, clients);
|
||||
_netManager.ServerSendToMany(new MsgChatMessage { Message = msg }, clients);
|
||||
|
||||
if (!recordReplay)
|
||||
return;
|
||||
@@ -317,8 +334,17 @@ namespace Content.Server.Chat.Managers
|
||||
}
|
||||
}
|
||||
|
||||
public void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string wrappedMessage, EntityUid source,
|
||||
bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
|
||||
public void ChatMessageToManyFiltered(
|
||||
Filter filter,
|
||||
ChatChannel channel,
|
||||
string message,
|
||||
string wrappedMessage,
|
||||
EntityUid source,
|
||||
bool hideChat,
|
||||
bool recordReplay,
|
||||
Color? colorOverride = null,
|
||||
string? audioPath = null,
|
||||
float audioVolume = 0)
|
||||
{
|
||||
if (!recordReplay && !filter.Recipients.Any())
|
||||
return;
|
||||
@@ -326,7 +352,7 @@ namespace Content.Server.Chat.Managers
|
||||
var clients = new List<INetChannel>();
|
||||
foreach (var recipient in filter.Recipients)
|
||||
{
|
||||
clients.Add(recipient.ConnectedClient);
|
||||
clients.Add(recipient.Channel);
|
||||
}
|
||||
|
||||
ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients, colorOverride, audioPath, audioVolume);
|
||||
@@ -339,7 +365,7 @@ namespace Content.Server.Chat.Managers
|
||||
user?.AddEntity(netSource);
|
||||
|
||||
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
|
||||
_netManager.ServerSendToAll(new MsgChatMessage() { Message = msg });
|
||||
_netManager.ServerSendToAll(new MsgChatMessage { Message = msg });
|
||||
|
||||
if (!recordReplay)
|
||||
return;
|
||||
|
||||
@@ -3,9 +3,11 @@ using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.White.Sponsors;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.White;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
@@ -16,6 +18,7 @@ namespace Content.Server.Connection
|
||||
public interface IConnectionManager
|
||||
{
|
||||
void Initialize();
|
||||
Task<bool> HavePrivilegedJoin(NetUserId userId); // WD-EDIT
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,6 +34,10 @@ namespace Content.Server.Connection
|
||||
[Dependency] private readonly ILocalizationManager _loc = default!;
|
||||
[Dependency] private readonly ServerDbEntryManager _serverDbEntry = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
//WD-EDIT
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netMgr.Connecting += NetMgrOnConnecting;
|
||||
@@ -152,13 +159,14 @@ namespace Content.Server.Connection
|
||||
}
|
||||
}
|
||||
|
||||
var wasInGame = EntitySystem.TryGet<GameTicker>(out var ticker) &&
|
||||
ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
|
||||
status == PlayerGameStatus.JoinedGame;
|
||||
if ((_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && adminData is null) && !wasInGame)
|
||||
//WD-EDIT
|
||||
var isQueueEnabled = _cfg.GetCVar(WhiteCVars.QueueEnabled);
|
||||
var isPrivileged = await HavePrivilegedJoin(e.UserId);
|
||||
if (_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && !isPrivileged && !isQueueEnabled)
|
||||
{
|
||||
return (ConnectionDenyReason.Full, Loc.GetString("soft-player-cap-full"), null);
|
||||
}
|
||||
//WD-EDIT
|
||||
|
||||
var bans = await _db.GetServerBansAsync(addr, userId, hwId, includeUnbanned: false);
|
||||
if (bans.Count > 0)
|
||||
@@ -205,5 +213,20 @@ namespace Content.Server.Connection
|
||||
await _db.AssignUserIdAsync(name, assigned);
|
||||
return assigned;
|
||||
}
|
||||
|
||||
//WD-EDIT
|
||||
public async Task<bool> HavePrivilegedJoin(NetUserId userId)
|
||||
{
|
||||
var adminData = await _dbManager.GetAdminDataForAsync(userId);
|
||||
|
||||
var havePriorityJoin = _sponsorsManager.TryGetInfo(userId, out var sponsorData) && sponsorData.HavePriorityJoin;
|
||||
var wasInGame = EntitySystem.TryGet<GameTicker>(out var ticker) &&
|
||||
ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
|
||||
status == PlayerGameStatus.JoinedGame;
|
||||
return adminData != null ||
|
||||
havePriorityJoin ||
|
||||
wasInGame;
|
||||
}
|
||||
//WD-EDIT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Server.White.JoinQueue;
|
||||
using Content.Server.White.Sponsors;
|
||||
|
||||
namespace Content.Server.Entry
|
||||
{
|
||||
@@ -41,7 +43,6 @@ namespace Content.Server.Entry
|
||||
private IVoteManager _voteManager = default!;
|
||||
private ServerUpdateManager _updateManager = default!;
|
||||
private PlayTimeTrackingManager? _playTimeTracking;
|
||||
private IEntitySystemManager? _sysMan;
|
||||
private IServerDbManager? _dbManager;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -82,31 +83,36 @@ namespace Content.Server.Entry
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
var dest = configManager.GetCVar(CCVars.DestinationFile);
|
||||
IoCManager.Resolve<ContentLocalizationManager>().Initialize();
|
||||
if (string.IsNullOrEmpty(dest)) //hacky but it keeps load times for the generator down.
|
||||
{
|
||||
_euiManager = IoCManager.Resolve<EuiManager>();
|
||||
_voteManager = IoCManager.Resolve<IVoteManager>();
|
||||
_updateManager = IoCManager.Resolve<ServerUpdateManager>();
|
||||
_playTimeTracking = IoCManager.Resolve<PlayTimeTrackingManager>();
|
||||
_sysMan = IoCManager.Resolve<IEntitySystemManager>();
|
||||
_dbManager = IoCManager.Resolve<IServerDbManager>();
|
||||
if (!string.IsNullOrEmpty(dest)) //hacky but it keeps load times for the generator down.
|
||||
return;
|
||||
|
||||
logManager.GetSawmill("Storage").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("db.ef").Level = LogLevel.Info;
|
||||
_euiManager = IoCManager.Resolve<EuiManager>();
|
||||
_voteManager = IoCManager.Resolve<IVoteManager>();
|
||||
_updateManager = IoCManager.Resolve<ServerUpdateManager>();
|
||||
_playTimeTracking = IoCManager.Resolve<PlayTimeTrackingManager>();
|
||||
IoCManager.Resolve<IEntitySystemManager>();
|
||||
_dbManager = IoCManager.Resolve<IServerDbManager>();
|
||||
|
||||
IoCManager.Resolve<IAdminLogManager>().Initialize();
|
||||
IoCManager.Resolve<IConnectionManager>().Initialize();
|
||||
_dbManager.Init();
|
||||
IoCManager.Resolve<IServerPreferencesManager>().Init();
|
||||
IoCManager.Resolve<INodeGroupFactory>().Initialize();
|
||||
IoCManager.Resolve<ContentNetworkResourceManager>().Initialize();
|
||||
IoCManager.Resolve<GhostKickManager>().Initialize();
|
||||
IoCManager.Resolve<ServerInfoManager>().Initialize();
|
||||
logManager.GetSawmill("Storage").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("db.ef").Level = LogLevel.Info;
|
||||
|
||||
_voteManager.Initialize();
|
||||
_updateManager.Initialize();
|
||||
_playTimeTracking.Initialize();
|
||||
}
|
||||
IoCManager.Resolve<IAdminLogManager>().Initialize();
|
||||
IoCManager.Resolve<IConnectionManager>().Initialize();
|
||||
_dbManager.Init();
|
||||
IoCManager.Resolve<IServerPreferencesManager>().Init();
|
||||
IoCManager.Resolve<INodeGroupFactory>().Initialize();
|
||||
IoCManager.Resolve<ContentNetworkResourceManager>().Initialize();
|
||||
IoCManager.Resolve<GhostKickManager>().Initialize();
|
||||
IoCManager.Resolve<ServerInfoManager>().Initialize();
|
||||
|
||||
//WD-EDIT
|
||||
IoCManager.Resolve<SponsorsManager>().Initialize();
|
||||
IoCManager.Resolve<JoinQueueManager>().Initialize();
|
||||
//WD-EDIT
|
||||
|
||||
_voteManager.Initialize();
|
||||
_updateManager.Initialize();
|
||||
_playTimeTracking.Initialize();
|
||||
}
|
||||
|
||||
public override void PostInit()
|
||||
|
||||
@@ -7,7 +7,6 @@ using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameTicking
|
||||
@@ -32,7 +31,7 @@ namespace Content.Server.GameTicking
|
||||
if (args.NewStatus != SessionStatus.Disconnected)
|
||||
{
|
||||
mind.Session = session;
|
||||
_pvsOverride.AddSessionOverride(GetNetEntity(mindId.Value), session);
|
||||
_pvsOverride.AddSessionOverride(mindId.Value, session);
|
||||
}
|
||||
|
||||
DebugTools.Assert(mind.Session == session);
|
||||
@@ -56,7 +55,7 @@ namespace Content.Server.GameTicking
|
||||
|
||||
// Make the player actually join the game.
|
||||
// timer time must be > tick length
|
||||
Timer.Spawn(0, () => _playerManager.JoinGame(args.Session));
|
||||
// Timer.Spawn(0, args.Session.JoinGame); // Moved to `JoinQueueManager`(WD-EDIT)
|
||||
|
||||
var record = await _dbManager.GetPlayerRecordByUserId(args.Session.UserId);
|
||||
var firstConnection = record != null &&
|
||||
@@ -124,7 +123,8 @@ namespace Content.Server.GameTicking
|
||||
mind.Session = null;
|
||||
}
|
||||
|
||||
_userDb.ClientDisconnected(session);
|
||||
if (_playerGameStatuses.ContainsKey(args.Session.UserId)) //WD-EDIT
|
||||
_userDb.ClientDisconnected(session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ namespace Content.Server.GameTicking
|
||||
_playerGameStatuses[session.UserId] = PlayerGameStatus.JoinedGame;
|
||||
_db.AddRoundPlayers(RoundId, session.UserId);
|
||||
|
||||
RaiseNetworkEvent(new TickerJoinGameEvent(), session.ConnectedClient);
|
||||
RaiseNetworkEvent(new TickerJoinGameEvent(), session.Channel);
|
||||
}
|
||||
|
||||
private void PlayerJoinLobby(ICommonSession session)
|
||||
@@ -174,7 +174,7 @@ namespace Content.Server.GameTicking
|
||||
_playerGameStatuses[session.UserId] = LobbyEnabled ? PlayerGameStatus.NotReadyToPlay : PlayerGameStatus.ReadyToPlay;
|
||||
_db.AddRoundPlayers(RoundId, session.UserId);
|
||||
|
||||
var client = session.ConnectedClient;
|
||||
var client = session.Channel;
|
||||
RaiseNetworkEvent(new TickerJoinLobbyEvent(), client);
|
||||
RaiseNetworkEvent(GetStatusMsg(session), client);
|
||||
RaiseNetworkEvent(GetInfoMsg(), client);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using Content.Server.White.JoinQueue;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Server.ServerStatus;
|
||||
@@ -28,6 +29,10 @@ namespace Content.Server.GameTicking
|
||||
/// </summary>
|
||||
[Dependency] private readonly SharedGameTicker _gameTicker = default!;
|
||||
|
||||
// WD-EDIT
|
||||
[Dependency] private readonly JoinQueueManager _queueManager = default!;
|
||||
// WD-EDIT
|
||||
|
||||
private void InitializeStatusShell()
|
||||
{
|
||||
IoCManager.Resolve<IStatusHost>().OnStatusRequest += GetStatusResponse;
|
||||
@@ -43,7 +48,7 @@ namespace Content.Server.GameTicking
|
||||
jObject["name"] = _baseServer.ServerName;
|
||||
jObject["map"] = _gameMapManager.GetSelectedMap()?.MapName;
|
||||
jObject["round_id"] = _gameTicker.RoundId;
|
||||
jObject["players"] = _playerManager.PlayerCount;
|
||||
jObject["players"] = _queueManager.ActualPlayersCount; //WD-EDIT
|
||||
jObject["soft_max_players"] = _cfg.GetCVar(CCVars.SoftMaxPlayers);
|
||||
jObject["panic_bunker"] = _cfg.GetCVar(CCVars.PanicBunkerEnabled);
|
||||
jObject["run_level"] = (int) _runLevel;
|
||||
|
||||
@@ -19,6 +19,8 @@ using Content.Server.ServerInfo;
|
||||
using Content.Server.ServerUpdates;
|
||||
using Content.Server.Voting.Managers;
|
||||
using Content.Server.Worldgen.Tools;
|
||||
using Content.Server.White.JoinQueue;
|
||||
using Content.Server.White.Sponsors;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Kitchen;
|
||||
@@ -58,6 +60,11 @@ namespace Content.Server.IoC
|
||||
IoCManager.Register<PoissonDiskSampler>();
|
||||
IoCManager.Register<DiscordWebhook>();
|
||||
IoCManager.Register<ServerDbEntryManager>();
|
||||
|
||||
// WD-EDIT
|
||||
IoCManager.Register<SponsorsManager>();
|
||||
IoCManager.Register<JoinQueueManager>();
|
||||
// WD-EDIT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Server.White.Sponsors;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
@@ -27,6 +28,10 @@ namespace Content.Server.Preferences.Managers
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protos = default!;
|
||||
|
||||
// WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsors = default!;
|
||||
// WD-EDIT
|
||||
|
||||
// Cache player prefs on the server so we don't need as much async hell related to them.
|
||||
private readonly Dictionary<NetUserId, PlayerPrefData> _cachedPlayerPrefs =
|
||||
new();
|
||||
@@ -99,7 +104,10 @@ namespace Content.Server.Preferences.Managers
|
||||
|
||||
var curPrefs = prefsData.Prefs!;
|
||||
|
||||
profile.EnsureValid();
|
||||
// WD-EDIT
|
||||
var allowedMarkings = _sponsors.TryGetInfo(message.MsgChannel.UserId, out var sponsor) ? sponsor.AllowedMarkings : new string[]{};
|
||||
profile.EnsureValid(allowedMarkings);
|
||||
// WD-EDIT
|
||||
|
||||
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
|
||||
{
|
||||
@@ -193,6 +201,15 @@ namespace Content.Server.Preferences.Managers
|
||||
async Task LoadPrefs()
|
||||
{
|
||||
var prefs = await GetOrCreatePreferencesAsync(session.UserId);
|
||||
|
||||
// WD-EDIT
|
||||
foreach (var (_, profile) in prefs.Characters)
|
||||
{
|
||||
var allowedMarkings = _sponsors.TryGetInfo(session.UserId, out var sponsor) ? sponsor.AllowedMarkings : new string[]{};
|
||||
profile.EnsureValid(allowedMarkings);
|
||||
}
|
||||
// WD-EDIT
|
||||
|
||||
prefsData.Prefs = prefs;
|
||||
prefsData.PrefsLoaded = true;
|
||||
|
||||
|
||||
132
Content.Server/White/JoinQueue/JoinQueueManager.cs
Normal file
132
Content.Server/White/JoinQueue/JoinQueueManager.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Connection;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.White;
|
||||
using Content.Shared.White.JoinQueue;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.White.JoinQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Manages new player connections when the server is full and queues them up, granting access when a slot becomes free
|
||||
/// </summary>
|
||||
public sealed class JoinQueueManager
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IConnectionManager _connectionManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IServerNetManager _netManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Queue of active player sessions
|
||||
/// </summary>
|
||||
private readonly List<ICommonSession> _queue = new(); // Real Queue class can't delete disconnected users
|
||||
|
||||
private bool _isEnabled;
|
||||
|
||||
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<MsgQueueUpdate>();
|
||||
|
||||
_cfg.OnValueChanged(WhiteCVars.QueueEnabled, OnQueueCVarChanged, true);
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private void OnQueueCVarChanged(bool value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
|
||||
if (value)
|
||||
return;
|
||||
|
||||
foreach (var session in _queue)
|
||||
{
|
||||
session.Channel.Disconnect("Queue was disabled");
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
switch (e.NewStatus)
|
||||
{
|
||||
case SessionStatus.Connected:
|
||||
{
|
||||
if (!_isEnabled)
|
||||
{
|
||||
SendToGame(e.Session);
|
||||
return;
|
||||
}
|
||||
|
||||
var isPrivileged = await _connectionManager.HavePrivilegedJoin(e.Session.UserId);
|
||||
var haveFreeSlot = _playerManager.PlayerCount < _cfg.GetCVar(CCVars.SoftMaxPlayers);
|
||||
if (isPrivileged || haveFreeSlot)
|
||||
{
|
||||
SendToGame(e.Session);
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Add(e.Session);
|
||||
ProcessQueue(false);
|
||||
break;
|
||||
}
|
||||
case SessionStatus.Disconnected:
|
||||
{
|
||||
_queue.Remove(e.Session);
|
||||
ProcessQueue(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If possible, takes the first player in the queue and sends him into the game
|
||||
/// </summary>
|
||||
private void ProcessQueue(bool isDisconnect)
|
||||
{
|
||||
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 ((!_isEnabled || haveFreeSlot) && queueContains)
|
||||
{
|
||||
var session = _queue.First();
|
||||
_queue.Remove(session);
|
||||
SendToGame(session);
|
||||
}
|
||||
|
||||
SendUpdateMessages();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends messages to all players in the queue with the current state of the queue
|
||||
/// </summary>
|
||||
private void SendUpdateMessages()
|
||||
{
|
||||
for (var i = 0; i < _queue.Count; i++)
|
||||
{
|
||||
_queue[i].Channel.SendMessage(new MsgQueueUpdate
|
||||
{
|
||||
Total = _queue.Count,
|
||||
Position = i + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Letting player's session into game, change player state
|
||||
/// </summary>
|
||||
private void SendToGame(ICommonSession s)
|
||||
{
|
||||
Timer.Spawn(0, () => _playerManager.JoinGame(s));
|
||||
}
|
||||
}
|
||||
92
Content.Server/White/Sponsors/SponsorsManager.cs
Normal file
92
Content.Server/White/Sponsors/SponsorsManager.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.White;
|
||||
using Content.Shared.White.Sponsors;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.White.Sponsors;
|
||||
|
||||
public sealed class SponsorsManager
|
||||
{
|
||||
[Dependency] private readonly IServerNetManager _netMgr = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private readonly HttpClient _httpClient = new();
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private string _apiUrl = string.Empty;
|
||||
|
||||
private readonly Dictionary<NetUserId, SponsorInfo> _cachedSponsors = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_sawmill = Logger.GetSawmill("sponsors");
|
||||
_cfg.OnValueChanged(WhiteCVars.SponsorsApiUrl, s => _apiUrl = s, true);
|
||||
|
||||
_netMgr.RegisterNetMessage<MsgSponsorInfo>();
|
||||
|
||||
_netMgr.Connecting += OnConnecting;
|
||||
_netMgr.Connected += OnConnected;
|
||||
_netMgr.Disconnect += OnDisconnect;
|
||||
}
|
||||
|
||||
public bool TryGetInfo(NetUserId userId, [NotNullWhen(true)] out SponsorInfo? sponsor)
|
||||
{
|
||||
return _cachedSponsors.TryGetValue(userId, out sponsor);
|
||||
}
|
||||
|
||||
private async Task OnConnecting(NetConnectingArgs e)
|
||||
{
|
||||
var info = await LoadSponsorInfo(e.UserId);
|
||||
if (info?.Tier == null)
|
||||
{
|
||||
_cachedSponsors.Remove(e.UserId); // Remove from cache if sponsor expired
|
||||
return;
|
||||
}
|
||||
|
||||
DebugTools.Assert(!_cachedSponsors.ContainsKey(e.UserId), "Cached data was found on client connect");
|
||||
|
||||
_cachedSponsors[e.UserId] = info;
|
||||
}
|
||||
|
||||
private void OnConnected(object? sender, NetChannelArgs e)
|
||||
{
|
||||
var info = _cachedSponsors.TryGetValue(e.Channel.UserId, out var sponsor) ? sponsor : null;
|
||||
var msg = new MsgSponsorInfo() { Info = info };
|
||||
_netMgr.ServerSendMessage(msg, e.Channel);
|
||||
}
|
||||
|
||||
private void OnDisconnect(object? sender, NetDisconnectedArgs e)
|
||||
{
|
||||
_cachedSponsors.Remove(e.Channel.UserId);
|
||||
}
|
||||
|
||||
private async Task<SponsorInfo?> LoadSponsorInfo(NetUserId userId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_apiUrl))
|
||||
return null;
|
||||
|
||||
var url = $"{_apiUrl}/sponsors/{userId.ToString()}";
|
||||
var response = await _httpClient.GetAsync(url);
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
return null;
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
var errorText = await response.Content.ReadAsStringAsync();
|
||||
_sawmill.Error(
|
||||
"Failed to get player sponsor OOC color from API: [{StatusCode}] {Response}",
|
||||
response.StatusCode,
|
||||
errorText);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await response.Content.ReadFromJsonAsync<SponsorInfo>();
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ namespace Content.Shared.Humanoid
|
||||
return new(color.RByte, color.GByte, color.BByte);
|
||||
}
|
||||
|
||||
public static HumanoidCharacterAppearance EnsureValid(HumanoidCharacterAppearance appearance, string species, Sex sex)
|
||||
public static HumanoidCharacterAppearance EnsureValid(HumanoidCharacterAppearance appearance, string species, string[] sponsorMarkings) //WD-EDIT
|
||||
{
|
||||
var hairStyleId = appearance.HairStyleId;
|
||||
var facialHairStyleId = appearance.FacialHairStyleId;
|
||||
@@ -205,27 +205,57 @@ namespace Content.Shared.Humanoid
|
||||
hairStyleId = HairStyles.DefaultHairStyle;
|
||||
}
|
||||
|
||||
// WD-EDIT
|
||||
if (proto.TryIndex(hairStyleId, out MarkingPrototype? hairProto) &&
|
||||
hairProto.SponsorOnly &&
|
||||
!sponsorMarkings.Contains(hairStyleId))
|
||||
{
|
||||
hairStyleId = HairStyles.DefaultHairStyle;
|
||||
}
|
||||
// WD-EDIT
|
||||
|
||||
if (!markingManager.MarkingsByCategory(MarkingCategories.FacialHair).ContainsKey(facialHairStyleId))
|
||||
{
|
||||
facialHairStyleId = HairStyles.DefaultFacialHairStyle;
|
||||
}
|
||||
|
||||
// WD-EDIT
|
||||
if (proto.TryIndex(facialHairStyleId, out MarkingPrototype? facialHairProto) &&
|
||||
facialHairProto.SponsorOnly &&
|
||||
!sponsorMarkings.Contains(facialHairStyleId))
|
||||
{
|
||||
facialHairStyleId = HairStyles.DefaultFacialHairStyle;
|
||||
}
|
||||
// WD-EDIT
|
||||
|
||||
var markingSet = new MarkingSet();
|
||||
var skinColor = appearance.SkinColor;
|
||||
if (proto.TryIndex(species, out SpeciesPrototype? speciesProto))
|
||||
if (!proto.TryIndex(species, out SpeciesPrototype? speciesProto))
|
||||
{
|
||||
markingSet = new MarkingSet(appearance.Markings, speciesProto.MarkingPoints, markingManager, proto);
|
||||
markingSet.EnsureValid(markingManager);
|
||||
|
||||
if (!Humanoid.SkinColor.VerifySkinColor(speciesProto.SkinColoration, skinColor))
|
||||
{
|
||||
skinColor = Humanoid.SkinColor.ValidSkinTone(speciesProto.SkinColoration, skinColor);
|
||||
}
|
||||
|
||||
markingSet.EnsureSpecies(species, skinColor, markingManager);
|
||||
markingSet.EnsureSexes(sex, markingManager);
|
||||
return new HumanoidCharacterAppearance(
|
||||
hairStyleId,
|
||||
hairColor,
|
||||
facialHairStyleId,
|
||||
facialHairColor,
|
||||
eyeColor,
|
||||
skinColor,
|
||||
markingSet.GetForwardEnumerator().ToList());
|
||||
}
|
||||
|
||||
markingSet = new MarkingSet(appearance.Markings, speciesProto.MarkingPoints, markingManager, proto);
|
||||
markingSet.EnsureValid(markingManager);
|
||||
|
||||
if (!Humanoid.SkinColor.VerifySkinColor(speciesProto.SkinColoration, skinColor))
|
||||
{
|
||||
skinColor = Humanoid.SkinColor.ValidSkinTone(speciesProto.SkinColoration, skinColor);
|
||||
}
|
||||
|
||||
markingSet.EnsureSpecies(species, skinColor, markingManager);
|
||||
|
||||
// WD-EDIT
|
||||
markingSet.FilterSponsor(sponsorMarkings, markingManager);
|
||||
// WD-EDIT
|
||||
|
||||
return new HumanoidCharacterAppearance(
|
||||
hairStyleId,
|
||||
hairColor,
|
||||
@@ -238,15 +268,21 @@ namespace Content.Shared.Humanoid
|
||||
|
||||
public bool MemberwiseEquals(ICharacterAppearance maybeOther)
|
||||
{
|
||||
if (maybeOther is not HumanoidCharacterAppearance other) return false;
|
||||
if (HairStyleId != other.HairStyleId) return false;
|
||||
if (!HairColor.Equals(other.HairColor)) return false;
|
||||
if (FacialHairStyleId != other.FacialHairStyleId) return false;
|
||||
if (!FacialHairColor.Equals(other.FacialHairColor)) return false;
|
||||
if (!EyeColor.Equals(other.EyeColor)) return false;
|
||||
if (!SkinColor.Equals(other.SkinColor)) return false;
|
||||
if (!Markings.SequenceEqual(other.Markings)) return false;
|
||||
return true;
|
||||
if (maybeOther is not HumanoidCharacterAppearance other)
|
||||
return false;
|
||||
if (HairStyleId != other.HairStyleId)
|
||||
return false;
|
||||
if (!HairColor.Equals(other.HairColor))
|
||||
return false;
|
||||
if (FacialHairStyleId != other.FacialHairStyleId)
|
||||
return false;
|
||||
if (!FacialHairColor.Equals(other.FacialHairColor))
|
||||
return false;
|
||||
if (!EyeColor.Equals(other.EyeColor))
|
||||
return false;
|
||||
if (!SkinColor.Equals(other.SkinColor))
|
||||
return false;
|
||||
return Markings.SequenceEqual(other.Markings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ namespace Content.Shared.Humanoid.Markings
|
||||
[DataField("sexRestriction")]
|
||||
public Sex? SexRestriction { get; private set; }
|
||||
|
||||
// WD-EDIT
|
||||
[DataField("sponsorOnly")] public bool SponsorOnly;
|
||||
// WD-EDIT
|
||||
|
||||
[DataField("followSkinColor")]
|
||||
public bool FollowSkinColor { get; private set; } = false;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Humanoid.Markings;
|
||||
|
||||
@@ -265,6 +264,37 @@ public sealed partial class MarkingSet
|
||||
}
|
||||
}
|
||||
|
||||
// WD-EDIT
|
||||
public void FilterSponsor(string[] sponsorMarkings, MarkingManager? markingManager = null, IPrototypeManager? prototypeManager = null)
|
||||
{
|
||||
IoCManager.Resolve(ref markingManager);
|
||||
IoCManager.Resolve(ref prototypeManager);
|
||||
|
||||
var toRemove = new List<(MarkingCategories category, string id)>();
|
||||
foreach (var (category, list) in Markings)
|
||||
{
|
||||
foreach (var marking in list)
|
||||
{
|
||||
if (prototypeManager.TryIndex<MarkingPrototype>(marking.MarkingId, out var proto) && !proto.SponsorOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var allowedToHave = sponsorMarkings.Contains(marking.MarkingId);
|
||||
if (!allowedToHave)
|
||||
{
|
||||
toRemove.Add((category, marking.MarkingId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var marking in toRemove)
|
||||
{
|
||||
Remove(marking.category, marking.id);
|
||||
}
|
||||
}
|
||||
// WD-EDIT
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the default markings as defined by the marking point set in this marking set are applied.
|
||||
/// </summary>
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Content.Shared.Preferences
|
||||
return Appearance.MemberwiseEquals(other.Appearance);
|
||||
}
|
||||
|
||||
public void EnsureValid()
|
||||
public void EnsureValid(string[] sponsorMarkings) //WD-EDIT
|
||||
{
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
@@ -433,7 +433,9 @@ namespace Content.Shared.Preferences
|
||||
flavortext = FormattedMessage.RemoveMarkup(FlavorText);
|
||||
}
|
||||
|
||||
var appearance = HumanoidCharacterAppearance.EnsureValid(Appearance, Species, Sex);
|
||||
// WD-EDIT
|
||||
var appearance = HumanoidCharacterAppearance.EnsureValid(Appearance, Species, sponsorMarkings);
|
||||
// WD-EDIT
|
||||
|
||||
var prefsUnavailableMode = PreferenceUnavailable switch
|
||||
{
|
||||
|
||||
@@ -13,6 +13,6 @@ namespace Content.Shared.Preferences
|
||||
/// <summary>
|
||||
/// Makes this profile valid so there's no bad data like negative ages.
|
||||
/// </summary>
|
||||
void EnsureValid();
|
||||
void EnsureValid(string[] sponsorMarkings); //WD-EDIT
|
||||
}
|
||||
}
|
||||
|
||||
36
Content.Shared/White/JoinQueue/MsgQueueUpdate.cs
Normal file
36
Content.Shared/White/JoinQueue/MsgQueueUpdate.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.White.JoinQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Sent from server to client with queue state for player
|
||||
/// Also initiates queue state on client
|
||||
/// </summary>
|
||||
public sealed class MsgQueueUpdate : NetMessage
|
||||
{
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// Total players in queue
|
||||
/// </summary>
|
||||
public int Total { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Player current position in queue (starts from 1)
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
62
Content.Shared/White/Sponsors/MsgSponsorInfo.cs
Normal file
62
Content.Shared/White/Sponsors/MsgSponsorInfo.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.IO;
|
||||
using System.Text.Json.Serialization;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.White.Sponsors;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SponsorInfo
|
||||
{
|
||||
[JsonPropertyName("tier")]
|
||||
public int? Tier { get; set; }
|
||||
|
||||
[JsonPropertyName("oocColor")]
|
||||
public string? OOCColor { get; set; }
|
||||
|
||||
[JsonPropertyName("priorityJoin")]
|
||||
public bool HavePriorityJoin { get; set; } = false;
|
||||
|
||||
[JsonPropertyName("allowedMarkings")]
|
||||
public string[] AllowedMarkings { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server sends sponsoring info to client on connect only if user is sponsor
|
||||
/// </summary>
|
||||
public sealed class MsgSponsorInfo : NetMessage
|
||||
{
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public SponsorInfo? Info;
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
|
||||
{
|
||||
var isSponsor = buffer.ReadBoolean();
|
||||
buffer.ReadPadBits();
|
||||
|
||||
if (!isSponsor)
|
||||
return;
|
||||
|
||||
var length = buffer.ReadVariableInt32();
|
||||
using var stream = new MemoryStream(length);
|
||||
buffer.ReadAlignedMemory(stream, length);
|
||||
serializer.DeserializeDirect(stream, out Info);
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
|
||||
{
|
||||
buffer.Write(Info != null);
|
||||
buffer.WritePadBits();
|
||||
|
||||
if (Info == null)
|
||||
return;
|
||||
|
||||
var stream = new MemoryStream();
|
||||
serializer.SerializeDirect(stream, Info);
|
||||
buffer.WriteVariableInt32((int) stream.Length);
|
||||
buffer.Write(stream.AsSpan());
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,20 @@ public sealed class WhiteCVars
|
||||
|
||||
public static readonly CVarDef<bool> ChatSlangFilter =
|
||||
CVarDef.Create("ic.slang_filter", true, CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
|
||||
|
||||
/*
|
||||
* Sponsors
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<string> SponsorsApiUrl =
|
||||
CVarDef.Create("sponsor.api_url", "", CVar.SERVERONLY);
|
||||
|
||||
/*
|
||||
* Queue
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool>
|
||||
QueueEnabled = CVarDef.Create("queue.enabled", false, CVar.SERVERONLY);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
bodyPart: HeadTop
|
||||
markingCategory: HeadTop
|
||||
speciesRestriction: [Human]
|
||||
sponsorOnly: true
|
||||
coloring:
|
||||
default:
|
||||
type:
|
||||
@@ -26,6 +27,7 @@
|
||||
bodyPart: Tail
|
||||
markingCategory: Tail
|
||||
speciesRestriction: [Human]
|
||||
sponsorOnly: true
|
||||
coloring:
|
||||
default:
|
||||
type:
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
Hair: MobHumanoidAnyMarking
|
||||
FacialHair: MobHumanoidAnyMarking
|
||||
Chest: MobHumanTorso
|
||||
HeadTop: MobHumanoidAnyMarking
|
||||
Tail: MobHumanoidAnyMarking
|
||||
Eyes: MobHumanoidEyes
|
||||
LArm: MobHumanLArm
|
||||
RArm: MobHumanRArm
|
||||
@@ -41,10 +43,10 @@
|
||||
points: 1
|
||||
required: false
|
||||
Tail: # the cat tail joke
|
||||
points: 0
|
||||
points: 1
|
||||
required: false
|
||||
HeadTop: # the cat ear joke
|
||||
points: 0
|
||||
points: 1
|
||||
required: false
|
||||
Chest:
|
||||
points: 1
|
||||
|
||||
Reference in New Issue
Block a user