void SocketHandler::SendPacket(const WorldPacket& data) const { m_socket->write(data.GetPacket().toLatin1() + (char)0x00); Log::Write(LOG_TYPE_DEBUG, "Send packet %s ( Header : %s )", GetOpcodeName(data.GetOpcode()).toLatin1().data(), GetOpcodeHeader(data.GetOpcode()).toLatin1().data()); if(data.GetPacket().length() > 0) Log::Write(LOG_TYPE_DEBUG, "Packet data : %s", QString(data.GetPacket()).toLatin1().data()); }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(PacketFilter& updater) { std::lock_guard<std::mutex> guard(m_recvQueueLock); ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed while (m_Socket && !m_Socket->IsClosed() && !m_recvQueue.empty()) { auto const packet = std::move(m_recvQueue.front()); m_recvQueue.pop_front(); /*#if 1 sLog.outError( "MOEP: %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); #endif*/ OpcodeHandler const& opHandle = opcodeTable[packet->GetOpcode()]; try { switch (opHandle.status) { case STATUS_LOGGEDIN: if (!_player) { // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets if (!m_playerRecentlyLogout) LogUnexpectedOpcode(*packet, "the player has not logged in yet"); } else if (_player->IsInWorld()) ExecuteOpcode(opHandle, *packet); // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer break; case STATUS_LOGGEDIN_OR_RECENTLY_LOGGEDOUT: if (!_player && !m_playerRecentlyLogout) { LogUnexpectedOpcode(*packet, "the player has not logged in yet and not recently logout"); } else // not expected _player or must checked in packet hanlder ExecuteOpcode(opHandle, *packet); break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(*packet, "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(*packet, "the player is still in world"); else ExecuteOpcode(opHandle, *packet); break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(*packet, "the player not pass queue yet"); break; } // single from authed time opcodes send in to after logout time // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes. if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL) m_playerRecentlyLogout = false; ExecuteOpcode(opHandle, *packet); break; case STATUS_NEVER: sLog.outError("SESSION: received not allowed opcode %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); break; case STATUS_UNHANDLED: DEBUG_LOG("SESSION: received not handled opcode %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); break; default: sLog.outError("SESSION: received wrong-status-req opcode %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); break; } } catch (ByteBufferException&) { sLog.outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog.HasLogLevelOrHigher(LOG_LVL_DEBUG)) { DEBUG_LOG("Dumping error causing packet:"); packet->hexlike(); } if (sWorld.getConfig(CONFIG_BOOL_KICK_PLAYER_ON_BAD_PACKET)) { DETAIL_LOG("Disconnecting session [account id %u / address %s] for badly formatted packet.", GetAccountId(), GetRemoteAddress().c_str()); KickPlayer(); } } } // check if we are safe to proceed with logout // logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { ///- If necessary, log the player out const time_t currTime = time(nullptr); if (m_Socket->IsClosed() || (ShouldLogOut(currTime) && !m_playerLoading)) LogoutPlayer(true); // finalize the session if disconnected. if (m_Socket->IsClosed()) return false; } return true; }
//Returns -1 if this thread is dead and needs to be killed int CCobThread::Tick(int deltaTime) { if (state == Sleep) { logOutput.Print("CobError: sleeping thread ticked!"); } if (state == Dead || !owner) { return -1; } state = Run; GCobEngine.SetCurThread(this); int r1, r2, r3, r4, r5, r6; vector<int> args; CCobThread *thread; execTrace.clear(); delayedAnims.clear(); //list<int>::iterator ei; vector<int>::iterator ei; #if COB_DEBUG > 0 if (COB_DEBUG_FILTER) logOutput.Print("Executing in %s (from %s)", script.scriptNames[callStack.back().functionId].c_str(), GetName().c_str()); #endif while (state == Run) { //int opcode = *(int *)&script.code[PC]; //Disabling exec trace gives about a 50% speedup on vm-intensive code //execTrace.push_back(PC); int opcode = GET_LONG_PC(); #if COB_DEBUG > 1 if (COB_DEBUG_FILTER) logOutput.Print("PC: %x opcode: %x (%s)", PC - 1, opcode, GetOpcodeName(opcode).c_str()); #endif switch(opcode) { case PUSH_CONSTANT: r1 = GET_LONG_PC(); stack.push_back(r1); break; case SLEEP: r1 = POP(); wakeTime = GCurrentTime + r1; state = Sleep; GCobEngine.AddThread(this); GCobEngine.SetCurThread(NULL); #if COB_DEBUG > 0 if (COB_DEBUG_FILTER) logOutput.Print("%s sleeping for %d ms", script.scriptNames[callStack.back().functionId].c_str(), r1); #endif return 0; case SPIN: r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); r3 = POP(); //speed r4 = POP(); //accel owner->Spin(r1, r2, r3, r4); break; case STOP_SPIN: r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); r3 = POP(); //decel //logOutput.Print("Stop spin of %s around %d", script.pieceNames[r1].c_str(), r2); owner->StopSpin(r1, r2, r3); break; case RETURN: retCode = POP(); if (callStack.back().returnAddr == -1) { #if COB_DEBUG > 0 if (COB_DEBUG_FILTER) logOutput.Print("%s returned %d", script.scriptNames[callStack.back().functionId].c_str(), retCode); #endif state = Dead; GCobEngine.SetCurThread(NULL); //callStack.pop_back(); //Leave values intact on stack in case caller wants to check them return -1; } PC = callStack.back().returnAddr; while (stack.size() > callStack.back().stackTop) { stack.pop_back(); } callStack.pop_back(); #if COB_DEBUG > 0 if (COB_DEBUG_FILTER) logOutput.Print("Returning to %s", script.scriptNames[callStack.back().functionId].c_str()); #endif break; case SHADE: r1 = GET_LONG_PC(); break; case DONT_SHADE: r1 = GET_LONG_PC(); break; case CACHE: r1 = GET_LONG_PC(); break; case DONT_CACHE: r1 = GET_LONG_PC(); break; case CALL: { r1 = GET_LONG_PC(); PC--; const string& name = script.scriptNames[r1]; if (name.find("lua_") == 0) { script.code[PC - 1] = LUA_CALL; LuaCall(); break; } script.code[PC - 1] = REAL_CALL; // fall through // } case REAL_CALL: r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); if (script.scriptLengths[r1] == 0) { //logOutput.Print("Preventing call to zero-len script %s", script.scriptNames[r1].c_str()); break; } struct callInfo ci; ci.functionId = r1; ci.returnAddr = PC; ci.stackTop = stack.size() - r2; callStack.push_back(ci); paramCount = r2; PC = script.scriptOffsets[r1]; #if COB_DEBUG > 0 if (COB_DEBUG_FILTER) logOutput.Print("Calling %s", script.scriptNames[r1].c_str()); #endif break; case LUA_CALL: LuaCall(); break; case POP_STATIC: r1 = GET_LONG_PC(); r2 = POP(); owner->staticVars[r1] = r2; //logOutput.Print("Pop static var %d val %d", r1, r2); break; case POP_STACK: POP(); break; case START: r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); if (script.scriptLengths[r1] == 0) { //logOutput.Print("Preventing start of zero-len script %s", script.scriptNames[r1].c_str()); break; } args.clear(); for (r3 = 0; r3 < r2; ++r3) { r4 = POP(); args.push_back(r4); } thread = SAFE_NEW CCobThread(script, owner); thread->Start(r1, args, true); //Seems that threads should inherit signal mask from creator thread->signalMask = signalMask; #if COB_DEBUG > 0 if (COB_DEBUG_FILTER) logOutput.Print("Starting %s %d", script.scriptNames[r1].c_str(), signalMask); #endif break; case CREATE_LOCAL_VAR: if (paramCount == 0) { stack.push_back(0); } else { paramCount--; } break; case GET_UNIT_VALUE: r1 = POP(); if ((r1 >= LUA0) && (r1 <= LUA9)) { stack.push_back(luaArgs[r1 - LUA0]); break; } ForceCommitAllAnims(); // getunitval could possibly read piece locations r1 = owner->GetUnitVal(r1, 0, 0, 0, 0); stack.push_back(r1); break; case JUMP_NOT_EQUAL: r1 = GET_LONG_PC(); r2 = POP(); if (r2 == 0) { PC = r1; } break; case JUMP: r1 = GET_LONG_PC(); //this seem to be an error in the docs.. //r2 = script.scriptOffsets[callStack.back().functionId] + r1; PC = r1; break; case POP_LOCAL_VAR: r1 = GET_LONG_PC(); r2 = POP(); stack[callStack.back().stackTop + r1] = r2; break; case PUSH_LOCAL_VAR: r1 = GET_LONG_PC(); r2 = stack[callStack.back().stackTop + r1]; stack.push_back(r2); break; case SET_LESS_OR_EQUAL: r2 = POP(); r1 = POP(); if (r1 <= r2) stack.push_back(1); else stack.push_back(0); break; case BITWISE_AND: r1 = POP(); r2 = POP(); stack.push_back(r1 & r2); break; case BITWISE_OR: //seems to want stack contents or'd, result places on stack r1 = POP(); r2 = POP(); stack.push_back(r1 | r2); break; case BITWISE_XOR: r1 = POP(); r2 = POP(); stack.push_back(r1 ^ r2); break; case BITWISE_NOT: r1 = POP(); stack.push_back(~r1); break; case EXPLODE: r1 = GET_LONG_PC(); r2 = POP(); owner->Explode(r1, r2); break; case PLAY_SOUND: r1 = GET_LONG_PC(); r2 = POP(); owner->PlayUnitSound(r1, r2); break; case PUSH_STATIC: r1 = GET_LONG_PC(); stack.push_back(owner->staticVars[r1]); //logOutput.Print("Push static %d val %d", r1, owner->staticVars[r1]); break; case SET_NOT_EQUAL: r1 = POP(); r2 = POP(); if (r1 != r2) stack.push_back(1); else stack.push_back(0); break; case SET_EQUAL: r1 = POP(); r2 = POP(); if (r1 == r2) stack.push_back(1); else stack.push_back(0); break; case SET_LESS: r2 = POP(); r1 = POP(); if (r1 < r2) stack.push_back(1); else stack.push_back(0); break; case SET_GREATER: r2 = POP(); r1 = POP(); if (r1 > r2) stack.push_back(1); else stack.push_back(0); break; case SET_GREATER_OR_EQUAL: r2 = POP(); r1 = POP(); if (r1 >= r2) stack.push_back(1); else stack.push_back(0); break; case RAND: r2 = POP(); r1 = POP(); r3 = gs->randInt() % (r2 - r1 + 1) + r1; stack.push_back(r3); break; case EMIT_SFX: r1 = POP(); r2 = GET_LONG_PC(); owner->EmitSfx(r1, r2); break; case MUL: r1 = POP(); r2 = POP(); stack.push_back(r1 * r2); break; case SIGNAL: r1 = POP(); owner->Signal(r1); break; case SET_SIGNAL_MASK: r1 = POP(); signalMask = r1; break; case TURN: r2 = POP(); r1 = POP(); r3 = GET_LONG_PC(); r4 = GET_LONG_PC(); //logOutput.Print("Turning piece %s axis %d to %d speed %d", script.pieceNames[r3].c_str(), r4, r2, r1); ForceCommitAnim(1, r3, r4); owner->Turn(r3, r4, r1, r2); break; case GET: r5 = POP(); r4 = POP(); r3 = POP(); r2 = POP(); r1 = POP(); if ((r1 >= LUA0) && (r1 <= LUA9)) { stack.push_back(luaArgs[r1 - LUA0]); break; } ForceCommitAllAnims(); r6 = owner->GetUnitVal(r1, r2, r3, r4, r5); stack.push_back(r6); break; case ADD: r2 = POP(); r1 = POP(); stack.push_back(r1 + r2); break; case SUB: r2 = POP(); r1 = POP(); r3 = r1 - r2; stack.push_back(r3); break; case DIV: r2 = POP(); r1 = POP(); if (r2 != 0) r3 = r1 / r2; else { r3 = 1000; //infinity! logOutput.Print("CobError: division by zero"); } stack.push_back(r3); break; case MOD: r2 = POP(); r1 = POP(); if (r2 != 0) stack.push_back(r1 % r2); else { stack.push_back(0); logOutput.Print("CobError: modulo division by zero"); } break; case MOVE: r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); r4 = POP(); r3 = POP(); ForceCommitAnim(2, r1, r2); owner->Move(r1, r2, r3, r4); break; case MOVE_NOW:{ r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); r3 = POP(); if (owner->smoothAnim) { DelayedAnim a; a.type = 2; a.piece = r1; a.axis = r2; a.dest = r3; delayedAnims.push_back(a); //logOutput.Print("Delayed move %s %d %d", owner->pieces[r1].name.c_str(), r2, r3); } else { owner->MoveNow(r1, r2, r3); } break;} case TURN_NOW:{ r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); r3 = POP(); if (owner->smoothAnim) { DelayedAnim a; a.type = 1; a.piece = r1; a.axis = r2; a.dest = r3; delayedAnims.push_back(a); } else { owner->TurnNow(r1, r2, r3); } break;} case WAIT_TURN: r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); //logOutput.Print("Waiting for turn on piece %s around axis %d", script.pieceNames[r1].c_str(), r2); if (owner->AddTurnListener(r1, r2, this)) { state = WaitTurn; GCobEngine.SetCurThread(NULL); return 0; } else break; case WAIT_MOVE: r1 = GET_LONG_PC(); r2 = GET_LONG_PC(); //logOutput.Print("Waiting for move on piece %s on axis %d", script.pieceNames[r1].c_str(), r2); if (owner->AddMoveListener(r1, r2, this)) { state = WaitMove; GCobEngine.SetCurThread(NULL); return 0; } break; case SET: r2 = POP(); r1 = POP(); //logOutput.Print("Setting unit value %d to %d", r1, r2); if ((r1 >= LUA0) && (r1 <= LUA9)) { luaArgs[r1 - LUA0] = r2; break; } owner->SetUnitVal(r1, r2); break; case ATTACH: r3 = POP(); r2 = POP(); r1 = POP(); owner->AttachUnit(r2, r1); break; case DROP: r1 = POP(); owner->DropUnit(r1); break; case LOGICAL_NOT: //Like bitwise, but only on values 1 and 0. r1 = POP(); if (r1 == 0) stack.push_back(1); else stack.push_back(0); break; case LOGICAL_AND: r1 = POP(); r2 = POP(); if (r1 && r2) stack.push_back(1); else stack.push_back(0); break; case LOGICAL_OR: r1 = POP(); r2 = POP(); if (r1 || r2) stack.push_back(1); else stack.push_back(0); break; case LOGICAL_XOR: r1 = POP(); r2 = POP(); if (!!r1 ^ !!r2) stack.push_back(1); else stack.push_back(0); break; case HIDE: r1 = GET_LONG_PC(); owner->SetVisibility(r1, false); //logOutput.Print("Hiding %d", r1); break; case SHOW:{ r1 = GET_LONG_PC(); int i; for (i = 0; i < COB_MaxWeapons; ++i) if (callStack.back().functionId == script.scriptIndex[COBFN_FirePrimary + i]) break; // If true, we are in a Fire-script and should show a special flare effect if (i < COB_MaxWeapons) { owner->ShowFlare(r1); } else { owner->SetVisibility(r1, true); } //logOutput.Print("Showing %d", r1); break;} default: logOutput.Print("CobError: Unknown opcode %x (in %s:%s at %x)", opcode, script.name.c_str(), script.scriptNames[callStack.back().functionId].c_str(), PC - 1); logOutput.Print("Exec trace:"); ei = execTrace.begin(); while (ei != execTrace.end()) { logOutput.Print("PC: %3x opcode: %s", *ei, GetOpcodeName(script.code[*ei]).c_str()); ei++; } state = Dead; GCobEngine.SetCurThread(NULL); return -1; break; } } GCobEngine.SetCurThread(NULL); return 0; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(PacketFilter& updater) { std::lock_guard<std::mutex> guard(m_recvQueueLock); ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed while (m_Socket && !m_Socket->IsClosed() && !m_recvQueue.empty()) { auto const packet = std::move(m_recvQueue.front()); m_recvQueue.pop_front(); /*#if 1 sLog.outError( "MOEP: %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); #endif*/ OpcodeHandler const& opHandle = opcodeTable[packet->GetOpcode()]; try { switch (opHandle.status) { case STATUS_LOGGEDIN: if (!_player) { // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets if (!m_playerRecentlyLogout) LogUnexpectedOpcode(*packet, "the player has not logged in yet"); } else if (_player->IsInWorld()) ExecuteOpcode(opHandle, *packet); // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer #ifdef BUILD_PLAYERBOT if (_player && _player->GetPlayerbotMgr()) _player->GetPlayerbotMgr()->HandleMasterIncomingPacket(*packet); #endif break; case STATUS_LOGGEDIN_OR_RECENTLY_LOGGEDOUT: if (!_player && !m_playerRecentlyLogout) { LogUnexpectedOpcode(*packet, "the player has not logged in yet and not recently logout"); } else // not expected _player or must checked in packet hanlder ExecuteOpcode(opHandle, *packet); break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(*packet, "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(*packet, "the player is still in world"); else ExecuteOpcode(opHandle, *packet); break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(*packet, "the player not pass queue yet"); break; } // single from authed time opcodes send in to after logout time // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes. if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL) m_playerRecentlyLogout = false; ExecuteOpcode(opHandle, *packet); break; case STATUS_NEVER: sLog.outError("SESSION: received not allowed opcode %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); break; case STATUS_UNHANDLED: DEBUG_LOG("SESSION: received not handled opcode %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); break; default: sLog.outError("SESSION: received wrong-status-req opcode %s (0x%.4X)", packet->GetOpcodeName(), packet->GetOpcode()); break; } } catch (ByteBufferException&) { sLog.outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog.HasLogLevelOrHigher(LOG_LVL_DEBUG)) { DEBUG_LOG("Dumping error causing packet:"); packet->hexlike(); } if (sWorld.getConfig(CONFIG_BOOL_KICK_PLAYER_ON_BAD_PACKET)) { DETAIL_LOG("Disconnecting session [account id %u / address %s] for badly formatted packet.", GetAccountId(), GetRemoteAddress().c_str()); KickPlayer(); } } } #ifdef BUILD_PLAYERBOT // Process player bot packets // The PlayerbotAI class adds to the packet queue to simulate a real player // since Playerbots are known to the World obj only by its master's WorldSession object // we need to process all master's bot's packets. if (GetPlayer() && GetPlayer()->GetPlayerbotMgr()) { for (PlayerBotMap::const_iterator itr = GetPlayer()->GetPlayerbotMgr()->GetPlayerBotsBegin(); itr != GetPlayer()->GetPlayerbotMgr()->GetPlayerBotsEnd(); ++itr) { Player* const botPlayer = itr->second; WorldSession* const pBotWorldSession = botPlayer->GetSession(); if (botPlayer->IsBeingTeleported()) botPlayer->GetPlayerbotAI()->HandleTeleportAck(); else if (botPlayer->IsInWorld()) { while (!pBotWorldSession->m_recvQueue.empty()) { auto const botpacket = std::move(pBotWorldSession->m_recvQueue.front()); pBotWorldSession->m_recvQueue.pop_front(); OpcodeHandler const& opHandle = opcodeTable[botpacket->GetOpcode()]; pBotWorldSession->ExecuteOpcode(opHandle, *botpacket); }; pBotWorldSession->m_recvQueue.clear(); } } } #endif // check if we are safe to proceed with logout // logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { switch (m_sessionState) { case WORLD_SESSION_STATE_CREATED: { if (m_requestSocket) { if (!IsOffline()) SetOffline(); m_Socket = m_requestSocket; m_requestSocket = nullptr; sLog.outString("New Session key %s", m_Socket->GetSessionKey().AsHexStr()); SendAuthOk(); } else { if (m_inQueue) SendAuthQueued(); else SendAuthOk(); } m_sessionState = WORLD_SESSION_STATE_CHAR_SELECTION; return true; } case WORLD_SESSION_STATE_CHAR_SELECTION: // waiting to go online // TODO:: Maybe check if have to send queue update? if (!m_Socket || (m_Socket && m_Socket->IsClosed())) { // directly remove this session return false; } if (ShouldLogOut(time(nullptr)) && !m_playerLoading) // check if delayed logout is fired LogoutPlayer(true); return true; case WORLD_SESSION_STATE_READY: { if (m_Socket && m_Socket->IsClosed()) { if (!_player) return false; // give the opportunity for this player to reconnect within 20 sec SetOffline(); } else if (ShouldLogOut(time(nullptr)) && !m_playerLoading) // check if delayed logout is fired LogoutPlayer(true); return true; } case WORLD_SESSION_STATE_OFFLINE: { if (ShouldDisconnect(time(nullptr))) // check if delayed logout is fired { LogoutPlayer(true); if (!m_requestSocket && (!m_Socket || m_Socket->IsClosed())) return false; } return true; } default: break; } } return true; }