Device network as ecs (#4205)
* Work on DeviceNetworkSystem * Implement device networking as ecs Remove mailing unit code for now Remove device network metadata * Implement integration tests for device networking * Remove manual updating DeviceNetworkSystem and use WaitRunTicks * Fix wrong component name in ignored components * Apply suggestions from code review Co-authored-by: mirrorcult <notzombiedude@gmail.com> * Rename NetworkUtils to DeviceNetworkConstants Change connection type constants to enum Remove create function from network payload class * Change broken nodegroup check in wirenet to grid check * Change ComponentManager to entity manager in DeviceNetworkSystem * Fix smaller mistakes * Wtf random test fail pls run them again smh * Fix DataField in DeviceNetworkComponent * Fix yaml in DeviceNetworkTest * Fix DeviceNetworkComponent DeviceNetId property Co-authored-by: Julian Giebel <j.giebel@netrocks.info> Co-authored-by: mirrorcult <notzombiedude@gmail.com>
This commit is contained in:
@@ -281,7 +281,10 @@ namespace Content.Client.Entry
|
|||||||
"TabletopGame",
|
"TabletopGame",
|
||||||
"LitOnPowered",
|
"LitOnPowered",
|
||||||
"TriggerOnSignalReceived",
|
"TriggerOnSignalReceived",
|
||||||
"ToggleDoorOnTrigger"
|
"ToggleDoorOnTrigger",
|
||||||
|
"DeviceNetworkConnection",
|
||||||
|
"WiredNetworkConnection",
|
||||||
|
"WirelessNetworkConnection"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
using Content.Server.DeviceNetwork;
|
||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests.DeviceNetwork
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
[TestOf(typeof(DeviceNetworkComponent))]
|
||||||
|
[TestOf(typeof(WiredNetworkComponent))]
|
||||||
|
[TestOf(typeof(WirelessNetworkComponent))]
|
||||||
|
public class DeviceNetworkTest : ContentIntegrationTest
|
||||||
|
{
|
||||||
|
private const string Prototypes = @"
|
||||||
|
- type: entity
|
||||||
|
name: DummyNetworkDevice
|
||||||
|
id: DummyNetworkDevice
|
||||||
|
components:
|
||||||
|
- type: DeviceNetworkComponent
|
||||||
|
frequency: 100
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: DummyWiredNetworkDevice
|
||||||
|
id: DummyWiredNetworkDevice
|
||||||
|
components:
|
||||||
|
- type: DeviceNetworkComponent
|
||||||
|
deviceNetId: 1
|
||||||
|
- type: WiredNetworkConnection
|
||||||
|
- type: ApcPowerReceiver
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: DummyWirelessNetworkDevice
|
||||||
|
id: DummyWirelessNetworkDevice
|
||||||
|
components:
|
||||||
|
- type: DeviceNetworkComponent
|
||||||
|
frequency: 100
|
||||||
|
deviceNetId: 1
|
||||||
|
- type: WirelessNetworkConnection
|
||||||
|
range: 100
|
||||||
|
";
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task NetworkDeviceSendAndReceive()
|
||||||
|
{
|
||||||
|
var options = new ServerContentIntegrationOption
|
||||||
|
{
|
||||||
|
ExtraPrototypes = Prototypes,
|
||||||
|
ContentBeforeIoC = () => {
|
||||||
|
IoCManager.Resolve<IEntitySystemManager>().LoadExtraSystemType<DeviceNetworkTestSystem>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var server = StartServerDummyTicker(options);
|
||||||
|
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
var mapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||||
|
var deviceNetSystem = entityManager.EntitySysManager.GetEntitySystem<DeviceNetworkSystem>();
|
||||||
|
var deviceNetTestSystem = entityManager.EntitySysManager.GetEntitySystem<DeviceNetworkTestSystem>();
|
||||||
|
|
||||||
|
|
||||||
|
IEntity device1 = null;
|
||||||
|
IEntity device2 = null;
|
||||||
|
DeviceNetworkComponent networkComponent1 = null;
|
||||||
|
DeviceNetworkComponent networkComponent2 = null;
|
||||||
|
|
||||||
|
var testValue = "test";
|
||||||
|
var payload = new NetworkPayload
|
||||||
|
{
|
||||||
|
["Test"] = testValue,
|
||||||
|
["testnumber"] = 1,
|
||||||
|
["testbool"] = true
|
||||||
|
};
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||||
|
|
||||||
|
device1 = entityManager.SpawnEntity("DummyNetworkDevice", MapCoordinates.Nullspace);
|
||||||
|
|
||||||
|
Assert.That(device1.TryGetComponent(out networkComponent1), Is.True);
|
||||||
|
Assert.That(networkComponent1.Open, Is.True);
|
||||||
|
Assert.That(networkComponent1.Address, Is.Not.EqualTo(string.Empty));
|
||||||
|
|
||||||
|
device2 = entityManager.SpawnEntity("DummyNetworkDevice", MapCoordinates.Nullspace);
|
||||||
|
|
||||||
|
Assert.That(device2.TryGetComponent(out networkComponent2), Is.True);
|
||||||
|
Assert.That(networkComponent2.Open, Is.True);
|
||||||
|
Assert.That(networkComponent2.Address, Is.Not.EqualTo(string.Empty));
|
||||||
|
|
||||||
|
Assert.That(networkComponent1.Address, Is.Not.EqualTo(networkComponent2.Address));
|
||||||
|
|
||||||
|
deviceNetSystem.QueuePacket(device1.Uid, networkComponent2.Address, networkComponent2.Frequency, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitRunTicks(1);
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
CollectionAssert.AreEquivalent(deviceNetTestSystem.LastPayload, payload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task WirelessNetworkDeviceSendAndReceive()
|
||||||
|
{
|
||||||
|
var options = new ServerContentIntegrationOption
|
||||||
|
{
|
||||||
|
ExtraPrototypes = Prototypes,
|
||||||
|
ContentBeforeIoC = () => {
|
||||||
|
IoCManager.Resolve<IEntitySystemManager>().LoadExtraSystemType<DeviceNetworkTestSystem>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var server = StartServerDummyTicker(options);
|
||||||
|
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
var mapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||||
|
var deviceNetSystem = entityManager.EntitySysManager.GetEntitySystem<DeviceNetworkSystem>();
|
||||||
|
var deviceNetTestSystem = entityManager.EntitySysManager.GetEntitySystem<DeviceNetworkTestSystem>();
|
||||||
|
|
||||||
|
|
||||||
|
IEntity device1 = null;
|
||||||
|
IEntity device2 = null;
|
||||||
|
DeviceNetworkComponent networkComponent1 = null;
|
||||||
|
DeviceNetworkComponent networkComponent2 = null;
|
||||||
|
WirelessNetworkComponent wirelessNetworkComponent = null;
|
||||||
|
|
||||||
|
var testValue = "test";
|
||||||
|
var payload = new NetworkPayload
|
||||||
|
{
|
||||||
|
["Test"] = testValue,
|
||||||
|
["testnumber"] = 1,
|
||||||
|
["testbool"] = true
|
||||||
|
};
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||||
|
|
||||||
|
device1 = entityManager.SpawnEntity("DummyWirelessNetworkDevice", MapCoordinates.Nullspace);
|
||||||
|
|
||||||
|
Assert.That(device1.TryGetComponent(out networkComponent1), Is.True);
|
||||||
|
Assert.That(device1.TryGetComponent(out wirelessNetworkComponent), Is.True);
|
||||||
|
Assert.That(networkComponent1.Open, 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(device2.TryGetComponent(out networkComponent2), Is.True);
|
||||||
|
Assert.That(networkComponent2.Open, Is.True);
|
||||||
|
Assert.That(networkComponent2.Address, Is.Not.EqualTo(string.Empty));
|
||||||
|
|
||||||
|
Assert.That(networkComponent1.Address, Is.Not.EqualTo(networkComponent2.Address));
|
||||||
|
|
||||||
|
deviceNetSystem.QueuePacket(device1.Uid, networkComponent2.Address, networkComponent2.Frequency, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitRunTicks(1);
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
CollectionAssert.AreEqual(deviceNetTestSystem.LastPayload, payload);
|
||||||
|
|
||||||
|
payload = new NetworkPayload
|
||||||
|
{
|
||||||
|
["Wirelesstest"] = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
wirelessNetworkComponent.Range = 0;
|
||||||
|
|
||||||
|
deviceNetSystem.QueuePacket(device1.Uid, networkComponent2.Address, networkComponent2.Frequency, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitRunTicks(1);
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
CollectionAssert.AreNotEqual(deviceNetTestSystem.LastPayload, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task WiredNetworkDeviceSendAndReceive()
|
||||||
|
{
|
||||||
|
var options = new ServerContentIntegrationOption
|
||||||
|
{
|
||||||
|
ExtraPrototypes = Prototypes,
|
||||||
|
ContentBeforeIoC = () => {
|
||||||
|
IoCManager.Resolve<IEntitySystemManager>().LoadExtraSystemType<DeviceNetworkTestSystem>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var server = StartServerDummyTicker(options);
|
||||||
|
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
var mapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||||
|
var deviceNetSystem = entityManager.EntitySysManager.GetEntitySystem<DeviceNetworkSystem>();
|
||||||
|
var deviceNetTestSystem = entityManager.EntitySysManager.GetEntitySystem<DeviceNetworkTestSystem>();
|
||||||
|
|
||||||
|
|
||||||
|
IEntity device1 = null;
|
||||||
|
IEntity device2 = null;
|
||||||
|
DeviceNetworkComponent networkComponent1 = null;
|
||||||
|
DeviceNetworkComponent networkComponent2 = null;
|
||||||
|
WiredNetworkComponent wiredNetworkComponent = null;
|
||||||
|
IMapGrid grid = null;
|
||||||
|
|
||||||
|
var testValue = "test";
|
||||||
|
var payload = new NetworkPayload
|
||||||
|
{
|
||||||
|
["Test"] = testValue,
|
||||||
|
["testnumber"] = 1,
|
||||||
|
["testbool"] = true
|
||||||
|
};
|
||||||
|
|
||||||
|
await server.WaitRunTicks(1);
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
var map = mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||||
|
grid = mapManager.CreateGrid(MapId.Nullspace);
|
||||||
|
|
||||||
|
device1 = entityManager.SpawnEntity("DummyWiredNetworkDevice", MapCoordinates.Nullspace);
|
||||||
|
|
||||||
|
Assert.That(device1.TryGetComponent(out networkComponent1), Is.True);
|
||||||
|
Assert.That(device1.TryGetComponent(out wiredNetworkComponent), Is.True);
|
||||||
|
Assert.That(networkComponent1.Open, 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(device2.TryGetComponent(out networkComponent2), Is.True);
|
||||||
|
Assert.That(networkComponent2.Open, Is.True);
|
||||||
|
Assert.That(networkComponent2.Address, Is.Not.EqualTo(string.Empty));
|
||||||
|
|
||||||
|
Assert.That(networkComponent1.Address, Is.Not.EqualTo(networkComponent2.Address));
|
||||||
|
|
||||||
|
deviceNetSystem.QueuePacket(device1.Uid, networkComponent2.Address, networkComponent2.Frequency, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitRunTicks(1);
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
//CollectionAssert.AreNotEqual(deviceNetTestSystem.LastPayload, payload);
|
||||||
|
|
||||||
|
entityManager.SpawnEntity("CableApcExtension", grid.MapToGrid(new MapCoordinates(new Robust.Shared.Maths.Vector2(0, 1), MapId.Nullspace)));
|
||||||
|
|
||||||
|
deviceNetSystem.QueuePacket(device1.Uid, networkComponent2.Address, networkComponent2.Frequency, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitRunTicks(1);
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
server.Assert(() => {
|
||||||
|
CollectionAssert.AreEqual(deviceNetTestSystem.LastPayload, payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
using Content.Server.DeviceNetwork;
|
||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Reflection;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests.DeviceNetwork
|
||||||
|
{
|
||||||
|
[Reflect(false)]
|
||||||
|
public class DeviceNetworkTestSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public NetworkPayload LastPayload = default;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<DeviceNetworkComponent, PacketSentEvent>(OnPacketReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPacketReceived(EntityUid uid, DeviceNetworkComponent component, PacketSentEvent args)
|
||||||
|
{
|
||||||
|
LastPayload = args.Data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class DeviceNetworkComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "DeviceNetworkComponent";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The device networks netID this DeviceNetworkComponent connects to.
|
||||||
|
/// The netID is used to seperate device networks that shouldn't interact with each other e.g. wireless and wired.
|
||||||
|
/// The default netID's are_
|
||||||
|
/// 0 -> Private
|
||||||
|
/// 1 -> Wired
|
||||||
|
/// 2 -> Wireless
|
||||||
|
/// </summary>
|
||||||
|
[DataField("deviceNetId")]
|
||||||
|
public int DeviceNetId { get; set; } = (int)DeviceNetworkConstants.ConnectionType.Private;
|
||||||
|
|
||||||
|
[DataField("frequency")]
|
||||||
|
public int Frequency { get; set; } = 0;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public bool Open;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Address = string.Empty;
|
||||||
|
|
||||||
|
[DataField("receiveAll")]
|
||||||
|
public bool ReceiveAll;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class WiredNetworkComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "WiredNetworkConnection";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sends and receives device network messages wirelessly. Devices sending and receiving need to be in range and on the same frequency.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public class WirelessNetworkComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "WirelessNetworkConnection";
|
||||||
|
|
||||||
|
[DataField("range")]
|
||||||
|
public int Range { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork.Connections
|
|
||||||
{
|
|
||||||
public abstract class BaseNetworkConnection : IDeviceNetworkConnection
|
|
||||||
{
|
|
||||||
protected readonly DeviceNetworkConnection Connection;
|
|
||||||
|
|
||||||
protected OnReceiveNetMessage MessageHandler;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public bool Open => Connection.Open;
|
|
||||||
[ViewVariables]
|
|
||||||
public string Address => Connection.Address;
|
|
||||||
[ViewVariables]
|
|
||||||
public int Frequency => Connection.Frequency;
|
|
||||||
|
|
||||||
protected BaseNetworkConnection(int netId, int frequency, OnReceiveNetMessage onReceive, bool receiveAll)
|
|
||||||
{
|
|
||||||
var network = IoCManager.Resolve<IDeviceNetwork>();
|
|
||||||
Connection = network.Register(netId, frequency, OnReceiveNetMessage, receiveAll);
|
|
||||||
MessageHandler = onReceive;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Send(int frequency, string address, Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
var data = ManipulatePayload(payload);
|
|
||||||
var metadata = GetMetadata();
|
|
||||||
return Connection.Send(frequency, address, data, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Send(string address, Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return Send(0, address, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Broadcast(int frequency, Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
var data = ManipulatePayload(payload);
|
|
||||||
var metadata = GetMetadata();
|
|
||||||
return Connection.Broadcast(frequency, data, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Broadcast(Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return Broadcast(0, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
Connection.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnReceiveNetMessage(int frequency, string sender, IReadOnlyDictionary<string, string> payload, Metadata metadata, bool broadcast)
|
|
||||||
{
|
|
||||||
if (CanReceive(frequency, sender, payload, metadata, broadcast))
|
|
||||||
{
|
|
||||||
MessageHandler(frequency, sender, payload, metadata, broadcast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract bool CanReceive(int frequency, string sender, IReadOnlyDictionary<string, string> payload, Metadata metadata, bool broadcast);
|
|
||||||
protected abstract Dictionary<string, string> ManipulatePayload(Dictionary<string, string> payload);
|
|
||||||
protected abstract Metadata GetMetadata();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork.Connections
|
|
||||||
{
|
|
||||||
public class DeviceNetworkConnection : IDeviceNetworkConnection
|
|
||||||
{
|
|
||||||
private readonly DeviceNetwork _network;
|
|
||||||
[ViewVariables]
|
|
||||||
private readonly int _netId;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public bool Open { get; private set; }
|
|
||||||
[ViewVariables]
|
|
||||||
public string Address { get; private set; }
|
|
||||||
[ViewVariables]
|
|
||||||
public int Frequency { get; private set; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public bool ReceiveAll
|
|
||||||
{
|
|
||||||
get => _network.GetDeviceReceiveAll(_netId, Frequency, Address);
|
|
||||||
set => _network.SetDeviceReceiveAll(_netId, Frequency, Address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeviceNetworkConnection(DeviceNetwork network, int netId, string address, int frequency)
|
|
||||||
{
|
|
||||||
_network = network;
|
|
||||||
_netId = netId;
|
|
||||||
Open = true;
|
|
||||||
Address = address;
|
|
||||||
Frequency = frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Send(int frequency, string address, IReadOnlyDictionary<string, string> payload, Metadata metadata)
|
|
||||||
{
|
|
||||||
return Open && _network.EnqueuePackage(_netId, frequency, address, payload, Address, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Send(int frequency, string address, Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return Send(frequency, address, payload, new Metadata());
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Send(string address, Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return Send(0, address, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Broadcast(int frequency, IReadOnlyDictionary<string, string> payload, Metadata metadata)
|
|
||||||
{
|
|
||||||
return Open && _network.EnqueuePackage(_netId, frequency, "", payload, Address, metadata, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Broadcast(int frequency, Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return Broadcast(frequency, payload, new Metadata());
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Broadcast(Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return Broadcast(0, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
_network.RemoveDevice(_netId, Frequency, Address);
|
|
||||||
Open = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork.Connections
|
|
||||||
{
|
|
||||||
public interface IDeviceNetworkConnection
|
|
||||||
{
|
|
||||||
public int Frequency { get; }
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a package to a specific device
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="frequency">The frequency the package should be send on</param>
|
|
||||||
/// <param name="address">The target devices address</param>
|
|
||||||
/// <param name="payload"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Send(int frequency, string address, Dictionary<string, string> payload);
|
|
||||||
/// <see cref="Send(int, string, Dictionary{string, string})"/>
|
|
||||||
public bool Send(string address, Dictionary<string, string> payload);
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a package to all devices
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="frequency">The frequency the package should be send on</param>
|
|
||||||
/// <param name="payload"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Broadcast(int frequency, Dictionary<string, string> payload);
|
|
||||||
/// <see cref="Broadcast(int, Dictionary{string, string})"/>
|
|
||||||
public bool Broadcast(Dictionary<string, string> payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Content.Server.NodeContainer;
|
|
||||||
using Content.Server.NodeContainer.NodeGroups;
|
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork.Connections
|
|
||||||
{
|
|
||||||
public class WiredNetworkConnection : BaseNetworkConnection
|
|
||||||
{
|
|
||||||
public const string WIRENET = "powernet";
|
|
||||||
|
|
||||||
private readonly IEntity _owner;
|
|
||||||
|
|
||||||
public WiredNetworkConnection(OnReceiveNetMessage onReceive, bool receiveAll, IEntity owner) : base(NetworkUtils.WIRED, 0, onReceive, receiveAll)
|
|
||||||
{
|
|
||||||
_owner = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool CanReceive(int frequency, string sender, IReadOnlyDictionary<string, string> payload, Metadata metadata, bool broadcast)
|
|
||||||
{
|
|
||||||
if (_owner.Deleted)
|
|
||||||
{
|
|
||||||
Connection.Close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_owner.TryGetComponent<ApcPowerReceiverComponent>(out var powerReceiver)
|
|
||||||
&& TryGetWireNet(powerReceiver, out var ownNet)
|
|
||||||
&& metadata.TryParseMetadata<INodeGroup>(WIRENET, out var senderNet))
|
|
||||||
{
|
|
||||||
return ownNet.Equals(senderNet);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Metadata GetMetadata()
|
|
||||||
{
|
|
||||||
if (_owner.Deleted)
|
|
||||||
{
|
|
||||||
Connection.Close();
|
|
||||||
return new Metadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_owner.TryGetComponent<ApcPowerReceiverComponent>(out var powerReceiver)
|
|
||||||
&& TryGetWireNet(powerReceiver, out var net))
|
|
||||||
{
|
|
||||||
var metadata = new Metadata
|
|
||||||
{
|
|
||||||
{WIRENET, net }
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Metadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Dictionary<string, string> ManipulatePayload(Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetWireNet(ApcPowerReceiverComponent apcPowerReceiver, [NotNullWhen(true)] out INodeGroup? net)
|
|
||||||
{
|
|
||||||
var provider = apcPowerReceiver.Provider;
|
|
||||||
if (provider != null && provider.ProviderOwner.TryGetComponent<NodeContainerComponent>(out var nodeContainer))
|
|
||||||
{
|
|
||||||
var nodes = nodeContainer.Nodes;
|
|
||||||
|
|
||||||
foreach (var node in nodes.Values)
|
|
||||||
{
|
|
||||||
if (node.NodeGroupID == NodeGroupID.WireNet && node.NodeGroup != null)
|
|
||||||
{
|
|
||||||
net = node.NodeGroup;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
net = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork.Connections
|
|
||||||
{
|
|
||||||
public class WirelessNetworkConnection : BaseNetworkConnection
|
|
||||||
{
|
|
||||||
public const string WIRELESS_POSITION = "position";
|
|
||||||
|
|
||||||
private readonly IEntity _owner;
|
|
||||||
|
|
||||||
private float _range;
|
|
||||||
public float Range { get => _range; set => _range = Math.Abs(value); }
|
|
||||||
|
|
||||||
public WirelessNetworkConnection(int frequency, OnReceiveNetMessage onReceive, bool receiveAll, IEntity owner, float range) : base(NetworkUtils.WIRELESS, frequency, onReceive, receiveAll)
|
|
||||||
{
|
|
||||||
_owner = owner;
|
|
||||||
Range = range;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool CanReceive(int frequency, string sender, IReadOnlyDictionary<string, string> payload, Metadata metadata, bool broadcast)
|
|
||||||
{
|
|
||||||
if (_owner.Deleted)
|
|
||||||
{
|
|
||||||
Connection.Close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.TryParseMetadata<Vector2>(WIRELESS_POSITION, out var position))
|
|
||||||
{
|
|
||||||
var ownPosition = _owner.Transform.WorldPosition;
|
|
||||||
var distance = (ownPosition - position).Length;
|
|
||||||
return distance <= Range;
|
|
||||||
}
|
|
||||||
//Only receive packages with the same frequency
|
|
||||||
return frequency == Frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Metadata GetMetadata()
|
|
||||||
{
|
|
||||||
if (_owner.Deleted)
|
|
||||||
{
|
|
||||||
Connection.Close();
|
|
||||||
return new Metadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
var position = _owner.Transform.WorldPosition;
|
|
||||||
var metadata = new Metadata
|
|
||||||
{
|
|
||||||
{WIRELESS_POSITION, position}
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Dictionary<string, string> ManipulatePayload(Dictionary<string, string> payload)
|
|
||||||
{
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Content.Server.DeviceNetwork.Connections;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork
|
|
||||||
{
|
|
||||||
public delegate void OnReceiveNetMessage(int frequency, string sender, IReadOnlyDictionary<string, string> payload, Metadata metadata, bool broadcast);
|
|
||||||
|
|
||||||
public class DeviceNetwork : IDeviceNetwork
|
|
||||||
{
|
|
||||||
private const int PACKAGES_PER_TICK = 30;
|
|
||||||
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
|
|
||||||
private readonly Dictionary<int, List<NetworkDevice>> _devices = new();
|
|
||||||
private readonly Queue<NetworkPackage> _packages = new();
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public DeviceNetworkConnection Register(int netId, int frequency, OnReceiveNetMessage messageHandler, bool receiveAll = false)
|
|
||||||
{
|
|
||||||
var address = GenerateValidAddress(netId, frequency);
|
|
||||||
var device = new NetworkDevice(frequency, address, messageHandler, receiveAll);
|
|
||||||
|
|
||||||
AddDevice(netId, device);
|
|
||||||
|
|
||||||
return new DeviceNetworkConnection(this, netId, address, frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeviceNetworkConnection Register(int netId, OnReceiveNetMessage messageHandler, bool receiveAll = false)
|
|
||||||
{
|
|
||||||
return Register(netId, 0, messageHandler, receiveAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
var count = Math.Min(PACKAGES_PER_TICK, _packages.Count);
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var package = _packages.Dequeue();
|
|
||||||
|
|
||||||
if (package.Broadcast)
|
|
||||||
{
|
|
||||||
BroadcastPackage(package);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SendPackage(package);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool EnqueuePackage(int netId, int frequency, string address, IReadOnlyDictionary<string, string> data, string sender, Metadata metadata, bool broadcast = false)
|
|
||||||
{
|
|
||||||
if (!_devices.ContainsKey(netId))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var package = new NetworkPackage(netId, frequency, address, broadcast, data, metadata, sender);
|
|
||||||
|
|
||||||
_packages.Enqueue(package);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveDevice(int netId, int frequency, string address)
|
|
||||||
{
|
|
||||||
if (TryDeviceWithAddress(netId, frequency, address, out var device))
|
|
||||||
{
|
|
||||||
_devices[netId].Remove(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDeviceReceiveAll(int netId, int frequency, string address, bool receiveAll)
|
|
||||||
{
|
|
||||||
if (TryDeviceWithAddress(netId, frequency, address, out var device))
|
|
||||||
{
|
|
||||||
device.ReceiveAll = receiveAll;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetDeviceReceiveAll(int netId, int frequency, string address)
|
|
||||||
{
|
|
||||||
if (TryDeviceWithAddress(netId, frequency, address, out var device))
|
|
||||||
{
|
|
||||||
return device.ReceiveAll;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GenerateValidAddress(int netId, int frequency)
|
|
||||||
{
|
|
||||||
var unique = false;
|
|
||||||
var devices = DevicesForFrequency(netId, frequency);
|
|
||||||
var address = "";
|
|
||||||
|
|
||||||
while (!unique)
|
|
||||||
{
|
|
||||||
address = _random.Next().ToString("x");
|
|
||||||
unique = !devices.Exists(device => device.Address == address);
|
|
||||||
}
|
|
||||||
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddDevice(int netId, NetworkDevice networkDevice)
|
|
||||||
{
|
|
||||||
if(!_devices.ContainsKey(netId))
|
|
||||||
_devices[netId] = new List<NetworkDevice>();
|
|
||||||
|
|
||||||
_devices[netId].Add(networkDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<NetworkDevice> DevicesForFrequency(int netId, int frequency)
|
|
||||||
{
|
|
||||||
if (!_devices.ContainsKey(netId))
|
|
||||||
return new List<NetworkDevice>();
|
|
||||||
|
|
||||||
var result = _devices[netId].FindAll(device => device.Frequency == frequency);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NetworkDevice? DeviceWithAddress(int netId, int frequency, string address)
|
|
||||||
{
|
|
||||||
var devices = DevicesForFrequency(netId, frequency);
|
|
||||||
|
|
||||||
var device = devices.Find(dvc => dvc.Address == address);
|
|
||||||
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryDeviceWithAddress(int netId, int frequency, string address,
|
|
||||||
[NotNullWhen(true)] out NetworkDevice? device)
|
|
||||||
{
|
|
||||||
return (device = DeviceWithAddress(netId, frequency, address)) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<NetworkDevice> DevicesWithReceiveAll(int netId, int frequency)
|
|
||||||
{
|
|
||||||
if (!_devices.ContainsKey(netId))
|
|
||||||
return new List<NetworkDevice>();
|
|
||||||
|
|
||||||
var result = _devices[netId].FindAll(device => device.Frequency == frequency && device.ReceiveAll);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BroadcastPackage(NetworkPackage package)
|
|
||||||
{
|
|
||||||
var devices = DevicesForFrequency(package.NetId, package.Frequency);
|
|
||||||
SendToDevices(devices, package, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendPackage(NetworkPackage package)
|
|
||||||
{
|
|
||||||
var devices = DevicesWithReceiveAll(package.NetId, package.Frequency);
|
|
||||||
|
|
||||||
if (TryDeviceWithAddress(package.NetId, package.Frequency, package.Address, out var device))
|
|
||||||
{
|
|
||||||
devices.Add(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
SendToDevices(devices, package, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendToDevices(List<NetworkDevice> devices, NetworkPackage package, bool broadcast)
|
|
||||||
{
|
|
||||||
foreach (var device in devices)
|
|
||||||
{
|
|
||||||
if (device.Address == package.Sender)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
device.ReceiveNetMessage(package.Frequency, package.Sender, package.Data, package.Metadata, broadcast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class NetworkDevice
|
|
||||||
{
|
|
||||||
internal NetworkDevice(int frequency, string address, OnReceiveNetMessage receiveNetMessage, bool receiveAll)
|
|
||||||
{
|
|
||||||
Frequency = frequency;
|
|
||||||
Address = address;
|
|
||||||
ReceiveNetMessage = receiveNetMessage;
|
|
||||||
ReceiveAll = receiveAll;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Frequency;
|
|
||||||
public string Address;
|
|
||||||
public OnReceiveNetMessage ReceiveNetMessage;
|
|
||||||
public bool ReceiveAll;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class NetworkPackage
|
|
||||||
{
|
|
||||||
internal NetworkPackage(
|
|
||||||
int netId,
|
|
||||||
int frequency,
|
|
||||||
string address,
|
|
||||||
bool broadcast,
|
|
||||||
IReadOnlyDictionary<string, string> data,
|
|
||||||
Metadata metadata,
|
|
||||||
string sender)
|
|
||||||
{
|
|
||||||
NetId = netId;
|
|
||||||
Frequency = frequency;
|
|
||||||
Address = address;
|
|
||||||
Broadcast = broadcast;
|
|
||||||
Data = data;
|
|
||||||
Metadata = metadata;
|
|
||||||
Sender = sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int NetId;
|
|
||||||
public int Frequency;
|
|
||||||
public string Address;
|
|
||||||
public bool Broadcast;
|
|
||||||
public IReadOnlyDictionary<string, string> Data { get; set; }
|
|
||||||
public Metadata Metadata;
|
|
||||||
public string Sender;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
Content.Server/DeviceNetwork/DeviceNetworkConstants.cs
Normal file
21
Content.Server/DeviceNetwork/DeviceNetworkConstants.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace Content.Server.DeviceNetwork
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of utilities to help with using device networks
|
||||||
|
/// </summary>
|
||||||
|
public static class DeviceNetworkConstants
|
||||||
|
{
|
||||||
|
public enum ConnectionType
|
||||||
|
{
|
||||||
|
Private,
|
||||||
|
Wired,
|
||||||
|
Wireless
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The key for command names
|
||||||
|
/// E.g. [DeviceNetworkConstants.Command] = "ping"
|
||||||
|
/// </summary>
|
||||||
|
public const string Command = "command";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork
|
|
||||||
{
|
|
||||||
[UsedImplicitly]
|
|
||||||
internal sealed class DeviceNetworkSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IDeviceNetwork _network = default!;
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
base.Update(frameTime);
|
|
||||||
_network.Update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using Content.Server.DeviceNetwork.Connections;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Package based device network allowing devices to communicate with eachother
|
|
||||||
/// </summary>
|
|
||||||
public interface IDeviceNetwork
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a device with the device network
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="netId"><see cref="NetworkUtils"/>The id of the network to register with</param>
|
|
||||||
/// <param name="frequency">The frequency the device receives packages on. Wired networks use frequency 0</param>
|
|
||||||
/// <param name="messageHandler">The delegate that gets called when the device receives a message</param>
|
|
||||||
/// <param name="receiveAll">If the device should receive all packages on its frequency or only ones addressed to itself</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public DeviceNetworkConnection Register(int netId, int frequency, OnReceiveNetMessage messageHandler, bool receiveAll = false);
|
|
||||||
/// <see cref="Register(int, int, OnReceiveNetMessage, bool)"/>
|
|
||||||
public DeviceNetworkConnection Register(int netId, OnReceiveNetMessage messageHandler, bool receiveAll = false);
|
|
||||||
|
|
||||||
public void Update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork
|
|
||||||
{
|
|
||||||
public class Metadata : Dictionary<string, object>
|
|
||||||
{
|
|
||||||
public bool TryParseMetadata<T>(string key, [NotNullWhen(true)] out T? data)
|
|
||||||
{
|
|
||||||
if (TryGetValue(key, out var value) && value is T typedValue)
|
|
||||||
{
|
|
||||||
data = typedValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
28
Content.Server/DeviceNetwork/NetworkPayload.cs
Normal file
28
Content.Server/DeviceNetwork/NetworkPayload.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Robust.Shared.Log;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork
|
||||||
|
{
|
||||||
|
public class NetworkPayload : Dictionary<string, object>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get a value from the payload and checks if that value is of type T.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type that should be casted to</typeparam>
|
||||||
|
/// <returns>Whether the value was present in the payload and of the required type</returns>
|
||||||
|
public bool TryGetValue<T>(string key, [NotNullWhen(true)] out T? value)
|
||||||
|
{
|
||||||
|
if (this.TryCastValue(key, out T? result))
|
||||||
|
{
|
||||||
|
value = result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.DeviceNetwork.Connections;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A collection of utilities to help with using device networks
|
|
||||||
/// </summary>
|
|
||||||
public static class NetworkUtils
|
|
||||||
{
|
|
||||||
public const int PRIVATE = 0;
|
|
||||||
public const int WIRED = 1;
|
|
||||||
public const int WIRELESS = 2;
|
|
||||||
|
|
||||||
public const string COMMAND = "command";
|
|
||||||
public const string MESSAGE = "message";
|
|
||||||
public const string PING = "ping";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles responding to pings.
|
|
||||||
/// </summary>
|
|
||||||
public static void PingResponse<T>(T connection, string sender, IReadOnlyDictionary<string, string> payload, string message = "") where T : IDeviceNetworkConnection
|
|
||||||
{
|
|
||||||
if (payload.TryGetValue(COMMAND, out var command) && command == PING)
|
|
||||||
{
|
|
||||||
var response = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{COMMAND, "ping_response"},
|
|
||||||
{MESSAGE, message}
|
|
||||||
};
|
|
||||||
|
|
||||||
connection.Send(connection.Frequency, sender, response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
286
Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs
Normal file
286
Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
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]
|
||||||
|
public class DeviceNetworkSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
private readonly Dictionary<int, List<DeviceNetworkComponent>> _connections = new();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (EntityManager.GetEntity(uid).TryGetComponent<DeviceNetworkComponent>(out var component))
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (EntityManager.GetEntity(uid).TryGetComponent<DeviceNetworkComponent>(out var component))
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
private string GenerateValidAddress(int netId)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DeviceNetworkComponent> ConnectionsForFrequency(int netId, int frequency)
|
||||||
|
{
|
||||||
|
if (!_connections.ContainsKey(netId))
|
||||||
|
return new List<DeviceNetworkComponent>();
|
||||||
|
|
||||||
|
var result = _connections[netId].FindAll(connection => connection.Frequency == frequency);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetConnectionWithAddress(int 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)
|
||||||
|
{
|
||||||
|
connection = result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection = default!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DeviceNetworkComponent> ConnectionsWithReceiveAll(int netId, int frequency)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
var receivers = ConnectionsWithReceiveAll(packet.Frequency, packet.NetId);
|
||||||
|
receivers.Add(connection);
|
||||||
|
|
||||||
|
SendToConnections(receivers, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BroadcastPacket(NetworkPacket packet)
|
||||||
|
{
|
||||||
|
var receivers = ConnectionsForFrequency(packet.Frequency, packet.NetId);
|
||||||
|
SendToConnections(receivers, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendToConnections(List<DeviceNetworkComponent> connections, NetworkPacket packet)
|
||||||
|
{
|
||||||
|
foreach (var connection in connections)
|
||||||
|
{
|
||||||
|
var beforeEvent = new BeforePacketSentEvent(packet.Sender.Owner.Uid);
|
||||||
|
RaiseLocalEvent(connection.Owner.Uid, beforeEvent, false);
|
||||||
|
|
||||||
|
if (!beforeEvent.Cancelled)
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(connection.Owner.Uid, new PacketSentEvent(connection.Frequency, packet.Sender.Address, packet.Data, packet.Broadcast) , false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct NetworkPacket
|
||||||
|
{
|
||||||
|
public int NetId;
|
||||||
|
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>
|
||||||
|
public class BeforePacketSentEvent : CancellableEntityEventArgs
|
||||||
|
{
|
||||||
|
/// <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>
|
||||||
|
public class PacketSentEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs
Normal file
39
Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using Content.Server.NodeContainer;
|
||||||
|
using Content.Server.NodeContainer.NodeGroups;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Systems
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class WiredNetworkSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<WiredNetworkComponent, BeforePacketSentEvent>(OnBeforePacketSent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if both devices are on the same grid
|
||||||
|
/// </summary>
|
||||||
|
private void OnBeforePacketSent(EntityUid uid, WiredNetworkComponent component, BeforePacketSentEvent args)
|
||||||
|
{
|
||||||
|
IEntity sender = EntityManager.GetEntity(args.Sender);
|
||||||
|
IEntity receiver = EntityManager.GetEntity(uid);
|
||||||
|
|
||||||
|
if (receiver.Transform.GridID != sender.Transform.GridID)
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Things to do in a future PR:
|
||||||
|
//Abstract out the connection between the apcExtensionCable and the apcPowerReceiver
|
||||||
|
//Traverse the power cables using path traversal
|
||||||
|
//Cache an optimized representation of the traversed path (Probably just cache Devices)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Systems
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class WirelessNetworkSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<WirelessNetworkComponent, BeforePacketSentEvent>(OnBeforePacketSent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the position of both the sending and receiving entity and checks if the receiver is in range of the sender.
|
||||||
|
/// </summary>
|
||||||
|
private void OnBeforePacketSent(EntityUid uid, WirelessNetworkComponent component, BeforePacketSentEvent args)
|
||||||
|
{
|
||||||
|
var sender = EntityManager.GetEntity(args.Sender);
|
||||||
|
|
||||||
|
var ownPosition = component.Owner.Transform.WorldPosition;
|
||||||
|
var position = sender.Transform.WorldPosition;
|
||||||
|
var distance = (ownPosition - position).Length;
|
||||||
|
|
||||||
|
if(sender.TryGetComponent<WirelessNetworkComponent>(out var sendingComponent) && distance > sendingComponent.Range)
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,7 +47,6 @@ namespace Content.Server.IoC
|
|||||||
IoCManager.Register<IConnectionManager, ConnectionManager>();
|
IoCManager.Register<IConnectionManager, ConnectionManager>();
|
||||||
IoCManager.Register<IObjectivesManager, ObjectivesManager>();
|
IoCManager.Register<IObjectivesManager, ObjectivesManager>();
|
||||||
IoCManager.Register<IAdminManager, AdminManager>();
|
IoCManager.Register<IAdminManager, AdminManager>();
|
||||||
IoCManager.Register<IDeviceNetwork, DeviceNetwork.DeviceNetwork>();
|
|
||||||
IoCManager.Register<EuiManager, EuiManager>();
|
IoCManager.Register<EuiManager, EuiManager>();
|
||||||
IoCManager.Register<IVoteManager, VoteManager>();
|
IoCManager.Register<IVoteManager, VoteManager>();
|
||||||
IoCManager.Register<INpcBehaviorManager, NpcBehaviorManager>();
|
IoCManager.Register<INpcBehaviorManager, NpcBehaviorManager>();
|
||||||
|
|||||||
Reference in New Issue
Block a user