Java Properties vs XML

Разрабатывая код продукта самостоятельно постоянно сталкиваешься с интересными вещами, на которые в другой ситуации не обратил бы внимания.

В этой заметке речь пойдёт о формате конфигурационного файла.

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

Данные стали выглядеть вот так.

#goblin
mob8.id=08
mob8.armour=light
mob8.speed=3.5
mob8.flying=true
mob8.fps=30
mob8.hpMod=1.01


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

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

private function parseMobs() : void
{
    mobs = new Vector.<MobTemplate>();
                        
    var i : int = 0;
    while (true)
    {
        var key : String = KEY_MOB + i;
        var id : String = getProperty(key + PARAMETER_ID);
                
        if (isEmpty(id))
        {
            break;
        }
                
        var mob : MobTemplate = new MobTemplate(id);
        mobs.push(mob);
        i++;
    }
}


Где getProperty пробегался по всему файлу ища параметр по ключу.

private function getProperty(key : String) : String
{
    for each (var str : String in strings)
    {
        if (str.indexOf(key) == 0)
        {
            return str.replace(key + '=', '');
        }
    }
    return null;
}


И так оно прекрасно работало во флеш версии.

Затем началось портирование игры на мобильные устройства, но ближе к завершению решили её не выпускать, а сделать на её основе что-то другое.

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

На третьем айпаде парсинг стал занимать 5800 мс.
На компьютере — 1800 мс.

Надо было что-то делать.

Простым парсером на Java я преобразовал конфигурационный файл в XML, допилил его ручками, затем адаптировал внутриигровой парсер к новому виду данных.

В итоге получилось следующее.

<!-- goblin -->
<mob id="8">
        <armour value="light" />
        <speed value="3.5" />
        <flying value="true" />
        <fps value="30" />
        <hpMod value="1.01" />
</mob>


Избыточность, в виде номера монстра, ушла в прошлое, ну а парсинг стал таким.

private function parseMob() : void
{
    mobs = new Vector.<MobTemplate>();

    var list : XMLList = Resources.getConfig().child(MOBS);

    for each (var xml : XML in list.children())
    {
        var id : String = xml.attribute(ID);

        var mob : MobTemplate = new MobTemplate(id);

        mobs.push(mob);
    }
}


Результат превзошёл мои ожидания.

На третьем айпаде парсинг стал занимать 270 мс, т.е. код стал работать в 21 раз быстрее.
На компьютере разница оказалась ещё более значимой — 50 мс, т.е. код стал в 36 раз быстрее.

К слову, на текущий момент, конфигурационным файл — это XML в 2779 строк.
До этого, в txt, он занимал 2377 строк.

В завершении хотелось бы спросить дорогих читателей — нужны ли подобные заметки, интересно ли про такое читать?
  • +7

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

+3
На мой взгляд, еще проще хранить конфиг в json. После загрузки один раз парсим в объект и дальше работаем с этим объектом. Ну и по размеру меньше :)
0
Так тут тоже самое, один раз парсится при загрузке.
+4
Сохраняешь настройки один раз в массив байт через writeObject и дальше читаешь его совершенно без надобности парсить его.


var mob : Object = {life:100, speed:25, attack:8};
var bytes : ByteArray = new ByteArray();
bytes.writeObject ( mob );


И загружаешь в проект этот бинарник (можно из файла).

Потом просто:


var myMob : Object = someByteArray.readObject();
trace ( myMob.life, myMob.speed, myMob.attack );
// 100 25 8
+1
Настройки постоянно меняются, дизайнеру проще xml править, что-то с кодом делать.
0
А кто мешает написать на Adobe Air за 9-15 минут приложение, которое будет кормиться XML файлами и выплевывать некий config.bin, который как bytearray тупо эмбедится в проект? Прогу на Adobe Air отдал геймдизайнеру или кому там. Он правит себе. Тебе выдает бинарный файл. Который парсится за от 0 до 50 мс на девайсе практически с невненямым объемом данных настроек :)

Плюс это некая защита от игроков, которые меняют конфиги для упрощения игры. Просто сейчас ты упрощаешь жизнь себе и тем самым усложняешь игроку :)
0
Твой вариант слишком сложный для использования во время разработки.

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

Другое дело перегнать все ресурсы в бинарники перед публикацией игры, это да.
0
В бинарник записываешь время последнего изменения xml. При парсинге бинарника сверяешь эту дату — если совпало — то все ок, если нет, но надо перепарсить xml. У меня примерно так сделано.
0
Что-то подобное нужно будет сделать перед публикацией игры, в процессе разработки себе жизнь усложнять не хочется.
0
ЭЭ. Видимо мы не поняли друг друга. В процессе разработки жизнь никак не усложнится. Совсем никак.
0
Т.е. получается, что ты один раз распарсил конфиг, сохранил содержимое куда-нибудь в шаред обджект, и пока конфиг в очередной раз не поменялся он берётся из шаред обджект уже распарсенный, правильно понимаю?
0
Да, вроде все так.
0
Здорово! А где можно загуглить формат байтов по которому кодируется объект?
чтобы потом знать как вручную распарсить массив байтов где-нибудь на сервере?
+1
Копать тут en.wikipedia.org/wiki/Action_Message_Format
Вообще некоторые сервера имеют встроенную поддержку парсинга AMF. Так же в сети много либ на любой вкус и цвет.
Если надо максимально сжать — можно использовать последний AFM3 формат.
+1
Как вариант, геймдизайнер правит файл json, а перед публикацией ты копипастишь его в as3 файл: таким образом и защита(конфиг вкомпилется в swf) и удобнее чем XML.
0
В релиз сборке надо только ByteArray и readObject — быстрее ты никак не считаешь и не распарсишь
0
А зачем он нужен-то ByteArray?
Создаем некий файл .json, правим, в релиз сборке вместо загрузки json, копипастим в as3 файл и объект с параметрами берем из него.

пример json:
[
        {
            "id": 1,
            "armour": "light",
            "speed": 3.5,
            "flying": true
        },
        {
            "id": 2,
            "armour": "light",
            "speed": 3.5,
            "flying": false
        }
]

Создаем as3 файл, например GoblinConfig.as3 создаем единственную статическую константу в нем, это и будет конфиг:
package setting {
public class GoblinConfig {
    public static const config:Array = [
        {
            "id": 1,
            "armour": "light",
            "speed": 3.5,
            "flying": true
        },
        {
            "id": 2,
            "armour": "light",
            "speed": 3.5,
            "flying": false
        }
    ]
}
}

Сюда естественно можно добавить и метод возвращающий конкретные настройки по id.
0
В этом случае не за чем. Но в случае с XML, который еще и парсить надо — то XML нафиг не надо. Вместо него лучше конечно ByteArray. Но если объект-конфиг лежит как часть as3 кода — тогда конечно лучше json. Из двух зол выбирать надо наименьшее. XML vs ByteArray — решается вопрос скорости считывания. Тут ByteArray лидер. А если (в данном контексте) ByteArray vs Json — то лучше json при условии, что ег оне будут парсить, а просто «подставят».
0
Интересный вариант.

Перегнал xml в json — получилось 3028 строк плюс необходимость ручками красоту навести.
Кроме того, поддержка xml в Eclipse гораздо лучше и удобней, чем json, да ещё у меня Flash Builder практически сразу упал, после открытия конфига в json.

Так что пока оставлю xml, но json буду иметь в виду, спасибо!
0
Тогда твой выбор очевиден — в релизной сборке юзай ByteArray для молнеиносного парсинга файлов
0
вот что-то не могу понять XML парсится в разы быстрее, чем файл в формате Java-properties? с чего-бы это? По размеру файлы ± одинаковые получаются — значит и парсить на порядок должше не должно. По моему у вас был просто неправильный алгоритм парсинга (по нескольку раз пробегался по файлу и искал параметр — вместо того чтобы пробежаться по файлу один раз и распарсить его) — так что и Java-properties бы работали без проблем
0
Да, я писал что парсинг java properties был в лоб, каждый ключ искался обходом по всем строкам в векторе.
Из-за того, что изначально конфиг был маленьким, то время парсинга в глаза не бросалось.
Увеличение скорости в 20-30 раз произошло, как я понимаю, из-за того, что xml парсится по уровням, т.е. на верхнем уровне, чтобы получить необходимый раздел, нужно сделать не до 2300 проверок, а всего до 11.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.