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
This commit is contained in:
Eduardo Bart 2012-05-11 15:02:57 -03:00
parent e1fad92110
commit 5a47e9d8a9
19 changed files with 329 additions and 192 deletions

1
BUGS
View File

@ -4,6 +4,7 @@ modules recursivity makes client crash, it should generate a warning
== P1 BUGS (affects game play) == P1 BUGS (affects game play)
sometimes minimap desync Z pos sometimes minimap desync Z pos
follow and autowalk doesn't cancel when walking via hotkeys 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: when walking on a tile with too many creatures, the following errors occurrs:
ERROR: [ProtocolGame::parseCreatureHealth] could not get greature ERROR: [ProtocolGame::parseCreatureHealth] could not get greature

1
TODO
View File

@ -44,6 +44,7 @@ multisample option in map view
move rendering of creatures names, skulls, etc to UI and scripts move rendering of creatures names, skulls, etc to UI and scripts
clean sprites cache periodically clean sprites cache periodically
handle corrupt errors in dat/spr handle corrupt errors in dat/spr
throw exceptions when fail to read a file
* framework * framework
rework Settings/g_configs rework Settings/g_configs

View File

@ -29,8 +29,28 @@ InputMessage::InputMessage()
void InputMessage::reset() void InputMessage::reset()
{ {
m_readPos = 0; m_messageSize = 0;
m_messageSize = 2; 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) uint8 InputMessage::getU8(bool peek)
@ -88,7 +108,7 @@ std::string InputMessage::getString()
bool InputMessage::canRead(int bytes) 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 false;
return true; return true;
} }

View File

@ -31,34 +31,43 @@ class InputMessage
public: public:
enum { enum {
BUFFER_MAXSIZE = 16384, BUFFER_MAXSIZE = 16384,
HEADER_POS = 0, MAX_HEADER_SIZE = 8,
HEADER_LENGTH = 2,
CHECKSUM_POS = 2,
CHECKSUM_LENGTH = 4,
DATA_POS = 6,
UNENCRYPTED_DATA_POS = 8
}; };
InputMessage(); InputMessage();
void reset(); 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); uint8 getU8(bool peek = false);
uint16 getU16(bool peek = false); uint16 getU16(bool peek = false);
uint32 getU32(bool peek = false); uint32 getU32(bool peek = false);
uint64 getU64(bool peek = false); uint64 getU64(bool peek = false);
std::string getString(); std::string getString();
void skipBytes(uint16 bytes) { m_readPos += bytes; } uint8* getReadBuffer() { return m_buffer + m_readPos; }
uint8* getBuffer() { return m_buffer; } 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; } uint16 getMessageSize() { return m_messageSize; }
void setMessageSize(uint16 messageSize) { m_messageSize = messageSize; } int getReadSize() { return m_readPos - m_headerPos; }
bool eof() { return m_readPos >= m_messageSize; } int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); }
bool eof() { return (m_readPos - m_headerPos) >= m_messageSize; }
private: private:
bool canRead(int bytes); bool canRead(int bytes);
void checkRead(int bytes); void checkRead(int bytes);
uint16 m_headerPos;
uint16 m_readPos; uint16 m_readPos;
uint16 m_messageSize; uint16 m_messageSize;
uint8 m_buffer[BUFFER_MAXSIZE]; uint8 m_buffer[BUFFER_MAXSIZE];

View File

@ -29,7 +29,8 @@ OutputMessage::OutputMessage()
void OutputMessage::reset() void OutputMessage::reset()
{ {
m_writePos = DATA_POS; m_writePos = MAX_HEADER_SIZE;
m_headerPos = MAX_HEADER_SIZE;
m_messageSize = 0; m_messageSize = 0;
} }
@ -91,6 +92,23 @@ void OutputMessage::addPaddingBytes(int bytes, uint8 byte)
m_messageSize += bytes; 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) bool OutputMessage::canWrite(int bytes)
{ {
if(m_writePos + bytes > BUFFER_MAXSIZE) if(m_writePos + bytes > BUFFER_MAXSIZE)

View File

@ -34,11 +34,7 @@ class OutputMessage : public LuaObject
public: public:
enum { enum {
BUFFER_MAXSIZE = 1024, BUFFER_MAXSIZE = 1024,
HEADER_POS = 0, MAX_HEADER_SIZE = 8
HEADER_LENGTH = 2,
CHECKSUM_POS = 2,
CHECKSUM_LENGTH = 4,
DATA_POS = 6
}; };
OutputMessage(); OutputMessage();
@ -53,15 +49,19 @@ public:
void addString(const std::string& value); void addString(const std::string& value);
void addPaddingBytes(int bytes, uint8 byte = 0); 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; } uint16 getMessageSize() { return m_messageSize; }
void setMessageSize(uint16 messageSize) { m_messageSize = messageSize; }
void setWritePos(uint16 writePos) { m_writePos = writePos; } void writeChecksum();
void writeMessageSize();
private: private:
bool canWrite(int bytes); bool canWrite(int bytes);
void checkWrite(int bytes); void checkWrite(int bytes);
uint16 m_headerPos;
uint16 m_writePos; uint16 m_writePos;
uint16 m_messageSize; uint16 m_messageSize;
uint8 m_buffer[BUFFER_MAXSIZE]; uint8 m_buffer[BUFFER_MAXSIZE];

View File

@ -69,40 +69,44 @@ void Protocol::send(OutputMessage& outputMessage)
if(m_xteaEncryptionEnabled) if(m_xteaEncryptionEnabled)
xteaEncrypt(outputMessage); xteaEncrypt(outputMessage);
// set checksum // write checksum
uint32 checksum = getAdlerChecksum(outputMessage.getBuffer() + OutputMessage::DATA_POS, outputMessage.getMessageSize()); if(m_checksumEnabled)
outputMessage.setWritePos(OutputMessage::CHECKSUM_POS); outputMessage.writeChecksum();
outputMessage.addU32(checksum);
// set size // wirte message size
uint16 messageSize = outputMessage.getMessageSize(); outputMessage.writeMessageSize();
outputMessage.setWritePos(OutputMessage::HEADER_POS);
outputMessage.addU16(messageSize);
// send // send
if(m_connection) if(m_connection)
m_connection->write(outputMessage.getBuffer(), outputMessage.getMessageSize()); m_connection->write(outputMessage.getHeaderBuffer(), outputMessage.getMessageSize());
} }
void Protocol::recv() void Protocol::recv()
{ {
m_inputMessage.reset(); 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) 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) void Protocol::internalRecvHeader(uint8* buffer, uint16 size)
{ {
memcpy(m_inputMessage.getBuffer() + InputMessage::HEADER_POS, buffer, size);
// read message size // read message size
uint16 dataSize = m_inputMessage.getU16(); m_inputMessage.fillBuffer(buffer, size);
m_inputMessage.setMessageSize(dataSize); uint16 remainingSize = m_inputMessage.readSize();
// schedule read for message data // read remaining message data
if(m_connection) 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) void Protocol::internalRecvData(uint8* buffer, uint16 size)
@ -113,19 +117,16 @@ void Protocol::internalRecvData(uint8* buffer, uint16 size)
return; return;
} }
memcpy(m_inputMessage.getBuffer() + InputMessage::CHECKSUM_POS, buffer, size); m_inputMessage.fillBuffer(buffer, size);
if(m_checksumEnabled) { if(m_checksumEnabled && !m_inputMessage.readChecksum()) {
uint32 checksum = getAdlerChecksum(m_inputMessage.getBuffer() + InputMessage::DATA_POS, m_inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH); logTraceError("got a network message with invalid checksum");
if(m_inputMessage.getU32() != checksum) { return;
logTraceError("got a network message with invalid checksum");
return;
}
} }
if(m_xteaEncryptionEnabled) { if(m_xteaEncryptionEnabled && !xteaDecrypt(m_inputMessage)) {
if(!xteaDecrypt(m_inputMessage)) logTraceError("failed to decrypt message");
return; return;
} }
onRecv(m_inputMessage); onRecv(m_inputMessage);
@ -143,16 +144,16 @@ void Protocol::generateXteaKey()
bool Protocol::xteaDecrypt(InputMessage& inputMessage) bool Protocol::xteaDecrypt(InputMessage& inputMessage)
{ {
uint16 messageSize = inputMessage.getMessageSize() - InputMessage::CHECKSUM_LENGTH; uint16 encryptedSize = inputMessage.getUnreadSize();
if(messageSize % 8 != 0) { if(encryptedSize % 8 != 0) {
logTraceError("invalid encrypted network message"); logTraceError("invalid encrypted network message");
return false; return false;
} }
uint32 *buffer = (uint32*)(inputMessage.getBuffer() + InputMessage::DATA_POS); uint32 *buffer = (uint32*)(inputMessage.getReadBuffer());
int readPos = 0; int readPos = 0;
while(readPos < messageSize/4) { while(readPos < encryptedSize/4) {
uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1]; uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
uint32 delta = 0x61C88647; uint32 delta = 0x61C88647;
uint32 sum = 0xC6EF3720; uint32 sum = 0xC6EF3720;
@ -166,37 +167,32 @@ bool Protocol::xteaDecrypt(InputMessage& inputMessage)
readPos = readPos + 2; readPos = readPos + 2;
} }
int tmp = inputMessage.getU16(); uint16 decryptedSize = inputMessage.getU16() + 2;
if(tmp > inputMessage.getMessageSize() - 4) { int sizeDelta = decryptedSize - encryptedSize;
if(sizeDelta > 0 || -sizeDelta > encryptedSize) {
logTraceError("invalid decrypted a network message"); logTraceError("invalid decrypted a network message");
return false; return false;
} }
inputMessage.setMessageSize(tmp + InputMessage::UNENCRYPTED_DATA_POS); inputMessage.setMessageSize(inputMessage.getMessageSize() + sizeDelta);
return true; return true;
} }
void Protocol::xteaEncrypt(OutputMessage& outputMessage) void Protocol::xteaEncrypt(OutputMessage& outputMessage)
{ {
uint16 messageLength = outputMessage.getMessageSize(); outputMessage.writeMessageSize();
uint16 encryptedSize = 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);
//add bytes until reach 8 multiple //add bytes until reach 8 multiple
if((messageLength % 8) != 0) { if((encryptedSize % 8) != 0) {
uint16 n = 8 - (messageLength % 8); uint16 n = 8 - (encryptedSize % 8);
outputMessage.addPaddingBytes(n); outputMessage.addPaddingBytes(n);
messageLength += n; encryptedSize += n;
} }
int readPos = 0; int readPos = 0;
uint32 *buffer = (uint32*)(outputMessage.getBuffer() + OutputMessage::DATA_POS); uint32 *buffer = (uint32*)(outputMessage.getDataBuffer() - 2);
while(readPos < messageLength / 4) { while(readPos < encryptedSize / 4) {
uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1]; uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
uint32 delta = 0x61C88647; uint32 delta = 0x61C88647;
uint32 sum = 0; uint32 sum = 0;
@ -210,21 +206,3 @@ void Protocol::xteaEncrypt(OutputMessage& outputMessage)
readPos = readPos + 2; 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;
}

View File

@ -64,7 +64,6 @@ protected:
private: private:
bool xteaDecrypt(InputMessage& inputMessage); bool xteaDecrypt(InputMessage& inputMessage);
void xteaEncrypt(OutputMessage& outputMessage); void xteaEncrypt(OutputMessage& outputMessage);
uint32 getAdlerChecksum(uint8* buffer, uint16 size);
bool m_checksumEnabled; bool m_checksumEnabled;
bool m_xteaEncryptionEnabled; bool m_xteaEncryptionEnabled;

View File

@ -326,6 +326,22 @@ inline float randomRange<float>(float min, float max) {
return min + (max - min)*dis(gen); 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 // shortcut for Fw::dump

View File

@ -269,16 +269,22 @@ CreaturePtr Map::getCreatureById(uint32 id)
LocalPlayerPtr localPlayer = g_game.getLocalPlayer(); LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
if(localPlayer && localPlayer->getId() == id) if(localPlayer && localPlayer->getId() == id)
return localPlayer; 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) void Map::removeCreatureById(uint32 id)
{ {
if(id == 0) if(id == 0)
return; return;
if(CreaturePtr creature = m_knownCreatures[id])
creature->setRemoved(true); auto it = m_knownCreatures.find(id);
m_knownCreatures.erase(id); if(it != m_knownCreatures.end())
it->second->setRemoved(true);
m_knownCreatures.erase(it);
} }
void Map::setCentralPosition(const Position& centralPosition) 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 // remove creatures from tiles that we are not aware anymore
for(const auto& pair : m_knownCreatures) { for(const auto& pair : m_knownCreatures) {
const CreaturePtr& creature = pair.second; const CreaturePtr& creature = pair.second;
if(creature) { if(!isAwareOfPosition(creature->getPosition()))
if(!isAwareOfPosition(creature->getPosition())) { removeThing(creature);
removeThing(creature);
}
}
else
logTraceError("invalid creature");
} }
// this fixes local player position when the local player is removed from the map, // this fixes local player position when the local player is removed from the map,

View File

@ -24,36 +24,35 @@
#include "spritemanager.h" #include "spritemanager.h"
#include "thing.h" #include "thing.h"
#include <framework/core/resourcemanager.h> #include <framework/core/resourcemanager.h>
#include <framework/core/filestream.h>
ThingsType g_thingsType; ThingsType g_thingsType;
ThingType ThingsType::m_emptyThingType; ThingType ThingsType::m_emptyThingType;
bool ThingsType::load(const std::string& file) bool ThingsType::load(const std::string& file)
{ {
try { FileStreamPtr fin = g_resources.openFile(file);
std::stringstream fin; if(!fin) {
g_resources.loadFile(file, fin); logError("unable to open dat file '", file, "'");
m_signature = Fw::getU32(fin);
int numThings[LastCategory];
for(int i = 0; i < LastCategory; ++i)
numThings[i] = Fw::getU16(fin);
numThings[Item] -= 99;
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;
} catch(Exception& e) {
logError("Failed to load dat from '", file, "': ", e.what());
return false; return false;
} }
m_signature = fin->getU32();
int numThings[LastCategory];
for(int i = 0; i < LastCategory; ++i)
numThings[i] = fin->getU16();
numThings[Item] -= 99;
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() void ThingsType::unload()
@ -62,37 +61,39 @@ void ThingsType::unload()
m_things[i].clear(); 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) { while(true) {
int property = Fw::getU8(fin); int property = fin->getU8();
if(property == ThingType::LastPropertyValue) if(property == ThingType::LastPropertyValue)
break; break;
thingType.m_properties[property] = true; thingType.m_properties[property] = true;
if(property == ThingType::IsGround) 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) 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) { else if(property == ThingType::HasLight) {
thingType.m_parameters[ThingType::LightLevel] = Fw::getU16(fin); thingType.m_parameters[ThingType::LightLevel] = fin->getU16();
thingType.m_parameters[ThingType::LightColor] = Fw::getU16(fin); thingType.m_parameters[ThingType::LightColor] = fin->getU16();
} }
else if(property == ThingType::HasDisplacement) { else if(property == ThingType::HasDisplacement) {
thingType.m_parameters[ThingType::DisplacementX] = Fw::getU16(fin); thingType.m_parameters[ThingType::DisplacementX] = fin->getU16();
thingType.m_parameters[ThingType::DisplacementY] = Fw::getU16(fin); thingType.m_parameters[ThingType::DisplacementY] = fin->getU16();
} }
else if(property == ThingType::HasElevation) 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) 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) 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) 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; int totalSprites = 1;
@ -102,7 +103,7 @@ void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType)
continue; continue;
} }
thingType.m_dimensions[i] = Fw::getU8(fin); thingType.m_dimensions[i] = fin->getU8();
if(i != ThingType::ExactSize) if(i != ThingType::ExactSize)
totalSprites *= thingType.m_dimensions[i]; totalSprites *= thingType.m_dimensions[i];
@ -111,7 +112,7 @@ void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType)
thingType.m_spritesIndex.resize(totalSprites); thingType.m_spritesIndex.resize(totalSprites);
thingType.m_sprites.resize(totalSprites); thingType.m_sprites.resize(totalSprites);
for(int i = 0; i < totalSprites; i++) 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) ThingType *ThingsType::getThingType(uint16 id, Categories category)

View File

@ -24,6 +24,7 @@
#define DATMANAGER_H #define DATMANAGER_H
#include <framework/global.h> #include <framework/global.h>
#include <framework/core/declarations.h>
#include "thingtype.h" #include "thingtype.h"
class ThingsType class ThingsType
@ -41,7 +42,7 @@ public:
bool load(const std::string& file); bool load(const std::string& file);
void unload(); void unload();
void parseThingType(std::stringstream& fin, ThingType& thingType); void parseThingType(const FileStreamPtr& fin, ThingType& thingType);
ThingType *getEmptyThingType() { return &m_emptyThingType; } ThingType *getEmptyThingType() { return &m_emptyThingType; }
ThingType *getThingType(uint16 id, Categories category); ThingType *getThingType(uint16 id, Categories category);

View File

@ -50,6 +50,9 @@ struct ThingType
IsStackable, IsStackable,
IsForceUse, IsForceUse,
IsMultiUse, IsMultiUse,
#if PROTOCOL<=810
IsRune,
#endif
IsWritable, IsWritable,
IsWritableOnce, IsWritableOnce,
IsFluidContainer, IsFluidContainer,

View File

@ -25,7 +25,7 @@
#include <otclient/global.h> #include <otclient/global.h>
#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" #error "the supplied protocol version is not supported"
#endif #endif
@ -51,6 +51,8 @@ namespace Proto {
constexpr int NumViolationReasons = 19; constexpr int NumViolationReasons = 19;
#elif PROTOCOL>=860 #elif PROTOCOL>=860
constexpr int NumViolationReasons = 20; constexpr int NumViolationReasons = 20;
#elif PROTOCOL>=810
constexpr int NumViolationReasons = 32;
#endif #endif
enum OsTypes { enum OsTypes {
@ -210,9 +212,7 @@ namespace Proto {
ClientRefreshContainer = 202, ClientRefreshContainer = 202,
ClientRequestOutfit = 210, ClientRequestOutfit = 210,
ClientChangeOutfit = 211, ClientChangeOutfit = 211,
#if PROTOCOL>=870 ClientMount = 212, // 870
ClientMount = 212,
#endif
ClientAddVip = 220, ClientAddVip = 220,
ClientRemoveVip = 221, ClientRemoveVip = 221,
ClientBugReport = 230, ClientBugReport = 230,
@ -242,7 +242,7 @@ namespace Proto {
ServerSpeakMonsterSay, ServerSpeakMonsterSay,
ServerSpeakMonsterYell, ServerSpeakMonsterYell,
// removed // unsupported
ServerSpeakRVRChannel = 255, ServerSpeakRVRChannel = 255,
ServerSpeakRVRAnswer, ServerSpeakRVRAnswer,
ServerSpeakRVRContinue, ServerSpeakRVRContinue,
@ -266,6 +266,29 @@ namespace Proto {
ServerSpeakChannelRed2 = 17, ServerSpeakChannelRed2 = 17,
ServerSpeakMonsterSay = 19, ServerSpeakMonsterSay = 19,
ServerSpeakMonsterYell 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 #endif
}; };
@ -289,9 +312,22 @@ namespace Proto {
MessageEventAdvance, MessageEventAdvance,
MessageEventDefault, MessageEventDefault,
MessageStatusDefault, MessageStatusDefault,
MessageInfoDescription , MessageInfoDescription,
MessageStatusSmall, MessageStatusSmall,
MessageConsoleBlue MessageConsoleBlue
#elif PROTOCOL>=810
MessageWarning = 18,
MessageEventAdvance,
MessageEventDefault,
MessageStatusDefault,
MessageInfoDescription,
MessageStatusSmall,
MessageConsoleBlue,
MessageConsoleRed,
// unsupported
MessageConsoleOrange = 255,
MessageConsoleOrange2,
#endif #endif
}; };

View File

@ -32,7 +32,6 @@ void ProtocolGame::login(const std::string& accountName, const std::string& acco
return; return;
} }
m_waitingLoginPacket = true;
m_accountName = accountName; m_accountName = accountName;
m_accountPassword = accountPassword; m_accountPassword = accountPassword;
m_characterName = characterName; m_characterName = characterName;
@ -43,17 +42,26 @@ void ProtocolGame::login(const std::string& accountName, const std::string& acco
void ProtocolGame::onConnect() void ProtocolGame::onConnect()
{ {
recv(); recv();
#if PROTOCOL>=860
m_waitingLoginPacket = true;
#else
sendLoginPacket(0, 0);
#endif
} }
void ProtocolGame::onRecv(InputMessage& inputMessage) void ProtocolGame::onRecv(InputMessage& inputMessage)
{ {
// only for protocol >= 860
if(m_waitingLoginPacket) { if(m_waitingLoginPacket) {
inputMessage.skipBytes(3); inputMessage.skipBytes(3);
uint32 timestamp = inputMessage.getU32(); uint32 timestamp = inputMessage.getU32();
uint8 unknown = inputMessage.getU8(); uint8 unknown = inputMessage.getU8();
m_waitingLoginPacket = false; m_waitingLoginPacket = false;
enableChecksum(); enableChecksum();
sendLoginPacket(timestamp, unknown); sendLoginPacket(timestamp, unknown);
recv(); recv();
} }

View File

@ -35,9 +35,12 @@
void ProtocolGame::parseMessage(InputMessage& msg) void ProtocolGame::parseMessage(InputMessage& msg)
{ {
int opcode = 0;
int prevOpcode = 0;
try { try {
while(!msg.eof()) { while(!msg.eof()) {
int opcode = msg.getU8(); opcode = msg.getU8();
switch(opcode) { switch(opcode) {
case Proto::GameServerInitGame: case Proto::GameServerInitGame:
@ -268,12 +271,13 @@ void ProtocolGame::parseMessage(InputMessage& msg)
parseExtendedOpcode(msg); parseExtendedOpcode(msg);
break; break;
default: default:
Fw::throwException("unknown opcode ", opcode); Fw::throwException("unknown opcode ", opcode, ", previous opcode is ", prevOpcode);
break; break;
} }
prevOpcode = opcode;
} }
} catch(Exception& e) { } 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) void ProtocolGame::parseTileAddThing(InputMessage& msg)
{ {
Position pos = parsePosition(msg); Position pos = parsePosition(msg);
int stackPos = msg.getU8(); int stackPos = -1;
#if PROTOCOL>=860
stackPos = msg.getU8();
#endif
ThingPtr thing = internalGetThing(msg); ThingPtr thing = internalGetThing(msg);
g_map.addThing(thing, pos, stackPos); g_map.addThing(thing, pos, stackPos);
@ -754,8 +762,12 @@ void ProtocolGame::parsePlayerStats(InputMessage& msg)
{ {
double health = msg.getU16(); double health = msg.getU16();
double maxHealth = msg.getU16(); double maxHealth = msg.getU16();
#if PROTOCOL>=860
double freeCapacity = msg.getU32() / 100.0; double freeCapacity = msg.getU32() / 100.0;
#if PROTOCOL >= 870 #else
double freeCapacity = msg.getU16() / 100.0;
#endif
#if PROTOCOL>=870
double experience = msg.getU64(); double experience = msg.getU64();
#else #else
double experience = msg.getU32(); double experience = msg.getU32();
@ -805,7 +817,9 @@ void ProtocolGame::parsePlayerState(InputMessage& msg)
void ProtocolGame::parsePlayerCancelAttack(InputMessage& msg) void ProtocolGame::parsePlayerCancelAttack(InputMessage& msg)
{ {
#if PROTOCOL>=860
msg.getU32(); // unknown msg.getU32(); // unknown
#endif
g_game.processAttackCancel(); g_game.processAttackCancel();
} }
@ -830,11 +844,11 @@ void ProtocolGame::parseCreatureSpeak(InputMessage& msg)
std::string name = msg.getString(); std::string name = msg.getString();
int level = msg.getU16(); int level = msg.getU16();
int serverType = msg.getU8(); int speakType = msg.getU8();
int channelId = 0; int channelId = 0;
Position creaturePos; Position creaturePos;
switch(serverType) { switch(speakType) {
case Proto::ServerSpeakSay: case Proto::ServerSpeakSay:
case Proto::ServerSpeakWhisper: case Proto::ServerSpeakWhisper:
case Proto::ServerSpeakYell: case Proto::ServerSpeakYell:
@ -859,12 +873,12 @@ void ProtocolGame::parseCreatureSpeak(InputMessage& msg)
msg.getU32(); msg.getU32();
break; break;
default: default:
logTraceError("unknown speak type ", serverType); logTraceError("unknown speak type ", speakType);
break; break;
} }
std::string message = msg.getString(); 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); g_game.processCreatureSpeak(name, level, type, message, channelId, creaturePos);
} }
@ -1140,8 +1154,8 @@ void ProtocolGame::setTileDescription(InputMessage& msg, Position position)
int stackPos = 0; int stackPos = 0;
while(true) { while(true) {
int inspectTileId = msg.getU16(true); int inspectItemId = msg.getU16(true);
if(inspectTileId >= 0xFF00) if(inspectItemId >= 0xFF00)
return; return;
else { else {
if(stackPos >= 10) if(stackPos >= 10)
@ -1159,8 +1173,8 @@ Outfit ProtocolGame::internalGetOutfit(InputMessage& msg)
{ {
Outfit outfit; Outfit outfit;
int id = msg.getU16(); int lookType = msg.getU16();
if(id != 0) { if(lookType != 0) {
outfit.setCategory(ThingsType::Creature); outfit.setCategory(ThingsType::Creature);
int head = msg.getU8(); int head = msg.getU8();
int body = msg.getU8(); int body = msg.getU8();
@ -1171,7 +1185,7 @@ Outfit ProtocolGame::internalGetOutfit(InputMessage& msg)
msg.getU16(); // mount msg.getU16(); // mount
#endif #endif
outfit.setId(id); outfit.setId(lookType);
outfit.setHead(head); outfit.setHead(head);
outfit.setBody(body); outfit.setBody(body);
outfit.setLegs(legs); outfit.setLegs(legs);
@ -1179,14 +1193,14 @@ Outfit ProtocolGame::internalGetOutfit(InputMessage& msg)
outfit.setAddons(addons); outfit.setAddons(addons);
} }
else { else {
int id = msg.getU16(); int lookTypeEx = msg.getU16();
if(id == 0) { if(lookTypeEx == 0) {
outfit.setCategory(ThingsType::Effect); outfit.setCategory(ThingsType::Effect);
outfit.setId(13); outfit.setId(13);
} }
else { else {
outfit.setCategory(ThingsType::Item); outfit.setCategory(ThingsType::Item);
outfit.setId(id); outfit.setId(lookTypeEx);
} }
} }
@ -1198,7 +1212,10 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg)
ThingPtr thing; ThingPtr thing;
int thingId = msg.getU16(); int thingId = msg.getU16();
assert(thingId != 0); if(thingId == 0) {
Fw::throwException("[ProtocolGame::internalGetThing] thingId == 0");
}
if(thingId == 0x0061 || thingId == 0x0062) { // add new creature if(thingId == 0x0061 || thingId == 0x0062) { // add new creature
CreaturePtr creature; CreaturePtr creature;
@ -1253,10 +1270,14 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg)
// emblem is sent only when the creature is not known // emblem is sent only when the creature is not known
int emblem = -1; int emblem = -1;
bool passable = false;
#if PROTOCOL>=860
if(thingId == 0x0061) if(thingId == 0x0061)
emblem = msg.getU8(); emblem = msg.getU8();
bool passable = (msg.getU8() == 0); passable = (msg.getU8() == 0);
#endif
if(creature) { if(creature) {
creature->setHealthPercent(healthPercent); creature->setHealthPercent(healthPercent);
@ -1277,8 +1298,9 @@ ThingPtr ProtocolGame::internalGetThing(InputMessage& msg)
thing = creature; thing = creature;
} else if(thingId == 0x0063) { // creature turn } else if(thingId == 0x0063) { // creature turn
parseCreatureTurn(msg); parseCreatureTurn(msg);
} else // item } else { // item
thing = internalGetItem(msg, thingId); thing = internalGetItem(msg, thingId);
}
return thing; return thing;
} }

View File

@ -37,7 +37,9 @@ void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown)
msg.addU16(Proto::ClientVersion); msg.addU16(Proto::ClientVersion);
int paddingBytes = 128;
msg.addU8(0); // first RSA byte must be 0 msg.addU8(0); // first RSA byte must be 0
paddingBytes -= 1;
// xtea key // xtea key
generateXteaKey(); generateXteaKey();
@ -45,20 +47,32 @@ void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown)
msg.addU32(m_xteaKey[1]); msg.addU32(m_xteaKey[1]);
msg.addU32(m_xteaKey[2]); msg.addU32(m_xteaKey[2]);
msg.addU32(m_xteaKey[3]); msg.addU32(m_xteaKey[3]);
msg.addU8(0); // is gm set? msg.addU8(0); // is gm set?
paddingBytes -= 17;
#if PROTOCOL>=860
enableChecksum();
msg.addString(m_accountName); msg.addString(m_accountName);
msg.addString(m_characterName); msg.addString(m_characterName);
msg.addString(m_accountPassword); msg.addString(m_accountPassword);
msg.addU32(timestamp); msg.addU32(timestamp);
msg.addU8(unknown); msg.addU8(unknown);
paddingBytes -= 11 + m_accountName.length() + m_characterName.length() + m_accountPassword.length();
#elif PROTOCOL>=810
msg.addU32(Fw::fromstring<uint32>(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 // 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 // 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); send(msg);

View File

@ -28,11 +28,6 @@
#include <otclient/core/thingstype.h> #include <otclient/core/thingstype.h>
#include <otclient/core/spritemanager.h> #include <otclient/core/spritemanager.h>
ProtocolLogin::ProtocolLogin()
{
enableChecksum();
}
void ProtocolLogin::login(const std::string& host, int port, const std::string& accountName, const std::string& accountPassword) void ProtocolLogin::login(const std::string& host, int port, const std::string& accountName, const std::string& accountPassword)
{ {
if(accountName.empty() || accountPassword.empty()) { if(accountName.empty() || accountPassword.empty()) {
@ -88,33 +83,49 @@ void ProtocolLogin::onError(const boost::system::error_code& error)
void ProtocolLogin::sendLoginPacket() void ProtocolLogin::sendLoginPacket()
{ {
OutputMessage oMsg; OutputMessage msg;
oMsg.addU8(Proto::ClientEnterAccount); msg.addU8(Proto::ClientEnterAccount);
oMsg.addU16(Proto::OsLinux); #ifdef WIN32
oMsg.addU16(Proto::ClientVersion); msg.addU16(Proto::OsWindows);
#else
msg.addU16(Proto::OsLinux);
#endif
msg.addU16(Proto::ClientVersion);
oMsg.addU32(g_thingsType.getSignature()); // data signature msg.addU32(g_thingsType.getSignature()); // data signature
oMsg.addU32(g_sprites.getSignature()); // sprite signature msg.addU32(g_sprites.getSignature()); // sprite signature
oMsg.addU32(Proto::PicSignature); // pic 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 // xtea key
generateXteaKey(); 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); msg.addU32(m_xteaKey[0]);
oMsg.addString(m_accountPassword); msg.addU32(m_xteaKey[1]);
msg.addU32(m_xteaKey[2]);
msg.addU32(m_xteaKey[3]);
paddingBytes -= 16;
// complete the 128 bytes for rsa encryption with zeros #if PROTOCOL>=860
oMsg.addPaddingBytes(128 - (21 + m_accountName.length() + m_accountPassword.length())); enableChecksum();
Rsa::encrypt((char*)oMsg.getBuffer() + InputMessage::DATA_POS + oMsg.getMessageSize() - 128, 128, Proto::RSA);
send(oMsg); msg.addString(m_accountName);
msg.addString(m_accountPassword);
paddingBytes -= 4 + m_accountName.length() + m_accountPassword.length();
#elif PROTOCOL>=810
msg.addU32(Fw::fromstring<uint32>(m_accountName));
msg.addString(m_accountPassword);
paddingBytes -= 6 + m_accountPassword.length();
#endif
msg.addPaddingBytes(paddingBytes); // complete the 128 bytes for rsa encryption with zeros
Rsa::encrypt((char*)msg.getWriteBuffer() - 128, 128, Proto::RSA);
send(msg);
enableXteaEncryption(); enableXteaEncryption();
recv(); recv();
} }

View File

@ -32,8 +32,6 @@ typedef std::shared_ptr<ProtocolLogin> ProtocolLoginPtr;
class ProtocolLogin : public Protocol class ProtocolLogin : public Protocol
{ {
public: public:
ProtocolLogin();
static ProtocolLoginPtr create() { return ProtocolLoginPtr(new ProtocolLogin); } static ProtocolLoginPtr create() { return ProtocolLoginPtr(new ProtocolLogin); }
void login(const std::string& host, int port, const std::string& accountName, const std::string& accountPassword); void login(const std::string& host, int port, const std::string& accountName, const std::string& accountPassword);