diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 672f56476c..90eb8ac7f0 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -281,7 +281,7 @@ namespace Content.Client.Entry "TabletopGame", "LitOnPowered", "TriggerOnSignalReceived", - "DeviceNetworkComponent", + "DeviceNetwork", "WiredNetworkConnection", "WirelessNetworkConnection", "ExtensionCableReceiver", diff --git a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs index bacbc0b025..4c5f6466d9 100644 --- a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs +++ b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs @@ -20,15 +20,18 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork name: DummyNetworkDevice id: DummyNetworkDevice components: - - type: DeviceNetworkComponent - frequency: 100 + - type: DeviceNetwork + transmitFrequency: 100 + receiveFrequency: 100 - type: entity name: DummyWiredNetworkDevice id: DummyWiredNetworkDevice components: - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Wired + transmitFrequency: 0 + receiveFrequency: 0 - type: WiredNetworkConnection - type: ApcPowerReceiver @@ -36,8 +39,9 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork name: DummyWirelessNetworkDevice id: DummyWirelessNetworkDevice components: - - type: DeviceNetworkComponent - frequency: 100 + - type: DeviceNetwork + transmitFrequency: 100 + receiveFrequency: 100 deviceNetId: Wireless - type: WirelessNetworkConnection range: 100 @@ -83,18 +87,18 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork device1 = entityManager.SpawnEntity("DummyNetworkDevice", MapCoordinates.Nullspace); Assert.That(entityManager.TryGetComponent(device1, out networkComponent1), Is.True); - Assert.That(networkComponent1.Open, Is.True); + Assert.That(networkComponent1.ReceiveFrequency != null, Is.True); Assert.That(networkComponent1.Address, Is.Not.EqualTo(string.Empty)); device2 = entityManager.SpawnEntity("DummyNetworkDevice", MapCoordinates.Nullspace); Assert.That(entityManager.TryGetComponent(device2, out networkComponent2), Is.True); - Assert.That(networkComponent2.Open, Is.True); + Assert.That(networkComponent1.ReceiveFrequency != null, Is.True); Assert.That(networkComponent2.Address, Is.Not.EqualTo(string.Empty)); Assert.That(networkComponent1.Address, Is.Not.EqualTo(networkComponent2.Address)); - deviceNetSystem.QueuePacket(device1, networkComponent2.Address, networkComponent2.Frequency, payload); + deviceNetSystem.QueuePacket(device1, networkComponent2.Address, payload, networkComponent2.ReceiveFrequency.Value); }); await server.WaitRunTicks(1); @@ -147,18 +151,18 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork Assert.That(entityManager.TryGetComponent(device1, out networkComponent1), Is.True); Assert.That(entityManager.TryGetComponent(device1, out wirelessNetworkComponent), Is.True); - Assert.That(networkComponent1.Open, Is.True); + Assert.That(networkComponent1.ReceiveFrequency != null, Is.True); Assert.That(networkComponent1.Address, Is.Not.EqualTo(string.Empty)); device2 = entityManager.SpawnEntity("DummyWirelessNetworkDevice", new MapCoordinates(new Robust.Shared.Maths.Vector2(0,50), MapId.Nullspace)); Assert.That(entityManager.TryGetComponent(device2, out networkComponent2), Is.True); - Assert.That(networkComponent2.Open, Is.True); + Assert.That(networkComponent2.ReceiveFrequency != null, Is.True); Assert.That(networkComponent2.Address, Is.Not.EqualTo(string.Empty)); Assert.That(networkComponent1.Address, Is.Not.EqualTo(networkComponent2.Address)); - deviceNetSystem.QueuePacket(device1, networkComponent2.Address, networkComponent2.Frequency, payload); + deviceNetSystem.QueuePacket(device1, networkComponent2.Address, payload, networkComponent2.ReceiveFrequency.Value); }); await server.WaitRunTicks(1); @@ -174,7 +178,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork wirelessNetworkComponent.Range = 0; - deviceNetSystem.QueuePacket(device1, networkComponent2.Address, networkComponent2.Frequency, payload); + deviceNetSystem.QueuePacket(device1, networkComponent2.Address, payload, networkComponent2.ReceiveFrequency.Value); }); await server.WaitRunTicks(1); @@ -234,18 +238,18 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork Assert.That(entityManager.TryGetComponent(device1, out networkComponent1), Is.True); Assert.That(entityManager.TryGetComponent(device1, out wiredNetworkComponent), Is.True); - Assert.That(networkComponent1.Open, Is.True); + Assert.That(networkComponent1.ReceiveFrequency != null, Is.True); Assert.That(networkComponent1.Address, Is.Not.EqualTo(string.Empty)); device2 = entityManager.SpawnEntity("DummyWiredNetworkDevice", new MapCoordinates(new Robust.Shared.Maths.Vector2(0, 2), MapId.Nullspace)); Assert.That(entityManager.TryGetComponent(device2, out networkComponent2), Is.True); - Assert.That(networkComponent2.Open, Is.True); + Assert.That(networkComponent2.ReceiveFrequency != null, Is.True); Assert.That(networkComponent2.Address, Is.Not.EqualTo(string.Empty)); Assert.That(networkComponent1.Address, Is.Not.EqualTo(networkComponent2.Address)); - deviceNetSystem.QueuePacket(device1, networkComponent2.Address, networkComponent2.Frequency, payload); + deviceNetSystem.QueuePacket(device1, networkComponent2.Address, payload, networkComponent2.ReceiveFrequency.Value); }); await server.WaitRunTicks(1); @@ -256,7 +260,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork entityManager.SpawnEntity("CableApcExtension", grid.MapToGrid(new MapCoordinates(new Robust.Shared.Maths.Vector2(0, 1), MapId.Nullspace))); - deviceNetSystem.QueuePacket(device1, networkComponent2.Address, networkComponent2.Frequency, payload); + deviceNetSystem.QueuePacket(device1, networkComponent2.Address, payload, networkComponent2.ReceiveFrequency.Value); }); await server.WaitRunTicks(1); diff --git a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTestSystem.cs b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTestSystem.cs index 7345c5c659..331940730e 100644 --- a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTestSystem.cs +++ b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTestSystem.cs @@ -16,10 +16,10 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork { base.Initialize(); - SubscribeLocalEvent(OnPacketReceived); + SubscribeLocalEvent(OnPacketReceived); } - private void OnPacketReceived(EntityUid uid, DeviceNetworkComponent component, PacketSentEvent args) + private void OnPacketReceived(EntityUid uid, DeviceNetworkComponent component, DeviceNetworkPacketEvent args) { LastPayload = args.Data; } diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs index 22123854a1..e67ec672d6 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs @@ -42,9 +42,6 @@ namespace Content.Server.Atmos.Monitor.Systems [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; #region Device Network API - - public const int Freq = AtmosMonitorSystem.AtmosMonitorApcFreq; - /// /// Command to set device data within the air alarm's network. /// @@ -93,7 +90,7 @@ namespace Content.Server.Atmos.Monitor.Systems [AirAlarmSetData] = data }; - _deviceNet.QueuePacket(uid, address, Freq, payload); + _deviceNet.QueuePacket(uid, address, payload); } /// @@ -110,7 +107,7 @@ namespace Content.Server.Atmos.Monitor.Systems [DeviceNetworkConstants.Command] = AirAlarmSyncCmd }; - _deviceNet.QueuePacket(uid, string.Empty, Freq, payload, true); + _deviceNet.QueuePacket(uid, null, payload); } /// @@ -129,7 +126,7 @@ namespace Content.Server.Atmos.Monitor.Systems [DeviceNetworkConstants.Command] = AirAlarmSyncCmd }; - _deviceNet.QueuePacket(uid, address, Freq, payload); + _deviceNet.QueuePacket(uid, address, payload); } /// @@ -148,7 +145,7 @@ namespace Content.Server.Atmos.Monitor.Systems [AirAlarmSetMode] = mode }; - _deviceNet.QueuePacket(uid, string.Empty, Freq, payload, true); + _deviceNet.QueuePacket(uid, null, payload); } #endregion @@ -157,7 +154,7 @@ namespace Content.Server.Atmos.Monitor.Systems public override void Initialize() { - SubscribeLocalEvent(OnPacketRecv); + SubscribeLocalEvent(OnPacketRecv); SubscribeLocalEvent(OnAtmosUpdate); SubscribeLocalEvent(OnAtmosAlarm); SubscribeLocalEvent(OnPowerChanged); @@ -383,7 +380,7 @@ namespace Content.Server.Atmos.Monitor.Systems SetData(uid, address, devData); } - private void OnPacketRecv(EntityUid uid, AirAlarmComponent controller, PacketSentEvent args) + private void OnPacketRecv(EntityUid uid, AirAlarmComponent controller, DeviceNetworkPacketEvent args) { if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? cmd)) return; diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs index ae0f7d4f2d..7fc0aa4896 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs @@ -11,10 +11,10 @@ namespace Content.Server.Atmos.Monitor.Systems { public override void Initialize() { - SubscribeLocalEvent(OnPacketRecv); + SubscribeLocalEvent(OnPacketRecv); } - private void OnPacketRecv(EntityUid uid, AtmosAlarmableComponent component, PacketSentEvent args) + private void OnPacketRecv(EntityUid uid, AtmosAlarmableComponent component, DeviceNetworkPacketEvent args) { if (component.IgnoreAlarms) return; diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index 28a3d05539..6ff2bf5315 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -62,11 +62,6 @@ namespace Content.Server.Atmos.Monitor.Systems /// public const string AtmosMonitorAlarmNetMax = "atmos_monitor_alarm_net_max"; - /// - /// Frequency (all prototypes that use AtmosMonitor should use this) - /// - public const int AtmosMonitorApcFreq = 1621; - public override void Initialize() { SubscribeLocalEvent(OnAtmosMonitorInit); @@ -76,7 +71,7 @@ namespace Content.Server.Atmos.Monitor.Systems SubscribeLocalEvent(OnFireEvent); SubscribeLocalEvent(OnPowerChangedEvent); SubscribeLocalEvent(BeforePacketRecv); - SubscribeLocalEvent(OnPacketRecv); + SubscribeLocalEvent(OnPacketRecv); } private void OnAtmosMonitorInit(EntityUid uid, AtmosMonitorComponent component, ComponentInit args) @@ -158,7 +153,7 @@ namespace Content.Server.Atmos.Monitor.Systems if (!component.NetEnabled) args.Cancel(); } - private void OnPacketRecv(EntityUid uid, AtmosMonitorComponent component, PacketSentEvent args) + private void OnPacketRecv(EntityUid uid, AtmosMonitorComponent component, DeviceNetworkPacketEvent args) { // sync the internal 'last alarm state' from // the other alarms, so that we can calculate @@ -389,7 +384,7 @@ namespace Content.Server.Atmos.Monitor.Systems [AtmosMonitorAlarmSrc] = prototype != null ? prototype.ID : string.Empty }; - _deviceNetSystem.QueuePacket(monitor.Owner, string.Empty, AtmosMonitorApcFreq, payload, true); + _deviceNetSystem.QueuePacket(monitor.Owner, null, payload); monitor.NetworkAlarmStates.Clear(); Alert(uid, AtmosMonitorAlarmType.Normal, null, monitor); @@ -411,7 +406,7 @@ namespace Content.Server.Atmos.Monitor.Systems [AtmosMonitorAlarmSrc] = prototype != null ? prototype.ID : string.Empty }; - _deviceNetSystem.QueuePacket(monitor.Owner, string.Empty, AtmosMonitorApcFreq, payload, true); + _deviceNetSystem.QueuePacket(monitor.Owner, null, payload); } /// @@ -445,7 +440,7 @@ namespace Content.Server.Atmos.Monitor.Systems [AtmosMonitorAlarmSrc] = source }; - _deviceNetSystem.QueuePacket(monitor.Owner, string.Empty, AtmosMonitorApcFreq, payload, true); + _deviceNetSystem.QueuePacket(monitor.Owner, null, payload); } /// diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index cb2407924d..fd126d31aa 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -35,7 +35,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems SubscribeLocalEvent(OnGasVentPumpLeaveAtmosphere); SubscribeLocalEvent(OnAtmosAlarm); SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnPacketRecv); + SubscribeLocalEvent(OnPacketRecv); } private void OnGasVentPumpUpdated(EntityUid uid, GasVentPumpComponent vent, AtmosDeviceUpdateEvent args) @@ -162,7 +162,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems component.Enabled = args.Powered; } - private void OnPacketRecv(EntityUid uid, GasVentPumpComponent component, PacketSentEvent args) + private void OnPacketRecv(EntityUid uid, GasVentPumpComponent component, DeviceNetworkPacketEvent args) { if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent netConn) || !EntityManager.TryGetComponent(uid, out AtmosAlarmableComponent alarmable) @@ -177,7 +177,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems payload.Add(DeviceNetworkConstants.Command, AirAlarmSystem.AirAlarmSyncData); payload.Add(AirAlarmSystem.AirAlarmSyncData, component.ToAirAlarmData()); - _deviceNetSystem.QueuePacket(uid, args.SenderAddress, AirAlarmSystem.Freq, payload); + _deviceNetSystem.QueuePacket(uid, args.SenderAddress, payload, device: netConn); return; case AirAlarmSystem.AirAlarmSetData: @@ -189,7 +189,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems payload.Add(DeviceNetworkConstants.Command, AirAlarmSystem.AirAlarmSetDataStatus); payload.Add(AirAlarmSystem.AirAlarmSetDataStatus, true); - _deviceNetSystem.QueuePacket(uid, string.Empty, AirAlarmSystem.Freq, payload, true); + _deviceNetSystem.QueuePacket(uid, null, payload, device: netConn); return; } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index daa93feaf5..3344f8dc67 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -36,7 +36,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems SubscribeLocalEvent(OnVentScrubberLeaveAtmosphere); SubscribeLocalEvent(OnAtmosAlarm); SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnPacketRecv); + SubscribeLocalEvent(OnPacketRecv); } private void OnVentScrubberUpdated(EntityUid uid, GasVentScrubberComponent scrubber, AtmosDeviceUpdateEvent args) @@ -136,7 +136,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnPowerChanged(EntityUid uid, GasVentScrubberComponent component, PowerChangedEvent args) => component.Enabled = args.Powered; - private void OnPacketRecv(EntityUid uid, GasVentScrubberComponent component, PacketSentEvent args) + private void OnPacketRecv(EntityUid uid, GasVentScrubberComponent component, DeviceNetworkPacketEvent args) { if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent netConn) || !EntityManager.TryGetComponent(uid, out AtmosAlarmableComponent alarmable) @@ -151,7 +151,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems payload.Add(DeviceNetworkConstants.Command, AirAlarmSystem.AirAlarmSyncData); payload.Add(AirAlarmSystem.AirAlarmSyncData, component.ToAirAlarmData()); - _deviceNetSystem.QueuePacket(uid, args.SenderAddress, AirAlarmSystem.Freq, payload); + _deviceNetSystem.QueuePacket(uid, args.SenderAddress, payload, device: netConn); return; case AirAlarmSystem.AirAlarmSetData: @@ -163,7 +163,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems payload.Add(DeviceNetworkConstants.Command, AirAlarmSystem.AirAlarmSetDataStatus); payload.Add(AirAlarmSystem.AirAlarmSetDataStatus, true); - _deviceNetSystem.QueuePacket(uid, string.Empty, AirAlarmSystem.Freq, payload, true); + _deviceNetSystem.QueuePacket(uid, null, payload, device: netConn); return; } diff --git a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs b/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs index 9e8f6f0f07..3e7b975577 100644 --- a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs +++ b/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs @@ -1,17 +1,16 @@ -using System; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; +using Content.Server.DeviceNetwork.Systems; +using Content.Shared.DeviceNetwork; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.DeviceNetwork.Components { [RegisterComponent] - [ComponentProtoName("DeviceNetworkComponent")] + [Friend(typeof(DeviceNetworkSystem), typeof(DeviceNet))] public sealed class DeviceNetworkComponent : Component { /// - /// Valid device network NetIDs. - /// The netID is used to separate device networks that shouldn't interact with each other e.g. wireless and wired. + /// Valid device network NetIDs. The netID is used to separate device networks that shouldn't interact with + /// each other e.g. wireless and wired. /// [Serializable] public enum ConnectionType @@ -21,20 +20,74 @@ namespace Content.Server.DeviceNetwork.Components Wireless, Apc } + // TODO allow devices to join more than one network? + + // TODO if wireless/wired is determined by ConnectionType, what is the point of WirelessNetworkComponent & the + // other network-type-specific components? Shouldn't DeviceNetId determine conectivity checks? [DataField("deviceNetId")] public ConnectionType DeviceNetId { get; set; } = ConnectionType.Private; - [DataField("frequency")] - public int Frequency { get; set; } = 0; + /// + /// The frequency that this device is listening on. + /// + [DataField("receiveFrequency")] + public uint? ReceiveFrequency; - [ViewVariables] - public bool Open; + /// + /// frequency prototype. Used to select a default frequency to listen to on. Used when the map is + /// initialized. + /// + [DataField("receiveFrequencyId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? ReceiveFrequencyId; - [ViewVariables] + /// + /// The frequency that this device going to try transmit on. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("transmitFrequency")] + public uint? TransmitFrequency; + + /// + /// frequency prototype. Used to select a default frequency to transmit on. Used when the map is + /// initialized. + /// + [DataField("transmitFrequencyId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? TransmitFrequencyId; + + /// + /// The address of the device, either on the network it is currently connected to or whatever address it + /// most recently used. + /// + [DataField("address")] public string Address = string.Empty; + /// + /// If true, the address was customized and should be preserved across networks. If false, a randomly + /// generated address will be created whenever this device connects to a network. + /// + [DataField("customAddress")] + public bool CustomAddress = false; + + /// + /// Prefix to prepend to any automatically generated addresses. Helps players to identify devices. This gets + /// localized. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("prefix")] + public string? Prefix; + + /// + /// Whether the device should listen for all device messages, regardless of the intended recipient. + /// [DataField("receiveAll")] public bool ReceiveAll; + + /// + /// Whether the device should attempt to join the network on map init. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("autoConnect")] + public bool AutoConnect = true; } } diff --git a/Content.Server/DeviceNetwork/DeviceNet.cs b/Content.Server/DeviceNetwork/DeviceNet.cs new file mode 100644 index 0000000000..dee192affa --- /dev/null +++ b/Content.Server/DeviceNetwork/DeviceNet.cs @@ -0,0 +1,236 @@ +using Content.Server.DeviceNetwork.Components; +using Robust.Shared.Random; +using static Content.Server.DeviceNetwork.Components.DeviceNetworkComponent; + +namespace Content.Server.DeviceNetwork; + +/// +/// Data class for storing and retrieving information about devices connected to a device network. +/// +/// +/// This basically just makes accessible via their addresses and frequencies on +/// some network. +/// +public sealed class DeviceNet +{ + /// + /// Devices, mapped by their "Address", which is just an int that gets converted to Hex for displaying to users. + /// This dictionary contains all devices connected to this network, though they may not be listening to any + /// specific frequency. + /// + public readonly Dictionary Devices = new(); + + /// + /// Devices listening on a given frequency. + /// + public readonly Dictionary> ListeningDevices = new(); + + /// + /// Devices listening to all packets on a given frequency, regardless of the intended recipient. + /// + public readonly Dictionary> ReceiveAllDevices = new(); + + private readonly IRobustRandom _random; + public readonly ConnectionType Type; + + public DeviceNet(ConnectionType netType, IRobustRandom random) + { + _random = random; + Type = netType; + } + + /// + /// Add a device to the network. + /// + public bool Add(DeviceNetworkComponent device) + { + if (device.CustomAddress) + { + // Only add if the device's existing address is available. + if (!Devices.TryAdd(device.Address, device)) + return false; + } + else + { + // Randomly generate a new address if the existing random one is invalid. Otherwise, keep the existing address + if (string.IsNullOrWhiteSpace(device.Address) || Devices.ContainsKey(device.Address)) + device.Address = GenerateValidAddress(device.Prefix); + + Devices[device.Address] = device; + } + + if (device.ReceiveFrequency is not uint freq) + return true; + + if (!ListeningDevices.TryGetValue(freq, out var devices)) + ListeningDevices[freq] = devices = new(); + + devices.Add(device); + + if (!device.ReceiveAll) + return true; + + if (!ReceiveAllDevices.TryGetValue(freq, out var receiveAlldevices)) + ReceiveAllDevices[freq] = receiveAlldevices = new(); + + receiveAlldevices.Add(device); + return true; + } + + /// + /// Remove a device from the network. + /// + public bool Remove(DeviceNetworkComponent device) + { + if (device.Address == null || Devices.Remove(device.Address)) + return false; + + if (device.ReceiveFrequency is not uint freq) + return true; + + if (ListeningDevices.TryGetValue(freq, out var listening)) + { + listening.Remove(device); + if (listening.Count == 0) + ListeningDevices.Remove(freq); + } + + if (device.ReceiveAll && ReceiveAllDevices.TryGetValue(freq, out var receiveAll)) + { + receiveAll.Remove(device); + if (receiveAll.Count == 0) + ListeningDevices.Remove(freq); + } + + return true; + } + + /// + /// Give an existing device a new randomly generated address. Useful if the device's address prefix was updated + /// and they want a new address to reflect that, or something like that. + /// + public bool RandomizeAddress(string oldAddress, string? prefix = null) + { + if (!Devices.Remove(oldAddress, out var device)) + return false; + + device.Address = GenerateValidAddress(prefix ?? device.Prefix); + device.CustomAddress = false; + Devices[device.Address] = device; + return true; + } + + /// + /// Update the address of an existing device. + /// + public bool UpdateAddress(string oldAddress, string newAddress) + { + if (Devices.ContainsKey(newAddress)) + return false; + + if (!Devices.Remove(oldAddress, out var device)) + return false; + + device.Address = newAddress; + device.CustomAddress = true; + Devices[newAddress] = device; + return true; + } + + /// + /// Make an existing network device listen to a new frequency. + /// + public bool UpdateReceiveFrequency(string address, uint? newFrequency) + { + if (!Devices.TryGetValue(address, out var device)) + return false; + + if (device.ReceiveFrequency == newFrequency) + return true; + + if (device.ReceiveFrequency is uint freq) + { + if (ListeningDevices.TryGetValue(freq, out var listening)) + { + listening.Remove(device); + if (listening.Count == 0) + ListeningDevices.Remove(freq); + } + + if (device.ReceiveAll && ReceiveAllDevices.TryGetValue(freq, out var receiveAll)) + { + receiveAll.Remove(device); + if (receiveAll.Count == 0) + ListeningDevices.Remove(freq); + } + } + + device.ReceiveFrequency = newFrequency; + + if (newFrequency == null) + return true; + + if (!ListeningDevices.TryGetValue(newFrequency.Value, out var devices)) + ListeningDevices[newFrequency.Value] = devices = new(); + + devices.Add(device); + + if (!device.ReceiveAll) + return true; + + if (!ReceiveAllDevices.TryGetValue(newFrequency.Value, out var receiveAlldevices)) + ReceiveAllDevices[newFrequency.Value] = receiveAlldevices = new(); + + receiveAlldevices.Add(device); + return true; + } + + /// + /// Make an existing network device listen to a new frequency. + /// + public bool UpdateReceiveAll(string address, bool receiveAll) + { + if (!Devices.TryGetValue(address, out var device)) + return false; + + if (device.ReceiveAll == receiveAll) + return true; + + device.ReceiveAll = receiveAll; + + if (device.ReceiveFrequency is not uint freq) + return true; + + // remove or add to set of listening devices + + HashSet? devices; + if (receiveAll) + { + if (!ReceiveAllDevices.TryGetValue(freq, out devices)) + ReceiveAllDevices[freq] = devices = new(); + devices.Add(device); + } + else if (ReceiveAllDevices.TryGetValue(freq, out devices)) + { + devices.Remove(device); + if (devices.Count == 0) + ReceiveAllDevices.Remove(freq); + } + + return true; + } + + /// + /// Generates a valid address by randomly generating one and checking if it already exists on the network. + /// + private string GenerateValidAddress(string? prefix) + { + prefix = string.IsNullOrWhiteSpace(prefix) ? null : Loc.GetString(prefix); + string address; + do + address = $"{prefix}{_random.Next():x}"; + while (Devices.ContainsKey(address)); + + return address; + } +} diff --git a/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs b/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs index b8d53ee423..f56d5a0096 100644 --- a/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs +++ b/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs @@ -5,11 +5,6 @@ namespace Content.Server.DeviceNetwork /// public static class DeviceNetworkConstants { - /// - /// Invalid address used for broadcasting - /// - public const string NullAddress = "######"; - #region Commands /// diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs index 7d09c57a91..ad1375e879 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs @@ -1,10 +1,10 @@ using Content.Server.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork; using JetBrains.Annotations; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; +using Robust.Shared.Prototypes; using Robust.Shared.Random; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using static Content.Server.DeviceNetwork.Components.DeviceNetworkComponent; namespace Content.Server.DeviceNetwork.Systems { @@ -16,15 +16,17 @@ namespace Content.Server.DeviceNetwork.Systems public sealed class DeviceNetworkSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - private readonly Dictionary> _connections = new(); - private readonly Queue _packets = new(); + private readonly Dictionary _networks = new(); + private readonly Queue _packets = new(); public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnNetworkStarted); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnNetworkShutdown); } @@ -36,72 +38,53 @@ namespace Content.Server.DeviceNetwork.Systems { 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 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) /// 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) + public void QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, DeviceNetworkComponent? device = null) { - if (EntityManager.TryGetComponent(uid, out var component)) - { - var packet = new NetworkPacket - { - NetId = component.DeviceNetId, - Address = address, - Frequency = frequency, - Broadcast = broadcast, - Data = data, - Sender = component - }; + if (!Resolve(uid, ref device, false)) + return; - _packets.Enqueue(packet); - } + if (device.Address == null) + return; + + frequency ??= device.TransmitFrequency; + + if (frequency != null) + _packets.Enqueue(new DeviceNetworkPacketEvent(device.DeviceNetId, address, frequency.Value, device.Address, uid, data)); } /// - /// Manually disconnect an entity with a DeviceNetworkComponent. + /// Automatically attempt to connect some devices when a map starts. /// - /// The Entity containing a DeviceNetworkComponent - public void Disconnect(EntityUid uid) + private void OnMapInit(EntityUid uid, DeviceNetworkComponent device, MapInitEvent args) { - if (EntityManager.TryGetComponent(uid, out var component)) + if (device.ReceiveFrequency == null + && device.ReceiveFrequencyId != null + && _protoMan.TryIndex(device.ReceiveFrequencyId, out var receive)) { - RemoveConnection(component); + device.ReceiveFrequency = receive.Frequency; } - } - /// - /// Automatically connect when an entity with a DeviceNetworkComponent starts up. - /// - private void OnNetworkStarted(EntityUid uid, DeviceNetworkComponent component, ComponentStartup args) - { - AddConnection(component); + if (device.TransmitFrequency == null + && device.TransmitFrequencyId != null + && _protoMan.TryIndex(device.TransmitFrequencyId, out var xmit)) + { + device.TransmitFrequency = xmit.Frequency; + } + + if (device.AutoConnect) + ConnectDevice(uid, device); } /// @@ -109,127 +92,197 @@ namespace Content.Server.DeviceNetwork.Systems /// 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); + DisconnectDevice(uid, component, false); } /// - /// Generates a valid address by randomly generating one and checking if it already exists on the device network with the given device netId. + /// 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. /// - private string GenerateValidAddress(DeviceNetworkComponent.ConnectionType netId) + public bool ConnectDevice(EntityUid uid, DeviceNetworkComponent? device = null) { - var unique = false; - var connections = _connections[netId]; - var address = ""; + if (!Resolve(uid, ref device, false)) + return false; - while (!unique) + if (!_networks.TryGetValue(device.DeviceNetId, out var network)) { - address = _random.Next().ToString("x"); - unique = !connections.Exists(connection => connection.Address == address); + network = new(device.DeviceNetId, _random); + _networks[device.DeviceNetId] = network; } - return address; + return network.Add(device); } - private List ConnectionsForFrequency(DeviceNetworkComponent.ConnectionType netId, int frequency) + /// + /// Disconnect an entity with a DeviceNetworkComponent. + /// + public bool DisconnectDevice(EntityUid uid, DeviceNetworkComponent? device, bool preventAutoConnect = true) { - if (!_connections.ContainsKey(netId)) - return new List(); + if (!Resolve(uid, ref device, false)) + return false; - var result = _connections[netId].FindAll(connection => connection.Frequency == frequency); + // If manually disconnected, don't auto reconnect when a game state is loaded. + if (preventAutoConnect) + device.AutoConnect = false; - 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) + if (!_networks.TryGetValue(device.DeviceNetId, out var network)) { - connection = result; - return true; + return false; } - connection = default!; - return false; + if (!network.Remove(device)) + return false; + + if (network.Devices.Count == 0) + _networks.Remove(device.DeviceNetId); + + return true; } - private List ConnectionsWithReceiveAll(DeviceNetworkComponent.ConnectionType netId, int frequency) + #region Get Device + /// + /// Get a list of devices listening on a given frequency on some network. + /// + private HashSet GetListeningDevices(ConnectionType netId, uint frequency) { - if (!_connections.ContainsKey(netId)) - return new List(); + if (_networks.TryGetValue(netId, out var network) && network.ListeningDevices.TryGetValue(frequency, out var devices)) + return devices; - var result = _connections[netId].FindAll(device => device.Frequency == frequency && device.ReceiveAll); - - return result; + return new(); } - private void SendPacket(NetworkPacket packet) + /// + /// Get a list of devices listening for ANY transmission on a given frequency, rather than just broadcast & addressed events. + /// + private HashSet GetRecieveAllDevices(ConnectionType netId, uint frequency) { - if (!TryGetConnectionWithAddress(packet.NetId, packet.Frequency, packet.Address, out var connection)) - return; + if (_networks.TryGetValue(netId, out var network) && network.ReceiveAllDevices.TryGetValue(frequency, out var devices)) + return devices; - var receivers = ConnectionsWithReceiveAll(packet.NetId, packet.Frequency); - receivers.Add(connection); - - SendToConnections(receivers, packet); + return new(); } - private void BroadcastPacket(NetworkPacket packet) + /// + /// Try to find a device on a network using its address. + /// + private bool TryGetDevice(ConnectionType netId, string address, [NotNullWhen(true)] out DeviceNetworkComponent? device) { - var receivers = ConnectionsForFrequency(packet.NetId, packet.Frequency); - SendToConnections(receivers, packet); + if (!_networks.TryGetValue(netId, out var network)) + { + device = null; + return false; + } + + return network.Devices.TryGetValue(address, out device); + } + #endregion + + #region Packet Sending + private void SendPacket(DeviceNetworkPacketEvent packet) + { + HashSet recipients; + + 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)); + + // 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); } - private void SendToConnections(List connections, NetworkPacket packet) + private void SendToConnections(HashSet connections, DeviceNetworkPacketEvent packet) { + var xform = Transform(packet.Sender); + + BeforePacketSentEvent beforeEv = new(packet.Sender, xform, _transformSystem.GetWorldPosition(xform)); + foreach (var connection in connections) { - var beforeEvent = new BeforePacketSentEvent(packet.Sender.Owner); - RaiseLocalEvent(connection.Owner, beforeEvent, false); + if (connection.Owner == packet.Sender) + continue; - if (!beforeEvent.Cancelled) - { - RaiseLocalEvent(connection.Owner, new PacketSentEvent(connection.Frequency, packet.Sender.Address, packet.Data, packet.Broadcast) , false); - } + RaiseLocalEvent(connection.Owner, beforeEv, false); + + if (!beforeEv.Cancelled) + RaiseLocalEvent(connection.Owner, packet, false); + else + beforeEv.Uncancel(); + } + } + #endregion + + #region Component Setter Functions + public void SetReceiveFrequency(EntityUid uid, uint? frequency, DeviceNetworkComponent? device = null) + { + if (!Resolve(uid, ref device, false)) + return; + + if (device.ReceiveFrequency == frequency) + return; + + if (!_networks.TryGetValue(device.DeviceNetId, out var deviceNet) || !deviceNet.UpdateReceiveFrequency(device.Address, frequency)) + device.ReceiveFrequency = frequency; + } + + public void SetTransmitFrequency(EntityUid uid, uint? frequency, DeviceNetworkComponent? device = null) + { + 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)) + return; + + if (device.ReceiveAll == receiveAll) + return; + + if (!_networks.TryGetValue(device.DeviceNetId, out var deviceNet) || !deviceNet.UpdateReceiveAll(device.Address, receiveAll)) + device.ReceiveAll = receiveAll; + } + + public void SetAddress(EntityUid uid, string address, DeviceNetworkComponent? device = null) + { + if (!Resolve(uid, ref device, false)) + return; + + if (device.Address == address) + { + device.CustomAddress = true; + return; + } + + if (!_networks.TryGetValue(device.DeviceNetId, out var deviceNet) || !deviceNet.UpdateAddress(device.Address, address)) + { + device.Address = address; + device.CustomAddress = true; } } - internal struct NetworkPacket + public void RandomizeAddress(EntityUid uid, string address, DeviceNetworkComponent? device = null) { - public DeviceNetworkComponent.ConnectionType NetId; - public int Frequency; - public string Address; - public bool Broadcast; - public NetworkPayload Data; - public DeviceNetworkComponent Sender; + 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; + } } + #endregion } /// @@ -241,46 +294,66 @@ namespace Content.Server.DeviceNetwork.Systems /// /// The EntityUid of the entity the packet was sent from. /// - public EntityUid Sender; + public readonly EntityUid Sender; - public BeforePacketSentEvent(EntityUid sender) + public readonly TransformComponent SenderTransform; + + /// + /// The senders current position in world coordinates. + /// + public readonly Vector2 SenderPosition; + + public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition) { Sender = sender; + SenderTransform = xform; + SenderPosition = senderPosition; } } /// /// Event raised when a device network packet gets sent. /// - public sealed class PacketSentEvent : EntityEventArgs + public sealed class DeviceNetworkPacketEvent : EntityEventArgs { + /// + /// The type of network that this packet is being sent on. + /// + public ConnectionType NetId; + /// /// The frequency the packet is sent on. /// - public int Frequency; + public readonly uint Frequency; + + /// + /// Address of the intended recipient. Null if the message was broadcast. + /// + public string? Address; /// /// The device network address of the sending entity. /// - public string SenderAddress; + public readonly string SenderAddress; /// - /// The data that is beeing sent. + /// The entity that sent the packet. /// - public NetworkPayload Data; + public EntityUid Sender; /// - /// Whether the packet was broadcasted. + /// The data that is being sent. /// - public bool Broadcast; + public readonly NetworkPayload Data; - public PacketSentEvent(int frequency, string senderAddress, NetworkPayload data, bool broadcast) + public DeviceNetworkPacketEvent(ConnectionType netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data) { + NetId = netId; + Address = address; Frequency = frequency; SenderAddress = senderAddress; + Sender = sender; Data = data; - Broadcast = broadcast; } - } } diff --git a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs index 20fd540829..d760fef394 100644 --- a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.DeviceNetwork.Components; +using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components.Devices; using Content.Shared.Interaction; using Robust.Shared.GameObjects; @@ -15,7 +15,7 @@ namespace Content.Server.DeviceNetwork.Systems.Devices base.Initialize(); SubscribeLocalEvent(OnInteracted); - SubscribeLocalEvent(OnPackedReceived); + SubscribeLocalEvent(OnPackedReceived); } /// @@ -28,13 +28,16 @@ namespace Content.Server.DeviceNetwork.Systems.Devices component.State = !component.State; + if (networkComponent.TransmitFrequency == null) + return; + var payload = new NetworkPayload { [DeviceNetworkConstants.Command] = DeviceNetworkConstants.CmdSetState, [DeviceNetworkConstants.StateEnabled] = component.State, }; - _deviceNetworkSystem.QueuePacket(uid, DeviceNetworkConstants.NullAddress, networkComponent.Frequency, payload, true); + _deviceNetworkSystem.QueuePacket(uid, null, payload, device: networkComponent); args.Handled = true; } @@ -42,7 +45,7 @@ namespace Content.Server.DeviceNetwork.Systems.Devices /// /// Listens to the command of other switches to sync state /// - private void OnPackedReceived(EntityUid uid, ApcNetSwitchComponent component, PacketSentEvent args) + private void OnPackedReceived(EntityUid uid, ApcNetSwitchComponent component, DeviceNetworkPacketEvent args) { if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? networkComponent) || args.SenderAddress == networkComponent.Address) return; if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || command != DeviceNetworkConstants.CmdSetState) return; diff --git a/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs index 5f9a75c766..38e9ff3ffc 100644 --- a/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs @@ -18,7 +18,7 @@ namespace Content.Server.DeviceNetwork.Systems /// private void OnBeforePacketSent(EntityUid uid, WiredNetworkComponent component, BeforePacketSentEvent args) { - if (EntityManager.GetComponent(uid).GridID != EntityManager.GetComponent(args.Sender).GridID) + if (EntityManager.GetComponent(uid).GridID != args.SenderTransform.GridID) { args.Cancel(); } diff --git a/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs index 5f1f52e9fc..164bf2e632 100644 --- a/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs @@ -18,11 +18,12 @@ namespace Content.Server.DeviceNetwork.Systems /// private void OnBeforePacketSent(EntityUid uid, WirelessNetworkComponent component, BeforePacketSentEvent args) { - var ownPosition = EntityManager.GetComponent(component.Owner).WorldPosition; - var position = EntityManager.GetComponent(args.Sender).WorldPosition; - var distance = (ownPosition - position).Length; + var ownPosition = args.SenderPosition; + var xform = Transform(uid); - if (EntityManager.TryGetComponent(args.Sender, out var sendingComponent) && distance > sendingComponent.Range) + if (xform.MapID != args.SenderTransform.MapID + || !TryComp(args.Sender, out var sendingComponent) + || (ownPosition - xform.WorldPosition).Length > sendingComponent.Range) { args.Cancel(); } diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index dab192ae55..52c33bfa68 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -51,7 +51,7 @@ namespace Content.Server.Light.EntitySystems SubscribeLocalEvent(HandleLightDamaged); SubscribeLocalEvent(OnSignalReceived); - SubscribeLocalEvent(OnPacketReceived); + SubscribeLocalEvent(OnPacketReceived); SubscribeLocalEvent(OnPowerChanged); } @@ -346,7 +346,7 @@ namespace Content.Server.Light.EntitySystems /// Turns the light on or of when receiving a command. /// The light is turned on or of according to the value /// - private void OnPacketReceived(EntityUid uid, PoweredLightComponent component, PacketSentEvent args) + private void OnPacketReceived(EntityUid uid, PoweredLightComponent component, DeviceNetworkPacketEvent args) { if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || command != DeviceNetworkConstants.CmdSetState) return; if (!args.Data.TryGetValue(DeviceNetworkConstants.StateEnabled, out bool enabled)) return; diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs index cc8397c0f0..7b9197220d 100644 --- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs @@ -21,7 +21,7 @@ namespace Content.Server.Medical.CrewMonitoring { base.Initialize(); SubscribeLocalEvent(OnRemove); - SubscribeLocalEvent(OnPacketReceived); + SubscribeLocalEvent(OnPacketReceived); } public override void Update(float frameTime) @@ -47,7 +47,7 @@ namespace Content.Server.Medical.CrewMonitoring component.ConnectedSensors.Clear(); } - private void OnPacketReceived(EntityUid uid, CrewMonitoringConsoleComponent component, PacketSentEvent args) + private void OnPacketReceived(EntityUid uid, CrewMonitoringConsoleComponent component, DeviceNetworkPacketEvent args) { var suitSensor = _sensors.PacketToSuitSensor(args.Data); if (suitSensor == null) diff --git a/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs b/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs index 068b4947a2..5d5246bc5e 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs @@ -40,10 +40,10 @@ namespace Content.Server.Medical.SuitSensors public string ActivationSlot = "jumpsuit"; /// - /// How often does sensor update its owners status (in seconds). + /// How often does sensor update its owners status (in seconds). Limited by the system update rate. /// [DataField("updateRate")] - public float UpdateRate = 2f; + public TimeSpan UpdateRate = TimeSpan.FromSeconds(2f); /// /// Current user that wears suit sensor. Null if nobody wearing it. diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index b415977418..257f25fac7 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -1,4 +1,4 @@ -using System; +using System; using Content.Server.Access.Systems; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; @@ -30,7 +30,7 @@ namespace Content.Server.Medical.SuitSensors [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - private const float UpdateRate = 0.5f; + private const float UpdateRate = 1f; private float _updateDif; public override void Initialize() @@ -58,8 +58,11 @@ namespace Content.Server.Medical.SuitSensors var sensors = EntityManager.EntityQuery(); foreach (var (sensor, device) in sensors) { + if (device.TransmitFrequency is not uint frequency) + continue; + // check if sensor is ready to update - if (curTime - sensor.LastUpdate < TimeSpan.FromSeconds(sensor.UpdateRate)) + if (curTime - sensor.LastUpdate < sensor.UpdateRate) continue; sensor.LastUpdate = curTime; @@ -70,7 +73,7 @@ namespace Content.Server.Medical.SuitSensors // broadcast it to device network var payload = SuitSensorToPacket(status); - _deviceNetworkSystem.QueuePacket(sensor.Owner, DeviceNetworkConstants.NullAddress, device.Frequency, payload, true); + _deviceNetworkSystem.QueuePacket(sensor.Owner, null, payload, device: device); } } diff --git a/Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs b/Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs new file mode 100644 index 0000000000..7c2612a54c --- /dev/null +++ b/Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs @@ -0,0 +1,26 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.DeviceNetwork; + +/// +/// A named device network frequency. Useful for ensuring entity prototypes can communicate with each other. +/// +[Prototype("deviceFrequency")] +[Serializable, NetSerializable] +public sealed class DeviceFrequencyPrototype : IPrototype +{ + [IdDataFieldAttribute] + public string ID { get; } = default!; + + // TODO Somehow Allow per-station or some other type of named but randomized frequencies? + [DataField("frequency", required: true)] + public uint Frequency; + + /// + /// Optional name for this frequency, for displaying in game. + /// + [DataField("name")] + public string? Name; + +} diff --git a/Resources/Locale/en-US/devices/device-network.ftl b/Resources/Locale/en-US/devices/device-network.ftl new file mode 100644 index 0000000000..8a859116df --- /dev/null +++ b/Resources/Locale/en-US/devices/device-network.ftl @@ -0,0 +1,8 @@ +# named frequencies +device-frequency-prototype-name-atmos = Atmospheric Devices +device-frequency-prototype-name-suit-sensors = Suit Sensors +device-frequency-prototype-name-lights = Smart Lights + +# prefixes for randomly generated device addresses +device-address-prefix-vent = Vnt- +device-address-prefix-scrubber = Scr- \ No newline at end of file diff --git a/Resources/Prototypes/Device/devicenet_frequencies.yml b/Resources/Prototypes/Device/devicenet_frequencies.yml new file mode 100644 index 0000000000..9b5ae0dd31 --- /dev/null +++ b/Resources/Prototypes/Device/devicenet_frequencies.yml @@ -0,0 +1,16 @@ +- type: deviceFrequency + id: AtmosMonitor + name: device-frequency-prototype-name-atmos + frequency: 1621 + +# Only listen to this frequency if you are a health or GPS monitor. Otherwise you will just slow down the server by constantly receiving periodic broadcasts from every player-entity. +- type: deviceFrequency + id: SuitSensor + name: device-frequency-prototype-name-suit-sensors + frequency: 1262 + +# This frequency will likely have a LARGE number of listening entities. Please don't broadcast on this frequency. +- type: deviceFrequency + id: SmartLight #used by powered lights. + name: device-frequency-prototype-name-lights + frequency: 1173 diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml index f5fbe03dae..0bab697a9a 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml @@ -4,8 +4,9 @@ id: ClothingWithSuitSensor components: - type: SuitSensor - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Wireless + transmitFrequencyId: SuitSensor - type: WirelessNetworkConnection range: 500 diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index d3fba7588c..2ffbf2385a 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -176,7 +176,8 @@ - AtmosphericsNitrogen - AtmosphericsCarbonDioxide - type: CrewMonitoringConsole - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Wireless + receiveFrequencyId: SuitSensor - type: WirelessNetworkConnection range: 500 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml index 0a8e5c3f92..4a553949b9 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml @@ -14,7 +14,8 @@ - key: enum.CrewMonitoringUIKey.Key type: CrewMonitoringBoundUserInterface - type: CrewMonitoringConsole - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Wireless + receiveFrequencyId: SuitSensor - type: WirelessNetworkConnection range: 500 diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index c6b5aea18d..de7857e72a 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -10,9 +10,9 @@ - AirAlarm - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc - frequency: 1621 + receiveFrequencyId: AtmosMonitor - type: ApcNetworkConnection - type: InteractionOutline - type: Damageable diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 255e1bfb88..44328e9515 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -137,8 +137,9 @@ - key: enum.CrewMonitoringUIKey.Key type: CrewMonitoringBoundUserInterface - type: CrewMonitoringConsole - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Wireless + receiveFrequencyId: SuitSensor - type: WirelessNetworkConnection range: 500 diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index 8439311a85..0c01ef93bc 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -26,9 +26,11 @@ components: - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc - frequency: 1621 + receiveFrequencyId: AtmosMonitor + transmitFrequencyId: AtmosMonitor + prefix: device-address-prefix-vent - type: ApcNetworkConnection - type: AtmosAlarmable alarmedBy: @@ -96,9 +98,11 @@ components: - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc - frequency: 1621 + receiveFrequencyId: AtmosMonitor + transmitFrequencyId: AtmosMonitor + prefix: device-address-prefix-scrubber - type: ApcNetworkConnection - type: AtmosAlarmable alarmedBy: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml index 78c430539f..fb03b84016 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml @@ -6,9 +6,10 @@ - type: WallMount - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc - frequency: 1621 + receiveFrequencyId: AtmosMonitor + transmitFrequencyId: AtmosMonitor - type: ApcNetworkConnection - type: AtmosMonitor temperatureThreshold: stationTemperature diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml index 8145c1cfc0..d325fdca39 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml @@ -6,9 +6,10 @@ - type: WallMount - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc - frequency: 1621 + receiveFrequencyId: AtmosMonitor + transmitFrequencyId: AtmosMonitor - type: ApcNetworkConnection - type: AtmosMonitor monitorFire: true diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml index 19b605ada8..d6b404c4f8 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml @@ -79,8 +79,9 @@ Heat: 5 - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc + receiveFrequencyId: SmartLight - type: ApcNetworkConnection - type: Appearance visuals: @@ -245,8 +246,9 @@ Heat: 5 - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc + receiveFrequencyId: SmartLight - type: ApcNetworkConnection - type: Appearance visuals: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml index ca219b0a80..3d8c5b1d66 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml @@ -58,8 +58,9 @@ graph: LightSwitch node: lightSwitch - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc + transmitFrequencyId: SmartLight # assuming people want to use it for light switches. - type: ApcNetworkConnection - type: ApcNetSwitch placement: diff --git a/Resources/Prototypes/Entities/Structures/lighting_ground.yml b/Resources/Prototypes/Entities/Structures/lighting_ground.yml index cde30987e5..cd8df304ec 100644 --- a/Resources/Prototypes/Entities/Structures/lighting_ground.yml +++ b/Resources/Prototypes/Entities/Structures/lighting_ground.yml @@ -77,8 +77,9 @@ Heat: 20 - type: ApcPowerReceiver - type: ExtensionCableReceiver - - type: DeviceNetworkComponent + - type: DeviceNetwork deviceNetId: Apc + receiveFrequencyId: SmartLight - type: ApcNetworkConnection - type: Construction graph: LightFixture