Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно. Вам необходимо обновить браузер или попробовать использовать другой.
Ищу человека который сможет обучить или направить в вопросе понимания кода Ла2.
Имею опыт в вебе более 5 лет.
Оплата почасовая на любую удобную для вас платежную систему, карту и так далее. Часовой пояс (GMT-7).
Для начала стоит самостоятельно изучить хотя бы основы программирования на Java, иначе вызвавшемуся помочь для начала придется именно этому учить, а отнюдь не пониманию "кода ла2".
Для начала стоит самостоятельно изучить хотя бы основы программирования на Java, иначе вызвавшемуся помочь для начала придется именно этому учить, а отнюдь не пониманию "кода ла2".
если есть опыт в 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;
}
Гайд для старта:
- В каждой сборке есть классы "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;
}
Гайд для старта:
- В каждой сборке есть классы "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;
}
Гайд для старта:
- В каждой сборке есть классы "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;
}
Ага, нашел кого спрашивать про обучение... Тут большая часть людей училась гавнокодить на примерах гавнокода из основы фри сборок которые были собраны изначально "по приколу" на коленке в туалете вместо чтения газет. Чему могут научить такие люди? Как делать правильный гавнокод?
Я сам много раз наступал на подобные грабли, изучая (и думая что так верно) структуры того как писали код в командах некоторых известных сборкоделов, и только спустя много лет траха и последующему переобучению более менее начинаешь шарить.
так что не советую учиться, чтобы потом переучиваться...
Можете мне аргументировано объяснить, почему КОД мобиуса - это говнокод?
Желательно хотя бы пару примеров, с разбором, почему так не стоит делать и как нужно?
Т.е безотносительно личности, политики комьюнити, личерства, читерства, дрочерства и прочего персонифицированного. Тупо код.
Можете мне аргументировано объяснить, почему КОД мобиуса - это говнокод?
Желательно хотя бы пару примеров, с разбором, почему так не стоит делать и как нужно?
Т.е безотносительно личности, политики комьюнити, личерства, читерства, дрочерства и прочего персонифицированного. Тупо код.
Иметь:
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 минут.
Ну тут как минимум из-за того что может в один момент использоваться для записи 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 минут.
Ну тут как минимум из-за того что может в один момент использоваться для записи 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 минут.
Ну тут как минимум из-за того что может в один момент использоваться для записи 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
{ //Ебаная скоба на всю строку
//Говнокод
} //Закрываем ебаной скобой
Ведь если к примеру писать вот так, то код нихуя работать не будет:
Как же 6лять раздражает, когда е6аной скобой занимают целую строку.
Java:
if
{ //Ебаная скоба на всю строку
//Говнокод
} //Ебаная скоба на всю строку, ведь далее мы юзаем какой-нибудь else
else
{ //Ебаная скоба на всю строку
//Говнокод
} //Закрываем ебаной скобой
Ведь если к примеру писать вот так, то код нихуя работать не будет:
На данном сайте используются файлы cookie, чтобы персонализировать содержимое и сохранить Ваш вход в систему, если Вы зарегистрируетесь.
Продолжая использовать этот сайт, Вы соглашаетесь на использование наших файлов cookie.