Merge remote-tracking branch 'refs/remotes/wizards/master'

# Conflicts:
#	Content.Server/Administration/Systems/AdminSystem.cs
#	Content.Server/GameTicking/GameTicker.Spawning.cs
#	Content.Server/IoC/ServerContentIoC.cs
#	Resources/Locale/en-US/_strings/administration/multi-server-kick.ftl
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml
This commit is contained in:
Vigers Ray 2025-01-22 05:27:53 +03:00
commit 3c2672efe9
45 changed files with 518 additions and 157 deletions

View file

@ -1,6 +1,4 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Content.Server.Database;
namespace Content.Server.Administration.Managers;
@ -30,36 +28,15 @@ public sealed partial class BanManager
private TimeSpan _banNotificationRateLimitStart;
private int _banNotificationRateLimitCount;
private void OnDatabaseNotification(DatabaseNotification notification)
private bool OnDatabaseNotificationEarlyFilter()
{
if (notification.Channel != BanNotificationChannel)
return;
if (notification.Payload == null)
{
_sawmill.Error("Got ban notification with null payload!");
return;
}
BanNotificationData data;
try
{
data = JsonSerializer.Deserialize<BanNotificationData>(notification.Payload)
?? throw new JsonException("Content is null");
}
catch (JsonException e)
{
_sawmill.Error($"Got invalid JSON in ban notification: {e}");
return;
}
if (!CheckBanRateLimit())
{
_sawmill.Verbose("Not processing ban notification due to rate limit");
return;
return false;
}
_taskManager.RunOnMainThread(() => ProcessBanNotification(data));
return true;
}
private async void ProcessBanNotification(BanNotificationData data)

View file

@ -75,7 +75,12 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
{
_netManager.RegisterNetMessage<MsgRoleBans>();
_db.SubscribeToNotifications(OnDatabaseNotification);
_db.SubscribeToJsonNotification<BanNotificationData>(
_taskManager,
_sawmill,
BanNotificationChannel,
ProcessBanNotification,
OnDatabaseNotificationEarlyFilter);
_userDbData.AddOnLoadPlayer(CachePlayerData);
_userDbData.AddOnPlayerDisconnect(ClearPlayerData);

View file

@ -0,0 +1,114 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Content.Server.Database;
using Content.Shared.CCVar;
using Robust.Server.Player;
using Robust.Shared.Asynchronous;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Content.Server.Administration.Managers;
/// <summary>
/// Handles kicking people that connect to multiple servers on the same DB at once.
/// </summary>
/// <seealso cref="CCVars.AdminAllowMultiServerPlay"/>
public sealed class MultiServerKickManager
{
public const string NotificationChannel = "multi_server_kick";
[Dependency] private readonly IPlayerManager _playerManager = null!;
[Dependency] private readonly IServerDbManager _dbManager = null!;
[Dependency] private readonly ILogManager _logManager = null!;
[Dependency] private readonly IConfigurationManager _cfg = null!;
[Dependency] private readonly IAdminManager _adminManager = null!;
[Dependency] private readonly ITaskManager _taskManager = null!;
[Dependency] private readonly IServerNetManager _netManager = null!;
[Dependency] private readonly ILocalizationManager _loc = null!;
[Dependency] private readonly ServerDbEntryManager _serverDbEntry = null!;
private ISawmill _sawmill = null!;
private bool _allowed;
public void Initialize()
{
_sawmill = _logManager.GetSawmill("multi_server_kick");
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_cfg.OnValueChanged(CCVars.AdminAllowMultiServerPlay, b => _allowed = b, true);
_dbManager.SubscribeToJsonNotification<NotificationData>(
_taskManager,
_sawmill,
NotificationChannel,
OnNotification,
OnNotificationEarlyFilter
);
}
// ReSharper disable once AsyncVoidMethod
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (_allowed)
return;
if (e.NewStatus != SessionStatus.InGame)
return;
// Send notification to other servers so they can kick this player that just connected.
try
{
await _dbManager.SendNotification(new DatabaseNotification
{
Channel = NotificationChannel,
Payload = JsonSerializer.Serialize(new NotificationData
{
PlayerId = e.Session.UserId,
ServerId = (await _serverDbEntry.ServerEntity).Id,
}),
});
}
catch (Exception ex)
{
_sawmill.Error($"Failed to send notification for multi server kick: {ex}");
}
}
private bool OnNotificationEarlyFilter()
{
if (_allowed)
{
_sawmill.Verbose("Received notification for player join, but multi server play is allowed on this server. Ignoring");
return false;
}
return true;
}
// ReSharper disable once AsyncVoidMethod
private async void OnNotification(NotificationData notification)
{
if (!_playerManager.TryGetSessionById(new NetUserId(notification.PlayerId), out var player))
return;
if (notification.ServerId == (await _serverDbEntry.ServerEntity).Id)
return;
if (_adminManager.IsAdmin(player, includeDeAdmin: true))
return;
_sawmill.Info($"Kicking {player} for connecting to another server. Multi-server play is not allowed.");
_netManager.DisconnectChannel(player.Channel, _loc.GetString("multi-server-kick-reason"));
}
private sealed class NotificationData
{
[JsonPropertyName("player_id")]
public Guid PlayerId { get; set; }
[JsonPropertyName("server_id")]
public int ServerId { get; set; }
}
}

View file

@ -85,13 +85,14 @@ public sealed class AdminSystem : EntitySystem
Subs.CVar(_config, CCVars.PanicBunkerMinAccountAge, OnPanicBunkerMinAccountAgeChanged, true);
Subs.CVar(_config, CCVars.PanicBunkerMinOverallMinutes, OnPanicBunkerMinOverallMinutesChanged, true);
SubscribeLocalEvent<IdentityChangedEvent>(OnIdentityChanged);
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<RoleAddedEvent>(OnRoleEvent);
SubscribeLocalEvent<RoleRemovedEvent>(OnRoleEvent);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
SubscribeLocalEvent<ActorComponent, EntityRenamedEvent>(OnPlayerRenamed);
SubscribeLocalEvent<ActorComponent, IdentityChangedEvent>(OnIdentityChanged);
IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Sunrise-Sponsors
}
@ -149,12 +150,9 @@ public sealed class AdminSystem : EntitySystem
return value ?? null;
}
private void OnIdentityChanged(ref IdentityChangedEvent ev)
private void OnIdentityChanged(Entity<ActorComponent> ent, ref IdentityChangedEvent ev)
{
if (!TryComp<ActorComponent>(ev.CharacterEntity, out var actor))
return;
UpdatePlayerList(actor.PlayerSession);
UpdatePlayerList(ent.Comp.PlayerSession);
}
private void OnRoleEvent(RoleEvent ev)

View file

@ -1809,6 +1809,8 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
#endregion
public abstract Task SendNotification(DatabaseNotification notification);
// SQLite returns DateTime as Kind=Unspecified, Npgsql actually knows for sure it's Kind=Utc.
// Normalize DateTimes here so they're always Utc. Thanks.
protected abstract DateTime NormalizeDatabaseTime(DateTime time);

View file

@ -350,6 +350,15 @@ namespace Content.Server.Database
/// <param name="notification">The notification to trigger</param>
void InjectTestNotification(DatabaseNotification notification);
/// <summary>
/// Send a notification to all other servers connected to the same database.
/// </summary>
/// <remarks>
/// The local server will receive the sent notification itself again.
/// </remarks>
/// <param name="notification">The notification to send.</param>
Task SendNotification(DatabaseNotification notification);
#endregion
}
@ -1045,6 +1054,12 @@ namespace Content.Server.Database
HandleDatabaseNotification(notification);
}
public Task SendNotification(DatabaseNotification notification)
{
DbWriteOpsMetric.Inc();
return RunDbCommand(() => _db.SendNotification(notification));
}
private async void HandleDatabaseNotification(DatabaseNotification notification)
{
lock (_notificationHandlers)

View file

@ -0,0 +1,76 @@
using System.Text.Json;
using Robust.Shared.Asynchronous;
namespace Content.Server.Database;
public static class ServerDbManagerExt
{
/// <summary>
/// Subscribe to a database notification on a specific channel, formatted as JSON.
/// </summary>
/// <param name="dbManager">The database manager to subscribe on.</param>
/// <param name="taskManager">The task manager used to run the main callback on the main thread.</param>
/// <param name="sawmill">Sawmill to log any errors to.</param>
/// <param name="channel">
/// The notification channel to listen on. Only notifications on this channel will be handled.
/// </param>
/// <param name="action">
/// The action to run on the notification data.
/// This runs on the main thread.
/// </param>
/// <param name="earlyFilter">
/// An early filter callback that runs before the JSON message is deserialized.
/// Return false to not handle the notification.
/// This does not run on the main thread.
/// </param>
/// <param name="filter">
/// A filter callback that runs after the JSON message is deserialized.
/// Return false to not handle the notification.
/// This does not run on the main thread.
/// </param>
/// <typeparam name="TData">The type of JSON data to deserialize.</typeparam>
public static void SubscribeToJsonNotification<TData>(
this IServerDbManager dbManager,
ITaskManager taskManager,
ISawmill sawmill,
string channel,
Action<TData> action,
Func<bool>? earlyFilter = null,
Func<TData, bool>? filter = null)
{
dbManager.SubscribeToNotifications(notification =>
{
if (notification.Channel != channel)
return;
if (notification.Payload == null)
{
sawmill.Error($"Got {channel} notification with null payload!");
return;
}
if (earlyFilter != null && !earlyFilter())
return;
TData data;
try
{
data = JsonSerializer.Deserialize<TData>(notification.Payload)
?? throw new JsonException("Content is null");
}
catch (JsonException e)
{
sawmill.Error($"Got invalid JSON in {channel} notification: {e}");
return;
}
if (filter != null && !filter(data))
return;
taskManager.RunOnMainThread(() =>
{
action(data);
});
});
}
}

View file

@ -2,6 +2,7 @@
using System.Threading;
using System.Threading.Tasks;
using Content.Server.Administration.Managers;
using Microsoft.EntityFrameworkCore;
using Npgsql;
namespace Content.Server.Database;
@ -17,6 +18,7 @@ public sealed partial class ServerDbPostgres
private static readonly string[] NotificationChannels =
[
BanManager.BanNotificationChannel,
MultiServerKickManager.NotificationChannel,
];
private static readonly TimeSpan ReconnectWaitIncrease = TimeSpan.FromSeconds(10);
@ -111,6 +113,14 @@ public sealed partial class ServerDbPostgres
});
}
public override async Task SendNotification(DatabaseNotification notification)
{
await using var db = await GetDbImpl();
await db.PgDbContext.Database.ExecuteSqlAsync(
$"SELECT pg_notify({notification.Channel}, {notification.Payload})");
}
public override void Shutdown()
{
_notificationTokenSource.Cancel();

View file

@ -537,6 +537,12 @@ namespace Content.Server.Database
return await base.AddAdminMessage(message);
}
public override Task SendNotification(DatabaseNotification notification)
{
// Notifications not implemented on SQLite.
return Task.CompletedTask;
}
protected override DateTime NormalizeDatabaseTime(DateTime time)
{
DebugTools.Assert(time.Kind == DateTimeKind.Unspecified);

View file

@ -171,6 +171,7 @@ namespace Content.Server.Entry
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
IoCManager.Resolve<IBanManager>().Initialize();
IoCManager.Resolve<IConnectionManager>().PostInit();
IoCManager.Resolve<MultiServerKickManager>().Initialize();
}
}

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Numerics;
using Content.Server._Sunrise.Station;
using Content.Server.Administration.Managers;
using Content.Server.Administration.Systems;
using Content.Server.GameTicking.Events;
using Content.Server.Ghost;
using Content.Server.Shuttles.Components;
@ -34,6 +35,7 @@ namespace Content.Server.GameTicking
{
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly AdminSystem _admin = default!;
[Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!;
[Dependency] private readonly NewLifeSystem _newLifeSystem = default!; // Sunrise-NewLife
@ -261,6 +263,7 @@ namespace Content.Server.GameTicking
_roles.MindAddJobRole(newMind, silent: silent, jobPrototype:jobId);
var jobName = _jobs.MindTryGetJobName(newMind);
_admin.UpdatePlayerList(player);
if (lateJoin && !silent)
{

View file

@ -78,6 +78,7 @@ namespace Content.Server.IoC
IoCManager.Register<MappingManager>();
IoCManager.Register<IWatchlistWebhookManager, WatchlistWebhookManager>();
IoCManager.Register<ConnectionManager>();
IoCManager.Register<MultiServerKickManager>();
IoCManager.Register<ServersHubManager>(); // Sunrise-Hub
}

View file

@ -22,6 +22,8 @@ public sealed partial class IdCardComponent : Component
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
public LocId? JobTitle;
[DataField]
[AutoNetworkedField]
private string? _jobTitle;
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWriteExecute)]

View file

@ -182,4 +182,11 @@ public sealed partial class CCVars
public static readonly CVarDef<bool> BanHardwareIds =
CVarDef.Create("ban.hardware_ids", true, CVar.SERVERONLY);
/// <summary>
/// If true, players are allowed to connect to multiple game servers at once.
/// If false, they will be kicked from the first when connecting to another.
/// </summary>
public static readonly CVarDef<bool> AdminAllowMultiServerPlay =
CVarDef.Create("admin.allow_multi_server_play", true, CVar.SERVERONLY);
}

View file

@ -24,5 +24,13 @@ public sealed partial class ContrabandComponent : Component
/// </summary>
[DataField]
[AutoNetworkedField]
public HashSet<ProtoId<DepartmentPrototype>>? AllowedDepartments = ["Security"];
public HashSet<ProtoId<DepartmentPrototype>> AllowedDepartments = new();
/// <summary>
/// Which jobs is this item restricted to?
/// If empty, no jobs are allowed to use this beyond the allowed departments.
/// </summary>
[DataField]
[AutoNetworkedField]
public HashSet<ProtoId<JobPrototype>> AllowedJobs = new();
}

View file

@ -19,8 +19,8 @@ public sealed partial class ContrabandSeverityPrototype : IPrototype
public LocId ExamineText;
/// <summary>
/// When examining the contraband, should this take into account the viewer's departments?
/// When examining the contraband, should this take into account the viewer's departments and job?
/// </summary>
[DataField]
public bool ShowDepartments;
public bool ShowDepartmentsAndJobs;
}

View file

@ -40,6 +40,7 @@ public sealed class ContrabandSystem : EntitySystem
contraband.Severity = other.Severity;
contraband.AllowedDepartments = other.AllowedDepartments;
contraband.AllowedJobs = other.AllowedJobs;
Dirty(uid, contraband);
}
@ -54,11 +55,15 @@ public sealed class ContrabandSystem : EntitySystem
using (args.PushGroup(nameof(ContrabandComponent)))
{
// TODO shouldn't department prototypes have a localized name instead of just using the ID for this?
var localizedDepartments = ent.Comp.AllowedDepartments.Select(p => Loc.GetString($"department-{p.Id}"));
var localizedJobs = ent.Comp.AllowedJobs.Select(p => _proto.Index(p).LocalizedName);
var severity = _proto.Index(ent.Comp.Severity);
if (severity.ShowDepartments && ent.Comp is { AllowedDepartments: not null })
if (severity.ShowDepartmentsAndJobs)
{
// TODO shouldn't department prototypes have a localized name instead of just using the ID for this?
var list = ContentLocalizationManager.FormatList(ent.Comp.AllowedDepartments.Select(p => Loc.GetString($"department-{p.Id}")).ToList());
//creating a combined list of jobs and departments for the restricted text
var list = ContentLocalizationManager.FormatList(localizedDepartments.Concat(localizedJobs).ToList());
// department restricted text
args.PushMarkup(Loc.GetString("contraband-examine-text-Restricted-department", ("departments", list)));
@ -69,23 +74,30 @@ public sealed class ContrabandSystem : EntitySystem
}
// text based on ID card
List<ProtoId<DepartmentPrototype>>? departments = null;
List<ProtoId<DepartmentPrototype>> departments = new();
var jobId = "";
if (_id.TryFindIdCard(args.Examiner, out var id))
{
departments = id.Comp.JobDepartments;
if (id.Comp.LocalizedJobTitle is not null)
{
jobId = id.Comp.LocalizedJobTitle;
}
}
// either its fully restricted, you have no departments, or your departments dont intersect with the restricted departments
if (ent.Comp.AllowedDepartments is null
|| departments is null
|| !departments.Intersect(ent.Comp.AllowedDepartments).Any())
// for the jobs we compare the localized string in case you use an agent ID or custom job name that is not a prototype
if (departments.Intersect(ent.Comp.AllowedDepartments).Any()
|| localizedJobs.Contains(jobId))
{
args.PushMarkup(Loc.GetString("contraband-examine-text-avoid-carrying-around"));
return;
// you are allowed to use this!
args.PushMarkup(Loc.GetString("contraband-examine-text-in-the-clear"));
}
else
{
// straight to jail!
args.PushMarkup(Loc.GetString("contraband-examine-text-avoid-carrying-around"));
}
// otherwise fine to use :tm:
args.PushMarkup(Loc.GetString("contraband-examine-text-in-the-clear"));
}
}
}

View file

@ -728,5 +728,14 @@ Entries:
id: 90
time: '2025-01-17T07:24:50.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33782
- author: PJB3005
changes:
- message: Introduced an option to automatically disconnect players from their current
server if they join another one within the same server community. Admins are
always exempt.
type: Add
id: 91
time: '2025-01-21T23:23:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/34563
Name: Admin
Order: 1

View file

@ -1,32 +1,4 @@
Entries:
- author: lzk228
changes:
- message: The captain now have special late join message.
type: Tweak
id: 7335
time: '2024-09-09T19:57:37.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/31991
- author: Thinbug0
changes:
- message: Teal gloves can now be found at the ClothesMate!
type: Add
id: 7336
time: '2024-09-09T21:47:53.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/31865
- author: chillyconmor
changes:
- message: Space Ninjas now have a new intro song.
type: Add
id: 7337
time: '2024-09-09T22:12:25.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/31055
- author: DieselMohawk
changes:
- message: Made Trooper Uniform accessible for Security Officers in loadouts
type: Fix
id: 7338
time: '2024-09-09T23:59:31.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32019
- author: IProduceWidgets
changes:
- message: Visitors now can have the correct Id cards and PDA!
@ -3892,3 +3864,43 @@
id: 7834
time: '2025-01-20T01:24:40.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/34527
- author: Compilatron144
changes:
- message: Raised Plasma Station's population limit
type: Tweak
id: 7835
time: '2025-01-21T04:56:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/34549
- author: sporkyz
changes:
- message: Galoshes are now restricted to janitor.
type: Tweak
- message: Double barrel shotguns, basic armor vests, beanbag/flare shotgun shells,
and bandoliers are now allowed for Bartenders, not just Security.
type: Tweak
- message: Lawyers now have access to the Security headset they spawn with, as well
as the encryption key inside.
type: Fix
- message: Sawn-off shotguns are now properly considered contraband instead of being
unrestricted.
type: Fix
id: 7836
time: '2025-01-21T09:51:27.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33385
- author: K-Dynamic
changes:
- message: Electrified doors and windoors now visibly spark when touched.
type: Tweak
- message: New tips have been added about opening electrified doors by throwing
your PDA or ID, and resetting doors that are electrified by AI.
type: Add
id: 7837
time: '2025-01-21T10:39:16.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/34502
- author: Nimfar11
changes:
- message: Adds Blueprint double emergency tank.
type: Add
id: 7838
time: '2025-01-21T16:08:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/34232

View file

@ -0,0 +1 @@
multi-server-kick-reason = Connected to different server in this community.

View file

@ -133,3 +133,5 @@ tips-dataset-132 = By right clicking on a player, and then clicking the heart ic
tips-dataset-133 = Monkeys and kobolds have a rare chance to be sentient. Ook!
tips-dataset-134 = You can tell if an area with firelocks up is spaced by looking to see if the firelocks have lights beside them.
tips-dataset-135 = Instead of picking it up, you can alt-click food to eat it. This also works for mice and other creatures without hands.
tips-dataset-136 = If you're trapped behind an electrified door, disable the APC or throw your ID at the door to avoid getting shocked!
tips-dataset-137 = If the AI electrifies a door and you have insulated gloves, snip and mend the power wire to reset their electrification!

View file

@ -2210,6 +2210,8 @@ entities:
713: -32,-30
714: -31,-19
- node:
cleanable: True
zIndex: 1
color: '#FFFFFFFF'
id: Dirt
decals:
@ -2226,11 +2228,6 @@ entities:
6708: -71,-37
6709: -70,-36
6710: -68,-36
- node:
cleanable: True
color: '#FFFFFFFF'
id: Dirt
decals:
5206: -56,-54
5207: -55,-55
5208: -55,-54
@ -2293,6 +2290,8 @@ entities:
7242: -21,19
7243: -22,20
- node:
cleanable: True
zIndex: 1
color: '#FFFFFFFF'
id: DirtHeavy
decals:
@ -2305,11 +2304,6 @@ entities:
6083: -21,5
6084: -21,4
6085: -22,4
- node:
cleanable: True
color: '#FFFFFFFF'
id: DirtHeavy
decals:
329: -54,-11
330: -50,-11
1572: -65,-34
@ -2365,11 +2359,14 @@ entities:
4063: -98,-18
- node:
cleanable: True
zIndex: 1
color: '#FFFFFFFF'
id: DirtHeavyMonotile
decals:
1579: -66,-34
- node:
cleanable: True
zIndex: 1
color: '#FFFFFFFF'
id: DirtLight
decals:
@ -2389,11 +2386,6 @@ entities:
6713: -71,-32
6714: -71,-31
6715: -71,-30
- node:
cleanable: True
color: '#FFFFFFFF'
id: DirtLight
decals:
1580: -64,-33
1581: -65,-33
1582: -64,-32
@ -2490,6 +2482,8 @@ entities:
6921: -153,-44
6922: -145,-45
- node:
cleanable: True
zIndex: 1
color: '#FFFFFFFF'
id: DirtMedium
decals:
@ -2652,11 +2646,6 @@ entities:
7221: -121,34
7222: -122,34
7223: -122,33
- node:
cleanable: True
color: '#FFFFFFFF'
id: DirtMedium
decals:
331: -54,-10
332: -51,-10
333: -52,-10

View file

@ -2,4 +2,4 @@
id: Tips
values:
prefix: tips-dataset-
count: 135
count: 137

View file

@ -577,7 +577,7 @@
# Belts without visualizers
- type: entity
parent: [ClothingBeltAmmoProviderBase, BaseRestrictedContraband]
parent: [ClothingBeltAmmoProviderBase, BaseSecurityBartenderContraband]
id: ClothingBeltBandolier
name: bandolier
description: A bandolier for holding shotgun ammunition.

View file

@ -226,7 +226,7 @@
- EncryptionKeyCommon
- type: entity
parent: [ClothingHeadset, BaseRestrictedContraband]
parent: [ClothingHeadset, BaseSecurityLawyerContraband]
id: ClothingHeadsetSecurity
name: security headset
description: This is used by your elite security force.

View file

@ -1,11 +1,12 @@
# Numbers for armor here largely taken from /tg/.
# NOTE: Half of the kind of armor you're probably thinking of is in vests.yml. These should probably be merged some day.
#Basic armor vest
#Basic armor vest for inheritance
- type: entity
parent: [ClothingOuterBaseMedium, AllowSuitStorageClothing, BaseRestrictedContraband]
id: ClothingOuterArmorBasic
id: ClothingOuterArmorBase
name: armor vest
abstract: true
description: A standard Type I armored vest that provides decent protection against most types of damage.
components:
- type: Sprite
@ -22,6 +23,11 @@
- type: ExplosionResistance
damageCoefficient: 0.90
#Standard armor vest, allowed for security and bartenders
- type: entity
parent: [ BaseSecurityBartenderContraband, ClothingOuterArmorBase]
id: ClothingOuterArmorBasic
#Alternate / slim basic armor vest
- type: entity
parent: ClothingOuterArmorBasic
@ -58,7 +64,7 @@
- type: GroupExamine
- type: entity
parent: ClothingOuterArmorBasic
parent: ClothingOuterArmorBase
id: ClothingOuterArmorBulletproof
name: bulletproof vest
description: A Type III heavy bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent.
@ -78,7 +84,7 @@
damageCoefficient: 0.80
- type: entity
parent: ClothingOuterArmorBasic
parent: ClothingOuterArmorBase
id: ClothingOuterArmorReflective
name: reflective vest
description: An armored vest with advanced shielding to protect against energy weapons.

View file

@ -10,7 +10,7 @@
sprite: Clothing/OuterClothing/Coats/bomber.rsi
- type: entity
parent: [ClothingOuterStorageBase, AllowSuitStorageClothing, ClothingOuterArmorBasic]
parent: [ClothingOuterStorageBase, AllowSuitStorageClothing, ClothingOuterArmorBase]
id: ClothingOuterCoatDetective
name: detective trenchcoat
description: An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business.

View file

@ -42,7 +42,7 @@
#Detective's vest
- type: entity
parent: [ClothingOuterArmorBasic, BaseRestrictedContraband]
parent: [ClothingOuterArmorBase, BaseRestrictedContraband]
id: ClothingOuterVestDetective
name: detective's vest
description: A hard-boiled private investigator's armored vest.

View file

@ -104,7 +104,7 @@
sprite: Clothing/Shoes/Specific/cult.rsi
- type: entity
parent: ClothingShoesBase
parent: [ ClothingShoesBase, BaseJanitorContraband ]
id: ClothingShoesGaloshes
name: galoshes
description: Rubber boots.

View file

@ -222,6 +222,7 @@
id: SalvageEquipmentRare
table: !type:GroupSelector
children:
- id: BlueprintDoubleEmergencyTank
- id: FultonBeacon
- id: Fulton
amount: !type:RangeNumberSelector

View file

@ -170,7 +170,7 @@
- state: robotics_label
- type: entity
parent: [ EncryptionKey, BaseSecurityContraband ]
parent: [ EncryptionKey, BaseSecurityLawyerContraband ]
id: EncryptionKeySecurity
name: security encryption key
description: An encryption key used by security.

View file

@ -37,3 +37,14 @@
- type: Blueprint
providedRecipes:
- SeismicCharge
- type: entity
parent: BaseBlueprint
id: BlueprintDoubleEmergencyTank
name: double emergency tank blueprint
description: A blueprint with a schematic of a double emergency tank. It can be inserted into an autolathe.
components:
- type: Blueprint
providedRecipes:
- DoubleEmergencyOxygenTank
- DoubleEmergencyNitrogenTank

View file

@ -50,7 +50,7 @@
- type: entity
id: ShellShotgunBeanbag
name: shell (.50 beanbag)
parent: BaseShellHitscanShotgun
parent: [ BaseShellHitscanShotgun, BaseSecurityBartenderContraband ] # Sunrise-Edit
components:
- type: Tag
tags:
@ -83,7 +83,7 @@
- type: entity
id: ShellShotgunFlare
name: shell (.50 flare)
parent: BaseShellShotgun
parent: [ BaseShellShotgun, BaseSecurityBartenderContraband ]
components:
- type: Tag
tags:

View file

@ -114,7 +114,7 @@
- type: entity
name: double-barreled shotgun
parent: [BaseWeaponShotgun, BaseGunWieldable, BaseMinorContraband]
parent: [BaseWeaponShotgun, BaseGunWieldable, BaseSecurityBartenderContraband]
id: WeaponShotgunDoubleBarreled
description: An immortal classic. Uses .50 shotgun shells.
components:
@ -203,7 +203,7 @@
- type: entity
name: sawn-off shotgun
parent: BaseWeaponShotgun
parent: [ BaseWeaponShotgun, BaseSecurityBartenderContraband ]
id: WeaponShotgunSawn
description: Groovy! Uses .50 shotgun shells.
components:

View file

@ -203,7 +203,6 @@
doAfterDuration: 4.0
- type: Contraband
severity: Syndicate
allowedDepartments: null
- type: Sprite
sprite: Objects/Weapons/Melee/e_dagger.rsi
layers:

View file

@ -5,8 +5,6 @@
components:
- type: Contraband
severity: Syndicate
# no one should be carrying this around visibly!
allowedDepartments: null
# minor contraband not departmentally restricted -- improvised weapons etc
- type: entity
@ -15,8 +13,6 @@
components:
- type: Contraband
severity: Minor
# according to space law no dept is authorized to have
allowedDepartments: null
# major contraband, for things like guns or weaponry that don't belong to any department and aren't syndicate specific
- type: entity
@ -25,7 +21,6 @@
components:
- type: Contraband
severity: Major
allowedDepartments: null
# minor contraband by default restricted to security only
- type: entity
@ -157,6 +152,33 @@
- type: Contraband
allowedDepartments: [ Medical, Science ]
# contraband restricted by job by some degree
- type: entity
id: BaseSecurityBartenderContraband
parent: BaseRestrictedContraband
abstract: true
components:
- type: Contraband
allowedDepartments: [ Security ]
allowedJobs: [ Bartender ]
- type: entity
id: BaseSecurityLawyerContraband
parent: BaseRestrictedContraband
abstract: true
components:
- type: Contraband
allowedDepartments: [ Security ]
allowedJobs: [ Lawyer ]
- type: entity
id: BaseJanitorContraband
parent: BaseRestrictedContraband
abstract: true
components:
- type: Contraband
allowedJobs: [ Janitor ]
# for ~objective items
- type: entity
id: BaseGrandTheftContraband

View file

@ -30,11 +30,16 @@
shader: unshaded
- state: panel_open
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: ["enum.ElectrifiedLayers.HUD"]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: ["enum.ElectrifiedLayers.Sparks"]
- type: AnimationPlayer
- type: Physics
- type: Fixtures
@ -77,6 +82,12 @@
- type: NavMapDoor
- type: DoorBolt
- type: Appearance
- type: GenericVisualizer
visuals:
enum.ElectrifiedVisuals.ShowSparks:
enum.ElectrifiedLayers.Sparks:
True: { visible: True }
False: { visible: False }
- type: WiresVisuals
- type: ElectrocutionHUDVisuals
- type: ApcPowerReceiver

View file

@ -27,11 +27,16 @@
shader: unshaded
- state: panel_open
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: ["enum.ElectrifiedLayers.HUD"]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: ["enum.ElectrifiedLayers.Sparks"]
- type: AnimationPlayer
- type: Physics
- type: Fixtures
@ -71,6 +76,12 @@
- type: AccessReader
containerAccessProvider: board
- type: Appearance
- type: GenericVisualizer
visuals:
enum.ElectrifiedVisuals.ShowSparks:
enum.ElectrifiedLayers.Sparks:
True: { visible: True }
False: { visible: False }
- type: WiresVisuals
- type: ElectrocutionHUDVisuals
- type: ApcPowerReceiver

View file

@ -43,11 +43,16 @@
map: ["enum.DoorVisualLayers.BaseEmergencyAccess"]
- state: panel_open
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: ["enum.ElectrifiedLayers.HUD"]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: ["enum.ElectrifiedLayers.Sparks"]
- type: AnimationPlayer
- type: ApcPowerReceiver
- type: ExtensionCableReceiver
@ -142,6 +147,12 @@
enum.WiresUiKey.Key:
type: WiresBoundUserInterface
- type: Appearance
- type: GenericVisualizer
visuals:
enum.ElectrifiedVisuals.ShowSparks:
enum.ElectrifiedLayers.Sparks:
True: { visible: True }
False: { visible: False }
- type: WiresVisuals
- type: ElectrocutionHUDVisuals
- type: Airtight
@ -183,11 +194,16 @@
- state: panel_open
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
visible: false
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.HUD" ]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.Sparks" ]
- type: Damageable
damageModifierSet: RGlass
- type: Destructible
@ -249,11 +265,16 @@
- state: panel_open
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
visible: false
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.HUD" ]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.Sparks" ]
- type: Destructible
thresholds:
- trigger:
@ -310,11 +331,16 @@
- state: panel_open
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
visible: false
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: ["enum.ElectrifiedLayers.HUD"]
map: [ "enum.ElectrifiedLayers.HUD" ]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.Sparks" ]
- type: Destructible
thresholds:
- trigger:
@ -376,11 +402,16 @@
- state: panel_open
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
visible: false
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.HUD" ]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.Sparks" ]
- type: Destructible
thresholds:
- trigger:
@ -437,11 +468,16 @@
- state: panel_open
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
visible: false
- state: electrified
- state: electrified_ai
sprite: Interface/Misc/ai_hud.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.HUD" ]
- state: electrified
sprite: Effects/electricity.rsi
shader: unshaded
visible: false
map: [ "enum.ElectrifiedLayers.Sparks" ]
- type: Destructible
thresholds:
- trigger:

View file

@ -2,8 +2,7 @@
id: Plasma
mapName: 'Plasma'
mapPath: /Maps/plasma.yml
minPlayers: 20
maxPlayers: 60
minPlayers: 30
stations:
Plasma:
stationProto: StandardNanotrasenStation
@ -18,10 +17,16 @@
- type: StationCargoShuttle
path: /Maps/Shuttles/cargo_plasma.yml
- type: StationJobs
availableJobs:
#service
availableJobs: #Total of 66 jobs roundstart, max of 93 inc. latejoins and trainees.
#command - 7
Captain: [ 1, 1 ]
HeadOfPersonnel: [ 1, 1 ]
ChiefEngineer: [ 1, 1 ]
ChiefMedicalOfficer: [ 1, 1 ]
ResearchDirector: [ 1, 1 ]
HeadOfSecurity: [ 1, 1 ]
Quartermaster: [ 1, 1 ]
#service - 18-21
Bartender: [ 2, 2 ]
Botanist: [ 2, 3 ]
Chef: [ 2, 2 ]
@ -30,37 +35,32 @@
Librarian: [ 1, 1 ]
ServiceWorker: [ 2, 2 ]
Reporter: [ 2, 3 ]
#engineering
ChiefEngineer: [ 1, 1 ]
Clown: [ 1, 2 ] # This might be fun
Mime: [ 1, 1 ]
Musician: [ 1, 1 ]
#engineering - 8-12
AtmosphericTechnician: [ 4, 4 ]
StationEngineer: [ 4, 4 ]
TechnicalAssistant: [ 4, 4 ]
#medical
ChiefMedicalOfficer: [ 1, 1 ]
#medical - 9-13
Chemist: [ 2, 2 ]
MedicalDoctor: [ 4, 4 ]
Paramedic: [ 2, 2 ]
MedicalIntern: [ 4, 4 ]
Psychologist: [ 1, 1 ]
#science
ResearchDirector: [ 1, 1 ]
#science - 7-13
Scientist: [ 4, 4 ]
ResearchAssistant: [ 4, 4 ]
StationAi: [ 1, 1 ]
Borg: [ 2, 4 ]
#security
HeadOfSecurity: [ 1, 1 ]
#security - 9-17
Warden: [ 1, 1 ]
SecurityOfficer: [ 6, 8 ]
Detective: [ 1, 2 ]
SecurityCadet: [ 4, 4 ]
Lawyer: [ 1, 2 ]
#supply
Quartermaster: [ 1, 1 ]
#supply - 8-10
SalvageSpecialist: [ 4, 4 ]
CargoTechnician: [ 4, 6 ]
#civilian
#civilian - the tiders yearn for the mines
Passenger: [ -1, -1 ]
Clown: [ 1, 1 ]
Mime: [ 1, 1 ]
Musician: [ 1, 1 ]

View file

@ -119,6 +119,20 @@
materials:
Steel: 300
- type: latheRecipe
id: DoubleEmergencyOxygenTank
result: DoubleEmergencyOxygenTank
completetime: 4
materials:
Steel: 250
- type: latheRecipe
id: DoubleEmergencyNitrogenTank
result: DoubleEmergencyNitrogenTank
completetime: 4
materials:
Steel: 250
- type: latheRecipe
id: ClothingShoesBootsMagSci
result: ClothingShoesBootsMagSci

View file

@ -14,7 +14,7 @@
- type: contrabandSeverity
id: Restricted
examineText: contraband-examine-text-Restricted
showDepartments: true
showDepartmentsAndJobs: true
# Having this as a regular crew member is considered grand theft. (nuke disk, captain's gear, objective items, etc)
- type: contrabandSeverity

View file

@ -12,20 +12,20 @@
range: 30 # 30 days
action: Deny
includeSecret: false
- !type:ConditionNotesPlaytimeRange # Deny for >=2 medium severity notes in the last 14 days
- !type:ConditionNotesPlaytimeRange # Deny for >=3 medium severity notes in the last 90 days
includeExpired: false
minimumSeverity: 2 # Medium
minimumNotes: 1
range: 14 # 14 Days
action: Deny
includeSecret: false
- !type:ConditionNotesPlaytimeRange # Deny for >=3 low severity notes in the last 14 days
includeExpired: false
minimumSeverity: 1 # Low
minimumNotes: 3
range: 14 # 14 Days
range: 90 # 90 Days
action: Deny
includeSecret: false
# - !type:ConditionNotesPlaytimeRange # Deny for >=3 low severity notes in the last 14 days
# includeExpired: false
# minimumSeverity: 1 # Low
# minimumNotes: 3
# range: 14 # 14 Days
# action: Deny
# includeSecret: false
- !type:ConditionManualWhitelistMembership # Allow whitelisted players
action: Allow
- !type:ConditionPlayerCount # Allow when <= 15 players are online

View file

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 812 B

View file

@ -8,7 +8,7 @@
},
"states": [
{
"name": "electrified",
"name": "electrified_ai",
"delays": [
[
0.2,
@ -34,4 +34,4 @@
]
}
]
}
}