• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Windows login in client 11.04

silveralol

Advanced OT User
Joined
Mar 16, 2010
Messages
1,480
Solutions
9
Reaction score
211
hello folks, I'm trying login with client 11.04
my login.php atm
Code:
<?php
require_once 'engine/init.php';

// Client 11 loginWebService
if($_SERVER['HTTP_USER_AGENT'] == "Mozilla/5.0" && $config['TFSVersion'] === 'TFS_10') {

    function jsonError($message, $code = 3) {
        die(json_encode(array('errorCode' => $code, 'errorMessage' => $message)));
    }

    header("Content-Type: application/json");
    $input = file_get_contents("php://input");

    // Based on tests, input length should be at least 67+ chars.
    if (strlen($input) > 10) {
        /* {
            'accountname' => 'username',
            'password' => 'superpass',
            'stayloggedin' => true,
            'token' => '123123', (or not set)
            'type' => 'login', (What other types do we have?)
        } */
        $jsonObject = json_decode($input);

        $username = sanitize($jsonObject->accountname);
        $password = SHA1($jsonObject->password);
        $token = (isset($jsonObject->token)) ? sanitize($jsonObject->token) : false;
     
        $fields = '`id`, `premdays`';
        if ($config['twoFactorAuthenticator']) $fields .= ', `secret`';

        $account = mysql_select_single("SELECT {$fields} FROM `accounts` WHERE `name`='{$username}' AND `password`='{$password}' LIMIT 1;");
        if ($account === false) {
            jsonError('Wrong username and/or password.');
        }

        if ($config['twoFactorAuthenticator'] === true && $account['secret'] !== null) {
            if ($token === false) {
                jsonError('Submit a valid two-factor authentication token.', 6);
            } else {
                require_once("engine/function/rfc6238.php");
                if (TokenAuth6238::verify($account['secret'], $token) !== true) {
                    jsonError('Two-factor authentication failed, token is wrong.', 6);
                }
            }
        }

        $players = mysql_select_multi("SELECT `name` FROM `players` WHERE `account_id`='".$account['id']."';");
        if ($players !== false) {

            $gameserver = $config['gameserver'];
            $response = array(
                'session' => array(
                    'sessionkey' => $username."\n".$jsonObject->password."\n".$token."\n".floor(time() / 30),
                    'lastlogintime' => 0,
                    'ispremium' => ($account['premdays'] > 0) ? true : false,
                    'premiumuntil' => time() + ($account['premdays'] * 86400),
                    'status' => 'active'
                ),
                'playdata' => array(
                    'worlds' => array(
                        array(
                            'id' => 0,
                            'name' => $gameserver['name'],
                            'externaladdress' => $gameserver['ip'],
                            'externalport' => $gameserver['port'],
                            'previewstate' => 0
                        )
                    ),
                    'characters' => array(
                        //array( 'worldid' => ASD, 'name' => asd ),
                    )
                )
            );

            foreach ($players as $player) {
                $response['playdata']['characters'][] = array('worldid' => 0, 'name' => $player['name']);
            }

            //error_log("= SESSION KEY: " . $response['session']['sessionkey']);
            die(json_encode($response));
        } else {
            jsonError("Character list is empty.");
        }
    } else {
        jsonError("Unrecognized event.");
    }
} // End client 11 loginWebService

logged_in_redirect();
include 'layout/overall/header.php';

if (empty($_POST) === false) {

    if ($config['log_ip']) {
        znote_visitor_insert_detailed_data(5);
    }

    $username = $_POST['username'];
    $password = $_POST['password'];

    if (empty($username) || empty($password)) {
        $errors[] = 'You need to enter a username and password.';
    } else if (strlen($username) > 32 || strlen($password) > 64) {
            $errors[] = 'Username or password is too long.';
    } else if (user_exist($username) === false) {
        $errors[] = 'Failed to authorize your account, are the details correct, have you <a href=\'register.php\'>register</a>ed?';
    } /*else if (user_activated($username) === false) {
        $errors[] = 'You havent activated your account! Please check your email. <br>Note it may appear in your junk/spam box.';
    } */else if ($config['use_token'] && !Token::isValid($_POST['token'])) {
        Token::debug($_POST['token']);
        $errors[] = 'Token is invalid.';
    } else {

        // Starting loging
        if ($config['TFSVersion'] == 'TFS_02' || $config['TFSVersion'] == 'TFS_10') $login = user_login($username, $password);
        else if ($config['TFSVersion'] == 'TFS_03') $login = user_login_03($username, $password);
        else $login = false;
        if ($login === false) {
            $errors[] = 'Username and password combination is wrong.';
        } else {
            // Check if user have access to login
            $status = false;
            if ($config['mailserver']['register']) {
                $authenticate = mysql_select_single("SELECT `id` FROM `znote_accounts` WHERE `account_id`='$login' AND `active`='1' LIMIT 1;");
                if ($authenticate !== false) {
                    $status = true;
                } else {
                    $errors[] = "Your account is not activated. An email should have been sent to you when you registered. Please find it and click the activation link to activate your account.";
                }
            } else $status = true;

            if ($status) {
                // Regular login success, now lets check authentication token code
                if ($config['TFSVersion'] == 'TFS_10' && $config['twoFactorAuthenticator']) {
                    require_once("engine/function/rfc6238.php");

                    // Two factor authentication code / token
                    $authcode = (isset($_POST['authcode'])) ? getValue($_POST['authcode']) : false;

                    // Load secret values from db
                    $query = mysql_select_single("SELECT `a`.`secret` AS `secret`, `za`.`secret` AS `znote_secret` FROM `accounts` AS `a` INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id` WHERE `a`.`id`='".(int)$login."' LIMIT 1;");

                    // If account table HAS a secret, we need to validate it
                    if ($query['secret'] !== NULL) {

                        // Validate the secret first to make sure all is good.
                        if (TokenAuth6238::verify($query['secret'], $authcode) !== true) {
                            $errors[] = "Submitted Two-Factor Authentication token is wrong.";
                            $errors[] = "Make sure to type the correct token from your mobile authenticator.";
                            $status = false;
                        }

                    } else {

                        // secret from accounts table is null/not set. Perhaps we can activate it:
                        if ($query['znote_secret'] !== NULL && $authcode !== false && !empty($authcode)) {

                            // Validate the secret first to make sure all is good.
                            if (TokenAuth6238::verify($query['znote_secret'], $authcode)) {
                                // Success, enable the 2FA system
                                mysql_update("UPDATE `accounts` SET `secret`= '".$query['znote_secret']."' WHERE `id`='$login';");
                            } else {
                                $errors[] = "Activating Two-Factor authentication failed.";
                                $errors[] = "Try to login without token and configure your app properly.";
                                $errors[] = "Submitted Two-Factor Authentication token is wrong.";
                                $errors[] = "Make sure to type the correct token from your mobile authenticator.";
                                $status = false;
                            }
                        }
                    }
                } // End tfs 1.0+ with 2FA auth

                if ($status) {
                    setSession('user_id', $login);

                    // if IP is not set (etc acc created before Znote AAC was in use)
                    $znote_data = user_znote_account_data($login);
                    if ($znote_data['ip'] == 0) {
                        $update_data = array(
                        'ip' => getIPLong(),
                        );
                        user_update_znote_account($update_data);
                    }

                    // Send them to myaccount.php
                    header('Location: myaccount.php');
                    exit();
                }
            }
        }
    }
} else {
    header('Location: index.php');
}

if (empty($errors) === false) {
    ?>
    <h2>We tried to log you in, but...</h2>
    <?php
    echo output_errors($errors);
}

include 'layout/overall/footer.php'; ?>
and my connnection.cpp and .h is from tfs 1.3
so, how I can start to find the way to get login on this version ? I want to have it to make the new monsters :3

bump
 
Last edited by a moderator:
You can get the new monsters from Tibia 10.10 outdated client, just copy the dat and spr to your tibia 10 and you are done ;)
 
@gunz
connection.cpp
Code:
[B]/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2015  Mark Samman <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "otpch.h"

#include "configmanager.h"
#include "connection.h"
#include "outputmessage.h"
#include "protocol.h"
#include "scheduler.h"
#include "server.h"

extern ConfigManager g_config;

Connection_ptr ConnectionManager::createConnection(boost::asio::io_service& io_service, ConstServicePort_ptr servicePort)
{
    std::lock_guard<std::mutex> lockClass(connectionManagerLock);

    auto connection = std::make_shared<Connection>(io_service, servicePort);
    connections.insert(connection);
    return connection;
}

void ConnectionManager::releaseConnection(const Connection_ptr& connection)
{
    std::lock_guard<std::mutex> lockClass(connectionManagerLock);

    connections.erase(connection);
}

void ConnectionManager::closeAll()
{
    std::lock_guard<std::mutex> lockClass(connectionManagerLock);

    for (const auto& connection : connections) {
        try {
            boost::system::error_code error;
            connection->socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
            connection->socket.close(error);
        }
        catch (boost::system::system_error&) {
        }
    }
    connections.clear();
}

// Connection

void Connection::close(bool force)
{
    //any thread
    ConnectionManager::getInstance().releaseConnection(shared_from_this());

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

    if (protocol) {
        g_dispatcher.addTask(
            createTask(std::bind(&Protocol::release, protocol)));
    }

    if (messageQueue.empty() || force) {
        closeSocket();
    }
    else {
        //will be closed by the destructor or onWriteOperation
    }
}

void Connection::closeSocket()
{
    if (socket.is_open()) {
        try {
            readTimer.cancel();
            writeTimer.cancel();
            boost::system::error_code error;
            socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
            socket.close(error);
        }
        catch (boost::system::system_error& e) {
            std::cout << "[Network error - Connection::closeSocket] " << e.what() << std::endl;
        }
    }
}

Connection::~Connection()
{
    closeSocket();
}

void Connection::accept(Protocol_ptr protocol)
{
    this->protocol = protocol;
    g_dispatcher.addTask(createTask(std::bind(&Protocol::onConnect, protocol)));

    accept();
}

void Connection::accept()
{
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
    try {
        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));
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::accept] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

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

    int32_t size = msg.decodeHeader();
    if (error || size <= 0 || size >= NETWORKMESSAGE_MAXSIZE - 16) {
        close(FORCE_CLOSE);
    }

    if (connectionState != CONNECTION_STATE_OPEN) {
        return;
    }

    uint32_t timePassed = std::max<uint32_t>(1, (time(nullptr) - timeConnected) + 1);
    if ((++packetsSent / timePassed) > static_cast<uint32_t>(g_config.getNumber(ConfigManager::MAX_PACKETS_PER_SECOND))) {
        std::cout << convertIPToString(getIP()) << " disconnected for exceeding packet per second limit." << std::endl;
        close();
        return;
    }

    if (timePassed > 2) {
        timeConnected = time(nullptr);
        packetsSent = 0;
    }

    try {
        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 packet content
        msg.setLength(size + NetworkMessage::HEADER_LENGTH);
        boost::asio::async_read(socket, boost::asio::buffer(msg.getBodyBuffer(), size),
            std::bind(&Connection::parsePacket, shared_from_this(), std::placeholders::_1));
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::parseHeader] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

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

    if (error) {
        close(FORCE_CLOSE);
    }

    if (connectionState != CONNECTION_STATE_OPEN) {
        return;
    }

    //Check packet checksum
    uint32_t checksum;
    int32_t len = msg.getLength() - msg.getBufferPosition() - NetworkMessage::CHECKSUM_LENGTH;
    if (len > 0) {
        checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + NetworkMessage::CHECKSUM_LENGTH, len);
    }
    else {
        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);
    }

    if (!receivedFirst) {
        // First message received
        receivedFirst = true;

        if (!protocol) {
            // Game protocol has already been created at this point
            protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this());
            if (!protocol) {
                close(FORCE_CLOSE);
                return;
            }
        }
        else {
            msg.skipBytes(1);    // Skip protocol ID
        }

        protocol->onRecvFirstMessage(msg);
    }
    else {
        protocol->onRecvMessage(msg);    // Send the packet to the current protocol
    }

    try {
        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));

        // Wait to the next packet
        boost::asio::async_read(socket,
            boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
            std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::parsePacket] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

void Connection::send(const OutputMessage_ptr& msg)
{
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
    if (connectionState != CONNECTION_STATE_OPEN) {
        return;
    }

    bool noPendingWrite = messageQueue.empty();
    messageQueue.emplace_back(msg);
    if (noPendingWrite) {
        internalSend(msg);
    }
}

void Connection::internalSend(const OutputMessage_ptr& msg)
{
    protocol->onSendMessage(msg);
    try {
        writeTimer.expires_from_now(boost::posix_time::seconds(Connection::write_timeout));
        writeTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()),
            std::placeholders::_1));

        boost::asio::async_write(socket,
            boost::asio::buffer(msg->getOutputBuffer(), msg->getLength()),
            std::bind(&Connection::onWriteOperation, shared_from_this(), std::placeholders::_1));
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::internalSend] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

uint32_t Connection::getIP()
{
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);

    // IP-address is expressed in network byte order
    boost::system::error_code error;
    const boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(error);
    if (error) {
        return 0;
    }

    return htonl(endpoint.address().to_v4().to_ulong());
}

void Connection::onWriteOperation(const boost::system::error_code& error)
{
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
    writeTimer.cancel();
    messageQueue.pop_front();

    if (error) {
        messageQueue.clear();
        close(FORCE_CLOSE);
        return;
    }

    if (!messageQueue.empty()) {
        internalSend(messageQueue.front());
    }
    else if (connectionState == CONNECTION_STATE_CLOSED) {
        closeSocket();
    }
}

void Connection::handleTimeout(ConnectionWeak_ptr connectionWeak, const boost::system::error_code& error)
{
    if (error == boost::asio::error::operation_aborted) {
        //The timer has been manually cancelled
        return;
    }

    if (auto connection = connectionWeak.lock()) {
        connection->close(FORCE_CLOSE);
    }
}
yes, it is outdated!
and .h
Code:
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2015  Mark Samman <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef FS_CONNECTION_H_FC8E1B4392D24D27A2F129D8B93A6348
#define FS_CONNECTION_H_FC8E1B4392D24D27A2F129D8B93A6348

#include <unordered_set>

#include "networkmessage.h"

class Protocol;
typedef std::shared_ptr<Protocol> Protocol_ptr;
class OutputMessage;
typedef std::shared_ptr<OutputMessage> OutputMessage_ptr;
class Connection;
typedef std::shared_ptr<Connection> Connection_ptr;
typedef std::weak_ptr<Connection> ConnectionWeak_ptr;
class ServiceBase;
typedef std::shared_ptr<ServiceBase> Service_ptr;
class ServicePort;
typedef std::shared_ptr<ServicePort> ServicePort_ptr;
typedef std::shared_ptr<const ServicePort> ConstServicePort_ptr;

class ConnectionManager
{
public:
    static ConnectionManager& getInstance() {
        static ConnectionManager instance;
        return instance;
    }

    Connection_ptr createConnection(boost::asio::io_service& io_service, ConstServicePort_ptr servicePort);
    void releaseConnection(const Connection_ptr& connection);
    void closeAll();

protected:
    ConnectionManager() = default;

    std::unordered_set<Connection_ptr> connections;
    std::mutex connectionManagerLock;
};

class Connection : public std::enable_shared_from_this<Connection>
{
public:
    // non-copyable
    Connection(const Connection&) = delete;
    Connection& operator=(const Connection&) = delete;

    enum { write_timeout = 30 };
    enum { read_timeout = 30 };

    enum ConnectionState_t {
        CONNECTION_STATE_OPEN,
        CONNECTION_STATE_CLOSED,
    };

    enum { FORCE_CLOSE = true };

    Connection(boost::asio::io_service& io_service,
        ConstServicePort_ptr service_port) :
        readTimer(io_service),
        writeTimer(io_service),
        service_port(service_port),
        socket(io_service) {
        connectionState = CONNECTION_STATE_OPEN;
        receivedFirst = false;
        packetsSent = 0;
        timeConnected = time(nullptr);
    }
    ~Connection();

    friend class ConnectionManager;

    void close(bool force = false);
    // Used by protocols that require server to send first
    void accept(Protocol_ptr protocol);
    void accept();

    void send(const OutputMessage_ptr& msg);

    uint32_t getIP();

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

    void onWriteOperation(const boost::system::error_code& error);

    static void handleTimeout(ConnectionWeak_ptr connectionWeak, const boost::system::error_code& error);

    void closeSocket();
    void internalSend(const OutputMessage_ptr& msg);

    boost::asio::ip::tcp::socket& getSocket() {
        return socket;
    }
    friend class ServicePort;

    NetworkMessage msg;

    boost::asio::deadline_timer readTimer;
    boost::asio::deadline_timer writeTimer;

    std::recursive_mutex connectionLock;

    std::list<OutputMessage_ptr> messageQueue;

    ConstServicePort_ptr service_port;
    Protocol_ptr protocol;

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

    time_t timeConnected;
    uint32_t packetsSent;

    bool connectionState;
    bool receivedFirst;
};

#endif
You can get the new monsters from Tibia 10.10 outdated client, just copy the dat and spr to your tibia 10 and you are done ;)
is not the best way to get it, because when I update the client or downgrade I'll have to edit the spr and dat again

[/B]

upppp
 
Last edited by a moderator:
You have to add code to parse world name in parseHeader function
I do the changes:
my connections.cpp
Code:
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017  Mark Samman <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "otpch.h"

#include "configmanager.h"
#include "connection.h"
#include "outputmessage.h"
#include "protocol.h"
#include "protocolgame.h"
#include "scheduler.h"
#include "server.h"

extern ConfigManager g_config;

Connection_ptr ConnectionManager::createConnection(boost::asio::io_service& io_service, ConstServicePort_ptr servicePort)
{
    std::lock_guard<std::mutex> lockClass(connectionManagerLock);

    auto connection = std::make_shared<Connection>(io_service, servicePort);
    connections.insert(connection);
    return connection;
}

void ConnectionManager::releaseConnection(const Connection_ptr& connection)
{
    std::lock_guard<std::mutex> lockClass(connectionManagerLock);

    connections.erase(connection);
}

void ConnectionManager::closeAll()
{
    std::lock_guard<std::mutex> lockClass(connectionManagerLock);

    for (const auto& connection : connections) {
        try {
            boost::system::error_code error;
            connection->socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
            connection->socket.close(error);
        }
        catch (boost::system::system_error&) {
        }
    }
    connections.clear();
}

// Connection

void Connection::close(bool force)
{
    //any thread
    ConnectionManager::getInstance().releaseConnection(shared_from_this());

    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);

    connectionState = CONNECTION_STATE_DISCONNECTED;

    if (protocol) {
        g_dispatcher.addTask(
            createTask(std::bind(&Protocol::release, protocol)));
    }

    if (messageQueue.empty() || force) {
        closeSocket();
    }
    else {
        //will be closed by the destructor or onWriteOperation
    }
}

void Connection::closeSocket()
{
    if (socket.is_open()) {
        try {
            readTimer.cancel();
            writeTimer.cancel();
            boost::system::error_code error;
            socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
            socket.close(error);
        }
        catch (boost::system::system_error& e) {
            std::cout << "[Network error - Connection::closeSocket] " << e.what() << std::endl;
        }
    }
}

Connection::~Connection()
{
    closeSocket();
}

void Connection::accept(Protocol_ptr protocol)
{
    this->protocol = protocol;
    g_dispatcher.addTask(createTask(std::bind(&Protocol::onConnect, protocol)));
    connectionState = CONNECTION_STATE_CONNECTING_STAGE2;

    accept();
}

void Connection::accept()
{
    if (connectionState == CONNECTION_STATE_PENDING) {
        connectionState = CONNECTION_STATE_CONNECTING_STAGE1;
    }
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
    try {
        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));

        if (!receivedLastChar && receivedName && connectionState == CONNECTION_STATE_CONNECTING_STAGE2) {
            // Read size of the first packet
            boost::asio::async_read(socket,
                boost::asio::buffer(msg.getBuffer(), 1),
                std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
        }
        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));
        }
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::accept] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

void Connection::parseHeader(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;
    }

    uint32_t timePassed = std::max<uint32_t>(1, (time(nullptr) - timeConnected) + 1);
    if ((++packetsSent / timePassed) > static_cast<uint32_t>(g_config.getNumber(ConfigManager::MAX_PACKETS_PER_SECOND))) {
        std::cout << convertIPToString(getIP()) << " disconnected for exceeding packet per second limit." << std::endl;
        close();
        return;
    }

    if (!receivedLastChar && connectionState == CONNECTION_STATE_CONNECTING_STAGE2) {
        uint8_t* msgBuffer = msg.getBuffer();

        if (!receivedName && msgBuffer[1] == 0x00) {
            receivedLastChar = true;
        }
        else {
            std::string serverName = g_config.getString(ConfigManager::SERVER_NAME) + "\n";

            if (!receivedName) {
                if ((char)msgBuffer[0] == serverName[0] && (char)msgBuffer[1] == serverName[1]) {
                    receivedName = true;
                    serverNameTime = 1;

                    accept();
                    return;
                }
                else {
                    std::cout << "[Network error - Connection::parseHeader] Invalid Client Login" << std::endl;
                    close(FORCE_CLOSE);
                    return;
                }
            }
            ++serverNameTime;

            if ((char)msgBuffer[0] == serverName[serverNameTime]) {
                if (msgBuffer[0] == 0x0A) {
                    receivedLastChar = true;
                }

                accept();
                return;
            }
            else {
                std::cout << "[Network error - Connection::parseHeader] Invalid Client Login" << std::endl;
                close(FORCE_CLOSE);
                return;
            }
        }
    }

    if (receivedLastChar && connectionState == CONNECTION_STATE_CONNECTING_STAGE2) {
        connectionState = CONNECTION_STATE_GAME;
    }

    if (timePassed > 2) {
        timeConnected = time(nullptr);
        packetsSent = 0;
    }

    uint16_t size = msg.getLengthHeader();
    if (size == 0 || size >= NETWORKMESSAGE_MAXSIZE - 16) {
        close(FORCE_CLOSE);
        return;
    }

    try {
        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 packet content
        msg.setLength(size + NetworkMessage::HEADER_LENGTH);
        boost::asio::async_read(socket, boost::asio::buffer(msg.getBodyBuffer(), size),
            std::bind(&Connection::parsePacket, shared_from_this(), std::placeholders::_1));
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::parseHeader] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

void Connection::parsePacket(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;
    }

    //Check packet checksum
    uint32_t checksum;
    int32_t len = msg.getLength() - msg.getBufferPosition() - NetworkMessage::CHECKSUM_LENGTH;
    if (len > 0) {
        checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + NetworkMessage::CHECKSUM_LENGTH, len);
    }
    else {
        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);
    }

    if (!receivedFirst) {
        // First message received
        receivedFirst = true;

        if (!protocol) {
            // Game protocol has already been created at this point
            protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this());
            if (!protocol) {
                close(FORCE_CLOSE);
                return;
            }
        }
        else {
            msg.skipBytes(1);    // Skip protocol ID
        }

        protocol->onRecvFirstMessage(msg);
    }
    else {
        protocol->onRecvMessage(msg);    // Send the packet to the current protocol
    }

    try {
        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));

        // Wait to the next packet
        boost::asio::async_read(socket,
            boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
            std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::parsePacket] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

void Connection::send(const OutputMessage_ptr& msg)
{
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
    if (connectionState == CONNECTION_STATE_DISCONNECTED) {
        return;
    }

    bool noPendingWrite = messageQueue.empty();
    messageQueue.emplace_back(msg);
    if (noPendingWrite) {
        internalSend(msg);
    }
}

void Connection::internalSend(const OutputMessage_ptr& msg)
{
    protocol->onSendMessage(msg);
    try {
        writeTimer.expires_from_now(boost::posix_time::seconds(Connection::write_timeout));
        writeTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()),
            std::placeholders::_1));

        boost::asio::async_write(socket,
            boost::asio::buffer(msg->getOutputBuffer(), msg->getLength()),
            std::bind(&Connection::onWriteOperation, shared_from_this(), std::placeholders::_1));
    }
    catch (boost::system::system_error& e) {
        std::cout << "[Network error - Connection::internalSend] " << e.what() << std::endl;
        close(FORCE_CLOSE);
    }
}

uint32_t Connection::getIP()
{
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);

    // IP-address is expressed in network byte order
    boost::system::error_code error;
    const boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(error);
    if (error) {
        return 0;
    }

    return htonl(endpoint.address().to_v4().to_ulong());
}

void Connection::onWriteOperation(const boost::system::error_code& error)
{
    std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
    writeTimer.cancel();
    messageQueue.pop_front();

    if (error) {
        messageQueue.clear();
        close(FORCE_CLOSE);
        return;
    }

    if (!messageQueue.empty()) {
        internalSend(messageQueue.front());
    }
    else if (connectionState == CONNECTION_STATE_DISCONNECTED) {
        closeSocket();
    }
}

void Connection::handleTimeout(ConnectionWeak_ptr connectionWeak, const boost::system::error_code& error)
{
    if (error == boost::asio::error::operation_aborted) {
        //The timer has been manually cancelled
        return;
    }

    if (auto connection = connectionWeak.lock()) {
        connection->close(FORCE_CLOSE);
    }
}
and h.
Code:
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017  Mark Samman <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef FS_CONNECTION_H_FC8E1B4392D24D27A2F129D8B93A6348
#define FS_CONNECTION_H_FC8E1B4392D24D27A2F129D8B93A6348

#include <unordered_set>

#include "networkmessage.h"

class Protocol;
using Protocol_ptr = std::shared_ptr<Protocol>;
class OutputMessage;
using OutputMessage_ptr = std::shared_ptr<OutputMessage>;
class Connection;
using Connection_ptr = std::shared_ptr<Connection>;
using ConnectionWeak_ptr = std::weak_ptr<Connection>;
class ServiceBase;
using Service_ptr = std::shared_ptr<ServiceBase>;
class ServicePort;
using ServicePort_ptr = std::shared_ptr<ServicePort>;
using ConstServicePort_ptr = std::shared_ptr<const ServicePort>;

class ConnectionManager
{
public:
    static ConnectionManager& getInstance() {
        static ConnectionManager instance;
        return instance;
    }

    Connection_ptr createConnection(boost::asio::io_service& io_service, ConstServicePort_ptr servicePort);
    void releaseConnection(const Connection_ptr& connection);
    void closeAll();

protected:
    ConnectionManager() = default;

    std::unordered_set<Connection_ptr> connections;
    std::mutex connectionManagerLock;
};

class Connection : public std::enable_shared_from_this<Connection>
{
public:
    // non-copyable
    Connection(const Connection&) = delete;
    Connection& operator=(const Connection&) = delete;

    enum { write_timeout = 30 };
    enum { read_timeout = 30 };

    enum ConnectionState_t : int8_t {
        CONNECTION_STATE_DISCONNECTED,
        CONNECTION_STATE_CONNECTING_STAGE1,
        CONNECTION_STATE_CONNECTING_STAGE2,
        CONNECTION_STATE_GAME,
        CONNECTION_STATE_PENDING
    };

    enum { FORCE_CLOSE = true };

    Connection(boost::asio::io_service& io_service,
        ConstServicePort_ptr service_port) :
        readTimer(io_service),
        writeTimer(io_service),
        service_port(std::move(service_port)),
        socket(io_service) {
        connectionState = CONNECTION_STATE_PENDING;
        packetsSent = 0;
        timeConnected = time(nullptr);
        receivedFirst = false;
        serverNameTime = 0;
        receivedName = false;
        receivedLastChar = false;
    }
    ~Connection();

    friend class ConnectionManager;

    void close(bool force = false);
    // Used by protocols that require server to send first
    void accept(Protocol_ptr protocol);
    void accept();

    void send(const OutputMessage_ptr& msg);

    uint32_t getIP();

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

    void onWriteOperation(const boost::system::error_code& error);

    static void handleTimeout(ConnectionWeak_ptr connectionWeak, const boost::system::error_code& error);

    void closeSocket();
    void internalSend(const OutputMessage_ptr& msg);

    boost::asio::ip::tcp::socket& getSocket() {
        return socket;
    }
    friend class ServicePort;

    NetworkMessage msg;



    boost::asio::deadline_timer readTimer;
    boost::asio::deadline_timer writeTimer;

    std::recursive_mutex connectionLock;

    std::list<OutputMessage_ptr> messageQueue;

    ConstServicePort_ptr service_port;
    Protocol_ptr protocol;

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

    time_t timeConnected;
    uint32_t packetsSent;

    int8_t connectionState;
    bool receivedFirst;

    uint32_t serverNameTime;
    bool receivedName;
    bool receivedLastChar;
};

#endif
try using a client 11.04 I got crash from the client, the character login but crash the client, I almost sure that is some byte, but how I can find the missing byte?

bump
 
Last edited by a moderator:
What is the debug message ?
Code:
EngineID: 1
HardwareID: b50ec3da6fd4f973ece43d60323bc6455178a043
OperatingSystem: Windows 10
Position: [33401,32427,15]
ProductName: Tibia Client
Timestamp: 1496143717
Version: 11.01.4098
the client 11 don't show any relevant information :|
 
yes, I got it working with the latest version of tibia 10 (the version that shows "outdated client etc"
but I want login with the client 11, to test somethings with the new client
 
Would appreciate Tibia 11 support too, I want to play on it so badly on my ots.

Bump for this thread.
 
I am also seeking for this feature, regarding the issue I keep getting this error:
bug.png

Using version 11.30, I've been trying to interpret these changes: 11.30 (sorry for no 11.20) · comedinha/Flash-News@ff75c4f · GitHub but still there's an issue with parseHeader in connection.cpp
 
I'm still with the same error, invalid client login.. bumperino

bump, still trying make it work, but don't zzzzz
 
Last edited by a moderator:
Back
Top