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 ;
2022-05-16 16:52:37 -07:00
using Robust.Shared.Utility ;
using System.Buffers ;
2021-10-11 23:41:18 +02:00
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-05-16 16:52:37 -07:00
private readonly DeviceNet [ ] _networks = new DeviceNet [ 4 ] ; // Number of ConnectionType enum values
2022-04-09 00:27:10 +12:00
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 ) ;
2022-05-16 16:52:37 -07:00
InitNetwork ( ConnectionType . Private ) ;
InitNetwork ( ConnectionType . Wired ) ;
InitNetwork ( ConnectionType . Wireless ) ;
InitNetwork ( ConnectionType . Apc ) ;
2021-10-11 23:41:18 +02:00
}
public override void Update ( float frameTime )
{
2022-05-16 16:52:37 -07:00
while ( _packets . TryDequeue ( out var packet ) )
2021-10-11 23:41:18 +02:00
{
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
}
2022-05-16 16:52:37 -07:00
private void InitNetwork ( ConnectionType connectionType ) = >
_networks [ ( int ) connectionType ] = new ( connectionType , _random ) ;
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
}
2022-05-16 16:52:37 -07:00
private DeviceNet GetNetwork ( ConnectionType connectionType ) = >
_networks [ ( int ) connectionType ] ;
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-05-16 16:52:37 -07:00
GetNetwork ( component . DeviceNetId ) . Remove ( component ) ;
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 ;
2022-05-16 16:52:37 -07:00
return GetNetwork ( device . DeviceNetId ) . 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 ;
2022-05-16 16:52:37 -07:00
return GetNetwork ( device . DeviceNetId ) . Remove ( device ) ;
}
2021-10-11 23:41:18 +02:00
2022-05-16 16:52:37 -07:00
public void SetReceiveFrequency ( EntityUid uid , uint? frequency , DeviceNetworkComponent ? device = null )
{
if ( ! Resolve ( uid , ref device , false ) )
return ;
2022-04-09 00:27:10 +12:00
2022-05-16 16:52:37 -07:00
if ( device . ReceiveFrequency = = frequency ) return ;
2022-04-09 00:27:10 +12:00
2022-05-16 16:52:37 -07:00
var deviceNet = GetNetwork ( device . DeviceNetId ) ;
deviceNet . Remove ( device ) ;
device . ReceiveFrequency = frequency ;
deviceNet . Add ( device ) ;
2021-10-11 23:41:18 +02:00
}
2022-05-16 16:52:37 -07:00
public void SetTransmitFrequency ( EntityUid uid , uint? frequency , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-05-16 16:52:37 -07:00
if ( Resolve ( uid , ref device , false ) )
device . TransmitFrequency = frequency ;
}
2022-04-09 00:27:10 +12:00
2022-05-16 16:52:37 -07:00
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 ) ;
2021-10-11 23:41:18 +02:00
}
2022-05-16 16:52:37 -07:00
public void SetAddress ( EntityUid uid , string address , DeviceNetworkComponent ? device = null )
2021-10-11 23:41:18 +02:00
{
2022-05-16 16:52:37 -07:00
if ( ! Resolve ( uid , ref device , false ) )
return ;
if ( device . Address = = address & & device . CustomAddress = = true ) return ;
2022-04-09 00:27:10 +12:00
2022-05-16 16:52:37 -07:00
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 ) ;
2022-04-09 00:27:10 +12:00
}
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>
2022-05-16 16:52:37 -07:00
private bool TryGetDevice ( ConnectionType netId , string address , [ NotNullWhen ( true ) ] out DeviceNetworkComponent ? device ) = >
GetNetwork ( netId ) . Devices . TryGetValue ( address , out device ) ;
2021-10-11 23:41:18 +02:00
2022-04-09 00:27:10 +12:00
private void SendPacket ( DeviceNetworkPacketEvent packet )
2021-10-11 23:41:18 +02:00
{
2022-05-16 16:52:37 -07:00
var network = GetNetwork ( packet . NetId ) ;
2022-04-09 00:27:10 +12:00
if ( packet . Address = = null )
{
2022-05-16 16:52:37 -07:00
if ( network . ListeningDevices . TryGetValue ( packet . Frequency , out var devices ) )
{
var deviceCopy = ArrayPool < DeviceNetworkComponent > . Shared . Rent ( devices . Count ) ;
devices . CopyTo ( deviceCopy ) ;
SendToConnections ( deviceCopy . AsSpan ( 0 , devices . Count ) , packet ) ;
ArrayPool < DeviceNetworkComponent > . Shared . Return ( deviceCopy ) ;
}
2022-04-09 00:27:10 +12:00
}
else
{
2022-05-16 16:52:37 -07:00
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 < DeviceNetworkComponent > . Shared . Rent ( totalDevices ) ;
if ( devices ! = null )
{
devices . CopyTo ( deviceCopy ) ;
}
if ( hasTargetedDevice )
{
deviceCopy [ totalDevices - 1 ] = device ! ;
}
SendToConnections ( deviceCopy . AsSpan ( 0 , totalDevices ) , packet ) ;
ArrayPool < DeviceNetworkComponent > . Shared . Return ( deviceCopy ) ;
2022-04-09 00:27:10 +12:00
}
2021-10-11 23:41:18 +02:00
}
2022-05-16 16:52:37 -07:00
private void SendToConnections ( ReadOnlySpan < 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
}
}
/// <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 ;
}
}
}