Исключения
Библиотека держит четыре ответа на четыре вопроса: «данные битые», «версия не та», «соединение закрыто», «объект уже выброшен». Свои типы заведены только там, где типа из BCL нет.
Битые данные с провода — InvalidDataException
Всё, что пришло не так, как обещал протокол: длина кадра вне диапазона, VarInt длиннее пяти байт, конверт сжатия без пакета, id пакета за концом кадра, распаковка не сошлась, чтение за концом буфера. Это тип из BCL, и он один и тот же в примитивах (MinecraftPrimitiveReader) и в транспорте (Framing/).
Отдельный случай — поток кончился посреди кадра: EndOfStreamException. Не мусор, а обрыв.
Версия не та — ProtocolNotSupportException
Пакет или тип не существует на той версии протокола, с которой идёт работа. Летит и при чтении, и при записи, и при типизированной отправке (ClientPacketExtensions.SendAsync), когда у пакета нет id для этой версии.
Родственные типы того же слоя: WrongLayerException — пакет собран неверно, для целевой версии не заполнена нужная группа полей; PacketDecodeException — обёртка типизированного декода с полем Error (Malformed, UnsupportedVersion, TrailingBytes).
PacketIo.TryDecode ловит только эти семейства и мусор с провода. Ошибка в самом ридере — NRE, нехватка памяти — не ошибка декода и проходит наружу.
Соединение закрыто — ConnectionAbortedException
Единственный собственный тип транспорта, наследник IOException. Причина закрытия лежит в InnerException, она же в CloseReason соединения. Пустая причина — закрылись чисто.
Правило одно и то же у MinecraftConnection и StreamingConnection:
после
DisposeAsync—ObjectDisposedException;на ошибку вызывающего, которая до потока не дошла (два чтения разом, соединение уже передано в streaming), —
InvalidOperationException, и соединение остаётся живым;OperationCanceledExceptionприходит только на свой токен, который отменил сам вызывающий;всё остальное —
ConnectionAbortedExceptionс причиной внутри.
Первый сбой потока выходит как есть и защёлкивается: следующий вызов уже сообщает о закрытом соединении и показывает ту же причину.
Отмена стоит по-разному на двух соединениях, и это не небрежность. У StreamingConnection чтение идёт большими кусками в свой буфер, поэтому отменённое чтение ничего не теряет и соединение живёт дальше. У MinecraftConnection кадр читается по одному: отменённый вызов уже съел часть кадра с провода и сдвинул шифр вместе с ней — продолжать нечего, соединение закрывается. Отправка фатальна у обоих: часть кадра могла уже уйти на провод. Токен, отменённый до начала вызова, не стоит ничего — соединение остаётся открытым.
Объект выброшен — ObjectDisposedException
Тип из BCL. Отвечают им и соединения, и кирпичи (PacketStreamReader, BufferedPacketReader, BufferedPacketWriter) после Dispose.