Иметь:
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());
}
}
Код стал меньше в два раза и понимание его осталось таким же с учетом всех видов группы.
Там еще есть прекрасное полотно с расчетом експы для пати/соло игрока, в котом только группа добавляется и чуть-чуть меняется из-за этого код, но я уже и так много времени потратил на это