using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Events;
using Content.Shared.DeviceNetwork.Systems;
using Robust.Shared.Random;
namespace Content.Server.DeviceNetwork.Systems;
///
public sealed partial class DeviceNetworkSystem : SharedDeviceNetworkSystem
{
[Dependency] private IRobustRandom _random = default!;
[Dependency] private SharedTransformSystem _transformSystem = default!;
[Dependency] private DeviceListSystem _deviceLists = default!;
[Dependency] private NetworkConfiguratorSystem _configurator = default!;
[Dependency] private EntityQuery _deviceQuery = default!;
///
/// Basically a cache of devices to connect them together faster.
///
private readonly Dictionary _networks = new(4);
///
/// Automatically attempt to connect some devices when a map starts.
///
[SubscribeLocalEvent]
private void OnMapInit(Entity ent, ref MapInitEvent args)
{
var device = ent.Comp;
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(ent.AsNullable());
}
///
/// Automatically disconnect when an entity with a DeviceNetworkComponent shuts down.
///
[SubscribeLocalEvent]
private void OnNetworkShutdown(Entity ent, ref ComponentShutdown args)
{
var component = ent.Comp;
foreach (var list in component.DeviceLists)
{
if (Deleted(list))
return;
_deviceLists.OnDeviceShutdown(list, ent);
}
foreach (var list in component.Configurators)
{
if (Deleted(list))
return;
_configurator.OnDeviceShutdown(list, ent);
}
if (TryGetNetwork(component.DeviceNetId, out var network))
network.Remove(ent);
}
///
/// Try to find a device on a network using its address.
///
private bool TryGetDevice(int netId, string address, [NotNullWhen(true)] out Device? device)
{
device = null;
if (!TryGetNetwork(netId, out var network)
|| !network.Devices.TryGetValue(address, out var foundDevice))
return false;
device = foundDevice;
return true;
}
///
/// Tries to get an already existing device network, and creates a new network if it doesn't exist.
///
/// False if the manager is not initialized.
///
private bool TryEnsureNetwork(int netId, [NotNullWhen(true)] out DeviceNet? network)
{
network = null;
if (_networks.TryGetValue(netId, out var deviceNet))
{
network = deviceNet;
return true;
}
var newDeviceNet = new DeviceNet(netId, _random);
_networks[netId] = newDeviceNet;
network = newDeviceNet;
return true;
}
///
/// Tries to get an already existing network.
///
/// False if the manager is not initialized, or the network wasn't found.
private bool TryGetNetwork(int netId, [NotNullWhen(true)] out DeviceNet? network)
{
network = null;
if (!_networks.TryGetValue(netId, out var deviceNet))
return false;
network = deviceNet;
return true;
}
private void SendPacket(ref DeviceNetworkPacketEvent packet) where T : INetworkPayload
{
if (!TryEnsureNetwork(packet.NetId, out var network))
return;
if (packet.Address == null)
{
// Broadcast to all listening devices
if (network.ListeningDevices.TryGetValue(packet.Frequency, out var devices) && CheckRecipientsList(packet, ref 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))
return;
if (!device.Value.ReceiveAll &&
device.Value.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.Value;
}
SendToConnections(deviceCopy.AsSpan(0, totalDevices), packet);
ArrayPool.Shared.Return(deviceCopy);
}
}
///
/// Sends the to the sending entity if the packets SendBeforeBroadcastAttemptEvent field is set to true.
/// The recipients is set to the modified recipient list.
///
/// false if the broadcast was canceled
private bool CheckRecipientsList(DeviceNetworkPacketEvent packet, ref HashSet recipients) where T : INetworkPayload
{
if (!packet.Sender.Comp.SendBroadcastAttemptEvent)
return true;
var beforeBroadcastAttemptEvent = new BeforeBroadcastAttemptEvent(recipients);
RaiseLocalEvent(packet.Sender, ref beforeBroadcastAttemptEvent, true);
if (beforeBroadcastAttemptEvent.Cancelled || beforeBroadcastAttemptEvent.ModifiedRecipients == null)
return false;
recipients = beforeBroadcastAttemptEvent.ModifiedRecipients;
return true;
}
private void SendToConnections(ReadOnlySpan connections, DeviceNetworkPacketEvent packet) where T : INetworkPayload
{
if (Deleted(packet.Sender))
{
return;
}
var xform = Transform(packet.Sender);
var senderPos = _transformSystem.GetWorldPosition(xform);
foreach (var connection in connections)
{
if (connection.Owner == packet.Sender.Owner)
continue;
var beforeEv = new BeforePacketSentEvent(packet.NetId,
packet.Address,
packet.Frequency,
packet.SenderAddress,
packet.Sender,
xform,
senderPos);
RaiseLocalEvent(connection.Owner, ref beforeEv);
if (beforeEv.Cancelled)
continue;
RaiseLocalEvent(connection.Owner, ref packet);
}
}
}