Пару месяцев назад назад я показал детям Minecraft, а чуть позже — купил им книгу по программированию в MineCraft. Правда, детям купил, чес-слово. Ну сам взял полистить, ну написал пару скриптов…

На этом история и закочнилась бы, но на днях мне довелось поучаствовать в хакатоне одного calltracking сервиса. Для тех, кто не в курсе, calltracking — эта такая штука, которая предоставляет статистику звонков. И что важно для нашей истории — эта статистика доступна по API.

В этот момент отдельные части сложились в цельную картину и я подумал — о! статистика звонков в Minecraft.



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

А если серьезно — то кто сказал что интерфейсы должны быть двумерными?
И кто сказал что трехмерный интерфейс это долго и сложно?
Вся затея у меня заняла 3 часа (57 строк на питоне), учитывая, что первые полчаса я разбирался как на python парсить джейсон.

Под катом — вся история целиком, видео с результатом и бонус для дочитавших до конца — все 3 часа разработки в 3 минутном time-lapse видео.

Чтобы это все заработало мне понадобилось 3 простых шага:

1. Поднимаем сервер Minecraft который позволяет взаимодействовать с миром Minecraft по API на Python
2. Берем статистику звонков по calltracking API
3. Создаем кубики в Minecraft.

Ок, поехали!

1. Сервер Minecraft


Сервер майнкрафта с красивым названием bukkit у меня уже был готов (скачать его можно с сайта книги вместе с очень подробными видеоинструкциями).
Версия игры — 1.6.4, скрипты будем писать на Python, т.к. этот сервер их понимает.

2. Берем статистику звонков по API


Тут тоже все просто, пару строк на питоне и все готово:

import json, requests
data = requests.get("https://istat24.com.ua/api/{your_api_key}/calls.json?counter_start_date=2016-02-01&counter_end_date=2016-02-05")
json = json.loads(data.content)

Пару слов про сам API.

Я использую API Calltracking сервиса iStat24 (в отделе разработки которого и происходил этот самый хакатон), он возвращает журнал звонков в JSON, а чтобы получить данные для определенного аккаунта нужно в этом самом аккаунте сгенерировать API_key, который у меня к хакатону был заготовлен заранее.

Пример звонка из JSON:

{
call_id: 5555555,
numberA: "380555555555",
numberB: "380555555555",
start: "2016-02-05 15:11:13",
duration: "00:03:57",
wait_duration: "00:00:07",
speak_duration: "00:03:50",
record: "http://url_to_the_audio_record.mp3",
accepted: 1,
direction: "incoming",
reklama_name: "Google organic",
reklama_id: 729
}

Из этого всего нас интересует:
wait_duration — время ожидания звонка (гудки, короче говоря)
speak_duration — время разговора
accepted — значение 1/0 определяет был звонок принят или пропущен.

Итого, имеем массив звонков. Теперь осталось их только представить визуально.

3. Создаем кубики в Minecraft


До этого все было просто, да? даже не просто — тривиально.
Вы наверное думаете что с этого места начнется какая-то магия? А вот и нет.
Дело в том, что все написано до нас.
Есть расчудесная библиотека на питоне — minecraftstuff, которая умеет делать все основные вещи в Minecraft.
Нам осталось только описать где и какого типа кубик мы хотим создать.

Подключаем и инициализируем библиотеку:

import mcpi.minecraft as minecraft
import mcpi.block as block
import mcpi.minecraftstuff as minecraftstuff

mc = minecraft.Minecraft.create()
mcdrawing = minecraftstuff.MinecraftDrawing(mc)

Теперь координаты. Minecraft — мир трехмерный, поэтому очевидно что нам нужны x,y и z.
Можно брать текущие координаты персонажа командой mc.player.getTilePos(), но я решил захардкодить определенное место в мире Minecrft (просто потому, что после каждой итерации мне нужно было очищать все пространство с предыдущей попыткой «строительства».) Для дебага удобнее, в общем.

Кубик рисуется командой mc.setBlock(x, y, z, blockType).

А как удалить кубик? Как оказалось — воздух в Minecraft — это тоже кубик. Поэтому вместо удаления кубиков — нужно просто нарисовать кубики с воздухом. Можно делать это поштучно при помощи это же команды mc.setBlock(x, y, z, block.AIR.id) — указывая тип блока «block.AIR.id». А можно использовать команду mc.setBlocks() которая забивает прямоугольную область кубиками нужного типа. В книжке написано что это быстрее, чем рисовать кубики поштучно.

В итоге у меня получился вот такой код для очистки пространства:

mc.postToChat("START")

startX=146; startY=0; startZ=-30 // хардкод начальных координат

# Clean up
mc.setBlocks(startX-2, startY-20, startZ+5, startX+2, startY+200, startZ-250, 8)
time.sleep(2)
mc.setBlocks(startX-3, startY-1, startZ+6, startX+3, startY+210, startZ-551, block.AIR.id)
time.sleep(2)

# cleanUp(pos.x, pos.y, pos.z, 40) # Clean up self
mc.postToChat("Clean up is done")

Лайвхак. Здесь я сначала забиваю пространство кубиками с ID=8 а потом забиваю это же пространство воздухом.
Это делается исключительно для дебага, чтобы было видно какой же именно участок через 2 секунды будет очищен. Иначе это совершенно не очевидно и занимает кучу времени подгадать нужные координаты.

Вообще, весь этот участок кода можно заменить на всего одну команду: mc.setBlocks(startX-3, startY-1, startZ+6, startX+3, startY+210, startZ-551, block.AIR.id), все остальное исключительно для дебага.

Чтобы было красивее, я решил что каждый звонок будет представлен в виде башни шириной (и толщиной) в 2 кубика — а длительность звонка будет представлена высотой башни (1 секунда = 1 кубик). Поэтому простенькая процедурка которая рисует башню заданной высоты:

def drawCall(x, y, z, length, blockType):
	length = (length, 200)[length>200]
	for i in range(y, y+length):
		mc.setBlock(x, i, z, blockType)
		mc.setBlock(x+1, i, z, blockType)
		mc.setBlock(x+1, i, z+1, blockType)
		mc.setBlock(x, i, z+1, blockType)

Обратите внимание, что в нее встроен дисторшн — потому что высота мира в Minecraft, как оказалось, 255 кубиков, поэтому если звонок был длиннее 255 секунд (а таких конечно же много) — они уходят выше «крыши мира» и продолжаются с «дна мира», что конечно же не эстетично.

Теперь у нас готово все, чтобы нарисовать звонки.
Просто пробегаемся по массиву звонков полученному из API и рисуем башни (используя кубики разных типов, чтобы визуально представить время ожидания звонка, время разговора и пропущенные звонки — красным цветом).

for call in json:
	offset+=3
	duration = get_sec(call['duration'])
	wait_duration = get_sec(call['wait_duration'])
	speak_duration = get_sec(call['speak_duration'])	
	
	if call['accepted'] == 1:
		drawCall(startX, startY, startZ-offset, wait_duration, 41) # wait_duration
		drawCall(startX, startY+wait_duration, startZ-offset, speak_duration, 133) #speak_duration
	else:		 
		drawCall(startX, startY, startZ-offset, duration, 152) # duration


На этом все, заходим в майнкрафт и любуемся трехмерной статистикой звонков.

Кстати, одно видео я записал сам, а второе — записал ребенок. Угадаете какое где?



Желтые кубики — время ожидания(гудки), зеленые — время разговора, красные — пропущенный звонок.

[ Исходник скрипта на Python ]

И обещанное в начале поста видео 3 часов разработки сжатое до 3 минут:



И напоследок вопрос, какие процессы/данные, по вашему, смотрелись бы лучше в трехмерном виде?
Навскидку:
— дашборд для отображения продакшн-серверов, в случае падения какого-либо из них — скрипт автоматически добавляет кубик-динамит и взрывает его :) если Minecraft вывести на офисный монитор и настроить звук погромче — должно впечатлять.
Еще идеи?

Комментарии (18)


  1. kvaps
    17.03.2016 16:27
    +2

    Это просто amazing!!

    в случае падения какого-либо из них — скрипт автоматически добавляет кубик-динамит и взрывает его
    Лучше взрывающегося крипера, с таким звуком будет эффектнее :)

    Идея для визуализации данных: статистика самих серверов Minecraft

    На вопрос какое видео ваше
    Делаю ставку на то, которое справа, под №1.
    Тому, что на левом видео, было интереснее попрыгать по фигурам по всем этим, а тому что на правом скорее продемонстрировать их. Ну и к тому же потому, что оно первое


    1. kashey
      18.03.2016 09:52
      +3

      В правом видео чувствуется рука старого опытного квакера.


      1. n0_quarter
        18.03.2016 10:50

        Все верно, q3, я бы еще распрыжку сделал но она в майнкрафт не предусмотрена почему-то.


        1. Newbilius
          18.03.2016 13:53

          Ну распрыжка — это баг физики, не все подобные баги воспроизводят)


          1. n0_quarter
            18.03.2016 16:26

            не знал, всегда был уверен, что это фича :)


  1. ForeverYoung
    17.03.2016 16:48
    +2

    (length, 200)[length>200]

    Ну есть же min


    1. dreamzor
      17.03.2016 18:25

      Нужно было [length, 200][length > 200], чтобы скобочки были одинаковые.


    1. grigorym
      18.03.2016 06:40
      +1

      Если (length, 200)[length>200] напишет кто-нибудь из моей команды — получит по мозгам и сильно.


      1. n0_quarter
        18.03.2016 10:52

        на хакатонах у вас тоже все строго?
        Я если честно гуглил синтаксис if и for во время разработки. питон не мое, я обычно на swift пишу.


  1. fleaump
    17.03.2016 19:32
    +2

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


    1. n0_quarter
      18.03.2016 10:48

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


  1. FSA
    17.03.2016 20:04

    Просмотрел видео 3 часовое. Как же вам не хватает второго монитора! Втрой монитор для разработчика просто рулит нереально!


  1. AlexFadeev
    17.03.2016 20:24
    +4

    Так а где статистика в 3Д?? Все равно ведь двумерная. просто с толщиной линий… =)

    Но в целом прикольно. =)


    1. n0_quarter
      18.03.2016 10:47

      резонное замечание, можно было использовать глубину башни для отображения еще какого-то параметра… качество связи например :)


  1. KhodeN
    18.03.2016 13:53
    +1

    А какой софтиной можно так писать видео, чтобы было компактно (а не 8 часов видео)? Например в конце просмотреть на 60x и понять, над чем дольше всего тупил, и сколько отвлекался?
    P.S. Под OS X


    1. n0_quarter
      18.03.2016 16:26

      Я использовал Screenflick.app. Там можно настроить кол-во кадров в секунду (например я ставил 5 кадров в секунду, но если писать весь день — то хватит и 1 в секунду или еще меньше.
      И потом можно экспортнуть в таймлапс видео с любой скоростью.


      1. KhodeN
        18.03.2016 17:22
        +1

        Спасибо за наводку! То, что нужно.