Сеть устройств
Сетевое взаимодействие устройств позволяет машинам и устройствам обмениваться данными друг с другом, соблюдая такие ограничения, как дальность или подключение к одной и той же энергосети. У игроков также будет возможность взаимодействовать с сетью устройств несколькими способами. Например, можно использовать анализатор пакетов в своём КПК (пока не реализовано), чтобы прослушивать сообщения, отправляемые по беспроводной сети устройств.
Краткий обзор
Сущности могут присоединяться к сети, и им будет назначен сетевой адрес. Затем эти сущности могут по желанию выбрать прослушивание на некоторой частоте. Любое устройство в сети может отправлять пакеты на любой частоте (не только на той, которую оно прослушивает). Пакеты могут либо широковещательно рассылаться, либо направляться на конкретный адрес. Сами пакеты имеют форму событий ECS. Системе достаточно слушать DeviceNetworkPacketEvent. Это событие содержит информацию об отправителе и NetworkPayload, который по сути является просто Dictionary<string, object>. Содержимое полезной нагрузки и способ его интерпретации жёстко закодированы в каждой системе. Некоторые часто используемые строковые ключи определены в DeviceNetworkConstants.cs.
Идентификаторы сети:
Идентификаторы сети устройств используются для разделения устройств на разные подсети. Это делается по причинам производительности и организации, а также чтобы игроки не взаимодействовали с пакетами, с которыми взаимодействовать не предполагается.
Идентификаторы сети устройств не определяют, как устройство подключается к другим устройствам (например, по беспроводной связи). Для этого используйте компоненты.
0 - PRIVATE netId: Private
Эта сеть не предназначена для взаимодействия со стороны игроков.
1 - WIRED netId: Wired
Используется для проводных сетей. Предназначен для использования с WiredNetworkComponent, если устройство не использует собственный идентификатор сети устройств.
2 - WIRELESS netId: Wireless
Используется для беспроводных сетей. Предназначен для использования с WirelessNetworkComponent, если устройство не использует собственный идентификатор сети устройств.
3 - ApcNet netId: Apc
Используется для проводных сетей, ограниченных удлинительными кабелями ЛКП. Например, выключатели освещения. Предназначен для использования с ApcNetworkComponent, если устройство не использует собственный идентификатор сети устройств.
4 - AtmosDevices netId: AtmosDevices
Используется для атмосферных устройств, таких как скрубберы, чтобы отделить их от других сетей
Компоненты сетевого подключения
Чтобы использовать сеть устройств, нужно добавить DeviceNetworkComponent и компонент нужного типа подключения. Можно также не использовать ни один из компонентов подключения, если вы хотите, чтобы устройство всегда могло отправлять и получать сообщения. Существуют разные типы компонентов подключения.
Компонент сети устройств
Это обязательный компонент для отправки и получения сообщений сети устройств.
YAML
components:
- type: DeviceNetwork
deviceNetId: <Private|Wired|Wireless|Apc>
receiveFrequency: <uint?>
receiveFrequencyId: <string?>
transmitFrequency: <uint?>
transmitFrequencyId: <string?>
address: <string>
customAddress: <bool>
prefix: <string>
receiveAll: <bool>
autoConnect: <bool>
sendBroadcastAttemptEvent: <bool>
deviceNetId
Задаёт DeviceNetId этого устройства. Если нужно добавить новый, добавьте его в DeviceNetIdDefaults в DeviceNetworkComponent.cs.
По умолчанию равно 0 (Private).
receiveFrequency Частота, на которой это подключение принимает пакеты. По умолчанию null, и в этом случае устройство не будет принимать никакие пакеты (хотя всё ещё сможет их отправлять).
Если только вашему устройству не требуется потенциально прослушивать сообщения, это значение следует оставлять null по причинам производительности. Например, хотя сенсоры костюмов каждого игрока отправляют пакеты на заданной частоте, они не прослушивают пакеты ни на какой частоте.
receiveFrequencyId
Строковый ID для FrequencyPrototype. Если это поле не null, то во время mapinit receiveFrequency компонента будет обновлён в соответствии с частотой прототипа. Эти прототипы по сути являются просто именованными uint. Например, прототип SuitSensor следует использовать для всех устройств, связанных с сенсорами костюмов или мониторами экипажа.
transmitFrequency Это частота по умолчанию, которую устройство использует для передачи пакетов. Она просто используется как аргумент по умолчанию для функции отправки пакетов.
transmitFrequencyId
Как receiveFrequencyId, но для transmitFrequency
address Адрес устройства в сети. Используется, чтобы у устройств были постоянные адреса при сохранении или загрузке карты. По умолчанию адреса автоматически генерируются при первом подключении к сети. Если поле address уже заполнено, а запрошенный адрес уже занят, будет просто сгенерирован новый адрес.
customAddress
Указывает, был ли address случайно сгенерирован при подключении к сети или задан явно. Если адрес является пользовательским, он не будет перезаписан автоматически сгенерированными адресами при подключении к сетям.
prefix
Строка-префикс, которая добавляется перед любыми случайно сгенерированными адресами (которые представляют собой просто шестнадцатеричные строки). Полезна для того, чтобы тип устройства можно было определить по адресу. Например, у всех атмос-вентиляций есть префикс Vnt-.
receiveAll
Если true, это устройство будет принимать все пакеты, отправленные на receiveFrequency, а не только широковещательные или адресованные адресу этого устройства. Может использоваться для подслушивания сообщений.
autoConnect Определяет, должно ли устройство пытаться присоединиться к сети при инициализации карты. Обычно true, если только устройство не было явно отключено от сети; в этом случае оно не должно подключаться повторно при повторной загрузке карты.
sendBroadcastAttemptEvent
Отправляет список получателей широковещательной рассылки отправителю перед рассылкой пакета, чтобы его можно было отфильтровать, если задано true.
По умолчанию false.
Компонент беспроводной сети
Компонент беспроводной сети проверяет, находятся ли устройства в радиусе действия друг друга, прежде чем разрешить отправку сообщения.
YAML
Позволяет устройству отправлять пакеты другим устройствам, если беспроводной радиус действия отправителя достаточно велик.
components:
- type: DeviceNetworkComponent
...
- type: WirelessNetworkConnection
range: <int>
range Радиус, на котором это подключение отправляет и принимает.
Компонент сети ЛКП
Очень трудно не допустить соединения ЛКП друг с другом при маппинге, а также трудно ограничить ЛКП конкретной комнатой или убедиться, что все нужные вам устройства подключены. Из-за этого в будущем этот компонент может быть удалён.
Позволяет устройствам отправлять пакеты друг другу, пока они получают питание от одного и того же ЛКП.
YAML
components:
- type: DeviceNetworkComponent
...
- type: ApcNetworkConnection
Компонент проводной сети
На данный момент он только проверяет, находятся ли устройства на одной сетке. Поведение будет реализовано в будущем.
YAML
components:
- type: DeviceNetworkComponent
...
- type: WiredNetworkComponent
Компонент сети, ограниченной станцией
Позволяет устройствам, принадлежащим одной станции, отправлять и получать пакеты друг от друга.
YAML
components:
- type: DeviceNetworkComponent
...
- type: StationLimitedNetwork
allowNonStationPackets: <bool>
allowNonStationPackets
Позволяет устройству получать пакеты от устройств, у которых нет компонента StationLimitedNetwork.
Компонент требования питания сети устройств
Не позволяет принимать какие-либо пакеты, если у устройства нет питания.
YAML
components:
- type: DeviceNetworkComponent
...
- type: DeviceNetworkRequiresPower
Система сети устройств
События
Система использует три события для отправки сообщений устройствам:
BeforeBroadcastAttemptEvent
Это событие отправляется сущности, которая собирается широковещательно разослать пакет, если sendBroadcastAttemptEvent установлен в true на отправляющем устройстве. Это событие можно использовать, чтобы изменить получателей этой рассылки или отменить её.
DETAILS
BeforeBroadcastAttemptEvent(IReadOnlySet<DeviceNetworkComponent> recipients)
recipients Список получателей, которым рассылка собирается отправить пакеты. Property: ModifiedRecipients Это свойство события, которое можно установить в новый список получателей. Если это свойство равно null, рассылка будет отменена.
BeforePacketSentEvent Это событие вызывается до отправки сообщения сети устройств. Другие системы подписываются на него, чтобы предотвратить отправку сообщения.
DETAILS
BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition)
sender EntityUid сущности, от которой было отправлено сообщение. xform Компонент трансформации отправителя. senderPosition Мировая позиция отправителя.
DeviceNetworkPacketEvent Событие, направленное сущности, когда получено сообщение сети устройств.
DETAILS
DeviceNetworkPacketEvent(ConnectionType netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data)
netId Сеть, которая использовалась для отправки пакета.
address Адрес, на который был отправлен пакет. Null, если пакет был разослан широковещательно.
frequency Частота, на которой было отправлено сообщение.
senderAddress Адрес сети устройств отправляющей сущности
sender EntityUid отправителя
data Данные, которые отправляются.
Методы
QueuePacket Отправляет заданную полезную нагрузку как сообщение сети устройств сущности с заданными адресом и частотой.
DETAILS
QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null)
uid EntityUid отправляющей сущности.
address Адрес сущности, которой отправляется пакет. Если null, пакет рассылается широковещательно.
data Данные, которые отправляются.
frequency Частота, на которой отправлять данные. Если null, по умолчанию будет использоваться текущая частота передачи устройства.
network Идентификатор сети, по которой отправлять.
Система сети устройств содержит больше публичных методов для таких вещей, как установка частоты прослушивания устройства.
Класс полезной нагрузки сети
Класс полезной нагрузки сети содержит данные, которые отправляются по сети устройств и в конечном счёте могут быть увидены игроком. Игрок сможет видеть и изменять примитивные типы, только если он может видеть или изменять полезную нагрузку.
Создание полезной нагрузки сети:
var payload = new NetworkPayload
{
["Key1"] = Value1,
["Key2"] = Value2,
["Key3"] = Value3
//...
};
Класс констант сети устройств
Класс DeviceNetworkConstants содержит общие идентификаторы сети и строковые константы для создания полезных нагрузок сети. Вы не ограничены использованием идентификаторов или строковых констант, предоставленных в этом классе, но по возможности следует стараться использовать COMMAND.
При создании полезных нагрузок сети старайтесь использовать константы для ключей и значений, которые всегда одинаковы.
Одним из примеров команды и сообщения являются почтовые блоки: get_mailer_tag для запроса к другим блокам и его сообщение mailer_tag для ответа своим тегом.
Стандартные константы полезной нагрузки:
COMMAND Ключ, используемый для указания команды/типа полезной нагрузки, например pda_mail.
Примеры:
Отправка сетевого сообщения
//Это где-то в верхней части вашей системы сущностей
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
public const string NetCmdPdaMail = "pda_mail";
public const string NetTargetMail = "target_mail_address";
public const string NetSenderMail = "sender_mail_address";
public const string NetMessage = "message";
...
//Когда вы хотите отправить сетевое сообщение, нужно сконструировать полезную нагрузку сети и отправить её, используя метод QueuePacket системы DeviceNetworkSystem.
var payload = new NetworkPayload
{
[DeviceNetworkConstants.COMMAND] = NetCmdPdaMail,
//Вы не ограничены использованием DeviceNetworkConstants.COMMAND
[NetMessage] = "Hi Bob, bla bla bla...",
[NetTargetMail] = "5525bob@nanotrasen.com",
[NetSenderMail] = "7361joe.genero@nanotrasen.com"
};
_deviceNetworkSystem.QueuePacket(uuidOfCurrentEntity, addressOfMailserverOrSomething, payload);
Отправляющая сущность должна быть либо предварительно настроена на отправку на почтовой частоте, либо вам потребуется получить прототип почтовой частоты и передать правильную частоту в качестве аргумента.
Получение и обработка сетевого сообщения
...
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
public const string NetCmdPing = "ping"
public const string NetMessage = "message";
public override void Initialize()
{
base.Initialize();
...
SubscribeLocalEvent<DeviceNetworkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
...
}
...
private void OnPacketReceived(Entity<DeviceNetworkComponent> ent, ref DeviceNetworkPacketEvent args)
{
//Поскольку мы делаем это рекомендуемым способом, используя константу команды, мы пытаемся получить её из полезной нагрузки
if (args.Data.TryGetValue(DeviceNetworkConstants.COMMAND, out String command))
{
//Если эта команда — команда PING (здесь вы можете проверить свою собственную команду)
if (command == NetCmdPing)
{
//Мы создаём полезную нагрузку, содержащую команду ping_response и "Hello World" в качестве сообщения.
//(В этом примере я просто передал имя команды строкой вместо создания константы.)
var payload = new NetworkPayload
{
[DeviceNetworkConstants.COMMAND] "ping_response",
[NetMessage] = "Hello World"
};
//И отправляем ответ на этот ping
_deviceNetworkSystem.QueuePacket(ent, args.SenderAddress, payload, args.Frequency);
}
}
}