2021-10-11 23:41:18 +02:00
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
{
/// <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 ! ;
2021-10-24 01:23:19 +02:00
private readonly Dictionary < DeviceNetworkComponent . ConnectionType , List < DeviceNetworkComponent > > _connections = new ( ) ;
2021-10-11 23:41:18 +02:00
private readonly Queue < NetworkPacket > _packets = new ( ) ;
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < DeviceNetworkComponent , ComponentStartup > ( OnNetworkStarted ) ;
SubscribeLocalEvent < DeviceNetworkComponent , ComponentShutdown > ( 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 ) ;
}
}
/// <summary>
/// Manually connect an entity with a DeviceNetworkComponent.
/// </summary>
/// <param name="uid">The Entity containing a DeviceNetworkComponent</param>
public void Connect ( EntityUid uid )
{
2021-12-03 12:51:44 +01:00
if ( EntityManager . TryGetComponent < DeviceNetworkComponent > ( uid , out var component ) )
2021-10-11 23:41:18 +02:00
{
AddConnection ( component ) ;
}
}
/// <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>
/// <param name="address">The address of the entity that the packet gets sent to when not broadcasting</param>
/// <param name="frequency">The frequency to send on</param>
/// <param name="data">The data to be sent</param>
/// <param name="broadcast">Send to all devices on the same device network on the given frequency</param>
public void QueuePacket ( EntityUid uid , string address , int frequency , NetworkPayload data , bool broadcast = false )
{
if ( EntityManager . TryGetComponent < DeviceNetworkComponent > ( uid , out var component ) )
{
var packet = new NetworkPacket
{
NetId = component . DeviceNetId ,
Address = address ,
Frequency = frequency ,
Broadcast = broadcast ,
Data = data ,
Sender = component
} ;
_packets . Enqueue ( packet ) ;
}
}
/// <summary>
/// Manually disconnect an entity with a DeviceNetworkComponent.
/// </summary>
/// <param name="uid">The Entity containing a DeviceNetworkComponent</param>
public void Disconnect ( EntityUid uid )
{
2021-12-03 12:51:44 +01:00
if ( EntityManager . TryGetComponent < DeviceNetworkComponent > ( uid , out var component ) )
2021-10-11 23:41:18 +02:00
{
RemoveConnection ( component ) ;
}
}
/// <summary>
/// Automatically connect when an entity with a DeviceNetworkComponent starts up.
/// </summary>
private void OnNetworkStarted ( EntityUid uid , DeviceNetworkComponent component , ComponentStartup args )
{
AddConnection ( component ) ;
}
/// <summary>
/// Automatically disconnect when an entity with a DeviceNetworkComponent shuts down.
/// </summary>
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 < DeviceNetworkComponent > ( ) ;
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 ) ;
}
/// <summary>
/// Generates a valid address by randomly generating one and checking if it already exists on the device network with the given device netId.
/// </summary>
2021-10-24 01:23:19 +02:00
private string GenerateValidAddress ( DeviceNetworkComponent . ConnectionType netId )
2021-10-11 23:41:18 +02:00
{
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 ;
}
2021-10-24 01:23:19 +02:00
private List < DeviceNetworkComponent > ConnectionsForFrequency ( DeviceNetworkComponent . ConnectionType netId , int frequency )
2021-10-11 23:41:18 +02:00
{
if ( ! _connections . ContainsKey ( netId ) )
return new List < DeviceNetworkComponent > ( ) ;
var result = _connections [ netId ] . FindAll ( connection = > connection . Frequency = = frequency ) ;
return result ;
}
2021-10-24 01:23:19 +02:00
private bool TryGetConnectionWithAddress ( DeviceNetworkComponent . ConnectionType netId , int frequency , string address , [ NotNullWhen ( true ) ] out DeviceNetworkComponent connection )
2021-10-11 23:41:18 +02:00
{
var connections = ConnectionsForFrequency ( netId , frequency ) ;
var result = connections . Find ( dvc = > dvc . Address = = address ) ;
if ( result ! = null )
{
connection = result ;
return true ;
}
connection = default ! ;
return false ;
}
2021-10-24 01:23:19 +02:00
private List < DeviceNetworkComponent > ConnectionsWithReceiveAll ( DeviceNetworkComponent . ConnectionType netId , int frequency )
2021-10-11 23:41:18 +02:00
{
if ( ! _connections . ContainsKey ( netId ) )
return new List < DeviceNetworkComponent > ( ) ;
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 ;
2021-10-24 01:23:19 +02:00
var receivers = ConnectionsWithReceiveAll ( packet . NetId , packet . Frequency ) ;
2021-10-11 23:41:18 +02:00
receivers . Add ( connection ) ;
SendToConnections ( receivers , packet ) ;
}
private void BroadcastPacket ( NetworkPacket packet )
{
2021-10-24 01:23:19 +02:00
var receivers = ConnectionsForFrequency ( packet . NetId , packet . Frequency ) ;
2021-10-11 23:41:18 +02:00
SendToConnections ( receivers , packet ) ;
}
private void SendToConnections ( List < DeviceNetworkComponent > connections , NetworkPacket packet )
{
foreach ( var connection in connections )
{
2021-12-03 15:53:09 +01:00
var beforeEvent = new BeforePacketSentEvent ( packet . Sender . Owner ) ;
RaiseLocalEvent ( connection . Owner , beforeEvent , false ) ;
2021-10-11 23:41:18 +02:00
if ( ! beforeEvent . Cancelled )
{
2021-12-03 15:53:09 +01:00
RaiseLocalEvent ( connection . Owner , new PacketSentEvent ( connection . Frequency , packet . Sender . Address , packet . Data , packet . Broadcast ) , false ) ;
2021-10-11 23:41:18 +02:00
}
}
}
internal struct NetworkPacket
{
2021-10-24 01:23:19 +02:00
public DeviceNetworkComponent . ConnectionType NetId ;
2021-10-11 23:41:18 +02:00
public int Frequency ;
public string Address ;
public bool Broadcast ;
public NetworkPayload Data ;
public DeviceNetworkComponent Sender ;
}
}
/// <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>
public EntityUid Sender ;
public BeforePacketSentEvent ( EntityUid sender )
{
Sender = sender ;
}
}
/// <summary>
/// Event raised when a device network packet gets sent.
/// </summary>
2022-02-16 00:23:23 -07:00
public sealed class PacketSentEvent : EntityEventArgs
2021-10-11 23:41:18 +02:00
{
/// <summary>
/// The frequency the packet is sent on.
/// </summary>
public int Frequency ;
/// <summary>
/// The device network address of the sending entity.
/// </summary>
public string SenderAddress ;
/// <summary>
/// The data that is beeing sent.
/// </summary>
public NetworkPayload Data ;
/// <summary>
/// Whether the packet was broadcasted.
/// </summary>
public bool Broadcast ;
public PacketSentEvent ( int frequency , string senderAddress , NetworkPayload data , bool broadcast )
{
Frequency = frequency ;
SenderAddress = senderAddress ;
Data = data ;
Broadcast = broadcast ;
}
}
}