Собственно под влиянием поста от @LaCTuK решил написать этот мануал.
Мануал разделю на два логических раздела теория и практика. Теоретическая часть также будет поделена на два раздела Основы и Описание команд. в обще примеры я буду писать по ходу, но главный пример использования команд будет в практической части. Для начала не много вводной информации, а также моих мыслей по этому поводу.
Скрипт – это автоматизация и упрощения действий. Давайте сразу договоримся и не будем разводить споров, хотя мне это будет и выгодно 🙂 скрипты это не читерство. Если вас удивляет, что в обще об этом начал разговор, то вот проф линк в топике группы в контакте не считайте за рекламу ж).
Моё мнение мысли, если разработчики дали возможность автоматизации и не ограничивают сие действие при игре по сети значит это не плохо и не читерство(слово не подходит) это скорее преимущество, но если ваш противник не использует это не значит, что и вы не должны это использовать.
Обозначения
Так как в тексте будет море команд и кодов я решил ввести не большие обозначения. каждая новая сточка в кодах будет начинаться с знака | также будут вести нумерацию для облегчения понимания. На пример код из первого примера в разделе практики:
1| alias recdemo «recon»
2| alias recon «record tf2demo; developer 1; echo Recording Demo!; developer 0; alias recdemo recoff»
3| alias recoff «stop; developer 1; echo Demo Complete!; developer 0; alias recdemo recon»
то есть писать их в скрипте не нужно просто для понимания текста тут решил писать так.
===Теоретическая часть===
==Основы==
Для Клиента корневым каталогом для скриптов будет:
Steam\steamapps\*логин в стиме*\team fortress 2\tf\cfg
В папке по дефолту будут следующие файлы:
*heavyweapons.cfg- конфиг для пулеметчика
*medic.cfg- конфиг для медика
*pyro.cfg- конфиг для поджигателя
*scout.cfg- конфиг для скаута
*sniper.cfg- конфиг для снайпера
*soldier.cfg- конфиг для солдата
*spy.cfg- конфиг для шпиона
Также игра загружает файлы vavle.rc его мы тут не видим, но спокойно можем создать через выше указанные приложения и написать в нём exec autoexec.cfg, что будет вызывать файл (который к слову тоже стоит создать) autoexec.cfg также при запуске игры.
Разберём файл vavle.rc
в нём мы написали exec autoexec.cfg
Важно каждая новая команда новая сточка!
Это означает выполнить прочесть кому как удобнее файл autoexec.cfg мы могли бы написать exec XXXX.cfg и тогда бы запустился файл XXXX.cfg. Почему autoexec.cfg это дефолт название файла для загрузки скриптов пользователей и лучше использовать его. А уже в нём(в файле autoexec.cfg) прописываем нужные нам при запуске скрипты. То есть получается такая система игра запускает vavle.rc который запускает autoexec.cfg который в свою очередь уже запускает все остальные ваши скрепты для всей игры в целом.
На пример у вас есть скрипт только для солдата.
Тогда для его запуска нужно в файле soldier.cfg прописать exec имя файла скрипта.cfg.
Хочу заметить Source не понимает русских символов в названии файлов.
Разберём не много файл config.cfg
также во многих скриптах встречаются комментарии которые можно оставлять в файлах скриптах. Комментарии располагаются через // и после них в строчке движком игры игнорируются слова не обозначающие команды игнорируються. Очень полезно подписывать, что делает та или иная команда или делать файл скрипта на блоки. комментарий оставляется также на новой строчки. Я буду писать комментарии для команды перед её исполнением.
Русский в комментариях разрешён.
==Описание команд==
ALIAS
Данная команда носит общий характер и применима повсеместно. Алиас буквально создает новую команду, состоящую из нескольких других, облегчает работу при создании сложных скриптов.
BUILD
Команда применима при написании скриптов для Инженера и Шпиона: если Инженер имеет достаточное количество металла, он начинает создавать выбранную конструкцию; Шпион в свою очередь берет в руки подрывное устройство и готов устанавливать жучков на сооружения противника.
В настоящий момент нельзя вызвать команду build повторно, необходимо переключиться в другой режим (взяв другое оружие) и потом использовать команду повторно.
DESTROY
Если ты построил в роли Инженера какое-либо устройство, с помощью этой команды ты можешь стать обладателем кучи металлолома.
DISGUISE
LASTDISGUISE
С помощью этой команды Шпион маскируется под предыдущего текущей маскировке персонажа, даже после смерти запоминается последняя маскировка. Команда является аналогом нажатия клавиши b. В режиме random происходит случайная выборка из персонажей для маскировки (исключая Шпиона и Разведчика).
LASTINV
MENUSELECT
Команда позволяет выбрать определенный пункт меню. Необходимо отметить, что под словом меню в данном случае подразумевается только меню типа списка голосовых команд, но никак не меню выбора персонажа и т.п.
PLAY
Проигрывает играющему мелодию (звук), сохраненный в формате wav. Вам потребуется GCF редактор для извлечения звуков из файлов формата gcf. Также ты можешь заставить проигрываться свои собственные файлы формата wav, тебе необходимо добавить эти файлы в папку:
Steam\steamapps\*логин в стиме*\team fortress 2\tf\sound\
Данная папка выступает корневый каталогом при указании относительного пути до файлов формата wav.
SLOT
VOICEMENU
Данная команда позволяет подавать голосовые сообщения напрямую (аналогично работе с кнопками z,x,c). По сути она позволяет Вам привязать голосовую команду к любой кнопке, в отличие от стандартной привязки (несколько нажатий на клавиши).
WAIT
Команда очень полезна при исполнении нескольких команд в одну строку. По сути, она приостанавливает выполнение текущих команд на X кадров, указанные в качестве необязательного параметра (по умолчанию значение равно 1). Значение подбираются методом проб и ошибок, 100 примерно равно 1 секунде. В качестве примера команда для модернизации постройки Инженера:
Необходимо учесть, что данная команда никак не исключает повторный нажатие на клавиши, выступает только как таймер при выполнении скриптов.
wait [_задержка_], где задержка любое значение от 1 и больше.
Это естественно не все команды, но они будут востребованы. Ниже расположены обозначение клавиш клавиатуры и мыши в скриптах.
Все управляющие клавиши клавиатуры, кнопки мыши и прокрутка средней кнопки мыши имеют определенные обозначения в скриптах. Вот полный список:
Пишем бота для онлайн-игры на JavaScript с применением AOP
1. Готовим ингредиенты
Важно! Игра должна работать в браузере, а не в клиенте. Причем не на Flash, а на HTML+JavaScript. На выходе у нас должно получиться расширение для Chrome, которое будет играть вместо нас.
2. Делаем расширение
О том как делается расширение я не буду подробно расписывать. На хабре об этом уже писали, например, тут. Приведу лишь коды, нужных нам файлов. В manifest.json
В строчке «matches»: [ «pernatsk.ru*» ] вам нужно будет указать адрес вашей игры. Файл background.js я использую для случаев, когда хочу инджектить на сайте свой JS кода. Собственно код background.js:
Важно! Если вы не понимаете, что мы делаем в этой единственной функции, то делать бота вам пока рано. Почитайте основы JavaScript. Вся работа у нас будет вестись в файле injected.js Его код пока такой:
Все эти файлы сохраняем в одной папке bot.
3. Первый пуск бота
4. Добавляем AOP
Для работы бота нам потребуются библиотеки. Мой любимый jQuery уже используется на Пернатске, поэтому добавлять его не будет. Добавим плагин AOP for Jquery. По хорошему это стоило запаковать в само расширение в виде отдельного файла, но я ленив. Поэтому просто добавим код bin/aop.pack.js первой строкой в наш injected.js. Проверим, что это работает изменив ai_on
Проверяем, что AOP нормально подключилось. В консоле разработчика теперь будет строчка «jQuery detected!» Сообщение будет только один раз, так как я отключаю совет после первого же срабатывания. Важно! Прочитайте документацию AOP for Jquery, чтобы понять jQuery.aop.after и bot[0].unweave().
5. Зачем мы будем использовать AOP
6. Учим бота первой команде
В injected.js добавим такой код:
По этой команде наша бот-птичка будет лететь в Пернатске за шишками. Код слегка мудренный, так как в Пернатске есть небольшая защита от ботов. Когда вы будете писать свои команды я рекомендую сначала опробовать их работоспособность в console, а уже потом переносить код в редактор. Чтобы протестировать и проверить работу нашей команды запустим в косноле код commands.conessearch() Все работает.
7. Ищем событие на которое должен реагировать бот
Тут есть два метода первый — анализируем код игры. Долго 🙁 Второй метод — воспользоваться AOP, и после всех функций, который срабатывали вывести в лог их имя. Потом выбрать нужные. Меняем ai_on()
Теперь у нас отражаются только те функции, которые еще не отображались. Их полный список мы храним в fnList. После пары минут там будут такие варианты функции для прицепки [«clearInterval», «$», «setTimeout», «timerTick», «serverTimeUpdate», «getComputedStyle», «setInterval», «tutorialArr», «showQ», «showQc», «updateBirdData», «viz», «unviz», «weatherUpdate»] Меняя target и регулярное выражение в method мы можем подобрать ту функцию, которая нам подойдет, чтобы к ней прицепиться. Для примера, я выбираю функцию weatherUpdate теперь каждый раз как будет меняться погода наша птичка будет лететь за шишками.
7. Учим бота реагировать на события
Мы снова меняем код функции ai_on()
Функцияю ai_off нужна, чтобы через консоль выключить бота.
Современные браузеры позволяют создавать игры с полноценной графикой. Рассказываем, как написать простые гонки на JavaScript и HTML5.
Сейчас браузеры дают JavaScript-разработчикам огромное количество возможностей для создания интересных сайтов. Раньше для этого использовался Flash — он был популярен, и на нём было создано бессчётное количество игр, плееров, необычных интерфейсов и так далее. Однако они уже не запустятся ни в одном современном браузере.
Дело в том, что технология Flash тяжеловесна, а также полна уязвимостей, поэтому от неё стали отказываться. Тем более что появилась альтернатива в виде HTML5 — в этой версии появился элемент canvas.
Canvas — это холст, на котором можно рисовать с помощью JS-команд. Его можно использовать для создания анимированных фонов, различных конструкторов и, самое главное, игр.
Из этой статьи вы узнаете, как создать браузерную игру на JavaScript и HTML5. Но прежде рекомендуем ознакомиться с объектно-ориентированным программированием в JS (достаточно понимать, что такое класс, метод и объект). Оно лучше всего подходит для создания игр, потому что позволяет работать с сущностями, а не с абстрактными данными. Однако есть и недостаток: ООП не поддерживается ни в одной из версий Internet Explorer.
Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.
Вёрстка страницы с игрой
Для начала нужно создать страницу, на которой будет отображаться холст. Для этого потребуется совсем немного HTML:
Теперь нужно добавить стили:
Обратите внимание, что в HTML элементу canvas были заданы нулевые ширина и высота, в то время как в CSS указано 100%. В этом плане холст ведёт себя как изображение. У него есть фактическое и видимое разрешение.
С помощью стилей меняется видимое разрешение. Однако при этом размеры картинки останутся прежними: она просто растянется или сожмётся. Поэтому фактические ширина и высота будут указаны позже — через скрипт.
Скрипт для игры
Для начала добавим заготовку скрипта для игры:
В этом скрипте есть всё, что необходимо для создания игры: данные (массивы), функции обновления, прорисовки и управления. Остаётся только дополнить это основной логикой. То есть указать, как именно объекты будут себя вести и как будут выводиться на холст.
Логика игры
Во время вызова функции Update () будут меняться состояния игровых объектов. После этого они отрисовываются на canvas с помощью функции Draw (). То есть на самом деле мы не двигаем объекты на холсте — мы рисуем их один раз, потом меняем координаты, стираем старое изображение и выводим объекты с новыми координатами. Всё это происходит так быстро, что создаётся иллюзия движения.
Рассмотрим это на примере дороги.
На холсте выводится вот такое изображение и постепенно двигается вниз. Сразу же следом будет выводиться ещё одна такая же картинка, благодаря чему создастся ощущение бесконечной дороги.
Для этого создадим класс Road:
В массив с фонами добавляются два объекта класса Road:
Теперь можно изменить функцию Update (), чтобы положение изображений менялось с каждым кадром.
Остаётся только добавить вывод этих изображений:
Теперь можно посмотреть, как это работает в игре:
Пора добавить игрока и NPC. Для этого нужно написать класс Car. В нём будет метод Move (), с помощью которого игрок управляет своим автомобилем. Движение NPC будет осуществляться с помощью Update (), в котором просто меняется координата Y.
Для многих начинающих игростроевцев, которые уже собирают свою команду, чтобы слепить на коленке очередной шедевр, программирование часто видится жутким монстром, с которым непонятно как бороться. Вроде и в 3D уже рисовать умеют, и в Photoshop кисточкой сноровисто работают, и другие полезные программы неплохо знают. Но как только дело доходит до кода, начинается паника и неразбериха: “ Кто спрограммирует? Кто напишет заветные строчки? А если и напишет, то как в них потом разобраться?! ”.
Данный материал открывает серию статей, в которых мы на простых примерах научим вас читать с листа и писать несложный программный код. Суперкрутыми программистами вы, разумеется, не станете (для этого надо учиться значительно дольше), но иероглифы кода перестанут быть для вас чем-то непонятным, от чего нужно держаться подальше.
В ООП существуют три основных принципа, на которых строятся практически все логические манипуляции с объектами.
Инкапсуляция — объединение внутри объекта данных с методами для их обработки. Например, если рассматривается класс “Лампочка”, то у него должны быть методы “включить свет” и “выключить свет”.
Наследование — создание класса объектов на основе уже существующего класса. При этом создаваемый класс будет не только содержать все данные и методы базового класса, но и обладать своими собственными. Методы базового класса могут быть переопределены. Примеры наследования и переопределения: если класс “Пегас” наследуется от класса “Лошадь”, то у первого появляется новое свойство — “крылья” и соответствующий метод “махать крыльями”. Все остальное у этих двух классов одинаковое. Если мы рассмотрим класс “Русалка”, основанный на классе “Человек”, то в данном случае будет иметь место переопределение свойства “ноги” на “рыбий хвост”, а метод “двигаться” будет вместо движения ног отвечать за перемещение хвоста.
Полиморфизм — что это такое, проще всего рассмотреть на конкретном примере. Рассмотрим два класса объектов — “Пегас” и “Лошадь”. Полиморфизм заключается в том, что мы можем рассматривать любого пегаса как лошадь и, например, у обоих объектов выполнять метод “кушать траву” или “бежать галопом”.
Многие начинающие программисты не различают понятия “класс” и “объект”. Но при этом эти два понятия различаются примерно так же, как и, скажем, чертеж устройства и готовое устройство в магазине. В жизни их спутать трудно.
Создание проекта
К сожалению, в UDE кнопка “Создать новый проект” отсутствует. Поэтому создаем в корневой папке UT2004 файл CreateNewClass.bat с командами:
echo class %pkg% extends //base class>> %pkg%\Classes\%pkg%.uc
Значение переменной pkg — это имя нашего проекта (в данном случае — Megakiller ). Остальные строки генерируют “пустой” скрипт.
Идея модификации
Перед изучением нижеследующей информации настоятельно рекомендуется изучить текстовый блок “ Анатомия Unreal-класса ”.
Внимание! Читая нижеследующие абзацы, смотрите соответствующие строки в листинге кода, приведенном на отдельной странице (номера строк указаны слева от кода).
Класс оружия
Рис. 1. Диалог создания дерева классов и архивов.
Класс альтернативной стрельбы
Рис. 2. Окна с деревом классов и списком архивов.
В начале файла объявляем несколько новых свойств для объектов класса ( строки 2-5 ): визуальный тип взрыва ( ExplosionClass ), визуальный тип следа от взрыва, например на стенах ( ExplosionDecalClass ). Тип луча при альтернативной стрельбе ( BeamEffectClass ) и радиус поражения при взрыве ( DamageRadius ).
Рис. 3. Диалог создания подкласса.
Классы повреждения
Рис. 4. Окно компиляции.
Класс игрового мутатора
— просто лежать на карте ( строка 15 ). Тут нам помогает класс WeaponPickup ( строка 15 ). Подмена осуществляется функцией ReplaceWith ( строка 19 ).
Рис. 5. Сообщение об ошибке и справка.
Дуэли на «Мегакиллерах» иногда превращают обычный бой в феерическое зрелище.
Чернокожий стрелок пытается безуспешно подстрелить «античную» мадам.
Местные леди способны просто-таки заряжать вас энергией!
Каждый Чебурашка мечтает о большой пушке!
Компиляция и тестирование
Для того чтобы вы сразу могли оценить действие мутатора и поиграть в него, мы выкладываем полную версию “Мегакиллера” на наш диск.
Анатомия Unreal-класса
Класс игры имеет унифицированную архитектуру. Структура файла следующая.
— Заголовок, содержащий объявление класса:
class ClassName extends BaseClass config(user); где ClassName — название нашего класса, BaseClass — базовый класс, на основе которого создается новый класс, config(user) — пример параметра в объявлении класса — указывает, что используются конфигурационные параметры из файла User.ini. Все игровые классы имеют в качестве родительского класс Object или любой из его классов-потомков. Лексемы class и extends являются зарезервированными обязательными атрибутами объявления игрового класса. Важно запомнить, что корректным является только тот uc-файл, имя которого совпадает с названием объявленного в нем класса. И в одном uc-файле должен быть описан только один класс.
— Объявления новых переменных (свойств класса) в следующем формате:
PropertyName; или var() SimpleType PropertyName;где PropertyClass — название класса создаваемого свойства, SimpleType — элементарный тип, такой как byte (байтовое значение от 0 до 255), int (знаковый целый тип), bool (логический тип), float (знаковый вещественный тип), string (символьная строка) и т.д. (полный список элементарных типов можно посмотреть в справке UDE), PropertyName — имя нового свойства.
— Объявления новых методов или переопределение унаследованных от базового класса имеют следующий вид:
— Задание параметров (свойств) по умолчанию класса:
defaultproperties где defaultproperties — лексема, указывающая начало блока свойств, Variable1Name — название свойства, представляющего собой некоторый другой объект класса ClassName, содержащегося в u-файле с именем Package. Параметр Variable2Name имеет элементарный тип, поэтому и присваиваемое значение Variable2Value должно иметь тот же тип. Символ «=» означает операцию присвоения значения справа переменной слева. Фигурные скобки обособляют в коде блок присвоений.
Вот и подошел к концу наш первый экскурс в мир игрового программирования. Мы не только сделали интересный мутатор, но и научились читать несложный программный код. В одном из ближайших номеров “Мании” мы продолжим разбираться в азах программирования и, изучив более продвинутые строки кода, создадим более сложный мутатор.
Разработка нагрузочных скриптов для браузерных/мобильных игр. Часть 1
Привет, Хабр. В прошлой статье я рассказал об автоматизации процесса нагрузочного тестирования в игровой компании, в которой я работаю. Теперь пришло время остановится на некоторых конкретных задачах, с которыми пришлось столкнуться в ходе подготовки к процессу тестирования самих игр.
Есть большая разница между тестированием разных банковских/retail-процессов и игр. В первом случае пользователи выполняют их задачи почти изолированно друг от друга и используют только те данные и элементы, которые видят в окне своих браузеров или других клиентов в данный момент, что облегчает разработку нагрузочных скриптов. В играх же пользователи (игроки) находятся в динамично меняющемся мире и часто подвержены влиянию друг друга. В моем воображении разница выглядит примерно так:
То есть в первом случае пользователи через череду однотипных действий приходят к конечному результату и уходят на следующий круг. Игра же — это рандомный хаос в центре которого находится игровой мир, на который игроки постоянно оказывают воздействие, меняют внутриигровые данные, оказывая непосредственное влияние как на себя, так и на других игроков. Также игроки могут общаться, объединяться в гильдии и рубиться PvP.
Таким образом, при разработке нагрузочных скриптов приходится считаться со множеством условий, динамическими данными и прочим. Мне кажется, чем-то подобным должны заниматься люди которые создают ботов для разных онлайн-игр, чтобы автоматизировать некоторые однотипные задачи. Но в своих тестах мы стараемся реализовать все игровые активности.
Проблема релевантных данных
При разработке скриптов для эмуляции банковских бизнес-процессов скрипты (обычно) опираются на данные, которые “видят” на конкретном этапе (на веб-странице например). То есть, чтобы перейти на следующий шаг необходимо лишь расфасовать в нужные места заранее подготовленные (или взятые из этой же страницы) данные и отправить их.
Одна из главных проблем при разработке скриптов для игры — это сложность самого слежение (трекинга) изменений, которые произошли до данного конкретного момента, перед выполнением команды. Информация об изменённом состоянии объектов, ресурсов, юнитов и пр. может прийти в любой момент, даже после выполнения не связанных с конкретными данными действий. Если пропустить данное обновление, то виртуальный пользователь (VU, тред в Jmeter) будет рассинхронизован с игрой и начнет генерить ошибки аля “недостаточно ресурсов” или «нет места на карте» и превращать нагрузочный тест в нечто бесполезное. Конечно всегда есть вероятность того, что всё же скрипт выдаст что-то типа “нельзя атаковать союзника”, если он секунду назад стал таковым, но то же самое произойдет и в настоящем клиенте.
Также осложняет жизнь то, что почти всегда все исходные данные и текущее положение дел в игровом мире приходят в клиент только при логине в игру (обычно это огромный JSON на несколько мегабайт) и далее по ходу игры клиент исходя из этих первоначальных данных и преходящих изменений находится в релевантном состоянии, то есть знает о текущем положении дел. То же самое необходимо реализовать и в скрипте, надо чтобы каждый VU “запомнил” что игра присылает на стадии логина и далее аккуратно передавать и изменять эти данные в ходе выполнения теста. Далее приведу пример как я решал проблему с одной из игр компании InnoGames.
Forge of Empires
(надеюсь это не зачтется за рекламу, нужно описать суть проблемы и решения, но не могу без краткого описания самой игры)
Это градостроительный симулятор в котором игрок начинает с эпохи каменного века и постепенно, развивая технологии, завоевывая провинции, воюя с другими игроками, продвигает и расширяет свой город до… эндгейма, который очень далеко.
Вновь зарегистрированный игрок после логина видит примерно следующее: пустое поле и одно главное большое здание (ГЗ), пару деревьев и дорог на нем:
Само незанятое поле и объекты разбиты на квадраты, в зависимости от размера приходится считаться с размером самого здания и свободного места на карте. Здания делятся на типы: жилые, производственные, военные, культурные, дороги и прочее. Разные здания производят разные ресурсы: жилые — население и деньги, производственные — товары и ресурсы, культурные — счастье и так далее. При постройке каждого здания необходимо учитывать те же самые ресурсы и если их не хватает нужно либо ждать, либо, например, построить новый дом, чтобы восполнить население. Чувствуете куда я клоню? Это не бухгалтерские проводки эмулировать 🙂
Строительство
В градостроительном симуляторе главным бизнес-процессом (назовем это так) является собственно само строительство. Это первая и главная проблема при создании скриптов для игр такого рода. Проблема постройки здания делится на несколько подзадач, которые необходимо решать одновременно:
После нескольких часов раздумий пришла идея простого алгоритма, я назвал его “строительство слоями”. Суть его в следующем. Как вы могли заметить на скриншоте сверху, ГЗ прижато к краю карты за которым уже ничего строить нельзя и это сыграло на руку. Каждый VU после логина первым делом строит дорогу по контуру карты и самого главного здания, а уже потом строит необходимые здания, возле этой дороги, пока есть место. Таким образом все здания построенные у дороги будут связаны с ГЗ. Далее мы строим следующий “слой” дороги уже по контуру построенный зданий. Таким образом, первоначальную дорогу мы строим, исходя из условия: например если слева граница карты, справа — пусто, а сверху и снизу от проверяемого квадрата пусто или что-то есть, то мы вероятно можем построить дорогу.
Примерно так (зеленый квадрат — ГЗ, жёлтый — дорога, черный — какое-либо здание):
Поехали
Так как данная игра общается с клиентом исключительно посредством http с JSON`ами, я использую в Jmeter дополнительную библиотеку org.json для работы и парсинга запросов/ответов в post- и pre- процессорах.
Первым делом, как я уже упоминал выше, необходимо в ходе логина, при выполнении инициализирующих пользовательскую сессию действий, правильно распарсить и сохранить все необходимые начальные данные. Касательно данной игры — это единственный момент, когда мы можем узнать и запомнить как выглядит наш город в данный момент, наши ресурсы, а также всю необходимую мета-информацию о стоимости зданий, юнитов, товаров, которая нам необходима впоследствии.
Для упрощения кода впоследствии и уменьшения потребляемой памяти каждым java-тредом мы сохраняем из всего набора данных только те, которые будем использовать, поэтому предварительно необходимо создать и подключить два простеньких вспомогательных класса Entity и ExistEntity — первый отвечает за любое доступное в игре здание в принципе (со стоимостью, размером, функциями и прочим), а второй за уже построенное в городе (с координатами).
Первый POST-запрос StaticData_getData возвращает огромный JSON весом в 1-2 мегабайта. Распарсим его, создадим структуру, например HashMap и заполним её объектами Entity с ключами id, чтобы впоследствии обращаться к этому хэш-мапу за информацией о каждом конкретном здании:
Теперь каждый виртуальный пользователь знает всю необходимую информацию о зданиях. Далее необходимо “запомнить” территорию, её размеры и текущее расположение зданий в самом городе. Я использовал также HashMap, в котором в качестве ключей используются объекты класса java.awt.Point с координатами X, Y, а в качестве значений String с названием типа здания в данной координате.
Сама территория города не является квадратом а состоит из набора открытых областей, размером 4×4, поэтому изначально мы заполняем данный хэш-мап нулями по всем координатам которые открыты и доступны пользователю. Помимо этого, нам необходимо использовать данные из предыдущего шага, т.к. мы получая только координаты здания из данного запроса, должны также “залить” другие координаты, исходя из ширины и высоты здания.
При помощи vars.putObject() теперь каждый тред (VU) будет знать всю необходимую информацию, остаётся только вовремя обновлять данные объекты на каждом этапе выполнения скрипта, если игра присылает соответствующие данные.
Строим
Теперь зная стоимость, размеры зданий, а также текущее расположение объектов на территории виртуального города, можно начинать строить новые здания. Первым шагом, как я писал раннее, является первый “слой” дороги по контуру карты, чтобы все последующие здания имели связь с главным.
Добавляем в HTTP Sampler jsr223 pre-processor и формируем запрос. Перебираем каждую квадратик, ищем пустой и тот, которого окружает хоть один (из 8) занятый другим объектом (включая границу) квадрат. Таким образом, мы “обведём” дорогой любой объект, включая границу территории (здесь есть большой простор для оптимизаций, надеюсь кто-нибудь подскажет более лучший алгоритм):
Далее нам необходимо построить само здание. Допустим, сейчас нам неважно какое, нам важен лишь его размер. Соответсвенно ищем на воображаемой карте такую координату, от которой на расстоянии ширины здания по оси X и высоты здания по оси Y находятся свободные квадраты, а также в одной из восьми квадратов по углам здания есть дорога (я правда проверяю 4 верхних, таким образом заполнение города идет сверху вниз):
Также необходимо удостовериться, что на всей желаемой территории будущего здания не окажется какого либо объекта (дерева например):
На самом верхнем уровне тест-плана Jmeter добавляем Post-processor который, будет реагировать на каждый входящий респонс от игры, парсить его и обновлять объекты, так как нам необходимо трекать изменение ресурсов, а также обновлять виртуальную карту новыми зданиями:
Как итого после одного 12-часового нагрузочного теста можно увидеть реально построенный город с различными зданиями, которые соединены с главным зданием, а значит вполне себе неплохо функционируют:
Спасибо за внимание, я решил не сваливать всё в кучу и разбить тему на несколько частей. Следующая часть будет посвящена решению той же самой проблемы, но в более жёстких условиях, когда клиент игры использует HTTP-протокол с protobuf, а обновления получает через web-сокет с STOMP.
Оставлю ссылку на наш гитхаб, может найдете что-то интересное.