Решили: у транспорта появилось ПОКОЛЕНИЕ сессии. Номер растёт на каждом Shutdown, сокет
запоминает тот, при котором родился, и события чужого поколения не попадают даже во входящую
очередь. Вторым рубежом состав сеанса больше не вычёркивает себя по «пир отключился».
Почему: Steam досказывает за закрытым соединением — OnDisconnected приходит не в момент
закрытия, а на ближайшем RunCallbacks, когда на месте старого сокета уже подняли новый. Отличить
такое эхо от настоящего события было нечем, и оно выходило наружу как факт про живую сессию.
Отвергли снятие Owner у закрываемого сокета: оно лечит только случай, когда объект соединения
новый, и молчит, когда Facepunch переиспользует прежний. Поколение отвечает на настоящий вопрос —
«из какой сессии это событие» — и отвечает одинаково для подключения, отключения и сообщения.
Грабли: один дефект дал два симптома, которые на прогоне выглядели как разные баги, и второй из них уводил в сторону убедительнее первого.
Первый: игрок побывал гостем, поднял свой хостинг и получил эхо «пир 0 отключился». Номер один, смысл разный — у гостя ноль означал чужого хозяина, у хозяина ноль означает его самого. Состав вычеркнул хозяина, и сеанс остался без него: «нас нет в составе сеанса (наш номер 0, участников 1)». Дальше сыпалось всё, что читает состав, — стороны, панель участников, права на кнопки.
Второй: подключение к чужому лобби падало с «Хост не ответил», хотя хост отвечал исправно. То же
эхо, только в состоянии Connecting. В логе рядом стояла строка [GameFlow] запрос «В главное меню», и на неё легко списать разрыв — но Shutdown чистит очередь входящих и наружу не поднимает
ничего, поэтому событие могло прийти только колбэком за уже закрытым соединением. Строка GameFlow
оказалась соседом по времени: игрок нажал кнопку на диалоге потерянной связи.
Отсюда же вторая мораль: текст ошибки назначается по состоянию, а не по причине. «Хост не
ответил» — это Connecting плюс разрыв, а не диагноз. Игрок читает диагноз.
Владелец правды: SteamNetTransport._generation, HostSessionRoster.OnPeerDisconnected,
тест StaleSessionEchoTests.