From 5a47e9d8a9c5beb984648b11dc80ae3cca0d0549 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Fri, 11 May 2012 15:02:57 -0300 Subject: [PATCH] support for protocol 810 * change in protocol/network classes to make compatible with older protocols * use filestream for reading dat * add many ifdefs for multi-protocol support --- BUGS | 1 + TODO | 1 + src/framework/net/inputmessage.cpp | 26 +++++- src/framework/net/inputmessage.h | 29 ++++--- src/framework/net/outputmessage.cpp | 20 ++++- src/framework/net/outputmessage.h | 16 ++-- src/framework/net/protocol.cpp | 106 ++++++++++--------------- src/framework/net/protocol.h | 1 - src/framework/util/tools.h | 16 ++++ src/otclient/core/map.cpp | 23 +++--- src/otclient/core/thingstype.cpp | 71 +++++++++-------- src/otclient/core/thingstype.h | 3 +- src/otclient/core/thingtype.h | 3 + src/otclient/net/protocolcodes.h | 48 +++++++++-- src/otclient/net/protocolgame.cpp | 10 ++- src/otclient/net/protocolgameparse.cpp | 62 ++++++++++----- src/otclient/net/protocolgamesend.cpp | 20 ++++- src/otclient/net/protocollogin.cpp | 57 +++++++------ src/otclient/net/protocollogin.h | 2 - 19 files changed, 326 insertions(+), 189 deletions(-) diff --git a/BUGS b/BUGS index e23f83c3..40249d59 100644 --- a/BUGS +++ b/BUGS @@ -4,6 +4,7 @@ modules recursivity makes client crash, it should generate a warning == P1 BUGS (affects game play) sometimes minimap desync Z pos follow and autowalk doesn't cancel when walking via hotkeys +when reading invalid spr/dat the client crashs when walking on a tile with too many creatures, the following errors occurrs: ERROR: [ProtocolGame::parseCreatureHealth] could not get greature diff --git a/TODO b/TODO index 91e88c6f..e89d4983 100644 --- a/TODO +++ b/TODO @@ -44,6 +44,7 @@ multisample option in map view move rendering of creatures names, skulls, etc to UI and scripts clean sprites cache periodically handle corrupt errors in dat/spr +throw exceptions when fail to read a file * framework rework Settings/g_configs diff --git a/src/framework/net/inputmessage.cpp b/src/framework/net/inputmessage.cpp index d1bba756..e0d3e8a6 100644 --- a/src/framework/net/inputmessage.cpp +++ b/src/framework/net/inputmessage.cpp @@ -29,8 +29,28 @@ InputMessage::InputMessage() void InputMessage::reset() { - m_readPos = 0; - m_messageSize = 2; + m_messageSize = 0; + m_readPos = MAX_HEADER_SIZE; + m_headerPos = MAX_HEADER_SIZE; +} + +void InputMessage::setHeaderSize(uint16 size) +{ + m_headerPos = MAX_HEADER_SIZE - size; + m_readPos = m_headerPos; +} + +void InputMessage::fillBuffer(uint8 *buffer, uint16 size) +{ + memcpy(m_buffer + m_readPos, buffer, size); + m_messageSize += size; +} + +bool InputMessage::readChecksum() +{ + uint32_t receivedCheck = getU32(); + uint32 checksum = Fw::getAdlerChecksum(m_buffer + m_readPos, getUnreadSize()); + return receivedCheck == checksum; } uint8 InputMessage::getU8(bool peek) @@ -88,7 +108,7 @@ std::string InputMessage::getString() bool InputMessage::canRead(int bytes) { - if((m_readPos + bytes > m_messageSize) || (m_readPos + bytes > BUFFER_MAXSIZE)) + if((m_readPos - m_headerPos + bytes > m_messageSize) || (m_readPos + bytes > BUFFER_MAXSIZE)) return false; return true; } diff --git a/src/framework/net/inputmessage.h b/src/framework/net/inputmessage.h index 31fa13d9..70047d54 100644 --- a/src/framework/net/inputmessage.h +++ b/src/framework/net/inputmessage.h @@ -31,34 +31,43 @@ class InputMessage public: enum { BUFFER_MAXSIZE = 16384, - HEADER_POS = 0, - HEADER_LENGTH = 2, - CHECKSUM_POS = 2, - CHECKSUM_LENGTH = 4, - DATA_POS = 6, - UNENCRYPTED_DATA_POS = 8 + MAX_HEADER_SIZE = 8, }; InputMessage(); void reset(); + void fillBuffer(uint8 *buffer, uint16 size); + uint16 readSize() { return getU16(); } + bool readChecksum(); + + void setHeaderSize(uint16 size); + void setMessageSize(uint16 size) { m_messageSize = size; } + + void skipBytes(uint16 bytes) { m_readPos += bytes; } + uint8 getU8(bool peek = false); uint16 getU16(bool peek = false); uint32 getU32(bool peek = false); uint64 getU64(bool peek = false); std::string getString(); - void skipBytes(uint16 bytes) { m_readPos += bytes; } - uint8* getBuffer() { return m_buffer; } + uint8* getReadBuffer() { return m_buffer + m_readPos; } + uint8* getHeaderBuffer() { return m_buffer + m_headerPos; } + uint8* getDataBuffer() { return m_buffer + MAX_HEADER_SIZE; } + uint16 getHeaderSize() { return (MAX_HEADER_SIZE - m_headerPos); } uint16 getMessageSize() { return m_messageSize; } - void setMessageSize(uint16 messageSize) { m_messageSize = messageSize; } - bool eof() { return m_readPos >= m_messageSize; } + int getReadSize() { return m_readPos - m_headerPos; } + int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); } + + bool eof() { return (m_readPos - m_headerPos) >= m_messageSize; } private: bool canRead(int bytes); void checkRead(int bytes); + uint16 m_headerPos; uint16 m_readPos; uint16 m_messageSize; uint8 m_buffer[BUFFER_MAXSIZE]; diff --git a/src/framework/net/outputmessage.cpp b/src/framework/net/outputmessage.cpp index f9db6f30..db108c81 100644 --- a/src/framework/net/outputmessage.cpp +++ b/src/framework/net/outputmessage.cpp @@ -29,7 +29,8 @@ OutputMessage::OutputMessage() void OutputMessage::reset() { - m_writePos = DATA_POS; + m_writePos = MAX_HEADER_SIZE; + m_headerPos = MAX_HEADER_SIZE; m_messageSize = 0; } @@ -91,6 +92,23 @@ void OutputMessage::addPaddingBytes(int bytes, uint8 byte) m_messageSize += bytes; } +void OutputMessage::writeChecksum() +{ + uint32 checksum = Fw::getAdlerChecksum(m_buffer + m_headerPos, m_messageSize); + assert(m_headerPos - 4 >= 0); + m_headerPos -= 4; + Fw::writeLE32(m_buffer + m_headerPos, checksum); + m_messageSize += 4; +} + +void OutputMessage::writeMessageSize() +{ + assert(m_headerPos - 2 >= 0); + m_headerPos -= 2; + Fw::writeLE16(m_buffer + m_headerPos, m_messageSize); + m_messageSize += 2; +} + bool OutputMessage::canWrite(int bytes) { if(m_writePos + bytes > BUFFER_MAXSIZE) diff --git a/src/framework/net/outputmessage.h b/src/framework/net/outputmessage.h index 94442b38..4f3c29bd 100644 --- a/src/framework/net/outputmessage.h +++ b/src/framework/net/outputmessage.h @@ -34,11 +34,7 @@ class OutputMessage : public LuaObject public: enum { BUFFER_MAXSIZE = 1024, - HEADER_POS = 0, - HEADER_LENGTH = 2, - CHECKSUM_POS = 2, - CHECKSUM_LENGTH = 4, - DATA_POS = 6 + MAX_HEADER_SIZE = 8 }; OutputMessage(); @@ -53,15 +49,19 @@ public: void addString(const std::string& value); void addPaddingBytes(int bytes, uint8 byte = 0); - uint8* getBuffer() { return m_buffer; } + uint8* getWriteBuffer() { return m_buffer + m_writePos; } + uint8* getHeaderBuffer() { return m_buffer + m_headerPos; } + uint8* getDataBuffer() { return m_buffer + MAX_HEADER_SIZE; } uint16 getMessageSize() { return m_messageSize; } - void setMessageSize(uint16 messageSize) { m_messageSize = messageSize; } - void setWritePos(uint16 writePos) { m_writePos = writePos; } + + void writeChecksum(); + void writeMessageSize(); private: bool canWrite(int bytes); void checkWrite(int bytes); + uint16 m_headerPos; uint16 m_writePos; uint16 m_messageSize; uint8 m_buffer[BUFFER_MAXSIZE]; diff --git a/src/framework/net/protocol.cpp b/src/framework/net/protocol.cpp index f019d462..d84545a9 100644 --- a/src/framework/net/protocol.cpp +++ b/src/framework/net/protocol.cpp @@ -69,40 +69,44 @@ void Protocol::send(OutputMessage& outputMessage) if(m_xteaEncryptionEnabled) xteaEncrypt(outputMessage); - // set checksum - uint32 checksum = getAdlerChecksum(outputMessage.getBuffer() + OutputMessage::DATA_POS, outputMessage.getMessageSize()); - outputMessage.setWritePos(OutputMessage::CHECKSUM_POS); - outputMessage.addU32(checksum); + // write checksum + if(m_checksumEnabled) + outputMessage.writeChecksum(); - // set size - uint16 messageSize = outputMessage.getMessageSize(); - outputMessage.setWritePos(OutputMessage::HEADER_POS); - outputMessage.addU16(messageSize); + // wirte message size + outputMessage.writeMessageSize(); // send if(m_connection) - m_connection->write(outputMessage.getBuffer(), outputMessage.getMessageSize()); + m_connection->write(outputMessage.getHeaderBuffer(), outputMessage.getMessageSize()); } void Protocol::recv() { m_inputMessage.reset(); + // first update message header size + int headerSize = 2; // 2 bytes for message size + if(m_checksumEnabled) + headerSize += 4; // 4 bytes for checksum + if(m_xteaEncryptionEnabled) + headerSize += 2; // 2 bytes for XTEA encrypted message size + m_inputMessage.setHeaderSize(headerSize); + + // read the first 2 bytes which contain the message size if(m_connection) - m_connection->read(InputMessage::HEADER_LENGTH, std::bind(&Protocol::internalRecvHeader, asProtocol(), std::placeholders::_1, std::placeholders::_2)); + m_connection->read(2, std::bind(&Protocol::internalRecvHeader, asProtocol(), std::placeholders::_1, std::placeholders::_2)); } void Protocol::internalRecvHeader(uint8* buffer, uint16 size) { - memcpy(m_inputMessage.getBuffer() + InputMessage::HEADER_POS, buffer, size); - // read message size - uint16 dataSize = m_inputMessage.getU16(); - m_inputMessage.setMessageSize(dataSize); + m_inputMessage.fillBuffer(buffer, size); + uint16 remainingSize = m_inputMessage.readSize(); - // schedule read for message data + // read remaining message data if(m_connection) - m_connection->read(dataSize, std::bind(&Protocol::internalRecvData, asProtocol(), std::placeholders::_1, std::placeholders::_2)); + m_connection->read(remainingSize, std::bind(&Protocol::internalRecvData, asProtocol(), std::placeholders::_1, std::placeholders::_2)); } void Protocol::internalRecvData(uint8* buffer, uint16 size) @@ -113,19 +117,16 @@ void Protocol::internalRecvData(uint8* buffer, uint16 size) return; } - memcpy(m_inputMessage.getBuffer() + InputMessage::CHECKSUM_POS, buffer, size); + m_inputMessage.fillBuffer(buffer, size); - if(m_checksumEnabled) { - uint32 checksum = getAdlerChecksum(m_inputMessage.getBuffer() + InputMessage::DATA_POS, m_inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH); - if(m_inputMessage.getU32() != checksum) { - logTraceError("got a network message with invalid checksum"); - return; - } + if(m_checksumEnabled && !m_inputMessage.readChecksum()) { + logTraceError("got a network message with invalid checksum"); + return; } - if(m_xteaEncryptionEnabled) { - if(!xteaDecrypt(m_inputMessage)) - return; + if(m_xteaEncryptionEnabled && !xteaDecrypt(m_inputMessage)) { + logTraceError("failed to decrypt message"); + return; } onRecv(m_inputMessage); @@ -143,16 +144,16 @@ void Protocol::generateXteaKey() bool Protocol::xteaDecrypt(InputMessage& inputMessage) { - uint16 messageSize = inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH; - if(messageSize % 8 != 0) { + uint16 encryptedSize = inputMessage.getUnreadSize(); + if(encryptedSize % 8 != 0) { logTraceError("invalid encrypted network message"); return false; } - uint32 *buffer = (uint32*)(inputMessage.getBuffer() + InputMessage::DATA_POS); + uint32 *buffer = (uint32*)(inputMessage.getReadBuffer()); int readPos = 0; - while(readPos < messageSize/4) { + while(readPos < encryptedSize/4) { uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1]; uint32 delta = 0x61C88647; uint32 sum = 0xC6EF3720; @@ -166,37 +167,32 @@ bool Protocol::xteaDecrypt(InputMessage& inputMessage) readPos = readPos + 2; } - int tmp = inputMessage.getU16(); - if(tmp > inputMessage.getMessageSize() - 4) { + uint16 decryptedSize = inputMessage.getU16() + 2; + int sizeDelta = decryptedSize - encryptedSize; + if(sizeDelta > 0 || -sizeDelta > encryptedSize) { logTraceError("invalid decrypted a network message"); return false; } - inputMessage.setMessageSize(tmp + InputMessage::UNENCRYPTED_DATA_POS); + inputMessage.setMessageSize(inputMessage.getMessageSize() + sizeDelta); return true; } void Protocol::xteaEncrypt(OutputMessage& outputMessage) { - uint16 messageLength = outputMessage.getMessageSize(); - - memmove(outputMessage.getBuffer() + OutputMessage::DATA_POS + 2, outputMessage.getBuffer() + OutputMessage::DATA_POS, messageLength); - *(uint16*)(outputMessage.getBuffer() + OutputMessage::DATA_POS) = messageLength; - - messageLength += 2; - outputMessage.setMessageSize(messageLength); - outputMessage.setWritePos(messageLength + OutputMessage::DATA_POS); + outputMessage.writeMessageSize(); + uint16 encryptedSize = outputMessage.getMessageSize(); //add bytes until reach 8 multiple - if((messageLength % 8) != 0) { - uint16 n = 8 - (messageLength % 8); + if((encryptedSize % 8) != 0) { + uint16 n = 8 - (encryptedSize % 8); outputMessage.addPaddingBytes(n); - messageLength += n; + encryptedSize += n; } int readPos = 0; - uint32 *buffer = (uint32*)(outputMessage.getBuffer() + OutputMessage::DATA_POS); - while(readPos < messageLength / 4) { + uint32 *buffer = (uint32*)(outputMessage.getDataBuffer() - 2); + while(readPos < encryptedSize / 4) { uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1]; uint32 delta = 0x61C88647; uint32 sum = 0; @@ -210,21 +206,3 @@ void Protocol::xteaEncrypt(OutputMessage& outputMessage) readPos = readPos + 2; } } - -uint32 Protocol::getAdlerChecksum(uint8* buffer, uint16 size) -{ - uint32 a = 1, b = 0; - while (size > 0) { - size_t tlen = size > 5552 ? 5552 : size; - size -= tlen; - do { - a += *buffer++; - b += a; - } while (--tlen); - - a %= 65521; - b %= 65521; - } - - return (b << 16) | a; -} diff --git a/src/framework/net/protocol.h b/src/framework/net/protocol.h index 5938de41..fa07e944 100644 --- a/src/framework/net/protocol.h +++ b/src/framework/net/protocol.h @@ -64,7 +64,6 @@ protected: private: bool xteaDecrypt(InputMessage& inputMessage); void xteaEncrypt(OutputMessage& outputMessage); - uint32 getAdlerChecksum(uint8* buffer, uint16 size); bool m_checksumEnabled; bool m_xteaEncryptionEnabled; diff --git a/src/framework/util/tools.h b/src/framework/util/tools.h index bd0287ec..24c3b654 100644 --- a/src/framework/util/tools.h +++ b/src/framework/util/tools.h @@ -326,6 +326,22 @@ inline float randomRange(float min, float max) { return min + (max - min)*dis(gen); } +inline uint32 getAdlerChecksum(uint8* buffer, uint16 size) { + register uint32 a = 1, b = 0, tlen; + while(size > 0) { + tlen = size > 5552 ? 5552 : size; + size -= tlen; + do { + a += *buffer++; + b += a; + } while (--tlen); + + a %= 65521; + b %= 65521; + } + return (b << 16) | a; +} + } // shortcut for Fw::dump diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp index cd681650..0f4e7f44 100644 --- a/src/otclient/core/map.cpp +++ b/src/otclient/core/map.cpp @@ -269,16 +269,22 @@ CreaturePtr Map::getCreatureById(uint32 id) LocalPlayerPtr localPlayer = g_game.getLocalPlayer(); if(localPlayer && localPlayer->getId() == id) return localPlayer; - return m_knownCreatures[id]; + auto it = m_knownCreatures.find(id); + if(it == m_knownCreatures.end()) + return nullptr; + return it->second; } void Map::removeCreatureById(uint32 id) { if(id == 0) return; - if(CreaturePtr creature = m_knownCreatures[id]) - creature->setRemoved(true); - m_knownCreatures.erase(id); + + auto it = m_knownCreatures.find(id); + if(it != m_knownCreatures.end()) + it->second->setRemoved(true); + + m_knownCreatures.erase(it); } void Map::setCentralPosition(const Position& centralPosition) @@ -288,13 +294,8 @@ void Map::setCentralPosition(const Position& centralPosition) // remove creatures from tiles that we are not aware anymore for(const auto& pair : m_knownCreatures) { const CreaturePtr& creature = pair.second; - if(creature) { - if(!isAwareOfPosition(creature->getPosition())) { - removeThing(creature); - } - } - else - logTraceError("invalid creature"); + if(!isAwareOfPosition(creature->getPosition())) + removeThing(creature); } // this fixes local player position when the local player is removed from the map, diff --git a/src/otclient/core/thingstype.cpp b/src/otclient/core/thingstype.cpp index a6450a73..dfc1f95f 100644 --- a/src/otclient/core/thingstype.cpp +++ b/src/otclient/core/thingstype.cpp @@ -24,36 +24,35 @@ #include "spritemanager.h" #include "thing.h" #include +#include ThingsType g_thingsType; ThingType ThingsType::m_emptyThingType; bool ThingsType::load(const std::string& file) { - try { - std::stringstream fin; - g_resources.loadFile(file, fin); - - m_signature = Fw::getU32(fin); + FileStreamPtr fin = g_resources.openFile(file); + if(!fin) { + logError("unable to open dat file '", file, "'"); + return false; + } - int numThings[LastCategory]; - for(int i = 0; i < LastCategory; ++i) - numThings[i] = Fw::getU16(fin); + m_signature = fin->getU32(); - numThings[Item] -= 99; + int numThings[LastCategory]; + for(int i = 0; i < LastCategory; ++i) + numThings[i] = fin->getU16(); - for(int i = 0; i < LastCategory; ++i) { - m_things[i].resize(numThings[i]); - for(int id = 0; id < numThings[i]; ++id) - parseThingType(fin, m_things[i][id]); - } + numThings[Item] -= 99; - m_loaded = true; - return true; - } catch(Exception& e) { - logError("Failed to load dat from '", file, "': ", e.what()); - return false; + for(int i = 0; i < LastCategory; ++i) { + m_things[i].resize(numThings[i]); + for(int id = 0; id < numThings[i]; ++id) + parseThingType(fin, m_things[i][id]); } + + m_loaded = true; + return true; } void ThingsType::unload() @@ -62,37 +61,39 @@ void ThingsType::unload() m_things[i].clear(); } -void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType) +void ThingsType::parseThingType(const FileStreamPtr& fin, ThingType& thingType) { - assert(fin.good()); - while(true) { - int property = Fw::getU8(fin); + int property = fin->getU8(); if(property == ThingType::LastPropertyValue) break; thingType.m_properties[property] = true; if(property == ThingType::IsGround) - thingType.m_parameters[ThingType::GroundSpeed] = Fw::getU16(fin); + thingType.m_parameters[ThingType::GroundSpeed] = fin->getU16(); else if(property == ThingType::IsWritable || property == ThingType::IsWritableOnce) - thingType.m_parameters[ThingType::MaxTextLenght] = Fw::getU16(fin); + thingType.m_parameters[ThingType::MaxTextLenght] = fin->getU16(); else if(property == ThingType::HasLight) { - thingType.m_parameters[ThingType::LightLevel] = Fw::getU16(fin); - thingType.m_parameters[ThingType::LightColor] = Fw::getU16(fin); + thingType.m_parameters[ThingType::LightLevel] = fin->getU16(); + thingType.m_parameters[ThingType::LightColor] = fin->getU16(); } else if(property == ThingType::HasDisplacement) { - thingType.m_parameters[ThingType::DisplacementX] = Fw::getU16(fin); - thingType.m_parameters[ThingType::DisplacementY] = Fw::getU16(fin); + thingType.m_parameters[ThingType::DisplacementX] = fin->getU16(); + thingType.m_parameters[ThingType::DisplacementY] = fin->getU16(); } else if(property == ThingType::HasElevation) - thingType.m_parameters[ThingType::Elevation] = Fw::getU16(fin); + thingType.m_parameters[ThingType::Elevation] = fin->getU16(); else if(property == ThingType::MiniMap) - thingType.m_parameters[ThingType::MiniMapColor] = Fw::getU16(fin); + thingType.m_parameters[ThingType::MiniMapColor] = fin->getU16(); else if(property == ThingType::LensHelp) - thingType.m_parameters[ThingType::LensHelpParameter] = Fw::getU16(fin); + thingType.m_parameters[ThingType::LensHelpParameter] = fin->getU16(); else if(property == ThingType::Cloth) - thingType.m_parameters[ThingType::ClothSlot] = Fw::getU16(fin); + thingType.m_parameters[ThingType::ClothSlot] = fin->getU16(); +#if PROTOCOL<=810 + else if(property == ThingType::IsRune) + thingType.m_properties[ThingType::IsStackable] = true; +#endif } int totalSprites = 1; @@ -102,7 +103,7 @@ void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType) continue; } - thingType.m_dimensions[i] = Fw::getU8(fin); + thingType.m_dimensions[i] = fin->getU8(); if(i != ThingType::ExactSize) totalSprites *= thingType.m_dimensions[i]; @@ -111,7 +112,7 @@ void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType) thingType.m_spritesIndex.resize(totalSprites); thingType.m_sprites.resize(totalSprites); for(int i = 0; i < totalSprites; i++) - thingType.m_spritesIndex[i] = Fw::getU16(fin); + thingType.m_spritesIndex[i] = fin->getU16(); } ThingType *ThingsType::getThingType(uint16 id, Categories category) diff --git a/src/otclient/core/thingstype.h b/src/otclient/core/thingstype.h index 509dfdf1..7186e1d1 100644 --- a/src/otclient/core/thingstype.h +++ b/src/otclient/core/thingstype.h @@ -24,6 +24,7 @@ #define DATMANAGER_H #include +#include #include "thingtype.h" class ThingsType @@ -41,7 +42,7 @@ public: bool load(const std::string& file); void unload(); - void parseThingType(std::stringstream& fin, ThingType& thingType); + void parseThingType(const FileStreamPtr& fin, ThingType& thingType); ThingType *getEmptyThingType() { return &m_emptyThingType; } ThingType *getThingType(uint16 id, Categories category); diff --git a/src/otclient/core/thingtype.h b/src/otclient/core/thingtype.h index 6cffe12f..f4c6c9f4 100644 --- a/src/otclient/core/thingtype.h +++ b/src/otclient/core/thingtype.h @@ -50,6 +50,9 @@ struct ThingType IsStackable, IsForceUse, IsMultiUse, +#if PROTOCOL<=810 + IsRune, +#endif IsWritable, IsWritableOnce, IsFluidContainer, diff --git a/src/otclient/net/protocolcodes.h b/src/otclient/net/protocolcodes.h index 8a78e352..da89d2d9 100644 --- a/src/otclient/net/protocolcodes.h +++ b/src/otclient/net/protocolcodes.h @@ -25,7 +25,7 @@ #include -#if PROTOCOL != 860 && PROTOCOL != 861 && PROTOCOL != 862 && PROTOCOL != 870 +#if PROTOCOL != 860 && PROTOCOL != 861 && PROTOCOL != 862 && PROTOCOL != 870 && PROTOCOL != 810 #error "the supplied protocol version is not supported" #endif @@ -51,6 +51,8 @@ namespace Proto { constexpr int NumViolationReasons = 19; #elif PROTOCOL>=860 constexpr int NumViolationReasons = 20; +#elif PROTOCOL>=810 + constexpr int NumViolationReasons = 32; #endif enum OsTypes { @@ -210,9 +212,7 @@ namespace Proto { ClientRefreshContainer = 202, ClientRequestOutfit = 210, ClientChangeOutfit = 211, -#if PROTOCOL>=870 - ClientMount = 212, -#endif + ClientMount = 212, // 870 ClientAddVip = 220, ClientRemoveVip = 221, ClientBugReport = 230, @@ -242,7 +242,7 @@ namespace Proto { ServerSpeakMonsterSay, ServerSpeakMonsterYell, - // removed + // unsupported ServerSpeakRVRChannel = 255, ServerSpeakRVRAnswer, ServerSpeakRVRContinue, @@ -266,6 +266,29 @@ namespace Proto { ServerSpeakChannelRed2 = 17, ServerSpeakMonsterSay = 19, ServerSpeakMonsterYell +#elif PROTOCOL>=810 + ServerSpeakSay = 1, + ServerSpeakWhisper, + ServerSpeakYell, + ServerSpeakPrivate, + ServerSpeakChannelYellow, + ServerSpeakRVRChannel, + ServerSpeakRVRAnswer, + ServerSpeakRVRContinue, + ServerSpeakBroadcast, + ServerSpeakChannelRed, + ServerSpeakPrivateRed, + ServerSpeakChannelOrange, + // 13 ?? + ServerSpeakChannelRed2 = 14, + // 15 ?? + ServerSpeakMonsterSay = 16, + ServerSpeakMonsterYell, + + // unsupported + ServerSpeakPrivatePlayerToNpc = 255, + ServerSpeakPrivateNpcToPlayer, + ServerSpeakChannelWhite #endif }; @@ -289,9 +312,22 @@ namespace Proto { MessageEventAdvance, MessageEventDefault, MessageStatusDefault, - MessageInfoDescription , + MessageInfoDescription, MessageStatusSmall, MessageConsoleBlue +#elif PROTOCOL>=810 + MessageWarning = 18, + MessageEventAdvance, + MessageEventDefault, + MessageStatusDefault, + MessageInfoDescription, + MessageStatusSmall, + MessageConsoleBlue, + MessageConsoleRed, + + // unsupported + MessageConsoleOrange = 255, + MessageConsoleOrange2, #endif }; diff --git a/src/otclient/net/protocolgame.cpp b/src/otclient/net/protocolgame.cpp index e027b8a6..69d180ee 100644 --- a/src/otclient/net/protocolgame.cpp +++ b/src/otclient/net/protocolgame.cpp @@ -32,7 +32,6 @@ void ProtocolGame::login(const std::string& accountName, const std::string& acco return; } - m_waitingLoginPacket = true; m_accountName = accountName; m_accountPassword = accountPassword; m_characterName = characterName; @@ -43,17 +42,26 @@ void ProtocolGame::login(const std::string& accountName, const std::string& acco void ProtocolGame::onConnect() { recv(); + +#if PROTOCOL>=860 + m_waitingLoginPacket = true; +#else + sendLoginPacket(0, 0); +#endif } void ProtocolGame::onRecv(InputMessage& inputMessage) { + // only for protocol >= 860 if(m_waitingLoginPacket) { inputMessage.skipBytes(3); uint32 timestamp = inputMessage.getU32(); uint8 unknown = inputMessage.getU8(); m_waitingLoginPacket = false; + enableChecksum(); + sendLoginPacket(timestamp, unknown); recv(); } diff --git a/src/otclient/net/protocolgameparse.cpp b/src/otclient/net/protocolgameparse.cpp index 0af5eeaf..0d586083 100644 --- a/src/otclient/net/protocolgameparse.cpp +++ b/src/otclient/net/protocolgameparse.cpp @@ -35,9 +35,12 @@ void ProtocolGame::parseMessage(InputMessage& msg) { + int opcode = 0; + int prevOpcode = 0; + try { while(!msg.eof()) { - int opcode = msg.getU8(); + opcode = msg.getU8(); switch(opcode) { case Proto::GameServerInitGame: @@ -268,12 +271,13 @@ void ProtocolGame::parseMessage(InputMessage& msg) parseExtendedOpcode(msg); break; default: - Fw::throwException("unknown opcode ", opcode); + Fw::throwException("unknown opcode ", opcode, ", previous opcode is ", prevOpcode); break; } + prevOpcode = opcode; } } catch(Exception& e) { - logTraceError(e.what()); + logError("Network exception (", msg.getUnreadSize(), " bytes unread, last opcode is ", opcode, ", prev opcode is ", prevOpcode, "): ", e.what()); } } @@ -392,7 +396,11 @@ void ProtocolGame::parseUpdateTile(InputMessage& msg) void ProtocolGame::parseTileAddThing(InputMessage& msg) { Position pos = parsePosition(msg); - int stackPos = msg.getU8(); + int stackPos = -1; + +#if PROTOCOL>=860 + stackPos = msg.getU8(); +#endif ThingPtr thing = internalGetThing(msg); g_map.addThing(thing, pos, stackPos); @@ -754,8 +762,12 @@ void ProtocolGame::parsePlayerStats(InputMessage& msg) { double health = msg.getU16(); double maxHealth = msg.getU16(); +#if PROTOCOL>=860 double freeCapacity = msg.getU32() / 100.0; -#if PROTOCOL >= 870 +#else + double freeCapacity = msg.getU16() / 100.0; +#endif +#if PROTOCOL>=870 double experience = msg.getU64(); #else double experience = msg.getU32(); @@ -805,7 +817,9 @@ void ProtocolGame::parsePlayerState(InputMessage& msg) void ProtocolGame::parsePlayerCancelAttack(InputMessage& msg) { +#if PROTOCOL>=860 msg.getU32(); // unknown +#endif g_game.processAttackCancel(); } @@ -830,11 +844,11 @@ void ProtocolGame::parseCreatureSpeak(InputMessage& msg) std::string name = msg.getString(); int level = msg.getU16(); - int serverType = msg.getU8(); + int speakType = msg.getU8(); int channelId = 0; Position creaturePos; - switch(serverType) { + switch(speakType) { case Proto::ServerSpeakSay: case Proto::ServerSpeakWhisper: case Proto::ServerSpeakYell: @@ -859,12 +873,12 @@ void ProtocolGame::parseCreatureSpeak(InputMessage& msg) msg.getU32(); break; default: - logTraceError("unknown speak type ", serverType); + logTraceError("unknown speak type ", speakType); break; } std::string message = msg.getString(); - Otc::SpeakType type = Proto::translateSpeakTypeFromServer(serverType); + Otc::SpeakType type = Proto::translateSpeakTypeFromServer(speakType); g_game.processCreatureSpeak(name, level, type, message, channelId, creaturePos); } @@ -1140,8 +1154,8 @@ void ProtocolGame::setTileDescription(InputMessage& msg, Position position) int stackPos = 0; while(true) { - int inspectTileId = msg.getU16(true); - if(inspectTileId >= 0xFF00) + int inspectItemId = msg.getU16(true); + if(inspectItemId >= 0xFF00) return; else { if(stackPos >= 10) @@ -1159,8 +1173,8 @@ Outfit ProtocolGame::internalGetOutfit(InputMessage& msg) { Outfit outfit; - int id = msg.getU16(); - if(id != 0) { + int lookType = msg.getU16(); + if(lookType != 0) { outfit.setCategory(ThingsType::Creature); int head = msg.getU8(); int body = msg.getU8(); @@ -1171,7 +1185,7 @@ Outfit ProtocolGame::internalGetOutfit(InputMessage& msg) msg.getU16(); // mount #endif - outfit.setId(id); + outfit.setId(lookType); outfit.setHead(head); outfit.setBody(body); outfit.setLegs(legs); @@ -1179,14 +1193,14 @@ Outfit ProtocolGame::internalGetOutfit(InputMessage& msg) outfit.setAddons(addons); } else { - int id = msg.getU16(); - if(id == 0) { + int lookTypeEx = msg.getU16(); + if(lookTypeEx == 0) { outfit.setCategory(ThingsType::Effect); outfit.setId(13); } else { outfit.setCategory(ThingsType::Item); - outfit.setId(id); + outfit.setId(lookTypeEx); } } @@ -1198,7 +1212,10 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg) ThingPtr thing; int thingId = msg.getU16(); - assert(thingId != 0); + if(thingId == 0) { + Fw::throwException("[ProtocolGame::internalGetThing] thingId == 0"); + } + if(thingId == 0x0061 || thingId == 0x0062) { // add new creature CreaturePtr creature; @@ -1253,10 +1270,14 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg) // emblem is sent only when the creature is not known int emblem = -1; + bool passable = false; + +#if PROTOCOL>=860 if(thingId == 0x0061) emblem = msg.getU8(); - bool passable = (msg.getU8() == 0); + passable = (msg.getU8() == 0); +#endif if(creature) { creature->setHealthPercent(healthPercent); @@ -1277,8 +1298,9 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg) thing = creature; } else if(thingId == 0x0063) { // creature turn parseCreatureTurn(msg); - } else // item + } else { // item thing = internalGetItem(msg, thingId); + } return thing; } diff --git a/src/otclient/net/protocolgamesend.cpp b/src/otclient/net/protocolgamesend.cpp index 2af4b5a8..79fbbf0d 100644 --- a/src/otclient/net/protocolgamesend.cpp +++ b/src/otclient/net/protocolgamesend.cpp @@ -37,7 +37,9 @@ void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown) msg.addU16(Proto::ClientVersion); + int paddingBytes = 128; msg.addU8(0); // first RSA byte must be 0 + paddingBytes -= 1; // xtea key generateXteaKey(); @@ -45,20 +47,32 @@ void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown) msg.addU32(m_xteaKey[1]); msg.addU32(m_xteaKey[2]); msg.addU32(m_xteaKey[3]); - msg.addU8(0); // is gm set? + paddingBytes -= 17; + +#if PROTOCOL>=860 + enableChecksum(); + msg.addString(m_accountName); msg.addString(m_characterName); msg.addString(m_accountPassword); msg.addU32(timestamp); msg.addU8(unknown); + paddingBytes -= 11 + m_accountName.length() + m_characterName.length() + m_accountPassword.length(); +#elif PROTOCOL>=810 + msg.addU32(Fw::fromstring(m_accountName)); + msg.addString(m_characterName); + msg.addString(m_accountPassword); + + paddingBytes -= 8 + m_characterName.length() + m_accountPassword.length(); +#endif // complete the 128 bytes for rsa encryption with zeros - msg.addPaddingBytes(128 - (29 + m_accountName.length() + m_characterName.length() + m_accountPassword.length())); + msg.addPaddingBytes(paddingBytes); // encrypt with RSA - Rsa::encrypt((char*)msg.getBuffer() + 6 + msg.getMessageSize() - 128, 128, Proto::RSA); + Rsa::encrypt((char*)msg.getWriteBuffer() - 128, 128, Proto::RSA); send(msg); diff --git a/src/otclient/net/protocollogin.cpp b/src/otclient/net/protocollogin.cpp index bbee2161..be1d4ca6 100644 --- a/src/otclient/net/protocollogin.cpp +++ b/src/otclient/net/protocollogin.cpp @@ -28,11 +28,6 @@ #include #include -ProtocolLogin::ProtocolLogin() -{ - enableChecksum(); -} - void ProtocolLogin::login(const std::string& host, int port, const std::string& accountName, const std::string& accountPassword) { if(accountName.empty() || accountPassword.empty()) { @@ -88,33 +83,49 @@ void ProtocolLogin::onError(const boost::system::error_code& error) void ProtocolLogin::sendLoginPacket() { - OutputMessage oMsg; + OutputMessage msg; - oMsg.addU8(Proto::ClientEnterAccount); - oMsg.addU16(Proto::OsLinux); - oMsg.addU16(Proto::ClientVersion); + msg.addU8(Proto::ClientEnterAccount); +#ifdef WIN32 + msg.addU16(Proto::OsWindows); +#else + msg.addU16(Proto::OsLinux); +#endif + msg.addU16(Proto::ClientVersion); - oMsg.addU32(g_thingsType.getSignature()); // data signature - oMsg.addU32(g_sprites.getSignature()); // sprite signature - oMsg.addU32(Proto::PicSignature); // pic signature + msg.addU32(g_thingsType.getSignature()); // data signature + msg.addU32(g_sprites.getSignature()); // sprite signature + msg.addU32(Proto::PicSignature); // pic signature - oMsg.addU8(0); // first RSA byte must be 0 + int paddingBytes = 128; + msg.addU8(0); // first RSA byte must be 0 + paddingBytes -= 1; // xtea key generateXteaKey(); - oMsg.addU32(m_xteaKey[0]); - oMsg.addU32(m_xteaKey[1]); - oMsg.addU32(m_xteaKey[2]); - oMsg.addU32(m_xteaKey[3]); - oMsg.addString(m_accountName); - oMsg.addString(m_accountPassword); + msg.addU32(m_xteaKey[0]); + msg.addU32(m_xteaKey[1]); + msg.addU32(m_xteaKey[2]); + msg.addU32(m_xteaKey[3]); + paddingBytes -= 16; + +#if PROTOCOL>=860 + enableChecksum(); + + msg.addString(m_accountName); + msg.addString(m_accountPassword); + paddingBytes -= 4 + m_accountName.length() + m_accountPassword.length(); +#elif PROTOCOL>=810 + msg.addU32(Fw::fromstring(m_accountName)); + msg.addString(m_accountPassword); + paddingBytes -= 6 + m_accountPassword.length(); +#endif - // complete the 128 bytes for rsa encryption with zeros - oMsg.addPaddingBytes(128 - (21 + m_accountName.length() + m_accountPassword.length())); - Rsa::encrypt((char*)oMsg.getBuffer() + InputMessage::DATA_POS + oMsg.getMessageSize() - 128, 128, Proto::RSA); + msg.addPaddingBytes(paddingBytes); // complete the 128 bytes for rsa encryption with zeros + Rsa::encrypt((char*)msg.getWriteBuffer() - 128, 128, Proto::RSA); - send(oMsg); + send(msg); enableXteaEncryption(); recv(); } diff --git a/src/otclient/net/protocollogin.h b/src/otclient/net/protocollogin.h index 8057a4f5..d1eef585 100644 --- a/src/otclient/net/protocollogin.h +++ b/src/otclient/net/protocollogin.h @@ -32,8 +32,6 @@ typedef std::shared_ptr ProtocolLoginPtr; class ProtocolLogin : public Protocol { public: - ProtocolLogin(); - static ProtocolLoginPtr create() { return ProtocolLoginPtr(new ProtocolLogin); } void login(const std::string& host, int port, const std::string& accountName, const std::string& accountPassword);