diff --git a/CMakeLists.txt b/CMakeLists.txt index 2161138c..83ec8868 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,8 @@ SET(SOURCES # game net src/protocollogin.cpp + src/protocolgame.cpp + src/protocolgameparse.cpp # framework core src/framework/core/dispatcher.cpp diff --git a/data/modules/mainmenu/ui/entergamewindow.otml b/data/modules/mainmenu/ui/entergamewindow.otml index bab0ac88..e4c58684 100644 --- a/data/modules/mainmenu/ui/entergamewindow.otml +++ b/data/modules/mainmenu/ui/entergamewindow.otml @@ -13,7 +13,7 @@ margin.top: 28 %textEdit#accountNameTextEdit - text: tibialua0 + text: otclient0 anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom @@ -28,7 +28,7 @@ margin.top: 8 %textEdit#passwordTextEdit - text: lua123456 + text: 123456 anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom diff --git a/src/framework/net/connection.h b/src/framework/net/connection.h index fbeb131d..bd36819b 100644 --- a/src/framework/net/connection.h +++ b/src/framework/net/connection.h @@ -36,7 +36,7 @@ private: asio::ip::tcp::resolver m_resolver; asio::ip::tcp::socket m_socket; - uint8 m_recvBuffer[65536]; + uint8 m_recvBuffer[65538]; uint16 m_recvSize; RecvCallback m_recvCallback; }; diff --git a/src/framework/net/inputmessage.cpp b/src/framework/net/inputmessage.cpp index 0e7cfc60..87733b58 100644 --- a/src/framework/net/inputmessage.cpp +++ b/src/framework/net/inputmessage.cpp @@ -11,33 +11,47 @@ void InputMessage::reset() m_messageSize = 2; } -uint8 InputMessage::getU8() +uint8 InputMessage::getU8(bool blockReadPos) { assert(canRead(1)); - return m_buffer[m_readPos++]; + uint8 v = m_buffer[m_readPos]; + + if(!blockReadPos) + m_readPos += 1; + + return v; } -uint16 InputMessage::getU16() +uint16 InputMessage::getU16(bool blockReadPos) { assert(canRead(2)); uint16 v = *(uint16_t*)(m_buffer + m_readPos); - m_readPos += 2; + + if(!blockReadPos) + m_readPos += 2; + return v; } -uint32 InputMessage::getU32() +uint32 InputMessage::getU32(bool blockReadPos) { assert(canRead(4)); uint32 v = *(uint32*)(m_buffer + m_readPos); - m_readPos += 4; + + if(!blockReadPos) + m_readPos += 4; + return v; } -uint64 InputMessage::getU64() +uint64 InputMessage::getU64(bool blockReadPos) { assert(canRead(8)); uint64 v = *(uint64*)(m_buffer + m_readPos); - m_readPos += 8; + + if(!blockReadPos) + m_readPos += 8; + return v; } diff --git a/src/framework/net/inputmessage.h b/src/framework/net/inputmessage.h index d2d0d9e3..4e8cad36 100644 --- a/src/framework/net/inputmessage.h +++ b/src/framework/net/inputmessage.h @@ -3,6 +3,9 @@ #include "netdeclarations.h" +// TODO remove this +#include <../position.h> + class InputMessage { public: @@ -20,12 +23,13 @@ public: void reset(); - uint8 getU8(); - uint16 getU16(); - uint32 getU32(); - uint64 getU64(); + uint8 getU8(bool blockReadPos = false); + uint16 getU16(bool blockReadPos = false); + uint32 getU32(bool blockReadPos = false); + uint64 getU64(bool blockReadPos = false); std::string getString(); + void skipBytes(uint16 bytes) { m_readPos += bytes; } uint8* getBuffer() { return m_buffer; } uint16 getMessageSize() { return m_messageSize; } void setMessageSize(uint16 messageSize) { m_messageSize = messageSize; } diff --git a/src/framework/net/netdeclarations.h b/src/framework/net/netdeclarations.h index b7fd64e8..e1bccfbf 100644 --- a/src/framework/net/netdeclarations.h +++ b/src/framework/net/netdeclarations.h @@ -20,12 +20,12 @@ typedef std::shared_ptr ProtocolPtr; "2907336840325241747827401343576296990629870233111328210165697754" \ "88792221429527047321331896351555606801473202394175817" -/* -#define RSA "1091201329673994292788609605089955415282375029027981291234687579" \ + +#define OTSERV_PUBLIC_RSA "1091201329673994292788609605089955415282375029027981291234687579" \ "3726629149257644633073969600111060390723088861007265581882535850" \ "3429057592827629436413108566029093628212635953836686562675849720" \ "6207862794310902180176810615217550567108238764764442605581471797" \ "07119674283982419152118103759076030616683978566631413" -*/ + #endif diff --git a/src/framework/net/protocol.cpp b/src/framework/net/protocol.cpp index 6e07866d..872385d7 100644 --- a/src/framework/net/protocol.cpp +++ b/src/framework/net/protocol.cpp @@ -6,6 +6,7 @@ Protocol::Protocol() : { m_connection->setErrorCallback(std::bind(&Protocol::onError, this, std::placeholders::_1)); m_xteaEncryptionEnabled = false; + m_checksumEnabled = true; } void Protocol::connect(const std::string& host, uint16 port) @@ -13,24 +14,24 @@ void Protocol::connect(const std::string& host, uint16 port) m_connection->connect(host, port, std::bind(&Protocol::onConnect, asProtocol())); } -void Protocol::send(OutputMessage* outputMessage) +void Protocol::send(OutputMessage& outputMessage) { // Encrypt 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); + uint32 checksum = getAdlerChecksum(outputMessage.getBuffer() + OutputMessage::DATA_POS, outputMessage.getMessageSize()); + outputMessage.setWritePos(OutputMessage::CHECKSUM_POS); + outputMessage.addU32(checksum); // Set size - uint16 messageSize = outputMessage->getMessageSize(); - outputMessage->setWritePos(OutputMessage::HEADER_POS); - outputMessage->addU16(messageSize); + uint16 messageSize = outputMessage.getMessageSize(); + outputMessage.setWritePos(OutputMessage::HEADER_POS); + outputMessage.addU16(messageSize); // Send - m_connection->send(outputMessage->getBuffer(), outputMessage->getMessageSize()); + m_connection->send(outputMessage.getBuffer(), outputMessage.getMessageSize()); } void Protocol::recv() @@ -54,17 +55,19 @@ void Protocol::internalRecvData(uint8* buffer, uint16 size) { memcpy(m_inputMessage.getBuffer() + InputMessage::CHECKSUM_POS, buffer, size); - uint32 checksum = getAdlerChecksum(m_inputMessage.getBuffer() + InputMessage::DATA_POS, m_inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH); - if(m_inputMessage.getU32() != checksum) { - // error - logError("Checksum is invalid."); - return; + if(m_checksumEnabled) { + uint32 checksum = getAdlerChecksum(m_inputMessage.getBuffer() + InputMessage::DATA_POS, m_inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH); + if(m_inputMessage.getU32() != checksum) { + // error + logError("Checksum is invalid."); + return; + } } if(m_xteaEncryptionEnabled) - xteaDecrypt(&m_inputMessage); + xteaDecrypt(m_inputMessage); - onRecv(&m_inputMessage); + onRecv(m_inputMessage); } void Protocol::onError(const boost::system::error_code& err) @@ -82,16 +85,16 @@ void Protocol::onError(const boost::system::error_code& err) callField("onError", message.str()); } -bool Protocol::xteaDecrypt(InputMessage* inputMessage) +bool Protocol::xteaDecrypt(InputMessage& inputMessage) { // FIXME: this function has not been tested yet - uint16 messageSize = inputMessage->getMessageSize() - InputMessage::CHECKSUM_LENGTH; + uint16 messageSize = inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH; if(messageSize % 8 != 0) { - //LOG_TRACE_DEBUG("not valid encrypted message size") + logDebug("not valid encrypted message size"); return false; } - uint32 *buffer = (uint32*)(inputMessage->getBuffer() + InputMessage::DATA_POS); + uint32 *buffer = (uint32*)(inputMessage.getBuffer() + InputMessage::DATA_POS); int readPos = 0; while(readPos < messageSize/4) { @@ -108,29 +111,29 @@ bool Protocol::xteaDecrypt(InputMessage* inputMessage) readPos = readPos + 2; } - int tmp = inputMessage->getU16(); - if(tmp > inputMessage->getMessageSize() - 4) { - //LOG_TRACE_DEBUG("not valid unencrypted message size") + int tmp = inputMessage.getU16(); + if(tmp > inputMessage.getMessageSize() - 4) { + logDebug("not valid unencrypted message size"); return false; } - inputMessage->setMessageSize(tmp + InputMessage::UNENCRYPTED_DATA_POS); + inputMessage.setMessageSize(tmp + InputMessage::UNENCRYPTED_DATA_POS); return true; } -void Protocol::xteaEncrypt(OutputMessage* outputMessage) +void Protocol::xteaEncrypt(OutputMessage& outputMessage) { - uint16 messageLength = outputMessage->getMessageSize(); + uint16 messageLength = outputMessage.getMessageSize(); //add bytes until reach 8 multiple if((messageLength % 8) != 0) { uint16 n = 8 - (messageLength % 8); - outputMessage->addPaddingBytes(n); + outputMessage.addPaddingBytes(n); messageLength += n; } int readPos = 0; - uint32 *buffer = (uint32*)outputMessage->getBuffer() + OutputMessage::DATA_POS; + uint32 *buffer = (uint32*)outputMessage.getBuffer() + OutputMessage::DATA_POS; while(readPos < messageLength / 4) { uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1]; uint32 delta = 0x61C88647; diff --git a/src/framework/net/protocol.h b/src/framework/net/protocol.h index 9f9c1051..f691f4bc 100644 --- a/src/framework/net/protocol.h +++ b/src/framework/net/protocol.h @@ -13,13 +13,13 @@ public: Protocol(); void connect(const std::string& host, uint16 port); - void send(OutputMessage* outputMessage); + void send(OutputMessage& outputMessage); void recv(); void internalRecvHeader(uint8* buffer, uint16 size); void internalRecvData(uint8* buffer, uint16 size); virtual void onConnect() = 0; - virtual void onRecv(InputMessage* inputMessage) = 0; + virtual void onRecv(InputMessage& inputMessage) = 0; virtual void onError(const boost::system::error_code& err); ProtocolPtr asProtocol() { return std::static_pointer_cast(shared_from_this()); } @@ -28,12 +28,12 @@ public: protected: uint32 m_xteaKey[4]; - bool m_xteaEncryptionEnabled; + bool m_checksumEnabled, m_xteaEncryptionEnabled; InputMessage m_inputMessage; private: - bool xteaDecrypt(InputMessage* inputMessage); - void xteaEncrypt(OutputMessage* outputMessage); + bool xteaDecrypt(InputMessage& inputMessage); + void xteaEncrypt(OutputMessage& outputMessage); uint32 getAdlerChecksum(uint8* buffer, uint16 size); ConnectionPtr m_connection; diff --git a/src/player.h b/src/player.h new file mode 100644 index 00000000..40823706 --- /dev/null +++ b/src/player.h @@ -0,0 +1,17 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include "position.h" + +class Player +{ +public: + + void setPosition(Position position) { m_position = position; } + Position getPosition() { return m_position; } + +private: + Position m_position; +}; + +#endif diff --git a/src/position.h b/src/position.h new file mode 100644 index 00000000..83b2e818 --- /dev/null +++ b/src/position.h @@ -0,0 +1,26 @@ +#ifndef POSITION_H +#define POSITION_H + +class Position +{ +public: + Position(uint16 x = 0, uint16 y = 0, uint8 z = 0) { + m_x = x; + m_y = y; + m_z = z; + } + + void setX(uint16 x) { m_x = x; } + void setY(uint16 y) { m_y = y; } + void setZ(uint8 z) { m_z = z; } + + uint16 getX() { return m_x; } + uint16 getY() { return m_y; } + uint8 getZ() { return m_z; } + +private: + uint16 m_x, m_y; + uint8 m_z; +}; + +#endif diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp new file mode 100755 index 00000000..f8067c57 --- /dev/null +++ b/src/protocolgame.cpp @@ -0,0 +1,95 @@ +#include "protocolgame.h" +#include + +ProtocolGame::ProtocolGame() +{ + m_checksumEnabled = false; +} + +ProtocolGame::~ProtocolGame() +{ +} + +void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, uint32 ip, uint16 port, const std::string& characterName) +{ + if(accountName.empty() || accountPassword.empty()) { + callField("onError", "You must enter an account name and password."); + return; + } + + m_accountName = accountName; + m_accountPassword = accountPassword; + m_characterName = characterName; + + char host[16]; + sprintf(host, "%d.%d.%d.%d", (uint8)ip, (uint8)(ip >> 8), (uint8)(ip >> 16), (uint8)(ip >> 24)); + + connect(host, port); +} + +void ProtocolGame::onConnect() +{ + recv(); +} + +void ProtocolGame::sendLoginPacket(uint32 timestamp, uint8 unknown) +{ + OutputMessage oMsg; + + oMsg.addU8(0x0A); // Protocol id + oMsg.addU16(0x02); // OS + oMsg.addU16(862); // Client version + + oMsg.addU8(0); // First RSA byte must be 0x00 // 1 + + // Generete xtea key. + m_xteaKey[0] = 432324; + m_xteaKey[1] = 24324; + m_xteaKey[2] = 423432; + m_xteaKey[3] = 234324; + + // Add xtea key + oMsg.addU32(m_xteaKey[0]); // 5 + oMsg.addU32(m_xteaKey[1]); // 9 + oMsg.addU32(m_xteaKey[2]); // 13 + oMsg.addU32(m_xteaKey[3]); // 17 + + oMsg.addU8(0); // is gm set? + oMsg.addString(m_accountName); // Account Name // 20 + oMsg.addString(m_characterName); // Character Name // 22 + oMsg.addString(m_accountPassword); // Password // 24 + + oMsg.addU32(timestamp); // 28 + oMsg.addU8(unknown); // 29 + + // Packet data must have since byte 0, start, 128 bytes + oMsg.addPaddingBytes(128 - (29 + m_accountName.length() + m_characterName.length() + m_accountPassword.length())); + + // Encrypt msg with RSA + if(!Rsa::encrypt((char*)oMsg.getBuffer() + 6 + oMsg.getMessageSize() - 128, 128, OTSERV_PUBLIC_RSA)) + return; + + send(oMsg); + + m_xteaEncryptionEnabled = true; + + recv(); +} + +void ProtocolGame::onRecv(InputMessage& inputMessage) +{ + static bool firstRecv = true; + if(firstRecv) { + inputMessage.skipBytes(3); + uint32 timestamp = inputMessage.getU32(); + uint8 unknown = inputMessage.getU8(); + + m_checksumEnabled = true; + sendLoginPacket(timestamp, unknown); + + firstRecv = false; + } + else { + parseMessage(inputMessage); + } +} diff --git a/src/protocolgame.h b/src/protocolgame.h new file mode 100755 index 00000000..6946e682 --- /dev/null +++ b/src/protocolgame.h @@ -0,0 +1,137 @@ +#ifndef PROTOCOLGAME_H +#define PROTOCOLGAME_H + +#include +#include "player.h" + +class ProtocolGame; +typedef std::shared_ptr ProtocolGamePtr; + +class ProtocolGame : public Protocol +{ + +public: + ProtocolGame(); + ~ProtocolGame(); + +public: + void login(const std::string& accountName, const std::string& accountPassword, uint32 ip, uint16 port, const std::string& characterName); + + void onConnect(); + void onRecv(InputMessage& inputMessage); + +private: + void sendLoginPacket(uint32 timestamp, uint8 unknown); + + void parseMessage(InputMessage& msg); + + void parsePlayerLogin(InputMessage& msg); + void parseGMActions(InputMessage& msg); + void parseErrorMessage(InputMessage& msg); + void parseFYIMessage(InputMessage& msg); + void parseWaitList(InputMessage& msg); + void parsePing(InputMessage&); + void parseDeath(InputMessage&); + void parseCanReportBugs(InputMessage& msg); + void parseMapDescription(InputMessage& msg); + void parseMoveNorth(InputMessage& msg); + void parseMoveEast(InputMessage& msg); + void parseMoveSouth(InputMessage& msg); + void parseMoveWest(InputMessage& msg); + void parseUpdateTile(InputMessage& msg); + void parseTileAddThing(InputMessage& msg); + void parseTileTransformThing(InputMessage& msg); + void parseTileRemoveThing(InputMessage& msg); + void parseCreatureMove(InputMessage& msg); + void parseOpenContainer(InputMessage& msg); + void parseCloseContainer(InputMessage& msg); + void parseContainerAddItem(InputMessage& msg); + void parseContainerUpdateItem(InputMessage& msg); + void parseContainerRemoveItem(InputMessage& msg); + void parseAddInventoryItem(InputMessage& msg); + void parseRemoveInventoryItem(InputMessage& msg); + void parseOpenShopWindow(InputMessage& msg); + void parsePlayerCash(InputMessage& msg); + void parseCloseShopWindow(InputMessage&); + void parseWorldLight(InputMessage& msg); + void parseMagicEffect(InputMessage& msg); + void parseAnimatedText(InputMessage& msg); + void parseDistanceShot(InputMessage& msg); + void parseCreatureSquare(InputMessage& msg); + void parseCreatureHealth(InputMessage& msg); + void parseCreatureLight(InputMessage& msg); + void parseCreatureOutfit(InputMessage& msg); + void parseCreatureSpeed(InputMessage& msg); + void parseCreatureSkulls(InputMessage& msg); + void parseCreatureShields(InputMessage& msg); + void parseItemTextWindow(InputMessage& msg); + void parseHouseTextWindow(InputMessage& msg); + void parsePlayerStats(InputMessage& msg); + void parsePlayerSkills(InputMessage& msg); + void parsePlayerIcons(InputMessage& msg); + void parsePlayerCancelAttack(InputMessage& msg); + void parseCreatureSpeak(InputMessage& msg); + void parseChannelList(InputMessage& msg); + void parseOpenChannel(InputMessage& msg); + void parseOpenPrivatePlayerChat(InputMessage& msg); + void parseOpenRuleViolation(InputMessage& msg); + void parseRuleViolationAF(InputMessage& msg); + void parseRuleViolationB0(InputMessage& msg); + void parseRuleViolationB1(InputMessage& msg); + void parseCreatePrivateChannel(InputMessage& msg); + void parseClosePrivateChannel(InputMessage& msg); + void parseSafeTradeRequest(InputMessage& msg); + void parseSafeTradeClose(InputMessage&); + void parseTextMessage(InputMessage& msg); + void parseCancelWalk(InputMessage& msg); + void parseFloorChangeUp(InputMessage& msg); + void parseFloorChangeDown(InputMessage& msg); + void parseOutfitWindow(InputMessage& msg); + void parseVipState(InputMessage& msg); + void parseVipLogin(InputMessage& msg); + void parseVipLogout(InputMessage& msg); + void parseShowTutorial(InputMessage& msg); + void parseAddMarker(InputMessage& msg); + void parseQuestList(InputMessage& msg); + void parseQuestPartList(InputMessage& msg); + + void setMapDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height); + void setFloorDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height, int32 offset, int32* skipTiles); + void setTileDescription(InputMessage& msg, Position position); + void internalGetThing(InputMessage& msg); + void internalCreatureOutfit(InputMessage& msg); + void internalGetItem(InputMessage& msg, uint16 id); + + Position parsePosition(InputMessage& msg); + + std::string m_accountName, m_accountPassword, m_characterName; + + // TODO this is unknown + Player m_player; +}; + +// TODO: place it somewhere else +enum SpeakClasses { + SPEAK_SAY = 0x01, //normal talk + SPEAK_WHISPER = 0x02, //whispering - #w text + SPEAK_YELL = 0x03, //yelling - #y text + SPEAK_PRIVATE_PN = 0x04, //Player-to-NPC speaking(NPCs channel) + SPEAK_PRIVATE_NP = 0x05, //NPC-to-Player speaking + SPEAK_PRIVATE = 0x06, //Players speaking privately to players + SPEAK_CHANNEL_Y = 0x07, //Yellow message in chat + SPEAK_CHANNEL_W = 0x08, //White message in chat + SPEAK_RVR_CHANNEL = 0x09, //Reporting rule violation - Ctrl+R + SPEAK_RVR_ANSWER = 0x0A, //Answering report + SPEAK_RVR_CONTINUE = 0x0B, //Answering the answer of the report + SPEAK_BROADCAST = 0x0C, //Broadcast a message - #b + SPEAK_CHANNEL_R1 = 0x0D, //Talk red on chat - #c + SPEAK_PRIVATE_RED = 0x0E, //Red private - @name@ text + SPEAK_CHANNEL_O = 0x0F, //Talk orange on text + //SPEAK_ = 0x10, //? + SPEAK_CHANNEL_R2 = 0x11, //Talk red anonymously on chat - #d + //SPEAK_ = 0x12, //? + SPEAK_MONSTER_SAY = 0x13, //Talk orange + SPEAK_MONSTER_YELL = 0x14 //Yell orange +}; + +#endif // PROTOCOLGAME_H diff --git a/src/protocolgameparse.cpp b/src/protocolgameparse.cpp new file mode 100755 index 00000000..5befad03 --- /dev/null +++ b/src/protocolgameparse.cpp @@ -0,0 +1,895 @@ +#include "protocolgame.h" +#include "player.h" +#include + +void ProtocolGame::parseMessage(InputMessage& msg) +{ + while(!msg.end()) { + uint8 opt = msg.getU8(); + + dump << "Protocol opt: " << std::hex << (int)opt; + + switch(opt) { + case 0x0A: + parsePlayerLogin(msg); + break; + case 0x0B: + parseGMActions(msg); + break; + case 0x14: + parseErrorMessage(msg); + break; + case 0x15: + parseFYIMessage(msg); + break; + case 0x16: + parseWaitList(msg); + break; + case 0x1E: + parsePing(msg); + break; + case 0x28: + parseDeath(msg); + break; + case 0x32: + parseCanReportBugs(msg); + break; + case 0x64: + parseMapDescription(msg); + break; + case 0x65: + parseMoveNorth(msg); + break; + case 0x66: + parseMoveEast(msg); + break; + case 0x67: + parseMoveSouth(msg); + break; + case 0x68: + parseMoveWest(msg); + break; + case 0x69: + parseUpdateTile(msg); + break; + case 0x6A: + parseTileAddThing(msg); + break; + case 0x6B: + parseTileTransformThing(msg); + break; + case 0x6C: + parseTileRemoveThing(msg); + break; + case 0x6D: + parseCreatureMove(msg); + break; + case 0x6E: + parseOpenContainer(msg); + break; + case 0x6F: + parseCloseContainer(msg); + break; + case 0x70: + parseContainerAddItem(msg); + break; + case 0x71: + parseContainerUpdateItem(msg); + break; + case 0x72: + parseContainerRemoveItem(msg); + break; + case 0x78: + parseAddInventoryItem(msg); + break; + case 0x79: + parseRemoveInventoryItem(msg); + break; + case 0x7A: + parseOpenShopWindow(msg); + break; + case 0x7B: + parsePlayerCash(msg); + break; + case 0x7C: + parseCloseShopWindow(msg); + break; + case 0x7D: + parseSafeTradeRequest(msg); + break; + case 0x7E: + parseSafeTradeRequest(msg); + break; + case 0x7F: + parseSafeTradeClose(msg); + break; + case 0x82: + parseWorldLight(msg); + break; + case 0x83: + parseMagicEffect(msg); + break; + case 0x84: + parseAnimatedText(msg); + break; + case 0x85: + parseDistanceShot(msg); + break; + case 0x86: + parseCreatureSquare(msg); + break; + case 0x8C: + parseCreatureHealth(msg); + break; + case 0x8D: + parseCreatureLight(msg); + break; + case 0x8E: + parseCreatureOutfit(msg); + break; + case 0x8F: + parseCreatureSpeed(msg); + break; + case 0x90: + parseCreatureSkulls(msg); + break; + case 0x91: + parseCreatureShields(msg); + break; + case 0x96: + parseItemTextWindow(msg); + break; + case 0x97: + parseHouseTextWindow(msg); + break; + case 0xA0: + parsePlayerStats(msg); + break; + case 0xA1: + parsePlayerSkills(msg); + break; + case 0xA2: + parsePlayerIcons(msg); + break; + case 0xA3: + parsePlayerCancelAttack(msg); + break; + case 0xAA: + parseCreatureSpeak(msg); + break; + case 0xAB: + parseChannelList(msg); + break; + case 0xAC: + parseOpenChannel(msg); + break; + case 0xAD: + parseOpenPrivatePlayerChat(msg); + break; + case 0xAE: + parseOpenRuleViolation(msg); + break; + case 0xAF: + parseRuleViolationAF(msg); + break; + case 0xB0: + parseRuleViolationB0(msg); + break; + case 0xB1: + parseRuleViolationB1(msg); + break; + case 0xB2: + parseCreatePrivateChannel(msg); + break; + case 0xB3: + parseClosePrivateChannel(msg); + break; + case 0xB4: + parseTextMessage(msg); + break; + case 0xB5: + parseCancelWalk(msg); + break; + case 0xBE: + parseFloorChangeUp(msg); + break; + case 0xBF: + parseFloorChangeDown(msg); + break; + case 0xC8: + parseOutfitWindow(msg); + break; + case 0xD2: + parseVipState(msg); + break; + case 0xD3: + parseVipLogin(msg); + break; + case 0xD4: + parseVipLogout(msg); + break; + case 0xDC: + parseShowTutorial(msg); + break; + case 0xDD: + parseAddMarker(msg); + break; + case 0xF0: + parseQuestList(msg); + break; + case 0xF1: + parseQuestPartList(msg); + break; + default: + logDebug("UNKNOWN PACKET BYTE.", opt); + //skipPacket = true; + break; + } + } +} + +void ProtocolGame::parsePlayerLogin(InputMessage& msg) +{ + msg.getU32(); // player id + msg.getU16(); // drawing speed. + msg.getU8(); // can report bugs +} + +void ProtocolGame::parseGMActions(InputMessage& msg) +{ + for(uint8 i = 0; i < 28; ++i) + msg.getU8(); +} + +void ProtocolGame::parseErrorMessage(InputMessage& msg) +{ + msg.getString(); // message +} + +void ProtocolGame::parseFYIMessage(InputMessage& msg) +{ + msg.getString(); // message +} + +void ProtocolGame::parseWaitList(InputMessage& msg) +{ + msg.getString(); // message + msg.getU8();// + 1 // time +} + +void ProtocolGame::parsePing(InputMessage&) +{ + //mSendmsg.reset(); + //mSendmsg.addByte(0x1E); + //sendMessage(mSendMsg); +} + +void ProtocolGame::parseDeath(InputMessage&) +{ + +} + +void ProtocolGame::parseCanReportBugs(InputMessage& msg) +{ + msg.getU8(); // report bugs +} + +void ProtocolGame::parseMapDescription(InputMessage& msg) +{ + m_player.setPosition(parsePosition(msg)); + setMapDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() - 6, m_player.getPosition().getZ(), 18, 14); +} + +void ProtocolGame::parseMoveNorth(InputMessage& msg) +{ + m_player.getPosition().setY(m_player.getPosition().getY() - 1); + setMapDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() - 6, m_player.getPosition().getZ(), 18, 1); +} + +void ProtocolGame::parseMoveEast(InputMessage& msg) +{ + m_player.getPosition().setX(m_player.getPosition().getX() + 1); + setMapDescription(msg, m_player.getPosition().getX() + 9, m_player.getPosition().getY() - 6, m_player.getPosition().getZ(), 1, 14); +} + +void ProtocolGame::parseMoveSouth(InputMessage& msg) +{ + m_player.getPosition().setY(m_player.getPosition().getY() + 1); + setMapDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() + 7, m_player.getPosition().getZ(), 18, 1); +} + +void ProtocolGame::parseMoveWest(InputMessage& msg) +{ + m_player.getPosition().setX(m_player.getPosition().getX() - 1); + setMapDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() - 6, m_player.getPosition().getZ(), 1, 14); +} + +void ProtocolGame::parseUpdateTile(InputMessage& msg) +{ + Position tilePos = parsePosition(msg); + uint16 thingId = msg.getU16(true); + if(thingId == 0xFF01) { + msg.getU16(); + } + else { + setTileDescription(msg, tilePos); + msg.getU16(); + } +} + +void ProtocolGame::parseTileAddThing(InputMessage& msg) +{ + parsePosition(msg); // tilePos + msg.getU8(); // stackPos + internalGetThing(msg); +} + +void ProtocolGame::parseTileTransformThing(InputMessage& msg) +{ + parsePosition(msg); // tilePos + msg.getU8(); // stackPos + + uint16 thingId = msg.getU16(); + if(thingId == 0x0061 || thingId == 0x0062 || thingId == 0x0063) { + msg.getU32(); // creatureId + msg.getU8(); // direction + } + else { + internalGetItem(msg, thingId); + } +} + +void ProtocolGame::parseTileRemoveThing(InputMessage& msg) +{ + parsePosition(msg); // tilePos + msg.getU8(); // stackPos +} + +void ProtocolGame::parseCreatureMove(InputMessage& msg) +{ + parsePosition(msg); // oldPos + msg.getU8(); // oldStackPos + parsePosition(msg); // newPos +} + +void ProtocolGame::parseOpenContainer(InputMessage& msg) +{ + msg.getU8(); // cid + msg.getU16(); // itemid + msg.getString(); // name + msg.getU8(); // capacity + msg.getU8(); // hasParent + uint8 size = msg.getU8(); // size + + for(uint32 i = 0; i < size; i++) + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseCloseContainer(InputMessage& msg) +{ + msg.getU8(); // cid +} + +void ProtocolGame::parseContainerAddItem(InputMessage& msg) +{ + msg.getU8(); // cid + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseContainerUpdateItem(InputMessage& msg) +{ + msg.getU8(); // cid + msg.getU8(); // slot + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseContainerRemoveItem(InputMessage& msg) +{ + msg.getU8(); // cid + msg.getU8(); // slot +} + +void ProtocolGame::parseAddInventoryItem(InputMessage& msg) +{ + msg.getU8(); // slot + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseRemoveInventoryItem(InputMessage& msg) +{ + msg.getU8(); // slot +} + +void ProtocolGame::parseOpenShopWindow(InputMessage& msg) +{ + uint8 listCount = msg.getU8(); + for(uint8 i = 0; i < listCount; ++i) { + msg.getU16(); // item id + msg.getU8(); // runecharges + msg.getString(); // item name + msg.getU32(); // weight + msg.getU32(); // buy price + msg.getU32(); // sell price + } +} + +void ProtocolGame::parsePlayerCash(InputMessage& msg) +{ + msg.getU32(); + uint8 size = msg.getU8(); + + for(int i = 0; i < size; i++) { + msg.getU16(); // itemid + msg.getU8(); // runecharges + } +} + +void ProtocolGame::parseCloseShopWindow(InputMessage&) +{ +} + +void ProtocolGame::parseSafeTradeRequest(InputMessage& msg) +{ + msg.getString(); // name + uint8 count = msg.getU8(); + + for(uint8 i = 0; i < count; i++) + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseSafeTradeClose(InputMessage&) +{ +} + +void ProtocolGame::parseWorldLight(InputMessage& msg) +{ + msg.getU8(); // level + msg.getU8(); // color +} + +void ProtocolGame::parseMagicEffect(InputMessage& msg) +{ + parsePosition(msg); // effect pos + msg.getU8(); // effect +} + +void ProtocolGame::parseAnimatedText(InputMessage& msg) +{ + parsePosition(msg); // textPos + msg.getU8(); // color + msg.getString(); // text +} + +void ProtocolGame::parseDistanceShot(InputMessage& msg) +{ + parsePosition(msg); // fromPos + parsePosition(msg); // toPos + msg.getU8(); // effect +} + +void ProtocolGame::parseCreatureSquare(InputMessage& msg) +{ + msg.getU32(); // creatureId + msg.getU8(); // color +} + +void ProtocolGame::parseCreatureHealth(InputMessage& msg) +{ + msg.getU32(); // creatureId + msg.getU8(); // percent +} + +void ProtocolGame::parseCreatureLight(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU8(); // level + msg.getU8(); // color +} + +void ProtocolGame::parseCreatureOutfit(InputMessage& msg) +{ + msg.getU32(); // creature id + internalCreatureOutfit(msg); +} + +void ProtocolGame::parseCreatureSpeed(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU16(); // speed +} + +void ProtocolGame::parseCreatureSkulls(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU8(); // skull +} + +void ProtocolGame::parseCreatureShields(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU8(); // shield +} + +void ProtocolGame::parseItemTextWindow(InputMessage& msg) +{ + msg.getU32(); // windowId + msg.getU16(); // itemid + msg.getU16(); // max length + msg.getString(); // text + msg.getString(); // writter + msg.getString(); // date +} + +void ProtocolGame::parseHouseTextWindow(InputMessage& msg) +{ + msg.getU8(); // unknown + msg.getU32(); // windowId + msg.getString(); // text +} + +void ProtocolGame::parsePlayerStats(InputMessage& msg) +{ + msg.getU16(); // health + msg.getU16(); // max health + msg.getU32(); // free capacity + msg.getU32(); // experience + msg.getU16(); // level + msg.getU8(); // level percent + msg.getU16(); // mana + msg.getU16(); // max mana + msg.getU8(); // magic level + msg.getU8(); // magic level percent + msg.getU8(); // soul + msg.getU16(); // stamina +} + +void ProtocolGame::parsePlayerSkills(InputMessage& msg) +{ + msg.getU8(); // fist skill + msg.getU8(); // fist percent + + msg.getU8(); // club skill + msg.getU8(); // club percent + + msg.getU8(); // sword skill + msg.getU8(); // sword percent + + msg.getU8(); // axe skill + msg.getU8(); // axe percent + + msg.getU8(); // distance skill + msg.getU8(); // distance percent + + msg.getU8(); // shielding skill + msg.getU8(); // shielding percent + + msg.getU8(); // fishing skill + msg.getU8(); // fishing percent +} + +void ProtocolGame::parsePlayerIcons(InputMessage& msg) +{ + msg.getU16(); // icons +} + +void ProtocolGame::parsePlayerCancelAttack(InputMessage& msg) +{ + msg.getU32(); +} + +void ProtocolGame::parseCreatureSpeak(InputMessage& msg) +{ + msg.getU32(); // unkSpeak + msg.getString(); // name + msg.getU16(); // level + uint8 type = msg.getU8(); + + switch(type) { + case SPEAK_SAY: + case SPEAK_WHISPER: + case SPEAK_YELL: + case SPEAK_MONSTER_SAY: + case SPEAK_MONSTER_YELL: + case SPEAK_PRIVATE_NP: + parsePosition(msg); // creaturePos + break; + case SPEAK_CHANNEL_R1: + case SPEAK_CHANNEL_R2: + case SPEAK_CHANNEL_O: + case SPEAK_CHANNEL_Y: + case SPEAK_CHANNEL_W: + msg.getU16(); // channelId + break; + case SPEAK_RVR_CHANNEL: + msg.getU32(); // time + break; + default: + //qDebug() << "Unknown speak type. opt: 0xAA, type: " << type; + break; + } + + msg.getString(); // message +} + +void ProtocolGame::parseChannelList(InputMessage& msg) +{ + uint8 count = msg.getU8(); + for(uint8 i = 0; i < count; i++) { + msg.getU16(); + msg.getString(); + } +} + +void ProtocolGame::parseOpenChannel(InputMessage& msg) +{ + msg.getU16(); // channelId + msg.getString(); // name +} + +void ProtocolGame::parseOpenPrivatePlayerChat(InputMessage& msg) +{ + msg.getString(); // name +} + +void ProtocolGame::parseOpenRuleViolation(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseRuleViolationAF(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseRuleViolationB0(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseRuleViolationB1(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseCreatePrivateChannel(InputMessage& msg) +{ + msg.getU16(); // channel id + msg.getString(); // channel name +} + +void ProtocolGame::parseClosePrivateChannel(InputMessage& msg) +{ + msg.getU16(); // channel id +} + +void ProtocolGame::parseTextMessage(InputMessage& msg) +{ + msg.getU8(); // messageType + std::string message = msg.getString(); + logDebug(message); +} + +void ProtocolGame::parseCancelWalk(InputMessage& msg) +{ + msg.getU8(); // direction +} + +void ProtocolGame::parseFloorChangeUp(InputMessage& msg) +{ + m_player.getPosition().setZ(m_player.getPosition().getZ() - 1); + + int32 skip = 0; + if(m_player.getPosition().getZ() == 7) + for(int32 i = 5; i >= 0; i--) + setFloorDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() - 6, i, 18, 14, 8 - i, &skip); + else if(m_player.getPosition().getZ() > 7) + setFloorDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() - 6, m_player.getPosition().getZ() - 2, 18, 14, 3, &skip); + + m_player.getPosition().setX(m_player.getPosition().getX() + 1); + m_player.getPosition().setY(m_player.getPosition().getY() + 1); +} + +void ProtocolGame::parseFloorChangeDown(InputMessage& msg) +{ + m_player.getPosition().setZ(m_player.getPosition().getZ() + 1); + + int32 skip = 0; + if(m_player.getPosition().getZ() == 8) { + int32 j, i; + for(i = m_player.getPosition().getZ(), j = -1; i < (int32)m_player.getPosition().getZ() + 3; ++i, --j) + setFloorDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() - 6, i, 18, 14, j, &skip); + } + else if(m_player.getPosition().getZ() > 8 && m_player.getPosition().getZ() < 14) + setFloorDescription(msg, m_player.getPosition().getX() - 8, m_player.getPosition().getY() - 6, m_player.getPosition().getZ() + 2, 18, 14, -3, &skip); + + m_player.getPosition().setX(m_player.getPosition().getX() - 1); + m_player.getPosition().setY(m_player.getPosition().getY() - 1); +} + +void ProtocolGame::parseOutfitWindow(InputMessage& msg) +{ + internalCreatureOutfit(msg); + uint8 outfitCount = msg.getU8(); + + for(int i = 0; i < outfitCount; i++) { + msg.getU16(); // outfit id + msg.getString(); // outfit name + msg.getU8(); // addons + } +} + +void ProtocolGame::parseVipState(InputMessage& msg) +{ + msg.getU32(); // player id + msg.getString(); // player name + msg.getU8(); // online +} + +void ProtocolGame::parseVipLogin(InputMessage& msg) +{ + msg.getU32(); // player id +} + +void ProtocolGame::parseVipLogout(InputMessage& msg) +{ + msg.getU32(); // player id +} + +void ProtocolGame::parseShowTutorial(InputMessage& msg) +{ + msg.getU8(); // tutorial id +} + +void ProtocolGame::parseAddMarker(InputMessage& msg) +{ + parsePosition(msg); // position + msg.getU8(); // icon + msg.getString(); // message +} + +void ProtocolGame::parseQuestList(InputMessage& msg) +{ + uint16 questsCount = msg.getU16(); + for(uint16 i = 0; i < questsCount; i++) { + msg.getU16(); // quest id + msg.getString(); // quest name + msg.getU8(); // quest state + } +} + +void ProtocolGame::parseQuestPartList(InputMessage& msg) +{ + msg.getU16(); // quest id + uint8 missionCount = msg.getU8(); + for(uint8 i = 0; i < missionCount; i++) { + msg.getString(); // quest name + msg.getString(); // quest description + } +} + +void ProtocolGame::setMapDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height) +{ + int startz, endz, zstep, skip = 0; + + if(z > 7) { + startz = z - 2; + endz = (15 < z+2) ? 15 : z+2; + zstep = 1; + } + else { + startz = 7; + endz = 0; + zstep = -1; + } + + for(int nz = startz; nz != endz + zstep; nz += zstep) + setFloorDescription(msg, x, y, nz, width, height, z - nz, &skip); +} + +void ProtocolGame::setFloorDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height, int32 offset, int32* skipTiles) +{ + int32 skip = *skipTiles; + + for(int32 nx = 0; nx < width; nx++) { + for(int32 ny = 0; ny < height; ny++) { + if(skip == 0) { + uint16 tileOpt = msg.getU16(true); + if(tileOpt >= 0xFF00) + skip = (msg.getU16() & 0xFF); + else { + Position pos(x + nx + offset, y + ny + offset, z); + setTileDescription(msg, pos); + skip = (msg.getU16() & 0xFF); + } + } + else + skip--; + } + } + *skipTiles = skip; +} + +void ProtocolGame::setTileDescription(InputMessage& msg, Position position) +{ + int n = 0; + while(1){ + n++; + uint16 inspectTileId = msg.getU16(true); + if(inspectTileId >= 0xFF00) + return; + else { + if(n > 10) { + logDebug("[ProtocolGame::setTileDescription] Too many things!."); + return; + } + internalGetThing(msg); + } + } +} + +void ProtocolGame::internalGetThing(InputMessage& msg) +{ + uint16 thingId = msg.getU16(); + if(thingId == 0x0061 || thingId == 0x0062) { // add new creature + if(thingId == 0x0062) { //creature is known + msg.getU32(); // creature id + } + else if(thingId == 0x0061) { //creature is not known + msg.getU32(); // remove id + msg.getU32(); // creature id + msg.getString(); // creature name + } + + msg.getU8(); // hp + msg.getU8(); // direction + internalCreatureOutfit(msg); + msg.getU8(); // level + msg.getU8(); // color + msg.getU16(); // speed + msg.getU8(); // skull + msg.getU8(); // shield + + if(thingId == 0x0061) // emblem is sent only in packet type 0x61 + msg.getU8(); + msg.getU8(); // impassable + + } + else if(thingId == 0x0063) { // creature turn + msg.getU32(); // creature id + msg.getU8(); // direction + } + else // item + internalGetItem(msg, thingId); +} + +void ProtocolGame::internalCreatureOutfit(InputMessage& msg) +{ + uint16 lookType = msg.getU16(); // looktype + if(lookType != 0) { + msg.getU8(); // lookhead + msg.getU8(); // lookbody + msg.getU8(); // looklegs + msg.getU8(); // lookfeet + msg.getU8(); // lookaddons + } + else { + msg.getU16(); // looktype + } +} + +void ProtocolGame::internalGetItem(InputMessage& msg, uint16 id) +{ + if(id == 0xFFFF) + id = msg.getU16(); + //if(Data::instance()->hasCountOrSubType(id)) + //msg.getU8(); +} + +Position ProtocolGame::parsePosition(InputMessage& msg) +{ + uint16 x = msg.getU16(); + uint16 y = msg.getU16(); + uint8 z = msg.getU8(); + + return Position(x, y, z); +} diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index 8b320f04..9a55dd6b 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -4,6 +4,9 @@ #include