Merge remote-tracking branch 'upstream/master' into ed-20-11-2024-upstream-sync
# Conflicts: # Content.Server/Chemistry/EntitySystems/InjectorSystem.cs # Content.Server/Traits/TraitSystem.cs # Content.Shared/CCVar/CCVars.cs # Content.Shared/Inventory/InventorySystem.Relay.cs # Resources/Maps/box.yml # Resources/Maps/core.yml
This commit is contained in:
@@ -54,7 +54,7 @@ public sealed class BanListEui : BaseEui
|
||||
|
||||
private async Task LoadBans(NetUserId userId)
|
||||
{
|
||||
foreach (var ban in await _db.GetServerBansAsync(null, userId, null))
|
||||
foreach (var ban in await _db.GetServerBansAsync(null, userId, null, null))
|
||||
{
|
||||
SharedServerUnban? unban = null;
|
||||
if (ban.Unban is { } unbanDef)
|
||||
@@ -74,7 +74,7 @@ public sealed class BanListEui : BaseEui
|
||||
? (address.address.ToString(), address.cidrMask)
|
||||
: null;
|
||||
|
||||
hwid = ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan());
|
||||
hwid = ban.HWId?.ToString();
|
||||
}
|
||||
|
||||
Bans.Add(new SharedServerBan(
|
||||
@@ -95,7 +95,7 @@ public sealed class BanListEui : BaseEui
|
||||
|
||||
private async Task LoadRoleBans(NetUserId userId)
|
||||
{
|
||||
foreach (var ban in await _db.GetServerRoleBansAsync(null, userId, null))
|
||||
foreach (var ban in await _db.GetServerRoleBansAsync(null, userId, null, null))
|
||||
{
|
||||
SharedServerUnban? unban = null;
|
||||
if (ban.Unban is { } unbanDef)
|
||||
@@ -115,7 +115,7 @@ public sealed class BanListEui : BaseEui
|
||||
? (address.address.ToString(), address.cidrMask)
|
||||
: null;
|
||||
|
||||
hwid = ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan());
|
||||
hwid = ban.HWId?.ToString();
|
||||
}
|
||||
RoleBans.Add(new SharedServerRoleBan(
|
||||
ban.Id,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Content.Server.Administration.Managers;
|
||||
@@ -8,7 +7,6 @@ using Content.Server.EUI;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Administration;
|
||||
@@ -27,7 +25,7 @@ public sealed class BanPanelEui : BaseEui
|
||||
private NetUserId? PlayerId { get; set; }
|
||||
private string PlayerName { get; set; } = string.Empty;
|
||||
private IPAddress? LastAddress { get; set; }
|
||||
private ImmutableArray<byte>? LastHwid { get; set; }
|
||||
private ImmutableTypedHwid? LastHwid { get; set; }
|
||||
private const int Ipv4_CIDR = 32;
|
||||
private const int Ipv6_CIDR = 64;
|
||||
|
||||
@@ -51,7 +49,7 @@ public sealed class BanPanelEui : BaseEui
|
||||
switch (msg)
|
||||
{
|
||||
case BanPanelEuiStateMsg.CreateBanRequest r:
|
||||
BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid?.ToImmutableArray(), r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles, r.Erase);
|
||||
BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid, r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles, r.Erase);
|
||||
break;
|
||||
case BanPanelEuiStateMsg.GetPlayerInfoRequest r:
|
||||
ChangePlayer(r.PlayerUsername);
|
||||
@@ -59,7 +57,7 @@ public sealed class BanPanelEui : BaseEui
|
||||
}
|
||||
}
|
||||
|
||||
private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableArray<byte>? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection<string>? roles, bool erase)
|
||||
private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableTypedHwid? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection<string>? roles, bool erase)
|
||||
{
|
||||
if (!_admins.HasAdminFlag(Player, AdminFlags.Ban))
|
||||
{
|
||||
@@ -155,7 +153,7 @@ public sealed class BanPanelEui : BaseEui
|
||||
ChangePlayer(located?.UserId, located?.Username ?? string.Empty, located?.LastAddress, located?.LastHWId);
|
||||
}
|
||||
|
||||
public void ChangePlayer(NetUserId? playerId, string playerName, IPAddress? lastAddress, ImmutableArray<byte>? lastHwid)
|
||||
public void ChangePlayer(NetUserId? playerId, string playerName, IPAddress? lastAddress, ImmutableTypedHwid? lastHwid)
|
||||
{
|
||||
PlayerId = playerId;
|
||||
PlayerName = playerName;
|
||||
|
||||
@@ -38,7 +38,7 @@ public sealed class BanListCommand : LocalizedCommands
|
||||
|
||||
if (shell.Player is not { } player)
|
||||
{
|
||||
var bans = await _dbManager.GetServerBansAsync(data.LastAddress, data.UserId, data.LastHWId, false);
|
||||
var bans = await _dbManager.GetServerBansAsync(data.LastAddress, data.UserId, data.LastLegacyHWId, data.LastModernHWIds, false);
|
||||
|
||||
if (bans.Count == 0)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed class RoleBanListCommand : IConsoleCommand
|
||||
if (shell.Player is not { } player)
|
||||
{
|
||||
|
||||
var bans = await _dbManager.GetServerRoleBansAsync(data.LastAddress, data.UserId, data.LastHWId, includeUnbanned);
|
||||
var bans = await _dbManager.GetServerRoleBansAsync(data.LastAddress, data.UserId, data.LastLegacyHWId, data.LastModernHWIds, includeUnbanned);
|
||||
|
||||
if (bans.Count == 0)
|
||||
{
|
||||
|
||||
@@ -65,7 +65,8 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
|
||||
|
||||
var netChannel = player.Channel;
|
||||
ImmutableArray<byte>? hwId = netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId;
|
||||
var roleBans = await _db.GetServerRoleBansAsync(netChannel.RemoteEndPoint.Address, player.UserId, hwId, false);
|
||||
var modernHwids = netChannel.UserData.ModernHWIds;
|
||||
var roleBans = await _db.GetServerRoleBansAsync(netChannel.RemoteEndPoint.Address, player.UserId, hwId, modernHwids, false);
|
||||
|
||||
var userRoleBans = new List<ServerRoleBanDef>();
|
||||
foreach (var ban in roleBans)
|
||||
@@ -132,7 +133,7 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
|
||||
}
|
||||
|
||||
#region Server Bans
|
||||
public async void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason)
|
||||
public async void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, uint? minutes, NoteSeverity severity, string reason)
|
||||
{
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
@@ -166,9 +167,7 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
|
||||
var addressRangeString = addressRange != null
|
||||
? $"{addressRange.Value.Item1}/{addressRange.Value.Item2}"
|
||||
: "null";
|
||||
var hwidString = hwid != null
|
||||
? string.Concat(hwid.Value.Select(x => x.ToString("x2")))
|
||||
: "null";
|
||||
var hwidString = hwid?.ToString() ?? "null";
|
||||
var expiresString = expires == null ? Loc.GetString("server-ban-string-never") : $"{expires}";
|
||||
|
||||
var key = _cfg.GetCVar(CCVars.AdminShowPIIOnBan) ? "server-ban-string" : "server-ban-string-no-pii";
|
||||
@@ -208,6 +207,7 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
|
||||
UserId = player.UserId,
|
||||
Address = player.Channel.RemoteEndPoint.Address,
|
||||
HWId = player.Channel.UserData.HWId,
|
||||
ModernHWIds = player.Channel.UserData.ModernHWIds,
|
||||
// It's possible for the player to not have cached data loading yet due to coincidental timing.
|
||||
// If this is the case, we assume they have all flags to avoid false-positives.
|
||||
ExemptFlags = _cachedBanExemptions.GetValueOrDefault(player, ServerBanExemptFlags.All),
|
||||
@@ -228,7 +228,7 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
|
||||
#region Job Bans
|
||||
// If you are trying to remove timeOfBan, please don't. It's there because the note system groups role bans by time, reason and banning admin.
|
||||
// Removing it will clutter the note list. Please also make sure that department bans are applied to roles with the same DateTimeOffset.
|
||||
public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan)
|
||||
public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(role, out JobPrototype? _))
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ public interface IBanManager
|
||||
/// <param name="minutes">Number of minutes to ban for. 0 and null mean permanent</param>
|
||||
/// <param name="severity">Severity of the resulting ban note</param>
|
||||
/// <param name="reason">Reason for the ban</param>
|
||||
public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, uint? minutes, NoteSeverity severity, string reason);
|
||||
public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, uint? minutes, NoteSeverity severity, string reason);
|
||||
public HashSet<string>? GetRoleBans(NetUserId playerUserId);
|
||||
public HashSet<ProtoId<JobPrototype>>? GetJobBans(NetUserId playerUserId);
|
||||
|
||||
@@ -37,7 +37,7 @@ public interface IBanManager
|
||||
/// <param name="reason">Reason for the ban</param>
|
||||
/// <param name="minutes">Number of minutes to ban for. 0 and null mean permanent</param>
|
||||
/// <param name="timeOfBan">Time when the ban was applied, used for grouping role bans</param>
|
||||
public void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan);
|
||||
public void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan);
|
||||
|
||||
/// <summary>
|
||||
/// Pardons a role ban for the specified target, username or GUID
|
||||
|
||||
@@ -5,16 +5,42 @@ using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Connection;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.Database;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Administration
|
||||
{
|
||||
public sealed record LocatedPlayerData(NetUserId UserId, IPAddress? LastAddress, ImmutableArray<byte>? LastHWId, string Username);
|
||||
/// <summary>
|
||||
/// Contains data resolved via <see cref="IPlayerLocator"/>.
|
||||
/// </summary>
|
||||
/// <param name="UserId">The ID of the located user.</param>
|
||||
/// <param name="LastAddress">The last known IP address that the user connected with.</param>
|
||||
/// <param name="LastHWId">
|
||||
/// The last known HWID that the user connected with.
|
||||
/// This should be used for placing new records involving HWIDs, such as bans.
|
||||
/// For looking up data based on HWID, use combined <see cref="LastLegacyHWId"/> and <see cref="LastModernHWIds"/>.
|
||||
/// </param>
|
||||
/// <param name="Username">The last known username for the user connected with.</param>
|
||||
/// <param name="LastLegacyHWId">
|
||||
/// The last known legacy HWID value this user connected with. Only use for old lookups!
|
||||
/// </param>
|
||||
/// <param name="LastModernHWIds">
|
||||
/// The set of last known modern HWIDs the user connected with.
|
||||
/// </param>
|
||||
public sealed record LocatedPlayerData(
|
||||
NetUserId UserId,
|
||||
IPAddress? LastAddress,
|
||||
ImmutableTypedHwid? LastHWId,
|
||||
string Username,
|
||||
ImmutableArray<byte>? LastLegacyHWId,
|
||||
ImmutableArray<ImmutableArray<byte>> LastModernHWIds);
|
||||
|
||||
/// <summary>
|
||||
/// Utilities for finding user IDs that extend to more than the server database.
|
||||
@@ -67,63 +93,42 @@ namespace Content.Server.Administration
|
||||
{
|
||||
// Check people currently on the server, the easiest case.
|
||||
if (_playerManager.TryGetSessionByUsername(playerName, out var session))
|
||||
{
|
||||
var userId = session.UserId;
|
||||
var address = session.Channel.RemoteEndPoint.Address;
|
||||
var hwId = session.Channel.UserData.HWId;
|
||||
return new LocatedPlayerData(userId, address, hwId, session.Name);
|
||||
}
|
||||
return ReturnForSession(session);
|
||||
|
||||
// Check database for past players.
|
||||
var record = await _db.GetPlayerRecordByUserName(playerName, cancel);
|
||||
if (record != null)
|
||||
return new LocatedPlayerData(record.UserId, record.LastSeenAddress, record.HWId, record.LastSeenUserName);
|
||||
return ReturnForPlayerRecord(record);
|
||||
|
||||
// If all else fails, ask the auth server.
|
||||
var authServer = _configurationManager.GetCVar(CVars.AuthServer);
|
||||
var requestUri = $"{authServer}api/query/name?name={WebUtility.UrlEncode(playerName)}";
|
||||
using var resp = await _httpClient.GetAsync(requestUri, cancel);
|
||||
|
||||
if (resp.StatusCode == HttpStatusCode.NotFound)
|
||||
return null;
|
||||
|
||||
if (!resp.IsSuccessStatusCode)
|
||||
{
|
||||
_sawmill.Error("Auth server returned bad response {StatusCode}!", resp.StatusCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
var responseData = await resp.Content.ReadFromJsonAsync<UserDataResponse>(cancellationToken: cancel);
|
||||
|
||||
if (responseData == null)
|
||||
{
|
||||
_sawmill.Error("Auth server returned null response!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LocatedPlayerData(new NetUserId(responseData.UserId), null, null, responseData.UserName);
|
||||
return await HandleAuthServerResponse(resp, cancel);
|
||||
}
|
||||
|
||||
public async Task<LocatedPlayerData?> LookupIdAsync(NetUserId userId, CancellationToken cancel = default)
|
||||
{
|
||||
// Check people currently on the server, the easiest case.
|
||||
if (_playerManager.TryGetSessionById(userId, out var session))
|
||||
{
|
||||
var address = session.Channel.RemoteEndPoint.Address;
|
||||
var hwId = session.Channel.UserData.HWId;
|
||||
return new LocatedPlayerData(userId, address, hwId, session.Name);
|
||||
}
|
||||
return ReturnForSession(session);
|
||||
|
||||
// Check database for past players.
|
||||
var record = await _db.GetPlayerRecordByUserId(userId, cancel);
|
||||
if (record != null)
|
||||
return new LocatedPlayerData(record.UserId, record.LastSeenAddress, record.HWId, record.LastSeenUserName);
|
||||
return ReturnForPlayerRecord(record);
|
||||
|
||||
// If all else fails, ask the auth server.
|
||||
var authServer = _configurationManager.GetCVar(CVars.AuthServer);
|
||||
var requestUri = $"{authServer}api/query/userid?userid={WebUtility.UrlEncode(userId.UserId.ToString())}";
|
||||
using var resp = await _httpClient.GetAsync(requestUri, cancel);
|
||||
|
||||
return await HandleAuthServerResponse(resp, cancel);
|
||||
}
|
||||
|
||||
private async Task<LocatedPlayerData?> HandleAuthServerResponse(HttpResponseMessage resp, CancellationToken cancel)
|
||||
{
|
||||
if (resp.StatusCode == HttpStatusCode.NotFound)
|
||||
return null;
|
||||
|
||||
@@ -134,14 +139,40 @@ namespace Content.Server.Administration
|
||||
}
|
||||
|
||||
var responseData = await resp.Content.ReadFromJsonAsync<UserDataResponse>(cancellationToken: cancel);
|
||||
|
||||
if (responseData == null)
|
||||
{
|
||||
_sawmill.Error("Auth server returned null response!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LocatedPlayerData(new NetUserId(responseData.UserId), null, null, responseData.UserName);
|
||||
return new LocatedPlayerData(new NetUserId(responseData.UserId), null, null, responseData.UserName, null, []);
|
||||
}
|
||||
|
||||
private static LocatedPlayerData ReturnForSession(ICommonSession session)
|
||||
{
|
||||
var userId = session.UserId;
|
||||
var address = session.Channel.RemoteEndPoint.Address;
|
||||
var hwId = session.Channel.UserData.GetModernHwid();
|
||||
return new LocatedPlayerData(
|
||||
userId,
|
||||
address,
|
||||
hwId,
|
||||
session.Name,
|
||||
session.Channel.UserData.HWId,
|
||||
session.Channel.UserData.ModernHWIds);
|
||||
}
|
||||
|
||||
private static LocatedPlayerData ReturnForPlayerRecord(PlayerRecord record)
|
||||
{
|
||||
var hwid = record.HWId;
|
||||
|
||||
return new LocatedPlayerData(
|
||||
record.UserId,
|
||||
record.LastSeenAddress,
|
||||
hwid,
|
||||
record.LastSeenUserName,
|
||||
hwid is { Type: HwidType.Legacy } ? hwid.Hwid : null,
|
||||
hwid is { Type: HwidType.Modern } ? [hwid.Hwid] : []);
|
||||
}
|
||||
|
||||
public async Task<LocatedPlayerData?> LookupIdByNameOrIdAsync(string playerName, CancellationToken cancel = default)
|
||||
|
||||
@@ -173,11 +173,11 @@ public sealed class PlayerPanelEui : BaseEui
|
||||
{
|
||||
_whitelisted = await _db.GetWhitelistStatusAsync(_targetPlayer.UserId);
|
||||
// This won't get associated ip or hwid bans but they were not placed on this account anyways
|
||||
_bans = (await _db.GetServerBansAsync(null, _targetPlayer.UserId, null)).Count;
|
||||
_bans = (await _db.GetServerBansAsync(null, _targetPlayer.UserId, null, null)).Count;
|
||||
// Unfortunately role bans for departments and stuff are issued individually. This means that a single role ban can have many individual role bans internally
|
||||
// The only way to distinguish whether a role ban is the same is to compare the ban time.
|
||||
// This is horrible and I would love to just erase the database and start from scratch instead but that's what I can do for now.
|
||||
_roleBans = (await _db.GetServerRoleBansAsync(null, _targetPlayer.UserId, null)).DistinctBy(rb => rb.BanTime).Count();
|
||||
_roleBans = (await _db.GetServerRoleBansAsync(null, _targetPlayer.UserId, null, null)).DistinctBy(rb => rb.BanTime).Count();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Clumsy;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Cluwne;
|
||||
using Content.Shared.Damage;
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Content.Server.Administration.Systems
|
||||
}
|
||||
|
||||
// Check if the user has been banned
|
||||
var ban = await _dbManager.GetServerBanAsync(null, e.Session.UserId, null);
|
||||
var ban = await _dbManager.GetServerBanAsync(null, e.Session.UserId, null, null);
|
||||
if (ban != null)
|
||||
{
|
||||
var banMessage = Loc.GetString("bwoink-system-player-banned", ("banReason", ban.Reason));
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
using Content.Server.Administration.Components;
|
||||
using Content.Shared.Climbing.Components;
|
||||
using Content.Shared.Climbing.Events;
|
||||
using Content.Shared.Climbing.Systems;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Clumsy;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
public sealed class SuperBonkSystem: EntitySystem
|
||||
public sealed class SuperBonkSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly BonkSystem _bonkSystem = default!;
|
||||
[Dependency] private readonly ClumsySystem _clumsySystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SuperBonkComponent, ComponentShutdown>(OnBonkShutdown);
|
||||
SubscribeLocalEvent<SuperBonkComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<SuperBonkComponent, ComponentShutdown>(OnBonkShutdown);
|
||||
}
|
||||
|
||||
public void StartSuperBonk(EntityUid target, float delay = 0.1f, bool stopWhenDead = false )
|
||||
public void StartSuperBonk(EntityUid target, float delay = 0.1f, bool stopWhenDead = false)
|
||||
{
|
||||
|
||||
//The other check in the code to stop when the target dies does not work if the target is already dead.
|
||||
@@ -31,7 +31,6 @@ public sealed class SuperBonkSystem: EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var hadClumsy = EnsureComp<ClumsyComponent>(target, out _);
|
||||
|
||||
var tables = EntityQueryEnumerator<BonkableComponent>();
|
||||
@@ -79,16 +78,17 @@ public sealed class SuperBonkSystem: EntitySystem
|
||||
private void Bonk(SuperBonkComponent comp)
|
||||
{
|
||||
var uid = comp.Tables.Current.Key;
|
||||
var bonkComp = comp.Tables.Current.Value;
|
||||
|
||||
// It would be very weird for something without a transform component to have a bonk component
|
||||
// but just in case because I don't want to crash the server.
|
||||
if (!HasComp<TransformComponent>(uid))
|
||||
if (!HasComp<TransformComponent>(uid) || !TryComp<ClumsyComponent>(comp.Target, out var clumsyComp))
|
||||
return;
|
||||
|
||||
_transformSystem.SetCoordinates(comp.Target, Transform(uid).Coordinates);
|
||||
|
||||
_bonkSystem.TryBonk(comp.Target, uid, bonkComp);
|
||||
_clumsySystem.HitHeadClumsy((comp.Target, clumsyComp), uid);
|
||||
|
||||
_audioSystem.PlayPvs(clumsyComp.TableBonkSound, comp.Target);
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(EntityUid uid, SuperBonkComponent comp, MobStateChangedEvent args)
|
||||
|
||||
@@ -25,6 +25,16 @@ public sealed class TagCommand : ToolshedCommand
|
||||
});
|
||||
}
|
||||
|
||||
[CommandImplementation("with")]
|
||||
public IEnumerable<EntityUid> With(
|
||||
[CommandInvocationContext] IInvocationContext ctx,
|
||||
[PipedArgument] IEnumerable<EntityUid> entities,
|
||||
[CommandArgument] ValueRef<string, Prototype<TagPrototype>> tag)
|
||||
{
|
||||
_tag ??= GetSys<TagSystem>();
|
||||
return entities.Where(e => _tag.HasTag(e, tag.Evaluate(ctx)!));
|
||||
}
|
||||
|
||||
[CommandImplementation("add")]
|
||||
public EntityUid Add(
|
||||
[CommandInvocationContext] IInvocationContext ctx,
|
||||
|
||||
Reference in New Issue
Block a user