F.A.Q. по игре Quake 3 Arena (2). Более полезен, чем другой.


Рендерер Quake III

Рендерер Quake III стал эволюционным развитием рендерера Quake II с аппаратным ускорением: классическая часть построена на архитектуре «двоичного разбиения»/«потенциально видимых наборов», но добавлены два новых заметных ключевых аспекта:

  • Система шейдеров, построенная поверх фиксированного конвейера OpenGL 1.X. Это было большим достижением для 1999 года. Она обеспечивала большое пространство для инноваций в эру до повсеместно распространённых сегодня вершинных, геометрических и фрагментных шейдеров.
  • Поддержка многоядерной архитектуры: клиент-серверная модель OpenGL блокирует некоторые методы и система потоков частично решает эту проблему.

Архитектура

Рендерер полностью содержится в renderer.lib и статично связан с quake3.exe:
Общая архитектура повторяет Quake Classic: в ней используется знаменитое сочетание BSP/PVS/карт освещения:

  • Предварительная обработка:
      Дизайнер игры создаёт с помощью QRadiant .map и сохраняет его.
  • q3bsp.exe разрезает карту двоичным разбиением пространства (BSP). Я писал об этом в обзоре рендерера Quake1.
  • Из BSP генерируется система порталов: я писал об этом в статье об инструменте Doom3 Dmap.
  • q3vis.exe использует систему порталов и генерирует PVS (потенциально видимый набор) для каждого листа. Каждый PVS сжимается и хранится в файле bsp, как описано в предыдущей статье.
  • Система порталов очищается.
  • q3light.exe вычисляет освещение для каждого полигона на карте и сохраняет результат как текстуры карт освещённости в файле bsp.
  • На этом этапе все предварительно рассчитанные данные (PVS и карты освещения) хранятся в файле .bsp.
  • Время выполнения:
      Движок загружает карту и bsp.
  • Когда необходима визуализация:
  • Движок разархивирует PVS для текущего листа и определяет, что видимо на самом деле.
  • Для каждого полигона он с помощью мультитекстурирования сочетает карту освещения с цветом.

Этап мультитекстурирования и карт освещения чётко заметен, если изменить движок и отображать только одно или другое:
Текстура, нарисованная дизайнером уровня/художниками:

Карта освещения, сгенерированная q3light.exe:

Окончательный результат при соединении с помощью мультитекстурирования во время выполнения:

Архитектура рендеринга была рассмотрена Брайаном Хуком (Brian Hook) на Game Developer Conference в 1999 году. К сожалению, видео с GDC Vault уже недоступно! [Зато оно есть на youtube.]

Шейдеры

Система шейдеров построена поверх фиксированного конвейера OpenGL 1.X, а потому очень затратна. Разработчики могут программировать модификации вершин, но также и добавлять проходы текстур. Это подробно рассмотрено в библии шейдеров Quake 3 Shader bible:

Многоядерный рендерер и SMP (симметричная многопроцессорность)

Многим неизвестно, что Quake III Arena была выпущена с поддержкой SMP с помощью cvariable r_smp. Фронтэнд и бекэнд обмениваются информацией через стандартную схему Producer-Consumer. Когда r_smp имеет значение 1, рисуемые поверхности попеременно сохраняются в двойной буфер, расположенный в ОЗУ. Фронтэнд (который называется в этом примере Main thread
(основным потоком)), попеременно выполняет запись в один из буферов, в то время как из другого выполняет чтение бекэнд (в этом примере называемый
Renderer thread
(потоком рендерера)).

Пример демонстрирует, как всё работает:

t0-t1:

  • Main thread решает, что отрисовывать, и записывает поверхности в surfacebuffer1.
  • Для потока Renderer thread нет данных, поэтому он заблокирован.
  • Поток GPU thread тоже ничего не делает.

t1-t2: повсюду начинаются процессы:

  • Поток Main thread решает, что будет видимо в следующем кадре. Он записывает поверхность в буфер surfacebuffer2: это типичный пример двойной буферизации.
  • Тем временем, поток Renderer thread выполняет вызов OpenGL и терпеливо ждёт, пока поток GPU thread не скопирует всё в надёжное место.
  • Поток GPU thread считывает поверхность оттуда, куда указывает Renderer thread.

Заметьте, что в t2:

  • Renderer thread всё ещё передаёт данные в GPU: используется SurfaceBuffer1.
  • Main thread завершил запись в SurfaceBuffer2… но не может начать запись в SurfaceBuffer1: он заблокирован

Этот случай (когда Renderer thread блокирует Main thread) очень часто возникает при игре в Quake III: Продемонстрируем ограничение блокировки одного из методов OpenGL API.
После t2:

  • Как только Renderer thread заканчивает с SurfaceBuffer1 (t3), он начинает закачивать поверхности из SurfaceBuffer2.
  • Как только он разблокируется (в t3), Main thread начинает работать в следующем кадре, записывая в SurfaceBuffer1.
  • В такой конфигурации GPU почти никогда не простаивает.

Примечание:

синхронизация выполняется через Windows Event Objects в winglimp.c (часть с ускорением SMP внизу).

Подборки команд

Параметры запуска

Данный список параметров запуска в CS:GO использует большинство игроков.

  • -console — открывает возможность использовать в игре консоль;
  • -novid — при запуске игры убирает заставку;
  • -threads 4 — заставляет использовать 4 ядра процессора (2 ядра, то ”-threads2”, 6 ядер, то ”-threads6”);
  • -refresh 120 — частота обновление монитора, если Ваш монитор не поддерживает такую частоту, то выставляем значение меньше;
  • -noforcemparms — отключаем акселерацию мышки в игре (скорость мышки всегда будет одинаковая);
  • -high — запускает игру с высоким приоритетом;
  • -tickrate 128 — рекомендуемое значение сетевого параметра , всем знакомый еще с CS 1.6;
  • +cl_cmdrate 128 — рекомендуемое значение сетевого параметра;
  • +cl_updaterate 128 — рекомендуемое значение сетевого параметра;
  • +rate 128000 — рекомендуемое значение сетевого параметра;
  • +ex_interpratio 1 — рекомендуемое значение сетевого параметра;

Команды для тренировки

В данный список мы постарались включить все необходимые команды для тренировок со своей командой.

  • sv_infinite_ammo 1 — бесконечные патроны в игре;
  • sv_grenade_trajectory 1 — прорисовывает всю траекторию полета гранаты, точками показывает, где граната соприкасалась с текстурами;
  • ammo_grenade_limit_total 111 — максимальное количество гранат у игрока;
  • sv_showimpacts 1 — показывает точки, в которые попали пули;
  • sv_showbullethits 1 — при попадании прорисовывает силуэт противника в данной точке;
  • cl_disable_ragdolls 1 — убирает Ragdoll физику, работает при включенной команде sv_cheats 1. Как правило данную команду используют при тренировке, когда fps просидает при большем количестве дымов;
  • dsp_slow_cpu 1 — Уменьшает качество звуков. Не рекомендуем использовать данную команду;
  • mat_disable_bloom 1 — данная команда отключает bloom эффект;
  • r_drawparticles 0 — убирает анимацию оружия, всплеска воды и.т.п;
  • mp_buy_anywhere 1 — позволяет покупать оружие на всей карте;
  • mp_freezetime 0 — убирает заморозку в начале раунда;
  • mp_buytime 3600 — увеличивает время на закупку оружия до 60 минут;
  • mp_roundtime_defuse 60 — увеличивает время раунда до 60 минут;
  • mp_maxmoney 55500 — увеличиваем максимальное количество денег до $55500;
  • mp_startmoney 55500 — увеличивает стартовое количество денег на $55500;
  • mp_warmup_end — закончить разминку;
  • mp_autoteambalance 0 — отключает автобаланс игроков в командах;
  • mp_warmuptime 55555 — данной командой устанавливается бесконечная разминка на карте;
  • mp_timelimit 50 — время до смены карты

Стандартный набор команд для тренировки. Просто скопируйте весь список в консоль:

sv_grenade_trajectory 1; ammo_grenade_limit_total 111; sv_showimpacts 1; mp_buy_anywhere 1; mp_freezetime 0; mp_buytime 3600; mp_roundtime_defuse 60; mp_maxmoney 55500; mp_startmoney 55500; mp_warmuptime 55555; mp_timelimit 50; bot_kick

Консольные команды CS:GO

Команды для ботов

  • bot_add — добавить бота в команду, в которой не хватает игроков;
  • bot_add_ct — добавить бота за контр-террористов;
  • bot_add_t — добавить бота за террористов;
  • bot_kick — с помощью данной команды можно кикнуть всех ботов;
  • bot_kick Bob — в таком случае мы кикним только одного бота с именем «Bob»;
  • bot_kill — если мы не указываем имя, то убиваем всех ботов;
  • bot_zombie 1 — с помощью данной команды мы заморозим всех ботов;
  • bot_dont_shoot — если мы не указываем имя, то все боты прекратят стрельбу;
  • bot_difficulty — устанавливает уровень интеллекта ботов (0 = легко, 1 = нормально, 2 = тяжело);
  • bot_stop — боты просто остановятся;
  • bot_mimic 1 — бот будет повторять действия игрока;
  • bot_mimic_yaw_offset 0 — с помощью этой команды мы отменяем повтор действий ботом игрока;
  • bot_crouch 1 — с помощью данной команды заставляем ботов присесть;
  • bot_place — бот появляется перед вами;

Настройка радара

При отсутствии нормальной инфы от своих тимейтов, необходимо постоянно следить за радаром и отслеживать их передвижение по карте. Для этого необходимо его правильно настроить и данные команды быстро помогут вам это сделать.

  • cl_radar_always_centered 0 — центрирование карты;
  • cl_radar_scale 0.3 (обычно 0.3 или 0.4) — размер карты;
  • cl_radar_icon_scale_min 0.7 (рекомендуем значение 0.7)- размер точек игроков на карте;

Настройка FPS

  • net_graph 3 — показывает количество fps;
  • fps_max 305 — указать максимальное количество fps в игре;
  • func_break_max_pieces 0 — количество осколков от объектов;

Пришло время подвести итоги данной статьи. Мы затронули большое количество консольных команд для Counter-Strike: Global Offensive, которые могут Вам пригодится в любой момент. Мы постарались написать развернуто, понятно и надеемся что многие игроки найдут что-то полезное для себя.

Сетевая модель

Сетевая модель Quake3 — это, без всяких сомнений, наиболее элегантная часть движка. На низком уровне Quake III по-прежнему абстрагирует обмен данными с модулем NetChannel, впервые появившимся в Quake World. Самое важное, что нужно понять:
В окружениях с быстрым ритмом изменений любая информация, не полученная при первой передаче, не стоит повторной отправки, потому что она всё равно устареет.

Поэтому в результате движок использует UDP/IP: в коде нет следов TCP/IP, потому что «надёжная передача» создаёт недопустимую задержку. Сетевой стек был улучшен двумя взаимоисключающими слоями:

  • Шифрование с использованием заранее переданного ключа.
  • Сжатие с помощью заранее вычисленного ключа Хаффмана.

Но самый удивительный дизайн находится на стороне сервера, где элегантная система минимизирует размер каждой датаграммы UDP и компенсирует ненадёжность UDP: история снапшотов генерирует дельта-паркеты с помощью интроспекции памяти.

Архитектура

Сторона клиента в сетевой модели довольно проста: клиент каждый кадр отправляет серверу команды и получает обновления состояния игры. Сторона сервера немного более сложна, потому что она должна передавать общее состояние игры на каждый клиент с учётом потерянных пакетов UDP. Этот механизм содержит три основных элемента:

  • Master Gamestate
    — это общее, истинное состояние вещей. Клиенты отправляют свои команды в Netchannel. Они преобразуются в event_t, которые изменяют состояние игры при получении сервером.
  • Для каждого клиента сервер хранит 32 последних состояния игры
    , отправляемых по сети в циклическом массиве: они называются снапшотами. Массив циклически перемещается с использованием знаменитого трюка с двоичной маской, о которой я рассказывал в статье о Quake World Network (элегантные решения).
  • У сервера также есть «пустое» состояние
    , котором каждое поле имеет значение 0. Оно используется для дельта-снапшотов, у которых нет «предыдущих состояний».

Когда сервер решает отправить клиенту обновление, он использует по порядку все три элемента, чтобы сгенерировать сообщение, которое потом передаётся через NetChannel.
Интересный факт:

хранение такого количества состояний игры для каждого игрока занимает большой объём памяти: по моим измерениям, 8 МБ для четверых игроков.

Система снапшотов

Чтобы понять систему снапшотов, приведу пример со следующими условиями:

  • Сервер отправляет обновление клиенту Client1.
  • Сервер пытается передать состояние, которое имеет четыре поля, клиенту Client2 (три целочисленных значения для position[X], position[Y], position[Z] и одно целочисленное значение для здоровья).
  • Связь осуществляется через UDP/IP: эти сообщения довольно часто теряются в Интернете.

Кадр 1 сервера:
Сервер получил несколько обновлений от каждого клиента. Они повлияли на общее состояние игры (зелёного цвета). Настало время передать состояние клиенту Client1:

Чтобы сгенерировать сообщение, сетевой модуль ВСЕГДА делает следующее:

  1. Копирует общее состояние игры в следующий слот истории клиента.
  2. Сравнивает его с другим снапшотом.

Именно это мы видим на следующем изображении.

  1. Общее состояние игры (Master gamestate) копируется с индексом 0 в историю Client1: теперь оно называется «Snapshot1».
  2. Поскольку это первое обновление, в истории Client1 правильных снапшотов, поэтому движок использует пустой снапшот «Dummy snapshot», в котором все поля обнулены. Это приводит к ПОЛНОМУ обновлению, потому что каждое поле отправляется в NetChannel.

Важнее всего здесь понять то, что если в истории клиента нет правильных снапшотов, то движок берёт для генерирования дельта-сообщения пустой снапшот. Это приводит к полному обновлению, отправляемому клиенту в 132 битах (каждому полю предшествует битовый маркер): [1 A_on32bits 1 B_on32bits 1 B_on32bits 1 C_on32bits].

Кадр 2 сервера: Давайте теперь переместимся немного в будущее: вот второй кадр сервера. Как мы видим, каждый клиент отправил команды, и все они повлияли на общее состояние игры Master gamestate: Client2 переместился по оси Y, поэтому теперь pos[1] равно E (синего цвета). Client1 тоже отправил команды, но, что более важно, он подтвердил получение предыдущего обновления, поэтому Snapshot1 был помечен как подтверждённый («ACK»):

Выполняется тот же самый процесс:

  1. Общее состояние игры копируется в следующий слот истории клиента: (индекс 1): это Snapshot2
  2. На этот раз у нас в истории клиента есть правильный снапшот (snapshot1). Сравниваем эти два снапшота

В результате по сети пересылается только частичное обновление (pos[1] = E ). В этом заключается красота такого дизайна: процесс всегда одинаков.
Примечание: поскольку каждому полю предшествует битовый маркер (1=изменилось, 0=не изменилось), для частичного обновления из примера выше используется 36 бит: [0 1 32bitsNewValue 0 0].

Кадр 3 сервера: Сделаем ещё один шаг вперёд, чтобы посмотреть, как система справляется с утерянными пакетами. Теперь мы находимся в кадре 3. Клиенты продолжают отправлять команды серверу. Client2 потерпел урон и здоровье теперь равно H. Но Client1 не подтвердил последнее обновление. Возможно, потерялся UDP сервера, возможно, потерялся ACK клиента, но в результате его невозможно использовать.

Несмотря на это, процесс остаётся тем же:

  1. Копируем общее состояние игры в следующий слот истории клиента: (индекс 2): это Snapshot3
  2. Сравниваем последний правильный подтверждённый снапшот (snapshot1).

В результате, сообщение отправляет его частично и содержит сочетание старых и новых изменений: (pos[1]=E и health=H). Заметьте, что snapshot1 может быть слишком устаревшим для использования. В этом случае движок снова использует «пустой снапшот», что приводит к полному обновлению.

Красота и элегантность системы — в её простоте. Один алгоритм автоматически:

  • Генерирует частичное или полное обновление.
  • Повторно отправляет в одном сообщении СТАРУЮ информацию, которая не была получена, и НОВУЮ информацию.

Интроспекция памяти на C

Вы можете задаться вопросом — как Quake3 сравнивает снапшоты интроспекции… ведь в C её нет.
Ответ заключается в следующем: каждое местоположение поля для netField_t создаётся предварительно с помощью массива и умных директив предварительной обработки:

typedef struct { char *name; int offset; int bits; } netField_t; // используем оператор преобразования в строку для сохранения типизации… #define NETF(x) #x,(int)&((entityState_t*)0)->x netField_t entityStateFields[] = { { NETF(pos.trTime), 32 }, { NETF(pos.trBase[0]), 0 }, { NETF(pos.trBase[1]), 0 }, … } Полный код этой части находится в MSG_WriteDeltaEntity из snapshot.c. Quake3 даже не знает, что сравнивает: он слепо использует индекс, смещение и размер entityStateFields и отправляет по сети различия.

Предварительная фрагментация

Углубившись в код, можно увидеть, что модуль NetChannel разрезает сообщения на блоки по 1400 байт (Netchan_Transmit), даже несмотря на то, что максимальный размер датаграммы UDP составляет 65507 байт. Так движок избегает разбивания пакетов роутерами при передаче через Интернет, потому что у большинства сетей максимальный размер пакета (MTU) равен 1500 байтам. Избавление от фрагментации в роутерах очень важно, потому что:

  • При поступлении в сеть роутер должен заблокировать пакет, пока фрагментирует его.
  • При покидании сети проблемы ещё серьёзнее, потому что необходимо дождаться всех частей датаграммы, а потом собрать их с большими затратами времени.

Сообщения с надёжной и ненадёжной передачей

Хоть система снапшотов и компенсирует утерянные в сети датаграммы UDP, некоторые сообщения и команды должны быть доставлены обязательно
(например, когда игрок выходит из игры или когда серверу нужно, чтобы клиент загрузил новый уровень).

Такая обязательность абстрагирована модулем NetChannel: я писал об этом в одном из предыдущих постов.

Рекомендуемое чтение

Один из разработчиков, Брайан Хук, написал небольшую статью о сетевой модели.
Автор Unlagged Нил «haste» Торонто (Neil «haste» Toronto) тоже её описывал.

Сетевые настройки CS:GO

  • net_channels 0 — отобразить информацию о канале в консоли (та же самая информация что и у команды net_graph);
  • net_graph 1 — включить панель информации о соединении;
  • net_graphheight 40 — высота net_graph панели;
  • net_graphmsecs 400 — изменения скорости обновления блока;
  • net_graphpos 1 — место положение net_graphа;
  • net_graphproportionalfont 0.5 — размер net_graph;
  • net_graphshowinterp 1 — показывает строку интерполяции;
  • net_graphshowlatency 1 — рисует график Ping и пакетов;
  • net_graphsolid 1 — выключить прозрачность лагомера;
  • net_graphtext 1 — включить текст в блоке;
  • net_maxroutable 1260 — максимальная фрагментация в байтах на пакет;
  • net_scale 5 — размер графика;
  • option_duck_method 0 — удерживать/одиночное нажатие клавишу приседания;
  • option_speed_method 0 — удерживать/одиночное нажатие клавишу бега;
  • rate 30000 — количество байтов, которые клиент может получить от сервера за секунду.

Виртуальная машина

Если предыдущие движки отдавали виртуальной машине на откуп только геймплей, то idtech3 поручает ей существенно более важные задачи. Среди прочего:

  • Визуализация запускается в клиентской виртуальной машине.
  • Механизм компенсации задержки целиком реализован в клиентской ВМ.

Более того, её дизайн гораздо более сложен: в нём сочетается защита/портируемость виртуальной машины Quake1 с высокой производительностью нативных DLL Quake2. Это достигается компиляцией на лету байт-кода в команды x86.
Интересный факт:

виртуальная машина изначально должна была стать простым интерпретатором байт-кода, но производительность оказалась очень низкой. Поэтому команда разработчиков написала компилятор x86 времени выполнения. Согласно файлу .plan от 16 августа 1999 года, с этой задачей справились за один день.

Архитектура

Виртуальная машина Quake III называется QVM. Постоянно загружены три её части:

  • Сторона клиента: загружены две виртуальные машины. В зависимости от состояния игры сообщения отправляются одной из них: cgame: получает сообщения в фазе боя. Выполняет только отсечение невидимой графики, предсказания и управляет renderer.lib.
  • q3_ui: получает сообщения в режиме меню. Использует системные вызовы для отрисовки меню.
  • Сторона сервера:
      game: всегда получает сообщения, выполняет игровую логику и использует bot.lib для работы ИИ.
  • Внутренности QVM

    Прежде чем приступить к описанию использования QVM, давайте проверим, как генерируется байт-код. Как обычно, я предпочитаю объяснять с помощью иллюстраций и краткого сопроводительного текста:
    quake3.exe и его интерпретатор байт-кода сгенерированы с помощью Visual Studio, но в байт-коде ВМ применяется совершенно другой подход:

    1. Каждый файл .c (модуль трансляции) компилируется отдельно при помощи LCC.
    2. LCC используется со специальным параметром, благодаря которому выполняется вывод не в PE (Windows Portable Executable), а в промежуточное представление, которое является текстовой сборкой стековой машины. Каждый созданный файл состоит из частей text, data и bss с экспортом и импортом символов.
    3. Специальный инструмент id Software под названием q3asm.exe получает все текстовые файлы сборок и собирают их вместе в файл .qvm. Кроме того, он преобразует всю информацию из текстового в двоичный вид (ради скорости, на случай, если невозможно применить нативные преобразованные файлы). Также q3asm.exe распознаёт вызываемые системой методы.
    4. После загрузки двоичного байт-кода quake3.exe преобразует его в команды x86 (не обязательно требуется).

    Внутренности LCC

    Вот конкретный пример, начинающийся с функции, которую нам нужно запустить в виртуальной машине: extern int variableA; int variableB; int variableC=0; int fooFunction(char* string){ return variableA + strlen(string); } Сохранённый в модуле трансляции module.c lcc.exe вызывается со специальным флагом, чтобы избежать генерации объекта Windows PE и выполнить вывод в промежуточное представление. Это файл вывода .obj LCC, соответствующий представленной выше функции C: data export variableC align 4 LABELV variableC byte 4 0 export fooFunction code proc fooFunction 4 4 ADDRFP4 0 INDIRP4 ARGP4 ADDRLP4 0 ADDRGP4 strlen CALLI4 ASGNI4 ARGP4 variableA INDIRI4 ADDRLP4 0 INDIRI4 ADDI4 RETI4 LABELV $1 endproc fooFunction 4 4 import strlen bss export variableB align 4 LABELV variableB skip 4 import variableA Несколько замечаний:

    • Байт-код разделён на части (text, data и bss): мы чётко видим bss (неинициализированные переменные), data (инициализированные переменные) и code (обычно называемую text)
    • Функции определяются с помощью сендвича из proc, endproc.
    • Промежуточное представление LCC — это стековая машина: все операции выполняются в стеке и никаких допущений о регистрах ЦП не делается.
    • В конце фразы LCC у нас есть группа файлов, импортирующих/экспортирующих переменные/функции.
    • Каждое объявление начинается с типа операции (например, ARGP4, ADDRGP4, CALLI4…). Каждый параметр и результат передаётся в стек.
    • Импорт и экспорт находятся здесь, поэтому ассемблер может «связать» модули трансляции вместе. Заметьте, что используется import strlen, потому что ни q3asm.exe, ни интерпретатор ВМ не обращаются к стандартной библиотеке C, strlen считается системным вызовом и выполняется виртуальной машиной.

    Такой текстовый файл генерируется для каждого файла .c в модуле ВМ.

    Внутренности q3asm.exe

    q3asm.exe получает текстовые файлы промежуточного представления LCC и собирает их вместе в файл .qvm:
    Здесь можно заметить следующее:

    • q3asm разбирается в каждом из символов импорта/экспорта в текстовых файлах.
    • Некоторые методы заданы предварительно в текстовом файле системных вызовов. Можно увидеть syscall для клиентской ВМ и для серверных ВМ. У символов системных вызовов есть атрибуты в виде отрицательных целочисленных значений, чтобы их мог распознать интерпретатор.
    • q3asm меняет представление с текстового на двоичное с целью получения пространства и скорости, но ничего более, никаких оптимизаций здесь не выполняется.
    • Первым собираемым методом должен быть
      vmMain, потому что это диспетчер вводимых сообщений. Кроме того, он
      должен
      находиться в 0x2D текстового сегмента байт-кода.

    QVM: как она работает

    Снова рисунок, демонстрирующий уникальную точку входа и уникальную точку выхода, выполняющие диспетчеризацию:
    Некоторые подробности:

    Сообщения (Quake3 -> ВМ) отправляются виртуальной машине следующим образом:

    • Любая часть Quake3 может вызвать VM_Call( vm_t *vm, int callnum, … ).
    • VMCall может получать до 11 параметров и записывает каждое 4-битное значение в байт-код ВМ (vm_t *vm) с 0x00 по 0x26.
    • VMCall записывает идентификатор сообщения в 0x2A.
    • Интерпретатор начинает интерпретировать опкоды в 0x2D (куда q3asm.exe записал vmMain).
    • vmMain используется для диспетчеризации и маршрутизации сообщения к соответствующему методу байт-кода.

    Список сообщений, отправляемых клиентской ВМ и серверной ВМ, представлены в конце каждого файла.
    Системные вызовы (ВМ -> Quake3) выполняются так:

    • Интерпретатор один за другим выполняет опкоды ВМ (VM_CallInterpreted).
    • Когда он встречает опкод CALLI4, то проверяет индекс метода в int.
    • Если значение отрицательное, то вызов системный.
    • Вызывается с параметрами указатель функции системного вызова (int (*systemCall)( int *parms )).
    • Функция, на которую указывает systemCall, используется для диспетчеризации и маршрутизации системного вызова к нужной части quake3.exe

    Список системных вызовов, предоставляемых клиентской ВМ и серверной ВМ, находится в начале каждого файла.
    Интересный факт:

    параметры всегда имеют очень простые типы, они или примитивные (char,int,float), или являются указателями на примитивные типы (char*,int[]). Подозреваю, что так сделано для минимизации проблем связи struct между Visual Studio и LCC.

    Интересный факт:

    ВМ Quake3 не выполняет динамическое подключение, поэтому разработчик мода QVM не имел доступа ни к каким библиотекам, даже к стандартной библиотеке C (strlen, memset здесь есть, но на самом деле являются системными вызовами). Некоторым удавалось эмулировать их с помощью предварительно заданного буфера: Malloc in QVM.

    Беспрецедентная свобода

    Благодаря переносу функций в виртуальную машину сообщество моддеров получило гораздо более широкие возможности. В Unlagged Нила Торонто система предсказаний была переписана с использованием «обратного согласнования».

    Проблема производительности и её решение

    Из-за такого длинного тулчейна разработка кода ВМ была сложной:

    • Тулчейн был медленным.
    • Тулчейн не был интегрирован в Visual Studio.
    • Для сборки QVM требовалось использование инструментов командной строки. Это мешало процессу разработки.
    • Из-за большого количества элементов тулчейна сложно было найти части, ответственные за ошибки.

    Поэтому idTech3 также имелась возможность загрузки нативных DLL для частей ВМ, и это решило все проблемы:
    В целом система ВМ была очень гибкой, потому что виртуальная машина имеет возможность выполнения:

    • Интерпретируемого байт-кода
    • Байт-кода, скомпилированного в команды x86
    • Кода, скомпилированного в Windows DLL

    Рекомендуемое чтение

    Quake3Arena ProMode

    Quake3Arena ProMode

    Вступление

    Challenge Pro Mode (CPM) – это модификация (мод) Q3A, которая предназначена для того, чтобы сделать игру более динамичной и интересной. Это не есть римейк QW или Q2, хотя некоторые из черт этих двух игр от id Software были внесены и в CPM.

    Изменения в физике

    Физика переделана так, чтобы придать движениям игрока большую свободу. Внесенные изменения позволяют делать более сложные трики, общий геймплей становится разнообразнее, интенсивнее и намного привлекательнее. Air Control (управление в воздухе) В отличие от стандартного Quake3Arena, где при зажатых стрейфах игрок не мог управлять движением в воздухе (например, повернуть в другую сторону), в CPM он получает полный контроль над движением, независимо от того – нажат у него стрейф или нет. Для справки: стрейф нажимают тогда, когда хотят двигаться быстрее. Отсюда происходит название «стрейф-джамп», из которого в дальнейшем вырос «распрыг» и т.д. Ground Acceleration Ускорение при ходьбе или беге. GA был довольно сильно увеличен для сокращения времени разгона, а именно с 10 до 15 пунктов. А чтобы игрок мог быстрее остановиться, с 6 до 8 увеличен коэффициент трения с полом. Все это сделано для увеличения общей скорости игры (при том, что максимальная скорость игрока в игре не изменилась). Двойные прыжки («дабл-джампы») Двойные прыжки – это трюк, перешедший из Quake 2. Производится он следующим образом. Подходишь поближе к какому-нибудь возвышению (поребрик, ящик и т.д.) и прыгаешь дважды: сперва с пола на самый край возвышения, и второй прыжок – уже с возвышения. Делаться это должно в одно, очень быстрое движение. Этот трик также перенесен в CPM. Правда, на большинстве карт в Q3 он практически бесполезен – за неимением ящиков. Разве что можно использовать при прыжках по лестницам и внезапных атаках из телепортеров (прыгаешь в телепортер и сразу вторично раз нажимаешь на прыжок, еще не успев еще выйти из телепорта, если сделаешь правильно – осуществишь очень высокий прыжок на выходе).

    Изменения в переключении оружия

    Оружие переключается мгновенно. Задержка между переключением в 450 мс (0.45 секунды), которая была в стандартном Q3 – убрана. Именно задержка мешала делать отличные комбинации, такие как меткий выстрел противнику под ноги из рокета, а потом, когда врага подкинет к потолку – пронзить его из рэйла или насадить на шафт (по желанию автора выстрела). Чтобы создавать скрипты для переключения оружия, которые реагирует на нажатие и отпускание кнопки (ага!), теперь можно использовать команду «+vstr [down_command] [up_command]». Вот пример: set shaft «weapon 6; wait 2; +attack» set unfire «-attack» bind mouse2 +vstr shaft unfire Теперь при нажатии второй кнопки мыши выполнится команда shaft и начнется стрельба из шафта, а при отпускании – стрельба прекратится.

    Установки оружия

    Gauntlet Сила отбрасывания кулака уменьшена в два раза. Machinegun Одна пуля отнимает 5 единиц здоровья во всех видах игры, а не только в тимплее. На старте дается 50 патронов. В каждой коробке теперь не 50, а 25 патронов. Shotgun В одном патроне для двустволки теперь 16 дробин вместо 11 прежних. Наносимые повреждения от одной дробины уменьшены с 10 до 7. Разброс дроби увеличен с 700 до 900. Cила отдачи при попадании увеличена на 35%. Максимальное число зарядов уменьшено с 200 до 100 патронов. Grenade Launcher Время перезарядки гранатомета уменьшено с 800 мс до 600 мс (0.6 сек), максимальное число зарядов уменьшено до 100 гранат. Rocket Launcher Скорость полета ракеты увеличена с 900 до 1000. Максимальное число носимых ракет уменьшено до 100. Отдача при попадании увеличена на 20% (не действует на рокетджампы). Lightning Gun Отдача при попадании увеличена на 55%. Railgun Максимальное число зарядов уменьшено до 100. В одной коробке лежит только 5 зарядов. Plasmagun Отдача при попадании увеличена на 50%. Наносимые повреждения с одного попадания уменьшены до 15 единиц (было 20).

    Система брони

    Броня не «утекает». Красная броня: максимум 200 единиц, при подборе дает 200 единиц, берет на себя 75% повреждений. Желтая броня: максимум 150 единиц, при подборе дает 100 единиц, берет на себя 60% повреждений. Шардзы дают по 2 единицы текущего вида брони – без ограничения по максимуму.

    Время респауна

    Здоровье и боеприпасы респаунятся каждые 30 секунд (в стандартном Q3A – 35 секунд здоровье и 40 секунд боеприпасы). Все артефакты появляются каждые 60 секунд, кроме активной брони, которая появляется раз в 120 секунд. Артефакты появляются сразу при старте матча. Оружие появляется каждые 15 секунд в дуэлях и FFA и каждые 30 секунд в тимплее. Мегахелс появляется каждые 20 секунд после окончания действия подобранного. Время задержки перед респауном игрока убрано (в стандартном Q3A – было равно 1700 мс).

    Изменения в звуках

    Звуки шагов Звуки шагов отключены. Теперь можно бегать не опасаясь того, что тебя услышат. В игре стало значительно больше адреналина и скорости. Звуки попаданий Каждый раз, когда попадаешь в противника, раздается негромкий звук различной тональности. В Q3A звук был один. В CPM тональностей четыре. Какой вы прозвучит – зависит от уровня нанесенных противнику повреждений: 1-25, 26-50, 51-75, 76+. Чем ниже тон звука, тем сильнее покоцан враг.

    Другие изменения

    Рюкзаки После смерти из игрока в CPM вываливается не последнее оружие, которое было у него в руках, а целый рюкзак с последним оружием и со всеми накопленными боеприпасами. Артефакты Квад увеличивает мощность оружия в 4 раза, а не в 3 как в стандартном Q3A. Активная броня берет на себя 75% повреждений, вместо 50%. Предметы Высота любого предмета увеличена на 30 пунктов, чтобы их нельзя было случайно перепрыгнуть.

    Команды управления сервером

    — powerups – включить/выключить артефакты — holdables – включить/выключить переносные артефакты — cmd timelimit – установить таймлимит — cmd fraglimit – установить фраглимит — cmd capturelimit – установить флаглимит — cmd overtime – установить овертайм — cmd weaponrespawn – установить время респауна оружия — weaponstyle – установить тип добавления боеприпасов при взятии оружия — g_doWarmup» 1..3 – установить режим вормапа — nextWarmup – перейти на следующий режим вормапа — ready – «готов к игре» — notready – «не готов к игре» — pause — пауза — forcestart – начать обратный отсчет без опроса готовности — break – остановить отсчет — callvote – использовать голосование — g_overtime – время овертайма в минутах, если значение «-1», то это означает игру до первого фрага/флага — /help – список всех команд управления сервером

    Параметры использования команды “Say”

    Для общения игроков в игре используются команды «say» и «say_team». Т.е. пишите в консоли «say MOE IMYA – BOBA!» и все на сервере видят как тебя зовут. Отличие первой команды от второй состоит в том, что первая выводит надписи всем игрокам на сервере, а вторая – только тем, кто из твоей команды. В CPM возможности этих команд расширены. Теперь внутри своих сообщений можно втыкать информацию о своем местоположении, здоровье и т.д. Для этого используются специальные коды. Каждый код должен начинаться с символа #. Т.е. к примеру код «#h» будет заменен на показатель твоего здоровья, например на «100». Если используешь неправильный код, то он не на что меняться не будет. Например, введенное «say_team #1» выведет «#1». В одном сообщении можно использовать сколько угодно кодов. А вот, собственно, и их список: — #h – текущее здоровье — #H – текущее здоровье с цветовой подсветкой цифр (меньше 50 – красные, от 50 до 100 – желтые, выше 100 – белые) — #a – текущая броня — #A – текущая броня с цветовой подсветкой (аналогично здоровью) — #p и #P – имеющиеся у вас артифакты (включая вражеский флаг в CTF). — #w – лучшее оружие, которое у вас есть (при наличии боеприпасов к нему) — #W – лучшее оружие, которое у вас есть с цветовой подсветкой, т.е. если у вас только пулемет или кулак, то их названия будут красного цвета, в противном случае белого — #l и #L – выведет надпись «Health» и/или «Armor», если их у вас меньше 50 — #m and #M – список всего оружия и боеприасов к нему (игнорирует пулемет и кулак) Например, могут быть полезными следующие сообщения: «say_team I’m low on #l!!» «say_team I need #m!!» «say_team I’ve got the #p» Новые консольные команды — report – выводит сообщение «say_team» с информацией о текущем здоровье, броне, лучшем оружии и артефактах. Фактически это алиас на «say_team [H:#H] [A:#A] [#W] #p».

    Новые HUD и SCOREBOARD

    HUD’ом называется интерфейс игрока. То есть вся информация на экране о здоровье, броне, оружии и т.д. и то как она представлена. В CPM к этому делу подошли очень серьезно и сделали HUD’ов на все вкусы. Вот общий список достоинства интерфейса в CPM: — различные виды индикации здоровья и брони — на экране постоянно выводится список видов имеющегося оружия и боеприпасов — возможность изменения цвета прицела — вывод информации о напарнике по команде при наведении на него прицела — в списке оружия текущее оружие выделяется размером — при взятии нового оружия в списке оно мигает, чтобы игрок не пропустил этот момент — иконка текущих боеприпасов не мигает при выстреле — на «scoreboard» (таблица фрагов) теперь кроме обычной информации выводится и число FPS игрока (обновляется каждые 20 секунд) — «scoreboard» также выводит информацию о готовности игрока и его цвета

    Команды управления HUD’ом

    — hudstyle # — выбор текущего вида интерфейса от 1 до 8. Можно с помощью настроек создавать свой собственный вариант — hudnext – выбор следующего вида интерфейса — hudprev – выбор предыдущего интерфейса — cg_hud_crosshairColor – выбор цвета прицела. Варианты: 0 – белый, 1 – красный, 2 – зеленый, 3 – желтый, 4 – синий, 5 – голубой, 6 – розовый. Внимание! Чтобы эта команда работала нужно отключить переменную cg_crosshairHealth. Т.е. ввести «cg_crosshairHealth 0» — cg_hud_crosshairTeamInfo – включить/выключить показ информации об игроках своей команды при наведении на них прицела. Работает только когда включена переменная cg_drawTeamOverlay — cg_hud_graphs – включает графики в некоторых видах интерфейса — cg_hud_statusbarStyle – выбор вида статусбара. 0 – стандартный вид как в Q3A. 1 – длинные горизонтальные полоски, 2 – короткие горизонтальные полоски, 3 – вертикальные полоски, 4 – информация о здоровье размещается вместе с информацией об оружии, 5 – три горизонтальные полоски. Имей в виду, что некоторые комбинации видов списков оружия и видов статус бара не будут работать — cg_hud_teamBackground – включает/выключает информацию о команде в статусбаре — cg_hud_weaponList – выбор режима отображения списка видов оружия. 0 – отключить, 1 – слева, 2 – справа, 3 – посередине — cg_hud_weaponListDrawAll – включает/выключает отображение всех видов оружия в списке (даже тех, которых нет сейчас у игрока) — cg_hud_weaponListBackground – включает/выключает выделение иконки текущего вида оружия синим фоном — cg_hud_weaponListFlash – включает/выключает мигание при взятии оружия новой иконки в списке видов оружия — cg_hud_drawPickup – включить/выключить отображение сообщений о взятии новых предметов — cg_hud_fragMessage – информация о фрагах

    Цвета игрока

    Используя специальные скины (модели) CPM, можно выбирать свои собственные цвета. Так же можно указать цвета противников — чтобы лучше их видеть. Вот список поддерживаемых моделей и скинов с новыми возможностями. Используйте команду «model», для изменения текущей модели/скина: — crash/default_pm — visor/default_pm — sarge/default_pm — mynx/default_pm — ranger/default_pm Модель игрока в CPM поделена на три части, цвет которых можно менять. Это голова, торс и ноги. Для изменения цветов модели используется команда «color». Цвет выстрела из рэйлгана совпадает с цветом вашей головы. В тимплее каждый игрок команды должны использовать одинаковые цвета (например, 2 4) для торса и ног, но могут иметь разные цвета головы модели. Вот образцы применения команд: — color [цвет головы],[цвет торса],[цвет ног] — color [цвет головы,[цвет тела] — color [один цвет на все] Список цветов: 0 = белый 1 = красный 2 = зеленый 3 = желтый 4 = сине-зеленый 5 = розовый 6 = какойтонепонятный 7 = оранжевый 8 = пурпурный 9 = голубой — cg_forceColors – эта команда используется для того, чтобы противник (в тимплее – вся команда соперников) имел те же цвета, что и у твоей модели.

    Источник: Obey the Game Law ( https://www.ogl.spb.ru ) Автор: littleB

    ©

    Искусственный интеллект

    Сообщество моддеров написало системы ботов для всех предыдущих движков idTech. В своё время довольно известными были две системы:

    • Для Quake1 был Omicron.
    • Для Quake2 написали Gladiator.

    Но для idTech3 система ботов была фундаментальной частью геймплея, поэтому её необходимо было разработать внутри компании и она должна была присутствовать в игре изначально. Но при разработке возникли серьёзные проблемы:
    Источник: страница 275 книги «Masters of Doom»:

    К тому же не был готов фундаментальный ингредиент игры — боты. Боты — это персонажи, управляемые компьютером. Правильный бот должен хорошо вписываться в игру, дополнять уровни и взаимодействовать с игроком. Для Quake III, которая была исключительно многопользовательской игрой, боты оказались неотъемлемой частью игры в одиночку. Они должны были стать безусловно сложными и действовать подобно человеку.
    Кармак впервые решил передать задачу создания ботов другому программисту компании, но потерпел неудачу. Он снова посчитал, что все, как и он, целеустремлённы и преданы работе. Но Кармак ошибался.

    Когда Грэм попытался остановить работу, обнаружилось, что боты совершенно неэффективны. Они не вели себя, как люди, и были просто ботами. Команда начала паниковать. Это был март 1999 года, и причины для страха конечно же были.

    Архитектура

    В результате над ботами работал Жан-Поль ван Ваверен (Mr.Elusive), и это забавно, ведь он написал «Omicron» и «Gladiator». Это объясняет, почему часть серверного кода ботов выделена в отдельный проект bot.lib:
    Я мог бы написать об этом, но Жан-Поль ван Ваверен (Jean-Paul van Waveren) сам написал 103-страничный труд с подробным объяснением. Более того, Алекс Шампана (Alex J. Champandard) создал обзор кода системы ботов, в котором описывается местоположение каждого модуля, упомянутого в труде ван Ваверена. Этих двух документов достаточно для понимания ИИ Quake3.

    Рейтинг
    ( 2 оценки, среднее 4.5 из 5 )
    Понравилась статья? Поделиться с друзьями: