Научи+Обучи.

PamPam

Путник
Участник
Сообщения
29
Розыгрыши
0
Репутация
-1
Реакции
6
Баллы
30
Хроники
  1. Chaotic Throne: High Five
  2. The Chaotic Throne 3: Goddess of Destruction
  3. Goddess of Destruction Awakening
  4. Goddess of Destruction Harmony
  5. Goddess of Destruction Tauti
  6. Goddess of Destruction Glory Days
  7. Goddess of Destruction Lindvior
  8. Goddess of Destruction Epeisodion / Valiance
Всем доброго времени суток.

Ищу человека который сможет обучить или направить в вопросе понимания кода Ла2.
Имею опыт в вебе более 5 лет.
Оплата почасовая на любую удобную для вас платежную систему, карту и так далее.
Часовой пояс (GMT-7).
 

Для начала стоит самостоятельно изучить хотя бы основы программирования на Java, иначе вызвавшемуся помочь для начала придется именно этому учить, а отнюдь не пониманию "кода ла2".
 
Для начала стоит самостоятельно изучить хотя бы основы программирования на Java, иначе вызвавшемуся помочь для начала придется именно этому учить, а отнюдь не пониманию "кода ла2".
Не совсем понимаю, что вы имеете ввиду.
Я действующий java dev с опытом более 5лет ,но в Web-e ,в game dev-e опыта нет, поэтому и ищу человека.
 
если есть опыт в java, то тогда не очень понятно зачем нужен учитель.
в плане понимания кода сервера и его работы в целом обычных познаний в яве должно хватать вроде вполне.
 
если есть опыт в java, то тогда не очень понятно зачем нужен учитель.
в плане понимания кода сервера и его работы в целом обычных познаний в яве должно хватать вроде вполне.
Для более быстрого освоения .
 
Гайд для старта:
- В каждой сборке есть классы "Npc", "Creature", "WorldObject", "Player" которые хранят в себе 80% используемых методов по всей сборке.
- 10% это статические классы по типу "Util" какой-то, который хранит в себе методы по типу "Посчитай сколько будет 3 2 4 и выбери макс число" или "посчитай какой heading (куда будет смотреть) будет у NPC который был тута, а пойдет туда" или же классы которые написаны попередниками и в которых нет смысла.
- 5% это классы для работы с пакетами - крипт, декрипт, связь с логин сервером и тд.
Ну и остальное это сами пакеты - чтение / запись - методы для удобного хранения World Object и другая лабуда для удобного хранения информации.

Еще есть такие приколы как "Geoengine" но туда если и лезть - то лучше сразу смотря на реализации в разных сборках чтоб собрать в кучу все то, что там происходит.

Ну и вот пример для записи пакетов. На пример "PayBack" УИшка. Информацию о том, какие данные принимает оно можно найти или Реверсом Engine или перехватив пакет с офа и разобрав его (Бог в помощь) или Реверсом слитых ПТС серверов или некоторые из них прячутся в Interface.u в классе "UIPacket".

Описание с interface.u
C-подобный:
struct _S_EX_PAYBACK_LIST
{
  var array<_PaybackRewardSet> vRewardSet;
  var int cEventIDType;
  var int nEndDatetime;
  var int nConsumedItemclassID;
  var int nUserConsumption;
};

struct _PaybackRewardSet
{
  var array<_PaybackRewardItem> vItemList;
  var int cSetIndex;
  var int nRequirement;
  var int cReceived;
};

struct _PaybackRewardItem
{
  var int nClassID;
  var int nAmount;
};
Как будет адаптировано в Java
Java:
    public boolean write(PacketWriter packet)
    {
        OutgoingPackets.EX_PAYBACK_LIST.writeId(packet);
        packet.writeD(3); // колличество строк // string size //nSize
        for (int i = 1; i < 4; i++)
        {
            //заполняем строку //fill the string
            //start _PaybackRewardSet
            packet.writeD(3); // колличество предметов в строке // item count in field (max 3)//i
            for (int j = 1; j < 4; j++)

            //start _PaybackRewardItem
            //заполняем предметы //fill the items
            {
                packet.writeD(57); // предмет // item id
                packet.writeD(1); // количество // count
            }
            //закончили заполнять предметы // item fill end
            //end _PaybackRewardItem

            packet.writeC(i); // cSetIndex
            packet.writeD(5); // сколько нужно потратить // how much you need //nRequirement
            packet.writeC(0x00); //получена ли награда // check reward receiving ( 0x00 - no, 0x01 - yes ) //cReceived

            //закончили заполнять строку // end string fill
            //end _PaybackRewardSet
        }
        packet.writeC(_EventID);    //cEventIDType
        packet.writeD(10);  //nEndDatetime
        packet.writeD(91663);  //Итем, который должен светиться вверху // display item //nConsumedItemclassID
        packet.writeD(7);  //Сколько всего было потрачено итемов // summary item spend //nUserConsumption
        return true;
    }
Что за что отвечает:
1684864332603.png
 
Гайд для старта:
- В каждой сборке есть классы "Npc", "Creature", "WorldObject", "Player" которые хранят в себе 80% используемых методов по всей сборке.
- 10% это статические классы по типу "Util" какой-то, который хранит в себе методы по типу "Посчитай сколько будет 3 2 4 и выбери макс число" или "посчитай какой heading (куда будет смотреть) будет у NPC который был тута, а пойдет туда" или же классы которые написаны попередниками и в которых нет смысла.
- 5% это классы для работы с пакетами - крипт, декрипт, связь с логин сервером и тд.
Ну и остальное это сами пакеты - чтение / запись - методы для удобного хранения World Object и другая лабуда для удобного хранения информации.

Еще есть такие приколы как "Geoengine" но туда если и лезть - то лучше сразу смотря на реализации в разных сборках чтоб собрать в кучу все то, что там происходит.

Ну и вот пример для записи пакетов. На пример "PayBack" УИшка. Информацию о том, какие данные принимает оно можно найти или Реверсом Engine или перехватив пакет с офа и разобрав его (Бог в помощь) или Реверсом слитых ПТС серверов или некоторые из них прячутся в Interface.u в классе "UIPacket".

Описание с interface.u
C-подобный:
struct _S_EX_PAYBACK_LIST
{
  var array<_PaybackRewardSet> vRewardSet;
  var int cEventIDType;
  var int nEndDatetime;
  var int nConsumedItemclassID;
  var int nUserConsumption;
};

struct _PaybackRewardSet
{
  var array<_PaybackRewardItem> vItemList;
  var int cSetIndex;
  var int nRequirement;
  var int cReceived;
};

struct _PaybackRewardItem
{
  var int nClassID;
  var int nAmount;
};
Как будет адаптировано в Java
Java:
    public boolean write(PacketWriter packet)
    {
        OutgoingPackets.EX_PAYBACK_LIST.writeId(packet);
        packet.writeD(3); // колличество строк // string size //nSize
        for (int i = 1; i < 4; i++)
        {
            //заполняем строку //fill the string
            //start _PaybackRewardSet
            packet.writeD(3); // колличество предметов в строке // item count in field (max 3)//i
            for (int j = 1; j < 4; j++)

            //start _PaybackRewardItem
            //заполняем предметы //fill the items
            {
                packet.writeD(57); // предмет // item id
                packet.writeD(1); // количество // count
            }
            //закончили заполнять предметы // item fill end
            //end _PaybackRewardItem

            packet.writeC(i); // cSetIndex
            packet.writeD(5); // сколько нужно потратить // how much you need //nRequirement
            packet.writeC(0x00); //получена ли награда // check reward receiving ( 0x00 - no, 0x01 - yes ) //cReceived

            //закончили заполнять строку // end string fill
            //end _PaybackRewardSet
        }
        packet.writeC(_EventID);    //cEventIDType
        packet.writeD(10);  //nEndDatetime
        packet.writeD(91663);  //Итем, который должен светиться вверху // display item //nConsumedItemclassID
        packet.writeD(7);  //Сколько всего было потрачено итемов // summary item spend //nUserConsumption
        return true;
    }
Что за что отвечает:
Посмотреть вложение 52499
Благодарю. Часть из этого я уже сам понял ,но большинство информации было полезно.
 
Гайд для старта:
- В каждой сборке есть классы "Npc", "Creature", "WorldObject", "Player" которые хранят в себе 80% используемых методов по всей сборке.
- 10% это статические классы по типу "Util" какой-то, который хранит в себе методы по типу "Посчитай сколько будет 3 2 4 и выбери макс число" или "посчитай какой heading (куда будет смотреть) будет у NPC который был тута, а пойдет туда" или же классы которые написаны попередниками и в которых нет смысла.
- 5% это классы для работы с пакетами - крипт, декрипт, связь с логин сервером и тд.
Ну и остальное это сами пакеты - чтение / запись - методы для удобного хранения World Object и другая лабуда для удобного хранения информации.

Еще есть такие приколы как "Geoengine" но туда если и лезть - то лучше сразу смотря на реализации в разных сборках чтоб собрать в кучу все то, что там происходит.

Ну и вот пример для записи пакетов. На пример "PayBack" УИшка. Информацию о том, какие данные принимает оно можно найти или Реверсом Engine или перехватив пакет с офа и разобрав его (Бог в помощь) или Реверсом слитых ПТС серверов или некоторые из них прячутся в Interface.u в классе "UIPacket".

Описание с interface.u
C-подобный:
struct _S_EX_PAYBACK_LIST
{
  var array<_PaybackRewardSet> vRewardSet;
  var int cEventIDType;
  var int nEndDatetime;
  var int nConsumedItemclassID;
  var int nUserConsumption;
};

struct _PaybackRewardSet
{
  var array<_PaybackRewardItem> vItemList;
  var int cSetIndex;
  var int nRequirement;
  var int cReceived;
};

struct _PaybackRewardItem
{
  var int nClassID;
  var int nAmount;
};
Как будет адаптировано в Java
Java:
    public boolean write(PacketWriter packet)
    {
        OutgoingPackets.EX_PAYBACK_LIST.writeId(packet);
        packet.writeD(3); // колличество строк // string size //nSize
        for (int i = 1; i < 4; i++)
        {
            //заполняем строку //fill the string
            //start _PaybackRewardSet
            packet.writeD(3); // колличество предметов в строке // item count in field (max 3)//i
            for (int j = 1; j < 4; j++)

            //start _PaybackRewardItem
            //заполняем предметы //fill the items
            {
                packet.writeD(57); // предмет // item id
                packet.writeD(1); // количество // count
            }
            //закончили заполнять предметы // item fill end
            //end _PaybackRewardItem

            packet.writeC(i); // cSetIndex
            packet.writeD(5); // сколько нужно потратить // how much you need //nRequirement
            packet.writeC(0x00); //получена ли награда // check reward receiving ( 0x00 - no, 0x01 - yes ) //cReceived

            //закончили заполнять строку // end string fill
            //end _PaybackRewardSet
        }
        packet.writeC(_EventID);    //cEventIDType
        packet.writeD(10);  //nEndDatetime
        packet.writeD(91663);  //Итем, который должен светиться вверху // display item //nConsumedItemclassID
        packet.writeD(7);  //Сколько всего было потрачено итемов // summary item spend //nUserConsumption
        return true;
    }
Что за что отвечает:
Посмотреть вложение 52499
Прикольно. Даже я начинающий всё понял.
 
Гайд для старта:
- В каждой сборке есть классы "Npc", "Creature", "WorldObject", "Player" которые хранят в себе 80% используемых методов по всей сборке.
- 10% это статические классы по типу "Util" какой-то, который хранит в себе методы по типу "Посчитай сколько будет 3 2 4 и выбери макс число" или "посчитай какой heading (куда будет смотреть) будет у NPC который был тута, а пойдет туда" или же классы которые написаны попередниками и в которых нет смысла.
- 5% это классы для работы с пакетами - крипт, декрипт, связь с логин сервером и тд.
Ну и остальное это сами пакеты - чтение / запись - методы для удобного хранения World Object и другая лабуда для удобного хранения информации.

Еще есть такие приколы как "Geoengine" но туда если и лезть - то лучше сразу смотря на реализации в разных сборках чтоб собрать в кучу все то, что там происходит.

Ну и вот пример для записи пакетов. На пример "PayBack" УИшка. Информацию о том, какие данные принимает оно можно найти или Реверсом Engine или перехватив пакет с офа и разобрав его (Бог в помощь) или Реверсом слитых ПТС серверов или некоторые из них прячутся в Interface.u в классе "UIPacket".

Описание с interface.u
C-подобный:
struct _S_EX_PAYBACK_LIST
{
  var array<_PaybackRewardSet> vRewardSet;
  var int cEventIDType;
  var int nEndDatetime;
  var int nConsumedItemclassID;
  var int nUserConsumption;
};

struct _PaybackRewardSet
{
  var array<_PaybackRewardItem> vItemList;
  var int cSetIndex;
  var int nRequirement;
  var int cReceived;
};

struct _PaybackRewardItem
{
  var int nClassID;
  var int nAmount;
};
Как будет адаптировано в Java
Java:
    public boolean write(PacketWriter packet)
    {
        OutgoingPackets.EX_PAYBACK_LIST.writeId(packet);
        packet.writeD(3); // колличество строк // string size //nSize
        for (int i = 1; i < 4; i++)
        {
            //заполняем строку //fill the string
            //start _PaybackRewardSet
            packet.writeD(3); // колличество предметов в строке // item count in field (max 3)//i
            for (int j = 1; j < 4; j++)

            //start _PaybackRewardItem
            //заполняем предметы //fill the items
            {
                packet.writeD(57); // предмет // item id
                packet.writeD(1); // количество // count
            }
            //закончили заполнять предметы // item fill end
            //end _PaybackRewardItem

            packet.writeC(i); // cSetIndex
            packet.writeD(5); // сколько нужно потратить // how much you need //nRequirement
            packet.writeC(0x00); //получена ли награда // check reward receiving ( 0x00 - no, 0x01 - yes ) //cReceived

            //закончили заполнять строку // end string fill
            //end _PaybackRewardSet
        }
        packet.writeC(_EventID);    //cEventIDType
        packet.writeD(10);  //nEndDatetime
        packet.writeD(91663);  //Итем, который должен светиться вверху // display item //nConsumedItemclassID
        packet.writeD(7);  //Сколько всего было потрачено итемов // summary item spend //nUserConsumption
        return true;
    }
Что за что отвечает:
Посмотреть вложение 52499
Я считаю пора курсы обучающие продавать.
 
Ага, нашел кого спрашивать про обучение... Тут большая часть людей училась гавнокодить на примерах гавнокода из основы фри сборок которые были собраны изначально "по приколу" на коленке в туалете вместо чтения газет. Чему могут научить такие люди? Как делать правильный гавнокод? :)
Я сам много раз наступал на подобные грабли, изучая (и думая что так верно) структуры того как писали код в командах некоторых известных сборкоделов, и только спустя много лет траха и последующему переобучению более менее начинаешь шарить.

так что не советую учиться, чтобы потом переучиваться...
 
Мобиус считается говнокодом в соверменном мире?:Huh:
 
  • Мне нравится
Реакции: jois
Можете мне аргументировано объяснить, почему КОД мобиуса - это говнокод?
Желательно хотя бы пару примеров, с разбором, почему так не стоит делать и как нужно?
Т.е безотносительно личности, политики комьюнити, личерства, читерства, дрочерства и прочего персонифицированного. Тупо код.
 
Можете мне аргументировано объяснить, почему КОД мобиуса - это говнокод?
Желательно хотя бы пару примеров, с разбором, почему так не стоит делать и как нужно?
Т.е безотносительно личности, политики комьюнити, личерства, читерства, дрочерства и прочего персонифицированного. Тупо код.
Иметь:
1. Закешированные ИД - ИМЯ персонажа.
2. Закешированная информация рейтинга;
3. Закешированная информация о всех мемберах клана / альянса;
(Там еще что-то было).
При этом восстанавливать информацию для некоторых пакетов / классов каждый раз когда требуется и базы данных. Почему бы не сделать это все в одном классе если информация зачастую о информации игроков (имя, левел, клан, альянса) нужны достаточно часто.

Инвентарь, который в угоду 10 милисекунд, был переделан и забит (х... забит) таким образом, что его можно задюпать из-за отстутсвия Concurrent.lock (ВРАНЬЕ, НЕТ ТАКОГО, Я НЕ НАШЕЛ (сарказм)).

Реализация инстанс зон (ну ок, я тут тоже приложил свою руку, но только в Храме Валакаса, ибо я его сделал на половину, а потом как-то и не потребовалось, а он просто взял и переназвал некоторые переменные) на очень низком уровне.
Что можно выделить из низкого уровня:
* Храм Валакаса - багается если просто пробежать и убить печать;
* Этис Ван Этина (соло) - просто прекрасный пример как не нужно делать - мобы стоят абы как та и просто страшно смотреть если играть с офа (ведь делали типо под ЦЦ версию);
* Таверны просто не работают. Фрею брать - нет дополнительных НПС, которые могут появится и быть с лутом. Таути - можна забагать если убить после печати Ангела до того, как он договорит и телепортирует. Остальных таверн просто не завезли.
* Убежище Бранштейна - стоят абы как, нету ивент тригеров (не понятно что в зоне происходит), итемы, которые внутри выдаются, не работают, у НПСов нет АИшки и тд...

Возвращаясь к коду...
= TimedHuntingZone - самая прекрасна реализация Сессионных Зон. Сделано по всем заветам, которых мобиус придерживается - економия на спичках и еще каким-то...
По итогу что мы получили:
* Жалобы на то, что народ через дырки в геодате или просто в текстурах пробивается в сесионную зону, ведь ради економии мы их сделаем в обычном мире (нас даже не смутило наличие отдельных иснстанс зон внути ДАТ файлов) (сейчас правда это прикрутили, но такая жесть была, я помню какие костыли ПИЛИЛИСЬ когда я пытался 3 куба ТОИ запихнуть в одну зону);
* Не нужные скрипты, которые проверяют есть ли ты в зоне или нет тебя в зоне для работы с НПСами и телепорта по зонам;
* Забитый болт на то что клиент багается время от времени при выходе с сессионных зон;

И опять, вренемся к коду.
Есть у нас такой прикол как "DailyTaskManager", в котором есть метод "resetClanBonus" (который там где он есть уже не нужен после 228 протокола), но мы не будем его чистить.
В чем его прелесть?
Java:
    private void resetClanBonus()
    {
        ClanTable.getInstance().getClans().forEach(Clan::resetClanBonus);
        LOGGER.info("Daily clan bonus has been resetted.");
    }
Выглядит просто, да? Просто берем все кланы и сбрасываем им бонус ежедневный :)
Давай посмотрим дальше...
Java:
    public void resetClanBonus()
    {
        // Save current state
        getVariables().set("PREVIOUS_MAX_ONLINE_PLAYERS", getMaxOnlineMembers());
        getVariables().set("PREVIOUS_HUNTING_POINTS", getHuntingPoints());
        
        // Reset
        _members.values().forEach(ClanMember::resetBonus);
        getVariables().remove("HUNTING_POINTS");
        
        // force store
        getVariables().storeMe();
        
        // Send Packet
        broadcastToOnlineMembers(ExPledgeBonusMarkReset.STATIC_PACKET);
    }
Тут уже переменные сбрасываются и дальше идет сброс для каждого игрока клана?
Java:
    public void resetBonus()
    {
        _onlineTime = 0;
        final PlayerVariables vars = getVariables();
        vars.set("CLAIMED_CLAN_REWARDS", 0);
        vars.storeMe();
    }
А, ну и просто идет сброс переменной и сохранение в базу.
Ничего такого, никто не думал о том, что на лайве (где не 2 человека и 10 персонажей в офлайне) отработка данного метода может занимать до 15 минут.

Еще у нас есть вот такое красивый метод...
Код:
    private void resetClanContributionList()
    {
        for (Clan clan : ClanTable.getInstance().getClans())
        {
            clan.getVariables().deleteWeeklyContribution();
        }
    }
    
        public boolean deleteWeeklyContribution()
    {
        try (Connection con = DatabaseFactory.getConnection())
        {
            // Clear previous entries.
            try (PreparedStatement st = con.prepareStatement(DELETE_WEAKLY_QUERY))
            {
                st.setInt(1, _objectId);
                st.execute();
            }
            
            // Clear all entries
            getSet().entrySet().stream().filter(it -> it.getKey().startsWith("CONTRIBUTION_WEEKLY_")).collect(Collectors.toList()).forEach(it -> getSet().remove(it.getKey()));
        }
        catch (Exception e)
        {
            LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't delete variables for: " + _objectId, e);
            return false;
        }
        return true;
    }
Ну тут как минимум из-за того что может в один момент использоваться для записи clanVariables - может просто ошибка выбиться и сбросится только для некоторых.
Та и как по мне - тут очень много кода, который пишется огромными полотнами, по нему очень сложно ориентироваться. Я пытаюсь перебраться и сделать 1 метод - на 1 экран, чтоб видел все что и где происходит.
Ну а полотен там хватает.
Еще хватает излишнего кода (дублирующего к примеру).
Есть у нас Attackable - calculateRewards.
Java:
                final Player player = (maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker.getActingPlayer();
                broadcastPacket(new SystemMessage(SystemMessageId.CONGRATULATIONS_YOUR_RAID_WAS_SUCCESSFUL));
                final int raidbossPoints = (int) (getTemplate().getRaidPoints() * Config.RATE_RAIDBOSS_POINTS);
                final Party party = player.getParty();
                if (party != null)
                {
                    final CommandChannel command = party.getCommandChannel();
                    final List<Player> members = new ArrayList<>();
                    if (command != null)
                    {
                        for (Player p : command.getMembers())
                        {
                            if (p.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                            {
                                members.add(p);
                            }
                        }
                    }
                    else
                    {
                        for (Player p : player.getParty().getMembers())
                        {
                            if (p.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                            {
                                members.add(p);
                            }
                        }
                    }
                    
                    members.forEach(p ->
                    {
                        final int points = (int) (Math.max(raidbossPoints / members.size(), 1) * p.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                        p.increaseRaidbossPoints(points);
                        p.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                        if (p.isNoble())
                        {
                            Hero.getInstance().setRBkilled(p.getObjectId(), getId());
                        }
                    });
                }
                else
                {
                    final int points = (int) (Math.max(raidbossPoints, 1) * player.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                    player.increaseRaidbossPoints(points);
                    player.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                    if (player.isNoble())
                    {
                        Hero.getInstance().setRBkilled(player.getObjectId(), getId());
                    }
                }
            }
Красивый код, не так ли? Но почему-бы не сделать вот так?
Java:
                broadcastPacket(new SystemMessage(SystemMessageId.CONGRATULATIONS_YOUR_RAID_WAS_SUCCESSFUL));
                final Player player = (maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker.getActingPlayer();
                final int raidbossPoints = (int) (getTemplate().getRaidPoints() * Config.RATE_RAIDBOSS_POINTS);
                final AbstractPlayerGroup group = player.isInCommandChannel() ? player.getCommandChannel() : player.getParty();
                final List<Player> members = group == null ? List.of(player) : new ArrayList<>();
                if (group != null)
                {
                    for (Player member : group.getMembers())
                    {
                        if (member.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                        {
                            members.add(member);
                        }
                    }
                }
                for (Player member : members)
                {
                    final int points = (int) (Math.max(raidbossPoints / members.size(), 1) * member.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                    member.increaseRaidbossPoints(points);
                    member.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                    if (member.isNoble())
                    {
                        Hero.getInstance().setRBkilled(member.getObjectId(), getId());
                    }
                }
Код стал меньше в два раза и понимание его осталось таким же с учетом всех видов группы.
Там еще есть прекрасное полотно с расчетом експы для пати/соло игрока, в котом только группа добавляется и чуть-чуть меняется из-за этого код, но я уже и так много времени потратил на это :)
 
Иметь:
1. Закешированные ИД - ИМЯ персонажа.
2. Закешированная информация рейтинга;
3. Закешированная информация о всех мемберах клана / альянса;
(Там еще что-то было).
При этом восстанавливать информацию для некоторых пакетов / классов каждый раз когда требуется и базы данных. Почему бы не сделать это все в одном классе если информация зачастую о информации игроков (имя, левел, клан, альянса) нужны достаточно часто.

Инвентарь, который в угоду 10 милисекунд, был переделан и забит (х... забит) таким образом, что его можно задюпать из-за отстутсвия Concurrent.lock (ВРАНЬЕ, НЕТ ТАКОГО, Я НЕ НАШЕЛ (сарказм)).

Реализация инстанс зон (ну ок, я тут тоже приложил свою руку, но только в Храме Валакаса, ибо я его сделал на половину, а потом как-то и не потребовалось, а он просто взял и переназвал некоторые переменные) на очень низком уровне.
Что можно выделить из низкого уровня:
* Храм Валакаса - багается если просто пробежать и убить печать;
* Этис Ван Этина (соло) - просто прекрасный пример как не нужно делать - мобы стоят абы как та и просто страшно смотреть если играть с офа (ведь делали типо под ЦЦ версию);
* Таверны просто не работают. Фрею брать - нет дополнительных НПС, которые могут появится и быть с лутом. Таути - можна забагать если убить после печати Ангела до того, как он договорит и телепортирует. Остальных таверн просто не завезли.
* Убежище Бранштейна - стоят абы как, нету ивент тригеров (не понятно что в зоне происходит), итемы, которые внутри выдаются, не работают, у НПСов нет АИшки и тд...

Возвращаясь к коду...
= TimedHuntingZone - самая прекрасна реализация Сессионных Зон. Сделано по всем заветам, которых мобиус придерживается - економия на спичках и еще каким-то...
По итогу что мы получили:
* Жалобы на то, что народ через дырки в геодате или просто в текстурах пробивается в сесионную зону, ведь ради економии мы их сделаем в обычном мире (нас даже не смутило наличие отдельных иснстанс зон внути ДАТ файлов) (сейчас правда это прикрутили, но такая жесть была, я помню какие костыли ПИЛИЛИСЬ когда я пытался 3 куба ТОИ запихнуть в одну зону);
* Не нужные скрипты, которые проверяют есть ли ты в зоне или нет тебя в зоне для работы с НПСами и телепорта по зонам;
* Забитый болт на то что клиент багается время от времени при выходе с сессионных зон;

И опять, вренемся к коду.
Есть у нас такой прикол как "DailyTaskManager", в котором есть метод "resetClanBonus" (который там где он есть уже не нужен после 228 протокола), но мы не будем его чистить.
В чем его прелесть?
Java:
    private void resetClanBonus()
    {
        ClanTable.getInstance().getClans().forEach(Clan::resetClanBonus);
        LOGGER.info("Daily clan bonus has been resetted.");
    }
Выглядит просто, да? Просто берем все кланы и сбрасываем им бонус ежедневный :)
Давай посмотрим дальше...
Java:
    public void resetClanBonus()
    {
        // Save current state
        getVariables().set("PREVIOUS_MAX_ONLINE_PLAYERS", getMaxOnlineMembers());
        getVariables().set("PREVIOUS_HUNTING_POINTS", getHuntingPoints());
       
        // Reset
        _members.values().forEach(ClanMember::resetBonus);
        getVariables().remove("HUNTING_POINTS");
       
        // force store
        getVariables().storeMe();
       
        // Send Packet
        broadcastToOnlineMembers(ExPledgeBonusMarkReset.STATIC_PACKET);
    }
Тут уже переменные сбрасываются и дальше идет сброс для каждого игрока клана?
Java:
    public void resetBonus()
    {
        _onlineTime = 0;
        final PlayerVariables vars = getVariables();
        vars.set("CLAIMED_CLAN_REWARDS", 0);
        vars.storeMe();
    }
А, ну и просто идет сброс переменной и сохранение в базу.
Ничего такого, никто не думал о том, что на лайве (где не 2 человека и 10 персонажей в офлайне) отработка данного метода может занимать до 15 минут.

Еще у нас есть вот такое красивый метод...
Код:
    private void resetClanContributionList()
    {
        for (Clan clan : ClanTable.getInstance().getClans())
        {
            clan.getVariables().deleteWeeklyContribution();
        }
    }
   
        public boolean deleteWeeklyContribution()
    {
        try (Connection con = DatabaseFactory.getConnection())
        {
            // Clear previous entries.
            try (PreparedStatement st = con.prepareStatement(DELETE_WEAKLY_QUERY))
            {
                st.setInt(1, _objectId);
                st.execute();
            }
           
            // Clear all entries
            getSet().entrySet().stream().filter(it -> it.getKey().startsWith("CONTRIBUTION_WEEKLY_")).collect(Collectors.toList()).forEach(it -> getSet().remove(it.getKey()));
        }
        catch (Exception e)
        {
            LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't delete variables for: " + _objectId, e);
            return false;
        }
        return true;
    }
Ну тут как минимум из-за того что может в один момент использоваться для записи clanVariables - может просто ошибка выбиться и сбросится только для некоторых.
Та и как по мне - тут очень много кода, который пишется огромными полотнами, по нему очень сложно ориентироваться. Я пытаюсь перебраться и сделать 1 метод - на 1 экран, чтоб видел все что и где происходит.
Ну а полотен там хватает.
Еще хватает излишнего кода (дублирующего к примеру).
Есть у нас Attackable - calculateRewards.
Java:
                final Player player = (maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker.getActingPlayer();
                broadcastPacket(new SystemMessage(SystemMessageId.CONGRATULATIONS_YOUR_RAID_WAS_SUCCESSFUL));
                final int raidbossPoints = (int) (getTemplate().getRaidPoints() * Config.RATE_RAIDBOSS_POINTS);
                final Party party = player.getParty();
                if (party != null)
                {
                    final CommandChannel command = party.getCommandChannel();
                    final List<Player> members = new ArrayList<>();
                    if (command != null)
                    {
                        for (Player p : command.getMembers())
                        {
                            if (p.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                            {
                                members.add(p);
                            }
                        }
                    }
                    else
                    {
                        for (Player p : player.getParty().getMembers())
                        {
                            if (p.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                            {
                                members.add(p);
                            }
                        }
                    }
                   
                    members.forEach(p ->
                    {
                        final int points = (int) (Math.max(raidbossPoints / members.size(), 1) * p.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                        p.increaseRaidbossPoints(points);
                        p.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                        if (p.isNoble())
                        {
                            Hero.getInstance().setRBkilled(p.getObjectId(), getId());
                        }
                    });
                }
                else
                {
                    final int points = (int) (Math.max(raidbossPoints, 1) * player.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                    player.increaseRaidbossPoints(points);
                    player.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                    if (player.isNoble())
                    {
                        Hero.getInstance().setRBkilled(player.getObjectId(), getId());
                    }
                }
            }
Красивый код, не так ли? Но почему-бы не сделать вот так?
Java:
                broadcastPacket(new SystemMessage(SystemMessageId.CONGRATULATIONS_YOUR_RAID_WAS_SUCCESSFUL));
                final Player player = (maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker.getActingPlayer();
                final int raidbossPoints = (int) (getTemplate().getRaidPoints() * Config.RATE_RAIDBOSS_POINTS);
                final AbstractPlayerGroup group = player.isInCommandChannel() ? player.getCommandChannel() : player.getParty();
                final List<Player> members = group == null ? List.of(player) : new ArrayList<>();
                if (group != null)
                {
                    for (Player member : group.getMembers())
                    {
                        if (member.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                        {
                            members.add(member);
                        }
                    }
                }
                for (Player member : members)
                {
                    final int points = (int) (Math.max(raidbossPoints / members.size(), 1) * member.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                    member.increaseRaidbossPoints(points);
                    member.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                    if (member.isNoble())
                    {
                        Hero.getInstance().setRBkilled(member.getObjectId(), getId());
                    }
                }
Код стал меньше в два раза и понимание его осталось таким же с учетом всех видов группы.
Там еще есть прекрасное полотно с расчетом експы для пати/соло игрока, в котом только группа добавляется и чуть-чуть меняется из-за этого код, но я уже и так много времени потратил на это :)
Ощущение, что тебя мобиус байтит на разбор....
 
У мобиуса на лайв серверах при онлайне 50+ периодически багается DailyTaskManager() и не сбрасывает счетчик зон или магазина. Как повезёт уж хихихи. Дырок много. Но у скриптов еще хуже.
 
ну как минимум заветы из книги чистый код он точно не соблюдает:). А так мне кажется хотя бы так........
 
Иметь:
1. Закешированные ИД - ИМЯ персонажа.
2. Закешированная информация рейтинга;
3. Закешированная информация о всех мемберах клана / альянса;
(Там еще что-то было).
При этом восстанавливать информацию для некоторых пакетов / классов каждый раз когда требуется и базы данных. Почему бы не сделать это все в одном классе если информация зачастую о информации игроков (имя, левел, клан, альянса) нужны достаточно часто.

Инвентарь, который в угоду 10 милисекунд, был переделан и забит (х... забит) таким образом, что его можно задюпать из-за отстутсвия Concurrent.lock (ВРАНЬЕ, НЕТ ТАКОГО, Я НЕ НАШЕЛ (сарказм)).

Реализация инстанс зон (ну ок, я тут тоже приложил свою руку, но только в Храме Валакаса, ибо я его сделал на половину, а потом как-то и не потребовалось, а он просто взял и переназвал некоторые переменные) на очень низком уровне.
Что можно выделить из низкого уровня:
* Храм Валакаса - багается если просто пробежать и убить печать;
* Этис Ван Этина (соло) - просто прекрасный пример как не нужно делать - мобы стоят абы как та и просто страшно смотреть если играть с офа (ведь делали типо под ЦЦ версию);
* Таверны просто не работают. Фрею брать - нет дополнительных НПС, которые могут появится и быть с лутом. Таути - можна забагать если убить после печати Ангела до того, как он договорит и телепортирует. Остальных таверн просто не завезли.
* Убежище Бранштейна - стоят абы как, нету ивент тригеров (не понятно что в зоне происходит), итемы, которые внутри выдаются, не работают, у НПСов нет АИшки и тд...

Возвращаясь к коду...
= TimedHuntingZone - самая прекрасна реализация Сессионных Зон. Сделано по всем заветам, которых мобиус придерживается - економия на спичках и еще каким-то...
По итогу что мы получили:
* Жалобы на то, что народ через дырки в геодате или просто в текстурах пробивается в сесионную зону, ведь ради економии мы их сделаем в обычном мире (нас даже не смутило наличие отдельных иснстанс зон внути ДАТ файлов) (сейчас правда это прикрутили, но такая жесть была, я помню какие костыли ПИЛИЛИСЬ когда я пытался 3 куба ТОИ запихнуть в одну зону);
* Не нужные скрипты, которые проверяют есть ли ты в зоне или нет тебя в зоне для работы с НПСами и телепорта по зонам;
* Забитый болт на то что клиент багается время от времени при выходе с сессионных зон;

И опять, вренемся к коду.
Есть у нас такой прикол как "DailyTaskManager", в котором есть метод "resetClanBonus" (который там где он есть уже не нужен после 228 протокола), но мы не будем его чистить.
В чем его прелесть?
Java:
    private void resetClanBonus()
    {
        ClanTable.getInstance().getClans().forEach(Clan::resetClanBonus);
        LOGGER.info("Daily clan bonus has been resetted.");
    }
Выглядит просто, да? Просто берем все кланы и сбрасываем им бонус ежедневный :)
Давай посмотрим дальше...
Java:
    public void resetClanBonus()
    {
        // Save current state
        getVariables().set("PREVIOUS_MAX_ONLINE_PLAYERS", getMaxOnlineMembers());
        getVariables().set("PREVIOUS_HUNTING_POINTS", getHuntingPoints());
     
        // Reset
        _members.values().forEach(ClanMember::resetBonus);
        getVariables().remove("HUNTING_POINTS");
     
        // force store
        getVariables().storeMe();
     
        // Send Packet
        broadcastToOnlineMembers(ExPledgeBonusMarkReset.STATIC_PACKET);
    }
Тут уже переменные сбрасываются и дальше идет сброс для каждого игрока клана?
Java:
    public void resetBonus()
    {
        _onlineTime = 0;
        final PlayerVariables vars = getVariables();
        vars.set("CLAIMED_CLAN_REWARDS", 0);
        vars.storeMe();
    }
А, ну и просто идет сброс переменной и сохранение в базу.
Ничего такого, никто не думал о том, что на лайве (где не 2 человека и 10 персонажей в офлайне) отработка данного метода может занимать до 15 минут.

Еще у нас есть вот такое красивый метод...
Код:
    private void resetClanContributionList()
    {
        for (Clan clan : ClanTable.getInstance().getClans())
        {
            clan.getVariables().deleteWeeklyContribution();
        }
    }
 
        public boolean deleteWeeklyContribution()
    {
        try (Connection con = DatabaseFactory.getConnection())
        {
            // Clear previous entries.
            try (PreparedStatement st = con.prepareStatement(DELETE_WEAKLY_QUERY))
            {
                st.setInt(1, _objectId);
                st.execute();
            }
         
            // Clear all entries
            getSet().entrySet().stream().filter(it -> it.getKey().startsWith("CONTRIBUTION_WEEKLY_")).collect(Collectors.toList()).forEach(it -> getSet().remove(it.getKey()));
        }
        catch (Exception e)
        {
            LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't delete variables for: " + _objectId, e);
            return false;
        }
        return true;
    }
Ну тут как минимум из-за того что может в один момент использоваться для записи clanVariables - может просто ошибка выбиться и сбросится только для некоторых.
Та и как по мне - тут очень много кода, который пишется огромными полотнами, по нему очень сложно ориентироваться. Я пытаюсь перебраться и сделать 1 метод - на 1 экран, чтоб видел все что и где происходит.
Ну а полотен там хватает.
Еще хватает излишнего кода (дублирующего к примеру).
Есть у нас Attackable - calculateRewards.
Java:
                final Player player = (maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker.getActingPlayer();
                broadcastPacket(new SystemMessage(SystemMessageId.CONGRATULATIONS_YOUR_RAID_WAS_SUCCESSFUL));
                final int raidbossPoints = (int) (getTemplate().getRaidPoints() * Config.RATE_RAIDBOSS_POINTS);
                final Party party = player.getParty();
                if (party != null)
                {
                    final CommandChannel command = party.getCommandChannel();
                    final List<Player> members = new ArrayList<>();
                    if (command != null)
                    {
                        for (Player p : command.getMembers())
                        {
                            if (p.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                            {
                                members.add(p);
                            }
                        }
                    }
                    else
                    {
                        for (Player p : player.getParty().getMembers())
                        {
                            if (p.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                            {
                                members.add(p);
                            }
                        }
                    }
                 
                    members.forEach(p ->
                    {
                        final int points = (int) (Math.max(raidbossPoints / members.size(), 1) * p.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                        p.increaseRaidbossPoints(points);
                        p.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                        if (p.isNoble())
                        {
                            Hero.getInstance().setRBkilled(p.getObjectId(), getId());
                        }
                    });
                }
                else
                {
                    final int points = (int) (Math.max(raidbossPoints, 1) * player.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                    player.increaseRaidbossPoints(points);
                    player.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                    if (player.isNoble())
                    {
                        Hero.getInstance().setRBkilled(player.getObjectId(), getId());
                    }
                }
            }
Красивый код, не так ли? Но почему-бы не сделать вот так?
Java:
                broadcastPacket(new SystemMessage(SystemMessageId.CONGRATULATIONS_YOUR_RAID_WAS_SUCCESSFUL));
                final Player player = (maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker.getActingPlayer();
                final int raidbossPoints = (int) (getTemplate().getRaidPoints() * Config.RATE_RAIDBOSS_POINTS);
                final AbstractPlayerGroup group = player.isInCommandChannel() ? player.getCommandChannel() : player.getParty();
                final List<Player> members = group == null ? List.of(player) : new ArrayList<>();
                if (group != null)
                {
                    for (Player member : group.getMembers())
                    {
                        if (member.calculateDistance3D(this) < Config.ALT_PARTY_RANGE)
                        {
                            members.add(member);
                        }
                    }
                }
                for (Player member : members)
                {
                    final int points = (int) (Math.max(raidbossPoints / members.size(), 1) * member.getStat().getValue(Stat.BONUS_RAID_POINTS, 1));
                    member.increaseRaidbossPoints(points);
                    member.sendPacket(new SystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_RAID_POINT_S).addInt(points));
                    if (member.isNoble())
                    {
                        Hero.getInstance().setRBkilled(member.getObjectId(), getId());
                    }
                }
Код стал меньше в два раза и понимание его осталось таким же с учетом всех видов группы.
Там еще есть прекрасное полотно с расчетом експы для пати/соло игрока, в котом только группа добавляется и чуть-чуть меняется из-за этого код, но я уже и так много времени потратил на это :)
Как же 6лять раздражает, когда е6аной скобой занимают целую строку.
Java:
if
{ //Ебаная скоба на всю строку
//Говнокод
} //Ебаная скоба на всю строку, ведь далее мы юзаем какой-нибудь else
else
{ //Ебаная скоба на всю строку
//Говнокод
} //Закрываем ебаной скобой

Ведь если к примеру писать вот так, то код нихуя работать не будет:
Java:
if {
//Говнокод
} else {
//Говнокод
}
 
  • Мне нравится
Реакции: KID
Как же 6лять раздражает, когда е6аной скобой занимают целую строку.
Java:
if
{ //Ебаная скоба на всю строку
//Говнокод
} //Ебаная скоба на всю строку, ведь далее мы юзаем какой-нибудь else
else
{ //Ебаная скоба на всю строку
//Говнокод
} //Закрываем ебаной скобой

Ведь если к примеру писать вот так, то код нихуя работать не будет:
Java:
if {
//Говнокод
} else {
//Говнокод
}
ну е... зачем скобки вообще использовать?
Java:
if
    code; code; code; code;
else
    for (;;;)
        code; code; code; code;
Вообще если по теме - l2jserver как и другие потом сборки которые пошли от них (как и l2jmobius) использует Allman (C#, C++) стиль для написания кода.
 
Назад
Сверху Снизу