Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for tibia 11 - only via protocol game. #2721

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 63 additions & 15 deletions src/connection.cpp
Expand Up @@ -67,10 +67,10 @@ void Connection::close(bool force)
ConnectionManager::getInstance().releaseConnection(shared_from_this());

std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
if (connectionState != CONNECTION_STATE_OPEN) {
if (connectionState == CONNECTION_STATE_DISCONNECTED) {
return;
}
connectionState = CONNECTION_STATE_CLOSED;
connectionState = CONNECTION_STATE_DISCONNECTED;

if (protocol) {
g_dispatcher.addTask(
Expand Down Expand Up @@ -108,6 +108,7 @@ void Connection::accept(Protocol_ptr protocol)
{
this->protocol = protocol;
g_dispatcher.addTask(createTask(std::bind(&Protocol::onConnect, protocol)));
connectionState = CONNECTION_STATE_CONNECTING;

accept();
}
Expand All @@ -119,16 +120,53 @@ void Connection::accept()
readTimer.expires_from_now(boost::posix_time::seconds(CONNECTION_READ_TIMEOUT));
readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()), std::placeholders::_1));

// Read size of the first packet
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
if (connectionState == CONNECTION_STATE_CONNECTING) {
// Read one-by-one ascii characters (new-line-terminated) to verify world name
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer(), 1),
std::bind(&Connection::parseProxyWorldNameIdentification, shared_from_this(), std::placeholders::_1));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer lambda instead of std::bind. std::bind can be hard to read and can result in larger object files and binaries due to type information that will not be produced by equivalent lambdas.

} else {
// Read size of the first packet
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer lambda instead of std::bind. std::bind can be hard to read and can result in larger object files and binaries due to type information that will not be produced by equivalent lambdas.

}


} catch (boost::system::system_error& e) {
std::cout << "[Network error - Connection::accept] " << e.what() << std::endl;
close(FORCE_CLOSE);
}
}

void Connection::parseProxyWorldNameIdentification(const boost::system::error_code& error)
{
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
readTimer.cancel();

if (error) {
close(FORCE_CLOSE);
return;
} else if (connectionState == CONNECTION_STATE_DISCONNECTED) {
return;
}

char b = msg.getBuffer()[0];
if (b == 0x0A) {
if (worldName != g_config.getString(ConfigManager::SERVER_NAME)) {
std::cout << convertIPToString(getIP()) << " disconnected for sending invalid world name." << std::endl;
close();
return;
}

connectionState = CONNECTION_STATE_CONNECTED;
} else {
worldName.push_back(b);

This comment was marked as off-topic.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for trivial type emplace_back ? :D

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW if you have no idea what you are doing always use push_back.
https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/LubvTXxs7k4/ThMsbV23DQAJ

}

accept();
}

void Connection::parseHeader(const boost::system::error_code& error)
{
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
Expand All @@ -137,7 +175,7 @@ void Connection::parseHeader(const boost::system::error_code& error)
if (error) {
close(FORCE_CLOSE);
return;
} else if (connectionState != CONNECTION_STATE_OPEN) {
} else if (connectionState == CONNECTION_STATE_DISCONNECTED) {
return;
}

Expand Down Expand Up @@ -182,7 +220,7 @@ void Connection::parsePacket(const boost::system::error_code& error)
if (error) {
close(FORCE_CLOSE);
return;
} else if (connectionState != CONNECTION_STATE_OPEN) {
} else if (connectionState == CONNECTION_STATE_DISCONNECTED) {
return;
}

Expand All @@ -195,10 +233,20 @@ void Connection::parsePacket(const boost::system::error_code& error)
checksum = 0;
}

uint32_t recvChecksum = msg.get<uint32_t>();
if (recvChecksum != checksum) {
// it might not have been the checksum, step back
msg.skipBytes(-NetworkMessage::CHECKSUM_LENGTH);
bool checksummed = true;
if (worldName.empty()) {
checksummed = msg.get<uint32_t>() == checksum;
if (!checksummed) {
// it might not have been the checksum, step back
msg.skipBytes(-NetworkMessage::CHECKSUM_LENGTH);
}
} else {
uint32_t sequenceNumber = msg.get<uint32_t>();
if (!receivedFirst || lastSequenceNumber - sequenceNumber == 1) {
lastSequenceNumber = sequenceNumber;
} else {
// the client has sent some data that was lost
}
}

if (!receivedFirst) {
Expand All @@ -207,7 +255,7 @@ void Connection::parsePacket(const boost::system::error_code& error)

if (!protocol) {
// Game protocol has already been created at this point
protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this());
protocol = service_port->make_protocol(checksummed, msg, shared_from_this());
if (!protocol) {
close(FORCE_CLOSE);
return;
Expand Down Expand Up @@ -239,7 +287,7 @@ void Connection::parsePacket(const boost::system::error_code& error)
void Connection::send(const OutputMessage_ptr& msg)
{
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
if (connectionState != CONNECTION_STATE_OPEN) {
if (connectionState == CONNECTION_STATE_DISCONNECTED) {
return;
}

Expand Down Expand Up @@ -295,7 +343,7 @@ void Connection::onWriteOperation(const boost::system::error_code& error)

if (!messageQueue.empty()) {
internalSend(messageQueue.front());
} else if (connectionState == CONNECTION_STATE_CLOSED) {
} else if (connectionState == CONNECTION_STATE_DISCONNECTED) {
closeSocket();
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/connection.h
Expand Up @@ -67,8 +67,9 @@ class Connection : public std::enable_shared_from_this<Connection>
Connection& operator=(const Connection&) = delete;

enum ConnectionState_t {
CONNECTION_STATE_OPEN,
CONNECTION_STATE_CLOSED,
CONNECTION_STATE_CONNECTING,
CONNECTION_STATE_CONNECTED,
CONNECTION_STATE_DISCONNECTED,
};

enum { FORCE_CLOSE = true };
Expand All @@ -94,6 +95,7 @@ class Connection : public std::enable_shared_from_this<Connection>
uint32_t getIP();

private:
void parseProxyWorldNameIdentification(const boost::system::error_code& error);
void parseHeader(const boost::system::error_code& error);
void parsePacket(const boost::system::error_code& error);

Expand All @@ -118,15 +120,18 @@ class Connection : public std::enable_shared_from_this<Connection>

std::list<OutputMessage_ptr> messageQueue;

std::string worldName;

ConstServicePort_ptr service_port;
Protocol_ptr protocol;

boost::asio::ip::tcp::socket socket;

time_t timeConnected;
uint32_t packetsSent = 0;
uint32_t lastSequenceNumber = 0;

bool connectionState = CONNECTION_STATE_OPEN;
ConnectionState_t connectionState = CONNECTION_STATE_CONNECTED;
bool receivedFirst = false;
};

Expand Down
6 changes: 3 additions & 3 deletions src/definitions.h
Expand Up @@ -24,9 +24,9 @@ static constexpr auto STATUS_SERVER_NAME = "The Forgotten Server";
static constexpr auto STATUS_SERVER_VERSION = "1.3";
static constexpr auto STATUS_SERVER_DEVELOPERS = "Mark Samman";

static constexpr auto CLIENT_VERSION_MIN = 1097;
static constexpr auto CLIENT_VERSION_MAX = 1098;
static constexpr auto CLIENT_VERSION_STR = "10.98";
static constexpr auto CLIENT_VERSION_MIN = 1100;
static constexpr auto CLIENT_VERSION_MAX = 1111;
static constexpr auto CLIENT_VERSION_STR = "11.11";

static constexpr auto AUTHENTICATOR_DIGITS = 6U;
static constexpr auto AUTHENTICATOR_PERIOD = 30U;
Expand Down
6 changes: 4 additions & 2 deletions src/outputmessage.h
Expand Up @@ -43,8 +43,10 @@ class OutputMessage : public NetworkMessage
add_header(info.length);
}

void addCryptoHeader(bool addChecksum) {
if (addChecksum) {
void addCryptoHeader(bool addSequence, bool addChecksum, uint32_t& sequenceNumber) {
if (addSequence) {
add_header(sequenceNumber++);
} else if (addChecksum) {
add_header(adlerChecksum(buffer + outputBufferStart, info.length));
}

Expand Down
4 changes: 2 additions & 2 deletions src/protocol.cpp
Expand Up @@ -26,14 +26,14 @@

extern RSA g_RSA;

void Protocol::onSendMessage(const OutputMessage_ptr& msg) const
void Protocol::onSendMessage(const OutputMessage_ptr& msg)
{
if (!rawMessages) {
msg->writeMessageLength();

if (encryptionEnabled) {
XTEA_encrypt(*msg);
msg->addCryptoHeader(checksumEnabled);
msg->addCryptoHeader(sequenceEnabled, checksumEnabled, sequenceNumber);
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/protocol.h
Expand Up @@ -35,7 +35,7 @@ class Protocol : public std::enable_shared_from_this<Protocol>

virtual void parsePacket(NetworkMessage&) {}

virtual void onSendMessage(const OutputMessage_ptr& msg) const;
virtual void onSendMessage(const OutputMessage_ptr& msg);
void onRecvMessage(NetworkMessage& msg);
virtual void onRecvFirstMessage(NetworkMessage& msg) = 0;
virtual void onConnect() {}
Expand Down Expand Up @@ -78,6 +78,9 @@ class Protocol : public std::enable_shared_from_this<Protocol>
void disableChecksum() {
checksumEnabled = false;
}
void enableSequence() {
sequenceEnabled = true;
}

static bool RSA_decrypt(NetworkMessage& msg);

Expand All @@ -99,7 +102,9 @@ class Protocol : public std::enable_shared_from_this<Protocol>
xtea::key key;
bool encryptionEnabled = false;
bool checksumEnabled = true;
bool sequenceEnabled = false;
bool rawMessages = false;
uint32_t sequenceNumber = 0;
};

#endif
22 changes: 22 additions & 0 deletions src/protocolgame.cpp
Expand Up @@ -244,6 +244,9 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)

OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>());
version = msg.get<uint16_t>();
if (version >= 1111) {
enableSequence();
}

msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision

Expand Down Expand Up @@ -1007,6 +1010,13 @@ void ProtocolGame::parseEditVip(NetworkMessage& msg)
const std::string description = msg.getString();
uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is 10 still the max icon in client 11?

bool notify = msg.getByte() != 0;
if (version >= 1110) {
int groupCount = msg.getByte();
for (int i = 0; i < groupCount; i++) {
msg.getByte(); // group id
}
}

addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify);
}

Expand Down Expand Up @@ -1334,6 +1344,7 @@ void ProtocolGame::sendBasicData()
msg.add<uint32_t>(0);
}
msg.addByte(player->getVocation()->getClientId());
msg.addByte(player->getVocation()->getId() != VOCATION_NONE ? 0x01 : 0x00);
msg.add<uint16_t>(0xFF); // number of known spells
for (uint8_t spellId = 0x00; spellId < 0xFF; spellId++) {
msg.addByte(spellId);
Expand Down Expand Up @@ -1999,6 +2010,10 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId)
msg.add<uint16_t>(0x00);
}

if (version >= 1101) {
msg.add<uint16_t>(0x00); // imbuement slots
}

MarketStatistics* statistics = IOMarket::getInstance().getPurchaseStatistics(itemId);
if (statistics) {
msg.addByte(0x01);
Expand Down Expand Up @@ -2732,6 +2747,9 @@ void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, const std::st
msg.add<uint32_t>(std::min<uint32_t>(10, icon));
msg.addByte(notify ? 0x01 : 0x00);
msg.addByte(status);
if (version >= 1110) {
msg.addByte(0x00); // group count
}
writeToOutputBuffer(msg);
}

Expand Down Expand Up @@ -2862,6 +2880,10 @@ void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bo
msg.addByte(creature->getSpeechBubble());
msg.addByte(0xFF); // MARK_UNMARKED

if (version >= 1110) {
msg.addByte(0x00); // inspection state
}

if (otherPlayer) {
msg.add<uint16_t>(otherPlayer->getHelpers());
} else {
Expand Down
6 changes: 6 additions & 0 deletions src/protocollogin.cpp
Expand Up @@ -126,6 +126,12 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
msg.skipBytes(2); // client OS

uint16_t version = msg.get<uint16_t>();

// if the player is connecting using the deprecated c++ client
if (version >= 1111) {
enableSequence();
}

if (version >= 971) {
msg.skipBytes(17);
} else {
Expand Down