/* * The ManaPlus Client * Copyright (C) 2012-2019 The ManaPlus 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 "actions/actions.h" #include "actormanager.h" #include "configuration.h" #include "game.h" #ifdef USE_OPENGL #include "graphicsmanager.h" #endif // USE_OPENGL #include "main.h" #include "spellmanager.h" #include "actions/actiondef.h" #include "being/localplayer.h" #include "being/playerinfo.h" #include "const/spells.h" #include "const/resources/skill.h" #include "fs/files.h" #include "gui/gui.h" #include "gui/popupmanager.h" #include "gui/sdlinput.h" #include "gui/windowmanager.h" #include "gui/shortcut/dropshortcut.h" #include "gui/shortcut/emoteshortcut.h" #include "gui/shortcut/itemshortcut.h" #include "gui/popups/popupmenu.h" #include "gui/windows/buydialog.h" #include "gui/windows/okdialog.h" #include "gui/windows/tradewindow.h" #include "gui/windows/quitdialog.h" #include "gui/windows/buyselldialog.h" #include "gui/windows/chatwindow.h" #include "gui/windows/helpwindow.h" #include "gui/windows/inventorywindow.h" #include "gui/windows/itemamountwindow.h" #include "gui/windows/npcdialog.h" #include "gui/windows/outfitwindow.h" #include "gui/windows/setupwindow.h" #include "gui/windows/shopwindow.h" #include "gui/windows/shortcutwindow.h" #include "gui/windows/skilldialog.h" #include "gui/windows/whoisonline.h" #include "gui/widgets/createwidget.h" #include "gui/widgets/tabs/chat/chattab.h" #include "input/inputactionoperators.h" #if defined USE_OPENGL #include "render/normalopenglgraphics.h" #endif // USE_OPENGL #include "net/adminhandler.h" #include "net/beinghandler.h" #include "net/buyingstorehandler.h" #include "net/buysellhandler.h" #include "net/chathandler.h" #include "net/download.h" #include "net/homunculushandler.h" #include "net/gamehandler.h" #include "net/inventoryhandler.h" #include "net/ipc.h" #include "net/mercenaryhandler.h" #include "net/net.h" #include "net/npchandler.h" #include "net/serverfeatures.h" #include "net/uploadcharinfo.h" #include "net/tradehandler.h" #include "net/vendinghandler.h" #include "resources/iteminfo.h" #include "resources/memorymanager.h" #include "resources/resourcemanager/resourcemanager.h" #include "utils/chatutils.h" #include "utils/foreach.h" #include "utils/gettext.h" #include "utils/parameters.h" #include "utils/timer.h" #ifdef TMWA_SUPPORT #include "net/playerhandler.h" #include "utils/mathutils.h" #endif // TMWA_SUPPORT PRAGMA48(GCC diagnostic push) PRAGMA48(GCC diagnostic ignored "-Wshadow") #ifdef ANDROID #ifndef USE_SDL2 #include #endif // USE_OPENGL #endif // ANDROID PRAGMA48(GCC diagnostic pop) #include #include "debug.h" extern std::string tradePartnerName; extern QuitDialog *quitDialog; extern time_t start_time; extern char **environ; namespace Actions { static int uploadUpdate(void *ptr, const DownloadStatusT status, size_t total A_UNUSED, const size_t remaining A_UNUSED) A_NONNULL(1); static int uploadUpdate(void *ptr, const DownloadStatusT status, size_t total A_UNUSED, const size_t remaining A_UNUSED) { if (status == DownloadStatus::Idle || status == DownloadStatus::Starting) return 0; UploadChatInfo *const info = reinterpret_cast(ptr); if (info == nullptr) return 0; if (status == DownloadStatus::Complete) { std::string str = Net::Download::getUploadResponse(); const size_t sz = str.size(); if (sz > 0) { if (str[sz - 1] == '\n') str = str.substr(0, sz - 1); str.append(info->addStr); ChatTab *const tab = info->tab; if (chatWindow != nullptr && (tab == nullptr || chatWindow->isTabPresent(tab))) { str = strprintf("%s [@@%s |%s@@]", info->text.c_str(), str.c_str(), str.c_str()); outStringNormal(tab, str, str); } else { CREATEWIDGET(OkDialog, // TRANSLATORS: file uploaded message _("File uploaded"), str, // TRANSLATORS: ok dialog button _("OK"), DialogType::OK, Modal_true, ShowCenter_false, nullptr, 260); } } } // delete2(info->upload) info->upload = nullptr; delete info; return 0; } static void uploadFile(const std::string &str, const std::string &fileName, const std::string &addStr, ChatTab *const tab) { UploadChatInfo *const info = new UploadChatInfo; Net::Download *const upload = new Net::Download(info, "http://ix.io", &uploadUpdate, false, true, false); info->upload = upload; info->text = str; info->addStr = addStr; info->tab = tab; upload->setFile(fileName, -1); upload->start(); } static Being *findBeing(const std::string &name, const bool npc) { if ((localPlayer == nullptr) || (actorManager == nullptr)) return nullptr; Being *being = nullptr; if (name.empty()) { being = localPlayer->getTarget(); } else { being = actorManager->findBeingByName( name, ActorType::Unknown); } if ((being == nullptr) && npc) { being = actorManager->findNearestLivingBeing( localPlayer, 1, ActorType::Npc, AllowSort_true); if (being != nullptr) { if (abs(being->getTileX() - localPlayer->getTileX()) > 1 || abs(being->getTileY() - localPlayer->getTileY()) > 1) { being = nullptr; } } } if ((being == nullptr) && npc) { being = actorManager->findNearestLivingBeing( localPlayer, 1, ActorType::Player, AllowSort_true); if (being != nullptr) { if (abs(being->getTileX() - localPlayer->getTileX()) > 1 || abs(being->getTileY() - localPlayer->getTileY()) > 1) { being = nullptr; } } } return being; } static Item *getItemByInvIndex(const int index, const InventoryTypeT invType) { const Inventory *inv = nullptr; switch (invType) { case InventoryType::Storage: inv = PlayerInfo::getStorageInventory(); break; case InventoryType::Inventory: inv = PlayerInfo::getInventory(); break; case InventoryType::Trade: case InventoryType::Npc: case InventoryType::Cart: case InventoryType::Vending: case InventoryType::MailEdit: case InventoryType::MailView: case InventoryType::Craft: case InventoryType::TypeEnd: default: break; } if (inv != nullptr) return inv->getItem(index); return nullptr; } static int getAmountFromEvent(const InputEvent &event, Item *&item0, const InventoryTypeT invType) { Item *const item = getItemByInvIndex(atoi(event.args.c_str()), invType); item0 = item; if (item == nullptr) return 0; std::string str = event.args; removeToken(str, " "); if (str.empty()) return 0; int amount = 0; if (str[0] == '-') { if (str.size() > 1) { amount = item->getQuantity() - atoi(str.substr(1).c_str()); if (amount <= 0 || amount > item->getQuantity()) amount = item->getQuantity(); } } else if (str == "/") { amount = item->getQuantity() / 2; } else if (str == "all") { amount = item->getQuantity(); } else { amount = atoi(str.c_str()); } return amount; } impHandler(emote) { const int emotion = 1 + (event.action - InputAction::EMOTE_1); if (emotion > 0) { if (emoteShortcut != nullptr) emoteShortcut->useEmotePlayer(emotion); if (Game::instance() != nullptr) Game::instance()->setValidSpeed(); return true; } return false; } impHandler(outfit) { if (inputManager.isActionActive(InputAction::WEAR_OUTFIT)) { const int num = event.action - InputAction::OUTFIT_1; if ((outfitWindow != nullptr) && num >= 0) { outfitWindow->wearOutfit(num, true, false); if (Game::instance() != nullptr) Game::instance()->setValidSpeed(); return true; } } else if (inputManager.isActionActive(InputAction::COPY_OUTFIT)) { const int num = event.action - InputAction::OUTFIT_1; if ((outfitWindow != nullptr) && num >= 0) { outfitWindow->copyOutfit(num); if (Game::instance() != nullptr) Game::instance()->setValidSpeed(); return true; } } return false; } impHandler0(mouseClick) { if ((guiInput == nullptr) || (gui == nullptr)) return false; int mouseX; int mouseY; Gui::getMouseState(mouseX, mouseY); guiInput->simulateMouseClick(mouseX, mouseY, MouseButton::RIGHT); return true; } impHandler0(ok) { // Close the Browser if opened if ((helpWindow != nullptr) && helpWindow->isWindowVisible()) { helpWindow->setVisible(Visible_false); return true; } // Close the config window, cancelling changes if opened else if ((setupWindow != nullptr) && setupWindow->isWindowVisible()) { setupWindow->action(ActionEvent(nullptr, "cancel")); return true; } else if (NpcDialog *const dialog = NpcDialog::getActive()) { dialog->action(ActionEvent(nullptr, "ok")); return true; } else if (popupMenu->isPopupVisible()) { popupMenu->select(); } return false; } impHandler(shortcut) { if (itemShortcutWindow != nullptr) { const int num = itemShortcutWindow->getTabIndex(); if (num >= 0 && num < CAST_S32(SHORTCUT_TABS)) { if (itemShortcut[num] != nullptr) { itemShortcut[num]->useItem(event.action - InputAction::SHORTCUT_1); } } return true; } return false; } impHandler0(quit) { if (Game::instance() == nullptr) return false; if (PopupManager::isPopupMenuVisible()) { PopupManager::closePopupMenu(); return true; } else if (quitDialog == nullptr) { CREATEWIDGETV(quitDialog, QuitDialog, &quitDialog); quitDialog->requestMoveToTop(); return true; } return false; } impHandler0(dropItem0) { if (dropShortcut != nullptr) { dropShortcut->dropFirst(); return true; } return false; } impHandler0(dropItem) { if (dropShortcut != nullptr) { dropShortcut->dropItems(1); return true; } return false; } impHandler(dropItemId) { const Inventory *const inv = PlayerInfo::getInventory(); if (inv == nullptr) return false; // +++ ignoring item color for now Item *const item = inv->findItem(atoi(event.args.c_str()), ItemColor_one); if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) { ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop, inventoryWindow, item, 0, 0); } return true; } impHandler(dropItemInv) { Item *const item = getItemByInvIndex(atoi(event.args.c_str()), InventoryType::Inventory); if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) { ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop, inventoryWindow, item, 0, 0); } return true; } impHandler(dropItemIdAll) { const Inventory *const inv = PlayerInfo::getInventory(); if (inv == nullptr) return false; // +++ ignoring item color for now Item *const item = inv->findItem(atoi(event.args.c_str()), ItemColor_one); if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true); return true; } impHandler(dropItemInvAll) { Item *const item = getItemByInvIndex(atoi(event.args.c_str()), InventoryType::Inventory); if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true); return true; } #ifdef TMWA_SUPPORT impHandler(heal) { if (Net::getNetworkType() != ServerType::TMWATHENA) return false; if (actorManager != nullptr && localPlayer != nullptr) { std::string args = event.args; if (!args.empty()) { const Being *being = nullptr; if (args[0] == ':') { being = actorManager->findBeing(fromInt(atoi( args.substr(1).c_str()), BeingId)); if (being != nullptr && being->getType() == ActorType::Monster) being = nullptr; } else { being = actorManager->findBeingByName(args, ActorType::Player); } if (being != nullptr) actorManager->heal(being); } else { Being *target = localPlayer->getTarget(); if (inputManager.isActionActive(InputAction::STOP_ATTACK)) { if (target == nullptr || target->getType() != ActorType::Player) { target = actorManager->findNearestLivingBeing( localPlayer, 10, ActorType::Player, AllowSort_true); } } else { if (target == nullptr) target = localPlayer; } actorManager->heal(target); } if (Game::instance() != nullptr) Game::instance()->setValidSpeed(); return true; } return false; } #else // TMWA_SUPPORT impHandler0(heal) { return false; } #endif // TMWA_SUPPORT impHandler0(healmd) { #ifdef TMWA_SUPPORT if (Net::getNetworkType() != ServerType::TMWATHENA) return false; if (actorManager != nullptr) { const int matk = PlayerInfo::getStatEffective(Attributes::PLAYER_MATK); int maxHealingRadius; // magic levels < 2 if (PlayerInfo::getSkillLevel(340) < 2 || PlayerInfo::getSkillLevel(341) < 2) { maxHealingRadius = matk / 100 + 1; } else { maxHealingRadius = (12 * fastSqrtInt(matk) + matk) / 100 + 1; } Being *target = actorManager->findMostDamagedPlayer(maxHealingRadius); if (target != nullptr) actorManager->heal(target); if (Game::instance() != nullptr) Game::instance()->setValidSpeed(); return true; } #endif // TMWA_SUPPORT return false; } impHandler0(itenplz) { #ifdef TMWA_SUPPORT if (Net::getNetworkType() != ServerType::TMWATHENA) return false; if (actorManager != nullptr) { if (playerHandler != nullptr && playerHandler->canUseMagic() && PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 3) { actorManager->itenplz(); } return true; } #endif // TMWA_SUPPORT return false; } impHandler0(setHome) { if (localPlayer != nullptr) { localPlayer->setHome(); return true; } return false; } impHandler0(magicAttack) { #ifdef TMWA_SUPPORT if (Net::getNetworkType() != ServerType::TMWATHENA) return false; if (localPlayer != nullptr) { localPlayer->magicAttack(); return true; } #endif // TMWA_SUPPORT return false; } impHandler0(copyEquippedToOutfit) { if (outfitWindow != nullptr) { outfitWindow->copyFromEquiped(); return true; } return false; } impHandler(pickup) { if (localPlayer == nullptr) return false; const std::string args = event.args; if (args.empty()) { localPlayer->pickUpItems(0); } else { FloorItem *const item = actorManager->findItem(fromInt( atoi(args.c_str()), BeingId)); if (item != nullptr) localPlayer->pickUp(item); } return true; } static void doSit() { if (inputManager.isActionActive(InputAction::EMOTE)) localPlayer->updateSit(); else localPlayer->toggleSit(); } impHandler0(sit) { if (localPlayer != nullptr) { doSit(); return true; } return false; } impHandler(screenshot) { Game::createScreenshot(event.args); return true; } impHandler0(ignoreInput) { return true; } impHandler(buy) { if (serverFeatures == nullptr) return false; const std::string args = event.args; Being *being = findBeing(args, false); if ((being == nullptr) && Net::getNetworkType() == ServerType::TMWATHENA) { if (whoIsOnline != nullptr) { const std::set &players = whoIsOnline->getOnlineNicks(); if (players.find(args) != players.end()) { if (buySellHandler != nullptr) buySellHandler->requestSellList(args); return true; } } return false; } if (being == nullptr) being = findBeing(args, true); if (being == nullptr) return false; if (being->getType() == ActorType::Npc) { if (npcHandler != nullptr) npcHandler->buy(being); return true; } else if (being->getType() == ActorType::Player) { if (vendingHandler != nullptr && Net::getNetworkType() != ServerType::TMWATHENA) { vendingHandler->open(being); } else if (buySellHandler != nullptr) { buySellHandler->requestSellList(being->getName()); } return true; } return false; } impHandler(sell) { if (serverFeatures == nullptr) return false; const std::string args = event.args; Being *being = findBeing(args, false); if (being == nullptr && Net::getNetworkType() == ServerType::TMWATHENA) { if (whoIsOnline != nullptr) { const std::set &players = whoIsOnline->getOnlineNicks(); if (players.find(args) != players.end()) { if (buySellHandler != nullptr) buySellHandler->requestBuyList(args); return true; } } return false; } if (being == nullptr) being = findBeing(args, true); if (being == nullptr) return false; if (being->getType() == ActorType::Npc) { if (npcHandler != nullptr) npcHandler->sell(being->getId()); return true; } else if (being->getType() == ActorType::Player) { if ((buyingStoreHandler != nullptr) && Net::getNetworkType() != ServerType::TMWATHENA) { buyingStoreHandler->open(being); } else if (buySellHandler != nullptr) { buySellHandler->requestBuyList(being->getName()); } return true; } return false; } impHandler(talk) { const std::string args = event.args; Being *being = nullptr; if (!args.empty() && args[0] == ':') { being = actorManager->findBeing(fromInt(atoi( args.substr(1).c_str()), BeingId)); } else { being = findBeing(args, true); } if (being == nullptr) return false; if (being->canTalk()) { being->talkTo(); } else if (being->getType() == ActorType::Player) { CREATEWIDGET(BuySellDialog, being->getName()); } return true; } impHandler0(stopAttack) { if (localPlayer != nullptr) { localPlayer->stopAttack(false); // not consume if target attack key pressed if (inputManager.isActionActive(InputAction::TARGET_ATTACK)) return false; return true; } return false; } impHandler0(untarget) { if (localPlayer != nullptr) { localPlayer->untarget(); return true; } return false; } impHandler(attack) { if ((localPlayer == nullptr) || (actorManager == nullptr)) return false; Being *target = nullptr; std::string args = event.args; if (!args.empty()) { if (args[0] != ':') { target = actorManager->findNearestByName(args, ActorType::Unknown); } else { target = actorManager->findBeing(fromInt(atoi( args.substr(1).c_str()), BeingId)); if (target != nullptr && target->getType() != ActorType::Monster) { target = nullptr; } } } if (target == nullptr) target = localPlayer->getTarget(); else localPlayer->setTarget(target); if (target != nullptr) localPlayer->attack(target, true, false); return true; } impHandler(targetAttack) { if ((localPlayer != nullptr) && (actorManager != nullptr)) { Being *target = nullptr; std::string args = event.args; const bool newTarget = !inputManager.isActionActive( InputAction::STOP_ATTACK); if (!args.empty()) { if (args[0] != ':') { target = actorManager->findNearestByName(args, ActorType::Unknown); } else { target = actorManager->findBeing(fromInt(atoi( args.substr(1).c_str()), BeingId)); if (target != nullptr && target->getType() != ActorType::Monster) { target = nullptr; } } } if ((target == nullptr) && (settings.targetingType == 0U)) target = localPlayer->getTarget(); if (target == nullptr) { target = actorManager->findNearestLivingBeing( localPlayer, 90, ActorType::Monster, AllowSort_true); } localPlayer->attack2(target, newTarget, false); return true; } return false; } impHandler0(attackHuman) { if ((actorManager == nullptr) || (localPlayer == nullptr)) return false; Being *const target = actorManager->findNearestPvpPlayer(); if (target != nullptr) { localPlayer->setTarget(target); localPlayer->attack2(target, true, false); } return true; } impHandler0(safeVideoMode) { WindowManager::setFullScreen(false); return true; } impHandler0(stopSit) { if (localPlayer != nullptr) { localPlayer->stopAttack(false); // not consume if target attack key pressed if (inputManager.isActionActive(InputAction::TARGET_ATTACK)) return false; if (localPlayer->getTarget() == nullptr) { doSit(); return true; } return true; } return false; } impHandler0(showKeyboard) { #if defined(ANDROID) || defined(__SWITCH__) #ifdef USE_SDL2 if (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE) SDL_StopTextInput(); else SDL_StartTextInput(); #else // USE_SDL2 SDL_ANDROID_ToggleScreenKeyboardTextInput(nullptr); #endif // USE_SDL2 return true; #else // ANDROID return false; #endif // ANDROID } impHandler0(showWindows) { if (popupMenu != nullptr) { popupMenu->showWindowsPopup(); return true; } return false; } impHandler0(openTrade) { const Being *const being = localPlayer->getTarget(); if ((being != nullptr) && being->getType() == ActorType::Player) { if (tradeHandler != nullptr) tradeHandler->request(being); tradePartnerName = being->getName(); if (tradeWindow != nullptr) tradeWindow->clear(); return true; } return false; } impHandler0(ipcToggle) { if (ipc != nullptr) { IPC::stop(); if (ipc == nullptr) { debugChatTab->chatLog("IPC service stopped.", ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } else { debugChatTab->chatLog("Unable to stop IPC service.", ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } } else { IPC::start(); if (ipc != nullptr) { debugChatTab->chatLog( strprintf("IPC service available on port %d", ipc->getPort()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } else { debugChatTab->chatLog("Unable to start IPC service", ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } } return true; } impHandler(where) { ChatTab *const tab = event.tab != nullptr ? event.tab : debugChatTab; if (tab == nullptr) return false; std::ostringstream where; where << Game::instance()->getCurrentMapName() << ", coordinates: " << ((localPlayer->getPixelX() - mapTileSize / 2) / mapTileSize) << ", " << ((localPlayer->getPixelY() - mapTileSize) / mapTileSize); tab->chatLog(where.str(), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); return true; } impHandler0(who) { if (chatHandler != nullptr) chatHandler->who(); return true; } impHandler0(cleanGraphics) { ResourceManager::clearCache(); if (debugChatTab != nullptr) { // TRANSLATORS: clear graphics command message debugChatTab->chatLog(_("Cache cleared"), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } return true; } impHandler0(cleanFonts) { if (gui != nullptr) gui->clearFonts(); if (debugChatTab != nullptr) { // TRANSLATORS: clear fonts cache message debugChatTab->chatLog(_("Cache cleared"), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } return true; } impHandler(trade) { if (actorManager == nullptr) return false; const Being *being = actorManager->findBeingByName( event.args, ActorType::Player); if (being == nullptr) being = localPlayer->getTarget(); if (being != nullptr) { if (tradeHandler != nullptr) tradeHandler->request(being); tradePartnerName = being->getName(); if (tradeWindow != nullptr) tradeWindow->clear(); } return true; } impHandler0(priceLoad) { if (shopWindow != nullptr) { shopWindow->loadList(); return true; } return false; } impHandler0(priceSave) { if (shopWindow != nullptr) { shopWindow->saveList(); return true; } return false; } impHandler0(cacheInfo) { if ((chatWindow == nullptr) || (debugChatTab == nullptr)) return false; /* Font *const font = chatWindow->getFont(); if (!font) return; const TextChunkList *const cache = font->getCache(); if (!cache) return; unsigned int all = 0; // TRANSLATORS: chat fonts message debugChatTab->chatLog(_("font cache size"), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); std::string str; for (int f = 0; f < 256; f ++) { if (!cache[f].size) { const unsigned int sz = CAST_S32(cache[f].size); all += sz; str.append(strprintf("%d: %u, ", f, sz)); } } debugChatTab->chatLog(str, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); // TRANSLATORS: chat fonts message debugChatTab->chatLog(strprintf("%s %d", _("Cache size:"), all), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); #ifdef DEBUG_FONT_COUNTERS debugChatTab->chatLog("", ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); debugChatTab->chatLog(strprintf("%s %d", // TRANSLATORS: chat fonts message _("Created:"), font->getCreateCounter()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); debugChatTab->chatLog(strprintf("%s %d", // TRANSLATORS: chat fonts message _("Deleted:"), font->getDeleteCounter()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); #endif */ return true; } impHandler0(disconnect) { if (!settings.options.uniqueSession) { if (gameHandler != nullptr) gameHandler->disconnect2(); return true; } else return false; } impHandler(undress) { if ((actorManager == nullptr) || (localPlayer == nullptr)) return false; const std::string args = event.args; StringVect pars; if (!splitParameters(pars, args, " ,", '\"')) return false; Being *target = nullptr; const size_t sz = pars.size(); if (sz == 0) { target = localPlayer->getTarget(); } else { if (pars[0][0] == ':') { target = actorManager->findBeing(fromInt(atoi( pars[0].substr(1).c_str()), BeingId)); if ((target != nullptr) && target->getType() == ActorType::Monster) target = nullptr; } else { target = actorManager->findNearestByName(args, ActorType::Unknown); } } if (sz == 2) { if (target != nullptr) { const int itemId = atoi(pars[1].c_str()); target->undressItemById(itemId); } } else { if ((target != nullptr) && (beingHandler != nullptr)) beingHandler->undress(target); } return true; } impHandler0(dirs) { if (debugChatTab == nullptr) return false; debugChatTab->chatLog("config directory: " + settings.configDir, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); debugChatTab->chatLog("logs directory: " + settings.localDataDir, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); debugChatTab->chatLog("screenshots directory: " + settings.screenshotDir, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); debugChatTab->chatLog("temp directory: " + settings.tempDir, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); return true; } impHandler0(uptime) { if (debugChatTab == nullptr) return false; if (cur_time < start_time) { // TRANSLATORS: uptime command debugChatTab->chatLog(strprintf(_("Client uptime: %s"), "unknown"), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } else { // TRANSLATORS: uptime command debugChatTab->chatLog(strprintf(_("Client uptime: %s"), timeDiffToString(CAST_S32(cur_time - start_time)).c_str()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } return true; } #ifdef DEBUG_DUMP_LEAKS1 static void showRes(std::string str, ResourceManager::Resources *res) { if (!res) return; str.append(toString(res->size())); if (debugChatTab) { debugChatTab->chatLog(str, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } logger->log(str); ResourceManager::ResourceIterator iter = res->begin(); const ResourceManager::ResourceIterator iter_end = res->end(); while (iter != iter_end) { if (iter->second && iter->second->mRefCount) { char type = ' '; char isNew = 'N'; if (iter->second->getDumped()) isNew = 'O'; else iter->second->setDumped(true); SubImage *const subImage = dynamic_cast( iter->second); Image *const image = dynamic_cast(iter->second); int id = 0; if (subImage) type = 'S'; else if (image) type = 'I'; if (image) id = image->getGLImage(); logger->log("Resource %c%c: %s (%d) id=%d", type, isNew, iter->second->getIdPath().c_str(), iter->second->mRefCount, id); } ++ iter; } } impHandler(dump) { if (!debugChatTab) return false; if (!event.args.empty()) { ResourceManager::Resources *res = ResourceManager::getResources(); // TRANSLATORS: dump command showRes(_("Resource images:"), res); res = ResourceManager::getOrphanedResources(); // TRANSLATORS: dump command showRes(_("Orphaned resource images:"), res); } else { ResourceManager::Resources *res = ResourceManager::getResources(); // TRANSLATORS: dump command debugChatTab->chatLog(_("Resource images:") + toString(res->size()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); res = ResourceManager::getOrphanedResources(); // TRANSLATORS: dump command debugChatTab->chatLog(_("Orphaned resource images:") + toString(res->size()), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } return true; } #elif defined ENABLE_MEM_DEBUG impHandler0(dump) { nvwa::check_leaks(); return true; } #else // DEBUG_DUMP_LEAKS1 impHandler0(dump) { return true; } #endif // DEBUG_DUMP_LEAKS1 impHandler0(serverIgnoreAll) { if (chatHandler != nullptr) chatHandler->ignoreAll(); return true; } impHandler0(serverUnIgnoreAll) { if (chatHandler != nullptr) chatHandler->unIgnoreAll(); return true; } PRAGMA6(GCC diagnostic push) PRAGMA6(GCC diagnostic ignored "-Wnull-dereference") impHandler0(error) { int *const ptr = nullptr; *(ptr + 1) = 20; // logger->log("test %d", *ptr); exit(1); } PRAGMA6(GCC diagnostic pop) impHandler(dumpGraphics) { std::string str = strprintf("%s,%s,%dX%dX%d,", PACKAGE_OS, SMALL_VERSION, mainGraphics->getWidth(), mainGraphics->getHeight(), mainGraphics->getBpp()); if (mainGraphics->getFullScreen()) str.append("F"); else str.append("W"); if (mainGraphics->getHWAccel()) str.append("H"); else str.append("S"); if (mainGraphics->getDoubleBuffer()) str.append("D"); else str.append("_"); #if defined USE_OPENGL str.append(strprintf(",%d", mainGraphics->getOpenGL())); #else // defined USE_OPENGL str.append(",0"); #endif // defined USE_OPENGL str.append(strprintf(",%f,", static_cast(settings.guiAlpha))) .append(config.getBoolValue("adjustPerfomance") ? "1" : "0") .append(config.getBoolValue("alphaCache") ? "1" : "0") .append(config.getBoolValue("enableMapReduce") ? "1" : "0") .append(config.getBoolValue("beingopacity") ? "1" : "0") .append(",") .append(config.getBoolValue("enableAlphaFix") ? "1" : "0") .append(config.getBoolValue("disableAdvBeingCaching") ? "1" : "0") .append(config.getBoolValue("disableBeingCaching") ? "1" : "0") .append(config.getBoolValue("particleeffects") ? "1" : "0") .append(strprintf(",%d-%d", fps, config.getIntValue("fpslimit"))); outStringNormal(event.tab, str, str); return true; } impHandler0(dumpEnvironment) { logger->log1("Start environment variables"); for (char **env = environ; *env != nullptr; ++ env) logger->log1(*env); logger->log1("End environment variables"); if (debugChatTab != nullptr) { // TRANSLATORS: dump environment command debugChatTab->chatLog(_("Environment variables dumped"), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } return true; } impHandler(dumpTests) { const std::string str = config.getStringValue("testInfo"); outStringNormal(event.tab, str, str); return true; } impHandler0(dumpOGL) { #ifdef USE_OPENGL #if !defined(ANDROID) && !defined(__native_client__) && !defined(__SWITCH__) NormalOpenGLGraphics::dumpSettings(); #endif // !defined(ANDROID) && !defined(__native_client__) && // !defined(__SWITCH__) #endif // USE_OPENGL return true; } #ifdef USE_OPENGL impHandler(dumpGL) { std::string str = graphicsManager.getGLVersion(); outStringNormal(event.tab, str, str); return true; } #else // USE_OPENGL impHandler0(dumpGL) { return true; } #endif // USE_OPENGL impHandler(dumpMods) { std::string str = "enabled mods: " + serverConfig.getValue("mods", ""); outStringNormal(event.tab, str, str); return true; } #if defined USE_OPENGL && defined DEBUG_SDLFONT impHandler0(testSdlFont) { Font *font = new Font("fonts/dejavusans.ttf", 18, TTF_STYLE_NORMAL); timespec time1; timespec time2; NullOpenGLGraphics *nullGraphics = new NullOpenGLGraphics; STD_VECTOR data; volatile int width = 0; for (int f = 0; f < 300; f ++) data.push_back("test " + toString(f) + "string"); nullGraphics->beginDraw(); clock_gettime(CLOCK_MONOTONIC, &time1); Color color(0, 0, 0, 255); for (int f = 0; f < 500; f ++) { FOR_EACH (STD_VECTOR::const_iterator, it, data) { width += font->getWidth(*it); font->drawString(nullGraphics, color, color, *it, 10, 10); } FOR_EACH (STD_VECTOR::const_iterator, it, data) font->drawString(nullGraphics, color, color, *it, 10, 10); font->doClean(); } clock_gettime(CLOCK_MONOTONIC, &time2); delete nullGraphics; delete font; int64_t diff = (static_cast( time2.tv_sec) * 1000000000LL + static_cast( time2.tv_nsec)) / 100000 - (static_cast( time1.tv_sec) * 1000000000LL + static_cast( time1.tv_nsec)) / 100000; if (debugChatTab) { debugChatTab->chatLog("sdlfont time: " + toString(diff), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } return true; } #endif // defined USE_OPENGL && defined DEBUG_SDLFONT impHandler0(createItems) { BuyDialog *const dialog = CREATEWIDGETR0(BuyDialog); const ItemDB::ItemInfos &items = ItemDB::getItemInfos(); FOR_EACH (ItemDB::ItemInfos::const_iterator, it, items) { const ItemInfo *const info = (*it).second; if (info == nullptr) continue; const int id = info->getId(); if (id <= 500) continue; dialog->addItem(id, ItemType::Unknown, ItemColor_one, 100, 0); } dialog->sort(); return true; } impHandler(createItem) { int id = 0; int amount = 0; if (adminHandler == nullptr) return false; if (parse2Int(event.args, id, amount)) adminHandler->createItems(id, ItemColor_one, amount); else adminHandler->createItems(atoi(event.args.c_str()), ItemColor_one, 1); return true; } impHandler(uploadConfig) { // TRANSLATORS: upload config chat message uploadFile(_("Config uploaded to:"), config.getFileName(), "?xml", event.tab); return true; } impHandler(uploadServerConfig) { // TRANSLATORS: upload config chat message uploadFile(_("Server config Uploaded to:"), serverConfig.getFileName(), "?xml", event.tab); return true; } impHandler(uploadLog) { // TRANSLATORS: upload log chat message uploadFile(_("Log uploaded to:"), settings.logFileName, "", event.tab); return true; } impHandler0(mercenaryFire) { if (mercenaryHandler != nullptr) mercenaryHandler->fire(); return true; } impHandler0(mercenaryToMaster) { if (mercenaryHandler != nullptr) mercenaryHandler->moveToMaster(); return true; } impHandler0(homunculusToMaster) { if (homunculusHandler != nullptr) homunculusHandler->moveToMaster(); return true; } impHandler0(homunculusFeed) { if (homunculusHandler != nullptr) homunculusHandler->feed(); return true; } impHandler(useItem) { StringVect pars; if (!splitParameters(pars, event.args, " ,", '\"')) return false; const int sz = CAST_S32(pars.size()); if (sz < 1) return false; const int itemId = atoi(pars[0].c_str()); if (itemId < SPELL_MIN_ID) { const Inventory *const inv = PlayerInfo::getInventory(); if (inv != nullptr) { ItemColor color = ItemColor_one; int16_t useType = 0; StringVect pars2; if (!splitParameters(pars2, pars[0], " ,", '\"')) return false; const int sz2 = CAST_S32(pars2.size()); if (sz2 < 1) return false; if (sz2 >= 2) color = fromInt(atoi(pars2[1].c_str()), ItemColor); if (sz >= 2) useType = CAST_S16(atoi(pars[1].c_str())); const Item *const item = inv->findItem(itemId, color); PlayerInfo::useEquipItem(item, useType, Sfx_true); } } else if (itemId < SKILL_MIN_ID && (spellManager != nullptr)) { spellManager->useItem(itemId); } else if (skillDialog != nullptr) { // +++ probably need get data parameter from args skillDialog->useItem(itemId, fromBool(config.getBoolValue("skillAutotarget"), AutoTarget), 0, std::string()); } return true; } impHandler(useItemInv) { int param1 = 0; int param2 = 0; const std::string args = event.args; if (parse2Int(args, param1, param2)) { Item *const item = getItemByInvIndex(param1, InventoryType::Inventory); PlayerInfo::useEquipItem(item, CAST_S16(param2), Sfx_true); } else { Item *const item = getItemByInvIndex(atoi(event.args.c_str()), InventoryType::Inventory); PlayerInfo::useEquipItem(item, 0, Sfx_true); } return true; } impHandler(invToStorage) { Item *item = nullptr; const int amount = getAmountFromEvent(event, item, InventoryType::Inventory); if (item == nullptr) return true; if (amount != 0) { if (inventoryHandler != nullptr) { inventoryHandler->moveItem2(InventoryType::Inventory, item->getInvIndex(), amount, InventoryType::Storage); } } else { ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreAdd, inventoryWindow, item, 0, 0); } return true; } impHandler(tradeAdd) { Item *item = nullptr; const int amount = getAmountFromEvent(event, item, InventoryType::Inventory); if ((item == nullptr) || PlayerInfo::isItemProtected(item->getId())) return true; if (amount != 0) { if (tradeWindow != nullptr) tradeWindow->tradeItem(item, amount, true); } else { ItemAmountWindow::showWindow(ItemAmountWindowUsage::TradeAdd, tradeWindow, item, 0, 0); } return true; } impHandler(storageToInv) { Item *item = nullptr; const int amount = getAmountFromEvent(event, item, InventoryType::Storage); if (amount != 0) { if ((inventoryHandler != nullptr) && (item != nullptr)) { inventoryHandler->moveItem2(InventoryType::Storage, item->getInvIndex(), amount, InventoryType::Inventory); } } else { ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreRemove, storageWindow, item, 0, 0); } return true; } impHandler(protectItem) { const int id = atoi(event.args.c_str()); if (id > 0) PlayerInfo::protectItem(id); return true; } impHandler(unprotectItem) { const int id = atoi(event.args.c_str()); if (id > 0) PlayerInfo::unprotectItem(id); return true; } impHandler(kick) { if ((localPlayer == nullptr) || (actorManager == nullptr)) return false; Being *target = nullptr; std::string args = event.args; if (!args.empty()) { if (args[0] != ':') { target = actorManager->findNearestByName(args, ActorType::Unknown); } else { target = actorManager->findBeing(fromInt(atoi( args.substr(1).c_str()), BeingId)); } } if (target == nullptr) target = localPlayer->getTarget(); if ((target != nullptr) && (adminHandler != nullptr)) adminHandler->kick(target->getId()); return true; } impHandler0(clearDrop) { if (dropShortcut != nullptr) dropShortcut->clear(true); return true; } impHandler0(testInfo) { if (actorManager != nullptr) { logger->log("actors count: %d", CAST_S32( actorManager->size())); return true; } return false; } impHandler(craftKey) { const int slot = (event.action - InputAction::CRAFT_1); if (slot >= 0 && slot < 9) { if (inventoryWindow != nullptr) inventoryWindow->moveItemToCraft(slot); return true; } return false; } impHandler0(resetGameModifiers) { GameModifiers::resetModifiers(); return true; } impHandler(barToChat) { if (chatWindow != nullptr) { chatWindow->addInputText(event.args, true); return true; } return false; } impHandler(seen) { if (actorManager == nullptr) return false; ChatTab *tab = event.tab; if (tab == nullptr) tab = localChatTab; if (tab == nullptr) return false; if (config.getBoolValue("enableIdCollecting") == false) { // TRANSLATORS: last seen disabled warning tab->chatLog(_("Last seen disabled. " "Enable in players / collect players ID and seen log."), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); return true; } const std::string name = event.args; if (name.empty()) return false; std::string dir = settings.usersDir; dir.append(stringToHexPath(name)).append("/seen.txt"); if (Files::existsLocal(dir)) { StringVect lines; Files::loadTextFileLocal(dir, lines); if (lines.size() < 3) { // TRANSLATORS: last seen error tab->chatLog(_("You have never seen this nick."), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); return true; } const std::string message = strprintf( // TRANSLATORS: last seen message _("Last seen for %s: %s"), name.c_str(), lines[2].c_str()); tab->chatLog(message, ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } else { // TRANSLATORS: last seen error tab->chatLog(_("You have not seen this nick before."), ChatMsgType::BY_SERVER, IgnoreRecord_false, TryRemoveColors_true); } return true; } impHandler(dumpMemoryUsage) { if (event.tab != nullptr) MemoryManager::printAllMemory(event.tab); else MemoryManager::printAllMemory(localChatTab); return true; } impHandler(setEmoteType) { const std::string &args = event.args; if (args == "player" || args.empty()) { settings.emoteType = EmoteType::Player; } else if (args == "pet") { settings.emoteType = EmoteType::Pet; } else if (args == "homun" || args == "homunculus") { settings.emoteType = EmoteType::Homunculus; } else if (args == "merc" || args == "mercenary") { settings.emoteType = EmoteType::Mercenary; } return true; } } // namespace Actions