using Content.Server.Configurable; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Disposal.Unit.EntitySystems; using Content.Server.Power.Components; using Content.Shared.DeviceNetwork; using Content.Shared.Disposal; using Content.Shared.Interaction; using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Utility; namespace Content.Server.Disposal.Mailing; public sealed class MailingUnitSystem : EntitySystem { [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; private const string MailTag = "mail"; private const string TagConfigurationKey = "tag"; private const string NetTag = "tag"; private const string NetSrc = "src"; private const string NetTarget = "target"; private const string NetCmdSent = "mail_sent"; private const string NetCmdRequest = "get_mailer_tag"; private const string NetCmdResponse = "mailer_tag"; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnPacketReceived); SubscribeLocalEvent(OnBeforeFlush); SubscribeLocalEvent(OnConfigurationUpdated); SubscribeLocalEvent(HandleActivate, before: new[] { typeof(DisposalUnitSystem) }); SubscribeLocalEvent(OnDisposalUnitUIStateChange); SubscribeLocalEvent(OnTargetSelected); } private void OnComponentInit(EntityUid uid, MailingUnitComponent component, ComponentInit args) { UpdateTargetList(uid, component); } private void OnPacketReceived(EntityUid uid, MailingUnitComponent component, DeviceNetworkPacketEvent args) { if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || !IsPowered(uid)) return; switch (command) { case NetCmdRequest: SendTagRequestResponse(uid, args, component.Tag); break; case NetCmdResponse when args.Data.TryGetValue(NetTag, out string? tag): //Add the received tag request response to the list of targets component.TargetList.Add(tag); UpdateUserInterface(uid, component); break; } } /// /// Sends the given tag as a response to a if it's not null /// private void SendTagRequestResponse(EntityUid uid, DeviceNetworkPacketEvent args, string? tag) { if (tag == null) return; var payload = new NetworkPayload { [DeviceNetworkConstants.Command] = NetCmdResponse, [NetTag] = tag }; _deviceNetworkSystem.QueuePacket(uid, args.Address, payload, args.Frequency); } /// /// Prevents the unit from flushing if no target is selected /// private void OnBeforeFlush(EntityUid uid, MailingUnitComponent component, BeforeDisposalFlushEvent args) { if (string.IsNullOrEmpty(component.Target)) { args.Cancel(); return; } args.Tags.Add(MailTag); args.Tags.Add(component.Target); BroadcastSentMessage(uid, component); } /// /// Broadcast that a mail was sent including the src and target tags /// private void BroadcastSentMessage(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) { if (string.IsNullOrEmpty(component.Tag) || string.IsNullOrEmpty(component.Target) || !Resolve(uid, ref device)) return; var payload = new NetworkPayload { [DeviceNetworkConstants.Command] = NetCmdSent, [NetSrc] = component.Tag, [NetTarget] = component.Target }; _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); } /// /// Clears the units target list and broadcasts a . /// The target list will then get populated with responses from all active mailing units on the same grid /// private void UpdateTargetList(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return; var payload = new NetworkPayload { [DeviceNetworkConstants.Command] = NetCmdRequest }; component.TargetList.Clear(); _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); } /// /// Gets called when the units tag got updated /// private void OnConfigurationUpdated(EntityUid uid, MailingUnitComponent component, ConfigurationSystem.ConfigurationUpdatedEvent args) { var configuration = args.Configuration.Config; if (!configuration.ContainsKey(TagConfigurationKey) || configuration[TagConfigurationKey] == string.Empty) { component.Tag = null; return; } component.Tag = configuration[TagConfigurationKey]; UpdateUserInterface(uid, component); } private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) { if (args.Handled || !args.Complex) return; if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) { return; } args.Handled = true; UpdateTargetList(uid, component); _userInterfaceSystem.OpenUi(uid, MailingUnitUiKey.Key, actor.PlayerSession); } /// /// Gets called when the disposal unit components ui state changes. This is required because the mailing unit requires a disposal unit component and overrides its ui /// private void OnDisposalUnitUIStateChange(EntityUid uid, MailingUnitComponent component, DisposalUnitUIStateUpdatedEvent args) { component.DisposalUnitInterfaceState = args.State; UpdateUserInterface(uid, component); } private void UpdateUserInterface(EntityUid uid, MailingUnitComponent component) { if (component.DisposalUnitInterfaceState == null) return; var state = new MailingUnitBoundUserInterfaceState(component.DisposalUnitInterfaceState, component.Target, component.TargetList.ShallowClone(), component.Tag); _userInterfaceSystem.SetUiState(uid, MailingUnitUiKey.Key, state); } private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args) { component.Target = args.Target; UpdateUserInterface(uid, component); } /// /// Checks if the unit is powered if an is present /// /// True if the power receiver component is powered or not present private bool IsPowered(EntityUid uid, ApcPowerReceiverComponent? powerReceiver = null) { if (Resolve(uid, ref powerReceiver) && !powerReceiver.Powered) return false; return true; } }