Вы определенно хотите использовать UDP.Для игры вам все равно, если позиция спрайта будет неправильной в течение короткого времени.Так что в этом случае идеальным является UDP.
Кроме того, если у вас есть какой-либо контроль над кодом сервера, я бы не стал использовать отдельные потоки для клиентов.Потоки полезны, если вам нужно вызывать библиотеки, которые вы не можете контролировать и которые можете заблокировать (например, потому что они касаются файла или пытаются выполнить дополнительное сетевое взаимодействие).Но они дорогие.Они потребляют много ресурсов и, как таковые, на самом деле делают вещи на медленнее , чем могли бы быть.
Так что для сервера сетевых игр, где задержка и производительность абсолютно необходимы, я бы просто использовал одинпоток для обработки очереди команд, которые имеют состояние, а затем убедитесь, что вы никогда не выполняете операцию, которая блокирует.Таким образом, каждая команда обрабатывается по порядку, ее состояние оценивается и обновляется (как лазерный взрыв, пересекающийся с другим объектом).Если для команды требуется блокировка (например, чтение из файла), вам необходимо выполнить неблокирующее чтение и соответствующим образом установить состояние этой команды, чтобы ваш командный процессор никогда не блокировал.Ключ в том, что командный процессор никогда не блокируется никогда.Это просто будет работать в цикле, но вам придется вызывать Thread.sleep (x) соответствующим образом, чтобы не тратить процессор.
Что касается клиентской стороны, когда клиент отправляет команду (например,они запускали лазер или что-то подобное), клиент генерировал объект ответа и вставлял его в карту с идентификатором последовательности в качестве ключа.Затем он отправляет запрос с идентификатором последовательности, и когда сервер отвечает этим идентификатором, вы просто просматриваете объект ответа на карте и декодируете ответ в этот объект.Это означает, что вы можете выполнять параллельные операции.