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:
parent
e1fad92110
commit
5a47e9d8a9
1
BUGS
1
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
|
||||
|
|
1
TODO
1
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -326,6 +326,22 @@ inline float randomRange<float>(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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -24,36 +24,35 @@
|
|||
#include "spritemanager.h"
|
||||
#include "thing.h"
|
||||
#include <framework/core/resourcemanager.h>
|
||||
#include <framework/core/filestream.h>
|
||||
|
||||
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);
|
||||
|
||||
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());
|
||||
FileStreamPtr fin = g_resources.openFile(file);
|
||||
if(!fin) {
|
||||
logError("unable to open dat file '", file, "'");
|
||||
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()
|
||||
|
@ -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)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define DATMANAGER_H
|
||||
|
||||
#include <framework/global.h>
|
||||
#include <framework/core/declarations.h>
|
||||
#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);
|
||||
|
|
|
@ -50,6 +50,9 @@ struct ThingType
|
|||
IsStackable,
|
||||
IsForceUse,
|
||||
IsMultiUse,
|
||||
#if PROTOCOL<=810
|
||||
IsRune,
|
||||
#endif
|
||||
IsWritable,
|
||||
IsWritableOnce,
|
||||
IsFluidContainer,
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#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"
|
||||
#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
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<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
|
||||
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);
|
||||
|
||||
|
|
|
@ -28,11 +28,6 @@
|
|||
#include <otclient/core/thingstype.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)
|
||||
{
|
||||
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;
|
||||
|
||||
// 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);
|
||||
#if PROTOCOL>=860
|
||||
enableChecksum();
|
||||
|
||||
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();
|
||||
recv();
|
||||
}
|
||||
|
|
|
@ -32,8 +32,6 @@ typedef std::shared_ptr<ProtocolLogin> 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);
|
||||
|
|
Loading…
Reference in New Issue