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();
|
public HashSet<string> SyncWithTags { get; private set; } = new();
|
||||||
|
|
||||||
[DataField("monitorAlertTypes")]
|
[DataField("monitorAlertTypes")]
|
||||||
public HashSet<AtmosMonitorThresholdType>? MonitorAlertTypes { get; private set; }
|
public AtmosMonitorThresholdTypeFlags MonitorAlertTypes { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If this device should receive only. If it can only
|
/// 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;
|
public AtmosAlarmType LastAlarmState = AtmosAlarmType.Normal;
|
||||||
|
|
||||||
[DataField("trippedThresholds")]
|
[DataField("trippedThresholds")]
|
||||||
public HashSet<AtmosMonitorThresholdType> TrippedThresholds = new();
|
public AtmosMonitorThresholdTypeFlags TrippedThresholds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registered devices in this atmos monitor. Alerts will be sent directly
|
/// Registered devices in this atmos monitor. Alerts will be sent directly
|
||||||
|
|||||||
@@ -108,9 +108,9 @@ public sealed class AtmosAlarmableSystem : EntitySystem
|
|||||||
break;
|
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))
|
if (!component.NetworkAlarmStates.ContainsKey(args.SenderAddress))
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
if (component.MonitorFire
|
if (component.MonitorFire
|
||||||
&& component.LastAlarmState != AtmosAlarmType.Danger)
|
&& component.LastAlarmState != AtmosAlarmType.Danger)
|
||||||
{
|
{
|
||||||
component.TrippedThresholds.Add(AtmosMonitorThresholdType.Temperature);
|
component.TrippedThresholds |= AtmosMonitorThresholdTypeFlags.Temperature;
|
||||||
Alert(uid, AtmosAlarmType.Danger, null, component); // technically???
|
Alert(uid, AtmosAlarmType.Danger, null, component); // technically???
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
&& component.TemperatureThreshold.CheckThreshold(args.Temperature, out var temperatureState)
|
&& component.TemperatureThreshold.CheckThreshold(args.Temperature, out var temperatureState)
|
||||||
&& temperatureState > component.LastAlarmState)
|
&& temperatureState > component.LastAlarmState)
|
||||||
{
|
{
|
||||||
component.TrippedThresholds.Add(AtmosMonitorThresholdType.Temperature);
|
component.TrippedThresholds |= AtmosMonitorThresholdTypeFlags.Temperature;
|
||||||
Alert(uid, AtmosAlarmType.Danger, null, component);
|
Alert(uid, AtmosAlarmType.Danger, null, component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,7 +259,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref monitor)) return;
|
if (!Resolve(uid, ref monitor)) return;
|
||||||
|
|
||||||
var state = AtmosAlarmType.Normal;
|
var state = AtmosAlarmType.Normal;
|
||||||
HashSet<AtmosMonitorThresholdType> alarmTypes = new(monitor.TrippedThresholds);
|
var alarmTypes = monitor.TrippedThresholds;
|
||||||
|
|
||||||
if (monitor.TemperatureThreshold != null
|
if (monitor.TemperatureThreshold != null
|
||||||
&& monitor.TemperatureThreshold.CheckThreshold(air.Temperature, out var temperatureState))
|
&& monitor.TemperatureThreshold.CheckThreshold(air.Temperature, out var temperatureState))
|
||||||
@@ -267,11 +267,11 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
if (temperatureState > state)
|
if (temperatureState > state)
|
||||||
{
|
{
|
||||||
state = temperatureState;
|
state = temperatureState;
|
||||||
alarmTypes.Add(AtmosMonitorThresholdType.Temperature);
|
alarmTypes |= AtmosMonitorThresholdTypeFlags.Temperature;
|
||||||
}
|
}
|
||||||
else if (temperatureState == AtmosAlarmType.Normal)
|
else if (temperatureState == AtmosAlarmType.Normal)
|
||||||
{
|
{
|
||||||
alarmTypes.Remove(AtmosMonitorThresholdType.Temperature);
|
alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Temperature;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,11 +282,11 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
if (pressureState > state)
|
if (pressureState > state)
|
||||||
{
|
{
|
||||||
state = pressureState;
|
state = pressureState;
|
||||||
alarmTypes.Add(AtmosMonitorThresholdType.Pressure);
|
alarmTypes |= AtmosMonitorThresholdTypeFlags.Pressure;
|
||||||
}
|
}
|
||||||
else if (pressureState == AtmosAlarmType.Normal)
|
else if (pressureState == AtmosAlarmType.Normal)
|
||||||
{
|
{
|
||||||
alarmTypes.Remove(AtmosMonitorThresholdType.Pressure);
|
alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Pressure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,17 +306,17 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
|
|
||||||
if (tripped)
|
if (tripped)
|
||||||
{
|
{
|
||||||
alarmTypes.Add(AtmosMonitorThresholdType.Gas);
|
alarmTypes |= AtmosMonitorThresholdTypeFlags.Gas;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
alarmTypes.Remove(AtmosMonitorThresholdType.Gas);
|
alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Gas;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the state of the current air doesn't match the last alarm state,
|
// if the state of the current air doesn't match the last alarm state,
|
||||||
// we update the 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);
|
Alert(uid, state, alarmTypes, monitor);
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The alarm state to set this monitor to.</param>
|
/// <param name="state">The alarm state to set this monitor to.</param>
|
||||||
/// <param name="alarms">The alarms that caused this alarm state.</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))
|
if (!Resolve(uid, ref monitor))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,86 +12,86 @@ namespace Content.Server.Chat.Managers;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ChatSanitizationManager : IChatSanitizationManager
|
public sealed class ChatSanitizationManager : IChatSanitizationManager
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, string> ShorthandToEmote = new()
|
private static readonly (Regex regex, string emoteKey)[] ShorthandToEmote =
|
||||||
{
|
[
|
||||||
{ ":)", "chatsan-smiles" },
|
Entry(":)", "chatsan-smiles"),
|
||||||
{ ":]", "chatsan-smiles" },
|
Entry(":]", "chatsan-smiles"),
|
||||||
{ "=)", "chatsan-smiles" },
|
Entry("=)", "chatsan-smiles"),
|
||||||
{ "=]", "chatsan-smiles" },
|
Entry("=]", "chatsan-smiles"),
|
||||||
{ "(:", "chatsan-smiles" },
|
Entry("(:", "chatsan-smiles"),
|
||||||
{ "[:", "chatsan-smiles" },
|
Entry("[:", "chatsan-smiles"),
|
||||||
{ "(=", "chatsan-smiles" },
|
Entry("(=", "chatsan-smiles"),
|
||||||
{ "[=", "chatsan-smiles" },
|
Entry("[=", "chatsan-smiles"),
|
||||||
{ "^^", "chatsan-smiles" },
|
Entry("^^", "chatsan-smiles"),
|
||||||
{ "^-^", "chatsan-smiles" },
|
Entry("^-^", "chatsan-smiles"),
|
||||||
{ ":(", "chatsan-frowns" },
|
Entry(":(", "chatsan-frowns"),
|
||||||
{ ":[", "chatsan-frowns" },
|
Entry(":[", "chatsan-frowns"),
|
||||||
{ "=(", "chatsan-frowns" },
|
Entry("=(", "chatsan-frowns"),
|
||||||
{ "=[", "chatsan-frowns" },
|
Entry("=[", "chatsan-frowns"),
|
||||||
{ "):", "chatsan-frowns" },
|
Entry("):", "chatsan-frowns"),
|
||||||
{ ")=", "chatsan-frowns" },
|
Entry(")=", "chatsan-frowns"),
|
||||||
{ "]:", "chatsan-frowns" },
|
Entry("]:", "chatsan-frowns"),
|
||||||
{ "]=", "chatsan-frowns" },
|
Entry("]=", "chatsan-frowns"),
|
||||||
{ ":D", "chatsan-smiles-widely" },
|
Entry(":D", "chatsan-smiles-widely"),
|
||||||
{ "D:", "chatsan-frowns-deeply" },
|
Entry("D:", "chatsan-frowns-deeply"),
|
||||||
{ ":O", "chatsan-surprised" },
|
Entry(":O", "chatsan-surprised"),
|
||||||
{ ":3", "chatsan-smiles" },
|
Entry(":3", "chatsan-smiles"),
|
||||||
{ ":S", "chatsan-uncertain" },
|
Entry(":S", "chatsan-uncertain"),
|
||||||
{ ":>", "chatsan-grins" },
|
Entry(":>", "chatsan-grins"),
|
||||||
{ ":<", "chatsan-pouts" },
|
Entry(":<", "chatsan-pouts"),
|
||||||
{ "xD", "chatsan-laughs" },
|
Entry("xD", "chatsan-laughs"),
|
||||||
{ ":'(", "chatsan-cries" },
|
Entry(":'(", "chatsan-cries"),
|
||||||
{ ":'[", "chatsan-cries" },
|
Entry(":'[", "chatsan-cries"),
|
||||||
{ "='(", "chatsan-cries" },
|
Entry("='(", "chatsan-cries"),
|
||||||
{ "='[", "chatsan-cries" },
|
Entry("='[", "chatsan-cries"),
|
||||||
{ ")':", "chatsan-cries" },
|
Entry(")':", "chatsan-cries"),
|
||||||
{ "]':", "chatsan-cries" },
|
Entry("]':", "chatsan-cries"),
|
||||||
{ ")'=", "chatsan-cries" },
|
Entry(")'=", "chatsan-cries"),
|
||||||
{ "]'=", "chatsan-cries" },
|
Entry("]'=", "chatsan-cries"),
|
||||||
{ ";-;", "chatsan-cries" },
|
Entry(";-;", "chatsan-cries"),
|
||||||
{ ";_;", "chatsan-cries" },
|
Entry(";_;", "chatsan-cries"),
|
||||||
{ "qwq", "chatsan-cries" },
|
Entry("qwq", "chatsan-cries"),
|
||||||
{ ":u", "chatsan-smiles-smugly" },
|
Entry(":u", "chatsan-smiles-smugly"),
|
||||||
{ ":v", "chatsan-smiles-smugly" },
|
Entry(":v", "chatsan-smiles-smugly"),
|
||||||
{ ">:i", "chatsan-annoyed" },
|
Entry(">:i", "chatsan-annoyed"),
|
||||||
{ ":i", "chatsan-sighs" },
|
Entry(":i", "chatsan-sighs"),
|
||||||
{ ":|", "chatsan-sighs" },
|
Entry(":|", "chatsan-sighs"),
|
||||||
{ ":p", "chatsan-stick-out-tongue" },
|
Entry(":p", "chatsan-stick-out-tongue"),
|
||||||
{ ";p", "chatsan-stick-out-tongue" },
|
Entry(";p", "chatsan-stick-out-tongue"),
|
||||||
{ ":b", "chatsan-stick-out-tongue" },
|
Entry(":b", "chatsan-stick-out-tongue"),
|
||||||
{ "0-0", "chatsan-wide-eyed" },
|
Entry("0-0", "chatsan-wide-eyed"),
|
||||||
{ "o-o", "chatsan-wide-eyed" },
|
Entry("o-o", "chatsan-wide-eyed"),
|
||||||
{ "o.o", "chatsan-wide-eyed" },
|
Entry("o.o", "chatsan-wide-eyed"),
|
||||||
{ "._.", "chatsan-surprised" },
|
Entry("._.", "chatsan-surprised"),
|
||||||
{ ".-.", "chatsan-confused" },
|
Entry(".-.", "chatsan-confused"),
|
||||||
{ "-_-", "chatsan-unimpressed" },
|
Entry("-_-", "chatsan-unimpressed"),
|
||||||
{ "smh", "chatsan-unimpressed" },
|
Entry("smh", "chatsan-unimpressed"),
|
||||||
{ "o/", "chatsan-waves" },
|
Entry("o/", "chatsan-waves"),
|
||||||
{ "^^/", "chatsan-waves" },
|
Entry("^^/", "chatsan-waves"),
|
||||||
{ ":/", "chatsan-uncertain" },
|
Entry(":/", "chatsan-uncertain"),
|
||||||
{ ":\\", "chatsan-uncertain" },
|
Entry(":\\", "chatsan-uncertain"),
|
||||||
{ "lmao", "chatsan-laughs" },
|
Entry("lmao", "chatsan-laughs"),
|
||||||
{ "lmfao", "chatsan-laughs" },
|
Entry("lmfao", "chatsan-laughs"),
|
||||||
{ "lol", "chatsan-laughs" },
|
Entry("lol", "chatsan-laughs"),
|
||||||
{ "lel", "chatsan-laughs" },
|
Entry("lel", "chatsan-laughs"),
|
||||||
{ "kek", "chatsan-laughs" },
|
Entry("kek", "chatsan-laughs"),
|
||||||
{ "rofl", "chatsan-laughs" },
|
Entry("rofl", "chatsan-laughs"),
|
||||||
{ "o7", "chatsan-salutes" },
|
Entry("o7", "chatsan-salutes"),
|
||||||
{ ";_;7", "chatsan-tearfully-salutes" },
|
Entry(";_;7", "chatsan-tearfully-salutes"),
|
||||||
{ "idk", "chatsan-shrugs" },
|
Entry("idk", "chatsan-shrugs"),
|
||||||
{ ";)", "chatsan-winks" },
|
Entry(";)", "chatsan-winks"),
|
||||||
{ ";]", "chatsan-winks" },
|
Entry(";]", "chatsan-winks"),
|
||||||
{ "(;", "chatsan-winks" },
|
Entry("(;", "chatsan-winks"),
|
||||||
{ "[;", "chatsan-winks" },
|
Entry("[;", "chatsan-winks"),
|
||||||
{ ":')", "chatsan-tearfully-smiles" },
|
Entry(":')", "chatsan-tearfully-smiles"),
|
||||||
{ ":']", "chatsan-tearfully-smiles" },
|
Entry(":']", "chatsan-tearfully-smiles"),
|
||||||
{ "=')", "chatsan-tearfully-smiles" },
|
Entry("=')", "chatsan-tearfully-smiles"),
|
||||||
{ "=']", "chatsan-tearfully-smiles" },
|
Entry("=']", "chatsan-tearfully-smiles"),
|
||||||
{ "(':", "chatsan-tearfully-smiles" },
|
Entry("(':", "chatsan-tearfully-smiles"),
|
||||||
{ "[':", "chatsan-tearfully-smiles" },
|
Entry("[':", "chatsan-tearfully-smiles"),
|
||||||
{ "('=", "chatsan-tearfully-smiles" },
|
Entry("('=", "chatsan-tearfully-smiles"),
|
||||||
{ "['=", "chatsan-tearfully-smiles" }
|
Entry("['=", "chatsan-tearfully-smiles"),
|
||||||
};
|
];
|
||||||
|
|
||||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||||
[Dependency] private readonly ILocalizationManager _loc = 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
|
// -1 is just a canary for nothing found yet
|
||||||
var lastEmoteIndex = -1;
|
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
|
// We're using sanitized as the original message until the end so that we can make sure the indices of
|
||||||
// the emotes are accurate.
|
// the emotes are accurate.
|
||||||
var lastMatch = r.Match(sanitized);
|
var lastMatch = r.Match(sanitized);
|
||||||
@@ -159,4 +146,21 @@ public sealed class ChatSanitizationManager : IChatSanitizationManager
|
|||||||
sanitized = message.Trim();
|
sanitized = message.Trim();
|
||||||
return emote is not null;
|
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 Robust.Shared.Utility;
|
||||||
using Content.Shared.Prying.Systems;
|
using Content.Shared.Prying.Systems;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
using Prometheus;
|
||||||
|
|
||||||
namespace Content.Server.NPC.Systems;
|
namespace Content.Server.NPC.Systems;
|
||||||
|
|
||||||
public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
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.
|
* 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.
|
* 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 object _obstacles = new();
|
||||||
|
|
||||||
|
private int _activeSteeringCount;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -244,12 +251,15 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
|||||||
};
|
};
|
||||||
var curTime = _timing.CurTime;
|
var curTime = _timing.CurTime;
|
||||||
|
|
||||||
|
_activeSteeringCount = 0;
|
||||||
|
|
||||||
Parallel.For(0, index, options, i =>
|
Parallel.For(0, index, options, i =>
|
||||||
{
|
{
|
||||||
var (uid, steering, mover, xform) = npcs[i];
|
var (uid, steering, mover, xform) = npcs[i];
|
||||||
Steer(uid, steering, mover, xform, frameTime, curTime);
|
Steer(uid, steering, mover, xform, frameTime, curTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ActiveSteeringGauge.Set(_activeSteeringCount);
|
||||||
|
|
||||||
if (_subscribedSessions.Count > 0)
|
if (_subscribedSessions.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -324,6 +334,8 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Interlocked.Increment(ref _activeSteeringCount);
|
||||||
|
|
||||||
var agentRadius = steering.Radius;
|
var agentRadius = steering.Radius;
|
||||||
var worldPos = _transform.GetWorldPosition(xform);
|
var worldPos = _transform.GetWorldPosition(xform);
|
||||||
var (layer, mask) = _physics.GetHardCollision(uid);
|
var (layer, mask) = _physics.GetHardCollision(uid);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Content.Shared.Mobs;
|
|||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.NPC;
|
using Content.Shared.NPC;
|
||||||
using Content.Shared.NPC.Systems;
|
using Content.Shared.NPC.Systems;
|
||||||
|
using Prometheus;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -19,6 +20,10 @@ namespace Content.Server.NPC.Systems
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class NPCSystem : EntitySystem
|
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 IConfigurationManager _configurationManager = default!;
|
||||||
[Dependency] private readonly HTNSystem _htn = default!;
|
[Dependency] private readonly HTNSystem _htn = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
@@ -138,6 +143,8 @@ namespace Content.Server.NPC.Systems
|
|||||||
|
|
||||||
// Add your system here.
|
// Add your system here.
|
||||||
_htn.UpdateNPC(ref _count, _maxUpdates, frameTime);
|
_htn.UpdateNPC(ref _count, _maxUpdates, frameTime);
|
||||||
|
|
||||||
|
ActiveGauge.Set(Count<ActiveNPCComponent>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnMobStateChange(EntityUid uid, HTNComponent component, MobStateChangedEvent args)
|
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.Movement.Systems;
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
using Content.Shared.Shuttles.Systems;
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Prometheus;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using DroneConsoleComponent = Content.Server.Shuttles.DroneConsoleComponent;
|
using DroneConsoleComponent = Content.Server.Shuttles.DroneConsoleComponent;
|
||||||
@@ -17,6 +18,10 @@ namespace Content.Server.Physics.Controllers;
|
|||||||
|
|
||||||
public sealed class MoverController : SharedMoverController
|
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 ThrusterSystem _thruster = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||||
|
|
||||||
@@ -97,6 +102,8 @@ public sealed class MoverController : SharedMoverController
|
|||||||
HandleMobMovement(mover, frameTime);
|
HandleMobMovement(mover, frameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActiveMoverGauge.Set(_movers.Count);
|
||||||
|
|
||||||
HandleShuttleMovement(frameTime);
|
HandleShuttleMovement(frameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,21 @@ namespace Content.Server.Speech.EntitySystems
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly ILocalizationManager _loc = default!;
|
[Dependency] private readonly ILocalizationManager _loc = default!;
|
||||||
|
|
||||||
|
private readonly Dictionary<ProtoId<ReplacementAccentPrototype>, (Regex regex, string replacement)[]>
|
||||||
|
_cachedReplacements = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<ReplacementAccentComponent, AccentGetEvent>(OnAccent);
|
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)
|
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)) : "";
|
return prototype.FullReplacements.Length != 0 ? Loc.GetString(_random.Pick(prototype.FullReplacements)) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prototype.WordReplacements == null)
|
|
||||||
return message;
|
|
||||||
|
|
||||||
// Prohibition of repeated word replacements.
|
// Prohibition of repeated word replacements.
|
||||||
// All replaced words placed in the final message are placed here as dashes (___) with the same length.
|
// 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,
|
// 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.
|
// ensuring that the replaced words cannot be replaced again.
|
||||||
var maskMessage = message;
|
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
|
// this is kind of slow but its not that bad
|
||||||
// essentially: go over all matches, try to match capitalization where possible, then replace
|
// essentially: go over all matches, try to match capitalization where possible, then replace
|
||||||
// rather than using regex.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
|
// fetch the match again as the character indices may have changed
|
||||||
Match match = Regex.Match(maskMessage, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase);
|
Match match = regex.Match(maskMessage);
|
||||||
var replacement = r;
|
var replacement = replace;
|
||||||
|
|
||||||
// Intelligently replace capitalization
|
// Intelligently replace capitalization
|
||||||
// two cases where we will do so:
|
// two cases where we will do so:
|
||||||
@@ -98,5 +105,40 @@ namespace Content.Server.Speech.EntitySystems
|
|||||||
|
|
||||||
return message;
|
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
|
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")),
|
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")),
|
4 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-4")),
|
||||||
5 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-5")),
|
5 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-5")),
|
||||||
6 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-6")),
|
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
|
// fields you can find this prototype in
|
||||||
public enum AtmosMonitorThresholdType
|
public enum AtmosMonitorThresholdType
|
||||||
{
|
{
|
||||||
Temperature,
|
Temperature = 0,
|
||||||
Pressure,
|
Pressure = 1,
|
||||||
Gas
|
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]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -36,5 +36,5 @@ public sealed partial class CCVars : CVars
|
|||||||
/// Set to true to disable parallel processing in the pow3r solver.
|
/// Set to true to disable parallel processing in the pow3r solver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly CVarDef<bool> DebugPow3rDisableParallel =
|
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>
|
/// </summary>
|
||||||
public Dictionary<EntityUid, bool> UsedMobMovement = new();
|
public Dictionary<EntityUid, bool> UsedMobMovement = new();
|
||||||
|
|
||||||
|
private readonly HashSet<EntityUid> _aroundColliderSet = [];
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
UpdatesBefore.Add(typeof(TileFrictionController));
|
UpdatesBefore.Add(typeof(TileFrictionController));
|
||||||
@@ -454,7 +456,9 @@ public abstract partial class SharedMoverController : VirtualController
|
|||||||
var (uid, collider, mover, transform) = entity;
|
var (uid, collider, mover, transform) = entity;
|
||||||
var enlargedAABB = _lookup.GetWorldAABB(entity.Owner, transform).Enlarged(mover.GrabRange);
|
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)
|
if (otherEntity == uid)
|
||||||
continue; // Don't try to push off of yourself!
|
continue; // Don't try to push off of yourself!
|
||||||
|
|||||||
@@ -14,6 +14,3 @@ force_client_hud_version_watermark = true
|
|||||||
|
|
||||||
[chat]
|
[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"
|
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