Решили: провод выбирается явно — Steam, если он поднят и мы не в автоматическом прогоне; иначе петля в своём процессе, и об этом пишется вслух. Плюс петля умеет отдать номер хоста тому, кто его просит.

Почему вообще выбор. Кооп проверялся только вживую вдвоём, потому что в тестах провода не было вовсе. Теперь гость — настоящая игра, хозяин — второй конец петли, и сценарий целиком проверяется в одном процессе.

Грабли, стоившие двух прогонов: в теневом прогоне поднялся ЖИВОЙ Steam. Клиент запущен на машине разработчика, и игра из batchmode радостно подключилась к настоящему аккаунту (в логе — Steam на связи: xGaida). Тестовая петля при этом осталась ни с кем не соединена, а тест падал с «гость не открыл место», хотя код был верен. Отсюда правило: автоматический прогон — это игра без игрока, окна нет, оверлея нет, звать некого — там сеть всегда петля, независимо от того, что запущено на машине.

Вторые грабли, тоньше: номер хоста достаётся тому, кто создал узел первым. Игра поднимает транспорт на старте, задолго до того, как выяснится роль сеанса, — то есть неизбежно занимает номер хоста, даже когда играет гостем. Её же «шлём хосту» уходило бы ей самой, и гостевую половину коопа проверить было бы нечем. Петля научилась переселять прежнего владельца номера — ровно то, что настоящая сеть делает рукопожатием, когда хост раздаёт номера при подключении.

Steam теперь поднимается в конструкторе, а не на Start: от ответа «поднялся или нет» зависит СОСТАВ контейнера, а фаза Start идёт уже после того, как все объекты созданы.

Что этим закрыто. Пять PlayMode-сценариев: боевой скоуп собирается в обеих ролях; хозяин раздаёт «где мы», паспорта и ленту; гость открывает объявленное место и его шов «кто на арене» отвечает; согласие гостя доезжает до хозяина. Последний проверяет не локальный флажок, а факт доставки — кнопка, живая на вид и мёртвая на деле, была у нас дважды.

Владелец правды: Game/RootLifetimeScope.cs (комментарий при выборе провода), Net/Transport/LoopbackNetwork.cs (CreateNode(claimHost)), тесты CoopScopeWiringTest, CoopHandoutScenarioTest, GuestFollowsTheHostTest.