using Content.Server.DeviceNetwork.Components; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Random; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; 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 class DeviceNetworkSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; private readonly Dictionary> _connections = new(); private readonly Queue _packets = new(); public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnNetworkStarted); SubscribeLocalEvent(OnNetworkShutdown); } public override void Update(float frameTime) { base.Update(frameTime); while (_packets.Count > 0) { var packet = _packets.Dequeue(); if(packet.Broadcast) { BroadcastPacket(packet); continue; } SendPacket(packet); } } /// /// Manually connect an entity with a DeviceNetworkComponent. /// /// The Entity containing a DeviceNetworkComponent public void Connect(EntityUid uid) { if (EntityManager.TryGetComponent(uid, out var component)) { AddConnection(component); } } /// /// 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 when not broadcasting /// The frequency to send on /// The data to be sent /// Send to all devices on the same device network on the given frequency public void QueuePacket(EntityUid uid, string address, int frequency, NetworkPayload data, bool broadcast = false) { if (EntityManager.TryGetComponent(uid, out var component)) { var packet = new NetworkPacket { NetId = component.DeviceNetId, Address = address, Frequency = frequency, Broadcast = broadcast, Data = data, Sender = component }; _packets.Enqueue(packet); } } /// /// Manually disconnect an entity with a DeviceNetworkComponent. /// /// The Entity containing a DeviceNetworkComponent public void Disconnect(EntityUid uid) { if (EntityManager.TryGetComponent(uid, out var component)) { RemoveConnection(component); } } /// /// Automatically connect when an entity with a DeviceNetworkComponent starts up. /// private void OnNetworkStarted(EntityUid uid, DeviceNetworkComponent component, ComponentStartup args) { AddConnection(component); } /// /// Automatically disconnect when an entity with a DeviceNetworkComponent shuts down. /// private void OnNetworkShutdown(EntityUid uid, DeviceNetworkComponent component, ComponentShutdown args) { RemoveConnection(component); } private bool AddConnection(DeviceNetworkComponent connection) { var netId = connection.DeviceNetId; if (!_connections.ContainsKey(netId)) _connections[netId] = new List(); if (!_connections[netId].Contains(connection)) { connection.Address = GenerateValidAddress(netId); _connections[netId].Add(connection); connection.Open = true; return true; } return false; } private bool RemoveConnection(DeviceNetworkComponent connection) { connection.Address = ""; connection.Open = false; return _connections[connection.DeviceNetId].Remove(connection); } /// /// Generates a valid address by randomly generating one and checking if it already exists on the device network with the given device netId. /// private string GenerateValidAddress(DeviceNetworkComponent.ConnectionType netId) { var unique = false; var connections = _connections[netId]; var address = ""; while (!unique) { address = _random.Next().ToString("x"); unique = !connections.Exists(connection => connection.Address == address); } return address; } private List ConnectionsForFrequency(DeviceNetworkComponent.ConnectionType netId, int frequency) { if (!_connections.ContainsKey(netId)) return new List(); var result = _connections[netId].FindAll(connection => connection.Frequency == frequency); return result; } private bool TryGetConnectionWithAddress(DeviceNetworkComponent.ConnectionType netId, int frequency, string address, [NotNullWhen(true)] out DeviceNetworkComponent connection) { var connections = ConnectionsForFrequency(netId, frequency); var result = connections.Find(dvc => dvc.Address == address); if(result != null) { connection = result; return true; } connection = default!; return false; } private List ConnectionsWithReceiveAll(DeviceNetworkComponent.ConnectionType netId, int frequency) { if (!_connections.ContainsKey(netId)) return new List(); var result = _connections[netId].FindAll(device => device.Frequency == frequency && device.ReceiveAll); return result; } private void SendPacket(NetworkPacket packet) { if (!TryGetConnectionWithAddress(packet.NetId, packet.Frequency, packet.Address, out var connection)) return; var receivers = ConnectionsWithReceiveAll(packet.NetId, packet.Frequency); receivers.Add(connection); SendToConnections(receivers, packet); } private void BroadcastPacket(NetworkPacket packet) { var receivers = ConnectionsForFrequency(packet.NetId, packet.Frequency); SendToConnections(receivers, packet); } private void SendToConnections(List connections, NetworkPacket packet) { foreach (var connection in connections) { var beforeEvent = new BeforePacketSentEvent(packet.Sender.Owner.Uid); RaiseLocalEvent(connection.Owner.Uid, beforeEvent, false); if (!beforeEvent.Cancelled) { RaiseLocalEvent(connection.Owner.Uid, new PacketSentEvent(connection.Frequency, packet.Sender.Address, packet.Data, packet.Broadcast) , false); } } } internal struct NetworkPacket { public DeviceNetworkComponent.ConnectionType NetId; public int Frequency; public string Address; public bool Broadcast; public NetworkPayload Data; public DeviceNetworkComponent Sender; } } /// /// Event raised before a device network packet is send. /// Subscribed to by other systems to prevent the packet from being sent. /// public class BeforePacketSentEvent : CancellableEntityEventArgs { /// /// The EntityUid of the entity the packet was sent from. /// public EntityUid Sender; public BeforePacketSentEvent(EntityUid sender) { Sender = sender; } } /// /// Event raised when a device network packet gets sent. /// public class PacketSentEvent : EntityEventArgs { /// /// The frequency the packet is sent on. /// public int Frequency; /// /// The device network address of the sending entity. /// public string SenderAddress; /// /// The data that is beeing sent. /// public NetworkPayload Data; /// /// Whether the packet was broadcasted. /// public bool Broadcast; public PacketSentEvent(int frequency, string senderAddress, NetworkPayload data, bool broadcast) { Frequency = frequency; SenderAddress = senderAddress; Data = data; Broadcast = broadcast; } } }