Решили: транспорт выбирается по факту — поднялся Steam, идём через него; не поднялся, поднимаем
петлю в своём процессе и говорим об этом предупреждением. Steam инициализируется в конструкторе
SteamBootstrap, а не на Start.
Почему инициализация переехала в конструктор: от ответа «поднялся или нет» зависит СОСТАВ
контейнера. Фаза Start идёт после того, как все объекты созданы, — спрашивать там значило бы
выбирать транспорт до того, как ответ известен.
Что отвергли. Вариант «тестовый хук в корне» (подменять транспорт только из теста) — производственный код обзаводится веткой ради тестов, а такие ветки живут вечно. Вариант «собирать приёмники гостя руками в тесте» — ровно та ручная сборка, которая 05.08 пропустила незарегистрированный интерфейс: тест соединяет систему правильно даже тогда, когда игра соединить её не может.
Ради чего это на самом деле. Петлю можно соединить саму с собой, и тогда сценарий «хозяин раздаёт, напарник принимает» проверяется в одном процессе, на живой разводке. Полноценные две игры в одном процессе не поднять — у них общий мир и одна камера, — поэтому вторым концом провода работает узел петли.
Побочная польза, не меньшая: без Steam кооп до сих пор молчал. 02.08 инициализация уехала вместе с удалённым netcode, и игра вела себя как при незапущенном клиенте — лобби «просто не создавалось», искали это живым тестом вдвоём. Теперь пустой провод называет себя вслух.
Грабли на будущее: PlayMode-сборка тестов не ссылается на Guildmaster.Net, и первый же
сценарный тест этого потребует. Правка .asmdef не видна compile-check.ps1 до рефреша в
редакторе — он собирает по Library/Bee/artifacts/*.rsp, которые генерирует Unity.
Владелец правды: Game/RootLifetimeScope.cs (фабрика транспорта и комментарий при ней),
Net/Session/SteamBootstrap.cs, тест CoopScopeWiringTest.