void GuildMgr::readGuildList() { QFile guildsfile(guildsFileName); m_guildMap.clear(); if (guildsfile.open(QIODevice::ReadOnly)) { while (!guildsfile.atEnd()) { char szGuildName[64] = {0}; guildsfile.readBlock(szGuildName, sizeof(szGuildName)); // seqDebug("GuildMgr::readGuildList - read guild '%s'", szGuildName); m_guildMap.push_back(QString::fromUtf8(szGuildName)); } guildsfile.close(); seqInfo("GuildMgr: Guildsfile loaded"); } else seqWarn("GuildMgr: Could not load guildsfile, %s", (const char*)guildsFileName); }
bool OPCodeXmlContentHandler::startElement(const QString&, const QString&, const QString& name, const QXmlAttributes& attr) { if (name == "opcode") { bool ok = false; // get the index of the id attribute int index = attr.index("id"); if (index == -1) { seqWarn("OPCodeXmlContentHandler::startElement(): opcode element without id!"); return false; // this is an error, something is wrong } // the id attribute is the opcode value uint16_t opcode = attr.value(index).toUShort(&ok, 16); #if 0 // ZBTEMP opcode += 2; #endif if (!ok) { seqWarn("OPCodeXmlContentHandler::startElement(): opcode '%s' failed to convert to uint16_t (result: %#04x)", attr.value(index).latin1(), opcode); return false; // this is an error } // get the index of the name attribute index = attr.index("name"); // if name attribute was found, set the opcode objects name if (index == -1) { seqWarn("OPCodeXmlContentHandler::startElement(): opcode %#04x missing name parameter!", opcode); return false; } // add/create the new opcode object m_currentOPCode = m_opcodeDB.add(opcode, attr.value(index)); if (!m_currentOPCode) { seqWarn("Failed to add opcode %04x", opcode); return false; } // get the index of the updated attribute index = attr.index("updated"); // if the updated attribute was found, set the objects updated field if (index != -1) m_currentOPCode->setUpdated(attr.value(index)); // get the index of the implicitlen attribute index = attr.index("implicitlen"); // if implicitlen attribute was found, set the objects implicitLen field if (index != -1) m_currentOPCode->setImplicitLen(attr.value(index).toUShort()); return true; } if ((name == "comment") && (m_currentOPCode)) { // clear any current comment m_currentComment = ""; m_inComment = true; return true; } if ((name == "payload") && (m_currentOPCode)) { // create a new payload object and make it the current one m_currentPayload = new EQPacketPayload(); // add the payload object to the opcode m_currentOPCode->append(m_currentPayload); // check for direction attribute int index = attr.index("dir"); // if an index attribute exists, then use it if (index != -1) { QString value = attr.value(index); if (value == "both") m_currentPayload->setDir(DIR_Client | DIR_Server); else if (value == "server") m_currentPayload->setDir(DIR_Server); else if (value == "client") m_currentPayload->setDir(DIR_Client); } // get the typename attribute index = attr.index("typename"); // if a typename attribute exist, then set the payload type if (index != -1) { QString value = attr.value(index); if (!value.isEmpty()) { if (!m_currentPayload->setType(m_typeDB, value)) seqWarn("Unknown payload typename '%s' for opcode '%04x'", value.latin1(), m_currentOPCode->opcode()); } } // attempt to retrieve the sizechecktype index = attr.index("sizechecktype"); // if a sizechecktype exists, then set the payload size check type if (index != -1) { QString value = attr.value(index); if (value.isEmpty() || (value == "none")) m_currentPayload->setSizeCheckType(SZC_None); else if (value == "match") m_currentPayload->setSizeCheckType(SZC_Match); else if (value == "modulus") m_currentPayload->setSizeCheckType(SZC_Modulus); } return true; } return true; }
void SpawnMonitor::loadSpawnPoints() { QString fileName; fileName = m_zoneName + ".sp"; QFileInfo fileInfo = m_dataLocMgr->findExistingFile("spawnpoints", fileName, false); if (!fileInfo.exists()) { seqWarn("Can't find spawn point file %s", (const char*)fileInfo.absFilePath()); return; } fileName = fileInfo.absFilePath(); QFile spFile(fileName); if (!spFile.open(IO_ReadOnly)) { seqWarn( "Can't open spawn point file %s", (const char*)fileName ); return; } QTextStream input( &spFile ); int16_t x, y, z; unsigned long diffTime; uint32_t count; QString name; while (!input.atEnd()) { input >> x; input >> y; input >> z; input >> diffTime; input >> count; name = input.readLine(); name = name.stripWhiteSpace(); EQPoint loc(x, y, z); SpawnPoint* p = new SpawnPoint( 0, loc, name, diffTime, count ); if (p) { QString key = p->key(); if (!m_points.find(key)) { m_points.insert(key, p); emit newSpawnPoint(p); } else { seqWarn("Warning: spawn point key already in use!"); delete p; } } } seqInfo("Loaded spawn points: %s", (const char*)fileName); m_modified = false; }
void SpawnMonitor::saveSpawnPoints() { // only save if modified if (!m_modified) return; if ( !m_zoneName.length() ) { seqWarn("Zone name not set in 'SpawnMonitor::saveSpawnPoints'!" ); return; } QString fileName; fileName = m_zoneName + ".sp"; QFileInfo fileInfo = m_dataLocMgr->findWriteFile("spawnpoints", fileName, false); fileName = fileInfo.absFilePath(); QString newName = fileName + ".new"; QFile spFile( newName ); if (!spFile.open(IO_WriteOnly)) { seqWarn("Failed to open %s for writing", (const char*)newName); return; } QTextStream output(&spFile); QAsciiDictIterator<SpawnPoint> it( m_points ); SpawnPoint* sp; while ((sp = it.current())) { ++it; output << sp->x() << " " << sp->y() << " " << sp->z() << " " << (unsigned long)sp->diffTime() << " " << sp->count() << " " << sp->name() << '\n'; } spFile.close(); QFileInfo fi( spFile ); QFile old( fileName ); QDir dir( fi.dir() ); QString backupName = fileName + ".bak"; if (old.exists()) { if (dir.rename( fileName, backupName)) { if (!dir.rename( newName, fileName)) seqWarn( "Failed to rename %s to %s", (const char*)newName, (const char*)fileName); } } else { if (!dir.rename(newName, fileName)) seqWarn("Failed to rename %s to %s", (const char*)newName, (const char*)fileName); } m_modified = false; seqInfo("Saved spawn points: %s", (const char*)fileName); }
bool EQStr::load(const QString& fileName) { // clear out any existing contents m_messageStrings.clear(); // create a QFile on the file QFile formatFile(fileName); // open the file read only if (!formatFile.open(IO_ReadOnly)) { seqWarn("EQStr: Failed to open '%s'", fileName.latin1()); return false; } // allocate a QCString large enough to hold the entire file QCString textData(formatFile.size() + 1); // read in the entire file formatFile.readBlock(textData.data(), textData.size()); // construct a regex to deal with either style line termination QRegExp lineTerm("[\r\n]{1,2}"); // split the data into lines at the line termination QStringList lines = QStringList::split(lineTerm, QString::fromUtf8(textData), false); // start iterating over the lines QStringList::Iterator it = lines.begin(); // first is the magic id string QString magicString = (*it++); int spc; uint32_t formatId; QString formatString; uint32_t maxFormatId = 0; // next skip over the count, etc... it++; // now iterate over the format lines for (; it != lines.end(); ++it) { // find the beginning space spc = (*it).find(' '); // convert the beginnign of the string to a ULong formatId = (*it).left(spc).toULong(); if (formatId > maxFormatId) maxFormatId = formatId; // insert the format string into the dictionary. m_messageStrings.insert(formatId, new QString((*it).mid(spc+1))); } // note that strings are loaded m_loaded = true; seqInfo("Loaded %d message strings from '%s' maxFormat=%d", m_messageStrings.count(), fileName.latin1(), maxFormatId); return true; }
///////////////////////////////////////////////////// // Handle a protocol level packet. This could be either a top-level // EQUDPIPPacket or a subpacket that is just an EQProtocolPacket. Either way // we use net opcodes here. void EQPacketStream::processPacket(EQProtocolPacket& packet, bool /*isSubpacket*/) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("-->EQPacketStream::processPacket, subpacket=%s on stream %s (%d)", (isSubpacket ? "true" : "false"), EQStreamStr[m_streamid], m_streamid); #endif if (IS_APP_OPCODE(packet.getNetOpCode())) { // This is an app-opcode directly on the wire with no wrapping protocol // information. Weird, but whatever gets the stream read, right? dispatchPacket(packet.payload(), packet.payloadLength(), packet.getNetOpCode(), m_opcodeDB.find(packet.getNetOpCode())); return; } // Process the net opcode switch (packet.getNetOpCode()) { case OP_Combined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found combined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Rolled up multiple packets inside this packet. Need to unroll them // and process them individually. subpacket starts after the net opcode. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; // OpCode (in net order) uint16_t subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Opcode is next. Net opcode or app opcode? if (subOpCode == 0) { // App opcode < 0x00ff. Skip the first byte and dispatch the app // opcode appropriately subpacket++; subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled special app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-3, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); } else if (IS_NET_OPCODE(subOpCode)) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled net opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Net opcode. false = copy. true = subpacket EQProtocolPacket spacket(subpacket, subpacketLength, false, true); processPacket(spacket, true); } else { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); } subpacket += subpacketLength; } } break; case OP_AppCombined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found appcombined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Multiple app op codes in the same packet. Need to unroll and dispatch // them. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; if (subpacketLength != 0xff) { // Dispatch app op code using given packet length. Net order! uint16_t subOpCode = *(uint16_t*)(subpacket); // Handle 3 byte opcodes properly if (subOpCode == 0) { // 3 byte opcode. Drop the first byte, opcode is byte 2 and 3 subpacket++; subpacketLength--; subOpCode = *(uint16_t*)(subpacket); } #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += subpacketLength; } else { // If original length is 0xff, it means it is a long one. The length // is 2 bytes and next. uint16_t longOne = eqntohuint16(subpacket); // Move past the 2 byte length subpacket += 2; // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)subpacket; // Handle 3 byte opcodes properly if (subOpCode == 0) { // 3 byte opcode. Drop the first byte, opcode is byte 2 and 3 subpacket++; longOne--; subOpCode = *(uint16_t*)(subpacket); } #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], longOne-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += longOne; } } } break; case OP_Packet: { // Normal unfragmented sequenced packet. uint16_t seq = packet.arqSeq(); emit seqReceive(seq, (int)m_streamid); // Future packet? if (seq == m_arqSeqExp) { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)(packet.payload()); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x, sub opcode %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode(), subOpCode); #endif // Opcode is next. Net opcode or app opcode? if (subOpCode == 0) { // App opcode < 0x00ff. Skip the first byte and dispatch the app // opcode appropriately subOpCode = *(uint16_t*)&packet.payload()[1]; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("EQPacket: special app opcode extracted for opcode 0000 on stream %s (%d). Opcode %04x", EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&packet.payload()[3], packet.payloadLength()-3, subOpCode, m_opcodeDB.find(subOpCode)); } else if (IS_NET_OPCODE(subOpCode)) { // Net opcode. false = no copy. true = subpacket. EQProtocolPacket spacket(packet.payload(), packet.payloadLength(), false, true); processPacket(spacket, true); } else { // App opcode. Dispatch, skipping opcode. dispatchPacket(&packet.payload()[2], packet.payloadLength()-2, subOpCode, m_opcodeDB.find(subOpCode)); } } else if ((seq > m_arqSeqExp && seq < (uint32_t(m_arqSeqExp + arqSeqWrapCutoff))) || seq < (int32_t(m_arqSeqExp) - arqSeqWrapCutoff)) { // Yeah, future packet. Push it on the packet cache. #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { #ifdef PACKET_PROCESS_DIAG // Past packet outside the cut off seqWarn("SEQ: received sequenced %spacket outside expected window on stream %s (%d) netopcode=%04x size=%d. Expecting seq=%04x got seq=%04x, window size %d, dropping packet as in the past.", (isSubpacket ? "sub" : ""), EQStreamStr[m_streamid], m_streamid, packet.getNetOpCode(), packet.payloadLength(), m_arqSeqExp, seq, arqSeqWrapCutoff); #endif } } break; case OP_Oversized: { // Fragmented sequenced data packet. uint16_t seq = packet.arqSeq(); emit seqReceive(seq, (int)m_streamid); // Future packet? if (seq == m_arqSeqExp) { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode()); #endif // Push the fragment on. m_fragment.addFragment(packet); if (m_fragment.isComplete()) { // OpCode from fragment. In network order. uint16_t fragOpCode = *(uint16_t*)(m_fragment.data()); #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: Completed oversized app packet on stream %s with seq %04x, total size %d opcode %04x", EQStreamStr[m_streamid], seq, m_fragment.size()-2, fragOpCode); #endif // dispatch fragment. Skip opcode. if (fragOpCode == 0) { // Special app opcode. Skip first byte and op is byte 2 and 3. fragOpCode = *(uint16_t*)(&m_fragment.data()[1]); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("EQPacket: special app opcode on completed fragment for opcode 0000 on stream %s (%d). Opcode %04x", EQStreamStr[m_streamid], m_streamid, fragOpCode); #endif dispatchPacket(&m_fragment.data()[3], m_fragment.size()-3, fragOpCode, m_opcodeDB.find(fragOpCode)); } else { dispatchPacket(&m_fragment.data()[2], m_fragment.size()-2, fragOpCode, m_opcodeDB.find(fragOpCode)); } m_fragment.reset(); } } else if ((seq > m_arqSeqExp && seq < (uint32_t(m_arqSeqExp + arqSeqWrapCutoff))) || seq < (int32_t(m_arqSeqExp) - arqSeqWrapCutoff)) { // Yeah, future packet. Push it on the packet cache. #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { #ifdef PACKET_PROCESS_DIAG // Past packet outside the cut off seqWarn("SEQ: received sequenced %spacket outside expected window on stream %s (%d) netopcode=%04x size=%d. Expecting seq=%04x got seq=%04x, window size %d, dropping packet as in the past.", (isSubpacket ? "sub" : ""), EQStreamStr[m_streamid], m_streamid, packet.getNetOpCode(), packet.payloadLength(), m_arqSeqExp, seq, arqSeqWrapCutoff); #endif } } break; case OP_SessionRequest: { // Session request from client to server. // // Sanity check the size. Don't assume any packet we see is an EQ // session request, since we're gonna cause a huge freakin' malloc // on the maxlength of the session which for some reason some people // won't enjoy! if (packet.payloadLength() != sizeof(SessionRequestStruct)) { // Either SessionRequestStruct changed or this isn't a session // request. #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: Ignoring SessionRequest %s:%u->%s:%u with invalid size %d.", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), packet.payloadLength()); #endif break; } // Pull off session request information SessionRequestStruct* request = (SessionRequestStruct*) packet.payload(); m_sessionId = eqntohuint32((uint8_t*)&(request->sessionId)); m_maxLength = eqntohuint32((uint8_t*)&(request->maxLength)); // Sanity check the max length requested if (m_maxLength > maxPacketSize) { seqWarn("EQPacket: SessionRequest wanted a max packet size of %d which is above our sane max packet size of %d. Using our max", m_maxLength, maxPacketSize); m_maxLength = maxPacketSize; } emit maxLength((int) m_maxLength, (int) m_streamid); #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest %s:%u->%s:%u, sessionId %u maxLength %u, awaiting key for stream %s (%d)", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), m_sessionId, m_maxLength, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionRequest contents: unknown %u, sessionId %u, maxLength %u", eqntohuint32((uint8_t*)&(request->unknown0000)), m_sessionId, m_maxLength); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 2) seqDebug("EQPacket: Raw SessionRequest: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x", packet.payload()[0], packet.payload()[1], packet.payload()[2], packet.payload()[3], packet.payload()[4], packet.payload()[5], packet.payload()[6], packet.payload()[7], packet.payload()[8], packet.payload()[9], packet.payload()[10], packet.payload()[11]); #endif m_arqSeqExp = 0; m_arqSeqFound = true; if (m_session_tracking_enabled) { // Save off client port for the stream so we can match against it // later. SessionRequest should always be an outer protocol packet // so we can cast it to EQUDPIPPacketFormat to get the ip headers. m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getSourcePort(); } } break; case OP_SessionResponse: { // Session response from server // Sanity check the size. Don't assume any packet we see is an EQ // session response, since we're gonna cause a huge freakin' malloc // on the maxlength of the session which for some reason some people // won't enjoy! if (packet.payloadLength() != sizeof(SessionResponseStruct)) { // Either SessionResponseStruct changed or this isn't a session // response. #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: Ignoring SessionResponse %s:%u->%s:%u with invalid size %d.", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), packet.payloadLength()); #endif break; } // Pull off session response information SessionResponseStruct* response = (SessionResponseStruct*) packet.payload(); m_maxLength = eqntohuint32((uint8_t*)&(response->maxLength)); m_sessionKey = eqntohuint32((uint8_t*)&(response->key)); m_sessionId = eqntohuint32((uint8_t*)&(response->sessionId)); // Sanity check the max length requested if (m_maxLength > maxPacketSize) { seqWarn("EQPacket: SessionResponse wanted a max packet size of %d which is above our sane max packet size of %d. Using our max", m_maxLength, maxPacketSize); m_maxLength = maxPacketSize; } emit maxLength((int) m_maxLength, (int) m_streamid); #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse found %s:%u->%s:%u, resetting expected seq, stream %s (%d) (session tracking %s)", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse sessionId %u maxLength %u, key is %u for stream %s (%d)", m_sessionId, m_maxLength, m_sessionKey, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionResponse contents: sessionId %u, key %u, unknown %u, unknown %u, maxLength %u, unknown %u", m_sessionId, m_sessionKey, eqntohuint16((uint8_t*)&(response->unknown0008)), response->unknown0010, m_maxLength, eqntohuint32((uint8_t*) &(response->unknown0015))); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 2) seqDebug("EQPacket: Raw SessionResponse: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x", packet.payload()[0], packet.payload()[1], packet.payload()[2], packet.payload()[3], packet.payload()[4], packet.payload()[5], packet.payload()[6], packet.payload()[7], packet.payload()[8], packet.payload()[9], packet.payload()[10], packet.payload()[11], packet.payload()[12], packet.payload()[13], packet.payload()[14], packet.payload()[15], packet.payload()[16], packet.payload()[17], packet.payload()[18]); #endif // Provide key to corresponding stream from this session/stream emit sessionKey(m_sessionId, m_streamid, m_sessionKey); m_arqSeqExp = 0; m_arqSeqFound = true; // Session tracking if (m_session_tracking_enabled) { // Save off client port for the stream so we can match against it // later. SessionRequest should always be an outer protocol packet // so we can cast it to EQUDPIPPacketFormat to get the ip headers. m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getDestPort(); // If this is the world server talking to us, reset session tracking if // it is on so we unlatch the client in case of getting kicked. if (m_streamid == world2client) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); } // If this is the zone server talking to us, close the latch and lock else if (m_streamid == zone2client) { // SessionResponse should always be an outer protocol packet, so // the EQProtocolPacket passed in can be cast back to // EQUDPIPPacketFormat, which we need to go to get access to the IP // headers! m_session_tracking_enabled = 2; emit lockOnClient(((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getDestPort()); emit sessionTrackingChanged(m_session_tracking_enabled); } } } break; case OP_SessionDisconnect: { #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionDisconnect found %s:%u->%s:%u, resetting expected seq, stream %s (%d) (session tracking %s)", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 2) seqDebug("EQPacket: Raw SessionDisconnect: %02x%02x %02x%02x %02x%02x %02x%02x", packet.payload()[0], packet.payload()[1], packet.payload()[2], packet.payload()[3], packet.payload()[4], packet.payload()[5], packet.payload()[6], packet.payload()[7]); #endif m_arqSeqExp = 0; // Clear cache resetCache(); // Signal closing. Unlatch session tracking if it is on. if (m_session_tracking_enabled) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); m_sessionClientPort = 0; } emit closing(m_sessionId, m_streamid); } break; case OP_Ack: case OP_AckFuture: case OP_AckAfterDisconnect: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on ACK for net opcode %04x seq %04x, stream %s (%d)", packet.getNetOpCode(), eqntohuint16(packet.payload()), EQStreamStr[m_streamid], m_streamid); #endif } break; case OP_KeepAlive: case OP_SessionStatRequest: case OP_SessionStatResponse: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on stats for net opcode %04x, stream %s (%d)", packet.getNetOpCode(), EQStreamStr[m_streamid], m_streamid); #endif } break; default : { seqWarn("EQPacket: Unhandled net opcode %04x, stream %s, size %d", packet.getNetOpCode(), EQStreamStr[m_streamid], packet.payloadLength()); } } }
//////////////////////////////////////////////////// // handle a new packet on the stream void EQPacketStream::handlePacket(EQUDPIPPacketFormat& packet) { emit numPacket(++m_packetCount, (int)m_streamid); // Packet is ours now. Logging needs to know this later on. packet.setSessionKey(getSessionKey()); // Only accept packets if we've been initialized unless they are // initialization packets! if (packet.getNetOpCode() != OP_SessionRequest && packet.getNetOpCode() != OP_SessionResponse && ! m_sessionKey) { #if (defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1)) || (defined(PACKET_SESSION_DIAG) && PACKET_SESSION_DIAG > 1) seqDebug("discarding packet %s:%d ==>%s:%d netopcode=%04x size=%d. Session not initialized. Need to zone to start picking up packets. Session tracking %s.", (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), (const char*)packet.getIPv4DestA(), packet.getDestPort(), packet.getNetOpCode(), packet.payloadLength(), (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif return; } // Only accept packets that correspond to our latched client port, if // it is set. This helps filter out multiple sessions on the same physical // host when two eq clients zone at the same time. The first one will win. if (m_sessionClientPort != 0 && ((dir() == DIR_Server && m_sessionClientPort != packet.getDestPort()) || (dir() == DIR_Client && m_sessionClientPort != packet.getSourcePort()))) { #if (defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1)) || (defined(PACKET_SESSION_DIAG) && PACKET_SESSION_DIAG > 1) seqDebug("discarding packet %s:%d ==>%s:%d netopcode=%04x size=%d. Multiple sessions on the same box? Ignoring all but one of them. Latched client port %d. Session tracking %s.", (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), (const char*)packet.getIPv4DestA(), packet.getDestPort(), packet.getNetOpCode(), packet.payloadLength(), m_sessionClientPort, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif return; } // Only accept packets that pass the EQ protocol-level CRC check. This helps // weed out non-EQ packets that we might see. #ifdef APPLY_CRC_CHECK if (packet.hasCRC()) { uint16_t calcedCRC = calculateCRC(packet); if (calcedCRC != packet.crc()) { seqWarn("INVALID PACKET: Bad CRC [%s:%d -> %s:%d] netOp %04x seq %04x len %d crc (%04x != %04x)", (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), (const char*)packet.getIPv4DestA(), packet.getDestPort(), packet.getNetOpCode(), packet.arqSeq(), packet.getUDPPayloadLength(), packet.crc(), calcedCRC); return; } } #endif /* APPLY_CRC_CHECK */ // Decode the packet first if (! packet.decode(m_maxLength)) { seqWarn("Packet decode failed for stream %s (%d), op %04x, flags %02x packet dropped.", EQStreamStr[m_streamid], m_streamid, packet.getNetOpCode(), packet.getFlags()); return; } #ifdef PACKET_DECODE_DIAG else if (packet.hasFlags()) { seqDebug("Successful decode for stream %s (%d), op %04x, flags %02x.", EQStreamStr[m_streamid], m_streamid, packet.getNetOpCode(), packet.getFlags()); } #endif // Raw packet emit rawPacket(packet.rawPayload(), packet.rawPayloadLength(), m_dir, packet.getNetOpCode()); processPacket(packet, false); // false = isn't subpacket // if the cache isn't empty, then process it. if (!m_cache.empty()) processCache(); }
void EQPacketStream::dispatchPacket(const uint8_t* data, size_t len, uint16_t opCode, const EQPacketOPCode* opcodeEntry) { emit decodedPacket(data, len, m_dir, opCode, opcodeEntry); bool unknown = true; // unless there is an opcode entry, there is nothing to dispatch... if (opcodeEntry) { EQPacketPayload* payload; EQPacketDispatch* dispatch; #ifdef PACKET_INFO_DIAG seqDebug( "dispatchPacket: attempting to dispatch opcode %#04x '%s'", opcodeEntry->opcode(), (const char*)opcodeEntry->name()); #endif // iterate over the payloads in the opcode entry, and dispatch matches EQPayloadListIterator pit(*opcodeEntry); bool found = false; while ((payload = pit.current()) != 0) { // see if this packet matches if (payload->match(data, len, m_dir)) { found = true; unknown = false; // #ifdef PACKET_INFO_DIAG seqDebug( "\tmatched payload, find dispatcher in dict (%d/%d)", m_dispatchers.count(), m_dispatchers.size()); #endif // find the dispather for the payload dispatch = m_dispatchers.find((void*)payload); // if found, dispatch if (dispatch) { #ifdef PACKET_INFO_DIAG seqDebug("\tactivating signal..."); #endif dispatch->activate(data, len, m_dir); } } // go to next possible payload ++pit; } #ifdef PACKET_PAYLOAD_SIZE_DIAG if (!found && !opcodeEntry->isEmpty()) { QString tempStr; tempStr.sprintf("%s (%#04x) (dataLen: %u) doesn't match:", (const char*)opcodeEntry->name(), opcodeEntry->opcode(), len); for (payload = pit.toFirst(); payload != 0; payload = ++pit) { if (payload->dir() & m_dir) { if (payload->sizeCheckType() == SZC_Match) tempStr += QString(" sizeof(%1):%2") .arg(payload->typeName()).arg(payload->typeSize()); else if (payload->sizeCheckType() == SZC_Modulus) tempStr += QString(" modulus of sizeof(%1):%2") .arg(payload->typeName()).arg(payload->typeSize()); } } seqWarn(tempStr); } #endif // PACKET_PAYLOAD_SIZE_DIAG } #ifdef PACKET_INFO_DIAG else { seqWarn("dispatchPacket(): buffer size %d opcode %04x stream %s (%d) not in opcodeDB", len, opCode, EQStreamStr[m_streamid], m_streamid); } #endif emit decodedPacket(data, len, m_dir, opCode, opcodeEntry, unknown); }
//////////////////////////////////////////////////// // Cache processing void EQPacketStream::processCache() { #if defined(PACKET_CACHE_DIAG) seqDebug("SEQ: START checking stream %s cache, arq %04x, cache count %04d", EQStreamStr[m_streamid], m_arqSeqExp, m_cache.size()); #endif EQPacketMap::iterator it; EQPacketMap::iterator eraseIt; EQProtocolPacket* packet; // check if the cache has grown large enough that we should give up // on seeing the current serverArqSeqExp // // If people see this a lot, they either have pathetic network cards, or // are having problems keeping up with packets (slow computer? Too much // net traffic?). Some possible solutions to this are to turn on session // tracking to filter out more PF_PACKET packets from getting passed out of // the kernel and to up the socket receive buffer sizes. See FAQ for // more information. if (m_cache.size() >= m_arqSeqGiveUp) { // ok, if the expected server arq sequence isn't here yet, give up // attempt to find the current expencted arq seq it = m_cache.find(m_arqSeqExp); // keep trying to find a new serverArqSeqExp if we haven't found a good // one yet... while(it == m_cache.end()) { seqWarn("SEQ: Giving up on finding arq %04x in stream %s cache, skipping!", m_arqSeqExp, EQStreamStr[m_streamid]); // incremente the expected arq sequence number m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); // attempt to find the new current expencted arq seq it = m_cache.find(m_arqSeqExp); } } else { // haven't given up yet, just try to find the current serverArqSeqExp // attempt to find the current expected ARQ seq it = m_cache.find(m_arqSeqExp); } // iterate over cache until we reach the end or run out of // immediate followers while (it != m_cache.end()) { // get the PacketFormat for the iterator packet = it->second; // make sure this is the expected packet // (we might have incremented to the one after the one returned // by find above). if (packet->arqSeq() != m_arqSeqExp) break; #ifdef PACKET_CACHE_DIAG seqDebug("SEQ: found next arq %04x in stream %s cache, cache count %04d", m_arqSeqExp, EQStreamStr[m_streamid], m_cache.size()); #endif // validate the packet with a crc check. If the packet is for an old // session, we probably shouldn't be using it! #ifdef APPLY_CRC_CHECK if (packet->hasCRC() && packet->crc() != calculateCRC(*packet)) { #if defined (PACKET_CACHE_DIAG) // Something's screwed up seqDebug("SEQ: INVALID PACKET: Bad CRC in packet in stream %s cache with arq %04x! Droping it, but leaving expected seq as %04x", EQStreamStr[m_streamid], packet->arqSeq(), m_arqSeqExp); #endif // Need to drop from the cache eraseIt = it; // increment the current position iterator it++; // erase the packet from the cache m_cache.erase(eraseIt); emit cacheSize(m_cache.size(), (int)m_streamid); #ifdef PACKET_CACHE_DIAG seqDebug("SEQ: REMOVING arq %04x from stream %s cache, cache count %04d", packet->arqSeq(), EQStreamStr[m_streamid], m_cache.size()); #endif // delete the packet delete packet; // No sense looping some more. break; } else #endif /* APPLY_CRC_CHECK */ { #if defined (PACKET_CACHE_DIAG) && (PACKET_CACHE_DIAG > 2) seqDebug("SEQ: Found next arq in stream %s cache, incrementing arq seq, %04x", EQStreamStr[m_streamid], packet->arqSeq()); #endif // Process the packet since it's next in the sequence and was just // received out of order processPacket(*packet, packet->isSubpacket()); // Need to drop from the cache eraseIt = it; // increment the current position iterator it++; // erase the packet from the cache m_cache.erase(eraseIt); emit cacheSize(m_cache.size(), (int)m_streamid); #ifdef PACKET_CACHE_DIAG seqDebug("SEQ: REMOVING arq %04x from stream %s cache, cache count %04d", packet->arqSeq(), EQStreamStr[m_streamid], m_cache.size()); #endif // delete the packet delete packet; if (m_arqSeqExp == 0) it = m_cache.begin(); } } #ifdef PACKET_CACHE_DIAG seqDebug("SEQ: FINISHED checking stream %s cache, arq %04x, cache count %04d", EQStreamStr[m_streamid], m_arqSeqExp, m_cache.size()); #endif }
//////////////////////////////////////////////////// // Reads packets and processes waiting packets from playback file void EQPacket::processPlaybackPackets (void) { #ifdef DEBUG_PACKET // debug ("processPackets()"); #endif /* DEBUG_PACKET */ /* Make sure we are not called while already busy */ if (m_busy_decoding) return; /* Set flag that we are busy decoding */ m_busy_decoding = true; unsigned char buffer[8192]; int size; /* in packet playback mode fetch packets from VPacket class */ time_t now; int timein = mTime(); int i = 0; long version = PACKETVERSION; // decode packets from the playback buffer do { size = m_vPacket->Playback((char *) buffer, sizeof(buffer), &now, &version); if (size) { i++; if (PACKETVERSION == version) { dispatchPacket ( size - sizeof (struct ether_header), (unsigned char *) buffer + sizeof (struct ether_header) ); } else { seqWarn("Error: The version of the packet stream has " \ "changed since '%s' was recorded - disabling playback", m_vPacket->getFileName()); // stop the timer, nothing more can be done... stop(); break; } } else break; } while ( (mTime() - timein) < 100); // check if we've reached the end of the recording if (m_vPacket->endOfData()) { seqInfo("End of playback file '%s' reached." "Playback Finished!", m_vPacket->getFileName()); // stop the timer, nothing more can be done... stop(); } /* Clear decoding flag */ m_busy_decoding = false; }
//////////////////////////////////////////////////// // decode, decrypt, and unroll packet void EQPacketStream::decodePacket(uint8_t *data, size_t len, uint16_t opCode) { emit rawPacket(data, len, m_dir, opCode); if (opCode & FLAG_CRYPTO || opCode & FLAG_COMP) { data = decodeOpCode (data, &len, opCode); if (data == NULL) return; emit rawPacket(data, len, m_dir, opCode); } const EQPacketOPCode* opcodeEntry = 0; // this works, but could really use a cleanup - mvern while (len > 2) { if (opCode & FLAG_COMBINED) { bool repeatop = false; uint8_t *dptr = data; size_t left = len; int32_t count = 1; if (opCode & FLAG_IMPLICIT) { opCode = opCode & ~FLAG_IMPLICIT; repeatop = true; } #ifdef PACKET_DECODE_DIAG seqDebug("unrolling on %s: 0x%04x", EQStreamStr[m_streamid], opCode); #endif opCode = opCode & ~FLAG_COMBINED; while (count > 0) { uint16_t size = 0; opcodeEntry = m_opcodeDB.find(opCode); if (opcodeEntry) { size = opcodeEntry->implicitLen(); #ifdef PACKET_DECODE_DIAG // ZBTEMP seqDebug("opcode %04x implicitlen %d", opCode, size); #endif } #ifdef PACKET_DECODE_DIAG else seqDebug("No opcodeEntry for %04x", opCode); #endif if (size == 0) { // Not an implicit length opcode if (dptr[0] == 0xff) { // > 255 length left--; dptr++; size = ntohs(*((uint16_t *)dptr)); left -= 2; dptr += 2; } else { // single octet length size = dptr[0]; left--; dptr++; } } #ifdef PACKET_DECODE_DIAG seqDebug ("size on %s: %d", EQStreamStr[m_streamid], size); #endif if (size > left) { seqWarn("error on %s: size > left (size=%d, left=%d, opcode=0x%04x)", EQStreamStr[m_streamid], size, left, opCode); return; } #ifdef PACKET_DECODE_DIAG seqDebug("sending from %s: 0x%04x, %d", EQStreamStr[m_streamid], opCode, size); #endif dispatchPacket(dptr, size, opCode, opcodeEntry); // next dptr += size; left -= size; count--; if (repeatop) { count = dptr[0]; dptr++; left--; repeatop = false; #ifdef PACKET_DECODE_DIAG seqDebug ("repeating %d times on %s", count, EQStreamStr[m_streamid]); #endif } } if (left > 2) { opCode = *((unsigned short *)dptr); dptr += 2; left -= 2; #ifdef PACKET_DECODE_DIAG seqDebug("doing leftover from %s: 0x%04x, %d", EQStreamStr[m_streamid], opCode, left); #endif if (opCode & FLAG_COMBINED) { data = dptr; len = left; continue; } else { #ifdef PACKET_DECODE_DIAG seqDebug("sending from %s: 0x%04x, %d", EQStreamStr[m_streamid], opCode, size); #endif dispatchPacket(dptr, left, opCode, m_opcodeDB.find(opCode)); } } return; } else break; } #ifdef PACKET_DECODE_DIAG seqDebug ("sending from %s: 0x%04x, %d", EQStreamStr[m_streamid], opCode, size); #endif dispatchPacket(data, len, opCode, m_opcodeDB.find(opCode)); }
ExperienceWindow::ExperienceWindow(const DataLocationMgr* dataLocMgr, Player* player, GroupMgr* groupMgr, ZoneMgr* zoneMgr, QWidget* parent, const char* name) : SEQWindow("Experience", "ShowEQ - Experience", parent, name), m_dataLocMgr(dataLocMgr), m_player(player), m_group(groupMgr), m_zoneMgr(zoneMgr) { /* Hopefully this is only called once to set up the window, so this is a good place to initialize some things which otherwise won't be. */ m_ratio = 1; m_timeframe = 0; m_calcZEM=0; m_ZEMviewtype = 0; m_view_menu = new QPopupMenu( this ); m_view_menu->insertItem( "&All Mobs", this, SLOT(viewAll()) ); m_view_menu->insertItem( "Previous &15 Minutes", this, SLOT(view15Minutes()) ); m_view_menu->insertItem( "Previous &30 Minutes", this, SLOT(view30Minutes()) ); m_view_menu->insertItem( "Previous &60 Minutes", this, SLOT(view60Minutes()) ); m_view_menu->setItemChecked( m_view_menu->idAt(0), true ); m_view_menu->insertSeparator(); m_exp_rate_menu = new QPopupMenu( this ); m_exp_rate_menu->insertItem( "per &minute", this, SLOT(viewRatePerMinute()) ); m_exp_rate_menu->insertItem( "per &hour", this, SLOT(viewRatePerHour()) ); m_exp_rate_menu->setItemChecked( m_exp_rate_menu->idAt(0), true ); m_view_menu->insertItem( "Experience &Rate", m_exp_rate_menu ); m_view_menu->insertSeparator(); m_view_menu->insertItem( "Clear Kills", this, SLOT(viewClear()) ); m_view_menu->insertSeparator(); m_ZEM_menu = new QPopupMenu( this ); m_ZEM_menu->insertItem( "Calculated Value", this, SLOT(viewZEMcalculated()) ); m_ZEM_menu->insertItem( "Raw Value", this, SLOT(viewZEMraw()) ); m_ZEM_menu->insertItem( "Percent Bonus", this, SLOT(viewZEMpercent()) ); m_ZEM_menu->setItemChecked( m_ZEM_menu->idAt(0), true ); m_view_menu->insertItem( "ZEM View Options", m_ZEM_menu ); m_view_menu->insertItem( "Calculate ZEM on next kill", this, SLOT(calcZEMNextKill()) ); m_layout = new QVBoxLayout(boxLayout()); m_menu_bar = new QMenuBar( this ); m_menu_bar->insertItem( "&View", m_view_menu ); //m_layout->addSpacing( m_menu_bar->height() + 5 ); m_layout->addWidget(m_menu_bar); QGroupBox *listGBox = new QVGroupBox( "Experience Log", this ); m_layout->addWidget( listGBox ); m_exp_listview = new SEQListView(preferenceName(), listGBox); m_exp_listview->addColumn("Time"); m_exp_listview->addColumn("Mob"); m_exp_listview->addColumn("Level"); m_exp_listview->addColumn("Base Exp"); m_exp_listview->addColumn("ZEM total"); m_exp_listview->addColumn("Class total"); m_exp_listview->addColumn("Group total"); m_exp_listview->addColumn("Experience Gained"); m_exp_listview->restoreColumns(); m_exp_listview->setMinimumSize( m_exp_listview->sizeHint().width(), 200 ); QGroupBox *statsGBox = new QVGroupBox( "Statistics", this ); m_layout->addWidget( statsGBox ); QGrid *statsGrid = new QGrid( 4, statsGBox ); new QLabel( "Total Experience Received:", statsGrid ); m_total_received = new QLabel( statsGrid ); new QLabel( "Play Time:", statsGrid ); m_play_time = new QLabel( statsGrid ); new QLabel( "Total Mobs Killed:", statsGrid ); m_mob_count = new QLabel( statsGrid ); m_experience_rate_label = new QLabel( "Experience Rate (per minute):", statsGrid ); m_experience_rate = new QLabel( statsGrid ); new QLabel( "Average Experience per Mob:", statsGrid ); m_average_per_mob = new QLabel( statsGrid ); new QLabel( "Estimated Kills To Level:", statsGrid ); m_kills_to_level = new QLabel( statsGrid ); new QLabel( "Experience Remaining:", statsGrid ); m_experience_remaining = new QLabel( statsGrid ); new QLabel( "Estimated Time To Level:", statsGrid ); m_time_to_level = new QLabel( statsGrid ); // ewww, why can't we just get it from QGrid? :( ((QGridLayout *)statsGrid->layout())->setColStretch( 1, 1 ); ((QGridLayout *)statsGrid->layout())->setColStretch( 3, 1 ); statsGrid->layout()->setSpacing( 5 ); updateAverage( ); // timer to update the average xp QTimer *timer = new QTimer( this ); connect( timer, SIGNAL(timeout()), SLOT(updateAverage())); timer->start(15*1000); // calculate every 15 seconds QFileInfo fileInfo = m_dataLocMgr->findWriteFile("logs", "exp.log"); m_log = fopen(fileInfo.absFilePath(), "a"); if (m_log == 0) { m_log_exp = 0; seqWarn("Error opening exp.log, no exp will be logged this session"); } fileInfo = m_dataLocMgr->findWriteFile("logs", "newexp.log"); m_newExpLogFile = fileInfo.absFilePath(); }
//////////////////////////////////////////////////// // This function decides the fate of the Everquest packet // and dispatches it to the correct packet stream for handling function void EQPacket::dispatchPacket(int size, unsigned char *buffer) { #ifdef DEBUG_PACKET debug ("dispatchPacket()"); #endif /* DEBUG_PACKET */ /* Setup variables */ // Create an object to parse the packet EQUDPIPPacketFormat packet(buffer, size, false); // signal a new packet emit newPacket(packet); /* Chat and Login Server Packets, Discard for now */ if ((packet.getDestPort() == ChatServerPort) || (packet.getSourcePort() == ChatServerPort)) return; if ((packet.getDestPort() == WorldServerChatPort) || (packet.getSourcePort() == WorldServerChatPort)) return; if (((packet.getDestPort() >= LoginServerMinPort) && (packet.getDestPort() <= LoginServerMaxPort)) || (packet.getSourcePort() >= LoginServerMinPort) && (packet.getSourcePort() <= LoginServerMaxPort)) return; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("%s", (const char*)packet.headerFlags((PACKET_PROCESS_DIAG < 3))); #endif if (!packet.isValid()) { if (m_crcWarnings) seqWarn("INVALID PACKET: Bad CRC32 [%s:%d -> %s:%d] seq %04x len %d crc32 (%08x != %08x)", (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), (const char*)packet.getIPv4DestA(), packet.getDestPort(), packet.seq(), packet.getRawPacketLength(), packet.crc32(), packet.calcCRC32()); return; } /* discard pure ack/req packets and non valid flags*/ if (packet.flagsHi() < 0x02 || packet.flagsHi() > 0x46 || size < 10) { #if defined(PACKET_PROCESS_DIAG) seqDebug("discarding packet %s:%d ==>%s:%d flagsHi=%d size=%d", (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), (const char*)packet.getIPv4DestA(), packet.getDestPort(), packet.flagsHi(), size); seqDebug("%s", (const char*)packet.headerFlags(false)); #endif return; } /* Client Detection */ if (m_detectingClient && packet.getSourcePort() == WorldServerGeneralPort) { m_ip = packet.getIPv4DestA(); m_client_addr = packet.getIPv4DestN(); m_detectingClient = false; emit clientChanged(m_client_addr); seqInfo("Client Detected: %s", (const char*)m_ip); } else if (m_detectingClient && packet.getDestPort() == WorldServerGeneralPort) { m_ip = packet.getIPv4SourceA(); m_client_addr = packet.getIPv4SourceN(); m_detectingClient = false; emit clientChanged(m_client_addr); seqInfo("Client Detected: %s", (const char*)m_ip); } /* end client detection */ if (packet.getSourcePort() == WorldServerChatPort) { dispatchWorldChatData(packet.payloadLength(), packet.payload(), DIR_Server); return; } else if (packet.getDestPort() == WorldServerChatPort) { dispatchWorldChatData(packet.payloadLength(), packet.payload(), DIR_Client); return; } /* stream identification */ if (packet.getIPv4SourceN() == m_client_addr) { if (packet.getDestPort() == WorldServerGeneralPort) m_client2WorldStream->handlePacket(packet); else m_client2ZoneStream->handlePacket(packet); } else if (packet.getIPv4DestN() == m_client_addr) { if (packet.getSourcePort() == WorldServerGeneralPort) m_world2ClientStream->handlePacket(packet); else m_zone2ClientStream->handlePacket(packet); } return; } /* end dispatchPacket() */
///////////////////////////////////////////////////// // Handle a protocol level packet. This could be either a top-level // EQUDPIPPacket or a subpacket that is just an EQProtocolPacket. Either way // we use net opcodes here. void EQPacketStream::processPacket(EQProtocolPacket& packet, bool isSubpacket) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("-->EQPacketStream::processPacket, subpacket=%s on stream %s (%d)", (isSubpacket ? "true" : "false"), EQStreamStr[m_streamid], m_streamid); #endif if (IS_APP_OPCODE(packet.getNetOpCode())) { // This is an app-opcode directly on the wire with no wrapping protocol // information. Weird, but whatever gets the stream read, right? dispatchPacket(packet.payload(), packet.payloadLength(), packet.getNetOpCode(), m_opcodeDB.find(packet.getNetOpCode())); return; } // Process the net opcode switch (packet.getNetOpCode()) { case OP_Combined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found combined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Rolled up multiple packets inside this packet. Need to unroll them // and process them individually. subpacket starts after the net opcode. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; // OpCode (in net order) uint16_t subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Opcode is next. Net opcode or app opcode? if (IS_NET_OPCODE(subOpCode)) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled net opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Net opcode. false = copy. true = subpacket EQProtocolPacket spacket(subpacket, subpacketLength, false, true); processPacket(spacket, true); } else { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); } subpacket += subpacketLength; } } break; case OP_AppCombined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found appcombined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Multiple app op codes in the same packet. Need to unroll and dispatch // them. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; if (subpacketLength != 0xff) { // Dispatch app op code using given packet length. Net order! uint16_t subOpCode = *(uint16_t*)(subpacket); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += subpacketLength; } else { // If original length is 0xff, it means it is a long one. The length // is 2 bytes and next. uint16_t longOne = eqntohuint16(subpacket); // Move past the 2 byte length subpacket += 2; // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], longOne-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += longOne; } } } break; case OP_Packet: { // Normal unfragmented sequenced packet. uint16_t seq = eqntohuint16(packet.payload()); emit seqReceive(seq, (int)m_streamid); if (seq >= m_arqSeqExp) { // Future packet? if (seq == m_arqSeqExp) { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)(&packet.payload()[2]); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x, sub opcode %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode(), subOpCode); #endif // App opcode or net opcode? if (IS_NET_OPCODE(subOpCode)) { // Net opcode. false = no copy. true = subpacket. EQProtocolPacket spacket(&packet.payload()[2], packet.payloadLength()-2, false, true); processPacket(spacket, true); } else { // App opcode. Dispatch, skipping seq and opcode. dispatchPacket(&packet.payload()[4], packet.payloadLength()-4, subOpCode, m_opcodeDB.find(subOpCode)); } } else if (seq < (uint32_t(m_arqSeqExp + arqSeqWrapCutoff)) || seq < (int32_t(m_arqSeqExp - arqSeqWrapCutoff))) { // Yeah, future packet. Push it on the packet cache. #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { // Past packet outside the cut off seqWarn("SEQ: received sequenced %spacket outside the bounds of reasonableness. Expecting seq=%04x got seq=%04x, reasonableness being %d in the future.", (isSubpacket ? "sub" : ""), m_arqSeqExp, seq, arqSeqWrapCutoff); } } else { // Spooky packet from the past. Boo! #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("discarding %spacket netopcode=%04x seq=%d size=%d on stream %s (%d). Packet is in the past. We've moved on.", (isSubpacket ? "sub" : ""), packet.getNetOpCode(), seq, packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif } } break; case OP_Oversized: { // Fragmented sequenced data packet. uint16_t seq = eqntohuint16(packet.payload()); emit seqReceive(seq, (int)m_streamid); if (seq >= m_arqSeqExp) { // Future packet? if (seq > m_arqSeqExp) { // Yeah, future packet. Push it on the packet cache. #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode()); #endif // Push the fragment on. m_fragment.addFragment(packet); if (m_fragment.isComplete()) { // OpCode from fragment. In network order. uint16_t fragOpCode = *(uint16_t*)(m_fragment.data()); #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: Completed oversized app packet on stream %s with seq %04x, total size %d opcode %04x", EQStreamStr[m_streamid], seq, m_fragment.size()-2, fragOpCode); #endif // dispatch fragment. Skip opcode. dispatchPacket(&m_fragment.data()[2], m_fragment.size()-2, fragOpCode, m_opcodeDB.find(fragOpCode)); m_fragment.reset(); } } } else { // Spooky packet from the past. Boo! #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("discarding packet netopcode=%04x seq=%d size=%d on stream %s (%d). Packet is in the past. We've moved on.", packet.getNetOpCode(), seq, packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif } } break; case OP_SessionRequest: { // Session request from client to server. #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif // Pull off session request information SessionRequestStruct* request = (SessionRequestStruct*) packet.payload(); m_sessionId = eqntohuint32((uint8_t*)&(request->sessionId)); m_maxLength = eqntohuint32((uint8_t*)&(request->maxLength)); #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest sessionId %u maxLength %u, awaiting key for stream %s (%d)", m_sessionId, m_maxLength, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionRequest contents: unknown %u, sessionId %u, maxLength %u", eqntohuint32((uint8_t*)&(request->unknown0000)), m_sessionId, m_maxLength); #endif m_arqSeqExp = 0; m_arqSeqFound = true; } break; case OP_SessionResponse: { // Session response from server #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif // Pull off session response information SessionResponseStruct* response = (SessionResponseStruct*) packet.payload(); m_maxLength = eqntohuint32((uint8_t*)&(response->maxLength)); m_sessionKey = eqntohuint32((uint8_t*)&(response->key)); m_sessionId = eqntohuint32((uint8_t*)&(response->sessionId)); #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse sessionId %u maxLength %u, key is %u for stream %s (%d)", m_sessionId, m_maxLength, m_sessionKey, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionResponse contents: sessionId %u, key %u, unknown %u, unknown %u, maxLength %u, unknown %u", m_sessionId, m_sessionKey, eqntohuint16((uint8_t*)&(response->unknown0008)), response->unknown0010, m_maxLength, eqntohuint32((uint8_t*) &(response->unknown0015))); #endif // Provide key to corresponding stream from this session/stream emit sessionKey(m_sessionId, m_streamid, m_sessionKey); m_arqSeqExp = 0; m_arqSeqFound = true; // Session tracking if (m_session_tracking_enabled) { // If this is the world server talking to us, reset session tracking if // it is on so we unlatch the client in case of getting kicked. if (m_streamid == world2client) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); } // If this is the zone server talking to us, close the latch and lock else if (m_streamid == zone2client) { // SessionResponse should always be an outer protocol packet, so // the EQProtocolPacket passed in can be cast back to // EQUDPIPPacketFormat, which we need to go to get access to the IP // headers! m_session_tracking_enabled = 2; emit lockOnClient(((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getDestPort()); emit sessionTrackingChanged(m_session_tracking_enabled); } } } break; case OP_SessionDisconnect: { #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionDisconnect found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif m_arqSeqExp = 0; // Clear cache resetCache(); // Signal closing. Unlatch session tracking if it is on. if (m_session_tracking_enabled) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); } emit closing(); } break; case OP_Ack: case OP_Resend: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on for net opcode %04x seq %04x, stream %s (%d)", packet.getNetOpCode(), eqntohuint16(packet.payload()), EQStreamStr[m_streamid], m_streamid); #endif } break; case OP_KeepAlive: case OP_SessionStatRequest: case OP_SessionStatResponse: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on for net opcode %04x, stream %s (%d)", packet.getNetOpCode(), EQStreamStr[m_streamid], m_streamid); #endif } break; default : { seqWarn("EQPacket: Unhandled net opcode %04x, stream %s, size %d", packet.getNetOpCode(), EQStreamStr[m_streamid], packet.payloadLength()); } } }
void GuildMgr::writeGuildList(const uint8_t* data, size_t len) { QFile guildsfile(guildsFileName); if (guildsfile.exists()) { if (!guildsfile.remove()) { seqWarn("GuildMgr: Could not remove old %s, unable to replace with server data!", guildsFileName.latin1()); return; } } if (!guildsfile.open(QIODevice::WriteOnly)) seqWarn("GuildMgr: Could not open %s for writing, unable to replace with server data!", guildsFileName.latin1()); QDataStream guildDataStream(&guildsfile); NetStream netStream(data, len); QString guildName; uint32_t size = 0; // to keep track of how much we're reading from the packet uint32_t guildId = 0; /* 0x48 in the packet starts the serialized list. See guildListStruct * and worldGuildListStruct in everquest.h */ // Skip the first guild in the list netStream.skipBytes(0x44); size += 0x44; while (!netStream.end()) { guildId = netStream.readUInt32NC(); guildName = netStream.readText(); size += 4; if (guildName.length()) { m_guildList[guildId] = guildName; // add guild name length, plus one for the null character size += guildName.length() + 1; } // theres an extra zero at the end of the packet if (size + 1 == len) break; } std::map<uint32_t, QString>::const_iterator it; for (it = m_guildList.begin(); it != m_guildList.end(); it++) { char szGuildName[64] = {0}; strcpy(szGuildName, it->second.latin1()); // seqDebug("GuildMgr::writeGuildList - add guild '%s' (%d)", szGuildName, it->first); guildDataStream.writeRawBytes(szGuildName, sizeof(szGuildName)); } guildsfile.close(); seqInfo("GuildMgr: New guildsfile written"); }