Minecraft MySQL-2-YAML

Posted: 2013-08-28 in IT, Software
Метки:

Довелось тут поучаствовать в поднятии и настройке сервера сей замечательной игры.
Игрушка сея вполне эпична, а уж присутствующие в ней глюки — ещё эпичнее.
Вот про мейнкрафт я немного и расскажу. …

В целом, сервер построен не сильно сложно — ядро, обслуживающее сетевые подключения + базовые действия в игровом мире, и куча плагинов, добавляющих нужные функции. Плагинами делается всё, даже авторизация пользователей.
Причем встречаются плагины диаметрально противоположного уровня качества. Скажем, плагин AuthMe вполне можно приводить в качестве примера правильной реализации хэширования паролей. Конструкция из двойных посоленных SHA-256 хэшей [ SHA256(SHA256(PASSWORD).SALT) ] — это очень и очень правильно. После сертифицированных серьёзными организациями операторских биллингов, хранящих пароли открытым текстом (как системные, так и абонентские) — как луч света в тёмном царстве.

Но бывает и совсем по-другому. Плагин WorldGuard, прекрасно работавший несколько месяцев, внезапно начал подвешивать сервер почти при любых операциях с регионами, выкидывая при этом всех игроков и отправляя сервер в ребут. Выяснилось, что перенос этих самых регионов из текстовых файлов в MySQL делается самой игрой, а вот для обратного преобразования потребовалось написать небольшой скрипт:

<?php

#   MySQL-2-Yaml
#   Minecraft WorldGuard regions converter
#   written by Amin, version 2013-08-27 0001
#   License: Creative Commons

$db_host = 'localhost';
$db_name = 'minecraft';
$db_user = 'minecraft_db_user';
$db_pswd = 'minecraft_db_password';

$cu_world = isset($_GET['world']) ? $_GET['world'] : 'world';
$cu_world = addslashes($cu_world);

$conn = mysql_connect($db_host, $db_user, $db_pswd) or print mysql_error();
mysql_selectdb($db_name) or print mysql_error();

print "<pre>regions:n";

$qr = mysql_query (" SELECT rc.* FROM `region_cuboid` rc
                    INNER JOIN world w ON rc.world_id = w.id
                    WHERE w.name = '$cu_world' ") or print mysql_error();
while ($rc = mysql_fetch_array($qr)) {
   $region_id = $rc['region_id'];
   $world_id = $rc['world_id'];
   print "    $region_id:
        type: cuboid".'
        min: {x: '.$rc['min_x'].', y: '.$rc['min_y'].', z: '.$rc['min_z'].'}
        max: {x: '.$rc['max_x'].', y: '.$rc['max_y'].', z: '.$rc['max_z'].'}
        priority: 0';
   $qf = mysql_query(" SELECT * FROM `region_flag` WHERE region_id = '$region_id' AND world_id = '$world_id' ") or print mysql_error();
      $flags_str = '';
      while ($rf = mysql_fetch_array($qf)) {
         $flags_str .= $rf['flag'].': '.substr($rf['value'], 0, -1).', ';
      }
      $flags_str = substr($flags_str, 0, -2);
   $qo = mysql_query("SELECT u.name FROM `region_players` rp
                      INNER JOIN `user` u ON u.id = rp.user_id
                      WHERE rp.region_id = '$region_id' AND rp.world_id = '$world_id' AND owner = '1' ") or print mysql_error();
      $owners_str = '';
      while ($ro = mysql_fetch_array($qo)) {
         $owners_str .= $ro['name'].', ';
      }
      $owners_str = substr($owners_str, 0, -2);
   $qp = mysql_query("SELECT u.name FROM `region_players` rp
                      INNER JOIN `user` u ON u.id = rp.user_id
                      WHERE rp.region_id = '$region_id' AND rp.world_id = '$world_id' AND owner = '0' ") or print mysql_error();
      $players_str = '';
      while ($ro = mysql_fetch_array($qp)) {
         $players_str .= $ro['name'].', ';
      }
      $players_str = substr($players_str, 0, -2);
   print "n        flags: {".$flags_str."}";
   print "n        owners:
            players: [".$owners_str."]";
   print "n        members:";
   if ($players_str != '') { print "n         players: [".$players_str."]"; } else { print " {}"; };
   print "n";
}

?>

Использование тривиально — выкладываем на веб-сервер или запускаем прямо в консоли. По умолчанию выгружаются регионы из мира world (мир по-умолчанию), для выгрузки других регионов скрипту надо передать соответствующий параметр. Вывод сбрасываем в файл, удаляем лишние строки в начале и конце файла, если они мешают. Иногда в базе может быть убитый не-английский текст. Писать обработчик мне было влом, если мешает — удалить вручную, он обычно выгружается "вопросиками". Но в нашем случае всё это помогло лишь частично. Выяснилось, что плагин очень странно реагировал на имена регионов, состоящие только из цифр, и только их переименование позволило избавиться от Java-Exception-ов, обильно засыпавших логи.

Важное дополнение: в YAML-конфигах уровни иерархии задаются числом пробелов, будьте внимательны.

- комментарии
  1. anonymous:

    makkarpov writes:И да, имена регионов из цифр надо, как я помню, брать в одинарные кавычки. Вообще где-то был онлайн-чекер валидности YAML.

  2. anonymous:

    makkarpov writes:Для выгрузки нормального текста надо послать еще один запрос перед основным — "SET NAMES utf8". Кстати, авторизация пользователей есть и в ядре — тот самый online-mode. При этом задействуется внешний сервер авторизации.

  3. anonymous:

    ALEX writes:Давно перевёл боевую базу на postgresql.Глюкомыскель только иногда снится.В кошмарах.

  4. Aminux:

    Если я правильно понял, то в онлайн-моде не работают "типо пераццкие" клиенты. Про UTF-8 не понял — там же вроде CP1251 в таблицах ? Это кстати к вопросу о том, почему UTF-8 должен всех убить. Его реализация "из коробки" уменьшила бы количество головной боли на порядок. То, что разрабы современных теплых ламповых линуксов сделали UTF-8 в именах файлов и консоли по умолчанию, отправив весь остальной зоопарк кодировок в вечный deprecated — великое благо. Разработчикам приложений давно пора последовать за этим мудрым примером.Про кавычки не понял — оно вообще в полях БД лежало. Валидатор на сгенеренный йамл не ругался. Проблема в том, что плагин WorldGuard — местами зело глючен и явно страдает от недостаточности проверок данных. Хуже — только реализация команды /topbalance в плагине iConomy. Там сперва посылается запрос списка игроков, затем для каждого шлется отдельный селект в базу, хотя те же данные легко извлекаются одним общим примитивным селектом с сортировкой. Разработчиков за такую реализацию стоило бы сослать на рудники. Урановую руду деревянной киркой добывать.Ну, в данной софтени есть три типа хранилища — MySQL, SQLite и текстовые файлы в YAML-формате. MySQL кстати вполне себе годная вещь, если конечно использовать его по уму.

  5. anonymous:

    makkarpov writes:Все циферные регионы берутся в кавычки. Пример: http://pastebin.com/8Zya3NQQ .В таблицах, вероятно, CP1251, хз, никогда не работал с WG MySQL-хранилищем. Суть в том, что даже если в таблице кодировка указана, как utf8_general_ci, не послав запроса с SET NAMES, ты в скрипте у себя увидишь крякозябли (а то и просто знаки вопроса). online-mode — это режим с авторизацией пользователей, побочным эффектом которого является отсеивание пираток. Однако этот online-mode легко перепиливается на альтернативные сервера авторизации, и все становится в ажуре. Вот примерное описание протокола авторизации: http://pastebin.com/e6c57q5W

  6. anonymous:

    makkarpov writes:Ну реализации YAML разные бывают, м.б. SnakeYAML (вроде так он называется) это не воспринимает. Касаемо онлайн-мода — это так, простой ликбез на тему того, что это не прихоть копирастов. Касаемо своего лаунчера — уж свой апдейтер я бы рекомендовал запилить, дабы не бежать на форум и не говорить, что мол, качайте новый майн отсюда.

  7. Aminux:

    Все циферные регионы берутся в кавычки.

    Хм, а валидатор проглотил. Это баг валидатора или особенность майнкрафта ?С SET NAMES в курсе, просто забыл про него. У нас всего один такой флаг был в тестовом регионе.На онлайн-мод мы забили, иначе там вообще никто играть не будет :DЧто касается лаунчера — мы пока свой лаунчер делать не готовы.