void AuctionHouseMgr::UpdatePendingAuctions() { for (auto itr = pendingAuctionMap.begin(); itr != pendingAuctionMap.end();) { ObjectGuid playerGUID = itr->first; if (Player* player = ObjectAccessor::FindConnectedPlayer(playerGUID)) { // Check if there were auctions since last update process if not if (PendingAuctionCount(player) == itr->second.second) { ++itr; PendingAuctionProcess(player); } else { ++itr; pendingAuctionMap[playerGUID].second = PendingAuctionCount(player); } } else { // Expire any auctions that we couldn't get a deposit for TC_LOG_WARN("auctionHouse", "Player %s was offline, unable to retrieve deposit!", playerGUID.ToString().c_str()); PlayerAuctions* thisAH = itr->second.first; ++itr; SQLTransaction trans = CharacterDatabase.BeginTransaction(); for (auto AHitr = thisAH->begin(); AHitr != thisAH->end();) { AuctionEntry* AH = (*AHitr); ++AHitr; AH->expire_time = time(NULL); AH->DeleteFromDB(trans); AH->SaveToDB(trans); } CharacterDatabase.CommitTransaction(trans); pendingAuctionMap.erase(playerGUID); delete thisAH; } } }
void BossAI::_DespawnAtEvade(Seconds delayToRespawn, Creature* who) { if (delayToRespawn < Seconds(2)) { TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %ld seconds, defaulting to 2.", delayToRespawn.count()); delayToRespawn = Seconds(2); } if (!who) who = me; if (TempSummon* whoSummon = who->ToTempSummon()) { TC_LOG_WARN("scripts", "_DespawnAtEvade called on a temporary summon."); whoSummon->UnSummon(); return; } who->DespawnOrUnsummon(0, Seconds(delayToRespawn)); if (instance && who == me) instance->SetBossState(_bossId, FAIL); }
void Warden::Update() { if (_initialized) { uint32 currentTimestamp = getMSTime(); uint32 diff = currentTimestamp - _previousTimestamp; _previousTimestamp = currentTimestamp; if (_dataSent) { uint32 maxClientResponseDelay = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_RESPONSE_DELAY); if (maxClientResponseDelay > 0) { // Kick player if client response delays more than set in config if (_clientResponseTimer > maxClientResponseDelay * IN_MILLISECONDS) { TC_LOG_WARN("warden", "%s (latency: %u, IP: %s) exceeded Warden module response delay for more than %s - disconnecting client", _session->GetPlayerInfo().c_str(), _session->GetLatency(), _session->GetRemoteAddress().c_str(), secsToTimeString(maxClientResponseDelay, true).c_str()); _session->KickPlayer(); } else _clientResponseTimer += diff; } } else { if (diff >= _checkTimer) { RequestData(); } else _checkTimer -= diff; } } }
void WardenWin::HandleHashResult(ByteBuffer &buff) { buff.rpos(buff.wpos()); // Verify key if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash, 20) != 0) { TC_LOG_WARN("warden", "%s failed hash reply. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str()); return; } TC_LOG_DEBUG("warden", "Request hash reply: succeed"); // Change keys here memcpy(_inputKey, Module.ClientKeySeed, 16); memcpy(_outputKey, Module.ServerKeySeed, 16); _inputCrypto.Init(_inputKey); _outputCrypto.Init(_outputKey); _initialized = true; _previousTimestamp = getMSTime(); }
PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) : m_rowCount(rowCount), m_rowPosition(0), m_fieldCount(fieldCount), m_rBind(NULL), m_stmt(stmt), m_res(result), m_isNull(NULL), m_length(NULL) { if (!m_res) return; if (m_stmt->bind_result_done) { delete[] m_stmt->bind->length; delete[] m_stmt->bind->is_null; } m_rBind = new MYSQL_BIND[m_fieldCount]; m_isNull = new my_bool[m_fieldCount]; m_length = new unsigned long[m_fieldCount]; memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount); memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount); memset(m_length, 0, sizeof(unsigned long) * m_fieldCount); //- This is where we store the (entire) resultset if (mysql_stmt_store_result(m_stmt)) { TC_LOG_WARN(LOG_FILTER_SQL, "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); return; } //- This is where we prepare the buffer based on metadata uint32 i = 0; MYSQL_FIELD* field = mysql_fetch_field(m_res); while (field) { size_t size = Field::SizeForType(field); m_rBind[i].buffer_type = field->type; m_rBind[i].buffer = malloc(size); memset(m_rBind[i].buffer, 0, size); m_rBind[i].buffer_length = size; m_rBind[i].length = &m_length[i]; m_rBind[i].is_null = &m_isNull[i]; m_rBind[i].error = NULL; m_rBind[i].is_unsigned = field->flags & UNSIGNED_FLAG; ++i; field = mysql_fetch_field(m_res); } //- This is where we bind the bind the buffer to the statement if (mysql_stmt_bind_result(m_stmt, m_rBind)) { TC_LOG_WARN(LOG_FILTER_SQL, "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); delete[] m_rBind; delete[] m_isNull; delete[] m_length; return; } m_rowCount = mysql_stmt_num_rows(m_stmt); m_rows.resize(uint32(m_rowCount)); while (_NextRow()) { m_rows[uint32(m_rowPosition)] = new Field[m_fieldCount]; for (uint64 fIndex = 0; fIndex < m_fieldCount; ++fIndex) { if (!*m_rBind[fIndex].is_null) m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( m_rBind[fIndex].buffer, m_rBind[fIndex].buffer_length, m_rBind[fIndex].buffer_type, *m_rBind[fIndex].length ); else switch (m_rBind[fIndex].buffer_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( "", m_rBind[fIndex].buffer_length, m_rBind[fIndex].buffer_type, *m_rBind[fIndex].length ); break; default: m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( 0, m_rBind[fIndex].buffer_length, m_rBind[fIndex].buffer_type, *m_rBind[fIndex].length ); } } m_rowPosition++; } m_rowPosition = 0; /// All data is buffered, let go of mysql c api structures CleanUp(); }
void WardenWin::HandleData(ByteBuffer &buff) { TC_LOG_DEBUG("warden", "Handle data"); _dataSent = false; _clientResponseTimer = 0; uint16 Length; buff >> Length; uint32 Checksum; buff >> Checksum; if (!IsValidCheckSum(Checksum, buff.contents() + buff.rpos(), Length)) { buff.rpos(buff.wpos()); TC_LOG_WARN("warden", "%s failed checksum. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str()); return; } // TIMING_CHECK { uint8 result; buff >> result; /// @todo test it. if (result == 0x00) { TC_LOG_WARN("warden", "%s failed timing check. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str()); return; } uint32 newClientTicks; buff >> newClientTicks; uint32 ticksNow = getMSTime(); uint32 ourTicks = newClientTicks + (ticksNow - _serverTicks); TC_LOG_DEBUG("warden", "ServerTicks %u", ticksNow); // Now TC_LOG_DEBUG("warden", "RequestTicks %u", _serverTicks); // At request TC_LOG_DEBUG("warden", "Ticks %u", newClientTicks); // At response TC_LOG_DEBUG("warden", "Ticks diff %u", ourTicks - newClientTicks); } WardenCheckResult* rs; WardenCheck *rd; uint8 type; uint16 checkFailed = 0; boost::shared_lock<boost::shared_mutex> lock(sWardenCheckMgr->_checkStoreLock); for (std::list<uint16>::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr) { rd = sWardenCheckMgr->GetWardenDataById(*itr); rs = sWardenCheckMgr->GetWardenResultById(*itr); type = rd->Type; switch (type) { case MEM_CHECK: { uint8 Mem_Result; buff >> Mem_Result; if (Mem_Result != 0) { TC_LOG_DEBUG("warden", "RESULT MEM_CHECK not 0x00, CheckId %u account Id %u", *itr, _session->GetAccountId()); checkFailed = *itr; continue; } if (memcmp(buff.contents() + buff.rpos(), rs->Result.AsByteArray(0, false).get(), rd->Length) != 0) { TC_LOG_DEBUG("warden", "RESULT MEM_CHECK fail CheckId %u account Id %u", *itr, _session->GetAccountId()); checkFailed = *itr; buff.rpos(buff.rpos() + rd->Length); continue; } buff.rpos(buff.rpos() + rd->Length); TC_LOG_DEBUG("warden", "RESULT MEM_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); break; } case PAGE_CHECK_A: case PAGE_CHECK_B: case DRIVER_CHECK: case MODULE_CHECK: { const uint8 byte = 0xE9; if (memcmp(buff.contents() + buff.rpos(), &byte, sizeof(uint8)) != 0) { if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) TC_LOG_DEBUG("warden", "RESULT PAGE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); if (type == MODULE_CHECK) TC_LOG_DEBUG("warden", "RESULT MODULE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); if (type == DRIVER_CHECK) TC_LOG_DEBUG("warden", "RESULT DRIVER_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); checkFailed = *itr; buff.rpos(buff.rpos() + 1); continue; } buff.rpos(buff.rpos() + 1); if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) TC_LOG_DEBUG("warden", "RESULT PAGE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); else if (type == MODULE_CHECK) TC_LOG_DEBUG("warden", "RESULT MODULE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); else if (type == DRIVER_CHECK) TC_LOG_DEBUG("warden", "RESULT DRIVER_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); break; } case LUA_STR_CHECK: { uint8 Lua_Result; buff >> Lua_Result; if (Lua_Result != 0) { TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); checkFailed = *itr; continue; } uint8 luaStrLen; buff >> luaStrLen; if (luaStrLen != 0) { char *str = new char[luaStrLen + 1]; memcpy(str, buff.contents() + buff.rpos(), luaStrLen); str[luaStrLen] = '\0'; // null terminator TC_LOG_DEBUG("warden", "Lua string: %s", str); delete[] str; } buff.rpos(buff.rpos() + luaStrLen); // Skip string TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId()); break; } case MPQ_CHECK: { uint8 Mpq_Result; buff >> Mpq_Result; if (Mpq_Result != 0) { TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK not 0x00 account id %u", _session->GetAccountId()); checkFailed = *itr; continue; } if (memcmp(buff.contents() + buff.rpos(), rs->Result.AsByteArray(0, false).get(), 20) != 0) // SHA1 { TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); checkFailed = *itr; buff.rpos(buff.rpos() + 20); // 20 bytes SHA1 continue; } buff.rpos(buff.rpos() + 20); // 20 bytes SHA1 TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId()); break; } default: // Should never happen break; } } if (checkFailed > 0) { WardenCheck* check = sWardenCheckMgr->GetWardenDataById(checkFailed); TC_LOG_WARN("warden", "%s failed Warden check %u. Action: %s", _session->GetPlayerInfo().c_str(), checkFailed, Penalty(check).c_str()); } // Set hold off timer, minimum timer should at least be 1 second uint32 holdOff = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF); _checkTimer = (holdOff < 1 ? 1 : holdOff) * IN_MILLISECONDS; }
void WardenMac::HandleHashResult(ByteBuffer &buff) { // test int keyIn[4]; keyData mod_seed = { { { { 0x4D, 0x80, 0x8D, 0x2C, 0x77, 0xD9, 0x05, 0xC4, 0x1A, 0x63, 0x80, 0xEC, 0x08, 0x58, 0x6A, 0xFE } } } }; for (int i = 0; i < 4; ++i) { keyIn[i] = mod_seed.ints.ints[i]; } int keyOut[4]; int keyIn1, keyIn2; keyOut[0] = keyIn[0]; keyIn[0] ^= 0xDEADBEEFu; keyIn1 = keyIn[1]; keyIn[1] -= 0x35014542u; keyIn2 = keyIn[2]; keyIn[2] += 0x5313F22u; keyIn[3] *= 0x1337F00Du; keyOut[1] = keyIn1 - 0x6A028A84; keyOut[2] = keyIn2 + 0xA627E44; keyOut[3] = 0x1337F00D * keyIn[3]; // end test buff.rpos(buff.wpos()); SHA1Hash sha1; sha1.UpdateData((uint8*)keyIn, 16); sha1.Finalize(); //const uint8 validHash[20] = { 0x56, 0x8C, 0x05, 0x4C, 0x78, 0x1A, 0x97, 0x2A, 0x60, 0x37, 0xA2, 0x29, 0x0C, 0x22, 0xB5, 0x25, 0x71, 0xA0, 0x6F, 0x4E }; // Verify key if (memcmp(buff.contents() + 1, sha1.GetDigest(), 20) != 0) { TC_LOG_WARN("warden", "%s failed hash reply. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str()); return; } TC_LOG_DEBUG("warden", "Request hash reply: succeed"); // client 7F96EEFDA5B63D20A4DF8E00CBF48304 //const uint8 client_key[16] = { 0x7F, 0x96, 0xEE, 0xFD, 0xA5, 0xB6, 0x3D, 0x20, 0xA4, 0xDF, 0x8E, 0x00, 0xCB, 0xF4, 0x83, 0x04 }; // server C2B7ADEDFCCCA9C2BFB3F85602BA809B //const uint8 server_key[16] = { 0xC2, 0xB7, 0xAD, 0xED, 0xFC, 0xCC, 0xA9, 0xC2, 0xBF, 0xB3, 0xF8, 0x56, 0x02, 0xBA, 0x80, 0x9B }; // change keys here memcpy(_inputKey, keyIn, 16); memcpy(_outputKey, keyOut, 16); _inputCrypto.Init(_inputKey); _outputCrypto.Init(_outputKey); _initialized = true; _previousTimestamp = getMSTime(); }
PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) : m_rowCount(rowCount), m_rowPosition(0), m_fieldCount(fieldCount), m_rBind(NULL), m_stmt(stmt), m_metadataResult(result), m_isNull(NULL), m_length(NULL) { if (!m_metadataResult) return; if (m_stmt->bind_result_done) { delete[] m_stmt->bind->length; delete[] m_stmt->bind->is_null; } m_rBind = new MYSQL_BIND[m_fieldCount]; m_isNull = new my_bool[m_fieldCount]; m_length = new unsigned long[m_fieldCount]; memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount); memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount); memset(m_length, 0, sizeof(unsigned long) * m_fieldCount); //- This is where we store the (entire) resultset if (mysql_stmt_store_result(m_stmt)) { TC_LOG_WARN("sql.sql", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); delete[] m_rBind; delete[] m_isNull; delete[] m_length; return; } m_rowCount = mysql_stmt_num_rows(m_stmt); //- This is where we prepare the buffer based on metadata MYSQL_FIELD* field = mysql_fetch_fields(m_metadataResult); std::size_t rowSize = 0; for (uint32 i = 0; i < m_fieldCount; ++i) { size_t size = Field::SizeForType(&field[i]); rowSize += size; m_rBind[i].buffer_type = field[i].type; m_rBind[i].buffer_length = size; m_rBind[i].length = &m_length[i]; m_rBind[i].is_null = &m_isNull[i]; m_rBind[i].error = NULL; m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG; } char* dataBuffer = new char[rowSize * m_rowCount]; for (uint32 i = 0, offset = 0; i < m_fieldCount; ++i) { m_rBind[i].buffer = dataBuffer + offset; offset += m_rBind[i].buffer_length; } //- This is where we bind the bind the buffer to the statement if (mysql_stmt_bind_result(m_stmt, m_rBind)) { TC_LOG_WARN("sql.sql", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); mysql_stmt_free_result(m_stmt); CleanUp(); delete[] m_isNull; delete[] m_length; return; } m_rows.resize(uint32(m_rowCount) * m_fieldCount); while (_NextRow()) { for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex) { unsigned long buffer_length = m_rBind[fIndex].buffer_length; unsigned long fetched_length = *m_rBind[fIndex].length; if (!*m_rBind[fIndex].is_null) { void* buffer = m_stmt->bind[fIndex].buffer; switch (m_rBind[fIndex].buffer_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: // warning - the string will not be null-terminated if there is no space for it in the buffer // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string // in this case using Field::GetCString will result in garbage // TODO: remove Field::GetCString and use boost::string_ref (currently proposed for TS as string_view, maybe in C++17) if (fetched_length < buffer_length) *((char*)buffer + fetched_length) = '\0'; break; default: break; } m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( buffer, m_rBind[fIndex].buffer_type, fetched_length); // move buffer pointer to next part m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize; } else { m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( nullptr, m_rBind[fIndex].buffer_type, *m_rBind[fIndex].length); } #ifdef TRINITY_DEBUG m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetMetadata(&field[fIndex], fIndex); #endif } m_rowPosition++; } m_rowPosition = 0; /// All data is buffered, let go of mysql c api structures mysql_stmt_free_result(m_stmt); }
void SystemMgr::LoadScriptSplineChains() { uint32 oldMSTime = getMSTime(); m_mSplineChainsMap.clear(); // 0 1 2 3 4 QueryResult resultMeta = WorldDatabase.Query("SELECT entry, chainId, splineId, expectedDuration, msUntilNext FROM script_spline_chain_meta ORDER BY entry asc, chainId asc, splineId asc"); // 0 1 2 3 4 5 6 QueryResult resultWP = WorldDatabase.Query("SELECT entry, chainId, splineId, wpId, x, y, z FROM script_spline_chain_waypoints ORDER BY entry asc, chainId asc, splineId asc, wpId asc"); if (!resultMeta || !resultWP) { TC_LOG_INFO("server.loading", ">> Loaded spline chain data for 0 chains, consisting of 0 splines with 0 waypoints. DB tables `script_spline_chain_meta` and `script_spline_chain_waypoints` are empty."); } else { uint32 chainCount = 0, splineCount = 0, wpCount = 0; do { Field* fieldsMeta = resultMeta->Fetch(); uint32 entry = fieldsMeta[0].GetUInt32(); uint16 chainId = fieldsMeta[1].GetUInt16(); uint8 splineId = fieldsMeta[2].GetUInt8(); std::vector<SplineChainLink>& chain = m_mSplineChainsMap[{entry, chainId}]; if (splineId != chain.size()) { TC_LOG_WARN("server.loading", "Creature #%u: Chain %u has orphaned spline %u, skipped.", entry, chainId, splineId); continue; } uint32 expectedDuration = fieldsMeta[3].GetUInt32(); uint32 msUntilNext = fieldsMeta[4].GetUInt32(); chain.emplace_back(expectedDuration, msUntilNext); if (splineId == 0) ++chainCount; ++splineCount; } while (resultMeta->NextRow()); do { Field* fieldsWP = resultWP->Fetch(); uint32 entry = fieldsWP[0].GetUInt32(); uint16 chainId = fieldsWP[1].GetUInt16(); uint8 splineId = fieldsWP[2].GetUInt8(), wpId = fieldsWP[3].GetUInt8(); float posX = fieldsWP[4].GetFloat(), posY = fieldsWP[5].GetFloat(), posZ = fieldsWP[6].GetFloat(); auto it = m_mSplineChainsMap.find({entry,chainId}); if (it == m_mSplineChainsMap.end()) { TC_LOG_WARN("server.loading", "Creature #%u has waypoint data for spline chain %u. No such chain exists - entry skipped.", entry, chainId); continue; } std::vector<SplineChainLink>& chain = it->second; if (splineId >= chain.size()) { TC_LOG_WARN("server.loading", "Creature #%u has waypoint data for spline (%u,%u). The specified chain does not have a spline with this index - entry skipped.", entry, chainId, splineId); continue; } SplineChainLink& spline = chain[splineId]; if (wpId != spline.Points.size()) { TC_LOG_WARN("server.loading", "Creature #%u has orphaned waypoint data in spline (%u,%u) at index %u. Skipped.", entry, chainId, splineId, wpId); continue; } spline.Points.emplace_back(posX, posY, posZ); ++wpCount; } while (resultWP->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded spline chain data for %u chains, consisting of %u splines with %u waypoints in %u ms", chainCount, splineCount, wpCount, GetMSTimeDiffToNow(oldMSTime)); } }
/// Launch the auth server extern int main(int argc, char **argv) { // Command line parsing to get the configuration file name char const* cfg_file = _TRINITY_REALM_CONFIG; int c = 1; while (c < argc) { if (strcmp(argv[c], "-c") == 0) { if (++c >= argc) { printf("Runtime-Error: -c option requires an input argument\n"); usage(argv[0]); return 1; } else cfg_file = argv[c]; } ++c; } if (!ConfigMgr::Load(cfg_file)) { printf("Invalid or missing configuration file : %s\n", cfg_file); printf("Verify that the file exists and has \'[authserver]\' written in the top of the file!\n"); return 1; } TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "%s (authserver)", _FULLVERSION); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "<Ctrl-C> to stop.\n"); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Using configuration file %s.", cfg_file); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " ##### # # ####### "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " # # ## ##### # # ## ## #### # # ###### # # # # ##### "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " # # # # # # # # # # # # # # # # # # # # # "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " # #### # # # # # # # # # # # # ##### # # # # # "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " # # ###### ##### # # # # # # # # # # # # # "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " # # # # # # # # # # # # # # # # # # # "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " ##### # # # # # # # #### ## ###### ####### #### # "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " C O R E "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " Based on TrinityCore http://www.TrinityCore.org\n "); TC_LOG_WARN(LOG_FILTER_AUTHSERVER, "%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true); #else ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true); #endif TC_LOG_DEBUG(LOG_FILTER_AUTHSERVER, "Max allowed open files is %d", ACE::max_handles()); // authserver PID file creation std::string pidfile = ConfigMgr::GetStringDefault("PidFile", ""); if (!pidfile.empty()) { uint32 pid = CreatePIDFile(pidfile); if (!pid) { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Cannot create PID file %s.\n", pidfile.c_str()); return 1; } TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Daemon PID: %u\n", pid); } // Initialize the database connection if (!StartDB()) return 1; // Get the list of realms for the server sRealmList->Initialize(ConfigMgr::GetIntDefault("RealmsStateUpdateDelay", 20)); if (sRealmList->size() == 0) { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "No valid realms specified."); return 1; } // Launch the listening network socket RealmAcceptor acceptor; int32 rmport = ConfigMgr::GetIntDefault("RealmServerPort", 3724); if (rmport < 0 || rmport > 0xFFFF) { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Specified port out of allowed range (1-65535)"); return 1; } std::string bind_ip = ConfigMgr::GetStringDefault("BindIP", "0.0.0.0"); ACE_INET_Addr bind_addr(uint16(rmport), bind_ip.c_str()); if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1) { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Auth server can not bind to %s:%d", bind_ip.c_str(), rmport); return 1; } // Initialise the signal handlers AuthServerSignalHandler SignalINT, SignalTERM; // Register authservers's signal handlers ACE_Sig_Handler Handler; Handler.register_handler(SIGINT, &SignalINT); Handler.register_handler(SIGTERM, &SignalTERM); ///- Handle affinity for multiple processors and process priority on Windows #ifdef _WIN32 { HANDLE hProcess = GetCurrentProcess(); uint32 Aff = ConfigMgr::GetIntDefault("UseProcessors", 0); if (Aff > 0) { ULONG_PTR appAff; ULONG_PTR sysAff; if (GetProcessAffinityMask(hProcess, &appAff, &sysAff)) { ULONG_PTR curAff = Aff & appAff; // remove non accessible processors if (!curAff) TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Processors marked in UseProcessors bitmask (hex) %x not accessible for authserver. Accessible processors bitmask (hex): %x", Aff, appAff); else if (SetProcessAffinityMask(hProcess, curAff)) TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Using processors (bitmask, hex): %x", curAff); else TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Can't set used processors (hex): %x", curAff); } } bool Prio = ConfigMgr::GetBoolDefault("ProcessPriority", false); if (Prio) { if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "The auth server process priority class has been set to HIGH"); else TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Can't set auth server process priority class."); } } #endif // maximum counter for next ping uint32 numLoops = (ConfigMgr::GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000)); uint32 loopCounter = 0; // Wait for termination signal while (!stopEvent) { // dont move this outside the loop, the reactor will modify it ACE_Time_Value interval(0, 100000); if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1) break; if ((++loopCounter) == numLoops) { loopCounter = 0; TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Ping MySQL to keep connection alive"); LoginDatabase.KeepAlive(); } } // Close the Database Pool and library StopDB(); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Halting process..."); return 0; }
/// Launch the auth server extern int main(int argc, char** argv) { // Command line parsing to get the configuration file name char const* configFile = _TRINITY_REALM_CONFIG; int count = 1; while (count < argc) { if (strcmp(argv[count], "-c") == 0) { if (++count >= argc) { printf("Runtime-Error: -c option requires an input argument\n"); usage(argv[0]); return 1; } else configFile = argv[count]; } ++count; } if (!sConfigMgr->LoadInitial(configFile)) { printf("Invalid or missing configuration file : %s\n", configFile); printf("Verify that the file exists and has \'[authserver]\' written in the top of the file!\n"); return 1; } TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "%s (authserver)", _FULLVERSION); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " A World of Warcraft: Cataclysm Core Emulator "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " _/_/ _/ _/_/_/ _/_/ _/_/_/ _/_/_/_/ "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " _/ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " _/_/_/_/ _/_/ _/_/ _/ _/ _/ _/_/_/ _/_/_/ "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " _/ _/ _/ _/ _/ _/_/_/ _/_/ _/ _/ _/_/_/_/ "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " Arkania Community (c) 2013"); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " <http://arkania.net/>"); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, " "); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "<Ctrl-C> to stop.\n"); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Using configuration file %s.", configFile); TC_LOG_WARN(LOG_FILTER_AUTHSERVER, "%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true); #else ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true); #endif TC_LOG_DEBUG(LOG_FILTER_AUTHSERVER, "Max allowed open files is %d", ACE::max_handles()); // authserver PID file creation std::string pidFile = sConfigMgr->GetStringDefault("PidFile", ""); if (!pidFile.empty()) { if (uint32 pid = CreatePIDFile(pidFile)) TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Daemon PID: %u\n", pid); else { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Cannot create PID file %s.\n", pidFile.c_str()); return 1; } } // Initialize the database connection if (!StartDB()) return 1; // Get the list of realms for the server sRealmList->Initialize(sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20)); if (sRealmList->size() == 0) { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "No valid realms specified."); return 1; } // Launch the listening network socket RealmAcceptor acceptor; int32 rmport = sConfigMgr->GetIntDefault("RealmServerPort", 3724); if (rmport < 0 || rmport > 0xFFFF) { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Specified port out of allowed range (1-65535)"); return 1; } std::string bind_ip = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); ACE_INET_Addr bind_addr(uint16(rmport), bind_ip.c_str()); if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1) { TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Auth server can not bind to %s:%d", bind_ip.c_str(), rmport); return 1; } // Initialize the signal handlers AuthServerSignalHandler SignalINT, SignalTERM; // Register authservers's signal handlers ACE_Sig_Handler Handler; Handler.register_handler(SIGINT, &SignalINT); Handler.register_handler(SIGTERM, &SignalTERM); ///- Handle affinity for multiple processors and process priority uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0); bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false); #ifdef _WIN32 // Windows { HANDLE hProcess = GetCurrentProcess(); if (affinity > 0) { ULONG_PTR appAff; ULONG_PTR sysAff; if (GetProcessAffinityMask(hProcess, &appAff, &sysAff)) { ULONG_PTR currentAffinity = affinity & appAff; // remove non accessible processors if (!currentAffinity) TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the authserver. Accessible processors bitmask (hex): %x", affinity, appAff); else if (SetProcessAffinityMask(hProcess, currentAffinity)) TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Using processors (bitmask, hex): %x", currentAffinity); else TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Can't set used processors (hex): %x", currentAffinity); } } if (highPriority) { if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "authserver process priority class set to HIGH"); else TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Can't set authserver process priority class."); } } #elif __linux__ // Linux if (affinity > 0) { cpu_set_t mask; CPU_ZERO(&mask); for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i) if (affinity & (1 << i)) CPU_SET(i, &mask); if (sched_setaffinity(0, sizeof(mask), &mask)) TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno)); else { CPU_ZERO(&mask); sched_getaffinity(0, sizeof(mask), &mask); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Using processors (bitmask, hex): %x", *(uint32*)(&mask)); } } if (highPriority) { if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY)) TC_LOG_ERROR(LOG_FILTER_AUTHSERVER, "Can't set authserver process priority class, error: %s", strerror(errno)); else TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "authserver process priority class set to %i", getpriority(PRIO_PROCESS, 0)); } #endif // maximum counter for next ping uint32 numLoops = (sConfigMgr->GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000)); uint32 loopCounter = 0; // Wait for termination signal while (!stopEvent) { // dont move this outside the loop, the reactor will modify it ACE_Time_Value interval(0, 100000); if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1) break; if ((++loopCounter) == numLoops) { loopCounter = 0; TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Ping MySQL to keep connection alive"); LoginDatabase.KeepAlive(); } } // Close the Database Pool and library StopDB(); TC_LOG_INFO(LOG_FILTER_AUTHSERVER, "Halting process..."); return 0; }
void WorldSession::HandleSendMail(WorldPackets::Mail::SendMail& packet) { if (packet.Info.Attachments.size() > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); return; } if (!CanOpenMailBox(packet.Info.Mailbox)) return; if (packet.Info.Target.empty()) return; Player* player = _player; if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } ObjectGuid receiverGuid; if (normalizePlayerName(packet.Info.Target)) receiverGuid = ObjectMgr::GetPlayerGUIDByName(packet.Info.Target); if (!receiverGuid) { TC_LOG_INFO("network", "Player %s is sending mail to %s (GUID: not existing!) with subject %s " "and body %s includes " SZFMTD " items, " SI64FMTD " copper and " SI64FMTD " COD copper with StationeryID = %d", GetPlayerInfo().c_str(), packet.Info.Target.c_str(), packet.Info.Subject.c_str(), packet.Info.Body.c_str(), packet.Info.Attachments.size(), packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID); player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } if (packet.Info.SendMoney < 0) { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_INTERNAL_ERROR); TC_LOG_WARN("cheat", "Player %s attempted to send mail to %s (%s) with negative money value (SendMoney: " SI64FMTD ")", GetPlayerInfo().c_str(), packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), packet.Info.SendMoney); return; } if (packet.Info.Cod < 0) { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_INTERNAL_ERROR); TC_LOG_WARN("cheat", "Player %s attempted to send mail to %s (%s) with negative COD value (Cod: " SI64FMTD ")", GetPlayerInfo().c_str(), packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), packet.Info.Cod); return; } TC_LOG_INFO("network", "Player %s is sending mail to %s (%s) with subject %s and body %s " "includes " SZFMTD " items, " SI64FMTD " copper and " SI64FMTD " COD copper with StationeryID = %d", GetPlayerInfo().c_str(), packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), packet.Info.Subject.c_str(), packet.Info.Body.c_str(), packet.Info.Attachments.size(), packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID); if (player->GetGUID() == receiverGuid) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 cost = !packet.Info.Attachments.empty() ? 30 * packet.Info.Attachments.size() : 30; // price hardcoded in client int64 reqmoney = cost + packet.Info.SendMoney; // Check for overflow if (reqmoney < packet.Info.SendMoney) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } if (!player->HasEnoughMoney(reqmoney) && !player->IsGameMaster()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receiver = ObjectAccessor::FindConnectedPlayer(receiverGuid); uint32 receiverTeam = 0; uint8 mailsCount = 0; //do not allow to send to one player more than 100 mails uint8 receiverLevel = 0; uint32 receiverAccountId = 0; uint32 receiverBnetAccountId = 0; if (receiver) { receiverTeam = receiver->GetTeam(); mailsCount = receiver->GetMailSize(); receiverLevel = receiver->getLevel(); receiverAccountId = receiver->GetSession()->GetAccountId(); receiverBnetAccountId = receiver->GetSession()->GetBattlenetAccountId(); } else { receiverTeam = ObjectMgr::GetPlayerTeamByGUID(receiverGuid); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_COUNT); stmt->setUInt64(0, receiverGuid.GetCounter()); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (result) { Field* fields = result->Fetch(); mailsCount = fields[0].GetUInt64(); } stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_LEVEL); stmt->setUInt64(0, receiverGuid.GetCounter()); result = CharacterDatabase.Query(stmt); if (result) { Field* fields = result->Fetch(); receiverLevel = fields[0].GetUInt8(); } receiverAccountId = ObjectMgr::GetPlayerAccountIdByGUID(receiverGuid); receiverBnetAccountId = Battlenet::AccountMgr::GetIdByGameAccount(receiverAccountId); } // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mailsCount > 100) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // test the receiver's Faction... or all items are account bound bool accountBound = !packet.Info.Attachments.empty(); for (auto const& att : packet.Info.Attachments) { if (Item* item = player->GetItemByGuid(att.ItemGUID)) { ItemTemplate const* itemProto = item->GetTemplate(); if (!itemProto || !(itemProto->GetFlags() & ITEM_FLAG_IS_BOUND_TO_ACCOUNT)) { accountBound = false; break; } } } if (!accountBound && player->GetTeam() != receiverTeam && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } if (receiverLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } std::vector<Item*> items; for (auto const& att : packet.Info.Attachments) { if (att.ItemGUID.IsEmpty()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = player->GetItemByGuid(att.ItemGUID); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != receiverAccountId) { if (!item->IsBattlenetAccountBound() || !player->GetSession()->GetBattlenetAccountId() || player->GetSession()->GetBattlenetAccountId() != receiverBnetAccountId) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_NOT_SAME_ACCOUNT); return; } } if (item->GetTemplate()->GetFlags() & ITEM_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (packet.Info.Cod && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsNotEmptyBag()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_DESTROY_NONEMPTY_BAG); return; } items.push_back(item); } player->SendMailResult(0, MAIL_SEND, MAIL_OK); player->ModifyMoney(-reqmoney); player->UpdateCriteria(CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(packet.Info.Subject, packet.Info.Body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (!packet.Info.Attachments.empty() || packet.Info.SendMoney > 0) { bool log = HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE); if (!packet.Info.Attachments.empty()) { for (auto const& item : items) { if (log) { sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail item: %s (Entry: %u Count: %u) " "to: %s (%s) (Account: %u)", GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), item->GetTemplate()->GetDefaultLocaleName(), item->GetEntry(), item->GetCount(), packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory item->SetOwnerGUID(receiverGuid); item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone draft.AddItem(item); } // if item send to character at another account, then apply item delivery delay needItemDelay = player->GetSession()->GetAccountId() != receiverAccountId; } if (log && packet.Info.SendMoney > 0) { sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail money: " SI64FMTD " to: %s (%s) (Account: %u)", GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), packet.Info.SendMoney, packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // Mail sent between guild members arrives instantly if (Guild* guild = sGuildMgr->GetGuildById(player->GetGuildId())) if (guild->IsMember(receiverGuid)) deliver_delay = 0; // don't ask for COD if there are no items if (packet.Info.Attachments.empty()) packet.Info.Cod = 0; // will delete item or place to receiver mail list draft .AddMoney(packet.Info.SendMoney) .AddCOD(packet.Info.Cod) .SendMailTo(trans, MailReceiver(receiver, receiverGuid.GetCounter()), MailSender(player), packet.Info.Body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); player->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); }
void WorldSession::HandleMovementOpcodes(WorldPacket & recvData) { uint16 opcode = recvData.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL; Vehicle* vehMover = mover->GetVehicleKit(); if (vehMover) if (mover->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) if (Unit* charmer = mover->GetCharmer()) if (charmer->GetTypeId() == TYPEID_PLAYER) plrMover = (Player*)charmer; // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvData.rfinish(); // prevent warnings spam return; } /* extract packet */ ObjectGuid guid; recvData >> guid.ReadAsPacked(); MovementInfo movementInfo; movementInfo.guid = guid; ReadMovementInfo(recvData, &movementInfo); recvData.rfinish(); // prevent warnings spam // prevent tampered movement data if (guid != mover->GetGUID()) return; if (!movementInfo.pos.IsPositionValid()) { recvData.rfinish(); // prevent warnings spam return; } /* handle special cases */ if (movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT)) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.transport.pos.GetPositionX() > 50 || movementInfo.transport.pos.GetPositionY() > 50 || movementInfo.transport.pos.GetPositionZ() > 50) { recvData.rfinish(); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation())) { recvData.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover && !plrMover->m_transport && !plrMover->m_temp_transport) { if (!plrMover->GetTransport()) { if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { plrMover->GetTransport()->RemovePassenger(plrMover); if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); else movementInfo.transport.Reset(); } } if (!plrMover->m_transport) if (Map *tempMap = mover->GetMap()) if (GameObject *tempTransport = tempMap->GetGameObject(movementInfo.transport.guid)) if (tempTransport->IsTransport()) plrMover->m_temp_transport = tempTransport; if ((!plrMover && !mover->GetTransport() && !mover->GetVehicle()) || (plrMover && !plrMover->m_vehicle && !plrMover->m_transport && !plrMover->m_temp_transport)) // Not sure if the first part is needed. Just added it for verbosity. { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ONTRANSPORT); } } else if (plrMover && (plrMover->m_transport || plrMover->m_temp_transport)) // if we were on a transport, leave { if (plrMover->m_transport) { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; } plrMover->m_temp_transport = NULL; movementInfo.transport.Reset(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) { // movement anticheat plrMover->m_anti_JumpCount = 0; plrMover->m_anti_JumpBaseZ = 0; if (!vehMover) plrMover->HandleFall(movementInfo); } if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } if (plrMover) sAnticheatMgr->StartHackDetection(plrMover, movementInfo, opcode); uint32 mstime = getMSTime(); /*----------------------*/ if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; // begin anti cheat bool check_passed = true; #ifdef ANTICHEAT_DEBUG TC_LOG_WARN("cheat", "AC2-%s > time: %d fall-time: %d | xyzo: %f, %f, %fo(%f) flags[%X] opcode[%s] | transport (xyzo): %f, %f, %fo(%f)", plrMover->GetName(), movementInfo.time, movementInfo.fallTime, movementInfo.pos.m_positionX, movementInfo.pos.m_positionY, movementInfo.pos.m_positionZ, movementInfo.pos.m_orientation, movementInfo.flags, LookupOpcodeName(opcode), movementInfo.transport.pos.m_positionX, movementInfo.transport.pos.m_positionY, movementInfo.transport.pos.m_positionZ, movementInfo.transport.pos.m_orientation); TC_LOG_WARN("cheat", "AC2-%s Transport > GUID: (low)%d - (high)%d", plrMover->GetName(), GUID_LOPART(movementInfo.transport.guid), GUID_HIPART(movementInfo.transport.guid)); #endif if (plrMover) { if (World::GetEnableMvAnticheat() && !plrMover->IsGameMaster() && !plrMover->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster() && !plrMover->GetCharmerOrOwnerPlayerOrPlayerItself()->GetVehicle()) { // calc time deltas int32 cClientTimeDelta = 1500; if (plrMover->m_anti_LastClientTime != 0) { cClientTimeDelta = movementInfo.time - plrMover->m_anti_LastClientTime; plrMover->m_anti_DeltaClientTime += cClientTimeDelta; plrMover->m_anti_LastClientTime = movementInfo.time; } else plrMover->m_anti_LastClientTime = movementInfo.time; const uint64 cServerTime = getMSTime(); uint32 cServerTimeDelta = 1500; if (plrMover->m_anti_LastServerTime != 0) { cServerTimeDelta = cServerTime - plrMover->m_anti_LastServerTime; plrMover->m_anti_DeltaServerTime += cServerTimeDelta; plrMover->m_anti_LastServerTime = cServerTime; } else plrMover->m_anti_LastServerTime = cServerTime; // resync times on client login (first 15 sec for heavy areas) if (plrMover->m_anti_DeltaServerTime < 15000 && plrMover->m_anti_DeltaClientTime < 15000) plrMover->m_anti_DeltaClientTime = plrMover->m_anti_DeltaServerTime; const int32 sync_time = plrMover->m_anti_DeltaClientTime - plrMover->m_anti_DeltaServerTime; #ifdef ANTICHEAT_DEBUG TC_LOG_WARN("cheat", "AC2-%s Time > cClientTimeDelta: %d, cServerTime: %d | deltaC: %d - deltaS: %d | SyncTime: %d", plrMover->GetName(), cClientTimeDelta, cServerTime, plrMover->m_anti_DeltaClientTime, plrMover->m_anti_DeltaServerTime, sync_time); #endif // mistiming checks const int32 GetMistimingDelta = abs(int32(World::GetMistimingDelta())); if (sync_time > GetMistimingDelta) { cClientTimeDelta = cServerTimeDelta; ++(plrMover->m_anti_MistimingCount); const bool bMistimingModulo = plrMover->m_anti_MistimingCount % 50 == 0; if (bMistimingModulo) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, mistiming exception #%d, mistiming: %dms Action: %s", plrMover->GetName().c_str(), plrMover->m_anti_MistimingCount, sync_time, &Warden::Penalty); #endif check_passed = false; } //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); /* Disabled, not passive at all, and apparently causing crashes: if (plrMover->m_anti_MistimingCount > World::GetMistimingAlarms()) { sWorld.SendWorldText(3, strcat("Kicking cheater: ", plrMover->GetName())); KickPlayer(); return; } */ } // end mistiming checks const uint32 curDest = plrMover->m_taxi.GetTaxiDestination(); // check taxi flight if (!curDest) { UnitMoveType move_type; // calculating section // current speed if (movementInfo.flags & MOVEMENTFLAG_FLYING) move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_FLIGHT_BACK : MOVE_FLIGHT; else if (movementInfo.flags & MOVEMENTFLAG_SWIMMING) move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_SWIM_BACK : MOVE_SWIM; else if (movementInfo.flags & MOVEMENTFLAG_WALKING) move_type = MOVE_WALK; // hmm... in first time after login player has MOVE_SWIMBACK instead MOVE_WALKBACK else move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_SWIM_BACK : MOVE_RUN; const float current_speed = mover->GetSpeed(move_type); // end current speed // movement distance const float delta_x = plrMover->m_transport || plrMover->m_temp_transport ? 0 : plrMover->GetPositionX() - movementInfo.pos.GetPositionX(); const float delta_y = plrMover->m_transport || plrMover->m_temp_transport ? 0 : plrMover->GetPositionY() - movementInfo.pos.GetPositionY(); const float delta_z = plrMover->m_transport || plrMover->m_temp_transport ? 0 : plrMover->GetPositionZ() - movementInfo.pos.GetPositionZ(); const float real_delta = plrMover->m_transport || plrMover->m_temp_transport ? 0 : pow(delta_x, 2) + pow(delta_y, 2); // end movement distance const bool no_fly_auras = !(plrMover->HasAuraType(SPELL_AURA_FLY) || plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED) || plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || plrMover->HasAuraType(SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS) || plrMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK)); const bool no_fly_flags = (movementInfo.flags & (MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_FLYING)) == 0; const bool no_swim_flags = (movementInfo.flags & MOVEMENTFLAG_SWIMMING) == 0; const bool no_swim_in_water = !mover->IsInWater(); const bool no_swim_above_water = movementInfo.pos.GetPositionZ() - 7.0f >= mover->GetBaseMap()->GetWaterLevel(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY()); const bool no_swim_water = no_swim_in_water && no_swim_above_water; const bool no_waterwalk_flags = (movementInfo.flags & MOVEMENTFLAG_WATERWALKING) == 0; const bool no_waterwalk_auras = !(plrMover->HasAuraType(SPELL_AURA_WATER_WALK) || plrMover->HasAuraType(SPELL_AURA_GHOST)); if (cClientTimeDelta < 0) cClientTimeDelta = 0; const float time_delta = cClientTimeDelta < 1500 ? float(cClientTimeDelta) / 1000.0f : 1.5f; // normalize time - 1.5 second allowed for heavy loaded server const float tg_z = (real_delta != 0 && no_fly_auras && no_swim_flags) ? (pow(delta_z, 2) / real_delta) : -99999; // movement distance tangents if (current_speed < plrMover->m_anti_Last_HSpeed && plrMover->m_anti_LastSpeedChangeTime == 0) plrMover->m_anti_LastSpeedChangeTime = movementInfo.time + uint32(floor(((plrMover->m_anti_Last_HSpeed / current_speed) * 1500)) + 100); // 100ms above for random fluctuation const float allowed_delta = plrMover->m_transport || plrMover->m_temp_transport ? 2 : // movement distance allowed delta pow(std::max(current_speed, plrMover->m_anti_Last_HSpeed) * time_delta, 2) +2 // minimum allowed delta +(tg_z > 2.2 ? pow(delta_z, 2) / 2.37f : 0); // mountain fall allowed delta if (movementInfo.time > plrMover->m_anti_LastSpeedChangeTime) { plrMover->m_anti_Last_HSpeed = current_speed; // store current speed plrMover->m_anti_Last_VSpeed = -2.3f; plrMover->m_anti_LastSpeedChangeTime = 0; } // end calculating section // AntiGravity (thanks to Meekro) const float JumpHeight = plrMover->m_anti_JumpBaseZ - movementInfo.pos.GetPositionZ(); if (no_fly_auras && no_swim_in_water && plrMover->m_anti_JumpBaseZ != 0 && JumpHeight < plrMover->m_anti_Last_VSpeed) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, AntiGravity exception. JumpHeight = %f, Allowed Vertical Speed = %f", plrMover->GetName().c_str(), JumpHeight, plrMover->m_anti_Last_VSpeed); #endif check_passed = false; //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); } // multi jump checks if (opcode == MSG_MOVE_JUMP) { if (no_fly_auras && no_swim_water) { if (plrMover->m_anti_JumpCount >= 1) { // don't process new jump packet check_passed = false; #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, Multijump exception.", plrMover->GetName().c_str(), JumpHeight, plrMover->m_anti_Last_VSpeed); #endif //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); plrMover->m_anti_JumpCount = 0; } else { plrMover->m_anti_JumpCount += 1; plrMover->m_anti_JumpBaseZ = movementInfo.pos.GetPositionZ(); } } else plrMover->m_anti_JumpCount = 0; } // speed and teleport hack checks if (real_delta > allowed_delta) { #ifdef ANTICHEAT_EXCEPTION_INFO if (real_delta < 4900.0f) { TC_LOG_WARN("cheat", "AC2-%s, speed exception | cDelta=%f aDelta=%f | cSpeed=%f lSpeed=%f deltaTime=%f", plrMover->GetName().c_str(), real_delta, allowed_delta, current_speed, plrMover->m_anti_Last_HSpeed, time_delta); } else { TC_LOG_WARN("cheat", "AC2-%s, teleport exception | cDelta=%f aDelta=%f | cSpeed=%f lSpeed=%f deltaTime=%f", plrMover->GetName().c_str(), real_delta, allowed_delta, current_speed, plrMover->m_anti_Last_HSpeed, time_delta); } #endif check_passed = false; //if (vehMover) // vehMover->Die(); //plrMover->FallGround(2); } // mountain hack checks // 1.56f (delta_z < GetPlayer()->m_anti_Last_VSpeed)) if (delta_z < plrMover->m_anti_Last_VSpeed && plrMover->m_anti_JumpCount == 0 && tg_z > 2.37f) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, mountain exception | tg_z=%f", plrMover->GetName().c_str(), tg_z); #endif check_passed = false; // if (vehMover) // vehMover->Die(); } // Fly hack checks if (no_fly_auras && !no_fly_flags) { #ifdef ANTICHEAT_EXCEPTION_INFO // Aura numbers: 201, 206, 207, 208, 209, 211 //TC_LOG_WARN("cheat", "AC2-%s, flight exception. {SPELL_AURA_FLY=[%X]} {SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED=[%X]} {SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED=[%X]} {SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS=[%X]} {SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK=[%X]} {plrMover->GetVehicle()=[%X]}", plrMover->GetName().c_str(), //Last Updated By ShopWoW.ir plrMover->HasAuraType(SPELL_AURA_FLY), plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED), plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED), plrMover->HasAuraType(SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS), plrMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK), plrMover->GetVehicle(); #endif check_passed = false; //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); } // Waterwalk checks if (no_waterwalk_auras && !no_waterwalk_flags) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, waterwalk exception. [%X]{SPELL_AURA_WATER_WALK=[%X]}", plrMover->GetName().c_str(), movementInfo.flags, plrMover->HasAuraType(SPELL_AURA_WATER_WALK)); #endif check_passed = false; //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); } // Teleport To Plane checks if (no_swim_in_water && movementInfo.pos.GetPositionZ() < 0.0001f && movementInfo.pos.GetPositionZ() > -0.0001f) { if (const Map *map = plrMover->GetMap()) { float plane_z = map->GetHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), MAX_HEIGHT) - movementInfo.pos.GetPositionZ(); plane_z = (plane_z < -500.0f) ? 0.0f : plane_z; // check holes in height map if (plane_z > 0.1f || plane_z < -0.1f) { #ifdef ANTICHEAT_DEBUG TC_LOG_WARN("cheat", "AC2-%s, teleport to plane exception. plane_z: %f", plrMover->GetName(), plane_z); #endif #ifdef ANTICHEAT_EXCEPTION_INFO if (plrMover->m_anti_TeleToPlane_Count > World::GetTeleportToPlaneAlarms()) { TC_LOG_WARN("cheat", "AC2-%s, teleport to plane exception. Exception count: %d", plrMover->GetName().c_str(), plrMover->m_anti_TeleToPlane_Count); /* Disabled, not passive at all, and apparently causing crashes: sWorld.SendWorldText(3, strcat("Kicking cheater: ", plrMover->GetName())); KickPlayer(); return; */ } #endif ++(plrMover->m_anti_TeleToPlane_Count); check_passed = false; //if (vehMover) // vehMover->Die(); } } } else plrMover->m_anti_TeleToPlane_Count = 0; } } } /* process position-change */ if (check_passed) { WorldPacket data(opcode, recvData.size()); movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); WriteMovementInfo(&data, &movementInfo); mover->SendMessageToSet(&data, _player); mover->m_movementInfo = movementInfo; // Some vehicles allow the passenger to turn by himself if (Vehicle* vehicle = mover->GetVehicle()) { if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(mover)) { if (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING) { if (movementInfo.pos.GetOrientation() != mover->GetOrientation()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); } } } return; } mover->UpdatePosition(movementInfo.pos); if (plrMover && !vehMover) // nothing is charmed, or player charmed { if (plrMover->IsSitState() && (movementInfo.flags & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING))) plrMover->SetStandState(UNIT_STAND_STATE_STAND); plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); float underMapValueZ; switch (plrMover->GetMapId()) { case 617: underMapValueZ = 3.0f; break; // Dalaran Sewers case 618: underMapValueZ = 28.0f; break; // Ring of Valor default: underMapValueZ = -500.0f; break; } if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY()) || movementInfo.pos.GetPositionZ() < underMapValueZ) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { plrMover->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->IsAlive()) plrMover->KillPlayer(); } } } // movement anticheat if (plrMover->m_anti_AlarmCount > 0) { TC_LOG_WARN("cheat", "AC2-%s produce %d anticheat alarms.", plrMover->GetName().c_str(), plrMover->m_anti_AlarmCount); plrMover->m_anti_AlarmCount = 0; } // end movement anticheat } } else if (plrMover) { if (plrMover->m_transport) { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; } plrMover->m_temp_transport = NULL; ++(plrMover->m_anti_AlarmCount); WorldPacket data; plrMover->SetUnitMovementFlags(0); plrMover->SendTeleportAckPacket(); plrMover->BuildHeartBeatMsg(&data); plrMover->SendMessageToSet(&data, true); } }