Optimizations from server profile (#38290)
* Properly cache regexes in chat sanitization/accents Wow I wonder if `new Regex()` has a cost to it *looks at server profile*. * Avoid lag caused by Tippy command completions CompletionHelper.PrototypeIDs explicitly says *not* to use it with EntityPrototype. Unsurprisingly, reporting a completion result for every entity prototype in the game is a *bad idea*. * Add active count metrics to some high-load systems Mover & NPCs I suspect the thing that caused the Leviathan round to shit itself on performance is NPC spam in space or something. So let's verify that. * Enable parallel processing on pow3r again Originally disabled due to a theory of it causing bugs, it was re-enabled on Vulture, and I'm not aware of it having caused any issues there. * Replace hashset with bitflags for AtmosMonitor alert types. Allocating these hashsets was like 20% of the CPU of atmos, somehow. * Cache HashSet used for space movement collider checks Turns out this was a ton of server allocations. Huh.
This commit is contained in:
committed by
GitHub
parent
d0c104e4b0
commit
444180c20d
@@ -48,7 +48,7 @@ public sealed partial class AtmosAlarmableComponent : Component
|
||||
public HashSet<string> SyncWithTags { get; private set; } = new();
|
||||
|
||||
[DataField("monitorAlertTypes")]
|
||||
public HashSet<AtmosMonitorThresholdType>? MonitorAlertTypes { get; private set; }
|
||||
public AtmosMonitorThresholdTypeFlags MonitorAlertTypes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If this device should receive only. If it can only
|
||||
|
||||
@@ -59,7 +59,7 @@ public sealed partial class AtmosMonitorComponent : Component
|
||||
public AtmosAlarmType LastAlarmState = AtmosAlarmType.Normal;
|
||||
|
||||
[DataField("trippedThresholds")]
|
||||
public HashSet<AtmosMonitorThresholdType> TrippedThresholds = new();
|
||||
public AtmosMonitorThresholdTypeFlags TrippedThresholds;
|
||||
|
||||
/// <summary>
|
||||
/// Registered devices in this atmos monitor. Alerts will be sent directly
|
||||
|
||||
@@ -108,9 +108,9 @@ public sealed class AtmosAlarmableSystem : EntitySystem
|
||||
break;
|
||||
}
|
||||
|
||||
if (args.Data.TryGetValue(AlertTypes, out HashSet<AtmosMonitorThresholdType>? types) && component.MonitorAlertTypes != null)
|
||||
if (args.Data.TryGetValue(AlertTypes, out AtmosMonitorThresholdTypeFlags types) && component.MonitorAlertTypes != AtmosMonitorThresholdTypeFlags.None)
|
||||
{
|
||||
isValid = types.Any(type => component.MonitorAlertTypes.Contains(type));
|
||||
isValid = (types & component.MonitorAlertTypes) != 0;
|
||||
}
|
||||
|
||||
if (!component.NetworkAlarmStates.ContainsKey(args.SenderAddress))
|
||||
|
||||
@@ -207,7 +207,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
if (component.MonitorFire
|
||||
&& component.LastAlarmState != AtmosAlarmType.Danger)
|
||||
{
|
||||
component.TrippedThresholds.Add(AtmosMonitorThresholdType.Temperature);
|
||||
component.TrippedThresholds |= AtmosMonitorThresholdTypeFlags.Temperature;
|
||||
Alert(uid, AtmosAlarmType.Danger, null, component); // technically???
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
&& component.TemperatureThreshold.CheckThreshold(args.Temperature, out var temperatureState)
|
||||
&& temperatureState > component.LastAlarmState)
|
||||
{
|
||||
component.TrippedThresholds.Add(AtmosMonitorThresholdType.Temperature);
|
||||
component.TrippedThresholds |= AtmosMonitorThresholdTypeFlags.Temperature;
|
||||
Alert(uid, AtmosAlarmType.Danger, null, component);
|
||||
}
|
||||
}
|
||||
@@ -259,7 +259,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
if (!Resolve(uid, ref monitor)) return;
|
||||
|
||||
var state = AtmosAlarmType.Normal;
|
||||
HashSet<AtmosMonitorThresholdType> alarmTypes = new(monitor.TrippedThresholds);
|
||||
var alarmTypes = monitor.TrippedThresholds;
|
||||
|
||||
if (monitor.TemperatureThreshold != null
|
||||
&& monitor.TemperatureThreshold.CheckThreshold(air.Temperature, out var temperatureState))
|
||||
@@ -267,11 +267,11 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
if (temperatureState > state)
|
||||
{
|
||||
state = temperatureState;
|
||||
alarmTypes.Add(AtmosMonitorThresholdType.Temperature);
|
||||
alarmTypes |= AtmosMonitorThresholdTypeFlags.Temperature;
|
||||
}
|
||||
else if (temperatureState == AtmosAlarmType.Normal)
|
||||
{
|
||||
alarmTypes.Remove(AtmosMonitorThresholdType.Temperature);
|
||||
alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Temperature;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,11 +282,11 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
if (pressureState > state)
|
||||
{
|
||||
state = pressureState;
|
||||
alarmTypes.Add(AtmosMonitorThresholdType.Pressure);
|
||||
alarmTypes |= AtmosMonitorThresholdTypeFlags.Pressure;
|
||||
}
|
||||
else if (pressureState == AtmosAlarmType.Normal)
|
||||
{
|
||||
alarmTypes.Remove(AtmosMonitorThresholdType.Pressure);
|
||||
alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Pressure;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,17 +306,17 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
|
||||
if (tripped)
|
||||
{
|
||||
alarmTypes.Add(AtmosMonitorThresholdType.Gas);
|
||||
alarmTypes |= AtmosMonitorThresholdTypeFlags.Gas;
|
||||
}
|
||||
else
|
||||
{
|
||||
alarmTypes.Remove(AtmosMonitorThresholdType.Gas);
|
||||
alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Gas;
|
||||
}
|
||||
}
|
||||
|
||||
// if the state of the current air doesn't match the last alarm state,
|
||||
// we update the state
|
||||
if (state != monitor.LastAlarmState || !alarmTypes.SetEquals(monitor.TrippedThresholds))
|
||||
if (state != monitor.LastAlarmState || alarmTypes != monitor.TrippedThresholds)
|
||||
{
|
||||
Alert(uid, state, alarmTypes, monitor);
|
||||
}
|
||||
@@ -327,7 +327,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <param name="state">The alarm state to set this monitor to.</param>
|
||||
/// <param name="alarms">The alarms that caused this alarm state.</param>
|
||||
public void Alert(EntityUid uid, AtmosAlarmType state, HashSet<AtmosMonitorThresholdType>? alarms = null, AtmosMonitorComponent? monitor = null)
|
||||
public void Alert(EntityUid uid, AtmosAlarmType state, AtmosMonitorThresholdTypeFlags? alarms = null, AtmosMonitorComponent? monitor = null)
|
||||
{
|
||||
if (!Resolve(uid, ref monitor))
|
||||
return;
|
||||
|
||||
@@ -12,86 +12,86 @@ namespace Content.Server.Chat.Managers;
|
||||
/// </summary>
|
||||
public sealed class ChatSanitizationManager : IChatSanitizationManager
|
||||
{
|
||||
private static readonly Dictionary<string, string> ShorthandToEmote = new()
|
||||
{
|
||||
{ ":)", "chatsan-smiles" },
|
||||
{ ":]", "chatsan-smiles" },
|
||||
{ "=)", "chatsan-smiles" },
|
||||
{ "=]", "chatsan-smiles" },
|
||||
{ "(:", "chatsan-smiles" },
|
||||
{ "[:", "chatsan-smiles" },
|
||||
{ "(=", "chatsan-smiles" },
|
||||
{ "[=", "chatsan-smiles" },
|
||||
{ "^^", "chatsan-smiles" },
|
||||
{ "^-^", "chatsan-smiles" },
|
||||
{ ":(", "chatsan-frowns" },
|
||||
{ ":[", "chatsan-frowns" },
|
||||
{ "=(", "chatsan-frowns" },
|
||||
{ "=[", "chatsan-frowns" },
|
||||
{ "):", "chatsan-frowns" },
|
||||
{ ")=", "chatsan-frowns" },
|
||||
{ "]:", "chatsan-frowns" },
|
||||
{ "]=", "chatsan-frowns" },
|
||||
{ ":D", "chatsan-smiles-widely" },
|
||||
{ "D:", "chatsan-frowns-deeply" },
|
||||
{ ":O", "chatsan-surprised" },
|
||||
{ ":3", "chatsan-smiles" },
|
||||
{ ":S", "chatsan-uncertain" },
|
||||
{ ":>", "chatsan-grins" },
|
||||
{ ":<", "chatsan-pouts" },
|
||||
{ "xD", "chatsan-laughs" },
|
||||
{ ":'(", "chatsan-cries" },
|
||||
{ ":'[", "chatsan-cries" },
|
||||
{ "='(", "chatsan-cries" },
|
||||
{ "='[", "chatsan-cries" },
|
||||
{ ")':", "chatsan-cries" },
|
||||
{ "]':", "chatsan-cries" },
|
||||
{ ")'=", "chatsan-cries" },
|
||||
{ "]'=", "chatsan-cries" },
|
||||
{ ";-;", "chatsan-cries" },
|
||||
{ ";_;", "chatsan-cries" },
|
||||
{ "qwq", "chatsan-cries" },
|
||||
{ ":u", "chatsan-smiles-smugly" },
|
||||
{ ":v", "chatsan-smiles-smugly" },
|
||||
{ ">:i", "chatsan-annoyed" },
|
||||
{ ":i", "chatsan-sighs" },
|
||||
{ ":|", "chatsan-sighs" },
|
||||
{ ":p", "chatsan-stick-out-tongue" },
|
||||
{ ";p", "chatsan-stick-out-tongue" },
|
||||
{ ":b", "chatsan-stick-out-tongue" },
|
||||
{ "0-0", "chatsan-wide-eyed" },
|
||||
{ "o-o", "chatsan-wide-eyed" },
|
||||
{ "o.o", "chatsan-wide-eyed" },
|
||||
{ "._.", "chatsan-surprised" },
|
||||
{ ".-.", "chatsan-confused" },
|
||||
{ "-_-", "chatsan-unimpressed" },
|
||||
{ "smh", "chatsan-unimpressed" },
|
||||
{ "o/", "chatsan-waves" },
|
||||
{ "^^/", "chatsan-waves" },
|
||||
{ ":/", "chatsan-uncertain" },
|
||||
{ ":\\", "chatsan-uncertain" },
|
||||
{ "lmao", "chatsan-laughs" },
|
||||
{ "lmfao", "chatsan-laughs" },
|
||||
{ "lol", "chatsan-laughs" },
|
||||
{ "lel", "chatsan-laughs" },
|
||||
{ "kek", "chatsan-laughs" },
|
||||
{ "rofl", "chatsan-laughs" },
|
||||
{ "o7", "chatsan-salutes" },
|
||||
{ ";_;7", "chatsan-tearfully-salutes" },
|
||||
{ "idk", "chatsan-shrugs" },
|
||||
{ ";)", "chatsan-winks" },
|
||||
{ ";]", "chatsan-winks" },
|
||||
{ "(;", "chatsan-winks" },
|
||||
{ "[;", "chatsan-winks" },
|
||||
{ ":')", "chatsan-tearfully-smiles" },
|
||||
{ ":']", "chatsan-tearfully-smiles" },
|
||||
{ "=')", "chatsan-tearfully-smiles" },
|
||||
{ "=']", "chatsan-tearfully-smiles" },
|
||||
{ "(':", "chatsan-tearfully-smiles" },
|
||||
{ "[':", "chatsan-tearfully-smiles" },
|
||||
{ "('=", "chatsan-tearfully-smiles" },
|
||||
{ "['=", "chatsan-tearfully-smiles" }
|
||||
};
|
||||
private static readonly (Regex regex, string emoteKey)[] ShorthandToEmote =
|
||||
[
|
||||
Entry(":)", "chatsan-smiles"),
|
||||
Entry(":]", "chatsan-smiles"),
|
||||
Entry("=)", "chatsan-smiles"),
|
||||
Entry("=]", "chatsan-smiles"),
|
||||
Entry("(:", "chatsan-smiles"),
|
||||
Entry("[:", "chatsan-smiles"),
|
||||
Entry("(=", "chatsan-smiles"),
|
||||
Entry("[=", "chatsan-smiles"),
|
||||
Entry("^^", "chatsan-smiles"),
|
||||
Entry("^-^", "chatsan-smiles"),
|
||||
Entry(":(", "chatsan-frowns"),
|
||||
Entry(":[", "chatsan-frowns"),
|
||||
Entry("=(", "chatsan-frowns"),
|
||||
Entry("=[", "chatsan-frowns"),
|
||||
Entry("):", "chatsan-frowns"),
|
||||
Entry(")=", "chatsan-frowns"),
|
||||
Entry("]:", "chatsan-frowns"),
|
||||
Entry("]=", "chatsan-frowns"),
|
||||
Entry(":D", "chatsan-smiles-widely"),
|
||||
Entry("D:", "chatsan-frowns-deeply"),
|
||||
Entry(":O", "chatsan-surprised"),
|
||||
Entry(":3", "chatsan-smiles"),
|
||||
Entry(":S", "chatsan-uncertain"),
|
||||
Entry(":>", "chatsan-grins"),
|
||||
Entry(":<", "chatsan-pouts"),
|
||||
Entry("xD", "chatsan-laughs"),
|
||||
Entry(":'(", "chatsan-cries"),
|
||||
Entry(":'[", "chatsan-cries"),
|
||||
Entry("='(", "chatsan-cries"),
|
||||
Entry("='[", "chatsan-cries"),
|
||||
Entry(")':", "chatsan-cries"),
|
||||
Entry("]':", "chatsan-cries"),
|
||||
Entry(")'=", "chatsan-cries"),
|
||||
Entry("]'=", "chatsan-cries"),
|
||||
Entry(";-;", "chatsan-cries"),
|
||||
Entry(";_;", "chatsan-cries"),
|
||||
Entry("qwq", "chatsan-cries"),
|
||||
Entry(":u", "chatsan-smiles-smugly"),
|
||||
Entry(":v", "chatsan-smiles-smugly"),
|
||||
Entry(">:i", "chatsan-annoyed"),
|
||||
Entry(":i", "chatsan-sighs"),
|
||||
Entry(":|", "chatsan-sighs"),
|
||||
Entry(":p", "chatsan-stick-out-tongue"),
|
||||
Entry(";p", "chatsan-stick-out-tongue"),
|
||||
Entry(":b", "chatsan-stick-out-tongue"),
|
||||
Entry("0-0", "chatsan-wide-eyed"),
|
||||
Entry("o-o", "chatsan-wide-eyed"),
|
||||
Entry("o.o", "chatsan-wide-eyed"),
|
||||
Entry("._.", "chatsan-surprised"),
|
||||
Entry(".-.", "chatsan-confused"),
|
||||
Entry("-_-", "chatsan-unimpressed"),
|
||||
Entry("smh", "chatsan-unimpressed"),
|
||||
Entry("o/", "chatsan-waves"),
|
||||
Entry("^^/", "chatsan-waves"),
|
||||
Entry(":/", "chatsan-uncertain"),
|
||||
Entry(":\\", "chatsan-uncertain"),
|
||||
Entry("lmao", "chatsan-laughs"),
|
||||
Entry("lmfao", "chatsan-laughs"),
|
||||
Entry("lol", "chatsan-laughs"),
|
||||
Entry("lel", "chatsan-laughs"),
|
||||
Entry("kek", "chatsan-laughs"),
|
||||
Entry("rofl", "chatsan-laughs"),
|
||||
Entry("o7", "chatsan-salutes"),
|
||||
Entry(";_;7", "chatsan-tearfully-salutes"),
|
||||
Entry("idk", "chatsan-shrugs"),
|
||||
Entry(";)", "chatsan-winks"),
|
||||
Entry(";]", "chatsan-winks"),
|
||||
Entry("(;", "chatsan-winks"),
|
||||
Entry("[;", "chatsan-winks"),
|
||||
Entry(":')", "chatsan-tearfully-smiles"),
|
||||
Entry(":']", "chatsan-tearfully-smiles"),
|
||||
Entry("=')", "chatsan-tearfully-smiles"),
|
||||
Entry("=']", "chatsan-tearfully-smiles"),
|
||||
Entry("(':", "chatsan-tearfully-smiles"),
|
||||
Entry("[':", "chatsan-tearfully-smiles"),
|
||||
Entry("('=", "chatsan-tearfully-smiles"),
|
||||
Entry("['=", "chatsan-tearfully-smiles"),
|
||||
];
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly ILocalizationManager _loc = default!;
|
||||
@@ -125,21 +125,8 @@ public sealed class ChatSanitizationManager : IChatSanitizationManager
|
||||
// -1 is just a canary for nothing found yet
|
||||
var lastEmoteIndex = -1;
|
||||
|
||||
foreach (var (shorthand, emoteKey) in ShorthandToEmote)
|
||||
foreach (var (r, emoteKey) in ShorthandToEmote)
|
||||
{
|
||||
// We have to escape it because shorthands like ":)" or "-_-" would break the regex otherwise.
|
||||
var escaped = Regex.Escape(shorthand);
|
||||
|
||||
// So there are 2 cases:
|
||||
// - If there is whitespace before it and after it is either punctuation, whitespace, or the end of the line
|
||||
// Delete the word and the whitespace before
|
||||
// - If it is at the start of the string and is followed by punctuation, whitespace, or the end of the line
|
||||
// Delete the word and the punctuation if it exists.
|
||||
var pattern =
|
||||
$@"\s{escaped}(?=\p{{P}}|\s|$)|^{escaped}(?:\p{{P}}|(?=\s|$))";
|
||||
|
||||
var r = new Regex(pattern, RegexOptions.RightToLeft | RegexOptions.IgnoreCase);
|
||||
|
||||
// We're using sanitized as the original message until the end so that we can make sure the indices of
|
||||
// the emotes are accurate.
|
||||
var lastMatch = r.Match(sanitized);
|
||||
@@ -159,4 +146,21 @@ public sealed class ChatSanitizationManager : IChatSanitizationManager
|
||||
sanitized = message.Trim();
|
||||
return emote is not null;
|
||||
}
|
||||
|
||||
private static (Regex regex, string emoteKey) Entry(string shorthand, string emoteKey)
|
||||
{
|
||||
// We have to escape it because shorthands like ":)" or "-_-" would break the regex otherwise.
|
||||
var escaped = Regex.Escape(shorthand);
|
||||
|
||||
// So there are 2 cases:
|
||||
// - If there is whitespace before it and after it is either punctuation, whitespace, or the end of the line
|
||||
// Delete the word and the whitespace before
|
||||
// - If it is at the start of the string and is followed by punctuation, whitespace, or the end of the line
|
||||
// Delete the word and the punctuation if it exists.
|
||||
var pattern = new Regex(
|
||||
$@"\s{escaped}(?=\p{{P}}|\s|$)|^{escaped}(?:\p{{P}}|(?=\s|$))",
|
||||
RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
return (pattern, emoteKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,16 @@ using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.Prying.Systems;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Prometheus;
|
||||
|
||||
namespace Content.Server.NPC.Systems;
|
||||
|
||||
public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
{
|
||||
private static readonly Gauge ActiveSteeringGauge = Metrics.CreateGauge(
|
||||
"npc_steering_active_count",
|
||||
"Amount of NPCs trying to actively do steering");
|
||||
|
||||
/*
|
||||
* We use context steering to determine which way to move.
|
||||
* This involves creating an array of possible directions and assigning a value for the desireability of each direction.
|
||||
@@ -87,6 +92,8 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
|
||||
private object _obstacles = new();
|
||||
|
||||
private int _activeSteeringCount;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -244,12 +251,15 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
};
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
_activeSteeringCount = 0;
|
||||
|
||||
Parallel.For(0, index, options, i =>
|
||||
{
|
||||
var (uid, steering, mover, xform) = npcs[i];
|
||||
Steer(uid, steering, mover, xform, frameTime, curTime);
|
||||
});
|
||||
|
||||
ActiveSteeringGauge.Set(_activeSteeringCount);
|
||||
|
||||
if (_subscribedSessions.Count > 0)
|
||||
{
|
||||
@@ -324,6 +334,8 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref _activeSteeringCount);
|
||||
|
||||
var agentRadius = steering.Radius;
|
||||
var worldPos = _transform.GetWorldPosition(xform);
|
||||
var (layer, mask) = _physics.GetHardCollision(uid);
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.NPC;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Prometheus;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
@@ -19,6 +20,10 @@ namespace Content.Server.NPC.Systems
|
||||
/// </summary>
|
||||
public sealed partial class NPCSystem : EntitySystem
|
||||
{
|
||||
private static readonly Gauge ActiveGauge = Metrics.CreateGauge(
|
||||
"npc_active_count",
|
||||
"Amount of NPCs that are actively processing");
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly HTNSystem _htn = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
@@ -138,6 +143,8 @@ namespace Content.Server.NPC.Systems
|
||||
|
||||
// Add your system here.
|
||||
_htn.UpdateNPC(ref _count, _maxUpdates, frameTime);
|
||||
|
||||
ActiveGauge.Set(Count<ActiveNPCComponent>());
|
||||
}
|
||||
|
||||
public void OnMobStateChange(EntityUid uid, HTNComponent component, MobStateChangedEvent args)
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Shuttles.Components;
|
||||
using Content.Shared.Shuttles.Systems;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
using DroneConsoleComponent = Content.Server.Shuttles.DroneConsoleComponent;
|
||||
@@ -17,6 +18,10 @@ namespace Content.Server.Physics.Controllers;
|
||||
|
||||
public sealed class MoverController : SharedMoverController
|
||||
{
|
||||
private static readonly Gauge ActiveMoverGauge = Metrics.CreateGauge(
|
||||
"physics_active_mover_count",
|
||||
"Active amount of InputMovers being processed by MoverController");
|
||||
|
||||
[Dependency] private readonly ThrusterSystem _thruster = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||
|
||||
@@ -97,6 +102,8 @@ public sealed class MoverController : SharedMoverController
|
||||
HandleMobMovement(mover, frameTime);
|
||||
}
|
||||
|
||||
ActiveMoverGauge.Set(_movers.Count);
|
||||
|
||||
HandleShuttleMovement(frameTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,21 @@ namespace Content.Server.Speech.EntitySystems
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ILocalizationManager _loc = default!;
|
||||
|
||||
private readonly Dictionary<ProtoId<ReplacementAccentPrototype>, (Regex regex, string replacement)[]>
|
||||
_cachedReplacements = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<ReplacementAccentComponent, AccentGetEvent>(OnAccent);
|
||||
|
||||
_proto.PrototypesReloaded += OnPrototypesReloaded;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_proto.PrototypesReloaded -= OnPrototypesReloaded;
|
||||
}
|
||||
|
||||
private void OnAccent(EntityUid uid, ReplacementAccentComponent component, AccentGetEvent args)
|
||||
@@ -48,27 +60,22 @@ namespace Content.Server.Speech.EntitySystems
|
||||
return prototype.FullReplacements.Length != 0 ? Loc.GetString(_random.Pick(prototype.FullReplacements)) : "";
|
||||
}
|
||||
|
||||
if (prototype.WordReplacements == null)
|
||||
return message;
|
||||
|
||||
// Prohibition of repeated word replacements.
|
||||
// All replaced words placed in the final message are placed here as dashes (___) with the same length.
|
||||
// The regex search goes through this buffer message, from which the already replaced words are crossed out,
|
||||
// ensuring that the replaced words cannot be replaced again.
|
||||
var maskMessage = message;
|
||||
|
||||
foreach (var (first, replace) in prototype.WordReplacements)
|
||||
foreach (var (regex, replace) in GetCachedReplacements(prototype))
|
||||
{
|
||||
var f = _loc.GetString(first);
|
||||
var r = _loc.GetString(replace);
|
||||
// this is kind of slow but its not that bad
|
||||
// essentially: go over all matches, try to match capitalization where possible, then replace
|
||||
// rather than using regex.replace
|
||||
for (int i = Regex.Count(maskMessage, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase); i > 0; i--)
|
||||
for (int i = regex.Count(maskMessage); i > 0; i--)
|
||||
{
|
||||
// fetch the match again as the character indices may have changed
|
||||
Match match = Regex.Match(maskMessage, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase);
|
||||
var replacement = r;
|
||||
Match match = regex.Match(maskMessage);
|
||||
var replacement = replace;
|
||||
|
||||
// Intelligently replace capitalization
|
||||
// two cases where we will do so:
|
||||
@@ -98,5 +105,40 @@ namespace Content.Server.Speech.EntitySystems
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private (Regex regex, string replacement)[] GetCachedReplacements(ReplacementAccentPrototype prototype)
|
||||
{
|
||||
if (!_cachedReplacements.TryGetValue(prototype.ID, out var replacements))
|
||||
{
|
||||
replacements = GenerateCachedReplacements(prototype);
|
||||
_cachedReplacements.Add(prototype.ID, replacements);
|
||||
}
|
||||
|
||||
return replacements;
|
||||
}
|
||||
|
||||
private (Regex regex, string replacement)[] GenerateCachedReplacements(ReplacementAccentPrototype prototype)
|
||||
{
|
||||
if (prototype.WordReplacements is not { } replacements)
|
||||
return [];
|
||||
|
||||
return replacements.Select(kv =>
|
||||
{
|
||||
var (first, replace) = kv;
|
||||
var firstLoc = _loc.GetString(first);
|
||||
var replaceLoc = _loc.GetString(replace);
|
||||
|
||||
var regex = new Regex($@"(?<!\w){firstLoc}(?!\w)", RegexOptions.IgnoreCase);
|
||||
|
||||
return (regex, replaceLoc);
|
||||
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
_cachedReplacements.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,13 @@ public sealed class TipsSystem : EntitySystem
|
||||
{
|
||||
return args.Length switch
|
||||
{
|
||||
1 => CompletionResult.FromHintOptions(CompletionHelper.SessionNames(), Loc.GetString("cmd-tippy-auto-1")),
|
||||
1 => CompletionResult.FromHintOptions(
|
||||
CompletionHelper.SessionNames(players: _playerManager),
|
||||
Loc.GetString("cmd-tippy-auto-1")),
|
||||
2 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-2")),
|
||||
3 => CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs<EntityPrototype>(), Loc.GetString("cmd-tippy-auto-3")),
|
||||
3 => CompletionResult.FromHintOptions(
|
||||
CompletionHelper.PrototypeIdsLimited<EntityPrototype>(args[2], _prototype),
|
||||
Loc.GetString("cmd-tippy-auto-3")),
|
||||
4 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-4")),
|
||||
5 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-5")),
|
||||
6 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-6")),
|
||||
|
||||
@@ -388,9 +388,21 @@ public enum AtmosMonitorLimitType //<todo.eoin Very similar to the above...
|
||||
// fields you can find this prototype in
|
||||
public enum AtmosMonitorThresholdType
|
||||
{
|
||||
Temperature,
|
||||
Pressure,
|
||||
Gas
|
||||
Temperature = 0,
|
||||
Pressure = 1,
|
||||
Gas = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitflags version of <see cref="AtmosMonitorThresholdType"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AtmosMonitorThresholdTypeFlags
|
||||
{
|
||||
None = 0,
|
||||
Temperature = 1 << 0,
|
||||
Pressure = 1 << 1,
|
||||
Gas = 1 << 2,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -36,5 +36,5 @@ public sealed partial class CCVars : CVars
|
||||
/// Set to true to disable parallel processing in the pow3r solver.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> DebugPow3rDisableParallel =
|
||||
CVarDef.Create("debug.pow3r_disable_parallel", true, CVar.SERVERONLY);
|
||||
CVarDef.Create("debug.pow3r_disable_parallel", false, CVar.SERVERONLY);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ public abstract partial class SharedMoverController : VirtualController
|
||||
/// </summary>
|
||||
public Dictionary<EntityUid, bool> UsedMobMovement = new();
|
||||
|
||||
private readonly HashSet<EntityUid> _aroundColliderSet = [];
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
UpdatesBefore.Add(typeof(TileFrictionController));
|
||||
@@ -454,7 +456,9 @@ public abstract partial class SharedMoverController : VirtualController
|
||||
var (uid, collider, mover, transform) = entity;
|
||||
var enlargedAABB = _lookup.GetWorldAABB(entity.Owner, transform).Enlarged(mover.GrabRange);
|
||||
|
||||
foreach (var otherEntity in lookupSystem.GetEntitiesIntersecting(transform.MapID, enlargedAABB))
|
||||
_aroundColliderSet.Clear();
|
||||
lookupSystem.GetEntitiesIntersecting(transform.MapID, enlargedAABB, _aroundColliderSet);
|
||||
foreach (var otherEntity in _aroundColliderSet)
|
||||
{
|
||||
if (otherEntity == uid)
|
||||
continue; // Don't try to push off of yourself!
|
||||
|
||||
@@ -14,6 +14,3 @@ force_client_hud_version_watermark = true
|
||||
|
||||
[chat]
|
||||
motd = "\n########################################################\n\n[font size=17]This is a test server. You can play with the newest changes to the game, but these [color=red]changes may not be final or stable[/color], and may be reverted. Please report bugs via our GitHub, forum, or community Discord.[/font]\n\n########################################################\n"
|
||||
|
||||
[debug]
|
||||
pow3r_disable_parallel = false
|
||||
|
||||
Reference in New Issue
Block a user