Calltracking в Minecraft или как быстро сделать трехмерный UI

    Пару месяцев назад назад я показал детям 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 вывести на офисный монитор и настроить звук погромче — должно впечатлять.
    Еще идеи?
    Поделиться публикацией

    Комментарии 19

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

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

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

        +3
        В правом видео чувствуется рука старого опытного квакера.
          0
          Все верно, q3, я бы еще распрыжку сделал но она в майнкрафт не предусмотрена почему-то.
            0
            Ну распрыжка — это баг физики, не все подобные баги воспроизводят)
              0
              не знал, всегда был уверен, что это фича :)
        +2
        (length, 200)[length>200]

        Ну есть же min
          0
          Нужно было [length, 200][length > 200], чтобы скобочки были одинаковые.
            +1
            Если (length, 200)[length>200] напишет кто-нибудь из моей команды — получит по мозгам и сильно.
              0
              на хакатонах у вас тоже все строго?
              Я если честно гуглил синтаксис if и for во время разработки. питон не мое, я обычно на swift пишу.
            +2
            Делать вывод из заббикса в майнкрафт. Дети сами скажут когда всё стало плохо.
              0
              Детский труд это нелегально. здесь должен быть смайлик, но на хабре запустили программу устранения смайликов.
              видимо все комменты должны быть серьезными =] не улыбаться.
              0
              Просмотрел видео 3 часовое. Как же вам не хватает второго монитора! Втрой монитор для разработчика просто рулит нереально!
                +4
                Так а где статистика в 3Д?? Все равно ведь двумерная. просто с толщиной линий… =)

                Но в целом прикольно. =)
                  0
                  резонное замечание, можно было использовать глубину башни для отображения еще какого-то параметра… качество связи например :)
                  +1
                  А какой софтиной можно так писать видео, чтобы было компактно (а не 8 часов видео)? Например в конце просмотреть на 60x и понять, над чем дольше всего тупил, и сколько отвлекался?
                  P.S. Под OS X
                    0
                    Я использовал Screenflick.app. Там можно настроить кол-во кадров в секунду (например я ставил 5 кадров в секунду, но если писать весь день — то хватит и 1 в секунду или еще меньше.
                    И потом можно экспортнуть в таймлапс видео с любой скоростью.
                      +1
                      Спасибо за наводку! То, что нужно.
                    0
                    Я что-то не понял. Это всё рассчитано на то, что у меня есть Raspberry Pi и сервер я держу на ней?
                    В статье нет ссылки на питоновскую библиотеку. Полез гуглить. И так и не понял где её найти. Везде упоминается некая сборка сервера Майнкрафта под Малинку, которая понимает скрипты на Питоне.
                    А где отдельно взять? Как её связать с существующим сервером на bukkit?

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое