Skip to content

Commit

Permalink
Merge pull request #351 from Rxup/upstream-sync
Browse files Browse the repository at this point in the history
Upstream sync
  • Loading branch information
Rxup authored Nov 28, 2023
2 parents 4b69438 + 239201f commit dd33fa9
Show file tree
Hide file tree
Showing 182 changed files with 340,466 additions and 154,372 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/update-wiki.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,13 @@ jobs:
api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
username: ${{ secrets.WIKI_BOT_USER }}
password: ${{ secrets.WIKI_BOT_PASS }}

- name: Upload mealrecipes_prototypes.json to wiki
uses: jtmullen/[email protected]
with:
wiki_text_file: ./bin/Content.Server/data/mealrecipes_prototypes.json
edit_summary: Update mealrecipes_prototypes.json via GitHub Actions
page_name: "${{ secrets.WIKI_PAGE_ROOT }}/mealrecipes_prototypes.json"
api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
username: ${{ secrets.WIKI_BOT_USER }}
password: ${{ secrets.WIKI_BOT_PASS }}
28 changes: 13 additions & 15 deletions Content.Server/Administration/Systems/AdminVerbSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,19 @@ private void AddAdminVerbs(GetVerbsEvent<Verb> args)
Impact = LogImpact.Extreme,
ConfirmationPopup = true
});

// Respawn
args.Verbs.Add(new Verb()
{
Text = Loc.GetString("admin-player-actions-respawn"),
Category = VerbCategory.Admin,
Act = () =>
{
_console.ExecuteCommand(player, $"respawn {targetActor.PlayerSession.Name}");
},
ConfirmationPopup = true,
// No logimpact as the command does it internally.
});
}

// Admin Logs
Expand Down Expand Up @@ -211,21 +224,6 @@ private void AddAdminVerbs(GetVerbsEvent<Verb> args)
Impact = LogImpact.Low
});

// Respawn
if (HasComp<ActorComponent>(args.Target))
{
args.Verbs.Add(new Verb()
{
Text = Loc.GetString("admin-player-actions-respawn"),
Category = VerbCategory.Admin,
Act = () =>
{
_console.ExecuteCommand(player, $"respawn {actor.PlayerSession.Name}");
},
ConfirmationPopup = true,
// No logimpact as the command does it internally.
});
}
}
}

Expand Down
84 changes: 84 additions & 0 deletions Content.Server/Chat/Managers/ChatManager.RateLimit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Runtime.InteropServices;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Robust.Shared.Enums;
using Robust.Shared.Player;
using Robust.Shared.Timing;

namespace Content.Server.Chat.Managers;

internal sealed partial class ChatManager
{
private readonly Dictionary<ICommonSession, RateLimitDatum> _rateLimitData = new();

public bool HandleRateLimit(ICommonSession player)
{
ref var datum = ref CollectionsMarshal.GetValueRefOrAddDefault(_rateLimitData, player, out _);
var time = _gameTiming.RealTime;
if (datum.CountExpires < time)
{
// Period expired, reset it.
var periodLength = _configurationManager.GetCVar(CCVars.ChatRateLimitPeriod);
datum.CountExpires = time + TimeSpan.FromSeconds(periodLength);
datum.Count = 0;
datum.Announced = false;
}

var maxCount = _configurationManager.GetCVar(CCVars.ChatRateLimitCount);
datum.Count += 1;

if (datum.Count <= maxCount)
return true;

// Breached rate limits, inform admins if configured.
if (_configurationManager.GetCVar(CCVars.ChatRateLimitAnnounceAdmins))
{
if (datum.NextAdminAnnounce < time)
{
SendAdminAlert(Loc.GetString("chat-manager-rate-limit-admin-announcement", ("player", player.Name)));
var delay = _configurationManager.GetCVar(CCVars.ChatRateLimitAnnounceAdminsDelay);
datum.NextAdminAnnounce = time + TimeSpan.FromSeconds(delay);
}
}

if (!datum.Announced)
{
DispatchServerMessage(player, Loc.GetString("chat-manager-rate-limited"), suppressLog: true);
_adminLogger.Add(LogType.ChatRateLimited, LogImpact.Medium, $"Player {player} breached chat rate limits");

datum.Announced = true;
}

return false;
}

private void PlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus == SessionStatus.Disconnected)
_rateLimitData.Remove(e.Session);
}

private struct RateLimitDatum
{
/// <summary>
/// Time stamp (relative to <see cref="IGameTiming.RealTime"/>) this rate limit period will expire at.
/// </summary>
public TimeSpan CountExpires;

/// <summary>
/// How many messages have been sent in the current rate limit period.
/// </summary>
public int Count;

/// <summary>
/// Have we announced to the player that they've been blocked in this rate limit period?
/// </summary>
public bool Announced;

/// <summary>
/// Time stamp (relative to <see cref="IGameTiming.RealTime"/>) of the
/// next time we can send an announcement to admins about rate limit breach.
/// </summary>
public TimeSpan NextAdminAnnounce;
}
}
12 changes: 11 additions & 1 deletion Content.Server/Chat/Managers/ChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.Mind;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Replays;
using Robust.Shared.Timing;
using Robust.Shared.Utility;

namespace Content.Server.Chat.Managers
{
/// <summary>
/// Dispatches chat messages to clients.
/// </summary>
internal sealed class ChatManager : IChatManager
internal sealed partial class ChatManager : IChatManager
{
private static readonly Dictionary<string, string> PatronOocColors = new()
{
Expand All @@ -42,6 +44,9 @@ internal sealed class ChatManager : IChatManager
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly INetConfigurationManager _netConfigManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;

private IServerSponsorsManager? _sponsorsManager; // Corvax-Sponsors

/// <summary>
Expand All @@ -62,6 +67,8 @@ public void Initialize()

_configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true);
_configurationManager.OnValueChanged(CCVars.AdminOocEnabled, OnAdminOocEnabledChanged, true);

_playerManager.PlayerStatusChanged += PlayerStatusChanged;
}

private void OnOocEnabledChanged(bool val)
Expand Down Expand Up @@ -181,6 +188,9 @@ public void SendHookOOC(string sender, string message)
/// <param name="type">The type of message.</param>
public void TrySendOOCMessage(ICommonSession player, string message, OOCChatType type)
{
if (!HandleRateLimit(player))
return;

// Check if message exceeds the character limit
if (message.Length > MaxMessageLength)
{
Expand Down
8 changes: 8 additions & 0 deletions Content.Server/Chat/Managers/IChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,13 @@ void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessag

[return: NotNullIfNotNull(nameof(author))]
ChatUser? EnsurePlayer(NetUserId? author);

/// <summary>
/// Called when a player sends a chat message to handle rate limits.
/// Will update counts and do necessary actions if breached.
/// </summary>
/// <param name="player">The player sending a chat message.</param>
/// <returns>False if the player has violated rate limits and should be blocked from sending further messages.</returns>
bool HandleRateLimit(ICommonSession player);
}
}
6 changes: 6 additions & 0 deletions Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ public void TrySendInGameICMessage(
return;
}

if (player != null && !_chatManager.HandleRateLimit(player))
return;

// Sus
if (player?.AttachedEntity is { Valid: true } entity && source != entity)
{
Expand Down Expand Up @@ -277,6 +280,9 @@ public void TrySendInGameOOCMessage(
if (!CanSendInGame(message, shell, player))
return;

if (player != null && !_chatManager.HandleRateLimit(player))
return;

// It doesn't make any sense for a non-player to send in-game OOC messages, whereas non-players may be sending
// in-game IC messages.
if (player?.AttachedEntity is not { Valid: true } entity || source != entity)
Expand Down
14 changes: 1 addition & 13 deletions Content.Server/Corvax/GuideGenerator/EntityEntry.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Linq;
using System.Text.Json.Serialization;
using Robust.Shared.Prototypes;

Expand All @@ -18,18 +17,7 @@ public sealed class EntityEntry
public EntityEntry(EntityPrototype proto)
{
Id = proto.ID;
if (proto.Name.Length > 1)
{
Name = char.ToUpper(proto.Name[0]) + proto.Name.Remove(0, 1);
}
else if (proto.Name.Length == 1)
{
Name = char.ToUpper(proto.Name[0]).ToString(); // xD
}
else
{
Name = proto.Name;
}
Name = TextTools.TextTools.CapitalizeString(proto.Name); // Corvax-Wiki
Description = proto.Description;
}
}
5 changes: 0 additions & 5 deletions Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;

namespace Content.Server.GuideGenerator;
Expand Down
72 changes: 72 additions & 0 deletions Content.Server/Corvax/GuideGenerator/GrindRecipeEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Text.Json.Serialization;
using Robust.Shared.Prototypes;
using Content.Shared.Chemistry.Reagent;
using Content.Server.Kitchen.Components;
using Content.Shared.Chemistry.Components.SolutionManager;

namespace Content.Server.GuideGenerator;

public sealed class GrindRecipeEntry
{
/// <summary>
/// Id of grindable item
/// </summary>
[JsonPropertyName("id")]
public string Id { get; }

/// <summary>
/// Human-readable name of recipe.
/// Should automatically be localized by default
/// </summary>
[JsonPropertyName("name")]
public string Name { get; }

/// <summary>
/// Type of recipe
/// </summary>
[JsonPropertyName("type")]
public string Type { get; }

/// <summary>
/// Item that will be grinded into something
/// </summary>
[JsonPropertyName("input")]
public string Input { get; }

/// <summary>
/// Dictionary of reagents that entity contains; aka "Recipe Result"
/// </summary>
[JsonPropertyName("result")]
public Dictionary<string, int> Result { get; } = new Dictionary<string, int>();


public GrindRecipeEntry(EntityPrototype proto)
{
Id = proto.ID;
Name = TextTools.TextTools.CapitalizeString(proto.Name);
Type = "grindableRecipes";
Input = proto.ID;
var foodSulitonName = "food"; // default to food because everything in prototypes defaults to "food"

// Now, to become a recipe, entity must:
// A) Have "Extractable" component on it.
// B) Have "SolutionContainerManager" component on it.
// C) Have "GrindableSolution" declared in "SolutionContainerManager" component.
// D) Have solution with name declared in "SolutionContainerManager.GrindableSolution" inside its "SolutionContainerManager" component.
// F) Have "Food" in its name (see Content.Server/Corvax/GuideGenerator/MealsRecipesJsonGenerator.cs)
if (proto.Components.TryGetComponent("Extractable", out var extractableComp) && proto.Components.TryGetComponent("SolutionContainerManager", out var solutionCompRaw))
{
var extractable = (ExtractableComponent) extractableComp;
var solutionComp = (SolutionContainerManagerComponent) solutionCompRaw;
foodSulitonName = extractable.GrindableSolution;

if (foodSulitonName != null && solutionComp.Solutions.ContainsKey(foodSulitonName))
{
foreach (ReagentQuantity reagent in solutionComp.Solutions[(string) foodSulitonName].Contents)
{
Result[reagent.Reagent.Prototype] = reagent.Quantity.Int();
}
}
}
}
}
Loading

0 comments on commit dd33fa9

Please sign in to comment.