Зачем Python в нашем моде#

Python работает отдельным процессом: принимает сообщения, проверяет намерения, ведёт сетевую сессию и организует доставку. Игра исполняет свои Lua/MD и нативные операции. Поэтому Python-функция с названием request обычно ещё не является действием над кораблём.

Эта статья продолжает разбор переименования. Небольшие примеры языка ниже можно читать отдельно; они не соединяются с игрой. В реальном файле object_names.py запись местами плотная. Допустимо мысленно раскладывать её на несколько строк, сохраняя порядок.

Функция, аргумент и отступ#

PYTHON
def display_name(raw):
    if raw == "":
        return "Стандартное имя"
    return raw

name = display_name("Север")

def объявляет функцию. raw — входной аргумент. Двоеточие начинает блок, отступ показывает, какие строки к нему относятся. return заканчивает вызов и возвращает значение. Здесь name содержит Север.

В Python нельзя свободно убирать отступы, как пробелы в XML. Строка после if с большим отступом выполняется только в его ветке. Конец блока определяется возвращением к прежнему уровню отступа.

Словарь — запись с именованными полями#

PYTHON
request = {
    "request_id": "lesson-1",
    "object": "ship-example",
    "revision": 1,
    "name": "Север",
}

object_key = request["object"]
scope = request.get("scope", "object")

Здесь {...} создаёт словарь, ключ: значение добавляет поле. request["object"] требует наличие ключа. get позволяет задать запасное значение. В примере поле scope не задано, поэтому получается object.

Значения учебные, это не пакет для ручной отправки хосту. Настоящая команда дополнительно имеет контекст, соединение и проходит допуск.

Другие контейнеры: [] — список, () часто обозначает кортеж, set() — пустое множество. Запись {"host", "guest"} создаёт множество, а {} — пустой словарь. Как и в Lua/MD, вид скобок нужно читать в контексте языка.

Основные операторы в файлах проекта#

Запись Значение
= Присвоить
==, != Проверить равенство / неравенство
<, <=, >, >= Сравнить
and, or, not Логические операции с коротким вычислением
in, not in Есть / нет в контейнере; у словаря проверяются ключи
is None, is not None Проверить отсутствие / наличие значения по соглашению Python
+, -, *, /, % Арифметика; для некоторых типов смысл другой
+= Изменить значение, например revision += 1
obj.field Доступ к атрибуту объекта
raw["name"] Доступ по ключу
# пояснение Комментарий

В Python пустые строки, списки, словари и нулевые числа ложны в условии; в Lua 0 и пустая строка истинны. Не переводи условия механически между языками.

Разбираем плотную строку из проверки намерения#

В intent(raw) есть выражение:

PYTHON
set(raw) - {"scope"} != {"request_id", "object", "revision", "name"}

После проверки, что raw — словарь, set(raw) получает множество его ключей. Вычитание убирает необязательный scope. Оставшийся набор должен точно совпасть с обязательными полями. Поэтому отсутствующее обязательное поле и неожиданное лишнее поле приводят к отказу.

Проверка ключей ещё не проверяет значения. Следом вызываются проверки token, integer, name_text. Это собственные функции проекта: найди их определения через rg -n, прежде чем приписывать им поведение стандартного Python.

Класс и self#

PYTHON
class PendingName:
    def __init__(self):
        self.pending = None

    def begin(self, name):
        self.pending = name

operation = PendingName()
operation.begin("Север")

Это учебный класс, его нет в API проекта. Класс описывает объект с данными и методами. __init__ задаёт начальное состояние. self — конкретный экземпляр. После вызова operation.pending содержит Север.

У реального ObjectNameAuthority в self находятся states, records, pending, responses, acks и другие поля. Значит, self.pending относится к данной подсистеме, а session.object_names позволяет получить эту подсистему из общей сессии.

Не путай такие поля с локальной переменной метода: локальное result живёт в вызове, а self.responses используется между вызовами.

Почему работа разделена на request, pump и observe#

Метод в ObjectNameAuthority Когда нужен Что делает
request Пришло намерение Проверить и записать ожидающую операцию либо ответ
pump Очередной проход обслуживания Начать загрузку или отправить/повторить команду движку
observe Пришли данные из X4 хоста Проверить наблюдение и обновить состояние/исход
stream Собирается снимок участнику Отобрать разрешённое состояние в пределах бюджета
acknowledge Пришёл ACK Учесть применение конкретной ревизии

Это имена и обязанности данного семейства. У соседних подсистем контракт может отличаться.

Разделение позволяет быстро закончить обработку запроса и продолжить работу позже, когда движок ответит. Занятый цикл while, ожидающий ответ в UI, заморозил бы интерфейс. Здесь ожидание представлено данными, а не удержанием вызова.

Ранний выход и исключение#

В исходниках часто встречается форма:

PYTHON
if not self.ready:
    return

Читай как «продолжать пока рано». return без значения даёт None. В request также встречаются return True и return False. Смысл этих флагов определяется вызывающим кодом. В этом семействе True не равен успешному переименованию: метод может вернуть True, сохранив ответ denied. Реальный исход находится в status и подтверждённых данных.

raise ProtocolError(...) создаёт исключение проекта. try/except позволяет вызывающей стороне обработать ожидаемую ошибку. Найди обработку, прежде чем решать, что такой отказ обрушает всю службу или, наоборот, всегда безопасно игнорируется.

Полезные встроенные операции#

Вызов Что делает
isinstance(raw, dict) Проверяет тип
len(rows) Количество элементов
dict(...) Создаёт словарь; форма с аргументами задаёт поля
set(raw) Создаёт множество, у словаря — из ключей
sorted(keys) Возвращает отсортированный список
value.encode("utf-8") Превращает строку в байты UTF-8
wire.split("|") Разделяет строку по символу
"|".join(parts) Собирает строковые части с разделителем

Из len(name) и len(name.encode('utf-8')) получаются разные ограничения. Например, кириллица занимает больше одного байта на букву в UTF-8. В object_names.py проверяется и текст, и его допустимый размер; не своди оба условия к одной произвольной длине.

Документация языка для дальнейшего чтения: управление потоком, структуры данных, классы. Эти основы полезны до изучения всего сетевого стека.

Что читать в C++ пока достаточно#

На первом проходе узнавай #include, функцию, аргументы, return, if, указатели * и обращения ./->. Нативный слой требует отдельного знания типов и ABI — соглашений, по которым взаимодействуют скомпилированные части.

extern "C" у luaopen_x4fo нужен для точки входа C-модуля с ожидаемым именем. ffi.cdef в Lua описывает C-интерфейс, но не реализует его и не подтверждает наличие функции. Подробный разбор смещений памяти не нужен для первого знакомства с переименованием.

Теперь переходи к первой самостоятельной задаче или вернись на старт.