/* * The ManaPlus Client * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-2020 The ManaPlus Developers * Copyright (C) 2020-2023 The ManaVerse Developers * * This file is part of The ManaPlus Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "net/eathena/guildrecv.h" #include "actormanager.h" #include "configuration.h" #include "notifymanager.h" #include "being/localplayer.h" #include "being/playerinfo.h" #include "enums/resources/notifytypes.h" #include "gui/windows/chatwindow.h" #include "gui/windows/skilldialog.h" #include "gui/windows/socialwindow.h" #include "gui/widgets/tabs/chat/guildtab.h" #include "net/beinghandler.h" #include "net/messagein.h" #include "net/eathena/guildhandler.h" #include "utils/delete2.h" #include "utils/checkutils.h" #include "utils/gettext.h" #include "debug.h" namespace EAthena { Guild *taGuild = nullptr; namespace GuildRecv { bool showBasicInfo = false; } // namespace GuildRecv void GuildRecv::processGuildCreateResponse(Net::MessageIn &msg) { const uint8_t flag = msg.readUInt8("flag"); switch (flag) { case 0: // Success NotifyManager::notify(NotifyTypes::GUILD_CREATED); break; case 1: // Already in a guild NotifyManager::notify(NotifyTypes::GUILD_ALREADY); break; case 2: // Unable to make (likely name already in use) NotifyManager::notify(NotifyTypes::GUILD_ERROR); break; case 3: // Emperium check failed NotifyManager::notify(NotifyTypes::GUILD_EMPERIUM_CHECK_FAILED); break; default: // Unknown response NotifyManager::notify(NotifyTypes::GUILD_ERROR); break; } } void GuildRecv::processGuildMasterOrMember(Net::MessageIn &msg) { msg.readInt32("type"); // Type (0x57 for member, 0xd7 for master) } void GuildRecv::processGuildBasicInfo(Net::MessageIn &msg) { const int guildId = msg.readInt32("guild id"); const int level = msg.readInt32("guild level"); const int members = msg.readInt32("connect member"); const int maxMembers = msg.readInt32("max member"); const int avgLevel = msg.readInt32("average level"); const int exp = msg.readInt32("exp"); const int nextExp = msg.readInt32("next exp"); msg.skip(12, "unused"); const int emblem = msg.readInt32("emblem id"); std::string name = msg.readString(24, "guild name"); std::string castle; std::string master; if (msg.getVersion() >= 20160622) { castle = msg.readString(16, "castles"); msg.readInt32("money, unused"); msg.readBeingId("leader char id"); } else { master = msg.readString(24, "master name"); castle = msg.readString(16, "castles"); msg.readInt32("money, unused"); } if (guildTab != nullptr && showBasicInfo) { showBasicInfo = false; // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Guild name: %s"), name.c_str()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); if (!master.empty()) { // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Guild master: %s"), master.c_str()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Guild level: %d"), level), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Online members: %d"), members), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Max members: %d"), maxMembers), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Average level: %d"), avgLevel), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Guild exp: %d"), exp), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Guild next exp: %d"), nextExp), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); // TRANSLATORS: guild info message guildTab->chatLog(strprintf(_("Guild castle: %s"), castle.c_str()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } Guild *const g = Guild::getGuild(CAST_S16(guildId)); if (g == nullptr) return; g->setName(name); g->setEmblemId(emblem); } void GuildRecv::processGuildAlianceInfo(Net::MessageIn &msg) { const int length = msg.readInt16("len"); if (length < 4) return; const int count = (length - 4) / 32; for (int i = 0; i < count; i++) { msg.readInt32("opposition"); msg.readInt32("guild id"); msg.readString(24, "guild name"); } } void GuildRecv::processGuildMemberList(Net::MessageIn &msg) { if (actorManager == nullptr) return; const int length = msg.readInt16("len"); if (length < 4) return; int guildSize = 0; if (msg.getVersion() >= 20161026) { guildSize = 34; } else { guildSize = 104; } const int count = (length - 4) / guildSize; if (taGuild == nullptr) { logger->log1("!taGuild"); return; } taGuild->clearMembers(); int onlineNum = 0; int totalNum = 0; for (int i = 0; i < count; i++) { const BeingId id = msg.readBeingId("account id"); const int charId = msg.readInt32("char id"); msg.readInt16("hair"); msg.readInt16("hair color"); const int gender = msg.readInt16("gender"); const int race = msg.readInt16("class"); const int level = msg.readInt16("level"); const int exp = msg.readInt32("exp"); const int online = msg.readInt32("online"); const int pos = msg.readInt32("position"); std::string name; if (msg.getVersion() < 20161026) { msg.skip(50, "unused"); name = msg.readString(24, "name"); } else { msg.readInt32("last login"); // for now unused name = actorManager->findCharById(charId); if (name.empty()) { beingHandler->requestNameByCharId(charId); } } GuildMember *const m = taGuild->addMember(id, charId, name); if (m != nullptr) { m->setOnline(online != 0); m->setID(id); m->setCharId(charId); m->setGender(Being::intToGender(CAST_U8(gender))); m->setLevel(level); m->setExp(exp); m->setPos(pos); m->setRace(race); Being *const being = actorManager->findBeingByName( name, ActorType::Player); if (being != nullptr) { being->setGuildName(taGuild->getName()); if (being->getLevel() != level) { being->setLevel(level); being->updateName(); } } if (online != 0) onlineNum ++; totalNum ++; } } taGuild->sort(); actorManager->updatePlayerGuild(); actorManager->updatePlayerColors(); if (socialWindow != nullptr) socialWindow->updateGuildCounter(onlineNum, totalNum); } void GuildRecv::processGuildPosNameList(Net::MessageIn &msg) { if (taGuild == nullptr) { logger->log1("!taGuild"); return; } const int length = msg.readInt16("len"); if (length < 4) return; const int count = (length - 4) / 28; for (int i = 0; i < count; i++) { const int id = msg.readInt32("position id"); const std::string name = msg.readString(24, "position name"); taGuild->addPos(id, name); } } void GuildRecv::processGuildPosInfoList(Net::MessageIn &msg) { const int length = msg.readInt16("len"); if (length < 4) return; const int count = (length - 4) / 16; for (int i = 0; i < count; i++) { msg.readInt32("id"); msg.readInt32("mode"); msg.readInt32("same id"); msg.readInt32("exp mode"); } } void GuildRecv::processGuildPositionChanged(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; msg.readInt16("len"); msg.readInt32("id"); msg.readInt32("mode"); msg.readInt32("same ip"); msg.readInt32("exp mode"); msg.readString(24, "name"); } void GuildRecv::processGuildMemberPosChange(Net::MessageIn &msg) { msg.readInt16("len"); const BeingId accountId = msg.readBeingId("account id"); const int charId = msg.readInt32("char id"); const int pos = msg.readInt32("position"); if (taGuild != nullptr) { GuildMember *const m = taGuild->getMember(accountId, charId); if (m != nullptr) m->setPos(pos); } } void GuildRecv::processGuildEmblemData(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; const int length = msg.readInt16("len"); msg.readInt32("guild id"); msg.readInt32("emblem id"); if (length < 12) return; msg.skip(length - 12, "emblem data"); } void GuildRecv::processGuildSkillInfo(Net::MessageIn &msg) { const int count = (msg.readInt16("len") - 6) / 37; msg.readInt16("skill points"); if (skillDialog != nullptr) skillDialog->hideSkills(SkillOwner::Guild); for (int i = 0; i < count; i++) { const int skillId = msg.readInt16("skill id"); const SkillType::SkillType inf = static_cast( msg.readInt32("inf")); const int level = msg.readInt16("skill level"); const int sp = msg.readInt16("sp"); const int range = msg.readInt16("range"); const std::string name = msg.readString(24, "skill name"); const Modifiable up = fromBool(msg.readUInt8("up flag"), Modifiable); PlayerInfo::setSkillLevel(skillId, level); if (skillDialog != nullptr) { if (!skillDialog->updateSkill(skillId, range, up, inf, sp)) { skillDialog->addSkill(SkillOwner::Guild, skillId, name, level, range, up, inf, sp); } } } if (skillDialog != nullptr) skillDialog->updateModels(); } void GuildRecv::processGuildNotice(Net::MessageIn &msg) { if (guildTab != nullptr) { const std::string msg1 = msg.readString(60, "msg1"); const std::string msg2 = msg.readString(120, "msg2"); guildTab->chatLog(msg1, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); guildTab->chatLog(msg2, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } else { msg.readString(60, "msg1"); msg.readString(120, "msg2"); } } void GuildRecv::processGuildInvite(Net::MessageIn &msg) { const int guildId = msg.readInt32("guild id"); if (socialWindow != nullptr) { const std::string guildName = msg.readString(24, "guild name"); socialWindow->showGuildInvite(guildName, guildId, ""); } else { msg.readString(24, "guild name"); } } void GuildRecv::processGuildInviteAck(Net::MessageIn &msg) { const uint8_t flag = msg.readUInt8("flag"); if (guildTab == nullptr) return; switch (flag) { case 0: NotifyManager::notify(NotifyTypes::GUILD_INVITE_FAILED); break; case 1: NotifyManager::notify(NotifyTypes::GUILD_INVITE_REJECTED); break; case 2: NotifyManager::notify(NotifyTypes::GUILD_INVITE_JOINED); break; case 3: NotifyManager::notify(NotifyTypes::GUILD_INVITE_FULL); break; default: NotifyManager::notify(NotifyTypes::GUILD_INVITE_ERROR); break; } } void GuildRecv::processGuildLeave(Net::MessageIn &msg) { const std::string nick = msg.readString(24, "nick"); msg.readString(40, "message"); if (taGuild != nullptr) taGuild->removeMember(nick); if (localPlayer == nullptr) return; if (nick == localPlayer->getName()) { if (taGuild != nullptr) { taGuild->removeFromMembers(); taGuild->clearMembers(); localPlayer->removeGuild(taGuild->getId()); } NotifyManager::notify(NotifyTypes::GUILD_LEFT); delete2(guildTab) if ((socialWindow != nullptr) && (taGuild != nullptr)) socialWindow->removeTab(taGuild); if (actorManager != nullptr) actorManager->updatePlayerColors(); } else { NotifyManager::notify(NotifyTypes::GUILD_USER_LEFT, nick); if (actorManager != nullptr) { Being *const b = actorManager->findBeingByName( nick, ActorType::Player); if (b != nullptr) b->clearGuilds(); if (taGuild != nullptr) taGuild->removeMember(nick); } } } void GuildRecv::processGuildLeave2(Net::MessageIn &msg) { const int charId = msg.readInt32("char id"); msg.readString(40, "message"); std::string nick; if (taGuild != nullptr) { const GuildMember *const member = taGuild->getMemberByCharId(charId); if (member != nullptr) { nick = member->getName(); taGuild->removeMember(member); } } if (localPlayer == nullptr) return; if (charId == PlayerInfo::getCharId()) { if (taGuild != nullptr) { taGuild->removeFromMembers(); taGuild->clearMembers(); localPlayer->removeGuild(taGuild->getId()); } NotifyManager::notify(NotifyTypes::GUILD_LEFT); delete2(guildTab) if ((socialWindow != nullptr) && (taGuild != nullptr)) socialWindow->removeTab(taGuild); if (actorManager != nullptr) actorManager->updatePlayerColors(); } else { NotifyManager::notify(NotifyTypes::GUILD_USER_LEFT, nick); if (actorManager != nullptr) { Being *const b = actorManager->findBeingByName( nick, ActorType::Player); if (b != nullptr) b->clearGuilds(); if (taGuild != nullptr) taGuild->removeMember(nick); } } } void GuildRecv::processGuildMessage(Net::MessageIn &msg) { const int msgLength = msg.readInt16("len") - 4; if (msgLength <= 0) return; if (guildTab != nullptr) { std::string chatMsg = msg.readString(msgLength, "message"); const size_t pos = chatMsg.find(" : ", 0); if (pos != std::string::npos) { const std::string sender_name = chatMsg.substr(0, pos); chatMsg.erase(0, pos + 3); trim(chatMsg); guildTab->chatLog(sender_name, chatMsg); } else { guildTab->chatLog(chatMsg, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } } else { DEBUGLOGSTR("invisible guild?"); msg.readString(msgLength, "message"); } } void GuildRecv::processGuildReqAlliance(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; msg.readInt32("id"); msg.readString(24, "name"); } void GuildRecv::processGuildReqAllianceAck(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; msg.readInt32("flag"); } void GuildRecv::processGuildDelAlliance(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; msg.readInt32("guild id"); msg.readInt32("flag"); } void GuildRecv::processGuildOppositionAck(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; msg.readUInt8("flag"); } void GuildRecv::processGuildBroken(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; msg.readInt32("flag"); } void GuildRecv::processGuildExpulsionContinue(const std::string &nick) { if (taGuild != nullptr) taGuild->removeMember(nick); if (localPlayer == nullptr) return; if (nick == localPlayer->getName()) { if (taGuild != nullptr) { taGuild->removeFromMembers(); taGuild->clearMembers(); } NotifyManager::notify(NotifyTypes::GUILD_KICKED); delete2(guildTab) if ((socialWindow != nullptr) && (taGuild != nullptr)) socialWindow->removeTab(taGuild); if (actorManager != nullptr) actorManager->updatePlayerColors(); } else { NotifyManager::notify(NotifyTypes::GUILD_USER_KICKED, nick); if (actorManager != nullptr) { Being *const b = actorManager->findBeingByName( nick, ActorType::Player); if (b != nullptr) b->clearGuilds(); if (taGuild != nullptr) taGuild->removeMember(nick); } } } void GuildRecv::processGuildUpdateCoords(Net::MessageIn &msg) { const BeingId id = msg.readBeingId("account id"); const int x = msg.readInt16("x"); const int y = msg.readInt16("y"); if (taGuild != nullptr) { GuildMember *const m = taGuild->getMember(id); if (m != nullptr) { m->setX(x); m->setY(y); } } } void GuildRecv::processGuildPositionInfo(Net::MessageIn &msg) { const int guildId = msg.readInt32("guild id"); const int emblem = msg.readInt32("elblem id"); PlayerInfo::setGuildPositionFlags( static_cast(msg.readInt32("mode"))); msg.readUInt8("guild master"); msg.readInt32("unused"); std::string guildName = msg.readString(24, "guild name"); Guild *const g = Guild::getGuild(CAST_S16(guildId)); if (g == nullptr) return; g->setName(guildName); g->setEmblemId(emblem); if (taGuild == nullptr) taGuild = g; if ((guildTab == nullptr) && (chatWindow != nullptr)) { guildTab = new GuildTab(chatWindow); if (localPlayer != nullptr) localPlayer->addGuild(taGuild); guildHandler->memberList(); } if (localPlayer != nullptr) { localPlayer->setGuild(g); localPlayer->setGuildName(g->getName()); } } void GuildRecv::processGuildMemberLogin(Net::MessageIn &msg) { const BeingId accountId = msg.readBeingId("account id"); const int charId = msg.readInt32("char id"); const int online = msg.readInt32("flag"); const GenderT gender = Being::intToGender(CAST_U8( msg.readInt16("sex"))); msg.readInt16("hair"); msg.readInt16("hair color"); if (taGuild != nullptr) { GuildMember *const m = taGuild->getMember(accountId, charId); if (m != nullptr) { m->setOnline(online != 0); if (online != 0) m->setGender(gender); if (guildTab != nullptr) guildTab->showOnline(m->getName(), fromBool(online, Online)); if (socialWindow != nullptr) socialWindow->updateGuildCounter(0, 0); } } } void GuildRecv::processGuildExpulsion(Net::MessageIn &msg) { const std::string nick = msg.readString(24, "name"); msg.readString(40, "message"); GuildRecv::processGuildExpulsionContinue(nick); } void GuildRecv::processGuildExpulsion2(Net::MessageIn &msg) { msg.readString(40, "message"); const int charId = msg.readInt32("char id"); std::string nick; if (taGuild != nullptr) { const GuildMember *const member = taGuild->getMemberByCharId(charId); if (member != nullptr) { nick = member->getName(); GuildRecv::processGuildExpulsionContinue(nick); } } } void GuildRecv::processGuildExpulsionList1(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; const int length = msg.readInt16("len"); if (length < 4) return; const int count = (length - 4) / 88; for (int i = 0; i < count; i++) { msg.readString(24, "char name"); msg.readString(24, "account name"); msg.readString(40, "message"); } } void GuildRecv::processGuildExpulsionList2(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; const int length = msg.readInt16("len"); if (length < 4) return; const int count = (length - 4) / 64; for (int i = 0; i < count; i++) { msg.readString(24, "name"); msg.readString(40, "message"); } } void GuildRecv::processGuildExpulsionList3(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; const int length = msg.readInt16("len"); if (length < 4) return; int count = (length - 4) / 44; for (int i = 0; i < count; i++) { msg.readInt32("char id"); msg.readString(40, "message"); } } void GuildRecv::processGuildEmblem(Net::MessageIn &msg) { UNIMPLEMENTEDPACKET; msg.readBeingId("being id"); msg.readInt32("guild id"); msg.readInt16("emblem id"); } void GuildRecv::processOnlineInfo(Net::MessageIn &msg) { // look like unused packet UNIMPLEMENTEDPACKET; msg.readBeingId("being id"); msg.readInt32("char id"); msg.readInt32("online"); } void GuildRecv::processGuildSetPosition(Net::MessageIn &msg) { const int positionLen = msg.readInt16("len") - 8; const BeingId beingId = msg.readBeingId("being id"); std::string position; if (positionLen > 0) { position = msg.readString(positionLen, "position"); } Being *const dstBeing = actorManager->findBeing(beingId); if (dstBeing != nullptr) { dstBeing->setGuildPos(position); dstBeing->addToCache(); } } } // namespace EAthena