2021-10-11 23:41:18 +02:00
using Content.Server.DeviceNetwork.Components ;
2022-04-09 00:27:10 +12:00
using Content.Shared.DeviceNetwork ;
2021-10-11 23:41:18 +02:00
using JetBrains.Annotations ;
2022-04-09 00:27:10 +12:00
using Robust.Shared.Prototypes ;
2021-10-11 23:41:18 +02:00
using Robust.Shared.Random ;
using System.Diagnostics.CodeAnalysis ;
2022-04-09 00:27:10 +12:00
using static Content . Server . DeviceNetwork . Components . DeviceNetworkComponent ;
2021-10-11 23:41:18 +02:00
namespace Content.Server.DeviceNetwork.Systems
{
/// <summary>
/// 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.
/// </summary>
[UsedImplicitly]
2022-02-16 00:23:23 -07:00
public sealed class DeviceNetworkSystem : EntitySystem
2021-10-11 23:41:18 +02:00
{
[Dependency] private readonly IRobustRandom _random = default ! ;
2022-04-09 00:27:10 +12:00
[Dependency] private readonly IPrototypeManager _protoMan = default ! ;
[Dependency] private readonly SharedTransformSystem _transformSystem = default ! ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
private readonly Dictionary < ConnectionType , DeviceNet > _networks = new ( ) ;
private readonly Queue < DeviceNetworkPacketEvent > _packets = new ( ) ;
2021-10-11 23:41:18 +02:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-04-09 00:27:10 +12:00
SubscribeLocalEvent < DeviceNetworkComponent , MapInitEvent > ( OnMapInit ) ;
2021-10-11 23:41:18 +02:00
SubscribeLocalEvent < DeviceNetworkComponent , ComponentShutdown > ( OnNetworkShutdown ) ;
}
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
while ( _packets . Count > 0 )
{
var packet = _packets . Dequeue ( ) ;
SendPacket ( packet ) ;
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="uid">The EntityUid of the sending entity</param>
2022-04-09 00:27:10 +12:00
/// <param name="address">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)</param>
2021-10-11 23:41:18 +02:00
/// <param name="frequency">The frequency to send on</param>
/// <param name="data">The data to be sent</param>
2022-04-09 00:27:10 +12:00
public void QueuePacket ( EntityUid uid , string? address , NetworkPayload data , uint? frequency = null , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
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 ) ) ;
2021-10-11 23:41:18 +02:00
}
/// <summary>
2022-04-09 00:27:10 +12:00
/// Automatically attempt to connect some devices when a map starts.
2021-10-11 23:41:18 +02:00
/// </summary>
2022-04-09 00:27:10 +12:00
private void OnMapInit ( EntityUid uid , DeviceNetworkComponent device , MapInitEvent args )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( device . ReceiveFrequency = = null
& & device . ReceiveFrequencyId ! = null
& & _protoMan . TryIndex < DeviceFrequencyPrototype > ( device . ReceiveFrequencyId , out var receive ) )
{
device . ReceiveFrequency = receive . Frequency ;
}
if ( device . TransmitFrequency = = null
& & device . TransmitFrequencyId ! = null
& & _protoMan . TryIndex < DeviceFrequencyPrototype > ( device . TransmitFrequencyId , out var xmit ) )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
device . TransmitFrequency = xmit . Frequency ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
if ( device . AutoConnect )
ConnectDevice ( uid , device ) ;
2021-10-11 23:41:18 +02:00
}
/// <summary>
2022-04-09 00:27:10 +12:00
/// Automatically disconnect when an entity with a DeviceNetworkComponent shuts down.
2021-10-11 23:41:18 +02:00
/// </summary>
2022-04-09 00:27:10 +12:00
private void OnNetworkShutdown ( EntityUid uid , DeviceNetworkComponent component , ComponentShutdown args )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
DisconnectDevice ( uid , component , false ) ;
2021-10-11 23:41:18 +02:00
}
/// <summary>
2022-04-09 00:27:10 +12:00
/// 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.
2021-10-11 23:41:18 +02:00
/// </summary>
2022-04-09 00:27:10 +12:00
public bool ConnectDevice ( EntityUid uid , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( ! Resolve ( uid , ref device , false ) )
return false ;
if ( ! _networks . TryGetValue ( device . DeviceNetId , out var network ) )
{
network = new ( device . DeviceNetId , _random ) ;
_networks [ device . DeviceNetId ] = network ;
}
return network . Add ( device ) ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
/// <summary>
/// Disconnect an entity with a DeviceNetworkComponent.
/// </summary>
public bool DisconnectDevice ( EntityUid uid , DeviceNetworkComponent ? device , bool preventAutoConnect = true )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( ! Resolve ( uid , ref device , false ) )
return false ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
// If manually disconnected, don't auto reconnect when a game state is loaded.
if ( preventAutoConnect )
device . AutoConnect = false ;
if ( ! _networks . TryGetValue ( device . DeviceNetId , out var network ) )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
return false ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
if ( ! network . Remove ( device ) )
return false ;
if ( network . Devices . Count = = 0 )
_networks . Remove ( device . DeviceNetId ) ;
return true ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
#region Get Device
/// <summary>
/// Get a list of devices listening on a given frequency on some network.
/// </summary>
private HashSet < DeviceNetworkComponent > GetListeningDevices ( ConnectionType netId , uint frequency )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( _networks . TryGetValue ( netId , out var network ) & & network . ListeningDevices . TryGetValue ( frequency , out var devices ) )
return devices ;
return new ( ) ;
2021-10-11 23:41:18 +02:00
}
/// <summary>
2022-04-09 00:27:10 +12:00
/// Get a list of devices listening for ANY transmission on a given frequency, rather than just broadcast & addressed events.
2021-10-11 23:41:18 +02:00
/// </summary>
2022-04-09 00:27:10 +12:00
private HashSet < DeviceNetworkComponent > GetRecieveAllDevices ( ConnectionType netId , uint frequency )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( _networks . TryGetValue ( netId , out var network ) & & network . ReceiveAllDevices . TryGetValue ( frequency , out var devices ) )
return devices ;
return new ( ) ;
}
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
/// <summary>
/// Try to find a device on a network using its address.
/// </summary>
private bool TryGetDevice ( ConnectionType netId , string address , [ NotNullWhen ( true ) ] out DeviceNetworkComponent ? device )
{
if ( ! _networks . TryGetValue ( netId , out var network ) )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
device = null ;
return false ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
return network . Devices . TryGetValue ( address , out device ) ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
#endregion
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
#region Packet Sending
private void SendPacket ( DeviceNetworkPacketEvent packet )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
HashSet < DeviceNetworkComponent > recipients ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
if ( packet . Address = = null )
{
// Broadcast to all listening devices
recipients = GetListeningDevices ( packet . NetId , packet . Frequency ) ;
}
else
{
// Add devices listening to all messages
recipients = new ( GetRecieveAllDevices ( packet . NetId , packet . Frequency ) ) ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
// add the intended recipient (if they are even listening).
if ( TryGetDevice ( packet . NetId , packet . Address , out var device ) & & device . ReceiveFrequency = = packet . Frequency )
recipients . Add ( device ) ;
}
SendToConnections ( recipients , packet ) ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
private void SendToConnections ( HashSet < DeviceNetworkComponent > connections , DeviceNetworkPacketEvent packet )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
var xform = Transform ( packet . Sender ) ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
BeforePacketSentEvent beforeEv = new ( packet . Sender , xform , _transformSystem . GetWorldPosition ( xform ) ) ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
foreach ( var connection in connections )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( connection . Owner = = packet . Sender )
continue ;
RaiseLocalEvent ( connection . Owner , beforeEv , false ) ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
if ( ! beforeEv . Cancelled )
RaiseLocalEvent ( connection . Owner , packet , false ) ;
else
beforeEv . Uncancel ( ) ;
}
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
#endregion
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
#region Component Setter Functions
public void SetReceiveFrequency ( EntityUid uid , uint? frequency , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( ! Resolve ( uid , ref device , false ) )
return ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
if ( device . ReceiveFrequency = = frequency )
return ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
if ( ! _networks . TryGetValue ( device . DeviceNetId , out var deviceNet ) | | ! deviceNet . UpdateReceiveFrequency ( device . Address , frequency ) )
device . ReceiveFrequency = frequency ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
public void SetTransmitFrequency ( EntityUid uid , uint? frequency , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
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 ) )
2021-10-11 23:41:18 +02:00
return ;
2022-04-09 00:27:10 +12:00
if ( device . ReceiveAll = = receiveAll )
return ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
if ( ! _networks . TryGetValue ( device . DeviceNetId , out var deviceNet ) | | ! deviceNet . UpdateReceiveAll ( device . Address , receiveAll ) )
device . ReceiveAll = receiveAll ;
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
public void SetAddress ( EntityUid uid , string address , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( ! Resolve ( uid , ref device , false ) )
return ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
if ( device . Address = = address )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
device . CustomAddress = true ;
return ;
}
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
if ( ! _networks . TryGetValue ( device . DeviceNetId , out var deviceNet ) | | ! deviceNet . UpdateAddress ( device . Address , address ) )
{
device . Address = address ;
device . CustomAddress = true ;
2021-10-11 23:41:18 +02:00
}
}
2022-04-09 00:27:10 +12:00
public void RandomizeAddress ( EntityUid uid , string address , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
if ( ! Resolve ( uid , ref device , false ) )
return ;
if ( ! _networks . TryGetValue ( device . DeviceNetId , out var deviceNet ) | | ! deviceNet . RandomizeAddress ( device . Address , address ) )
{
var prefix = string . IsNullOrWhiteSpace ( device . Prefix ) ? null : Loc . GetString ( device . Prefix ) ;
device . Address = $"{prefix}{_random.Next():x}" ;
device . CustomAddress = false ;
}
2021-10-11 23:41:18 +02:00
}
2022-04-09 00:27:10 +12:00
#endregion
2021-10-11 23:41:18 +02:00
}
/// <summary>
/// Event raised before a device network packet is send.
/// Subscribed to by other systems to prevent the packet from being sent.
/// </summary>
2022-02-16 00:23:23 -07:00
public sealed class BeforePacketSentEvent : CancellableEntityEventArgs
2021-10-11 23:41:18 +02:00
{
/// <summary>
/// The EntityUid of the entity the packet was sent from.
/// </summary>
2022-04-09 00:27:10 +12:00
public readonly EntityUid Sender ;
public readonly TransformComponent SenderTransform ;
/// <summary>
/// The senders current position in world coordinates.
/// </summary>
public readonly Vector2 SenderPosition ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
public BeforePacketSentEvent ( EntityUid sender , TransformComponent xform , Vector2 senderPosition )
2021-10-11 23:41:18 +02:00
{
Sender = sender ;
2022-04-09 00:27:10 +12:00
SenderTransform = xform ;
SenderPosition = senderPosition ;
2021-10-11 23:41:18 +02:00
}
}
/// <summary>
/// Event raised when a device network packet gets sent.
/// </summary>
2022-04-09 00:27:10 +12:00
public sealed class DeviceNetworkPacketEvent : EntityEventArgs
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
/// <summary>
/// The type of network that this packet is being sent on.
/// </summary>
public ConnectionType NetId ;
2021-10-11 23:41:18 +02:00
/// <summary>
/// The frequency the packet is sent on.
/// </summary>
2022-04-09 00:27:10 +12:00
public readonly uint Frequency ;
/// <summary>
/// Address of the intended recipient. Null if the message was broadcast.
/// </summary>
public string? Address ;
2021-10-11 23:41:18 +02:00
/// <summary>
/// The device network address of the sending entity.
/// </summary>
2022-04-09 00:27:10 +12:00
public readonly string SenderAddress ;
2021-10-11 23:41:18 +02:00
/// <summary>
2022-04-09 00:27:10 +12:00
/// The entity that sent the packet.
2021-10-11 23:41:18 +02:00
/// </summary>
2022-04-09 00:27:10 +12:00
public EntityUid Sender ;
2021-10-11 23:41:18 +02:00
/// <summary>
2022-04-09 00:27:10 +12:00
/// The data that is being sent.
2021-10-11 23:41:18 +02:00
/// </summary>
2022-04-09 00:27:10 +12:00
public readonly NetworkPayload Data ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
public DeviceNetworkPacketEvent ( ConnectionType netId , string? address , uint frequency , string senderAddress , EntityUid sender , NetworkPayload data )
2021-10-11 23:41:18 +02:00
{
2022-04-09 00:27:10 +12:00
NetId = netId ;
Address = address ;
2021-10-11 23:41:18 +02:00
Frequency = frequency ;
SenderAddress = senderAddress ;
2022-04-09 00:27:10 +12:00
Sender = sender ;
2021-10-11 23:41:18 +02:00
Data = data ;
}
}
}