Skip to content

Commit

Permalink
Merge branch 'master' into weapon-rebalance
Browse files Browse the repository at this point in the history
  • Loading branch information
nomad0260 committed Mar 11, 2024
2 parents 81d605c + 9831574 commit 0759a1e
Show file tree
Hide file tree
Showing 39 changed files with 1,057 additions and 393 deletions.
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
contact_links:
- name: Предложение
url: https://discord.station14.ru
url: https://backmen.ru/forums/forum/5-%D0%BF%D1%80%D0%B5%D0%B4%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B8-%D0%BA%D1%80%D0%B8%D1%82%D0%B8%D0%BA%D0%B0/
about: Свои предложения можете оставлять в соответствующем канале Discord.
- name: Сообщить об уязвимости
url: https://discord.station14.ru
about: Пожалуйста, сообщайте о серьезных эксплойтах и уязвимостях безопасности Morty#7384 (369476049836310528) в Discord.
url: https://backmen.ru/tickets/support/?do=form
about: Пожалуйста, сообщайте о серьезных эксплойтах и уязвимостях безопасности backmen (156474902340173824) в Discord.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public void Update(UpdateReinforcementUi updateReinforcementUi)
if (cur < min)
{
OnKeySelected.Invoke((uint)member, (uint)min);
cur = min;
r.ModelCount = cur = min;
}

r.JobName.Text = Loc.GetString("reinforcement-row", ("name", Loc.GetString(r.Model!.Name)), ("job", jobTitle));
Expand Down Expand Up @@ -179,6 +179,7 @@ private void UpdateRows()
}

r.PlusBtn.Disabled = max <= cur || r.Model?.MaxCount <= r.ModelCount;
r.MinusBtn.Disabled = r.Model?.MinCount >= r.ModelCount;
}
}

Expand Down
259 changes: 259 additions & 0 deletions Content.Server/Backmen/Ghost/Roles/GhostRoleRollerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
using System.Linq;
using System.Threading;
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Server.Ghost.Roles;
using Content.Server.Ghost.Roles.Components;
using Content.Shared.Backmen.CCVar;
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.GameTicking;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Timer = Robust.Shared.Timing.Timer;

namespace Content.Server.Backmen.Ghost.Roles;

public sealed class GhostRoleRollerSystem : EntitySystem
{
private record TimerQueue(TimeSpan Start, TimeSpan DeadLine)
{
public bool IsFinished { get; set; } = false;
public bool IsProcess { get; set; } = false;
public TimeSpan? FinishDate { get; set; }
public NetUserId? FinishUser { get; set; }
public Dictionary<NetUserId,float> Bids { get; } = new();
public CancellationTokenSource TokenSource { get; } = new();
}

[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly GhostRoleSystem _ghostRoleSystem = default!;

private ISawmill _sawmill = default!;

private readonly Dictionary<uint, TimerQueue> _queues = new();
private readonly Dictionary<NetUserId, List<TimerQueue>> _history = new();
private readonly HashSet<NetUserId> _busy = new();

private bool _enabled = false;
private TimeSpan _roleTimer = TimeSpan.FromSeconds(3);
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<RoundRestartCleanupEvent>(OnCleanup);
_cfg.OnValueChanged(CCVars.GhostRollerTime, f =>
{
_roleTimer = TimeSpan.FromSeconds(f);
}, true);

_cfg.OnValueChanged(CCVars.GhostRollerEnabled, f =>
{
_enabled = f;
if(!f)
Cleanup();
}, true);

_sawmill = _logManager.GetSawmill("backmen.ghost");

_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
}

public override void Shutdown()
{
base.Shutdown();

_playerManager.PlayerStatusChanged -= PlayerManagerOnPlayerStatusChanged;
}

private void PlayerManagerOnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus is SessionStatus.Disconnected or SessionStatus.Connected)
{
_busy.Remove(e.Session.UserId);
}
}

private void OnCleanup(RoundRestartCleanupEvent ev)
{
Cleanup();
}

private void Cleanup()
{
_queues.Clear();
_history.Clear();
_busy.Clear();
}

public void RegisterGhostRole(Entity<GhostRoleComponent> role)
{
if(!_enabled)
return;

var now = _gameTicker.RoundDuration();
var q = new TimerQueue(now, now + _roleTimer + TimeSpan.FromSeconds(1));
if(_queues.TryAdd(role.Comp.Identifier, q))
Timer.Spawn(_roleTimer, () => EndRoleTimer(role), q.TokenSource.Token);
}

public void UnregisterGhostRole(Entity<GhostRoleComponent> role)
{
if(!_enabled)
return;

if (!_queues.TryGetValue(role.Comp.Identifier, out var queue) || queue.IsFinished)
return;
foreach (var (user, _) in queue.Bids)
{
_busy.Remove(user);
}
queue.TokenSource.Cancel(false);
}

public void Takeover(Entity<GhostRoleComponent> role, ref TakeGhostRoleEvent ev)
{
if(!_enabled)
return;

var now = _gameTicker.RoundDuration();

if (_busy.Contains(ev.Player.UserId))
{
_chatManager.DispatchServerMessage(ev.Player, Loc.GetString("ghostroller-busy"), true);
ev.TookRole = true;
return;
}

if (TerminatingOrDeleted(role) ||
!_queues.TryGetValue(role.Comp.Identifier, out var queue) ||
queue.IsFinished ||
queue.DeadLine < now ||
queue.TokenSource.Token.IsCancellationRequested)
return;

if (queue.IsProcess)
{
_chatManager.DispatchServerMessage(ev.Player, Loc.GetString("ghostroller-is-process"), true);
ev.TookRole = true;
return;
}

ev.TookRole = true;

_busy.Add(ev.Player.UserId);
var bid = _random.Next(0, 100);
queue.Bids.TryAdd(ev.Player.UserId, bid);
_history.TryAdd(ev.Player.UserId, new List<TimerQueue>());
_history[ev.Player.UserId].Add(queue);
_chatManager.DispatchServerMessage(ev.Player, Loc.GetString("ghostroller-notify-bid",("entity",role.Owner),("roll",bid)), true);
}

private void EndRoleTimer(Entity<GhostRoleComponent> role)
{
if(!_enabled)
return;

if (!_queues.TryGetValue(role.Comp.Identifier, out var queue))
{
_sawmill.Error($"queue {role.Comp.Identifier} not exist!");
return;
}

try
{
queue.IsProcess = true;

if (TerminatingOrDeleted(role) || queue.TokenSource.Token.IsCancellationRequested)
{
_sawmill.Error($"queue {role.Comp.Identifier} delete or canceled!");
if (queue.Bids.Count > 0)
{
SendToAllPlayerInQueue(queue, Loc.GetString("ghostroller-notify-canceled"));
}

queue.IsFinished = true;
return;
}

var now = _gameTicker.RoundDuration();

startpick:
if (queue.Bids.Count == 0)
{
_sawmill.Error($"queue {role.Comp.Identifier} no binds!");
queue.IsFinished = true;
return;
}

var winner = queue.Bids.MaxBy(x => x.Value);

if (_playerManager.TryGetSessionById(winner.Key, out var player))
{


queue.FinishDate = now;
queue.FinishUser = winner.Key;

var ev = new TakeGhostRoleEvent(player);
RaiseLocalEvent(role, ref ev);

if (!ev.TookRole)
{
SendToAllPlayerInQueue(queue, Loc.GetString("ghostroller-cant-be-took"));
return;
}


if (player.AttachedEntity != null)
_adminLogger.Add(LogType.GhostRoleTaken, LogImpact.Low,
$"{player:player} took the {role.Comp.RoleName:roleName} ghost role {ToPrettyString(player.AttachedEntity.Value):entity} with roll {winner.Value}");

SendToAllPlayerInQueue(queue,
Loc.GetString("ghostroller-notify-winner", ("name", player.Name), ("entity", role.Owner), ("roll", winner.Value)), true);
}
else
{
queue.Bids.Remove(winner.Key);
goto startpick; //repick
}

queue.IsFinished = true;
}
finally
{
queue.IsProcess = false;
foreach (var (user, _) in queue.Bids)
{
_busy.Remove(user);
}
}
}

private void SendToAllPlayerInQueue(TimerQueue queue, string msg, bool cloneBUi = false)
{
var filter = Filter.Empty();
foreach (var (userId, _) in queue.Bids)
{
if(!_playerManager.TryGetSessionById(userId, out var sess))
continue;
filter.AddPlayer(sess);
_ghostRoleSystem.CloseEui(sess);
}
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(msg)));
_chatManager.ChatMessageToManyFiltered( filter, ChatChannel.Server, msg, wrappedMessage, default, false, true, null);
}

}
8 changes: 8 additions & 0 deletions Content.Server/Ghost/Roles/GhostRoleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public sealed class GhostRoleSystem : EntitySystem
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly Backmen.RoleWhitelist.WhitelistSystem _roleWhitelist = default!; // backmen: whitelist
[Dependency] private readonly Backmen.Ghost.Roles.GhostRoleRollerSystem _roleRoller = default!; // backmen: ghost roller
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;

private uint _nextRoleIdentifier;
Expand Down Expand Up @@ -186,6 +187,7 @@ public void RegisterGhostRole(Entity<GhostRoleComponent> role)
return;

_ghostRoles[role.Comp.Identifier = GetNextRoleIdentifier()] = role;
_roleRoller.RegisterGhostRole(role); // backmen: ghost roller
UpdateAllEui();
}

Expand All @@ -196,6 +198,7 @@ public void UnregisterGhostRole(Entity<GhostRoleComponent> role)
return;

_ghostRoles.Remove(comp.Identifier);
_roleRoller.UnregisterGhostRole(role); // backmen: ghost roller
UpdateAllEui();
}

Expand All @@ -210,6 +213,11 @@ public void Takeover(ICommonSession player, uint identifier)
// end-backmen: whitelist

var ev = new TakeGhostRoleEvent(player);
// start-backmen: ghost roller
_roleRoller.Takeover(role, ref ev);
if(ev.TookRole)
return;
// end-backmen: ghost roller
RaiseLocalEvent(role, ref ev);

if (!ev.TookRole)
Expand Down
13 changes: 13 additions & 0 deletions Content.Shared/Backmen/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,17 @@ public static readonly CVarDef<bool>

public static readonly CVarDef<int> BlobPlayersPer =
CVarDef.Create("blob.players_per", 20, CVar.SERVERONLY);


/*
* enabling a roll to enter a ghost role for one player from the vote
*/
public static readonly CVarDef<bool>
GhostRollerEnabled = CVarDef.Create("ghost.roller_enabled", false, CVar.SERVERONLY);

/// <summary>
/// the time that will be given to throw a number to vote for the ghost role
/// </summary>
public static readonly CVarDef<float> GhostRollerTime =
CVarDef.Create("ghost.roller_time", 10f, CVar.REPLICATED | CVar.SERVER);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ public sealed partial class ReinforcementPrototype : IPrototype
public int MaxCount = 1;

[DataField("min")]
public int MinCount = 1;
public int MinCount = 0;
}
7 changes: 7 additions & 0 deletions Resources/Locale/ru-RU/backmen/GhostRoleRollerSystem.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

ghostroller-cant-be-took = Данную роль нельзя взять
ghostroller-notify-winner = В голосовании за { $entity }, победил { $name }, с роллом { $roll }
ghostroller-notify-canceled = Голосование за роль было отменено
ghostroller-busy = Вы уже участвуете в раздаче!
ghostroller-is-process = Раздача уже в процессе!
ghostroller-notify-bid = Вы участвуте за { $entity } с роллом { $roll }
2 changes: 1 addition & 1 deletion Resources/Locale/ru-RU/backmen/reinforcement.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ reinforcement-error-list-2 = Ошибка! Список превышает ог
reinforcement-error-brief = Ошибка! Брифинг пуст!
reinforcement-team-size = Размер команды:
reinforcement-team-size-min = минмум: { $num }
reinforcement-team-size-min = минимум: { $num }
reinforcement-team-size-max = максимум: { $num }
reinforcement-ghostrole-name = Подкрепление: { $name }
Expand Down
1 change: 1 addition & 0 deletions Resources/Locale/ru-RU/materials/units.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ materials-unit-chunk = куски руды
# bills of spesos... not very good but they are not (yet?) used for crafting anything
# also the lathe/atm would need bigger denominations to output...
materials-unit-bill = банкноты
materials-unit-bill-of-exchange = вексель
Loading

0 comments on commit 0759a1e

Please sign in to comment.