Merge remote-tracking branch 'refs/remotes/wizards/master'
# Conflicts: # Content.Client/Info/LinkBanner.cs # Resources/Locale/en-US/_strings/atmos/gas-pipe-sensor.ftl # Resources/Locale/en-US/_strings/station-events/events/greytide-virus.ftl # Resources/Prototypes/Datasets/Names/diona.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
This commit is contained in:
commit
0c1e15e516
165 changed files with 15734 additions and 1263 deletions
|
|
@ -22,11 +22,11 @@ namespace Content.Client.Administration.UI.BanPanel;
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string?, (IPAddress, int)?, bool, ImmutableTypedHwid?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string>? PlayerChanged;
|
||||
private string? PlayerUsername { get; set; }
|
||||
private (IPAddress, int)? IpAddress { get; set; }
|
||||
private byte[]? Hwid { get; set; }
|
||||
private ImmutableTypedHwid? Hwid { get; set; }
|
||||
private double TimeEntered { get; set; }
|
||||
private uint Multiplier { get; set; }
|
||||
private bool HasBanFlag { get; set; }
|
||||
|
|
@ -371,9 +371,8 @@ public sealed partial class BanPanel : DefaultWindow
|
|||
private void OnHwidChanged()
|
||||
{
|
||||
var hwidString = HwidLine.Text;
|
||||
var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '=');
|
||||
Hwid = new byte[length];
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _))
|
||||
ImmutableTypedHwid? hwid = null;
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid))
|
||||
{
|
||||
ErrorLevel |= ErrorLevelEnum.Hwid;
|
||||
HwidLine.ModulateSelfOverride = Color.Red;
|
||||
|
|
@ -390,7 +389,7 @@ public sealed partial class BanPanel : DefaultWindow
|
|||
Hwid = null;
|
||||
return;
|
||||
}
|
||||
Hwid = Convert.FromHexString(hwidString);
|
||||
Hwid = hwid;
|
||||
}
|
||||
|
||||
private void OnTypeChanged()
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ public sealed partial class NoteEdit : FancyWindow
|
|||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = false;
|
||||
PermanentCheckBox.Pressed = true;
|
||||
SubmitButton.Disabled = true;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Message: // Message: these are shown to the player when they log on
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
using Content.Shared.Electrocution;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Electrocution;
|
||||
|
||||
/// <summary>
|
||||
/// Shows the Electrocution HUD to entities with the ShowElectrocutionHUDComponent.
|
||||
/// </summary>
|
||||
public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<ElectrocutionHUDVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(Entity<ShowElectrocutionHUDComponent> ent, ref LocalPlayerAttachedEvent args)
|
||||
{
|
||||
ShowHUD();
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(Entity<ShowElectrocutionHUDComponent> ent, ref LocalPlayerDetachedEvent args)
|
||||
{
|
||||
RemoveHUD();
|
||||
}
|
||||
|
||||
private void OnInit(Entity<ShowElectrocutionHUDComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (_playerMan.LocalEntity == ent)
|
||||
{
|
||||
ShowHUD();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<ShowElectrocutionHUDComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (_playerMan.LocalEntity == ent)
|
||||
{
|
||||
RemoveHUD();
|
||||
}
|
||||
}
|
||||
|
||||
// Show the HUD to the client.
|
||||
// We have to look for all current entities that can be electrified and toggle the HUD layer on if they are.
|
||||
private void ShowHUD()
|
||||
{
|
||||
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
|
||||
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, appearanceComp))
|
||||
continue;
|
||||
|
||||
if (electrified)
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, true);
|
||||
else
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the HUD from the client.
|
||||
// Find all current entities that can be electrified and hide the HUD layer.
|
||||
private void RemoveHUD()
|
||||
{
|
||||
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
|
||||
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
|
||||
{
|
||||
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the HUD layer if an entity becomes (de-)electrified
|
||||
protected override void OnAppearanceChange(EntityUid uid, ElectrocutionHUDVisualsComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, args.Component))
|
||||
return;
|
||||
|
||||
var player = _playerMan.LocalEntity;
|
||||
if (electrified && HasComp<ShowElectrocutionHUDComponent>(player))
|
||||
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, true);
|
||||
else
|
||||
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ namespace Content.Client.Eui
|
|||
/// </summary>
|
||||
protected void SendMessage(EuiMessageBase msg)
|
||||
{
|
||||
var netMsg = _netManager.CreateNetMessage<MsgEuiMessage>();
|
||||
var netMsg = new MsgEuiMessage();
|
||||
netMsg.Id = Id;
|
||||
netMsg.Message = msg;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,9 +37,7 @@ namespace Content.Client.Info
|
|||
AddInfoButton("server-info-website-button", CCVars.InfoLinksWebsite);
|
||||
AddInfoButton("server-info-wiki-button", CCVars.InfoLinksWiki);
|
||||
AddInfoButton("server-info-forum-button", CCVars.InfoLinksForum);
|
||||
// Sunrise-Start
|
||||
AddInfoButton("server-info-telegram-button", SunriseCCVars.InfoLinksTelegram);
|
||||
// Sunrise-End
|
||||
AddInfoButton("server-info-telegram-button", CCVars.InfoLinksTelegram);
|
||||
|
||||
var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
|
||||
var guidebookButton = new Button() { Text = Loc.GetString("server-info-guidebook-button") };
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ public partial class BaseShuttleControl : MapGridControl
|
|||
}
|
||||
}
|
||||
|
||||
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 matrix, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 gridToView, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||
{
|
||||
var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp);
|
||||
var minimapScale = MinimapScale;
|
||||
|
|
@ -264,7 +264,7 @@ public partial class BaseShuttleControl : MapGridControl
|
|||
Extensions.EnsureLength(ref _allVertices, totalData);
|
||||
|
||||
_drawJob.MidPoint = midpoint;
|
||||
_drawJob.Matrix = matrix;
|
||||
_drawJob.Matrix = gridToView;
|
||||
_drawJob.MinimapScale = minimapScale;
|
||||
_drawJob.Vertices = gridData.Vertices;
|
||||
_drawJob.ScaledVertices = _allVertices;
|
||||
|
|
@ -286,7 +286,7 @@ public partial class BaseShuttleControl : MapGridControl
|
|||
|
||||
private record struct GridDrawJob : IParallelRobustJob
|
||||
{
|
||||
public int BatchSize => 16;
|
||||
public int BatchSize => 64;
|
||||
|
||||
public float MinimapScale;
|
||||
public Vector2 MidPoint;
|
||||
|
|
@ -297,12 +297,7 @@ public partial class BaseShuttleControl : MapGridControl
|
|||
|
||||
public void Execute(int index)
|
||||
{
|
||||
var vert = Vertices[index];
|
||||
var adjustedVert = Vector2.Transform(vert, Matrix);
|
||||
adjustedVert = adjustedVert with { Y = -adjustedVert.Y };
|
||||
|
||||
var scaledVert = ScalePosition(adjustedVert, MinimapScale, MidPoint);
|
||||
ScaledVertices[index] = scaledVert;
|
||||
ScaledVertices[index] = Vector2.Transform(Vertices[index], Matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public sealed partial class NavScreen : BoxContainer
|
|||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private SharedTransformSystem _xformSystem;
|
||||
|
||||
private EntityUid? _consoleEntity; // Entity of controlling console
|
||||
private EntityUid? _shuttleEntity;
|
||||
|
||||
public NavScreen()
|
||||
|
|
@ -35,6 +36,12 @@ public sealed partial class NavScreen : BoxContainer
|
|||
_shuttleEntity = shuttle;
|
||||
}
|
||||
|
||||
public void SetConsole(EntityUid? console)
|
||||
{
|
||||
_consoleEntity = console;
|
||||
NavRadar.SetConsole(console);
|
||||
}
|
||||
|
||||
private void OnIFFTogglePressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
NavRadar.ShowIFF ^= true;
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
|
|||
{
|
||||
var coordinates = _entManager.GetCoordinates(cState.NavState.Coordinates);
|
||||
NavContainer.SetShuttle(coordinates?.EntityId);
|
||||
NavContainer.SetConsole(owner);
|
||||
MapContainer.SetShuttle(coordinates?.EntityId);
|
||||
MapContainer.SetConsole(owner);
|
||||
|
||||
|
|
|
|||
|
|
@ -107,16 +107,19 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
DrawCircles(handle);
|
||||
var gridNent = EntManager.GetNetEntity(GridEntity);
|
||||
var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value);
|
||||
var ourGridMatrix = _xformSystem.GetWorldMatrix(GridEntity.Value);
|
||||
var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero);
|
||||
var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix);
|
||||
var ourGridToWorld = _xformSystem.GetWorldMatrix(GridEntity.Value);
|
||||
var selectedDockToOurGrid = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero);
|
||||
var selectedDockToWorld = Matrix3x2.Multiply(selectedDockToOurGrid, ourGridToWorld);
|
||||
|
||||
Matrix3x2.Invert(worldFromDock, out var offsetMatrix);
|
||||
Box2 viewBoundsWorld = Matrix3Helpers.TransformBox(selectedDockToWorld, new Box2(-WorldRangeVector, WorldRangeVector));
|
||||
|
||||
Matrix3x2.Invert(selectedDockToWorld, out var worldToSelectedDock);
|
||||
var selectedDockToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
|
||||
|
||||
// Draw nearby grids
|
||||
var controlBounds = PixelSizeBox;
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(gridXform.MapID, new Box2(mapPos.Position - WorldRangeVector, mapPos.Position + WorldRangeVector), ref _grids);
|
||||
_mapManager.FindGridsIntersecting(gridXform.MapID, viewBoundsWorld, ref _grids);
|
||||
|
||||
// offset the dotted-line position to the bounds.
|
||||
Vector2? viewedDockPos = _viewedState != null ? MidPointVector : null;
|
||||
|
|
@ -136,11 +139,11 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
if (grid.Owner != GridEntity && !_shuttles.CanDraw(grid.Owner, iffComp: iffComp))
|
||||
continue;
|
||||
|
||||
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, offsetMatrix);
|
||||
var curGridToWorld = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||
var curGridToView = curGridToWorld * worldToSelectedDock * selectedDockToView;
|
||||
var color = _shuttles.GetIFFColor(grid.Owner, grid.Owner == GridEntity, component: iffComp);
|
||||
|
||||
DrawGrid(handle, matty, grid, color);
|
||||
DrawGrid(handle, curGridToView, grid, color);
|
||||
|
||||
// Draw any docks on that grid
|
||||
if (!DockState.Docks.TryGetValue(EntManager.GetNetEntity(grid), out var gridDocks))
|
||||
|
|
@ -151,23 +154,24 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
if (ViewedDock == dock.Entity)
|
||||
continue;
|
||||
|
||||
var position = Vector2.Transform(dock.Coordinates.Position, matty);
|
||||
|
||||
var otherDockRotation = Matrix3Helpers.CreateRotation(dock.Angle);
|
||||
var scaledPos = ScalePosition(position with {Y = -position.Y});
|
||||
|
||||
if (!controlBounds.Contains(scaledPos.Floored()))
|
||||
// This box is the AABB of all the vertices we draw below.
|
||||
var dockRenderBoundsLocal = new Box2(-0.5f, -0.7f, 0.5f, 0.5f);
|
||||
var currentDockToCurGrid = Matrix3Helpers.CreateTransform(dock.Coordinates.Position, dock.Angle);
|
||||
var currentDockToWorld = Matrix3x2.Multiply(currentDockToCurGrid, curGridToWorld);
|
||||
var dockRenderBoundsWorld = Matrix3Helpers.TransformBox(currentDockToWorld, dockRenderBoundsLocal);
|
||||
if (!viewBoundsWorld.Intersects(dockRenderBoundsWorld))
|
||||
continue;
|
||||
|
||||
// Draw the dock's collision
|
||||
var collisionBL = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(-0.2f, -0.7f), otherDockRotation), matty);
|
||||
Vector2.Transform(new Vector2(-0.2f, -0.7f), otherDockRotation), curGridToView);
|
||||
var collisionBR = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(0.2f, -0.7f), otherDockRotation), matty);
|
||||
Vector2.Transform(new Vector2(0.2f, -0.7f), otherDockRotation), curGridToView);
|
||||
var collisionTR = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(0.2f, -0.5f), otherDockRotation), matty);
|
||||
Vector2.Transform(new Vector2(0.2f, -0.5f), otherDockRotation), curGridToView);
|
||||
var collisionTL = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(-0.2f, -0.5f), otherDockRotation), matty);
|
||||
Vector2.Transform(new Vector2(-0.2f, -0.5f), otherDockRotation), curGridToView);
|
||||
|
||||
var verts = new[]
|
||||
{
|
||||
|
|
@ -181,13 +185,6 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
collisionBL,
|
||||
};
|
||||
|
||||
for (var i = 0; i < verts.Length; i++)
|
||||
{
|
||||
var vert = verts[i];
|
||||
vert.Y = -vert.Y;
|
||||
verts[i] = ScalePosition(vert);
|
||||
}
|
||||
|
||||
var collisionCenter = verts[0] + verts[1] + verts[3] + verts[5];
|
||||
|
||||
var otherDockConnection = Color.ToSrgb(Color.Pink);
|
||||
|
|
@ -195,10 +192,10 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, verts, otherDockConnection);
|
||||
|
||||
// Draw the dock itself
|
||||
var dockBL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f), matty);
|
||||
var dockBR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f), matty);
|
||||
var dockTR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f), matty);
|
||||
var dockTL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f), matty);
|
||||
var dockBL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f), curGridToView);
|
||||
var dockBR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f), curGridToView);
|
||||
var dockTR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f), curGridToView);
|
||||
var dockTL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f), curGridToView);
|
||||
|
||||
verts = new[]
|
||||
{
|
||||
|
|
@ -212,13 +209,6 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
dockBL
|
||||
};
|
||||
|
||||
for (var i = 0; i < verts.Length; i++)
|
||||
{
|
||||
var vert = verts[i];
|
||||
vert.Y = -vert.Y;
|
||||
verts[i] = ScalePosition(vert);
|
||||
}
|
||||
|
||||
Color otherDockColor;
|
||||
|
||||
if (HighlightedDock == dock.Entity)
|
||||
|
|
@ -253,9 +243,11 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
collisionCenter /= 4;
|
||||
var range = viewedDockPos.Value - collisionCenter;
|
||||
|
||||
if (range.Length() < SharedDockingSystem.DockingHiglightRange * MinimapScale)
|
||||
var maxRange = SharedDockingSystem.DockingHiglightRange * MinimapScale;
|
||||
var maxRangeSq = maxRange * maxRange;
|
||||
if (range.LengthSquared() < maxRangeSq)
|
||||
{
|
||||
if (_viewedState?.GridDockedWith == null)
|
||||
if (dock.GridDockedWith == null)
|
||||
{
|
||||
var coordsOne = EntManager.GetCoordinates(_viewedState!.Coordinates);
|
||||
var coordsTwo = EntManager.GetCoordinates(dock.Coordinates);
|
||||
|
|
@ -265,10 +257,11 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
var rotA = _xformSystem.GetWorldRotation(coordsOne.EntityId) + _viewedState!.Angle;
|
||||
var rotB = _xformSystem.GetWorldRotation(coordsTwo.EntityId) + dock.Angle;
|
||||
|
||||
var distance = (mapOne.Position - mapTwo.Position).Length();
|
||||
var distanceSq = (mapOne.Position - mapTwo.Position).LengthSquared();
|
||||
|
||||
var inAlignment = _dockSystem.InAlignment(mapOne, rotA, mapTwo, rotB);
|
||||
var canDock = distance < SharedDockingSystem.DockRange && inAlignment;
|
||||
var maxDockDistSq = SharedDockingSystem.DockRange * SharedDockingSystem.DockRange;
|
||||
var canDock = distanceSq < maxDockDistSq && inAlignment;
|
||||
|
||||
if (dockButton != null)
|
||||
dockButton.Disabled = !canDock || !canDockChange;
|
||||
|
|
@ -297,7 +290,8 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
|||
{
|
||||
// Because it's being layed out top-down we have to arrange for first frame.
|
||||
container.Arrange(PixelRect);
|
||||
var containerPos = scaledPos / UIScale - container.DesiredSize / 2 - new Vector2(0f, 0.75f) * MinimapScale;
|
||||
var dockPositionInView = Vector2.Transform(dock.Coordinates.Position, curGridToView);
|
||||
var containerPos = dockPositionInView / UIScale - container.DesiredSize / 2 - new Vector2(0f, 0.75f) * MinimapScale;
|
||||
SetPosition(container, containerPos);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
/// </summary>
|
||||
private EntityCoordinates? _coordinates;
|
||||
|
||||
/// <summary>
|
||||
/// Entity of controlling console
|
||||
/// </summary>
|
||||
private EntityUid? _consoleEntity;
|
||||
|
||||
private Angle? _rotation;
|
||||
|
||||
private Dictionary<NetEntity, List<DockingPortState>> _docks = new();
|
||||
|
|
@ -57,6 +62,11 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
_rotation = angle;
|
||||
}
|
||||
|
||||
public void SetConsole(EntityUid? consoleEntity)
|
||||
{
|
||||
_consoleEntity = consoleEntity;
|
||||
}
|
||||
|
||||
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindUp(args);
|
||||
|
|
@ -139,40 +149,35 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
}
|
||||
|
||||
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
|
||||
var offset = _coordinates.Value.Position;
|
||||
var posMatrix = Matrix3Helpers.CreateTransform(offset, _rotation.Value);
|
||||
var posMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, _rotation.Value);
|
||||
var ourEntRot = RotateWithEntity ? _transform.GetWorldRotation(xform) : _rotation.Value;
|
||||
var ourEntMatrix = Matrix3Helpers.CreateTransform(_transform.GetWorldPosition(xform), ourEntRot);
|
||||
var ourWorldMatrix = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
|
||||
Matrix3x2.Invert(ourWorldMatrix, out var ourWorldMatrixInvert);
|
||||
var shuttleToWorld = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
|
||||
Matrix3x2.Invert(shuttleToWorld, out var worldToShuttle);
|
||||
var shuttleToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
|
||||
|
||||
// Draw our grid in detail
|
||||
var ourGridId = xform.GridUid;
|
||||
if (EntManager.TryGetComponent<MapGridComponent>(ourGridId, out var ourGrid) &&
|
||||
fixturesQuery.HasComponent(ourGridId.Value))
|
||||
{
|
||||
var ourGridMatrix = _transform.GetWorldMatrix(ourGridId.Value);
|
||||
var matrix = Matrix3x2.Multiply(ourGridMatrix, ourWorldMatrixInvert);
|
||||
var ourGridToWorld = _transform.GetWorldMatrix(ourGridId.Value);
|
||||
var ourGridToShuttle = Matrix3x2.Multiply(ourGridToWorld, worldToShuttle);
|
||||
var ourGridToView = ourGridToShuttle * shuttleToView;
|
||||
var color = _shuttles.GetIFFColor(ourGridId.Value, self: true);
|
||||
|
||||
DrawGrid(handle, matrix, (ourGridId.Value, ourGrid), color);
|
||||
DrawDocks(handle, ourGridId.Value, matrix);
|
||||
DrawGrid(handle, ourGridToView, (ourGridId.Value, ourGrid), color);
|
||||
DrawDocks(handle, ourGridId.Value, ourGridToView);
|
||||
}
|
||||
|
||||
var invertedPosition = _coordinates.Value.Position - offset;
|
||||
invertedPosition.Y = -invertedPosition.Y;
|
||||
// Don't need to transform the InvWorldMatrix again as it's already offset to its position.
|
||||
|
||||
// Draw radar position on the station
|
||||
var radarPos = invertedPosition;
|
||||
const float radarVertRadius = 2f;
|
||||
|
||||
var radarPosVerts = new Vector2[]
|
||||
{
|
||||
ScalePosition(radarPos + new Vector2(0f, -radarVertRadius)),
|
||||
ScalePosition(radarPos + new Vector2(radarVertRadius / 2f, 0f)),
|
||||
ScalePosition(radarPos + new Vector2(0f, radarVertRadius)),
|
||||
ScalePosition(radarPos + new Vector2(radarVertRadius / -2f, 0f)),
|
||||
ScalePosition(new Vector2(0f, -radarVertRadius)),
|
||||
ScalePosition(new Vector2(radarVertRadius / 2f, 0f)),
|
||||
ScalePosition(new Vector2(0f, radarVertRadius)),
|
||||
ScalePosition(new Vector2(radarVertRadius / -2f, 0f)),
|
||||
};
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, radarPosVerts, Color.Lime);
|
||||
|
|
@ -197,8 +202,8 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
if (!_shuttles.CanDraw(gUid, gridBody, iff))
|
||||
continue;
|
||||
|
||||
var gridMatrix = _transform.GetWorldMatrix(gUid);
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, ourWorldMatrixInvert);
|
||||
var curGridToWorld = _transform.GetWorldMatrix(gUid);
|
||||
var curGridToView = curGridToWorld * worldToShuttle * shuttleToView;
|
||||
|
||||
var labelColor = _shuttles.GetIFFColor(grid, self: false, iff);
|
||||
var coordColor = new Color(labelColor.R * 0.8f, labelColor.G * 0.8f, labelColor.B * 0.8f, 0.5f);
|
||||
|
|
@ -213,8 +218,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
{
|
||||
var gridBounds = grid.Comp.LocalAABB;
|
||||
|
||||
var gridCentre = Vector2.Transform(gridBody.LocalCenter, matty);
|
||||
gridCentre.Y = -gridCentre.Y;
|
||||
var gridCentre = Vector2.Transform(gridBody.LocalCenter, curGridToView);
|
||||
|
||||
var distance = gridCentre.Length();
|
||||
var labelText = Loc.GetString("shuttle-console-iff-label", ("name", labelName),
|
||||
|
|
@ -230,9 +234,8 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
// y-offset the control to always render below the grid (vertically)
|
||||
var yOffset = Math.Max(gridBounds.Height, gridBounds.Width) * MinimapScale / 1.8f;
|
||||
|
||||
// The actual position in the UI. We centre the label by offsetting the matrix position
|
||||
// by half the label's width, plus the y-offset
|
||||
var gridScaledPosition = ScalePosition(gridCentre) - new Vector2(0, -yOffset);
|
||||
// The actual position in the UI.
|
||||
var gridScaledPosition = gridCentre - new Vector2(0, -yOffset);
|
||||
|
||||
// Normalize the grid position if it exceeds the viewport bounds
|
||||
// normalizing it instead of clamping it preserves the direction of the vector and prevents corner-hugging
|
||||
|
|
@ -264,18 +267,32 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
}
|
||||
|
||||
// Detailed view
|
||||
var gridAABB = gridMatrix.TransformBox(grid.Comp.LocalAABB);
|
||||
var gridAABB = curGridToWorld.TransformBox(grid.Comp.LocalAABB);
|
||||
|
||||
// Skip drawing if it's out of range.
|
||||
if (!gridAABB.Intersects(viewAABB))
|
||||
continue;
|
||||
|
||||
DrawGrid(handle, matty, grid, labelColor);
|
||||
DrawDocks(handle, gUid, matty);
|
||||
DrawGrid(handle, curGridToView, grid, labelColor);
|
||||
DrawDocks(handle, gUid, curGridToView);
|
||||
}
|
||||
|
||||
// If we've set the controlling console, and it's on a different grid
|
||||
// to the shuttle itself, then draw an additional marker to help the
|
||||
// player determine where they are relative to the shuttle.
|
||||
if (_consoleEntity != null && xformQuery.TryGetComponent(_consoleEntity, out var consoleXform))
|
||||
{
|
||||
if (consoleXform.ParentUid != _coordinates.Value.EntityId)
|
||||
{
|
||||
var consolePositionWorld = _transform.GetWorldPosition((EntityUid)_consoleEntity);
|
||||
var p = Vector2.Transform(consolePositionWorld, worldToShuttle * shuttleToView);
|
||||
handle.DrawCircle(p, 5, Color.ToSrgb(Color.Cyan), true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 matrix)
|
||||
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 gridToView)
|
||||
{
|
||||
if (!ShowDocks)
|
||||
return;
|
||||
|
|
@ -283,33 +300,32 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
|||
const float DockScale = 0.6f;
|
||||
var nent = EntManager.GetNetEntity(uid);
|
||||
|
||||
const float sqrt2 = 1.41421356f;
|
||||
const float dockRadius = DockScale * sqrt2;
|
||||
// Worst-case bounds used to cull a dock:
|
||||
Box2 viewBounds = new Box2(-dockRadius, -dockRadius, Size.X + dockRadius, Size.Y + dockRadius);
|
||||
if (_docks.TryGetValue(nent, out var docks))
|
||||
{
|
||||
foreach (var state in docks)
|
||||
{
|
||||
var position = state.Coordinates.Position;
|
||||
var uiPosition = Vector2.Transform(position, matrix);
|
||||
|
||||
if (uiPosition.Length() > (WorldRange * 2f) - DockScale)
|
||||
var positionInView = Vector2.Transform(position, gridToView);
|
||||
if (!viewBounds.Contains(positionInView))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var color = Color.ToSrgb(Color.Magenta);
|
||||
|
||||
var verts = new[]
|
||||
{
|
||||
Vector2.Transform(position + new Vector2(-DockScale, -DockScale), matrix),
|
||||
Vector2.Transform(position + new Vector2(DockScale, -DockScale), matrix),
|
||||
Vector2.Transform(position + new Vector2(DockScale, DockScale), matrix),
|
||||
Vector2.Transform(position + new Vector2(-DockScale, DockScale), matrix),
|
||||
Vector2.Transform(position + new Vector2(-DockScale, -DockScale), gridToView),
|
||||
Vector2.Transform(position + new Vector2(DockScale, -DockScale), gridToView),
|
||||
Vector2.Transform(position + new Vector2(DockScale, DockScale), gridToView),
|
||||
Vector2.Transform(position + new Vector2(-DockScale, DockScale), gridToView),
|
||||
};
|
||||
|
||||
for (var i = 0; i < verts.Length; i++)
|
||||
{
|
||||
var vert = verts[i];
|
||||
vert.Y = -vert.Y;
|
||||
verts[i] = ScalePosition(vert);
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, color.WithAlpha(0.8f));
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineStrip, verts, color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ using static Robust.Client.UserInterface.Controls.LineEdit;
|
|||
namespace Content.Client.UserInterface.Systems.Chat.Widgets;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
#pragma warning disable RA0003
|
||||
[Virtual]
|
||||
public partial class ChatBox : UIWidget
|
||||
#pragma warning restore RA0003
|
||||
{
|
||||
private readonly ChatUIController _controller;
|
||||
private readonly IEntityManager _entManager;
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
|||
buttonHeading.AddStyleClass(ContainerButton.StyleClassButton);
|
||||
buttonHeading.Label.HorizontalAlignment = HAlignment.Center;
|
||||
buttonHeading.Label.HorizontalExpand = true;
|
||||
buttonHeading.Margin = new Thickness(8, 0, 8, 2);
|
||||
|
||||
var body = new CollapsibleBody
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,17 +11,21 @@
|
|||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
SeparationOverride="5"
|
||||
>
|
||||
<GridContainer
|
||||
Rows="2"
|
||||
HSeparationOverride="5"
|
||||
VSeparationOverride="5"
|
||||
HorizontalExpand="True">
|
||||
<ui:MenuButton
|
||||
Name="EscapeButton"
|
||||
Access="Internal"
|
||||
Icon="{xe:Tex '/Textures/Interface/hamburger.svg.192dpi.png'}"
|
||||
BoundKey = "{x:Static ic:EngineKeyFunctions.EscapeMenu}"
|
||||
ToolTip="{Loc 'game-hud-open-escape-menu-button-tooltip'}"
|
||||
MinSize="70 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonOpenRight}"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
<ui:MenuButton
|
||||
Name="GuidebookButton"
|
||||
|
|
@ -29,7 +33,7 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/VerbIcons/information.svg.192dpi.png'}"
|
||||
ToolTip="{Loc 'game-hud-open-guide-menu-button-tooltip'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenGuidebook}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
|
|
@ -39,7 +43,7 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/character.svg.192dpi.png'}"
|
||||
ToolTip="{Loc 'game-hud-open-character-menu-button-tooltip'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenCharacterMenu}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
|
|
@ -49,7 +53,7 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/emotes.svg.192dpi.png'}"
|
||||
ToolTip="{Loc 'game-hud-open-emotes-menu-button-tooltip'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenEmotesMenu}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
|
|
@ -59,7 +63,7 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/hammer.svg.192dpi.png'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenCraftingMenu}"
|
||||
ToolTip="{Loc 'game-hud-open-crafting-menu-button-tooltip'}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
|
|
@ -69,7 +73,7 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/fist.svg.192dpi.png'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenActionsMenu}"
|
||||
ToolTip="{Loc 'game-hud-open-actions-menu-button-tooltip'}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
|
|
@ -79,7 +83,7 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/gavel.svg.192dpi.png'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenAdminMenu}"
|
||||
ToolTip="{Loc 'game-hud-open-admin-menu-button-tooltip'}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
|
|
@ -89,7 +93,7 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/sandbox.svg.192dpi.png'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenSandboxWindow}"
|
||||
ToolTip="{Loc 'game-hud-open-sandbox-menu-button-tooltip'}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
|
|
@ -99,8 +103,9 @@
|
|||
Icon="{xe:Tex '/Textures/Interface/info.svg.192dpi.png'}"
|
||||
BoundKey = "{x:Static is:ContentKeyFunctions.OpenAHelp}"
|
||||
ToolTip="{Loc 'ui-options-function-open-a-help'}"
|
||||
MinSize="42 64"
|
||||
MinSize="48 64"
|
||||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonOpenLeft}"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
</GridContainer>
|
||||
</widgets:GameTopMenuBar>
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||
// No bans on record
|
||||
Assert.Multiple(async () =>
|
||||
{
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(1), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Is.Empty);
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Is.Empty);
|
||||
});
|
||||
|
||||
// Try to pardon a ban that does not exist
|
||||
|
|
@ -43,9 +43,9 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||
// Still no bans on record
|
||||
Assert.Multiple(async () =>
|
||||
{
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(1), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Is.Empty);
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Is.Empty);
|
||||
});
|
||||
|
||||
var banReason = "test";
|
||||
|
|
@ -57,9 +57,9 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||
// Should have one ban on record now
|
||||
Assert.Multiple(async () =>
|
||||
{
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Not.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Not.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(1), Is.Not.Null);
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1));
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1));
|
||||
});
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
|
|
@ -70,13 +70,13 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||
await server.WaitPost(() => sConsole.ExecuteCommand("pardon 2"));
|
||||
|
||||
// The existing ban is unaffected
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Not.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Not.Null);
|
||||
|
||||
var ban = await sDatabase.GetServerBanAsync(1);
|
||||
Assert.Multiple(async () =>
|
||||
{
|
||||
Assert.That(ban, Is.Not.Null);
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1));
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1));
|
||||
|
||||
// Check that it matches
|
||||
Assert.That(ban.Id, Is.EqualTo(1));
|
||||
|
|
@ -95,7 +95,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||
await server.WaitPost(() => sConsole.ExecuteCommand("pardon 1"));
|
||||
|
||||
// No bans should be returned
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null);
|
||||
|
||||
// Direct id lookup returns a pardoned ban
|
||||
var pardonedBan = await sDatabase.GetServerBanAsync(1);
|
||||
|
|
@ -105,7 +105,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||
Assert.That(pardonedBan, Is.Not.Null);
|
||||
|
||||
// The list is still returned since that ignores pardons
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1));
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1));
|
||||
|
||||
Assert.That(pardonedBan.Id, Is.EqualTo(1));
|
||||
Assert.That(pardonedBan.UserId, Is.EqualTo(clientId));
|
||||
|
|
@ -133,13 +133,13 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||
Assert.Multiple(async () =>
|
||||
{
|
||||
// No bans should be returned
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null);
|
||||
Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null);
|
||||
|
||||
// Direct id lookup returns a pardoned ban
|
||||
Assert.That(await sDatabase.GetServerBanAsync(1), Is.Not.Null);
|
||||
|
||||
// The list is still returned since that ignores pardons
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1));
|
||||
Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1));
|
||||
});
|
||||
|
||||
// Reconnect client. Slightly faster than dirtying the pair.
|
||||
|
|
|
|||
2072
Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.Designer.cs
generated
Normal file
2072
Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ModernHwid : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hwid_type",
|
||||
table: "server_role_ban",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hwid_type",
|
||||
table: "server_ban",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "last_seen_hwid_type",
|
||||
table: "player",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hwid_type",
|
||||
table: "connection_log",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hwid_type",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hwid_type",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_seen_hwid_type",
|
||||
table: "player");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hwid_type",
|
||||
table: "connection_log");
|
||||
}
|
||||
}
|
||||
}
|
||||
2076
Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.Designer.cs
generated
Normal file
2076
Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ConnectionTrust : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<float>(
|
||||
name: "trust",
|
||||
table: "connection_log",
|
||||
type: "real",
|
||||
nullable: false,
|
||||
defaultValue: 0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "trust",
|
||||
table: "connection_log");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -512,20 +512,6 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
b.ToTable("assigned_user_id", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Blacklist",
|
||||
b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("UserId")
|
||||
.HasName("PK_blacklist");
|
||||
|
||||
b.ToTable("blacklist", (string) null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.BanTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -571,6 +557,19 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
b.ToTable("ban_template", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Blacklist", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("UserId")
|
||||
.HasName("PK_blacklist");
|
||||
|
||||
b.ToTable("blacklist", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -589,10 +588,6 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("smallint")
|
||||
.HasColumnName("denied");
|
||||
|
||||
b.Property<byte[]>("HWId")
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
|
|
@ -603,6 +598,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("time");
|
||||
|
||||
b.Property<float>("Trust")
|
||||
.HasColumnType("real")
|
||||
.HasColumnName("trust");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
|
@ -718,10 +717,6 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("inet")
|
||||
.HasColumnName("last_seen_address");
|
||||
|
||||
b.Property<byte[]>("LastSeenHWId")
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("last_seen_hwid");
|
||||
|
||||
b.Property<DateTime>("LastSeenTime")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_seen_time");
|
||||
|
|
@ -1065,10 +1060,6 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<byte[]>("HWId")
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("hidden");
|
||||
|
|
@ -1199,10 +1190,6 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<byte[]>("HWId")
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("hidden");
|
||||
|
|
@ -1644,6 +1631,34 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.IsRequired()
|
||||
.HasConstraintName("FK_connection_log_server_server_id");
|
||||
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("ConnectionLogId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("connection_log_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("hwid_type");
|
||||
|
||||
b1.HasKey("ConnectionLogId");
|
||||
|
||||
b1.ToTable("connection_log");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ConnectionLogId")
|
||||
.HasConstraintName("FK_connection_log_connection_log_connection_log_id");
|
||||
});
|
||||
|
||||
b.Navigation("HWId");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
|
|
@ -1659,6 +1674,37 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
||||
{
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("PlayerId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("player_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("last_seen_hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("last_seen_hwid_type");
|
||||
|
||||
b1.HasKey("PlayerId");
|
||||
|
||||
b1.ToTable("player");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PlayerId")
|
||||
.HasConstraintName("FK_player_player_player_id");
|
||||
});
|
||||
|
||||
b.Navigation("LastSeenHWId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Profile", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Preference", "Preference")
|
||||
|
|
@ -1753,8 +1799,36 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_ban_round_round_id");
|
||||
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("ServerBanId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("server_ban_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("hwid_type");
|
||||
|
||||
b1.HasKey("ServerBanId");
|
||||
|
||||
b1.ToTable("server_ban");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ServerBanId")
|
||||
.HasConstraintName("FK_server_ban_server_ban_server_ban_id");
|
||||
});
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("HWId");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
|
|
@ -1802,8 +1876,36 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_role_ban_round_round_id");
|
||||
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("ServerRoleBanId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("server_role_ban_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("hwid_type");
|
||||
|
||||
b1.HasKey("ServerRoleBanId");
|
||||
|
||||
b1.ToTable("server_role_ban");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ServerRoleBanId")
|
||||
.HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id");
|
||||
});
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("HWId");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
|
|
|
|||
1995
Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.Designer.cs
generated
Normal file
1995
Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ModernHwid : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hwid_type",
|
||||
table: "server_role_ban",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hwid_type",
|
||||
table: "server_ban",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "last_seen_hwid_type",
|
||||
table: "player",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hwid_type",
|
||||
table: "connection_log",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hwid_type",
|
||||
table: "server_role_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hwid_type",
|
||||
table: "server_ban");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_seen_hwid_type",
|
||||
table: "player");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hwid_type",
|
||||
table: "connection_log");
|
||||
}
|
||||
}
|
||||
}
|
||||
1999
Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.Designer.cs
generated
Normal file
1999
Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ConnectionTrust : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<float>(
|
||||
name: "trust",
|
||||
table: "connection_log",
|
||||
type: "REAL",
|
||||
nullable: false,
|
||||
defaultValue: 0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "trust",
|
||||
table: "connection_log");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -483,19 +483,6 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
b.ToTable("assigned_user_id", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Blacklist",
|
||||
b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("UserId")
|
||||
.HasName("PK_blacklist");
|
||||
|
||||
b.ToTable("blacklist", (string) null);
|
||||
});
|
||||
modelBuilder.Entity("Content.Server.Database.BanTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -539,6 +526,19 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
b.ToTable("ban_template", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Blacklist", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("UserId")
|
||||
.HasName("PK_blacklist");
|
||||
|
||||
b.ToTable("blacklist", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -555,10 +555,6 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("denied");
|
||||
|
||||
b.Property<byte[]>("HWId")
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
|
|
@ -569,6 +565,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("TEXT")
|
||||
.HasColumnName("time");
|
||||
|
||||
b.Property<float>("Trust")
|
||||
.HasColumnType("REAL")
|
||||
.HasColumnName("trust");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("user_id");
|
||||
|
|
@ -675,10 +675,6 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_seen_address");
|
||||
|
||||
b.Property<byte[]>("LastSeenHWId")
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("last_seen_hwid");
|
||||
|
||||
b.Property<DateTime>("LastSeenTime")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_seen_time");
|
||||
|
|
@ -1003,10 +999,6 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<byte[]>("HWId")
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("hidden");
|
||||
|
|
@ -1131,10 +1123,6 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expiration_time");
|
||||
|
||||
b.Property<byte[]>("HWId")
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b.Property<bool>("Hidden")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("hidden");
|
||||
|
|
@ -1566,6 +1554,34 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.IsRequired()
|
||||
.HasConstraintName("FK_connection_log_server_server_id");
|
||||
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("ConnectionLogId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("connection_log_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("hwid_type");
|
||||
|
||||
b1.HasKey("ConnectionLogId");
|
||||
|
||||
b1.ToTable("connection_log");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ConnectionLogId")
|
||||
.HasConstraintName("FK_connection_log_connection_log_connection_log_id");
|
||||
});
|
||||
|
||||
b.Navigation("HWId");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
|
|
@ -1581,6 +1597,37 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Player", b =>
|
||||
{
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("PlayerId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("player_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("last_seen_hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("last_seen_hwid_type");
|
||||
|
||||
b1.HasKey("PlayerId");
|
||||
|
||||
b1.ToTable("player");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PlayerId")
|
||||
.HasConstraintName("FK_player_player_player_id");
|
||||
});
|
||||
|
||||
b.Navigation("LastSeenHWId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Profile", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Preference", "Preference")
|
||||
|
|
@ -1675,8 +1722,36 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_ban_round_round_id");
|
||||
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("ServerBanId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("server_ban_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("hwid_type");
|
||||
|
||||
b1.HasKey("ServerBanId");
|
||||
|
||||
b1.ToTable("server_ban");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ServerBanId")
|
||||
.HasConstraintName("FK_server_ban_server_ban_server_ban_id");
|
||||
});
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("HWId");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
|
|
@ -1724,8 +1799,36 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasForeignKey("RoundId")
|
||||
.HasConstraintName("FK_server_role_ban_round_round_id");
|
||||
|
||||
b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
|
||||
{
|
||||
b1.Property<int>("ServerRoleBanId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("server_role_ban_id");
|
||||
|
||||
b1.Property<byte[]>("Hwid")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("hwid");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("hwid_type");
|
||||
|
||||
b1.HasKey("ServerRoleBanId");
|
||||
|
||||
b1.ToTable("server_role_ban");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ServerRoleBanId")
|
||||
.HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id");
|
||||
});
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("HWId");
|
||||
|
||||
b.Navigation("LastEditedBy");
|
||||
|
||||
b.Navigation("Round");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
|
|
@ -327,6 +329,47 @@ namespace Content.Server.Database
|
|||
.HasForeignKey(w => w.PlayerUserId)
|
||||
.HasPrincipalKey(p => p.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Changes for modern HWID integration
|
||||
modelBuilder.Entity<Player>()
|
||||
.OwnsOne(p => p.LastSeenHWId)
|
||||
.Property(p => p.Hwid)
|
||||
.HasColumnName("last_seen_hwid");
|
||||
|
||||
modelBuilder.Entity<Player>()
|
||||
.OwnsOne(p => p.LastSeenHWId)
|
||||
.Property(p => p.Type)
|
||||
.HasDefaultValue(HwidType.Legacy);
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.OwnsOne(p => p.HWId)
|
||||
.Property(p => p.Hwid)
|
||||
.HasColumnName("hwid");
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.OwnsOne(p => p.HWId)
|
||||
.Property(p => p.Type)
|
||||
.HasDefaultValue(HwidType.Legacy);
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>()
|
||||
.OwnsOne(p => p.HWId)
|
||||
.Property(p => p.Hwid)
|
||||
.HasColumnName("hwid");
|
||||
|
||||
modelBuilder.Entity<ServerRoleBan>()
|
||||
.OwnsOne(p => p.HWId)
|
||||
.Property(p => p.Type)
|
||||
.HasDefaultValue(HwidType.Legacy);
|
||||
|
||||
modelBuilder.Entity<ConnectionLog>()
|
||||
.OwnsOne(p => p.HWId)
|
||||
.Property(p => p.Hwid)
|
||||
.HasColumnName("hwid");
|
||||
|
||||
modelBuilder.Entity<ConnectionLog>()
|
||||
.OwnsOne(p => p.HWId)
|
||||
.Property(p => p.Type)
|
||||
.HasDefaultValue(HwidType.Legacy);
|
||||
}
|
||||
|
||||
public virtual IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
||||
|
|
@ -520,7 +563,7 @@ namespace Content.Server.Database
|
|||
public string LastSeenUserName { get; set; } = null!;
|
||||
public DateTime LastSeenTime { get; set; }
|
||||
public IPAddress LastSeenAddress { get; set; } = null!;
|
||||
public byte[]? LastSeenHWId { get; set; }
|
||||
public TypedHwid? LastSeenHWId { get; set; }
|
||||
|
||||
// Data that changes with each round
|
||||
public List<Round> Rounds { get; set; } = null!;
|
||||
|
|
@ -669,7 +712,7 @@ namespace Content.Server.Database
|
|||
int Id { get; set; }
|
||||
Guid? PlayerUserId { get; set; }
|
||||
NpgsqlInet? Address { get; set; }
|
||||
byte[]? HWId { get; set; }
|
||||
TypedHwid? HWId { get; set; }
|
||||
DateTime BanTime { get; set; }
|
||||
DateTime? ExpirationTime { get; set; }
|
||||
string Reason { get; set; }
|
||||
|
|
@ -754,7 +797,7 @@ namespace Content.Server.Database
|
|||
/// <summary>
|
||||
/// Hardware ID of the banned player.
|
||||
/// </summary>
|
||||
public byte[]? HWId { get; set; }
|
||||
public TypedHwid? HWId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time when the ban was applied by an administrator.
|
||||
|
|
@ -892,7 +935,7 @@ namespace Content.Server.Database
|
|||
public DateTime Time { get; set; }
|
||||
|
||||
public IPAddress Address { get; set; } = null!;
|
||||
public byte[]? HWId { get; set; }
|
||||
public TypedHwid? HWId { get; set; }
|
||||
|
||||
public ConnectionDenyReason? Denied { get; set; }
|
||||
|
||||
|
|
@ -909,6 +952,8 @@ namespace Content.Server.Database
|
|||
|
||||
public List<ServerBanHit> BanHits { get; set; } = null!;
|
||||
public Server Server { get; set; } = null!;
|
||||
|
||||
public float Trust { get; set; }
|
||||
}
|
||||
|
||||
public enum ConnectionDenyReason : byte
|
||||
|
|
@ -946,7 +991,7 @@ namespace Content.Server.Database
|
|||
public Guid? PlayerUserId { get; set; }
|
||||
[Required] public TimeSpan PlaytimeAtNote { get; set; }
|
||||
public NpgsqlInet? Address { get; set; }
|
||||
public byte[]? HWId { get; set; }
|
||||
public TypedHwid? HWId { get; set; }
|
||||
|
||||
public DateTime BanTime { get; set; }
|
||||
|
||||
|
|
@ -1207,4 +1252,37 @@ namespace Content.Server.Database
|
|||
/// <seealso cref="ServerBan.Hidden"/>
|
||||
public bool Hidden { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A hardware ID value together with its <see cref="HwidType"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ImmutableTypedHwid"/>
|
||||
[Owned]
|
||||
public sealed class TypedHwid
|
||||
{
|
||||
public byte[] Hwid { get; set; } = default!;
|
||||
public HwidType Type { get; set; }
|
||||
|
||||
[return: NotNullIfNotNull(nameof(immutable))]
|
||||
public static implicit operator TypedHwid?(ImmutableTypedHwid? immutable)
|
||||
{
|
||||
if (immutable == null)
|
||||
return null;
|
||||
|
||||
return new TypedHwid
|
||||
{
|
||||
Hwid = immutable.Hwid.ToArray(),
|
||||
Type = immutable.Type,
|
||||
};
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull(nameof(hwid))]
|
||||
public static implicit operator ImmutableTypedHwid?(TypedHwid? hwid)
|
||||
{
|
||||
if (hwid == null)
|
||||
return null;
|
||||
|
||||
return new ImmutableTypedHwid(hwid.Hwid.ToImmutableArray(), hwid.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ namespace Content.Server.Database
|
|||
}
|
||||
}
|
||||
|
||||
public class SnakeCaseConvention :
|
||||
public partial class SnakeCaseConvention :
|
||||
IEntityTypeAddedConvention,
|
||||
IEntityTypeAnnotationChangedConvention,
|
||||
IPropertyAddedConvention,
|
||||
|
|
@ -99,22 +99,27 @@ namespace Content.Server.Database
|
|||
|
||||
public static string RewriteName(string name)
|
||||
{
|
||||
var regex = new Regex("[A-Z]+", RegexOptions.Compiled);
|
||||
return regex.Replace(
|
||||
name,
|
||||
(Match match) => {
|
||||
if (match.Index == 0 && (match.Value == "FK" || match.Value == "PK" || match.Value == "IX")) {
|
||||
return match.Value;
|
||||
return UpperCaseLocator()
|
||||
.Replace(
|
||||
name,
|
||||
(Match match) => {
|
||||
if (match.Index == 0 && (match.Value == "FK" || match.Value == "PK" || match.Value == "IX")) {
|
||||
return match.Value;
|
||||
}
|
||||
if (match.Value == "HWI")
|
||||
return (match.Index == 0 ? "" : "_") + "hwi";
|
||||
if (match.Index == 0)
|
||||
return match.Value.ToLower();
|
||||
if (match.Length > 1)
|
||||
return $"_{match.Value[..^1].ToLower()}_{match.Value[^1..^0].ToLower()}";
|
||||
|
||||
// Do not add a _ if there is already one before this. This happens with owned entities.
|
||||
if (name[match.Index - 1] == '_')
|
||||
return match.Value.ToLower();
|
||||
|
||||
return "_" + match.Value.ToLower();
|
||||
}
|
||||
if (match.Value == "HWI")
|
||||
return (match.Index == 0 ? "" : "_") + "hwi";
|
||||
if (match.Index == 0)
|
||||
return match.Value.ToLower();
|
||||
if (match.Length > 1)
|
||||
return $"_{match.Value[..^1].ToLower()}_{match.Value[^1..^0].ToLower()}";
|
||||
return "_" + match.Value.ToLower();
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
public virtual void ProcessEntityTypeAdded(
|
||||
|
|
@ -332,5 +337,8 @@ namespace Content.Server.Database
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("[A-Z]+", RegexOptions.Compiled)]
|
||||
private static partial Regex UpperCaseLocator();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
@ -156,7 +154,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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -100,7 +100,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)
|
||||
|
|
@ -167,7 +168,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)
|
||||
|
|
@ -207,9 +208,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";
|
||||
|
|
@ -256,6 +255,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),
|
||||
|
|
@ -276,7 +276,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);
|
||||
|
||||
public void WebhookUpdateRoleBans(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, IReadOnlyCollection<string> roles, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -176,7 +176,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));
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ public sealed partial class AtmosMonitorComponent : Component
|
|||
[DataField("gasThresholds")]
|
||||
public Dictionary<Gas, AtmosAlarmThreshold>? GasThresholds;
|
||||
|
||||
// Stores a reference to the gas on the tile this is on.
|
||||
/// <summary>
|
||||
/// Stores a reference to the gas on the tile this entity is on (or the pipe network it monitors; see <see cref="MonitorsPipeNet"/>).
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public GasMixture? TileGas;
|
||||
|
||||
|
|
@ -65,4 +67,19 @@ public sealed partial class AtmosMonitorComponent : Component
|
|||
/// </summary>
|
||||
[DataField("registeredDevices")]
|
||||
public HashSet<string> RegisteredDevices = new();
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether this device monitors its own internal pipe network rather than the surrounding atmosphere.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If 'true', the entity will require a NodeContainerComponent with one or more PipeNodes to function.
|
||||
/// </remarks>
|
||||
[DataField]
|
||||
public bool MonitorsPipeNet = false;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the name of the pipe node that this device is monitoring.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string NodeNameMonitoredPipe = "monitored";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ using Content.Server.Atmos.Piping.Components;
|
|||
using Content.Server.Atmos.Piping.EntitySystems;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
|
|
@ -25,6 +28,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||
[Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly NodeContainerSystem _nodeContainerSystem = default!;
|
||||
|
||||
// Commands
|
||||
public const string AtmosMonitorSetThresholdCmd = "atmos_monitor_set_threshold";
|
||||
|
|
@ -56,8 +60,15 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||
|
||||
private void OnAtmosDeviceEnterAtmosphere(EntityUid uid, AtmosMonitorComponent atmosMonitor, ref AtmosDeviceEnabledEvent args)
|
||||
{
|
||||
if (atmosMonitor.MonitorsPipeNet && _nodeContainerSystem.TryGetNode<PipeNode>(uid, atmosMonitor.NodeNameMonitoredPipe, out var pipeNode))
|
||||
{
|
||||
atmosMonitor.TileGas = pipeNode.Air;
|
||||
return;
|
||||
}
|
||||
|
||||
atmosMonitor.TileGas = _atmosphereSystem.GetContainingMixture(uid, true);
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, AtmosMonitorComponent component, MapInitEvent args)
|
||||
{
|
||||
if (component.TemperatureThresholdId != null)
|
||||
|
|
@ -206,7 +217,7 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||
if (!this.IsPowered(uid, EntityManager))
|
||||
return;
|
||||
|
||||
if (args.Grid == null)
|
||||
if (args.Grid == null)
|
||||
return;
|
||||
|
||||
// if we're not monitoring atmos, don't bother
|
||||
|
|
@ -215,6 +226,10 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||
&& component.GasThresholds == null)
|
||||
return;
|
||||
|
||||
// If monitoring a pipe network, get its most recent gas mixture
|
||||
if (component.MonitorsPipeNet && _nodeContainerSystem.TryGetNode<PipeNode>(uid, component.NodeNameMonitoredPipe, out var pipeNode))
|
||||
component.TileGas = pipeNode.Air;
|
||||
|
||||
UpdateState(uid, component.TileGas, component);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,11 +116,14 @@ namespace Content.Server.Connection
|
|||
|
||||
var serverId = (await _serverDbEntry.ServerEntity).Id;
|
||||
|
||||
var hwid = e.UserData.GetModernHwid();
|
||||
var trust = e.UserData.Trust;
|
||||
|
||||
if (deny != null)
|
||||
{
|
||||
var (reason, msg, banHits) = deny.Value;
|
||||
|
||||
var id = await _db.AddConnectionLogAsync(userId, e.UserName, addr, e.UserData.HWId, reason, serverId);
|
||||
var id = await _db.AddConnectionLogAsync(userId, e.UserName, addr, hwid, trust, reason, serverId);
|
||||
if (banHits is { Count: > 0 })
|
||||
await _db.AddServerBanHitsAsync(id, banHits);
|
||||
|
||||
|
|
@ -132,12 +135,12 @@ namespace Content.Server.Connection
|
|||
}
|
||||
else
|
||||
{
|
||||
await _db.AddConnectionLogAsync(userId, e.UserName, addr, e.UserData.HWId, null, serverId);
|
||||
await _db.AddConnectionLogAsync(userId, e.UserName, addr, hwid, trust, null, serverId);
|
||||
|
||||
if (!ServerPreferencesManager.ShouldStorePrefs(e.AuthType))
|
||||
return;
|
||||
|
||||
await _db.UpdatePlayerRecordAsync(userId, e.UserName, addr, e.UserData.HWId);
|
||||
await _db.UpdatePlayerRecordAsync(userId, e.UserName, addr, hwid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +198,9 @@ namespace Content.Server.Connection
|
|||
hwId = null;
|
||||
}
|
||||
|
||||
var bans = await _db.GetServerBansAsync(addr, userId, hwId, includeUnbanned: false);
|
||||
var modernHwid = e.UserData.ModernHWIds;
|
||||
|
||||
var bans = await _db.GetServerBansAsync(addr, userId, hwId, modernHwid, includeUnbanned: false);
|
||||
if (bans.Count > 0)
|
||||
{
|
||||
var firstBan = bans[0];
|
||||
|
|
|
|||
24
Content.Server/Connection/UserDataExt.cs
Normal file
24
Content.Server/Connection/UserDataExt.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using Content.Shared.Database;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Connection;
|
||||
|
||||
/// <summary>
|
||||
/// Helper functions for working with <see cref="NetUserData"/>.
|
||||
/// </summary>
|
||||
public static class UserDataExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the preferred HWID that should be used for new records related to a player.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Players can have zero or more HWIDs, but for logging things like connection logs we generally
|
||||
/// only want a single one. This method returns a nullable method.
|
||||
/// </remarks>
|
||||
public static ImmutableTypedHwid? GetModernHwid(this NetUserData userData)
|
||||
{
|
||||
return userData.ModernHWIds.Length == 0
|
||||
? null
|
||||
: new ImmutableTypedHwid(userData.ModernHWIds[0], HwidType.Modern);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Content.Server.IP;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Database;
|
||||
|
|
@ -52,9 +53,28 @@ public static class BanMatcher
|
|||
return true;
|
||||
}
|
||||
|
||||
return player.HWId is { Length: > 0 } hwIdVar
|
||||
&& ban.HWId != null
|
||||
&& hwIdVar.AsSpan().SequenceEqual(ban.HWId.Value.AsSpan());
|
||||
switch (ban.HWId?.Type)
|
||||
{
|
||||
case HwidType.Legacy:
|
||||
if (player.HWId is { Length: > 0 } hwIdVar
|
||||
&& hwIdVar.AsSpan().SequenceEqual(ban.HWId.Hwid.AsSpan()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case HwidType.Modern:
|
||||
if (player.ModernHWIds is { Length: > 0 } modernHwIdVar)
|
||||
{
|
||||
foreach (var hwid in modernHwIdVar)
|
||||
{
|
||||
if (hwid.AsSpan().SequenceEqual(ban.HWId.Hwid.AsSpan()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -73,10 +93,15 @@ public static class BanMatcher
|
|||
public IPAddress? Address;
|
||||
|
||||
/// <summary>
|
||||
/// The hardware ID of the player.
|
||||
/// The LEGACY hardware ID of the player. Corresponds with <see cref="NetUserData.HWId"/>.
|
||||
/// </summary>
|
||||
public ImmutableArray<byte>? HWId;
|
||||
|
||||
/// <summary>
|
||||
/// The modern hardware IDs of the player. Corresponds with <see cref="NetUserData.ModernHWIds"/>.
|
||||
/// </summary>
|
||||
public ImmutableArray<ImmutableArray<byte>>? ModernHWIds;
|
||||
|
||||
/// <summary>
|
||||
/// Exemption flags the player has been granted.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Shared.Network;
|
||||
|
|
@ -121,7 +120,7 @@ public sealed record PlayerRecord(
|
|||
string LastSeenUserName,
|
||||
DateTimeOffset LastSeenTime,
|
||||
IPAddress LastSeenAddress,
|
||||
ImmutableArray<byte>? HWId);
|
||||
ImmutableTypedHwid? HWId);
|
||||
|
||||
public sealed record RoundRecord(int Id, DateTimeOffset? StartDate, ServerRecord Server);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
|
|
@ -13,7 +12,7 @@ namespace Content.Server.Database
|
|||
public int? Id { get; }
|
||||
public NetUserId? UserId { get; }
|
||||
public (IPAddress address, int cidrMask)? Address { get; }
|
||||
public ImmutableArray<byte>? HWId { get; }
|
||||
public ImmutableTypedHwid? HWId { get; }
|
||||
|
||||
public DateTimeOffset BanTime { get; }
|
||||
public DateTimeOffset? ExpirationTime { get; }
|
||||
|
|
@ -28,7 +27,7 @@ namespace Content.Server.Database
|
|||
public ServerBanDef(int? id,
|
||||
NetUserId? userId,
|
||||
(IPAddress, int)? address,
|
||||
ImmutableArray<byte>? hwId,
|
||||
TypedHwid? hwId,
|
||||
DateTimeOffset banTime,
|
||||
DateTimeOffset? expirationTime,
|
||||
int? roundId,
|
||||
|
|
|
|||
|
|
@ -396,12 +396,14 @@ namespace Content.Server.Database
|
|||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The HWId of the user.</param>
|
||||
/// <param name="hwId">The legacy HWId of the user.</param>
|
||||
/// <param name="modernHWIds">The modern HWIDs of the user.</param>
|
||||
/// <returns>The user's latest received un-pardoned ban, or null if none exist.</returns>
|
||||
public abstract Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId);
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds);
|
||||
|
||||
/// <summary>
|
||||
/// Looks up an user's ban history.
|
||||
|
|
@ -410,13 +412,15 @@ namespace Content.Server.Database
|
|||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The HWId of the user.</param>
|
||||
/// <param name="hwId">The legacy HWId of the user.</param>
|
||||
/// <param name="modernHWIds">The modern HWIDs of the user.</param>
|
||||
/// <param name="includeUnbanned">Include pardoned and expired bans.</param>
|
||||
/// <returns>The user's ban history.</returns>
|
||||
public abstract Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned);
|
||||
|
||||
public abstract Task AddServerBanAsync(ServerBanDef serverBan);
|
||||
|
|
@ -507,11 +511,13 @@ namespace Content.Server.Database
|
|||
/// <param name="address">The IP address of the user.</param>
|
||||
/// <param name="userId">The NetUserId of the user.</param>
|
||||
/// <param name="hwId">The Hardware Id of the user.</param>
|
||||
/// <param name="modernHWIds">The modern HWIDs of the user.</param>
|
||||
/// <param name="includeUnbanned">Whether expired and pardoned bans are included.</param>
|
||||
/// <returns>The user's role ban history.</returns>
|
||||
public abstract Task<List<ServerRoleBanDef>> GetServerRoleBansAsync(IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned);
|
||||
|
||||
public abstract Task<ServerRoleBanDef> AddServerRoleBanAsync(ServerRoleBanDef serverRoleBan);
|
||||
|
|
@ -601,7 +607,7 @@ namespace Content.Server.Database
|
|||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
ImmutableTypedHwid? hwId)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
|
|
@ -618,7 +624,7 @@ namespace Content.Server.Database
|
|||
record.LastSeenTime = DateTime.UtcNow;
|
||||
record.LastSeenAddress = address;
|
||||
record.LastSeenUserName = userName;
|
||||
record.LastSeenHWId = hwId.ToArray();
|
||||
record.LastSeenHWId = hwId;
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
|
@ -664,7 +670,7 @@ namespace Content.Server.Database
|
|||
player.LastSeenUserName,
|
||||
new DateTimeOffset(NormalizeDatabaseTime(player.LastSeenTime)),
|
||||
player.LastSeenAddress,
|
||||
player.LastSeenHWId?.ToImmutableArray());
|
||||
player.LastSeenHWId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -673,11 +679,11 @@ namespace Content.Server.Database
|
|||
/*
|
||||
* CONNECTION LOG
|
||||
*/
|
||||
public abstract Task<int> AddConnectionLogAsync(
|
||||
NetUserId userId,
|
||||
public abstract Task<int> AddConnectionLogAsync(NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId,
|
||||
ImmutableTypedHwid? hwId,
|
||||
float trust,
|
||||
ConnectionDenyReason? denied,
|
||||
int serverId);
|
||||
|
||||
|
|
|
|||
|
|
@ -69,12 +69,14 @@ namespace Content.Server.Database
|
|||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The hardware ID of the user.</param>
|
||||
/// <param name="hwId">The legacy HWID of the user.</param>
|
||||
/// <param name="modernHWIds">The modern HWIDs of the user.</param>
|
||||
/// <returns>The user's latest received un-pardoned ban, or null if none exist.</returns>
|
||||
Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId);
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds);
|
||||
|
||||
/// <summary>
|
||||
/// Looks up an user's ban history.
|
||||
|
|
@ -82,13 +84,15 @@ namespace Content.Server.Database
|
|||
/// </summary>
|
||||
/// <param name="address">The ip address of the user.</param>
|
||||
/// <param name="userId">The id of the user.</param>
|
||||
/// <param name="hwId">The HWId of the user.</param>
|
||||
/// <param name="hwId">The legacy HWId of the user.</param>
|
||||
/// <param name="modernHWIds">The modern HWIDs of the user.</param>
|
||||
/// <param name="includeUnbanned">If true, bans that have been expired or pardoned are also included.</param>
|
||||
/// <returns>The user's ban history.</returns>
|
||||
Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned=true);
|
||||
|
||||
Task AddServerBanAsync(ServerBanDef serverBan);
|
||||
|
|
@ -137,12 +141,14 @@ namespace Content.Server.Database
|
|||
/// <param name="address">The IP address of the user.</param>
|
||||
/// <param name="userId">The NetUserId of the user.</param>
|
||||
/// <param name="hwId">The Hardware Id of the user.</param>
|
||||
/// <param name="modernHWIds">The modern HWIDs of the user.</param>
|
||||
/// <param name="includeUnbanned">Whether expired and pardoned bans are included.</param>
|
||||
/// <returns>The user's role ban history.</returns>
|
||||
Task<List<ServerRoleBanDef>> GetServerRoleBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned = true);
|
||||
|
||||
Task<ServerRoleBanDef> AddServerRoleBanAsync(ServerRoleBanDef serverBan);
|
||||
|
|
@ -180,7 +186,7 @@ namespace Content.Server.Database
|
|||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId);
|
||||
ImmutableTypedHwid? hwId);
|
||||
Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel = default);
|
||||
Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel = default);
|
||||
#endregion
|
||||
|
|
@ -191,7 +197,8 @@ namespace Content.Server.Database
|
|||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId,
|
||||
ImmutableTypedHwid? hwId,
|
||||
float trust,
|
||||
ConnectionDenyReason? denied,
|
||||
int serverId);
|
||||
|
||||
|
|
@ -480,20 +487,22 @@ namespace Content.Server.Database
|
|||
public Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetServerBanAsync(address, userId, hwId));
|
||||
return RunDbCommand(() => _db.GetServerBanAsync(address, userId, hwId, modernHWIds));
|
||||
}
|
||||
|
||||
public Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned=true)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, includeUnbanned));
|
||||
return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, modernHWIds, includeUnbanned));
|
||||
}
|
||||
|
||||
public Task AddServerBanAsync(ServerBanDef serverBan)
|
||||
|
|
@ -537,10 +546,11 @@ namespace Content.Server.Database
|
|||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned = true)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetServerRoleBansAsync(address, userId, hwId, includeUnbanned));
|
||||
return RunDbCommand(() => _db.GetServerRoleBansAsync(address, userId, hwId, modernHWIds, includeUnbanned));
|
||||
}
|
||||
|
||||
public Task<ServerRoleBanDef> AddServerRoleBanAsync(ServerRoleBanDef serverRoleBan)
|
||||
|
|
@ -582,7 +592,7 @@ namespace Content.Server.Database
|
|||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId)
|
||||
ImmutableTypedHwid? hwId)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.UpdatePlayerRecord(userId, userName, address, hwId));
|
||||
|
|
@ -604,12 +614,13 @@ namespace Content.Server.Database
|
|||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId,
|
||||
ImmutableTypedHwid? hwId,
|
||||
float trust,
|
||||
ConnectionDenyReason? denied,
|
||||
int serverId)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.AddConnectionLogAsync(userId, userName, address, hwId, denied, serverId));
|
||||
return RunDbCommand(() => _db.AddConnectionLogAsync(userId, userName, address, hwId, trust, denied, serverId));
|
||||
}
|
||||
|
||||
public Task AddServerBanHitsAsync(int connection, IEnumerable<ServerBanDef> bans)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.IP;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
|
|
@ -73,7 +74,8 @@ namespace Content.Server.Database
|
|||
public override async Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds)
|
||||
{
|
||||
if (address == null && userId == null && hwId == null)
|
||||
{
|
||||
|
|
@ -84,7 +86,7 @@ namespace Content.Server.Database
|
|||
|
||||
var exempt = await GetBanExemptionCore(db, userId);
|
||||
var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value);
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt, newPlayer)
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, modernHWIds, db, includeUnbanned: false, exempt, newPlayer)
|
||||
.OrderByDescending(b => b.BanTime);
|
||||
|
||||
var ban = await query.FirstOrDefaultAsync();
|
||||
|
|
@ -94,7 +96,9 @@ namespace Content.Server.Database
|
|||
|
||||
public override async Task<List<ServerBanDef>> GetServerBansAsync(IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId, bool includeUnbanned)
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned)
|
||||
{
|
||||
if (address == null && userId == null && hwId == null)
|
||||
{
|
||||
|
|
@ -105,7 +109,7 @@ namespace Content.Server.Database
|
|||
|
||||
var exempt = await GetBanExemptionCore(db, userId);
|
||||
var newPlayer = !await db.PgDbContext.Player.AnyAsync(p => p.UserId == userId);
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt, newPlayer);
|
||||
var query = MakeBanLookupQuery(address, userId, hwId, modernHWIds, db, includeUnbanned, exempt, newPlayer);
|
||||
|
||||
var queryBans = await query.ToArrayAsync();
|
||||
var bans = new List<ServerBanDef>(queryBans.Length);
|
||||
|
|
@ -127,6 +131,7 @@ namespace Content.Server.Database
|
|||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
DbGuardImpl db,
|
||||
bool includeUnbanned,
|
||||
ServerBanExemptFlags? exemptFlags,
|
||||
|
|
@ -134,16 +139,11 @@ namespace Content.Server.Database
|
|||
{
|
||||
DebugTools.Assert(!(address == null && userId == null && hwId == null));
|
||||
|
||||
IQueryable<ServerBan>? query = null;
|
||||
|
||||
if (userId is { } uid)
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.PlayerUserId == uid.UserId);
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
var query = MakeBanLookupQualityShared<ServerBan, ServerUnban>(
|
||||
userId,
|
||||
hwId,
|
||||
modernHWIds,
|
||||
db.PgDbContext.Ban);
|
||||
|
||||
if (address != null && !exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP))
|
||||
{
|
||||
|
|
@ -156,15 +156,6 @@ namespace Content.Server.Database
|
|||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (hwId != null && hwId.Value.Length > 0)
|
||||
{
|
||||
var newQ = db.PgDbContext.Ban
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.HWId!.SequenceEqual(hwId.Value.ToArray()));
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
DebugTools.Assert(
|
||||
query != null,
|
||||
"At least one filter item (IP/UserID/HWID) must have been given to make query not null.");
|
||||
|
|
@ -186,6 +177,49 @@ namespace Content.Server.Database
|
|||
return query.Distinct();
|
||||
}
|
||||
|
||||
private static IQueryable<TBan>? MakeBanLookupQualityShared<TBan, TUnban>(
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
DbSet<TBan> set)
|
||||
where TBan : class, IBanCommon<TUnban>
|
||||
where TUnban : class, IUnbanCommon
|
||||
{
|
||||
IQueryable<TBan>? query = null;
|
||||
|
||||
if (userId is { } uid)
|
||||
{
|
||||
var newQ = set
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.PlayerUserId == uid.UserId);
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (hwId != null && hwId.Value.Length > 0)
|
||||
{
|
||||
var newQ = set
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.HWId!.Type == HwidType.Legacy && b.HWId!.Hwid.SequenceEqual(hwId.Value.ToArray()));
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (modernHWIds != null)
|
||||
{
|
||||
foreach (var modernHwid in modernHWIds)
|
||||
{
|
||||
var newQ = set
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.HWId!.Type == HwidType.Modern && b.HWId!.Hwid.SequenceEqual(modernHwid.ToArray()));
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private static ServerBanDef? ConvertBan(ServerBan? ban)
|
||||
{
|
||||
if (ban == null)
|
||||
|
|
@ -211,7 +245,7 @@ namespace Content.Server.Database
|
|||
ban.Id,
|
||||
uid,
|
||||
ban.Address.ToTuple(),
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.HWId,
|
||||
ban.BanTime,
|
||||
ban.ExpirationTime,
|
||||
ban.RoundId,
|
||||
|
|
@ -249,7 +283,7 @@ namespace Content.Server.Database
|
|||
db.PgDbContext.Ban.Add(new ServerBan
|
||||
{
|
||||
Address = serverBan.Address.ToNpgsqlInet(),
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
HWId = serverBan.HWId,
|
||||
Reason = serverBan.Reason,
|
||||
Severity = serverBan.Severity,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
|
|
@ -297,6 +331,7 @@ namespace Content.Server.Database
|
|||
public override async Task<List<ServerRoleBanDef>> GetServerRoleBansAsync(IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned)
|
||||
{
|
||||
if (address == null && userId == null && hwId == null)
|
||||
|
|
@ -306,7 +341,7 @@ namespace Content.Server.Database
|
|||
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
var query = MakeRoleBanLookupQuery(address, userId, hwId, db, includeUnbanned)
|
||||
var query = MakeRoleBanLookupQuery(address, userId, hwId, modernHWIds, db, includeUnbanned)
|
||||
.OrderByDescending(b => b.BanTime);
|
||||
|
||||
return await QueryRoleBans(query);
|
||||
|
|
@ -334,19 +369,15 @@ namespace Content.Server.Database
|
|||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
DbGuardImpl db,
|
||||
bool includeUnbanned)
|
||||
{
|
||||
IQueryable<ServerRoleBan>? query = null;
|
||||
|
||||
if (userId is { } uid)
|
||||
{
|
||||
var newQ = db.PgDbContext.RoleBan
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.PlayerUserId == uid.UserId);
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
var query = MakeBanLookupQualityShared<ServerRoleBan, ServerRoleUnban>(
|
||||
userId,
|
||||
hwId,
|
||||
modernHWIds,
|
||||
db.PgDbContext.RoleBan);
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
|
|
@ -357,15 +388,6 @@ namespace Content.Server.Database
|
|||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (hwId != null && hwId.Value.Length > 0)
|
||||
{
|
||||
var newQ = db.PgDbContext.RoleBan
|
||||
.Include(p => p.Unban)
|
||||
.Where(b => b.HWId!.SequenceEqual(hwId.Value.ToArray()));
|
||||
|
||||
query = query == null ? newQ : query.Union(newQ);
|
||||
}
|
||||
|
||||
if (!includeUnbanned)
|
||||
{
|
||||
query = query?.Where(p =>
|
||||
|
|
@ -402,7 +424,7 @@ namespace Content.Server.Database
|
|||
ban.Id,
|
||||
uid,
|
||||
ban.Address.ToTuple(),
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.HWId,
|
||||
ban.BanTime,
|
||||
ban.ExpirationTime,
|
||||
ban.RoundId,
|
||||
|
|
@ -440,7 +462,7 @@ namespace Content.Server.Database
|
|||
var ban = new ServerRoleBan
|
||||
{
|
||||
Address = serverRoleBan.Address.ToNpgsqlInet(),
|
||||
HWId = serverRoleBan.HWId?.ToArray(),
|
||||
HWId = serverRoleBan.HWId,
|
||||
Reason = serverRoleBan.Reason,
|
||||
Severity = serverRoleBan.Severity,
|
||||
BanningAdmin = serverRoleBan.BanningAdmin?.UserId,
|
||||
|
|
@ -476,7 +498,8 @@ namespace Content.Server.Database
|
|||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId,
|
||||
ImmutableTypedHwid? hwId,
|
||||
float trust,
|
||||
ConnectionDenyReason? denied,
|
||||
int serverId)
|
||||
{
|
||||
|
|
@ -488,9 +511,10 @@ namespace Content.Server.Database
|
|||
Time = DateTime.UtcNow,
|
||||
UserId = userId.UserId,
|
||||
UserName = userName,
|
||||
HWId = hwId.ToArray(),
|
||||
HWId = hwId,
|
||||
Denied = denied,
|
||||
ServerId = serverId
|
||||
ServerId = serverId,
|
||||
Trust = trust,
|
||||
};
|
||||
|
||||
db.PgDbContext.ConnectionLog.Add(connectionLog);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Content.Server.Administration.Logs;
|
|||
using Content.Server.IP;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
|
|
@ -80,22 +81,24 @@ namespace Content.Server.Database
|
|||
public override async Task<ServerBanDef?> GetServerBanAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
return (await GetServerBanQueryAsync(db, address, userId, hwId, includeUnbanned: false)).FirstOrDefault();
|
||||
return (await GetServerBanQueryAsync(db, address, userId, hwId, modernHWIds, includeUnbanned: false)).FirstOrDefault();
|
||||
}
|
||||
|
||||
public override async Task<List<ServerBanDef>> GetServerBansAsync(
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
return (await GetServerBanQueryAsync(db, address, userId, hwId, includeUnbanned)).ToList();
|
||||
return (await GetServerBanQueryAsync(db, address, userId, hwId, modernHWIds, includeUnbanned)).ToList();
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ServerBanDef>> GetServerBanQueryAsync(
|
||||
|
|
@ -103,6 +106,7 @@ namespace Content.Server.Database
|
|||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned)
|
||||
{
|
||||
var exempt = await GetBanExemptionCore(db, userId);
|
||||
|
|
@ -119,6 +123,7 @@ namespace Content.Server.Database
|
|||
UserId = userId,
|
||||
ExemptFlags = exempt ?? default,
|
||||
HWId = hwId,
|
||||
ModernHWIds = modernHWIds,
|
||||
IsNewPlayer = newPlayer,
|
||||
};
|
||||
|
||||
|
|
@ -161,7 +166,7 @@ namespace Content.Server.Database
|
|||
Reason = serverBan.Reason,
|
||||
Severity = serverBan.Severity,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
HWId = serverBan.HWId,
|
||||
BanTime = serverBan.BanTime.UtcDateTime,
|
||||
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
|
||||
RoundId = serverBan.RoundId,
|
||||
|
|
@ -205,6 +210,7 @@ namespace Content.Server.Database
|
|||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds,
|
||||
bool includeUnbanned)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
|
@ -214,7 +220,7 @@ namespace Content.Server.Database
|
|||
var queryBans = await GetAllRoleBans(db.SqliteDbContext, includeUnbanned);
|
||||
|
||||
return queryBans
|
||||
.Where(b => RoleBanMatches(b, address, userId, hwId))
|
||||
.Where(b => RoleBanMatches(b, address, userId, hwId, modernHWIds))
|
||||
.Select(ConvertRoleBan)
|
||||
.ToList()!;
|
||||
}
|
||||
|
|
@ -237,7 +243,8 @@ namespace Content.Server.Database
|
|||
ServerRoleBan ban,
|
||||
IPAddress? address,
|
||||
NetUserId? userId,
|
||||
ImmutableArray<byte>? hwId)
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableArray<ImmutableArray<byte>>? modernHWIds)
|
||||
{
|
||||
if (address != null && ban.Address is not null && address.IsInSubnet(ban.Address.ToTuple().Value))
|
||||
{
|
||||
|
|
@ -249,7 +256,27 @@ namespace Content.Server.Database
|
|||
return true;
|
||||
}
|
||||
|
||||
return hwId is { Length: > 0 } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId);
|
||||
switch (ban.HWId?.Type)
|
||||
{
|
||||
case HwidType.Legacy:
|
||||
if (hwId is { Length: > 0 } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId.Hwid))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case HwidType.Modern:
|
||||
if (modernHWIds != null)
|
||||
{
|
||||
foreach (var modernHWId in modernHWIds)
|
||||
{
|
||||
if (modernHWId.AsSpan().SequenceEqual(ban.HWId.Hwid))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override async Task<ServerRoleBanDef> AddServerRoleBanAsync(ServerRoleBanDef serverBan)
|
||||
|
|
@ -262,7 +289,7 @@ namespace Content.Server.Database
|
|||
Reason = serverBan.Reason,
|
||||
Severity = serverBan.Severity,
|
||||
BanningAdmin = serverBan.BanningAdmin?.UserId,
|
||||
HWId = serverBan.HWId?.ToArray(),
|
||||
HWId = serverBan.HWId,
|
||||
BanTime = serverBan.BanTime.UtcDateTime,
|
||||
ExpirationTime = serverBan.ExpirationTime?.UtcDateTime,
|
||||
RoundId = serverBan.RoundId,
|
||||
|
|
@ -316,7 +343,7 @@ namespace Content.Server.Database
|
|||
ban.Id,
|
||||
uid,
|
||||
ban.Address.ToTuple(),
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.HWId,
|
||||
// SQLite apparently always reads DateTime as unspecified, but we always write as UTC.
|
||||
DateTime.SpecifyKind(ban.BanTime, DateTimeKind.Utc),
|
||||
ban.ExpirationTime == null ? null : DateTime.SpecifyKind(ban.ExpirationTime.Value, DateTimeKind.Utc),
|
||||
|
|
@ -376,7 +403,7 @@ namespace Content.Server.Database
|
|||
ban.Id,
|
||||
uid,
|
||||
ban.Address.ToTuple(),
|
||||
ban.HWId == null ? null : ImmutableArray.Create(ban.HWId),
|
||||
ban.HWId,
|
||||
// SQLite apparently always reads DateTime as unspecified, but we always write as UTC.
|
||||
DateTime.SpecifyKind(ban.BanTime, DateTimeKind.Utc),
|
||||
ban.ExpirationTime == null ? null : DateTime.SpecifyKind(ban.ExpirationTime.Value, DateTimeKind.Utc),
|
||||
|
|
@ -412,7 +439,8 @@ namespace Content.Server.Database
|
|||
NetUserId userId,
|
||||
string userName,
|
||||
IPAddress address,
|
||||
ImmutableArray<byte> hwId,
|
||||
ImmutableTypedHwid? hwId,
|
||||
float trust,
|
||||
ConnectionDenyReason? denied,
|
||||
int serverId)
|
||||
{
|
||||
|
|
@ -424,9 +452,10 @@ namespace Content.Server.Database
|
|||
Time = DateTime.UtcNow,
|
||||
UserId = userId.UserId,
|
||||
UserName = userName,
|
||||
HWId = hwId.ToArray(),
|
||||
HWId = hwId,
|
||||
Denied = denied,
|
||||
ServerId = serverId
|
||||
ServerId = serverId,
|
||||
Trust = trust,
|
||||
};
|
||||
|
||||
db.SqliteDbContext.ConnectionLog.Add(connectionLog);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Shared.Network;
|
||||
|
|
@ -10,7 +9,7 @@ public sealed class ServerRoleBanDef
|
|||
public int? Id { get; }
|
||||
public NetUserId? UserId { get; }
|
||||
public (IPAddress address, int cidrMask)? Address { get; }
|
||||
public ImmutableArray<byte>? HWId { get; }
|
||||
public ImmutableTypedHwid? HWId { get; }
|
||||
|
||||
public DateTimeOffset BanTime { get; }
|
||||
public DateTimeOffset? ExpirationTime { get; }
|
||||
|
|
@ -26,7 +25,7 @@ public sealed class ServerRoleBanDef
|
|||
int? id,
|
||||
NetUserId? userId,
|
||||
(IPAddress, int)? address,
|
||||
ImmutableArray<byte>? hwId,
|
||||
ImmutableTypedHwid? hwId,
|
||||
DateTimeOffset banTime,
|
||||
DateTimeOffset? expirationTime,
|
||||
int? roundId,
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|
|||
activated.TimeLeft -= frameTime;
|
||||
if (activated.TimeLeft <= 0 || !IsPowered(uid, electrified, transform))
|
||||
{
|
||||
_appearance.SetData(uid, ElectrifiedVisuals.IsPowered, false);
|
||||
_appearance.SetData(uid, ElectrifiedVisuals.ShowSparks, false);
|
||||
RemComp<ActivatedElectrifiedComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
|
@ -217,7 +217,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|
|||
return false;
|
||||
|
||||
EnsureComp<ActivatedElectrifiedComponent>(uid);
|
||||
_appearance.SetData(uid, ElectrifiedVisuals.IsPowered, true);
|
||||
_appearance.SetData(uid, ElectrifiedVisuals.ShowSparks, true);
|
||||
|
||||
siemens *= electrified.SiemensCoefficient;
|
||||
if (!DoCommonElectrocutionAttempt(targetUid, uid, ref siemens) || siemens <= 0)
|
||||
|
|
@ -488,15 +488,4 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|
|||
}
|
||||
_audio.PlayPvs(electrified.ShockNoises, targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume));
|
||||
}
|
||||
|
||||
public void SetElectrifiedWireCut(Entity<ElectrifiedComponent> ent, bool value)
|
||||
{
|
||||
if (ent.Comp.IsWireCut == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent.Comp.IsWireCut = value;
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Content.Server.Popups;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind;
|
||||
using Robust.Shared.Console;
|
||||
using Content.Server.GameTicking;
|
||||
|
||||
namespace Content.Server.Ghost
|
||||
{
|
||||
|
|
@ -23,6 +25,14 @@ namespace Content.Server.Ghost
|
|||
return;
|
||||
}
|
||||
|
||||
var gameTicker = _entities.System<GameTicker>();
|
||||
if (!gameTicker.PlayerGameStatuses.TryGetValue(player.UserId, out var playerStatus) ||
|
||||
playerStatus is not PlayerGameStatus.JoinedGame)
|
||||
{
|
||||
shell.WriteLine("ghost-command-error-lobby");
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.AttachedEntity is { Valid: true } frozen &&
|
||||
_entities.HasComponent<AdminFrozenComponent>(frozen))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,8 +18,11 @@ public sealed partial class CableComponent : Component
|
|||
[DataField]
|
||||
public EntProtoId CableDroppedOnCutPrototype = "CableHVStack1";
|
||||
|
||||
/// <summary>
|
||||
/// The tool quality needed to cut the cable. Setting to null prevents cutting.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<ToolQualityPrototype> CuttingQuality = SharedToolSystem.CutQuality;
|
||||
public ProtoId<ToolQualityPrototype>? CuttingQuality = SharedToolSystem.CutQuality;
|
||||
|
||||
/// <summary>
|
||||
/// Checked by <see cref="CablePlacerComponent"/> to determine if there is
|
||||
|
|
|
|||
|
|
@ -35,7 +35,10 @@ public sealed partial class CableSystem : EntitySystem
|
|||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, cable.CuttingDelay, cable.CuttingQuality, new CableCuttingFinishedEvent());
|
||||
if (cable.CuttingQuality != null)
|
||||
{
|
||||
args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, cable.CuttingDelay, cable.CuttingQuality, new CableCuttingFinishedEvent());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCableCut(EntityUid uid, CableComponent cable, DoAfterEvent args)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public sealed partial class PowerWireAction : BaseWireAction
|
|||
[DataField("pulseTimeout")]
|
||||
private int _pulseTimeout = 30;
|
||||
|
||||
private ElectrocutionSystem _electrocutionSystem = default!;
|
||||
private ElectrocutionSystem _electrocution = default!;
|
||||
|
||||
public override object StatusKey { get; } = PowerWireActionKey.Status;
|
||||
|
||||
|
|
@ -105,8 +105,8 @@ public sealed partial class PowerWireAction : BaseWireAction
|
|||
&& !EntityManager.TryGetComponent(used, out electrified))
|
||||
return;
|
||||
|
||||
_electrocutionSystem.SetElectrifiedWireCut((used, electrified), setting);
|
||||
electrified.Enabled = setting;
|
||||
_electrocution.SetElectrifiedWireCut((used, electrified), setting);
|
||||
_electrocution.SetElectrified((used, electrified), setting);
|
||||
}
|
||||
|
||||
/// <returns>false if failed, true otherwise, or if the entity cannot be electrified</returns>
|
||||
|
|
@ -120,7 +120,7 @@ public sealed partial class PowerWireAction : BaseWireAction
|
|||
// always set this to true
|
||||
SetElectrified(wire.Owner, true, electrified);
|
||||
|
||||
var electrifiedAttempt = _electrocutionSystem.TryDoElectrifiedAct(wire.Owner, user);
|
||||
var electrifiedAttempt = _electrocution.TryDoElectrifiedAct(wire.Owner, user);
|
||||
|
||||
// if we were electrified, then return false
|
||||
return !electrifiedAttempt;
|
||||
|
|
@ -161,7 +161,7 @@ public sealed partial class PowerWireAction : BaseWireAction
|
|||
{
|
||||
base.Initialize();
|
||||
|
||||
_electrocutionSystem = EntityManager.System<ElectrocutionSystem>();
|
||||
_electrocution = EntityManager.System<ElectrocutionSystem>();
|
||||
}
|
||||
|
||||
// This should add a wire into the entity's state, whether it be
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ public sealed partial class DungeonJob
|
|||
}
|
||||
|
||||
var tileDef = _prototype.Index(tileProto);
|
||||
data.SpawnGroups.TryGetValue(DungeonDataKey.WallMounts, out var spawnProto);
|
||||
if (!data.SpawnGroups.TryGetValue(DungeonDataKey.WallMounts, out var spawnProto))
|
||||
{
|
||||
// caves can have no walls
|
||||
return;
|
||||
}
|
||||
|
||||
var checkedTiles = new HashSet<Vector2i>();
|
||||
var allExterior = new HashSet<Vector2i>(dungeon.CorridorExteriorTiles);
|
||||
|
|
|
|||
|
|
@ -13,11 +13,12 @@ public sealed class ServerInfoManager
|
|||
private static readonly (CVarDef<string> cVar, string icon, string name)[] Vars =
|
||||
{
|
||||
// @formatter:off
|
||||
(CCVars.InfoLinksDiscord, "discord", "info-link-discord"),
|
||||
(CCVars.InfoLinksForum, "forum", "info-link-forum"),
|
||||
(CCVars.InfoLinksGithub, "github", "info-link-github"),
|
||||
(CCVars.InfoLinksWebsite, "web", "info-link-website"),
|
||||
(CCVars.InfoLinksWiki, "wiki", "info-link-wiki")
|
||||
(CCVars.InfoLinksDiscord, "discord", "info-link-discord"),
|
||||
(CCVars.InfoLinksForum, "forum", "info-link-forum"),
|
||||
(CCVars.InfoLinksGithub, "github", "info-link-github"),
|
||||
(CCVars.InfoLinksWebsite, "web", "info-link-website"),
|
||||
(CCVars.InfoLinksWiki, "wiki", "info-link-wiki"),
|
||||
(CCVars.InfoLinksTelegram, "telegram", "info-link-telegram")
|
||||
// @formatter:on
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -234,12 +234,6 @@ public sealed partial class ShuttleSystem
|
|||
|
||||
if (TryComp<PhysicsComponent>(shuttleUid, out var shuttlePhysics))
|
||||
{
|
||||
// Static physics type is set when station anchor is enabled
|
||||
if (shuttlePhysics.BodyType == BodyType.Static)
|
||||
{
|
||||
reason = Loc.GetString("shuttle-console-static");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Too large to FTL
|
||||
if (FTLMassLimit > 0 && shuttlePhysics.Mass > FTLMassLimit)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
using Content.Server.StationEvents.Events;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Destructible.Thresholds;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.StationEvents.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Greytide Virus event specific configuration
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(GreytideVirusRule))]
|
||||
public sealed partial class GreytideVirusRuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Range from which the severity is randomly picked from.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public MinMax SeverityRange = new(1, 3);
|
||||
|
||||
/// <summary>
|
||||
/// Severity corresponding to the number of access groups affected.
|
||||
/// Will pick randomly from the SeverityRange if not specified.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int? Severity;
|
||||
|
||||
/// <summary>
|
||||
/// Access groups to pick from.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<AccessGroupPrototype>> AccessGroups = new();
|
||||
|
||||
/// <summary>
|
||||
/// Entities with this access level will be ignored.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<AccessLevelPrototype>> Blacklist = new();
|
||||
}
|
||||
96
Content.Server/StationEvents/Events/GreytideVirusRule.cs
Normal file
96
Content.Server/StationEvents/Events/GreytideVirusRule.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.StationEvents.Events;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Greytide Virus event
|
||||
/// This will open and bolt airlocks and unlock lockers from randomly selected access groups.
|
||||
/// </summary>
|
||||
public sealed class GreytideVirusRule : StationEventSystem<GreytideVirusRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly AccessReaderSystem _access = default!;
|
||||
[Dependency] private readonly SharedDoorSystem _door = default!;
|
||||
[Dependency] private readonly LockSystem _lock = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
protected override void Added(EntityUid uid, GreytideVirusRuleComponent virusComp, GameRuleComponent gameRule, GameRuleAddedEvent args)
|
||||
{
|
||||
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
|
||||
return;
|
||||
|
||||
// pick severity randomly from range if not specified otherwise
|
||||
virusComp.Severity ??= virusComp.SeverityRange.Next(_random);
|
||||
virusComp.Severity = Math.Min(virusComp.Severity.Value, virusComp.AccessGroups.Count);
|
||||
|
||||
stationEvent.StartAnnouncement = Loc.GetString("station-event-greytide-virus-start-announcement", ("severity", virusComp.Severity.Value));
|
||||
base.Added(uid, virusComp, gameRule, args);
|
||||
}
|
||||
protected override void Started(EntityUid uid, GreytideVirusRuleComponent virusComp, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, virusComp, gameRule, args);
|
||||
|
||||
if (virusComp.Severity == null)
|
||||
return;
|
||||
|
||||
// pick random access groups
|
||||
var chosen = _random.GetItems(virusComp.AccessGroups, virusComp.Severity.Value, allowDuplicates: false);
|
||||
|
||||
// combine all the selected access groups
|
||||
var accessIds = new HashSet<ProtoId<AccessLevelPrototype>>();
|
||||
foreach (var group in chosen)
|
||||
{
|
||||
if (_prototype.TryIndex(group, out var proto))
|
||||
accessIds.UnionWith(proto.Tags);
|
||||
}
|
||||
|
||||
var firelockQuery = GetEntityQuery<FirelockComponent>();
|
||||
var accessQuery = GetEntityQuery<AccessReaderComponent>();
|
||||
|
||||
var lockQuery = AllEntityQuery<LockComponent>();
|
||||
while (lockQuery.MoveNext(out var lockUid, out var lockComp))
|
||||
{
|
||||
if (!accessQuery.TryComp(lockUid, out var accessComp))
|
||||
continue;
|
||||
|
||||
// check access
|
||||
// the AreAccessTagsAllowed function is a little weird because it technically has support for certain tags to be locked out of opening something
|
||||
// which might have unintened side effects (see the comments in the function itself)
|
||||
// but no one uses that yet, so it is fine for now
|
||||
if (!_access.AreAccessTagsAllowed(accessIds, accessComp) || _access.AreAccessTagsAllowed(virusComp.Blacklist, accessComp))
|
||||
continue;
|
||||
|
||||
// open lockers
|
||||
_lock.Unlock(lockUid, null, lockComp);
|
||||
}
|
||||
|
||||
var airlockQuery = AllEntityQuery<AirlockComponent, DoorComponent>();
|
||||
while (airlockQuery.MoveNext(out var airlockUid, out var airlockComp, out var doorComp))
|
||||
{
|
||||
// don't space everything
|
||||
if (firelockQuery.HasComp(airlockUid))
|
||||
continue;
|
||||
|
||||
// use the access reader from the door electronics if they exist
|
||||
if (!_access.GetMainAccessReader(airlockUid, out var accessComp))
|
||||
continue;
|
||||
|
||||
// check access
|
||||
if (!_access.AreAccessTagsAllowed(accessIds, accessComp) || _access.AreAccessTagsAllowed(virusComp.Blacklist, accessComp))
|
||||
continue;
|
||||
|
||||
// open and bolt airlocks
|
||||
_door.TryOpenAndBolt(airlockUid, doorComp, airlockComp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ namespace Content.Server.VendingMachines
|
|||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _light = default!;
|
||||
|
||||
private const float WallVendEjectDistanceFromWall = 1f;
|
||||
|
||||
|
|
@ -334,6 +335,12 @@ namespace Content.Server.VendingMachines
|
|||
finalState = VendingMachineVisualState.Off;
|
||||
}
|
||||
|
||||
if (_light.TryGetLight(uid, out var pointlight))
|
||||
{
|
||||
var lightState = finalState != VendingMachineVisualState.Broken && finalState != VendingMachineVisualState.Off;
|
||||
_light.SetEnabled(uid, lightState, pointlight);
|
||||
}
|
||||
|
||||
_appearanceSystem.SetData(uid, VendingMachineVisuals.VisualState, finalState);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ public abstract partial class ComponentWireAction<TComponent> : BaseWireAction w
|
|||
public override bool Cut(EntityUid user, Wire wire)
|
||||
{
|
||||
base.Cut(user, wire);
|
||||
return EntityManager.TryGetComponent(wire.Owner, out TComponent? component) && Cut(user, wire, component);
|
||||
// if the entity doesn't exist, we need to return true otherwise the wire sprite is never updated
|
||||
return EntityManager.TryGetComponent(wire.Owner, out TComponent? component) ? Cut(user, wire, component) : true;
|
||||
}
|
||||
|
||||
public override bool Mend(EntityUid user, Wire wire)
|
||||
{
|
||||
base.Mend(user, wire);
|
||||
return EntityManager.TryGetComponent(wire.Owner, out TComponent? component) && Mend(user, wire, component);
|
||||
return EntityManager.TryGetComponent(wire.Owner, out TComponent? component) ? Mend(user, wire, component) : true;
|
||||
}
|
||||
|
||||
public override void Pulse(EntityUid user, Wire wire)
|
||||
|
|
|
|||
64
Content.Shared.Database/TypedHwid.cs
Normal file
64
Content.Shared.Database/TypedHwid.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Content.Shared.Database;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a raw HWID value together with its type.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class ImmutableTypedHwid(ImmutableArray<byte> hwid, HwidType type)
|
||||
{
|
||||
public readonly ImmutableArray<byte> Hwid = hwid;
|
||||
public readonly HwidType Type = type;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var b64 = Convert.ToBase64String(Hwid.AsSpan());
|
||||
return Type == HwidType.Modern ? $"V2-{b64}" : b64;
|
||||
}
|
||||
|
||||
public static bool TryParse(string value, [NotNullWhen(true)] out ImmutableTypedHwid? hwid)
|
||||
{
|
||||
var type = HwidType.Legacy;
|
||||
if (value.StartsWith("V2-", StringComparison.Ordinal))
|
||||
{
|
||||
value = value["V2-".Length..];
|
||||
type = HwidType.Modern;
|
||||
}
|
||||
|
||||
var array = new byte[GetBase64ByteLength(value)];
|
||||
if (!Convert.TryFromBase64String(value, array, out _))
|
||||
{
|
||||
hwid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReSharper disable once UseCollectionExpression
|
||||
// Do not use collection expression, C# compiler is weird and it fails sandbox.
|
||||
hwid = new ImmutableTypedHwid(ImmutableArray.Create(array), type);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int GetBase64ByteLength(string value)
|
||||
{
|
||||
// Why is .NET like this man wtf.
|
||||
return 3 * (value.Length / 4) - value.TakeLast(2).Count(c => c == '=');
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents different types of HWIDs as exposed by the engine.
|
||||
/// </summary>
|
||||
public enum HwidType
|
||||
{
|
||||
/// <summary>
|
||||
/// The legacy HWID system. Should only be used for checking old existing database bans.
|
||||
/// </summary>
|
||||
Legacy = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The modern HWID system.
|
||||
/// </summary>
|
||||
Modern = 1,
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ public static class BanPanelEuiStateMsg
|
|||
{
|
||||
public string? Player { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
public byte[]? Hwid { get; set; }
|
||||
public ImmutableTypedHwid? Hwid { get; set; }
|
||||
public uint Minutes { get; set; }
|
||||
public string Reason { get; set; }
|
||||
public NoteSeverity Severity { get; set; }
|
||||
|
|
@ -34,7 +34,7 @@ public static class BanPanelEuiStateMsg
|
|||
public bool UseLastHwid { get; set; }
|
||||
public bool Erase { get; set; }
|
||||
|
||||
public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, byte[]? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles, bool erase)
|
||||
public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, ImmutableTypedHwid? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles, bool erase)
|
||||
{
|
||||
Player = player;
|
||||
IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}";
|
||||
|
|
|
|||
|
|
@ -51,4 +51,10 @@ public sealed partial class CCVars
|
|||
/// </summary>
|
||||
public static readonly CVarDef<string> InfoLinksAppeal =
|
||||
CVarDef.Create("infolinks.appeal", "", CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Link to Telegram channel to show in the launcher.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> InfoLinksTelegram =
|
||||
CVarDef.Create("infolinks.telegram", "", CVar.SERVER | CVar.REPLICATED);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ namespace Content.Shared.CCVar;
|
|||
|
||||
public sealed partial class CCVars
|
||||
{
|
||||
/// <summary>
|
||||
/// Delay for auto-orientation. Used for people arriving via arrivals.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<double> AutoOrientDelay =
|
||||
CVarDef.Create("shuttle.auto_orient_delay", 2.0, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// If true then the camera will match the grid / map and is unchangeable.
|
||||
/// - When traversing grids it will snap to 0 degrees rotation.
|
||||
|
|
|
|||
|
|
@ -151,6 +151,10 @@ public sealed partial class ClimbSystem : VirtualController
|
|||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// If already climbing then don't show outlines.
|
||||
if (TryComp(args.Dragged, out ClimbingComponent? climbing) && climbing.IsClimbing)
|
||||
return;
|
||||
|
||||
var canVault = args.User == args.Dragged
|
||||
? CanVault(component, args.User, uid, out _)
|
||||
: CanVault(component, args.User, args.Dragged, uid, out _);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public sealed partial class DoorComponent : Component
|
|||
/// <summary>
|
||||
/// When the door is active, this is the time when the state will next update.
|
||||
/// </summary>
|
||||
[AutoNetworkedField]
|
||||
[AutoNetworkedField, ViewVariables]
|
||||
public TimeSpan? NextStateChange;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,19 @@ namespace Content.Shared.Doors
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the door's bolt status was changed.
|
||||
/// </summary>
|
||||
public sealed class DoorBoltsChangedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly bool BoltsDown;
|
||||
|
||||
public DoorBoltsChangedEvent(bool boltsDown)
|
||||
{
|
||||
BoltsDown = boltsDown;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the door is determining whether it is able to open.
|
||||
/// Cancel to stop the door from being opened.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public abstract class SharedAirlockSystem : EntitySystem
|
|||
|
||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
|
||||
SubscribeLocalEvent<AirlockComponent, DoorStateChangedEvent>(OnStateChanged);
|
||||
SubscribeLocalEvent<AirlockComponent, DoorBoltsChangedEvent>(OnBoltsChanged);
|
||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorDeniedEvent>(OnBeforeDoorDenied);
|
||||
SubscribeLocalEvent<AirlockComponent, GetPryTimeModifierEvent>(OnGetPryMod);
|
||||
|
|
@ -70,6 +71,13 @@ public abstract class SharedAirlockSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnBoltsChanged(EntityUid uid, AirlockComponent component, DoorBoltsChangedEvent args)
|
||||
{
|
||||
// If unbolted, reset the auto close timer
|
||||
if (!args.BoltsDown)
|
||||
UpdateAutoClose(uid, component);
|
||||
}
|
||||
|
||||
private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args)
|
||||
{
|
||||
if (!CanChangeState(uid, component))
|
||||
|
|
@ -145,7 +153,7 @@ public abstract class SharedAirlockSystem : EntitySystem
|
|||
ent.Comp.EmergencyAccess = value;
|
||||
Dirty(ent, ent.Comp); // This only runs on the server apparently so we need this.
|
||||
UpdateEmergencyLightStatus(ent, ent.Comp);
|
||||
|
||||
|
||||
var sound = ent.Comp.EmergencyAccess ? ent.Comp.EmergencyOnSound : ent.Comp.EmergencyOffSound;
|
||||
if (predicted)
|
||||
Audio.PlayPredicted(sound, ent, user: user);
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ public abstract partial class SharedDoorSystem
|
|||
Dirty(ent, ent.Comp);
|
||||
UpdateBoltLightStatus(ent);
|
||||
|
||||
// used to reset the auto-close timer after unbolting
|
||||
var ev = new DoorBoltsChangedEvent(value);
|
||||
RaiseLocalEvent(ent.Owner, ev);
|
||||
|
||||
var sound = value ? ent.Comp.BoltDownSound : ent.Comp.BoltUpSound;
|
||||
if (predicted)
|
||||
Audio.PlayPredicted(sound, ent, user: user);
|
||||
|
|
|
|||
|
|
@ -400,6 +400,25 @@ public abstract partial class SharedDoorSystem : EntitySystem
|
|||
Dirty(uid, door);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens and then bolts a door.
|
||||
/// Different from emagging this does not remove the access reader, so it can be repaired by simply unbolting the door.
|
||||
/// </summary>
|
||||
public bool TryOpenAndBolt(EntityUid uid, DoorComponent? door = null, AirlockComponent? airlock = null)
|
||||
{
|
||||
if (!Resolve(uid, ref door, ref airlock))
|
||||
return false;
|
||||
|
||||
if (IsBolted(uid) || !airlock.Powered || door.State != DoorState.Closed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetState(uid, DoorState.Emagging, door);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Closing
|
||||
|
|
@ -465,17 +484,17 @@ public abstract partial class SharedDoorSystem : EntitySystem
|
|||
if (!Resolve(uid, ref door, ref physics))
|
||||
return false;
|
||||
|
||||
door.Partial = true;
|
||||
|
||||
// Make sure no entity walked into the airlock when it started closing.
|
||||
if (!CanClose(uid, door))
|
||||
{
|
||||
door.NextStateChange = GameTiming.CurTime + door.OpenTimeTwo;
|
||||
door.State = DoorState.Opening;
|
||||
AppearanceSystem.SetData(uid, DoorVisuals.State, DoorState.Opening);
|
||||
door.State = DoorState.Open;
|
||||
AppearanceSystem.SetData(uid, DoorVisuals.State, DoorState.Open);
|
||||
Dirty(uid, door);
|
||||
return false;
|
||||
}
|
||||
|
||||
door.Partial = true;
|
||||
SetCollidable(uid, true, door, physics);
|
||||
door.NextStateChange = GameTiming.CurTime + door.CloseTimeTwo;
|
||||
Dirty(uid, door);
|
||||
|
|
@ -709,6 +728,8 @@ public abstract partial class SharedDoorSystem : EntitySystem
|
|||
}
|
||||
|
||||
door.NextStateChange = GameTiming.CurTime + delay.Value;
|
||||
Dirty(uid, door);
|
||||
|
||||
_activeDoors.Add((uid, door));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,33 +74,38 @@ namespace Content.Shared.DrawDepth
|
|||
/// </summary>
|
||||
Items = DrawDepthTag.Default + 3,
|
||||
|
||||
Mobs = DrawDepthTag.Default + 4,
|
||||
|
||||
OverMobs = DrawDepthTag.Default + 5,
|
||||
/// <summary>
|
||||
/// Stuff that should be drawn below mobs, but on top of items. Like muzzle flash.
|
||||
/// </summary>
|
||||
BelowMobs = DrawDepthTag.Default + 4,
|
||||
|
||||
Doors = DrawDepthTag.Default + 6,
|
||||
Mobs = DrawDepthTag.Default + 5,
|
||||
|
||||
OverMobs = DrawDepthTag.Default + 6,
|
||||
|
||||
Doors = DrawDepthTag.Default + 7,
|
||||
|
||||
/// <summary>
|
||||
/// Blast doors and shutters which go over the usual doors.
|
||||
/// </summary>
|
||||
BlastDoors = DrawDepthTag.Default + 7,
|
||||
BlastDoors = DrawDepthTag.Default + 8,
|
||||
|
||||
/// <summary>
|
||||
/// Stuff that needs to draw over most things, but not effects, like Kudzu.
|
||||
/// </summary>
|
||||
Overdoors = DrawDepthTag.Default + 8,
|
||||
Overdoors = DrawDepthTag.Default + 9,
|
||||
|
||||
/// <summary>
|
||||
/// Explosions, fire, melee swings. Whatever.
|
||||
/// </summary>
|
||||
Effects = DrawDepthTag.Default + 9,
|
||||
Effects = DrawDepthTag.Default + 10,
|
||||
|
||||
Ghosts = DrawDepthTag.Default + 10,
|
||||
Ghosts = DrawDepthTag.Default + 11,
|
||||
|
||||
/// <summary>
|
||||
/// Use this selectively if it absolutely needs to be drawn above (almost) everything else. Examples include
|
||||
/// the pointing arrow, the drag & drop ghost-entity, and some debug tools.
|
||||
/// </summary>
|
||||
Overlays = DrawDepthTag.Default + 11,
|
||||
Overlays = DrawDepthTag.Default + 12,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Content.Shared.Electrocution;
|
||||
|
||||
/// <summary>
|
||||
/// Handles toggling sprite layers for the electrocution HUD to show if an entity with the ElectrifiedComponent is electrified.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ElectrocutionHUDVisualsComponent : Component;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Electrocution;
|
||||
|
||||
/// <summary>
|
||||
/// Allow an entity to see the Electrocution HUD showing electrocuted doors.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ShowElectrocutionHUDComponent : Component;
|
||||
|
|
@ -5,11 +5,13 @@ namespace Content.Shared.Electrocution;
|
|||
[Serializable, NetSerializable]
|
||||
public enum ElectrifiedLayers : byte
|
||||
{
|
||||
Powered
|
||||
Sparks,
|
||||
HUD,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ElectrifiedVisuals : byte
|
||||
{
|
||||
IsPowered
|
||||
ShowSparks, // only shown when zapping someone, deactivated after a short time
|
||||
IsElectrified, // if the entity is electrified or not, used for the AI HUD
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ namespace Content.Shared.Electrocution
|
|||
{
|
||||
public abstract class SharedElectrocutionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
@ -35,6 +37,19 @@ namespace Content.Shared.Electrocution
|
|||
|
||||
ent.Comp.Enabled = value;
|
||||
Dirty(ent, ent.Comp);
|
||||
|
||||
_appearance.SetData(ent.Owner, ElectrifiedVisuals.IsElectrified, value);
|
||||
}
|
||||
|
||||
public void SetElectrifiedWireCut(Entity<ElectrifiedComponent> ent, bool value)
|
||||
{
|
||||
if (ent.Comp.IsWireCut == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent.Comp.IsWireCut = value;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <param name="uid">Entity being electrocuted.</param>
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ namespace Content.Shared.Movement.Components;
|
|||
/// <summary>
|
||||
/// Automatically rotates eye upon grid traversals.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||
public sealed partial class AutoOrientComponent : Component
|
||||
{
|
||||
|
||||
[DataField, AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan? NextChange;
|
||||
}
|
||||
|
|
|
|||
51
Content.Shared/Movement/Systems/AutoOrientSystem.cs
Normal file
51
Content.Shared/Movement/Systems/AutoOrientSystem.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Movement.Systems;
|
||||
|
||||
public sealed class AutoOrientSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedMoverController _mover = default!;
|
||||
|
||||
private TimeSpan _delay = TimeSpan.Zero;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<AutoOrientComponent, EntParentChangedMessage>(OnEntParentChanged);
|
||||
|
||||
Subs.CVar(_cfgManager, CCVars.AutoOrientDelay, OnAutoOrient, true);
|
||||
}
|
||||
|
||||
private void OnAutoOrient(double obj)
|
||||
{
|
||||
_delay = TimeSpan.FromSeconds(obj);
|
||||
}
|
||||
|
||||
private void OnEntParentChanged(Entity<AutoOrientComponent> ent, ref EntParentChangedMessage args)
|
||||
{
|
||||
ent.Comp.NextChange = _timing.CurTime + _delay;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<AutoOrientComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (comp.NextChange <= _timing.CurTime)
|
||||
{
|
||||
comp.NextChange = null;
|
||||
Dirty(uid, comp);
|
||||
_mover.ResetCamera(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,8 +57,6 @@ namespace Content.Shared.Movement.Systems
|
|||
SubscribeLocalEvent<InputMoverComponent, ComponentHandleState>(OnMoverHandleState);
|
||||
SubscribeLocalEvent<InputMoverComponent, EntParentChangedMessage>(OnInputParentChange);
|
||||
|
||||
SubscribeLocalEvent<AutoOrientComponent, EntParentChangedMessage>(OnAutoParentChange);
|
||||
|
||||
SubscribeLocalEvent<FollowedComponent, EntParentChangedMessage>(OnFollowedParentChange);
|
||||
|
||||
Subs.CVar(_configManager, CCVars.CameraRotationLocked, obj => CameraRotationLocked = obj, true);
|
||||
|
|
@ -146,11 +144,6 @@ namespace Content.Shared.Movement.Systems
|
|||
|
||||
protected virtual void HandleShuttleInput(EntityUid uid, ShuttleButtons button, ushort subTick, bool state) {}
|
||||
|
||||
private void OnAutoParentChange(Entity<AutoOrientComponent> entity, ref EntParentChangedMessage args)
|
||||
{
|
||||
ResetCamera(entity.Owner);
|
||||
}
|
||||
|
||||
public void RotateCamera(EntityUid uid, Angle angle)
|
||||
{
|
||||
if (CameraRotationLocked || !MoverQuery.TryGetComponent(uid, out var mover))
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public abstract partial class SharedStationAiSystem
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to bolt door. If wire was cut (AI) or its not powered - notifies AI and does nothing.
|
||||
/// Attempts to toggle the door's emergency access. If wire was cut (AI) or its not powered - notifies AI and does nothing.
|
||||
/// </summary>
|
||||
private void OnAirlockEmergencyAccess(EntityUid ent, AirlockComponent component, StationAiEmergencyAccessEvent args)
|
||||
{
|
||||
|
|
@ -48,7 +48,7 @@ public abstract partial class SharedStationAiSystem
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to bolt door. If wire was cut (AI or for one of power-wires) or its not powered - notifies AI and does nothing.
|
||||
/// Attempts to electrify the door. If wire was cut (AI or for one of power-wires) or its not powered - notifies AI and does nothing.
|
||||
/// </summary>
|
||||
private void OnElectrified(EntityUid ent, ElectrifiedComponent component, StationAiElectrifiedEvent args)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -394,6 +394,9 @@ public abstract partial class SharedStationAiSystem : EntitySystem
|
|||
|
||||
private void OnAiInsert(Entity<StationAiCoreComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != StationAiCoreComponent.Container)
|
||||
return;
|
||||
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.Linq;
|
|||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Storage;
|
||||
|
|
@ -145,27 +146,23 @@ public abstract class SharedStationSpawningSystem : EntitySystem
|
|||
if (startingGear.Storage.Count > 0)
|
||||
{
|
||||
var coords = _xformSystem.GetMapCoordinates(entity);
|
||||
var ents = new ValueList<EntityUid>();
|
||||
_inventoryQuery.TryComp(entity, out var inventoryComp);
|
||||
|
||||
foreach (var (slot, entProtos) in startingGear.Storage)
|
||||
foreach (var (slotName, entProtos) in startingGear.Storage)
|
||||
{
|
||||
ents.Clear();
|
||||
if (entProtos.Count == 0)
|
||||
if (entProtos == null || entProtos.Count == 0)
|
||||
continue;
|
||||
|
||||
if (inventoryComp != null &&
|
||||
InventorySystem.TryGetSlotEntity(entity, slot, out var slotEnt, inventoryComponent: inventoryComp) &&
|
||||
InventorySystem.TryGetSlotEntity(entity, slotName, out var slotEnt, inventoryComponent: inventoryComp) &&
|
||||
_storageQuery.TryComp(slotEnt, out var storage))
|
||||
{
|
||||
foreach (var ent in entProtos)
|
||||
{
|
||||
ents.Add(Spawn(ent, coords));
|
||||
}
|
||||
|
||||
foreach (var ent in ents)
|
||||
foreach (var entProto in entProtos)
|
||||
{
|
||||
_storage.Insert(slotEnt.Value, ent, out _, storageComp: storage, playSound: false);
|
||||
var spawnedEntity = Spawn(entProto, coords);
|
||||
|
||||
_storage.Insert(slotEnt.Value, spawnedEntity, out _, storageComp: storage, playSound: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,154 +1,4 @@
|
|||
Entries:
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- message: Legends tell of horrifying Goliaths that roam the mining asteroid.
|
||||
type: Add
|
||||
id: 7137
|
||||
time: '2024-08-18T16:22:36.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30839
|
||||
- author: Beck Thompson
|
||||
changes:
|
||||
- message: Cutting food now moves the sliced pieces a small amount!
|
||||
type: Tweak
|
||||
id: 7138
|
||||
time: '2024-08-18T21:18:20.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31166
|
||||
- author: lzk228
|
||||
changes:
|
||||
- message: Pizza and pizza box now have 3x2 size in inventory.
|
||||
type: Tweak
|
||||
- message: Pizze box is 4x2 inside and always will have a pizza with a knife inside.
|
||||
type: Tweak
|
||||
- message: Pizza box have whitelist for utensils and pizza.
|
||||
type: Tweak
|
||||
id: 7139
|
||||
time: '2024-08-18T21:55:42.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31171
|
||||
- author: Deatherd
|
||||
changes:
|
||||
- message: Sharks Go RAWR!
|
||||
type: Tweak
|
||||
id: 7140
|
||||
time: '2024-08-18T22:18:07.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31142
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Fixed the radiation collector warning light thresholds.
|
||||
type: Fix
|
||||
id: 7141
|
||||
time: '2024-08-18T22:25:02.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31175
|
||||
- author: Potato1234_x
|
||||
changes:
|
||||
- message: Added tea plants that can be dried and ground to make tea powder which
|
||||
can then be used to make tea.
|
||||
type: Add
|
||||
- message: Added blue pumpkins. Currently useless but recipe uses are coming soon.
|
||||
type: Add
|
||||
id: 7142
|
||||
time: '2024-08-18T22:28:18.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/25092
|
||||
- author: deltanedas
|
||||
changes:
|
||||
- message: Added Memory Cells for storing logic signals persistently.
|
||||
type: Add
|
||||
id: 7143
|
||||
time: '2024-08-18T22:34:43.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/24983
|
||||
- author: Psychpsyo
|
||||
changes:
|
||||
- message: The random sentience event is back and can no longer pick things that
|
||||
aren't even on the station.
|
||||
type: Add
|
||||
id: 7144
|
||||
time: '2024-08-18T23:41:12.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29123
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- message: Ore can no longer be destroyed by explosions. Happy blast mining.
|
||||
type: Tweak
|
||||
id: 7145
|
||||
time: '2024-08-19T01:55:49.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31182
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Mobs without hands can no longer toggle other players' suit pieces.
|
||||
type: Fix
|
||||
id: 7146
|
||||
time: '2024-08-19T02:41:27.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31152
|
||||
- author: Goldminermac
|
||||
changes:
|
||||
- message: Chocolate-chip and blueberry pancakes can now be in stacks of up to nine
|
||||
for consistency.
|
||||
type: Fix
|
||||
id: 7147
|
||||
time: '2024-08-19T02:42:58.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31123
|
||||
- author: tosatur
|
||||
changes:
|
||||
- message: Made hydroponics alert light more orange
|
||||
type: Tweak
|
||||
id: 7148
|
||||
time: '2024-08-19T02:48:47.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31088
|
||||
- author: redmushie
|
||||
changes:
|
||||
- message: News management console now checks for Service ID card access instead
|
||||
of the manifest
|
||||
type: Fix
|
||||
id: 7149
|
||||
time: '2024-08-19T02:55:44.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31160
|
||||
- author: Moomoobeef
|
||||
changes:
|
||||
- message: Added pitchers for the chef who wants to serve beverages too.
|
||||
type: Add
|
||||
id: 7150
|
||||
time: '2024-08-19T03:01:26.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31105
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- message: Space carp and Sharkminnows now drop teeth when butchered.
|
||||
type: Add
|
||||
- message: Added new bounties for carp and shark teeth.
|
||||
type: Add
|
||||
id: 7151
|
||||
time: '2024-08-19T03:04:59.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31070
|
||||
- author: to4no_fix
|
||||
changes:
|
||||
- message: Now it takes 5 seconds to take off or put on a muzzle
|
||||
type: Tweak
|
||||
- message: Now it takes 5 seconds to take off or put on a blindfold
|
||||
type: Tweak
|
||||
- message: Added a recipe for producing a straitjacket, it opens when researching
|
||||
the Special Means technology, it can be produced at the security techfab
|
||||
type: Add
|
||||
id: 7152
|
||||
time: '2024-08-19T03:05:25.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31095
|
||||
- author: Magicalus
|
||||
changes:
|
||||
- message: Suit sensors, borgs, and PDAs can no longer be saved to device-lists.
|
||||
type: Tweak
|
||||
id: 7153
|
||||
time: '2024-08-19T03:13:04.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30997
|
||||
- author: UBlueberry
|
||||
changes:
|
||||
- message: The guidebook entries for all antagonists have been revised.
|
||||
type: Tweak
|
||||
id: 7154
|
||||
time: '2024-08-19T03:16:05.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31075
|
||||
- author: TheWaffleJesus
|
||||
changes:
|
||||
- message: ERT Chaplains now have blessings to use their bible.
|
||||
type: Fix
|
||||
id: 7155
|
||||
time: '2024-08-19T03:19:19.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30993
|
||||
- author: DieselMohawk
|
||||
changes:
|
||||
- message: Reshaped the Security Helmet
|
||||
|
|
@ -3933,3 +3783,146 @@
|
|||
id: 7636
|
||||
time: '2024-11-22T02:56:05.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31076
|
||||
- author: chromiumboy
|
||||
changes:
|
||||
- message: Added the gas pipe sensor. These sensors monitor the mixture of gases
|
||||
passing through their pipe sub-network and report this information to any connected
|
||||
air alarms
|
||||
type: Add
|
||||
id: 7637
|
||||
time: '2024-11-22T03:46:10.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33128
|
||||
- author: IProduceWidgets
|
||||
changes:
|
||||
- message: The terminal is more tamper proof.
|
||||
type: Fix
|
||||
id: 7638
|
||||
time: '2024-11-22T22:50:41.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33281
|
||||
- author: MissKay1994
|
||||
changes:
|
||||
- message: The salvage vendor now has enough equipment for everyone
|
||||
type: Tweak
|
||||
id: 7639
|
||||
time: '2024-11-23T02:53:48.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33437
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: The AI and observers can now see if doors are electrified.
|
||||
type: Add
|
||||
id: 7640
|
||||
time: '2024-11-23T06:37:15.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33466
|
||||
- author: Winkarst-cpu
|
||||
changes:
|
||||
- message: Now submit button in admin notes becomes disabled on switching type back
|
||||
to note.
|
||||
type: Fix
|
||||
id: 7641
|
||||
time: '2024-11-23T06:41:28.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33456
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- message: Fix airlock animations mispredicting if the closing animation is interrupted,
|
||||
e.g. via walking into it.
|
||||
type: Fix
|
||||
id: 7642
|
||||
time: '2024-11-23T09:31:08.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33481
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- message: Drag-drop outline no longer shows the vaulting outlines if you're vaulting.
|
||||
type: Tweak
|
||||
id: 7643
|
||||
time: '2024-11-23T11:19:59.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33477
|
||||
- author: goet
|
||||
changes:
|
||||
- message: Useless wires some vending machines have can be cut now.
|
||||
type: Fix
|
||||
id: 7644
|
||||
time: '2024-11-23T11:41:37.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32447
|
||||
- author: IProduceWidgets
|
||||
changes:
|
||||
- message: Arrivals shuttle is more tamper proof.
|
||||
type: Fix
|
||||
id: 7645
|
||||
time: '2024-11-23T15:14:13.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33284
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- message: The auto-orientation when showing up on the arrivals shuttle now has
|
||||
a delay to it.
|
||||
type: Tweak
|
||||
id: 7646
|
||||
time: '2024-11-23T16:52:58.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33479
|
||||
- author: Winkarst-cpu
|
||||
changes:
|
||||
- message: Now muzzle flashes are displayed below mobs.
|
||||
type: Tweak
|
||||
id: 7647
|
||||
time: '2024-11-24T04:20:00.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33465
|
||||
- author: MilenVolf
|
||||
changes:
|
||||
- message: Expeditions can now be completed prematurely again by making an FTL jump.
|
||||
type: Fix
|
||||
id: 7648
|
||||
time: '2024-11-24T08:11:47.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33507
|
||||
- author: deltanedas
|
||||
changes:
|
||||
- message: Fixed expeditions on cave planets not having any ore.
|
||||
type: Fix
|
||||
id: 7649
|
||||
time: '2024-11-24T08:49:31.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32890
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Fixed doors not auto-closing correctly after being unbolted in an open
|
||||
state.
|
||||
type: Fix
|
||||
id: 7650
|
||||
time: '2024-11-25T04:26:54.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33524
|
||||
- author: Schrodinger71
|
||||
changes:
|
||||
- message: Fixed a bug letting players type "ghost" in the console and then see
|
||||
the whole chat while being in the lobby.
|
||||
type: Fix
|
||||
id: 7651
|
||||
time: '2024-11-25T07:20:32.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33529
|
||||
- author: Minemoder
|
||||
changes:
|
||||
- message: The shark plushie now goes rawr when hitting something or being thrown.
|
||||
type: Tweak
|
||||
id: 7652
|
||||
time: '2024-11-25T12:23:57.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33540
|
||||
- author: red15
|
||||
changes:
|
||||
- message: Vending machine lights turns off when broken.
|
||||
type: Fix
|
||||
id: 7653
|
||||
time: '2024-11-25T12:35:14.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33513
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- message: Adjusted the top menu on the separated game screen. The buttons will
|
||||
now form multiple rows and no longer overflow into the viewport.
|
||||
type: Tweak
|
||||
id: 7654
|
||||
time: '2024-11-26T00:59:35.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33047
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Added the greytide virus station event. It will bolt open all doors in
|
||||
a few randomly chosen departments and unlock lockers with the corresponding
|
||||
access.
|
||||
type: Add
|
||||
id: 7655
|
||||
time: '2024-11-26T13:50:20.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33547
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,5 @@
|
|||
gas-pipe-sensor-distribution-loop = Distribution loop
|
||||
gas-pipe-sensor-waste-loop = Waste loop
|
||||
gas-pipe-sensor-mixed-air = Mixed air
|
||||
gas-pipe-sensor-teg-hot-loop = TEG hot loop
|
||||
gas-pipe-sensor-teg-cold-loop = TEG cold loop
|
||||
|
|
@ -3,3 +3,4 @@ ghost-command-help-text = The ghost command turns you into a ghost and makes the
|
|||
Please note that you cannot return to your character's body after ghosting.
|
||||
ghost-command-no-session = You have no session, you can't ghost.
|
||||
ghost-command-denied = You cannot ghost right now.
|
||||
ghost-command-error-lobby = You can't ghost right now. You are not in the game!
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ server-info-discord-button = Discord
|
|||
server-info-website-button = Website
|
||||
server-info-wiki-button = Wiki
|
||||
server-info-forum-button = Forum
|
||||
server-info-telegram-button = Telegram
|
||||
server-info-report-button = Report Bugs
|
||||
server-info-credits-button = Credits
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ info-link-forum = Forum
|
|||
info-link-github = GitHub
|
||||
info-link-website = Website
|
||||
info-link-wiki = Wiki
|
||||
info-link-telegram = Telegram
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ shuttle-pilot-end = Stopped piloting
|
|||
shuttle-console-in-ftl = Currently in FTL
|
||||
shuttle-console-mass = Too large to FTL
|
||||
shuttle-console-prevent = You are unable to pilot this ship
|
||||
shuttle-console-static = Grid is static
|
||||
|
||||
# NAV
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
station-event-greytide-virus-start-announcement = Gr3y.T1d3 virus detected in the station's secure locking encryption subroutines. Severity level of { $severity }. Recommend station AI involvement.
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -154,7 +154,7 @@
|
|||
sprite: Objects/Specific/Service/vending_machine_restock.rsi
|
||||
state: base
|
||||
product: CrateVendingMachineRestockSalvageEquipmentFilled
|
||||
cost: 1000
|
||||
cost: 1500
|
||||
category: cargoproduct-category-name-engineering
|
||||
group: market
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
- id: ClothingBeltSecurityFilled
|
||||
- id: Flash
|
||||
- id: ClothingEyesGlassesSecurity
|
||||
- id: ClothingHeadsetAltSecurity
|
||||
- id: ClothingHandsGlovesCombat
|
||||
- id: ClothingShoesBootsJack
|
||||
- id: ClothingOuterCoatWarden
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
- id: ClothingBeltSecurityFilled
|
||||
- id: Flash
|
||||
- id: ClothingEyesGlassesSecurity
|
||||
- id: ClothingHeadsetAltSecurity
|
||||
- id: ClothingHandsGlovesCombat
|
||||
- id: ClothingShoesBootsJack
|
||||
- id: ClothingOuterCoatWarden
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
startingInventory:
|
||||
Crowbar: 2
|
||||
Pickaxe: 4
|
||||
OreBag: 2
|
||||
OreBag: 4
|
||||
Flare: 4
|
||||
FlashlightLantern: 2
|
||||
HandheldGPSBasic: 2
|
||||
RadioHandheld: 2
|
||||
WeaponGrapplingGun: 2
|
||||
WeaponGrapplingGun: 4
|
||||
WeaponProtoKineticAccelerator: 4
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
skipChecks: true
|
||||
- type: Ghost
|
||||
- type: GhostHearing
|
||||
- type: ShowElectrocutionHUD
|
||||
- type: IntrinsicRadioReceiver
|
||||
- type: ActiveRadio
|
||||
receiveAllChannels: true
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
- type: IgnoreUIRange
|
||||
- type: StationAiHeld
|
||||
- type: StationAiOverlay
|
||||
- type: ShowElectrocutionHUD
|
||||
- type: ActionGrant
|
||||
actions:
|
||||
- ActionJumpToCore
|
||||
|
|
|
|||
|
|
@ -498,12 +498,22 @@
|
|||
- type: Sprite
|
||||
sprite: Objects/Fun/sharkplush.rsi
|
||||
state: blue
|
||||
- type: EmitSoundOnLand
|
||||
sound:
|
||||
path: /Audio/Items/Toys/rawr.ogg
|
||||
- type: EmitSoundOnTrigger
|
||||
sound:
|
||||
path: /Audio/Items/Toys/rawr.ogg
|
||||
- type: EmitSoundOnUse
|
||||
sound:
|
||||
path: /Audio/Items/Toys/rawr.ogg
|
||||
- type: EmitSoundOnActivate
|
||||
sound:
|
||||
path: /Audio/Items/Toys/rawr.ogg
|
||||
- type: MeleeWeapon
|
||||
wideAnimationRotation: 180
|
||||
soundHit:
|
||||
path: /Audio/Items/Toys/rawr.ogg
|
||||
- type: Item
|
||||
heldPrefix: blue
|
||||
storedRotation: -90
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
reagents:
|
||||
- ReagentId: Cellulose
|
||||
Quantity: 6
|
||||
- type: Item
|
||||
heldPrefix: cardboard
|
||||
|
||||
- type: entity
|
||||
parent: MaterialCardboard
|
||||
|
|
@ -136,6 +138,8 @@
|
|||
- type: Construction
|
||||
graph: WebObjects # not sure if I should either keep this here or just make another prototype. Will keep it here just in case.
|
||||
node: cloth
|
||||
- type: Item
|
||||
heldPrefix: cloth
|
||||
|
||||
- type: entity
|
||||
parent: MaterialCloth
|
||||
|
|
@ -196,6 +200,8 @@
|
|||
tags:
|
||||
- ClothMade
|
||||
- RawMaterial
|
||||
- type: Item
|
||||
heldPrefix: durathread
|
||||
|
||||
- type: entity
|
||||
parent: MaterialDurathread
|
||||
|
|
@ -335,8 +341,7 @@
|
|||
sprite: Objects/Materials/materials.rsi
|
||||
state: corgihide
|
||||
- type: Item
|
||||
sprite: Clothing/Head/Misc/hides.rsi
|
||||
heldPrefix: corgi
|
||||
heldPrefix: corgihide
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Misc/hides.rsi
|
||||
equippedPrefix: corgi2
|
||||
|
|
@ -428,6 +433,8 @@
|
|||
tags:
|
||||
- ClothMade
|
||||
- RawMaterial
|
||||
- type: Item
|
||||
heldPrefix: cotton
|
||||
|
||||
- type: entity
|
||||
parent: MaterialCotton
|
||||
|
|
@ -480,6 +487,8 @@
|
|||
tags:
|
||||
- ClothMade
|
||||
- RawMaterial
|
||||
- type: Item
|
||||
heldPrefix: pyrotton
|
||||
|
||||
- type: entity
|
||||
parent: MaterialPyrotton
|
||||
|
|
@ -539,6 +548,8 @@
|
|||
- ReagentId: Honk
|
||||
Quantity: 5
|
||||
- type: Appearance
|
||||
- type: Item
|
||||
heldPrefix: bananium
|
||||
|
||||
- type: entity
|
||||
parent: MaterialBananium
|
||||
|
|
@ -589,6 +600,9 @@
|
|||
tags:
|
||||
- ClothMade
|
||||
- RawMaterial
|
||||
- type: Item
|
||||
sprite: Objects/Materials/silk.rsi
|
||||
heldPrefix: silk
|
||||
|
||||
- type: entity
|
||||
parent: MaterialWebSilk
|
||||
|
|
@ -711,6 +725,8 @@
|
|||
reagents:
|
||||
- ReagentId: Vitamin
|
||||
Quantity: 3
|
||||
- type: Item
|
||||
heldPrefix: bones
|
||||
|
||||
- type: entity
|
||||
parent: MaterialBones
|
||||
|
|
@ -765,6 +781,8 @@
|
|||
- goliath_hide_3
|
||||
- type: Item
|
||||
size: Large
|
||||
heldPrefix: goliathhide
|
||||
sprite: Objects/Materials/hide.rsi
|
||||
shape:
|
||||
- 0,0,2,2
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
reagents:
|
||||
- ReagentId: Gold
|
||||
Quantity: 10
|
||||
- type: Item
|
||||
heldPrefix: gold
|
||||
|
||||
- type: entity
|
||||
parent: GoldOre
|
||||
|
|
@ -77,6 +79,8 @@
|
|||
reagents:
|
||||
- ReagentId: Carbon
|
||||
Quantity: 20
|
||||
- type: Item
|
||||
heldPrefix: diamond
|
||||
|
||||
- type: entity
|
||||
parent: DiamondOre
|
||||
|
|
@ -108,6 +112,8 @@
|
|||
reagents:
|
||||
- ReagentId: Iron
|
||||
Quantity: 10
|
||||
- type: Item
|
||||
heldPrefix: iron
|
||||
|
||||
- type: entity
|
||||
id: SteelOre1
|
||||
|
|
@ -144,6 +150,8 @@
|
|||
reagents:
|
||||
- ReagentId: Plasma
|
||||
Quantity: 10
|
||||
- type: Item
|
||||
heldPrefix: plasma
|
||||
|
||||
- type: entity
|
||||
parent: PlasmaOre
|
||||
|
|
@ -175,6 +183,8 @@
|
|||
reagents:
|
||||
- ReagentId: Silver
|
||||
Quantity: 10
|
||||
- type: Item
|
||||
heldPrefix: silver
|
||||
|
||||
- type: entity
|
||||
parent: SilverOre
|
||||
|
|
@ -206,6 +216,8 @@
|
|||
reagents:
|
||||
- ReagentId: Silicon
|
||||
Quantity: 10
|
||||
- type: Item
|
||||
heldPrefix: spacequartz
|
||||
|
||||
- type: entity
|
||||
parent: SpaceQuartz
|
||||
|
|
@ -245,6 +257,8 @@
|
|||
- ReagentId: Radium
|
||||
Quantity: 2
|
||||
canReact: false
|
||||
- type: Item
|
||||
heldPrefix: uranium
|
||||
|
||||
- type: entity
|
||||
parent: UraniumOre
|
||||
|
|
@ -285,6 +299,8 @@
|
|||
Quantity: 2
|
||||
- ReagentId: Honk
|
||||
Quantity: 5
|
||||
- type: Item
|
||||
heldPrefix: bananium
|
||||
|
||||
- type: entity
|
||||
parent: BananiumOre
|
||||
|
|
@ -324,6 +340,8 @@
|
|||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Coal: 100
|
||||
- type: Item
|
||||
heldPrefix: coal
|
||||
|
||||
- type: entity
|
||||
parent: Coal
|
||||
|
|
@ -381,6 +399,8 @@
|
|||
Quantity: 10
|
||||
- ReagentId: Iodine
|
||||
Quantity: 5
|
||||
- type: Item
|
||||
heldPrefix: salt
|
||||
|
||||
- type: entity
|
||||
parent: SaltOre
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
#Sunrise-end
|
||||
- type: Item
|
||||
size: Normal
|
||||
# heldPrefix: rods
|
||||
heldPrefix: rods
|
||||
- type: Construction
|
||||
graph: MetalRod
|
||||
node: MetalRod
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
- type: TimedDespawn
|
||||
lifetime: 0.4
|
||||
- type: Sprite
|
||||
drawdepth: Effects
|
||||
drawdepth: BelowMobs
|
||||
layers:
|
||||
- shader: unshaded
|
||||
map: ["enum.EffectLayers.Unshaded"]
|
||||
|
|
@ -315,9 +315,11 @@
|
|||
- EmitterBolt
|
||||
- type: TimedDespawn
|
||||
lifetime: 3
|
||||
# Sunrise-Start
|
||||
- type: Reflective
|
||||
reflective:
|
||||
- Energy
|
||||
# Sunrise-End
|
||||
|
||||
- type: entity
|
||||
name: watcher bolt
|
||||
|
|
@ -703,33 +705,6 @@
|
|||
- type: StaminaDamageOnCollide
|
||||
damage: 80
|
||||
|
||||
# Sunrise-Start
|
||||
- type: entity
|
||||
id: BaseBulletGrenade
|
||||
parent: BaseItem
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Projectile
|
||||
damage:
|
||||
types:
|
||||
Blunt: 5
|
||||
deleteOnCollide: false
|
||||
- type: StartTimerOnShoot
|
||||
- type: OnUseTimerTrigger
|
||||
delay: 2
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.25,-0.25,0.25,0.25"
|
||||
density: 50
|
||||
mask:
|
||||
- ItemMask
|
||||
restitution: 0.05
|
||||
friction: 0.5
|
||||
# Sunrise-End
|
||||
|
||||
- type: entity
|
||||
id: BulletGrenadeBlast
|
||||
name: blast grenade
|
||||
|
|
@ -1083,6 +1058,7 @@
|
|||
proto: BulletDisablerSmg
|
||||
count: 3 #bit stronger than a disabler if you hit your shots you goober, still not a 2 hit stun though
|
||||
spread: 9
|
||||
|
||||
# SUNRISE
|
||||
|
||||
- type: entity
|
||||
|
|
@ -1211,7 +1187,6 @@
|
|||
soundHit:
|
||||
collection: WeakHit
|
||||
|
||||
# Sunrise-AEG
|
||||
- type: entity
|
||||
name: energy bolt
|
||||
id: BulletEnergyGunLaser
|
||||
|
|
@ -1252,7 +1227,6 @@
|
|||
soundHit:
|
||||
collection: WeakHit
|
||||
|
||||
# Sunrise-NT
|
||||
- type: entity
|
||||
id: BulletRocketNT
|
||||
name: rocket
|
||||
|
|
@ -1274,3 +1248,28 @@
|
|||
radius: 3.5
|
||||
color: orange
|
||||
energy: 0.5
|
||||
|
||||
- type: entity
|
||||
id: BaseBulletGrenade
|
||||
parent: BaseItem
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Projectile
|
||||
damage:
|
||||
types:
|
||||
Blunt: 5
|
||||
deleteOnCollide: false
|
||||
- type: StartTimerOnShoot
|
||||
- type: OnUseTimerTrigger
|
||||
delay: 2
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.25,-0.25,0.25,0.25"
|
||||
density: 50
|
||||
mask:
|
||||
- ItemMask
|
||||
restitution: 0.05
|
||||
friction: 0.5
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue