using Content.Server.DeviceNetwork.Components; using Content.Shared.DeviceNetwork; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using System.Buffers; using System.Diagnostics.CodeAnalysis; using static Content.Server.DeviceNetwork.Components.DeviceNetworkComponent; namespace Content.Server.DeviceNetwork.Systems { /// /// Entity system that handles everything device network related. /// Device networking allows machines and devices to communicate with each other while adhering to restrictions like range or being connected to the same powernet. /// [UsedImplicitly] public sealed class DeviceNetworkSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; private readonly DeviceNet[] _networks = new DeviceNet[4]; // Number of ConnectionType enum values private readonly Queue _packets = new(); public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnNetworkShutdown); InitNetwork(ConnectionType.Private); InitNetwork(ConnectionType.Wired); InitNetwork(ConnectionType.Wireless); InitNetwork(ConnectionType.Apc); } public override void Update(float frameTime) { while (_packets.TryDequeue(out var packet)) { SendPacket(packet); } } /// /// Sends the given payload as a device network packet to the entity with the given address and frequency. /// Addresses are given to the DeviceNetworkComponent of an entity when connecting. /// /// The EntityUid of the sending entity /// The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender) /// The frequency to send on /// The data to be sent public void QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return; if (device.Address == null) return; frequency ??= device.TransmitFrequency; if (frequency != null) _packets.Enqueue(new DeviceNetworkPacketEvent(device.DeviceNetId, address, frequency.Value, device.Address, uid, data)); } private void InitNetwork(ConnectionType connectionType) => _networks[(int) connectionType] = new(connectionType, _random); /// /// Automatically attempt to connect some devices when a map starts. /// private void OnMapInit(EntityUid uid, DeviceNetworkComponent device, MapInitEvent args) { if (device.ReceiveFrequency == null && device.ReceiveFrequencyId != null && _protoMan.TryIndex(device.ReceiveFrequencyId, out var receive)) { device.ReceiveFrequency = receive.Frequency; } if (device.TransmitFrequency == null && device.TransmitFrequencyId != null && _protoMan.TryIndex(device.TransmitFrequencyId, out var xmit)) { device.TransmitFrequency = xmit.Frequency; } if (device.AutoConnect) ConnectDevice(uid, device); } private DeviceNet GetNetwork(ConnectionType connectionType) => _networks[(int) connectionType]; /// /// Automatically disconnect when an entity with a DeviceNetworkComponent shuts down. /// private void OnNetworkShutdown(EntityUid uid, DeviceNetworkComponent component, ComponentShutdown args) { GetNetwork(component.DeviceNetId).Remove(component); } /// /// Connect an entity with a DeviceNetworkComponent. Note that this will re-use an existing address if the /// device already had one configured. If there is a clash, the device cannot join the network. /// public bool ConnectDevice(EntityUid uid, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return false; return GetNetwork(device.DeviceNetId).Add(device); } /// /// Disconnect an entity with a DeviceNetworkComponent. /// public bool DisconnectDevice(EntityUid uid, DeviceNetworkComponent? device, bool preventAutoConnect = true) { if (!Resolve(uid, ref device, false)) return false; // If manually disconnected, don't auto reconnect when a game state is loaded. if (preventAutoConnect) device.AutoConnect = false; return GetNetwork(device.DeviceNetId).Remove(device); } public void SetReceiveFrequency(EntityUid uid, uint? frequency, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return; if (device.ReceiveFrequency == frequency) return; var deviceNet = GetNetwork(device.DeviceNetId); deviceNet.Remove(device); device.ReceiveFrequency = frequency; deviceNet.Add(device); } public void SetTransmitFrequency(EntityUid uid, uint? frequency, DeviceNetworkComponent? device = null) { if (Resolve(uid, ref device, false)) device.TransmitFrequency = frequency; } public void SetReceiveAll(EntityUid uid, bool receiveAll, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return; if (device.ReceiveAll == receiveAll) return; var deviceNet = GetNetwork(device.DeviceNetId); deviceNet.Remove(device); device.ReceiveAll = receiveAll; deviceNet.Add(device); } public void SetAddress(EntityUid uid, string address, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return; if (device.Address == address && device.CustomAddress == true) return; var deviceNet = GetNetwork(device.DeviceNetId); deviceNet.Remove(device); device.CustomAddress = true; device.Address = address; deviceNet.Add(device); } public void RandomizeAddress(EntityUid uid, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return; var deviceNet = GetNetwork(device.DeviceNetId); deviceNet.Remove(device); device.CustomAddress = false; device.Address = ""; deviceNet.Add(device); } /// /// Try to find a device on a network using its address. /// private bool TryGetDevice(ConnectionType netId, string address, [NotNullWhen(true)] out DeviceNetworkComponent? device) => GetNetwork(netId).Devices.TryGetValue(address, out device); private void SendPacket(DeviceNetworkPacketEvent packet) { var network = GetNetwork(packet.NetId); if (packet.Address == null) { if (network.ListeningDevices.TryGetValue(packet.Frequency, out var devices)) { var deviceCopy = ArrayPool.Shared.Rent(devices.Count); devices.CopyTo(deviceCopy); SendToConnections(deviceCopy.AsSpan(0, devices.Count), packet); ArrayPool.Shared.Return(deviceCopy); } } else { var totalDevices = 0; var hasTargetedDevice = false; if (network.ReceiveAllDevices.TryGetValue(packet.Frequency, out var devices)) { totalDevices += devices.Count; } if (TryGetDevice(packet.NetId, packet.Address, out var device) && !device.ReceiveAll && device.ReceiveFrequency == packet.Frequency) { totalDevices += 1; hasTargetedDevice = true; } var deviceCopy = ArrayPool.Shared.Rent(totalDevices); if (devices != null) { devices.CopyTo(deviceCopy); } if (hasTargetedDevice) { deviceCopy[totalDevices - 1] = device!; } SendToConnections(deviceCopy.AsSpan(0, totalDevices), packet); ArrayPool.Shared.Return(deviceCopy); } } private void SendToConnections(ReadOnlySpan connections, DeviceNetworkPacketEvent packet) { var xform = Transform(packet.Sender); BeforePacketSentEvent beforeEv = new(packet.Sender, xform, _transformSystem.GetWorldPosition(xform)); foreach (var connection in connections) { if (connection.Owner == packet.Sender) continue; RaiseLocalEvent(connection.Owner, beforeEv, false); if (!beforeEv.Cancelled) RaiseLocalEvent(connection.Owner, packet, false); else beforeEv.Uncancel(); } } } /// /// Event raised before a device network packet is send. /// Subscribed to by other systems to prevent the packet from being sent. /// public sealed class BeforePacketSentEvent : CancellableEntityEventArgs { /// /// The EntityUid of the entity the packet was sent from. /// public readonly EntityUid Sender; public readonly TransformComponent SenderTransform; /// /// The senders current position in world coordinates. /// public readonly Vector2 SenderPosition; public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition) { Sender = sender; SenderTransform = xform; SenderPosition = senderPosition; } } /// /// Event raised when a device network packet gets sent. /// public sealed class DeviceNetworkPacketEvent : EntityEventArgs { /// /// The type of network that this packet is being sent on. /// public ConnectionType NetId; /// /// The frequency the packet is sent on. /// public readonly uint Frequency; /// /// Address of the intended recipient. Null if the message was broadcast. /// public string? Address; /// /// The device network address of the sending entity. /// public readonly string SenderAddress; /// /// The entity that sent the packet. /// public EntityUid Sender; /// /// The data that is being sent. /// public readonly NetworkPayload Data; public DeviceNetworkPacketEvent(ConnectionType netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data) { NetId = netId; Address = address; Frequency = frequency; SenderAddress = senderAddress; Sender = sender; Data = data; } } }