diff --git a/src/BuddyManager.cpp b/src/BuddyManager.cpp index 7967247..3f4ba9d 100644 --- a/src/BuddyManager.cpp +++ b/src/BuddyManager.cpp @@ -138,28 +138,20 @@ void BuddyManager::reqBuddyByName(CNSocket* sock, CNPacketData* data) { INITSTRUCT(sP_FE2CL_REP_PC_FIND_NAME_MAKE_BUDDY_SUCC, resp); - CNSocket* otherSock = nullptr; - - for (auto& pair : PlayerManager::players) { - Player* plr = pair.second; - if (strcmp(U16toU8(plr->PCStyle.szFirstName).c_str(), U16toU8(pkt->szFirstName).c_str()) == 0 - && strcmp(U16toU8(plr->PCStyle.szLastName).c_str(), U16toU8(pkt->szLastName).c_str()) == 0 - && !playerHasBuddyWithID(plrReq, plr->iID)) { - otherSock = pair.first; - break; - } - } - + CNSocket* otherSock = PlayerManager::getSockFromName(U16toU8(pkt->szFirstName), U16toU8(pkt->szLastName)); if (otherSock == nullptr) return; // no player found + Player *otherPlr = PlayerManager::getPlayer(otherSock); + if (playerHasBuddyWithID(plrReq, otherPlr->iID)) + return; + resp.iPCUID = plrReq->PCStyle.iPC_UID; resp.iNameCheckFlag = plrReq->PCStyle.iNameCheck; memcpy(resp.szFirstName, plrReq->PCStyle.szFirstName, sizeof(plrReq->PCStyle.szFirstName)); memcpy(resp.szLastName, plrReq->PCStyle.szLastName, sizeof(plrReq->PCStyle.szLastName)); otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_FIND_NAME_MAKE_BUDDY_SUCC, sizeof(sP_FE2CL_REP_PC_FIND_NAME_MAKE_BUDDY_SUCC)); - } // Accepting buddy request @@ -317,11 +309,26 @@ void BuddyManager::reqBuddyFreechat(CNSocket* sock, CNPacketData* data) { if (otherSock == nullptr) return; // buddy offline + Player *otherPlr = PlayerManager::getPlayer(otherSock); + resp.iFromPCUID = plr->PCStyle.iPC_UID; resp.iToPCUID = pkt->iBuddyPCUID; resp.iEmoteCode = pkt->iEmoteCode; std::string fullChat = ChatManager::sanitizeText(U16toU8(pkt->szFreeChat)); + + if (fullChat.length() > 1 && fullChat[0] == CMD_PREFIX) { // PREFIX + ChatManager::runCmd(fullChat, sock); + return; + } + + if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__MUTE_FREECHAT) + return; + + std::string logLine = "[BuddyChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; + std::cout << logLine << std::endl; + ChatManager::dump.push_back(logLine); + U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); sock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_BUDDY_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_BUDDY_FREECHAT_MESSAGE_SUCC)); // confirm send to sender @@ -343,11 +350,18 @@ void BuddyManager::reqBuddyMenuchat(CNSocket* sock, CNPacketData* data) { if (otherSock == nullptr) return; // buddy offline + Player *otherPlr = PlayerManager::getPlayer(otherSock); + resp.iFromPCUID = plr->PCStyle.iPC_UID; resp.iToPCUID = pkt->iBuddyPCUID; resp.iEmoteCode = pkt->iEmoteCode; std::string fullChat = ChatManager::sanitizeText(U16toU8(pkt->szFreeChat)); + std::string logLine = "[BuddyMenuChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; + + std::cout << logLine << std::endl; + ChatManager::dump.push_back(logLine); + U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); sock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_BUDDY_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_BUDDY_MENUCHAT_MESSAGE_SUCC)); // confirm send to sender diff --git a/src/CNLoginServer.cpp b/src/CNLoginServer.cpp index f20818a..3e1ed31 100644 --- a/src/CNLoginServer.cpp +++ b/src/CNLoginServer.cpp @@ -214,7 +214,7 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&*it, P_LS2CL_REP_CHAR_INFO, sizeof(sP_LS2CL_REP_CHAR_INFO)); DEBUGLOG( - std::string message = "Login Server: Loaded " + std::to_string(resp.iCharCount) + "character"; + std::string message = "Login Server: Loaded " + std::to_string(resp.iCharCount) + " character"; if ((int)resp.iCharCount > 1) message += "s"; std::cout << message << std::endl; diff --git a/src/ChatManager.cpp b/src/ChatManager.cpp index e7a7d9c..579658d 100644 --- a/src/ChatManager.cpp +++ b/src/ChatManager.cpp @@ -24,7 +24,7 @@ std::vector parseArgs(std::string full) { return std::vector(begin, end); } -bool runCmd(std::string full, CNSocket* sock) { +bool ChatManager::runCmd(std::string full, CNSocket* sock) { std::vector args = parseArgs(full); std::string cmd = args[0].substr(1, args[0].size() - 1); @@ -851,15 +851,19 @@ void ChatManager::chatHandler(CNSocket* sock, CNPacketData* data) { Player* plr = PlayerManager::getPlayer(sock); std::string fullChat = sanitizeText(U16toU8(chat->szFreeChat)); - - std::cout << "[FreeChat] " << PlayerManager::getPlayerName(plr, false) << ": " << fullChat << std::endl; - dump.push_back(PlayerManager::getPlayerName(plr, true) + ": " + fullChat); - if (fullChat.length() > 1 && fullChat[0] == CMD_PREFIX) { // PREFIX runCmd(fullChat, sock); return; } + if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__MUTE_FREECHAT) + return; + + std::string logLine = "[FreeChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; + + std::cout << logLine << std::endl; + dump.push_back(logLine); + // send to client INITSTRUCT(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, resp); @@ -881,9 +885,10 @@ void ChatManager::menuChatHandler(CNSocket* sock, CNPacketData* data) { Player *plr = PlayerManager::getPlayer(sock); std::string fullChat = sanitizeText(U16toU8(chat->szFreeChat)); + std::string logLine = "[MenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; - std::cout << "[MenuChat] " << PlayerManager::getPlayerName(plr, false) << ": " << fullChat << std::endl; - dump.push_back(PlayerManager::getPlayerName(plr, true) + ": " + fullChat); + std::cout << logLine << std::endl; + dump.push_back(logLine); // send to client INITSTRUCT(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, resp); @@ -959,9 +964,10 @@ void ChatManager::announcementHandler(CNSocket* sock, CNPacketData* data) { default: break; } - - std::cout << "[Bcast " << announcement->iAreaType << "] " << PlayerManager::getPlayerName(plr, false) << ": " << U16toU8(msg.szAnnounceMsg) << std::endl; - dump.push_back("**" + PlayerManager::getPlayerName(plr, true) + ": " + U16toU8(msg.szAnnounceMsg) + "**"); + + std::string logLine = "[Bcast " + std::to_string(announcement->iAreaType) + "] " + PlayerManager::getPlayerName(plr, false) + ": " + U16toU8(msg.szAnnounceMsg); + std::cout << logLine << std::endl; + dump.push_back("**" + logLine + "**"); } // we only allow plain ascii, at least for now diff --git a/src/ChatManager.hpp b/src/ChatManager.hpp index d6c825c..1b015ac 100644 --- a/src/ChatManager.hpp +++ b/src/ChatManager.hpp @@ -21,6 +21,7 @@ namespace ChatManager { extern std::vector dump; void init(); + bool runCmd(std::string full, CNSocket* sock); void registerCommand(std::string cmd, int requiredLevel, CommandHandler handlr, std::string help = ""); void chatHandler(CNSocket* sock, CNPacketData* data); diff --git a/src/Defines.hpp b/src/Defines.hpp index a1e8ab0..040372e 100644 --- a/src/Defines.hpp +++ b/src/Defines.hpp @@ -1,13 +1,6 @@ /* enum definitions from the client */ #pragma once -/* - * TODO: It might be a good idea to make this file build-specific, but - * I'm pretty sure there were seldom any new packets added, and they're - * probably always going to be the non-essential things that we won't be - * implementing just yet anyway. - */ - // floats const float VALUE_BATTERY_EMPTY_PENALTY = 0.5f; const float CN_EP_RANK_1 = 0.8f; @@ -16,6 +9,21 @@ const float CN_EP_RANK_3 = 0.5f; const float CN_EP_RANK_4 = 0.3f; const float CN_EP_RANK_5 = 0.29f; +// methods of finding players for GM commands +enum eCN_GM_TargetSearchBy { + eCN_GM_TargetSearchBy__PC_ID, // player id + eCN_GM_TargetSearchBy__PC_Name, // firstname, lastname + eCN_GM_TargetSearchBy__PC_UID // account id +}; + +enum eCN_GM_TeleportType { + eCN_GM_TeleportMapType__XYZ, + eCN_GM_TeleportMapType__MapXYZ, + eCN_GM_TeleportMapType__MyLocation, + eCN_GM_TeleportMapType__SomeoneLocation, + eCN_GM_TeleportMapType__Unstick +}; + // NPC classes enum NPCClass { NPC_BASE = 0, diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp index 84edaa2..9756882 100644 --- a/src/GroupManager.cpp +++ b/src/GroupManager.cpp @@ -172,6 +172,18 @@ void GroupManager::chatGroup(CNSocket* sock, CNPacketData* data) { std::string fullChat = ChatManager::sanitizeText(U16toU8(chat->szFreeChat)); + if (fullChat.length() > 1 && fullChat[0] == CMD_PREFIX) { // PREFIX + ChatManager::runCmd(fullChat, sock); + return; + } + + if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__MUTE_FREECHAT) + return; + + std::string logLine = "[GroupChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; + std::cout << logLine << std::endl; + ChatManager::dump.push_back(logLine); + // send to client INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp); @@ -194,6 +206,10 @@ void GroupManager::menuChatGroup(CNSocket* sock, CNPacketData* data) { return; std::string fullChat = ChatManager::sanitizeText(U16toU8(chat->szFreeChat)); + std::string logLine = "[GroupMenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; + + std::cout << logLine << std::endl; + ChatManager::dump.push_back(logLine); // send to client INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp); diff --git a/src/ItemManager.cpp b/src/ItemManager.cpp index d9b88b4..27f7f43 100644 --- a/src/ItemManager.cpp +++ b/src/ItemManager.cpp @@ -5,6 +5,7 @@ #include "NanoManager.hpp" #include "NPCManager.hpp" #include "Player.hpp" +#include "ChatManager.hpp" #include // for memset() #include @@ -808,30 +809,34 @@ void ItemManager::itemTradeChatHandler(CNSocket* sock, CNPacketData* data) { sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT* pacdat = (sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT*)data->buf; INITSTRUCT(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT, resp); + Player *plr = PlayerManager::getPlayer(sock); resp.iID_Request = pacdat->iID_Request; resp.iID_From = pacdat->iID_From; resp.iID_To = pacdat->iID_To; - memcpy(resp.szFreeChat, pacdat->szFreeChat, sizeof(pacdat->szFreeChat)); - - resp.iEmoteCode = pacdat->iEmoteCode; + std::string fullChat = ChatManager::sanitizeText(U16toU8(pacdat->szFreeChat)); + U8toU16(fullChat, resp.szFreeChat, sizeof(resp.szFreeChat)); int iID_Check; - if (pacdat->iID_Request == pacdat->iID_From) { iID_Check = pacdat->iID_To; } else { iID_Check = pacdat->iID_From; } - CNSocket* otherSock = sock; + CNSocket* otherSock = PlayerManager::getSockFromID(iID_Check);; + if (otherSock == nullptr) + return; - for (auto pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - } - } + Player *otherPlr = PlayerManager::getPlayer(otherSock); + + std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; + + std::cout << logLine << std::endl; + ChatManager::dump.push_back(logLine); + + resp.iEmoteCode = pacdat->iEmoteCode; sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index b383cb1..84191b8 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -37,16 +37,21 @@ void PlayerManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_MOVETRANSPORTATION, moveSliderPlayer); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SLOPE, moveSlopePlayer); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GOTO, gotoPlayer); - REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_SET_VALUE, setSpecialPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_SET_VALUE, setValuePlayer); REGISTER_SHARD_PACKET(P_CL2FE_REP_LIVE_CHECK, heartbeatPlayer); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_REGEN, revivePlayer); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EXIT, exitGame); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH, setSpecialSwitchPlayer); REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH, setGMSpecialSwitchPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_TARGET_PC_SPECIAL_STATE_ONOFF, setGMSpecialOnOff); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_ON, enterPlayerVehicle); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, exitPlayerVehicle); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, changePlayerGuide); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET, setFirstUseFlag); + REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_LOCATION, locatePlayer); + REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_KICK_PLAYER, kickPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_TO_PC, warpToPlayer); + REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_TARGET_PC_TELEPORT, teleportPlayer); } void PlayerManager::addPlayer(CNSocket* key, Player plr) { @@ -664,6 +669,10 @@ void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_GOTO)) return; // ignore the malformed packet + Player *plr = getPlayer(sock); + if (plr->accountLevel > 50) + return; + sP_CL2FE_REQ_PC_GOTO* gotoData = (sP_CL2FE_REQ_PC_GOTO*)data->buf; INITSTRUCT(sP_FE2CL_REP_PC_GOTO_SUCC, response); @@ -677,12 +686,15 @@ void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) { sendPlayerTo(sock, gotoData->iToX, gotoData->iToY, gotoData->iToZ, INSTANCE_OVERWORLD); } -void PlayerManager::setSpecialPlayer(CNSocket* sock, CNPacketData* data) { +void PlayerManager::setValuePlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_GM_REQ_PC_SET_VALUE)) return; // ignore the malformed packet + Player *plr = getPlayer(sock); + if (plr->accountLevel > 50) + return; + sP_CL2FE_GM_REQ_PC_SET_VALUE* setData = (sP_CL2FE_GM_REQ_PC_SET_VALUE*)data->buf; - Player *plr = PlayerManager::getPlayer(sock); INITSTRUCT(sP_FE2CL_GM_REP_PC_SET_VALUE, response); @@ -897,6 +909,29 @@ void PlayerManager::setGMSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data) setSpecialState(sock, data); } +void PlayerManager::setSpecialState(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH)) + return; // ignore the malformed packet + + sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH* setData = (sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH*)data->buf; + Player *plr = getPlayer(sock); + + // HACK: work around the invisible weapon bug + if (setData->iSpecialStateFlag == CN_SPECIAL_STATE_FLAG__FULL_UI) + ItemManager::updateEquips(sock, plr); + + INITSTRUCT(sP_FE2CL_PC_SPECIAL_STATE_CHANGE, response); + + plr->iSpecialState ^= setData->iSpecialStateFlag; + + response.iPC_ID = setData->iPC_ID; + response.iReqSpecialStateFlag = setData->iSpecialStateFlag; + response.iSpecialState = plr->iSpecialState; + + sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC, sizeof(sP_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC)); + sendToViewable(sock, (void*)&response, P_FE2CL_PC_SPECIAL_STATE_CHANGE, sizeof(sP_FE2CL_PC_SPECIAL_STATE_CHANGE)); +} + void PlayerManager::changePlayerGuide(CNSocket *sock, CNPacketData *data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_CHANGE_MENTOR)) return; @@ -945,6 +980,186 @@ void PlayerManager::setFirstUseFlag(CNSocket* sock, CNPacketData* data) { plr->iFirstUseFlag[1] |= (1ULL << (flag->iFlagCode - 65)); } +void PlayerManager::setGMSpecialOnOff(CNSocket *sock, CNPacketData *data) { + if (data->size != sizeof(sP_CL2FE_GM_REQ_TARGET_PC_SPECIAL_STATE_ONOFF)) + return; // sanity check + + Player *plr = getPlayer(sock); + + // access check + if (plr->accountLevel > 30) + return; + + sP_CL2FE_GM_REQ_TARGET_PC_SPECIAL_STATE_ONOFF *req = (sP_CL2FE_GM_REQ_TARGET_PC_SPECIAL_STATE_ONOFF*)data->buf; + + CNSocket *otherSock = getSockFromAny(req->eTargetSearchBy, req->iTargetPC_ID, req->iTargetPC_UID, + U16toU8(req->szTargetPC_FirstName), U16toU8(req->szTargetPC_LastName)); + if (otherSock == nullptr) { + ChatManager::sendServerMessage(sock, "player to teleport not found"); + return; + } + + Player *otherPlr = getPlayer(otherSock); + if (req->iONOFF) + otherPlr->iSpecialState |= req->iSpecialStateFlag; + else + otherPlr->iSpecialState &= ~req->iSpecialStateFlag; + + // this is only used for muting players, so no need to update the client since that logic is server-side +} + +void PlayerManager::locatePlayer(CNSocket *sock, CNPacketData *data) { + if (data->size != sizeof(sP_CL2FE_GM_REQ_PC_LOCATION)) + return; // sanity check + + Player *plr = getPlayer(sock); + + // access check + if (plr->accountLevel > 30) + return; + + sP_CL2FE_GM_REQ_PC_LOCATION *req = (sP_CL2FE_GM_REQ_PC_LOCATION*)data->buf; + + CNSocket *otherSock = getSockFromAny(req->eTargetSearchBy, req->iTargetPC_ID, req->iTargetPC_UID, + U16toU8(req->szTargetPC_FirstName), U16toU8(req->szTargetPC_LastName)); + if (otherSock == nullptr) { + ChatManager::sendServerMessage(sock, "player not found"); + return; + } + + INITSTRUCT(sP_FE2CL_GM_REP_PC_LOCATION, resp); + Player *otherPlr = getPlayer(otherSock); + + resp.iTargetPC_UID = otherPlr->accountId; + resp.iTargetPC_ID = otherPlr->iID; + resp.iShardID = 0; // sharding is unsupported + resp.iMapType = !!PLAYERID(otherPlr->instanceID); // private instance or not + resp.iMapID = PLAYERID(otherPlr->instanceID); + resp.iMapNum = MAPNUM(otherPlr->instanceID); + resp.iX = otherPlr->x; + resp.iY = otherPlr->y; + resp.iZ = otherPlr->z; + + memcpy(resp.szTargetPC_FirstName, otherPlr->PCStyle.szFirstName, sizeof(resp.szTargetPC_FirstName)); + memcpy(resp.szTargetPC_LastName, otherPlr->PCStyle.szLastName, sizeof(resp.szTargetPC_LastName)); + + sock->sendPacket((void*)&resp, P_FE2CL_GM_REP_PC_LOCATION, sizeof(sP_FE2CL_GM_REP_PC_LOCATION)); +} + +void PlayerManager::kickPlayer(CNSocket *sock, CNPacketData *data) { + if (data->size != sizeof(sP_CL2FE_GM_REQ_KICK_PLAYER)) + return; // sanity check + + Player *plr = getPlayer(sock); + + // access check + if (plr->accountLevel > 30) + return; + + sP_CL2FE_GM_REQ_KICK_PLAYER *req = (sP_CL2FE_GM_REQ_KICK_PLAYER*)data->buf; + + CNSocket *otherSock = getSockFromAny(req->eTargetSearchBy, req->iTargetPC_ID, req->iTargetPC_UID, + U16toU8(req->szTargetPC_FirstName), U16toU8(req->szTargetPC_LastName)); + if (otherSock == nullptr) { + ChatManager::sendServerMessage(sock, "player not found"); + return; + } + + Player *otherPlr = getPlayer(otherSock); + + if (otherPlr->accountLevel > plr->accountLevel) { + ChatManager::sendServerMessage(sock, "player has higher access level"); + return; + } + + INITSTRUCT(sP_FE2CL_REP_PC_EXIT_SUCC, response); + + response.iID = otherPlr->iID; + response.iExitCode = 3; // "a GM has terminated your connection" + + // send to target player + otherSock->sendPacket((void*)&response, P_FE2CL_REP_PC_EXIT_SUCC, sizeof(sP_FE2CL_REP_PC_EXIT_SUCC)); +} + +void PlayerManager::warpToPlayer(CNSocket *sock, CNPacketData *data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_WARP_TO_PC)) + return; // sanity check + + Player *plr = getPlayer(sock); + + // access check + if (plr->accountLevel > 30) + return; + + sP_CL2FE_REQ_PC_WARP_TO_PC *req = (sP_CL2FE_REQ_PC_WARP_TO_PC*)data->buf; + + Player *otherPlr = getPlayerFromID(req->iPC_ID); + if (otherPlr == nullptr) { + ChatManager::sendServerMessage(sock, "player not found"); + return; + } + + sendPlayerTo(sock, otherPlr->x, otherPlr->y, otherPlr->z, otherPlr->instanceID); +} + +// GM teleport command +void PlayerManager::teleportPlayer(CNSocket *sock, CNPacketData *data) { + if (data->size != sizeof(sP_CL2FE_GM_REQ_TARGET_PC_TELEPORT)) + return; // sanity check + + Player *plr = getPlayer(sock); + + // access check + if (plr->accountLevel > 30) + return; + + sP_CL2FE_GM_REQ_TARGET_PC_TELEPORT *req = (sP_CL2FE_GM_REQ_TARGET_PC_TELEPORT*)data->buf; + + // player to teleport + CNSocket *targetSock = getSockFromAny(req->eTargetPCSearchBy, req->iTargetPC_ID, req->iTargetPC_UID, + U16toU8(req->szTargetPC_FirstName), U16toU8(req->szTargetPC_LastName)); + if (targetSock == nullptr) { + ChatManager::sendServerMessage(sock, "player to teleport not found"); + return; + } + + CNSocket *goalSock = nullptr; + Player *goalPlr = nullptr; + Player *targetPlr = nullptr; + uint64_t instance = plr->instanceID; + const int unstickRange = 400; + + switch (req->eTeleportType) { + case eCN_GM_TeleportMapType__MyLocation: + sendPlayerTo(targetSock, plr->x, plr->y, plr->z); + break; + case eCN_GM_TeleportMapType__MapXYZ: + instance = req->iToMap; + // fallthrough + case eCN_GM_TeleportMapType__XYZ: + sendPlayerTo(targetSock, req->iToX, req->iToY, req->iToZ, instance); + break; + case eCN_GM_TeleportMapType__SomeoneLocation: + // player to teleport to + goalSock = getSockFromAny(req->eGoalPCSearchBy, req->iGoalPC_ID, req->iGoalPC_UID, + U16toU8(req->szGoalPC_FirstName), U16toU8(req->szGoalPC_LastName)); + if (goalSock == nullptr) { + ChatManager::sendServerMessage(sock, "teleportation target player not found"); + return; + } + goalPlr = getPlayer(goalSock); + + sendPlayerTo(targetSock, goalPlr->x, goalPlr->y, goalPlr->z, goalPlr->instanceID); + break; + case eCN_GM_TeleportMapType__Unstick: + targetPlr = getPlayer(targetSock); + + sendPlayerTo(targetSock, targetPlr->x - unstickRange/2 + rand() % unstickRange, + targetPlr->y - unstickRange/2 + rand() % unstickRange, targetPlr->z + 80); + break; + } +} + #pragma region Helper methods Player *PlayerManager::getPlayer(CNSocket* key) { if (players.find(key) != players.end()) @@ -1014,32 +1229,9 @@ void PlayerManager::exitDuplicate(int accountId) { } } -void PlayerManager::setSpecialState(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH)) - return; // ignore the malformed packet - - Player *plr = getPlayer(sock); - - sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH* setData = (sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH*)data->buf; - - // HACK: work around the invisible weapon bug - if (setData->iSpecialStateFlag == CN_SPECIAL_STATE_FLAG__FULL_UI) - ItemManager::updateEquips(sock, plr); - - INITSTRUCT(sP_FE2CL_PC_SPECIAL_STATE_CHANGE, response); - - plr->iSpecialState ^= setData->iSpecialStateFlag; - - response.iPC_ID = setData->iPC_ID; - response.iReqSpecialStateFlag = setData->iSpecialStateFlag; - response.iSpecialState = plr->iSpecialState; - - sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC, sizeof(sP_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC)); - sendToViewable(sock, (void*)&response, P_FE2CL_PC_SPECIAL_STATE_CHANGE, sizeof(sP_FE2CL_PC_SPECIAL_STATE_CHANGE)); -} - +// TODO: just call getPlayer() after getSockFromID()? Player *PlayerManager::getPlayerFromID(int32_t iID) { - for (auto& pair : PlayerManager::players) + for (auto& pair : players) if (pair.second->iID == iID) return pair.second; @@ -1047,10 +1239,39 @@ Player *PlayerManager::getPlayerFromID(int32_t iID) { } CNSocket *PlayerManager::getSockFromID(int32_t iID) { - for (auto& pair : PlayerManager::players) + for (auto& pair : players) if (pair.second->iID == iID) return pair.first; return nullptr; } + +CNSocket *PlayerManager::getSockFromName(std::string firstname, std::string lastname) { + for (auto& pair : players) + if (U16toU8(pair.second->PCStyle.szFirstName) == firstname + && U16toU8(pair.second->PCStyle.szLastName) == lastname) + return pair.first; + + return nullptr; +} + +CNSocket *PlayerManager::getSockFromAny(int by, int id, int uid, std::string firstname, std::string lastname) { + switch (by) { + case eCN_GM_TargetSearchBy__PC_ID: + assert(id != 0); + return getSockFromID(id); + case eCN_GM_TargetSearchBy__PC_UID: // account id; not player id + assert(uid != 0); + for (auto& pair : players) + if (pair.second->accountId == uid) + return pair.first; + case eCN_GM_TargetSearchBy__PC_Name: + assert(firstname != "" && lastname != ""); // XXX: remove this if we start messing around with edited names? + return getSockFromName(firstname, lastname); + } + + // not found + return nullptr; +} + #pragma endregion diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index 9b753cf..e57966f 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -38,14 +38,19 @@ namespace PlayerManager { void moveSliderPlayer(CNSocket* sock, CNPacketData* data); void moveSlopePlayer(CNSocket* sock, CNPacketData* data); void gotoPlayer(CNSocket* sock, CNPacketData* data); - void setSpecialPlayer(CNSocket* sock, CNPacketData* data); + void setValuePlayer(CNSocket* sock, CNPacketData* data); void heartbeatPlayer(CNSocket* sock, CNPacketData* data); void revivePlayer(CNSocket* sock, CNPacketData* data); void exitGame(CNSocket* sock, CNPacketData* data); void setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data); void setGMSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data); + void setGMSpecialOnOff(CNSocket* sock, CNPacketData *data); void changePlayerGuide(CNSocket *sock, CNPacketData *data); + void locatePlayer(CNSocket *sock, CNPacketData *data); + void kickPlayer(CNSocket *sock, CNPacketData *data); + void warpToPlayer(CNSocket *sock, CNPacketData *data); + void teleportPlayer(CNSocket *sock, CNPacketData *data); void enterPlayerVehicle(CNSocket* sock, CNPacketData* data); void exitPlayerVehicle(CNSocket* sock, CNPacketData* data); @@ -61,6 +66,8 @@ namespace PlayerManager { void setSpecialState(CNSocket* sock, CNPacketData* data); Player *getPlayerFromID(int32_t iID); CNSocket *getSockFromID(int32_t iID); + CNSocket *getSockFromName(std::string firstname, std::string lastname); + CNSocket *getSockFromAny(int by, int id, int uid, std::string firstname, std::string lastname); void sendNanoBookSubset(CNSocket *sock); }