2022-12-19 10:41:47 +13:00
using System.Linq ;
2022-09-05 17:55:44 -07:00
using Content.Server.DeviceNetwork.Components ;
2022-06-10 03:28:24 +02:00
using Content.Shared.Access.Components ;
using Content.Shared.Access.Systems ;
using Content.Shared.Database ;
using Content.Shared.DeviceNetwork ;
using Content.Shared.Interaction ;
using Content.Shared.Popups ;
using Content.Shared.Verbs ;
using JetBrains.Annotations ;
using Robust.Server.GameObjects ;
using Robust.Shared.Audio ;
using Robust.Shared.Player ;
2023-02-26 18:48:57 +11:00
using Robust.Shared.Utility ;
2022-06-10 03:28:24 +02:00
namespace Content.Server.DeviceNetwork.Systems ;
[UsedImplicitly]
2022-09-05 17:55:44 -07:00
public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
2022-06-10 03:28:24 +02:00
{
[Dependency] private readonly DeviceListSystem _deviceListSystem = default ! ;
[Dependency] private readonly SharedPopupSystem _popupSystem = default ! ;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default ! ;
[Dependency] private readonly AccessReaderSystem _accessSystem = default ! ;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default ! ;
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-08-30 21:56:42 -07:00
SubscribeLocalEvent < NetworkConfiguratorComponent , MapInitEvent > ( OnMapInit ) ;
2022-06-10 03:28:24 +02:00
//Interaction
SubscribeLocalEvent < NetworkConfiguratorComponent , AfterInteractEvent > ( ( uid , component , args ) = > OnUsed ( uid , component , args . Target , args . User , args . CanReach ) ) ; //TODO: Replace with utility verb?
//Verbs
SubscribeLocalEvent < NetworkConfiguratorComponent , GetVerbsEvent < UtilityVerb > > ( OnAddInteractVerb ) ;
SubscribeLocalEvent < DeviceNetworkComponent , GetVerbsEvent < AlternativeVerb > > ( OnAddAlternativeSaveDeviceVerb ) ;
//UI
SubscribeLocalEvent < NetworkConfiguratorComponent , BoundUIClosedEvent > ( OnUiClosed ) ;
SubscribeLocalEvent < NetworkConfiguratorComponent , NetworkConfiguratorRemoveDeviceMessage > ( OnRemoveDevice ) ;
SubscribeLocalEvent < NetworkConfiguratorComponent , NetworkConfiguratorClearDevicesMessage > ( OnClearDevice ) ;
SubscribeLocalEvent < NetworkConfiguratorComponent , NetworkConfiguratorButtonPressedMessage > ( OnConfigButtonPressed ) ;
SubscribeLocalEvent < DeviceListComponent , ComponentRemove > ( OnComponentRemoved ) ;
}
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
foreach ( var component in EntityManager . EntityQuery < NetworkConfiguratorComponent > ( ) )
{
2023-02-26 18:48:57 +11:00
var uid = component . Owner ;
2022-06-10 03:28:24 +02:00
if ( component . ActiveDeviceList ! = null & & EntityManager . EntityExists ( component . ActiveDeviceList . Value ) & &
2023-02-26 18:48:57 +11:00
_interactionSystem . InRangeUnobstructed ( uid , component . ActiveDeviceList . Value ) )
2022-06-10 03:28:24 +02:00
{
2023-02-26 18:48:57 +11:00
continue ;
2022-06-10 03:28:24 +02:00
}
//The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device.
2023-02-26 18:48:57 +11:00
_uiSystem . GetUiOrNull ( uid , NetworkConfiguratorUiKey . Configure ) ? . CloseAll ( ) ;
2022-06-10 03:28:24 +02:00
}
}
2022-08-30 21:56:42 -07:00
private void OnMapInit ( EntityUid uid , NetworkConfiguratorComponent component , MapInitEvent args )
{
component . Devices . Clear ( ) ;
2022-08-30 22:06:06 -07:00
UpdateUiState ( uid , component ) ;
2022-08-30 21:56:42 -07:00
}
2022-06-10 03:28:24 +02:00
private void TryAddNetworkDevice ( EntityUid ? targetUid , EntityUid configuratorUid , EntityUid userUid ,
NetworkConfiguratorComponent ? configurator = null )
{
if ( ! Resolve ( configuratorUid , ref configurator ) )
return ;
TryAddNetworkDevice ( targetUid , userUid , configurator ) ;
}
private void TryAddNetworkDevice ( EntityUid ? targetUid , EntityUid userUid , NetworkConfiguratorComponent configurator , DeviceNetworkComponent ? device = null )
{
2022-06-28 22:54:08 +10:00
if ( ! targetUid . HasValue | | ! Resolve ( targetUid . Value , ref device , false ) )
2022-06-10 03:28:24 +02:00
return ;
2022-08-30 21:56:42 -07:00
var address = device . Address ;
if ( string . IsNullOrEmpty ( address ) )
2022-06-10 03:28:24 +02:00
{
2022-09-01 21:36:54 -07:00
// This primarily checks if the entity in question is pre-map init or not.
// This is because otherwise, anything that uses DeviceNetwork will not
// have an address populated, as all devices that use DeviceNetwork
// obtain their address on map init. If the entity is post-map init,
// and it still doesn't have an address, it will fail. Otherwise,
// it stores the entity's UID as a string for visual effect, that way
// a mapper can reference the devices they've gathered by UID, instead of
// by device network address. These entries, if the multitool is still in
// the map after it being saved, are cleared upon mapinit.
2022-08-30 21:56:42 -07:00
if ( MetaData ( targetUid . Value ) . EntityLifeStage = = EntityLifeStage . MapInitialized )
{
_popupSystem . PopupCursor ( Loc . GetString ( "network-configurator-device-failed" , ( "device" , targetUid ) ) ,
2022-12-19 10:41:47 +13:00
userUid ) ;
2022-08-30 21:56:42 -07:00
return ;
}
address = $"UID: {targetUid.Value.ToString()}" ;
2022-06-10 03:28:24 +02:00
}
if ( configurator . Devices . ContainsValue ( targetUid . Value ) )
{
2022-12-19 10:41:47 +13:00
_popupSystem . PopupCursor ( Loc . GetString ( "network-configurator-device-already-saved" , ( "device" , targetUid ) ) , userUid ) ;
2022-06-10 03:28:24 +02:00
return ;
}
2022-08-30 21:56:42 -07:00
configurator . Devices . Add ( address , targetUid . Value ) ;
2022-06-10 03:28:24 +02:00
_popupSystem . PopupCursor ( Loc . GetString ( "network-configurator-device-saved" , ( "address" , device . Address ) , ( "device" , targetUid ) ) ,
2022-12-19 10:41:47 +13:00
userUid , PopupType . Medium ) ;
2022-06-10 03:28:24 +02:00
UpdateUiState ( configurator . Owner , configurator ) ;
}
private bool AccessCheck ( EntityUid target , EntityUid ? user , NetworkConfiguratorComponent component )
{
if ( ! TryComp ( target , out AccessReaderComponent ? reader ) | | user = = null )
return false ;
if ( _accessSystem . IsAllowed ( user . Value , reader ) )
return true ;
2022-06-12 19:45:47 -04:00
SoundSystem . Play ( component . SoundNoAccess . GetSound ( ) , Filter . Pvs ( user . Value ) , target , AudioParams . Default . WithVolume ( - 2f ) . WithPitchScale ( 1.2f ) ) ;
2022-12-19 10:41:47 +13:00
_popupSystem . PopupEntity ( Loc . GetString ( "network-configurator-device-access-denied" ) , target , user . Value ) ;
2022-06-10 03:28:24 +02:00
return false ;
}
private void OnComponentRemoved ( EntityUid uid , DeviceListComponent component , ComponentRemove args )
{
2023-02-26 18:48:57 +11:00
_uiSystem . GetUiOrNull ( uid , NetworkConfiguratorUiKey . Configure ) ? . CloseAll ( ) ;
2022-06-10 03:28:24 +02:00
}
#region Interactions
/// <summary>
/// Either adds a device to the device list or shows the config ui if the target is ant entity with a device list
/// </summary>
private void OnUsed ( EntityUid uid , NetworkConfiguratorComponent component , EntityUid ? target , EntityUid user , bool canReach = true )
{
if ( ! canReach )
return ;
if ( ! HasComp < DeviceListComponent > ( target ) )
{
TryAddNetworkDevice ( target , user , component ) ;
return ;
}
OpenDeviceListUi ( target , user , component ) ;
}
#endregion
#region Verbs
/// <summary>
/// Adds the interaction verb which is either configuring device lists or saving a device onto the configurator
/// </summary>
private void OnAddInteractVerb ( EntityUid uid , NetworkConfiguratorComponent component , GetVerbsEvent < UtilityVerb > args )
{
if ( ! args . CanAccess | | ! args . CanInteract | | ! args . Using . HasValue | | ! HasComp < DeviceNetworkComponent > ( args . Target ) )
return ;
var isDeviceList = HasComp < DeviceListComponent > ( args . Target ) ;
UtilityVerb verb = new ( )
{
Text = Loc . GetString ( isDeviceList ? "network-configurator-configure" : "network-configurator-save-device" ) ,
2023-02-26 18:48:57 +11:00
Icon = isDeviceList ?
2023-04-20 20:16:01 +10:00
new SpriteSpecifier . Texture ( new ( "/Textures/Interface/VerbIcons/settings.svg.192dpi.png" ) ) :
new SpriteSpecifier . Texture ( new ( "/Textures/Interface/VerbIcons/in.svg.192dpi.png" ) ) ,
2022-06-10 03:28:24 +02:00
Act = ( ) = > OnUsed ( uid , component , args . Target , args . User ) ,
Impact = LogImpact . Low
} ;
args . Verbs . Add ( verb ) ;
}
/// <summary>
/// Powerful. Funny alt interact using.
/// Adds an alternative verb for saving a device on the configurator for entities with the <see cref="DeviceListComponent"/>.
/// Allows alt clicking entities with a network configurator that would otherwise trigger a different action like entities
/// with a <see cref="DeviceListComponent"/>
/// </summary>
private void OnAddAlternativeSaveDeviceVerb ( EntityUid uid , DeviceNetworkComponent component , GetVerbsEvent < AlternativeVerb > args )
{
if ( ! args . CanAccess | | ! args . CanInteract | | ! args . Using . HasValue | | ! HasComp < NetworkConfiguratorComponent > ( args . Using . Value )
| | ! HasComp < DeviceListComponent > ( args . Target ) )
return ;
AlternativeVerb verb = new ( )
{
Text = Loc . GetString ( "network-configurator-save-device" ) ,
2023-04-20 20:16:01 +10:00
Icon = new SpriteSpecifier . Texture ( new ( "/Textures/Interface/VerbIcons/in.svg.192dpi.png" ) ) ,
2022-06-10 03:28:24 +02:00
Act = ( ) = > TryAddNetworkDevice ( args . Target , args . Using . Value , args . User ) ,
Impact = LogImpact . Low
} ;
args . Verbs . Add ( verb ) ;
}
#endregion
#region UI
/// <summary>
/// Opens the config ui. It can be used to modify the devices in the targets device list.
/// </summary>
private void OpenDeviceListUi ( EntityUid ? targetUid , EntityUid userUid , NetworkConfiguratorComponent configurator )
{
if ( ! targetUid . HasValue | | ! TryComp ( userUid , out ActorComponent ? actor ) | | ! AccessCheck ( targetUid . Value , userUid , configurator ) )
return ;
configurator . ActiveDeviceList = targetUid ;
2022-09-05 17:55:44 -07:00
Dirty ( configurator ) ;
2022-06-10 03:28:24 +02:00
_uiSystem . GetUiOrNull ( configurator . Owner , NetworkConfiguratorUiKey . Configure ) ? . Open ( actor . PlayerSession ) ;
2022-09-05 18:22:39 -07:00
_uiSystem . TrySetUiState (
configurator . Owner ,
NetworkConfiguratorUiKey . Configure ,
new DeviceListUserInterfaceState (
_deviceListSystem . GetDeviceList ( configurator . ActiveDeviceList . Value )
. Select ( v = > ( v . Key , MetaData ( v . Value ) . EntityName ) ) . ToHashSet ( ) ) ) ;
2022-06-10 03:28:24 +02:00
}
/// <summary>
/// Sends the list of saved devices to the ui
/// </summary>
private void UpdateUiState ( EntityUid uid , NetworkConfiguratorComponent component )
{
HashSet < ( string address , string name ) > devices = new ( ) ;
HashSet < string > invalidDevices = new ( ) ;
foreach ( var pair in component . Devices )
{
if ( ! Exists ( pair . Value ) )
{
invalidDevices . Add ( pair . Key ) ;
continue ;
}
devices . Add ( ( pair . Key , Name ( pair . Value ) ) ) ;
}
//Remove saved entities that don't exist anymore
foreach ( var invalidDevice in invalidDevices )
{
component . Devices . Remove ( invalidDevice ) ;
}
_uiSystem . GetUiOrNull ( uid , NetworkConfiguratorUiKey . List ) ? . SetState ( new NetworkConfiguratorUserInterfaceState ( devices ) ) ;
}
/// <summary>
/// Clears the active device list when the ui is closed
/// </summary>
private void OnUiClosed ( EntityUid uid , NetworkConfiguratorComponent component , BoundUIClosedEvent args )
{
component . ActiveDeviceList = null ;
}
/// <summary>
/// Removes a device from the saved devices list
/// </summary>
private void OnRemoveDevice ( EntityUid uid , NetworkConfiguratorComponent component , NetworkConfiguratorRemoveDeviceMessage args )
{
component . Devices . Remove ( args . Address ) ;
UpdateUiState ( uid , component ) ;
}
/// <summary>
/// Clears the saved devices
/// </summary>
private void OnClearDevice ( EntityUid uid , NetworkConfiguratorComponent component , NetworkConfiguratorClearDevicesMessage _ )
{
component . Devices . Clear ( ) ;
UpdateUiState ( uid , component ) ;
}
/// <summary>
/// Handles all the button presses from the config ui.
/// Modifies, copies or visualizes the targets device list
/// </summary>
private void OnConfigButtonPressed ( EntityUid uid , NetworkConfiguratorComponent component , NetworkConfiguratorButtonPressedMessage args )
{
if ( ! component . ActiveDeviceList . HasValue )
return ;
2022-09-05 18:22:39 -07:00
var result = DeviceListUpdateResult . NoComponent ;
2022-06-10 03:28:24 +02:00
switch ( args . ButtonKey )
{
case NetworkConfiguratorButtonKey . Set :
2022-09-05 18:22:39 -07:00
result = _deviceListSystem . UpdateDeviceList ( component . ActiveDeviceList . Value , new HashSet < EntityUid > ( component . Devices . Values ) ) ;
2022-06-10 03:28:24 +02:00
break ;
case NetworkConfiguratorButtonKey . Add :
2022-09-05 18:22:39 -07:00
result = _deviceListSystem . UpdateDeviceList ( component . ActiveDeviceList . Value , new HashSet < EntityUid > ( component . Devices . Values ) , true ) ;
2022-06-10 03:28:24 +02:00
break ;
case NetworkConfiguratorButtonKey . Clear :
2022-09-05 18:22:39 -07:00
result = _deviceListSystem . UpdateDeviceList ( component . ActiveDeviceList . Value , new HashSet < EntityUid > ( ) ) ;
2022-06-10 03:28:24 +02:00
break ;
case NetworkConfiguratorButtonKey . Copy :
component . Devices = _deviceListSystem . GetDeviceList ( component . ActiveDeviceList . Value ) ;
UpdateUiState ( uid , component ) ;
2022-09-05 18:22:39 -07:00
return ;
2022-06-10 03:28:24 +02:00
case NetworkConfiguratorButtonKey . Show :
2022-09-05 17:55:44 -07:00
// This should be done client-side.
// _deviceListSystem.ToggleVisualization(component.ActiveDeviceList.Value);
2022-06-10 03:28:24 +02:00
break ;
}
2022-09-05 18:22:39 -07:00
var resultText = result switch
{
DeviceListUpdateResult . TooManyDevices = > Loc . GetString ( "network-configurator-too-many-devices" ) ,
DeviceListUpdateResult . UpdateOk = > Loc . GetString ( "network-configurator-update-ok" ) ,
_ = > "error"
} ;
2022-12-19 10:41:47 +13:00
_popupSystem . PopupCursor ( Loc . GetString ( resultText ) , args . Session , PopupType . Medium ) ;
2022-09-05 18:22:39 -07:00
_uiSystem . TrySetUiState (
component . Owner ,
NetworkConfiguratorUiKey . Configure ,
new DeviceListUserInterfaceState (
_deviceListSystem . GetDeviceList ( component . ActiveDeviceList . Value )
. Select ( v = > ( v . Key , MetaData ( v . Value ) . EntityName ) ) . ToHashSet ( ) ) ) ;
2022-06-10 03:28:24 +02:00
}
#endregion
}