Рейтинг темы:
  • 0 Голос(ов) - 0 в среднем
  • 1
  • 2
  • 3
  • 4
  • 5
Как вляпаться на ровном месте или форк старого доброго С4
#11
Apoloser Написал:Нагрузка в том что любой эмулятор к чему то стремится, в случае Lineage к PTS. Я думаю объяснил внятно

Окей. Вас понял, вопрос к ТС.
Цитата:Зачем: Just 4 fun. Конечно, переработать код такого качества в что-то приемлемое - вопрос непростой (в плане времени), но все же постепенно будем двигаться вперед. Задача довольно понятная - сделать решение приемлемого для С4 качества, при этом не унаследовать все архаизмы из имеющихся решений.
Механики работы тех или иных скриптов и не будет никогда работать идентично ПТС на эмуляторах.

С подходом "Just 4 fun", вы думаете, все сложиться гуд, и проект не сольется через 3-4 месяца разбирания, переписывания тру механики? Каждый когда-то "ломается", всем рано или поздно становиться "впадлу", ибо
1) это не благодарное дело работать на шару.
2) Да просто зае***ь.
3) уникум ЧП.
This is fact of life.
P.S. Кого обидел, либо вызвал пукановзрывательную реакцию, заранее извините плызочки, никого не хотел ... :redlol::redlol::redlol:.
Ответ
#12
Я думаю, что у некоторых сложилось немного ложное представление о приоритетах и ресурсоемкости задач. Нам есть чем заняться, определенно, С4 - просто старт для opensource решений. Были мысли опубликовать и HF под MIT, посмотрим, что получится на данной итерации. К тому же, ряд задач переоценен - скажем, та же миграция с Ant'a до сих пор не сделана в l2j (как и сплит LS/GS, о котором я читал еще в 2013, кажется, а работы, по факту, на час).

Что касается механики - безусловно, все непросто. К тому же, у нас нет человека со скиллами RE, поэтому этот вопрос не так очевиден. Но ничего, со временем, думаю, найдется решения и для этого момента.

А что касается благородности opensource - тут каждый видит свои плюсы и минусы сам) Для меня это не есть неблагородное дело.

P.S. Да, что касается "надоест" - опять же, смотря что видеть в таких решениях. Я уже давно не developer, например, и по роду деятельности занимаюсь другими делами, а dev - это хобби, которое не надоедает. К тому же, даже внутри команды есть необходимая самодостаточность для обмена опытом и некоторого проф гейна. Мы все разные по профилю компетентности, есть чем поделиться работая над opensource. Если соберется хоть небольшая экосистема - отлично, нет - тоже не так страшно)

Всем спасибо за комментарии!
Ответ
#13
Обновим новости. Дело не стоит на месте, хотя пока что все движется в спокойном вечерне-кодинговом темпе. Итак, из основных нововведений:
  • Продолжение интеграции с Spring, добавление связки Spring/SpringData JPA/Hibernate (JPA impl)
  • Реализация с нуля retail-like CommunityBoard, использование JTwig (templating), четкое разделение на Service/DAO/Model слои
  • Незначительный рефакторинг кода, Java8 местами

Немного деталей.

1. Spring/SpringData JPA/Hibernate

Такой зоопарк позволяет очень быстро решать задачу создания четко определенного DAO-уровня. Hibernate здесь выполняет роль имплементации JPA2. Кто сталкивался с этой темой, думаю, сразу поймет в чем плюс, в качестве примера можно привести реализацию DAO для вкладки Memo (заметки) в CB:

Код:
public interface PlayerMemoDAO extends JpaRepository<PlayerMemo, Long>
{
    List<PlayerMemo> findAllByPlayerIdOrderByCreateDateDesc(int playerId);
    
    Optional<PlayerMemo> findByPlayerIdAndTitle(int playerId, String title);
}

Вы спросите "А где же имплементация и базовые CRUD-операции?" - ответ прост: все делает SpringData, достаточно только заэкстендить интерфейс JpaRepository, все остальное сделает фреймворк. Потребуются дополнительные (отличные от простых CRUD-методов) методы? Достаточно дописать в интерфейс нужные сигнатуры функций. Идея в том, что фреймворк разбирает название функции и генерирует нужный код. Например, findAllByPlayerIdAndTitle делится на:
  • findAll - аналог SELECT *
  • By - аналог WHERE
  • PlayerIdAndTitle - аналог player_id = ? and title = ? , значения берутся из аргументов

Ну и, разумеется, нужна модель PlayerMemo с разметкой JPA.

Не будем вдаваться в детали как это работает "под капотом", если интересно узнать подробности, то все очень детально описано в retail docs: click

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

2. Community Board

Взглянув на то, что есть в шаре, еще раз, стало понятно, что проще все выкинуть, нежели чем пытаться что-то поправить (опять же смотрим выше - DAO/Service как таковых нет, все в одной куче). В этом пункте можно выделить несколько подпунктов:

2.1. Tempaltes

Скажем так, делать конкатенацию строк контента в коде - слегка моветон. Когда возникает ряд состояний и повторяющихся конструкций - это моветон вдвойне. К тому же, представление сильно мешается с контроллером, что совсем плохо. В итоге на помощь приходит шаблонизация. Вариантов тут довольно много, можно брать как logic-less (e.g. Mustache), так и logic-full (e.g. FreeMaker, JTwig) решения. Конечно, более привлекательно для данных задач выглядят вторые. В итоге выбор пал на JTwig как наиболее приятный по идеологии. JTwig позволяет решать 99% задач, возникающих по ходу дела, те, что не решаются из коробки, можно доделать через расширения (доп. функции). Из основных удобств:
  • Structured layouts (наследование, включения и пр.)
  • Циклы, управление (if/else, foreach etc.)
  • Передача любых переменных, доступ к полям\методам (тут + немного рефакторинга уже от нас, чтобы была возможность работать с static-методами и переменными)
  • множество built-in функций для решения различных задач при работе со строками, датами, константами, массивами и пр.

На примере это выглядит так (ниже только часть шаблона):
Код:
{% for memo in memos|slice(itemsPerPage ** (page - 1), itemsPerPage) %}
                <tr>
                    <td FIXWIDTH=5></td>
                    <td FIXWIDTH=415><a action="bypass _bbsmemo:action:read:memoid:{{ memo.id }}">{{ memo.title }}</a>
                    </td>
                    <td FIXWIDTH=70 align=center></td>
                    <td FIXWIDTH=120 align=center>{{ formatter.format(memo.createDate) }}</td>
                </tr>
            {% endfor %}

Здесь memos - список заметок игрока, itemsPerPage - кол-во страниц для вывода на страницу (paging). Таким простым образом выводится список всех заметок (за основу layout'a взят PTS).

Из дополнительных плюсов стоит отметить довольно сильную поддержку в IntellijIDEA и высокую совместимость с оригиналом (Twig, PHP).

Также отмечу, что имплементации Twig в Java-мире, как минимум, две. Про используемый нами можно почитать тут: http://jtwig.org. Также есть Pebble. Принципиальная разница в том, что JTwig использует PG Parboiled. В общем, весьма интересная штука, для собственного развития однозначно стоит посмотреть (в том числе в части Parboiled).

2.2. Refactoring

Так как все с чистого листа, то рефакторинга, как такового, не случилось, но все же возник ряд интересных, на мой взгляд, моментов и решений. Первое - это соглашение о формировании bypass'ов. Кто видел оригинал PTS, явно отметит, что это ужас. К примеру, что можно найти на той же вкладке memo:
  • _mmcrea
  • _mmlist_2_
  • _mmlist_1 (почему нет подчеркивания - а черт его знает)
  • Write 5 -2 0 Search _ _
  • _mmmodi_1
  • _mmdele_1

Во-первых, читаемость таких bypass'ов, скажем так, не лучшая. Во-вторых, весьма сложно структурировать потоки данных, придется еще сгородить огород. Выход позаимствован из нашей HF работы, он довольно громоздкий, но решает массу задач. Пример bypass'a:

Код:
bypass _bbsmemo:action:delete:id:1

Что и как здесь работает? Первая часть _bbsmemo всегда одна в рамках менеджера (например, в рамках одной вкладки или раздела). Это решает ряд проблем с костылями, которые были раньше. Теперь создается менеджер крайне просто:

Код:
@CommunityBoardHandler(bypasses = "_bbsmemo")
public class BBSMemoManager extends AbstractCommunityBoardHandler
{
    @Override
    public void processBypassInner(L2PcInstance player, String action, Map<String, String> params, boolean write)
    {
    }
}

в processBypassInner прокидываются все команды, начинающиеся с _bbsmemo. Одна точка входа, что очень удобно. При этом Write-команды приходят сюда же. Далее определяется enum с нужными Action'ами (как минимум INDEX). Возвращаясь к bypass - action:delete как раз отвечает за нужный Action. Как вы уже могли догадаться, после идут параметры в виде пар ключ-значение. Опять же, это громоздко, но очень удобно, читаемо, легко рефакторится и сопровождается.

Вторая часть - это то самое четкое разделение на DAO/Service. Теперь никакого бардака, менеджер BBS отвечает только за прием\обработку байпассов, делегирует работу в сервис, там происходит вся кухня и работа с DAO. И все это завязано на IoC, что очень удобно.

3. Прочий рефакторинг, Java 8

По мере работы так или иначе попадается мусор (точнее, попадается он всегда, потому что абсолютно все ужасно, от оформления (начиная с эстетики в виде переменных с подчеркиваниями вида __value и заканчивая архитектурными бедами вида L2PcInstance на 10+к строк).

Небольшой пример до-после для загрузки территорий:

Было:

Код:
    public static Integer[][] get2DIntArray(String[] resultFields, String usedTables, String whereClause)
    {
        long start = System.currentTimeMillis();

        String query = "";
        Integer res[][] = null;

        java.sql.Connection con = null;
        try
        {
            con = L2DatabaseFactory.getInstance().getConnection();
            query = L2DatabaseFactory.getInstance().prepQuerySelect(resultFields, usedTables, whereClause, false);
            PreparedStatement statement = con.prepareStatement(query);
            ResultSet rset = statement.executeQuery();

            int rows = 0;
            while(rset.next())
                rows++;

            res = new Integer[rows - 1][resultFields.length];

            rset.first();

            int row = 0;
            while(rset.next())
            {
                for(int i = 0; i < resultFields.length; i++)
                    res[row][i] = rset.getInt(i + 1);
                row++;
            }
            rset.close();
            statement.close();
        }
        catch(Exception e)
        {
            _log.error("Error in query '" + query + "'", e);
        }
        finally
        {
            try
            {
                con.close();
            }
            catch(Exception e)
            {
            }
        }

        if(_log.isTraceEnabled())
            _log.trace("Get all rows in query '" + query + "' in " + (System.currentTimeMillis() - start) + "ms");

        return res;
    }

Код:
    public void reload_data()
    {
        _territory = new FastMap<>();

        Integer[][] point = SqlUtils.get2DIntArray(new String[]{"loc_id","loc_x","loc_y","loc_zmin","loc_zmax","proc"}, "locations", "loc_id > 0");
        for(Integer[] row : point)
        {
//            _log.info("row = "+row[0]);
            Integer terr = row[0];
            if(terr == null)
            {
                _log.warn("Null territory!");
                continue;
            }

            if(_territory.get(terr) == null)
            {
                L2Territory t = new L2Territory(terr);
                _territory.put(terr, t);
               }
            _territory.get(terr).add(row[1],row[2],row[3],row[4],row[5]);
        }
    }

Стало:

Код:
    @PostConstruct
    public void reload()
    {
        log.debug("Loading territories.. ");
        territories = new ConcurrentHashMap<>();

        try (Stream<TerritoryPoint> allAndStream = territoryDAO.findAllAsStream())
        {
            Collector<TerritoryPoint, L2Territory, L2Territory> downstream = Collector.of(
                (Supplier<L2Territory>) L2Territory::new, L2Territory::addPoint, L2Territory::merge);

            territories = allAndStream.collect(Collectors.groupingBy(TerritoryPoint::getId, ConcurrentHashMap::new,
                downstream));
        }

        log.debug("{} territories loaded", territories.size());
    }

+ интерфейс уже известного JpaRepository, еще 5 строчек, приводить не буду.

SpringData JPA закрыл этот жуткий ужас всего несколькими строками, Java 8 Stream API отлично решил задачу группировки (с простым кастомным коллектором). Помимо украшательств, тут же ушла и ошибка, допущенная в SqlUtils, которую глазом не сразу и заметишь (к вопросу об уровне абстракции и почему рутина - не всегда хорошо).

Резюме

Вот такая вышла ночная повесть, надеюсь, не слишком утомительно. Предвкушая вопросы вида "зачем так тяжело", "почему столько !@#$ enterprise-огородов", "зачем оно под хайлоад" и пр. отмечу несколько субъективных убеждений:
  • L2 в текущем состоянии уже не хайлоад. С4 - тем более
  • Оригинальный код ~2005-2006 года, сейчас 2015. За 10 лет деревья стали-таки зеленее, а вычислительные мощности доступнее и производительнее - сейчас уже можно не особо переживать о том, что приложение отточит пару лишних Gb памяти. Конечно, это этом нужно думать, но не так, как десять лет назад
  • Производительность - это вопрос как палка о двух концах. Конечно, такие решения - это лишний оверхед. Но, во-первых, см. п2, во-вторых - если поставить на весы скорость разработки, гейн и перфоманс, то далеко не факт, что написав в 5 раз больше кода получится хотя бы в два раза более быстрое решение. Яркий тому пример - как раз этот код. Вся эта рутина ничего не стоит, когда лажают на простых вещах, не говоря уже о Concurency. По моему сугубо личному мнению, значительно эффективнее сначала прототипировать, потом оптимизировать. Вооружившись профайлером, можно эффективно проработать узкие места и переписать все ботлнеки.

Такие новости к 3 утра. Да, ну и правки конфигов, разумеется!

До скорого!
Ответ
#14
Почему именно jtwig? В java-мире есть еще куча других шаблонизаторов, как пример - Velocity, который я использую. Меньше наворотов, легче синтаксис и все нужные вещи из коробки.
m0nster.art - clear client patches, linkz to utils & code.
Гадаю по капче.
Ответ
#15
похвальное начинание, моя молодость)
Ответ
#16
Pointer*Rage Написал:Почему именно jtwig? В java-мире есть еще куча других шаблонизаторов, как пример - Velocity, который я использую. Меньше наворотов, легче синтаксис и все нужные вещи из коробки.

Шаблонизаторов, действительно, очень много, выбрать нужный не так непросто, соглашусь. Почему именно JTwig? Есть ряд доводов, часть из них, скрывать не буду, довольно субъективные:
  • Функциональность и расширяемость - богатый набор built-in функций, если не хватает - очень быстро пишутся расширения, что позволяет удачно переносить view-логику в шаблон;
  • Совместимость с HF - немаловажный фактор. Часть шаблонов (например, те же пагинаторы на страницах списка заметок и кланов) можно заимствовать практически as-is;
  • Синтаксис - заимствован из Twig, это не столь важно, но все же упрощает редактирование шаблонов другим участникам команды, кто работал с twig'ом в мире php. Хотя это капля в море при выборе решения, конечно, но некоторый плюс дает;
  • - Parboiled. Опять же, это больше субъективизм, но очень нравится весьма элегантный, на мой взгляд, подход. С точки зрения потребителя никакого значения не имеет, но если хочется покопаться "под капотом" (что мы иногда и делаем), то это некоторый приятный бонус.

Upd: забыл упомянуть еще один приятный бонус - интеграция с Spring MVC. Непосредственно к c4 отношения, конечно, не имеет, но опять же вопрос унификации - удобно, когда в ряде проектов один и тот же шаблонизатор, можно эффективно повторно использовать часть шаблонов.

Velocity много не использовал, но все же думаю, что он +- решит те же задачи. По поводу синтаксиса - думаю, что примерно одинаково, все субъективно, Velocity выглядит более лаконично, но разница не столь велика, как мне кажется. К тому же, хорошая поддержка в IDE нивелирует эту особенность.

Да, и еще одна деталь, о которой я забыл упомянуть в предыдущем посте. Такой подход дает заметно больший объем на выходе (переносы строк, выравнивание и пр.). Но когда шаблон вырастает до ~100 строк, использовать подход вида "запишем все в одну строчку без отступов" (который так любят во многих решениях) уже неприемлемо. В итоге на помощь приходит HtmlCompressor - маленький, но очень полезный инструмент. В зависимости от шаблона его эффективность (сохраненный объем) может быть очень высока, статистику не собирал, но по логам примерно 30-60%. Работает очень быстро, никакого ботлнека не возникает, что тоже приятно.
Ответ
#17
Вы не поверите, но в этом году у меня так же возникла мысль: "а почему никто на Java так и не допилил сервер хроник C4?" Все команды, которые когда-либо пытались что-то сделать - опускали руки не доделав. Те, кто пытался опустить Interludе до С4 так же бросали это дело не закончив.
С полгода назад поискав что-то стоящее - ничего не нашел. А в этом году на глаза попался (совершенно случайно) форум http://java-build.ru/ и я понял - есть еще надежда, ведутся работы! Я в Java-коде толком не смыслю, но датапак (в том числе html) править умею, руки растут с нужного места. Хотел внести свою скромную лепту в этот проект, но не смог зарегистрироваться на том форуме (на мыло не приходит ссылка активации). Но думаю с доступностью PTS сервера и его датапака моя помощь врят ли понадобится.
Искренне Желаю Вам удачи и терпения в реализации этих многими любимых хроник!
Ответ
#18
ntking Написал:Вы не поверите, но в этом году у меня так же возникла мысль: "а почему никто на Java так и не допилил сервер хроник C4?" Все команды, которые когда-либо пытались что-то сделать - опускали руки не доделав. Те, кто пытался опустить Interludе до С4 так же бросали это дело не закончив.
С полгода назад поискав что-то стоящее - ничего не нашел. А в этом году на глаза попался (совершенно случайно) форум http://java-build.ru/ и я понял - есть еще надежда, ведутся работы! Я в Java-коде толком не смыслю, но датапак (в том числе html) править умею, руки растут с нужного места. Хотел внести свою скромную лепту в этот проект, но не смог зарегистрироваться на том форуме (на мыло не приходит ссылка активации). Но думаю с доступностью PTS сервера и его датапака моя помощь врят ли понадобится.
Искренне Желаю Вам удачи и терпения в реализации этих многими любимых хроник!

Опять реклама конфиг билдеров?

R7u195 - 15/01/2015
Цитата:- Игрок не может использовать какие-либо итемы если на нем висит эфект Fear

R8u2 - 23/03/2015
Цитата:- Нельзя использовать банки когда на игроке висит эффект Fear

Ну если что-то переписывать, то нормально. А так костыль на костыле.
Родился, живу и когда-нибудь умру.
Ответ
#19
Donatte, не надо палить контору! само по себе исправления подобного навыка в "23/03/2015" просто абсурдно
Ответ
#20
Donatte Написал:Опять реклама конфиг билдеров?
Не понял о чем ты, если честно, но не о какой рекламе и в мыслях небыло.
Приятно видеть, что легендарные (в моем понимании) игры не забыты и по ним ведется работа.
Даже если их надолго не хватит "ходить по граблям", будет серьезный задел для желающих продолжить это дело.
Ответ


Возможно похожие темы ...
Тема Автор Ответы Просмотры Последний пост
  Доброго времени суток (Команда + реализация) Creat1v 0 1,020 01-11-2015, 11:22 AM
Последний пост: Creat1v
  Всем доброго времени, суток есть сборка но нету где её поставить. Eligant 12 2,905 12-22-2011, 04:37 PM
Последний пост: NotSpecified
  Как закрепить моба на определенном месте. Aristocrat 1 2,280 07-18-2009, 06:22 PM
Последний пост: Voltage
  Доброго времени суток, нужне небольшая помощь или точнее сказать разъяснение MuKPOH 2 2,324 06-05-2009, 03:00 PM
Последний пост: MuKPOH

Перейти к форуму:


Пользователи, просматривающие эту тему: 2 Гость(ей)