• 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!
  • 2026 staff recruitment is open! Check it out and consider applying!

C++ Convert OTCv8 code New Walking for TFS 1.4.2

Marko999x

ArchezOt soon
Premium User
Joined
Dec 14, 2017
Messages
3,958
Solutions
103
Reaction score
3,092
Location
Germany
Hey can someone please convert this for newer tfs aka 1.4.2 please? I have 0% experience with c++ Thanks you

 
Last edited:
well you have to do a very simple trick, first remove the trytofixautowalk thing it eats cpu when many are online then use this function it converts and reverts back between vector and list because using vector here wouldn't be nice to erase and insert in walking instructions sent by client


this is my protocolgame.cpp functions i think its easy to declare them by yourself in header file
LUA:
void ProtocolGame::parseNewWalking(NetworkMessage& msg)
{
    uint32_t playerWalkId = msg.get<uint32_t>();
    int32_t predictiveWalkId = msg.get<int32_t>(); // extension for proxy system, currently not used
    Position playerPosition = msg.getPosition(); // local player position before moving, including prewalk
    uint8_t flags = msg.getByte(); // 0x01 - prewalk, 0x02 - autowalk

    uint16_t numdirs = msg.get<uint16_t>();
    if (numdirs == 0 || numdirs > 4096) {
        return;
    }

    std::list<Direction> pathList;
    for (uint16_t i = 0; i < numdirs; ++i) {
        uint8_t rawdir = msg.getByte();
        switch (rawdir) {
        case 1: pathList.push_back(DIRECTION_EAST); break;
        case 2: pathList.push_back(DIRECTION_NORTHEAST); break;
        case 3: pathList.push_back(DIRECTION_NORTH); break;
        case 4: pathList.push_back(DIRECTION_NORTHWEST); break;
        case 5: pathList.push_back(DIRECTION_WEST); break;
        case 6: pathList.push_back(DIRECTION_SOUTHWEST); break;
        case 7: pathList.push_back(DIRECTION_SOUTH); break;
        case 8: pathList.push_back(DIRECTION_SOUTHEAST); break;
        default: break; // Optionally handle unexpected values
        }
    }

    // Convert list to vector
    std::vector<Direction> pathVector(pathList.begin(), pathList.end());
    std::reverse(pathVector.begin(), pathVector.end());  // this is the tricky part about it that might mess up with your autowalk (pathfinding execution but pathfinding itself will be working correctly) (it have to be executed in reverse of list)

    uint32_t playerId = player->getID();

    auto self = getThis();
    g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::WalkingC, self, playerWalkId, predictiveWalkId, playerId, playerPosition, flags, std::move(pathVector))));
}

void ProtocolGame::WalkingC(uint32_t playerWalkId, int32_t predictiveWalkId, uint32_t playerId, Position playerPosition, uint8_t flags, std::vector<Direction> path)
{
bool preWalk = flags & 0x01;
Position destination = getNextPosition(path.front(), playerPosition);
if (preWalk && predictiveWalkId < walkMatrix.get(destination)) {
    walkId += 1;
    sendWalkId();
    return;
}

if (playerWalkId < walkId) {
    // this walk has been sent before player received previous newCancelWalk, so it's invalid, ignore it
    return;
}

g_game.playerNewWalk(playerId, playerPosition, flags, path);
}
void ProtocolGame::checkPredictiveWalking(const Position& pos)
{
    if (!otclientV8)
        return;

    if (walkMatrix.inRange(pos) && !g_game.map.canWalkTo(*player, pos)) {
        int newValue = walkMatrix.update(pos);
        sendPredictiveCancel(pos, newValue);
    }
}

void ProtocolGame::sendPredictiveCancel(const Position& pos, int value)
{
    if (!otclientV8)
        return;

    NetworkMessage msg;
    msg.addByte(0x46);
    msg.addPosition(pos);
    msg.addByte(player->getDirection());
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendWalkId()
{
    if (!otclientV8)
        return;

    NetworkMessage msg;
    msg.addByte(0x47);
    msg.add<uint32_t>(walkId);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendNewCancelWalk()
{
    if (!otclientV8)
        return;

    NetworkMessage msg;
    msg.addByte(0x45);
    msg.addByte(player->getDirection());
    writeToOutputBuffer(msg);
    walkId += 1;
    sendWalkId();
}

and this is my playerNewWalk function as well


Code:
void Game::playerNewWalk(uint32_t playerId, Position pos, uint8_t flags, std::vector<Direction>& listDir)
{
    Player* player = getPlayerByID(playerId);
    if (!player) {
        return;
    }

    player->resetIdleTime();
    player->setNextWalkTask(nullptr);
    player->setNextWalkActionTask(nullptr);

    // bool withPreWalk = flags & 0x01;
    bool autoWalk = flags & 0x04;

    if (pos.x != 0 && pos.y != 0 && pos != player->getPosition()) {
        auto& dirs = player->getListWalkDir();
        Position nextpos = player->getPosition();

        int limit = 3;
        for (auto& dir : dirs) {
            nextpos = getNextPosition(dir, nextpos);
            if (--limit == 0) break;
        }

        if (!autoWalk) {
            // Manual walk desync, check if it can be fixed
            if (limit == 0 || nextpos != pos) {
                player->sendNewCancelWalk();
                return;
            }

            // Append listDir to dirs
            dirs.insert(dirs.end(), listDir.begin(), listDir.end());
            return;
        }
        else {
            // Auto walk desync, check if it can be fixed
            if (limit > 0 && nextpos == pos) {
                // Append listDir to dirs
                dirs.insert(dirs.end(), listDir.begin(), listDir.end());
                return;
            }

            // Can't be fixed, so maybe find another way
            // WARNING: This loop may use extra CPU but improves autowalk (map click) much better
            for (int x = 0; x < 3; ++x) {
                if (listDir.empty()) {
                    player->sendNewCancelWalk();
                    return;
                }

                // Convert listDir to std::list for easier manipulation
                std::list<Direction> tempList(listDir.begin(), listDir.end());

                // Remove directions from the front of tempList
                for (int i = 0; i < 2; ++i) {
                    if (tempList.empty())
                        break;
                    pos = getNextPosition(tempList.front(), pos);
                    tempList.pop_front();
                }

                std::vector<Direction> newPath;
                if (player->getPathTo(pos, newPath, 0, 0, true, true)) {
                    // Reverse the path in place
                    std::reverse(newPath.begin(), newPath.end());

                    // Convert newPath to std::list and insert at the beginning of tempList
                    tempList.insert(tempList.begin(), newPath.begin(), newPath.end());

                    // Update the original listDir
                    listDir.assign(tempList.begin(), tempList.end());
                    break;
                }
            }
        }
    }

    player->startAutoWalk(listDir);
}

i made sure it would adapt with default tfs 1.4.2 so any changes it required is done from inside the functions itself, the cases that might show harsh I/O is not because it's only at special situations that might happen 5 times / minute with 100 players autoWalking, but in general it is a very smooth walking

ps: you just need to replace those functions but the rest should be direct difference between the original commit and your source, after that use those functions to solve the errors, (don't change std::list keep it vector because the new functions are customized to support vector dir list)
 
well you have to do a very simple trick, first remove the trytofixautowalk thing it eats cpu when many are online then use this function it converts and reverts back between vector and list because using vector here wouldn't be nice to erase and insert in walking instructions sent by client


this is my protocolgame.cpp functions i think its easy to declare them by yourself in header file
LUA:
void ProtocolGame::parseNewWalking(NetworkMessage& msg)
{
    uint32_t playerWalkId = msg.get<uint32_t>();
    int32_t predictiveWalkId = msg.get<int32_t>(); // extension for proxy system, currently not used
    Position playerPosition = msg.getPosition(); // local player position before moving, including prewalk
    uint8_t flags = msg.getByte(); // 0x01 - prewalk, 0x02 - autowalk

    uint16_t numdirs = msg.get<uint16_t>();
    if (numdirs == 0 || numdirs > 4096) {
        return;
    }

    std::list<Direction> pathList;
    for (uint16_t i = 0; i < numdirs; ++i) {
        uint8_t rawdir = msg.getByte();
        switch (rawdir) {
        case 1: pathList.push_back(DIRECTION_EAST); break;
        case 2: pathList.push_back(DIRECTION_NORTHEAST); break;
        case 3: pathList.push_back(DIRECTION_NORTH); break;
        case 4: pathList.push_back(DIRECTION_NORTHWEST); break;
        case 5: pathList.push_back(DIRECTION_WEST); break;
        case 6: pathList.push_back(DIRECTION_SOUTHWEST); break;
        case 7: pathList.push_back(DIRECTION_SOUTH); break;
        case 8: pathList.push_back(DIRECTION_SOUTHEAST); break;
        default: break; // Optionally handle unexpected values
        }
    }

    // Convert list to vector
    std::vector<Direction> pathVector(pathList.begin(), pathList.end());
    std::reverse(pathVector.begin(), pathVector.end());  // this is the tricky part about it that might mess up with your autowalk (pathfinding execution but pathfinding itself will be working correctly) (it have to be executed in reverse of list)

    uint32_t playerId = player->getID();

    auto self = getThis();
    g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::WalkingC, self, playerWalkId, predictiveWalkId, playerId, playerPosition, flags, std::move(pathVector))));
}

void ProtocolGame::WalkingC(uint32_t playerWalkId, int32_t predictiveWalkId, uint32_t playerId, Position playerPosition, uint8_t flags, std::vector<Direction> path)
{
bool preWalk = flags & 0x01;
Position destination = getNextPosition(path.front(), playerPosition);
if (preWalk && predictiveWalkId < walkMatrix.get(destination)) {
    walkId += 1;
    sendWalkId();
    return;
}

if (playerWalkId < walkId) {
    // this walk has been sent before player received previous newCancelWalk, so it's invalid, ignore it
    return;
}

g_game.playerNewWalk(playerId, playerPosition, flags, path);
}
void ProtocolGame::checkPredictiveWalking(const Position& pos)
{
    if (!otclientV8)
        return;

    if (walkMatrix.inRange(pos) && !g_game.map.canWalkTo(*player, pos)) {
        int newValue = walkMatrix.update(pos);
        sendPredictiveCancel(pos, newValue);
    }
}

void ProtocolGame::sendPredictiveCancel(const Position& pos, int value)
{
    if (!otclientV8)
        return;

    NetworkMessage msg;
    msg.addByte(0x46);
    msg.addPosition(pos);
    msg.addByte(player->getDirection());
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendWalkId()
{
    if (!otclientV8)
        return;

    NetworkMessage msg;
    msg.addByte(0x47);
    msg.add<uint32_t>(walkId);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendNewCancelWalk()
{
    if (!otclientV8)
        return;

    NetworkMessage msg;
    msg.addByte(0x45);
    msg.addByte(player->getDirection());
    writeToOutputBuffer(msg);
    walkId += 1;
    sendWalkId();
}

and this is my playerNewWalk function as well


Code:
void Game::playerNewWalk(uint32_t playerId, Position pos, uint8_t flags, std::vector<Direction>& listDir)
{
    Player* player = getPlayerByID(playerId);
    if (!player) {
        return;
    }

    player->resetIdleTime();
    player->setNextWalkTask(nullptr);
    player->setNextWalkActionTask(nullptr);

    // bool withPreWalk = flags & 0x01;
    bool autoWalk = flags & 0x04;

    if (pos.x != 0 && pos.y != 0 && pos != player->getPosition()) {
        auto& dirs = player->getListWalkDir();
        Position nextpos = player->getPosition();

        int limit = 3;
        for (auto& dir : dirs) {
            nextpos = getNextPosition(dir, nextpos);
            if (--limit == 0) break;
        }

        if (!autoWalk) {
            // Manual walk desync, check if it can be fixed
            if (limit == 0 || nextpos != pos) {
                player->sendNewCancelWalk();
                return;
            }

            // Append listDir to dirs
            dirs.insert(dirs.end(), listDir.begin(), listDir.end());
            return;
        }
        else {
            // Auto walk desync, check if it can be fixed
            if (limit > 0 && nextpos == pos) {
                // Append listDir to dirs
                dirs.insert(dirs.end(), listDir.begin(), listDir.end());
                return;
            }

            // Can't be fixed, so maybe find another way
            // WARNING: This loop may use extra CPU but improves autowalk (map click) much better
            for (int x = 0; x < 3; ++x) {
                if (listDir.empty()) {
                    player->sendNewCancelWalk();
                    return;
                }

                // Convert listDir to std::list for easier manipulation
                std::list<Direction> tempList(listDir.begin(), listDir.end());

                // Remove directions from the front of tempList
                for (int i = 0; i < 2; ++i) {
                    if (tempList.empty())
                        break;
                    pos = getNextPosition(tempList.front(), pos);
                    tempList.pop_front();
                }

                std::vector<Direction> newPath;
                if (player->getPathTo(pos, newPath, 0, 0, true, true)) {
                    // Reverse the path in place
                    std::reverse(newPath.begin(), newPath.end());

                    // Convert newPath to std::list and insert at the beginning of tempList
                    tempList.insert(tempList.begin(), newPath.begin(), newPath.end());

                    // Update the original listDir
                    listDir.assign(tempList.begin(), tempList.end());
                    break;
                }
            }
        }
    }

    player->startAutoWalk(listDir);
}

i made sure it would adapt with default tfs 1.4.2 so any changes it required is done from inside the functions itself, the cases that might show harsh I/O is not because it's only at special situations that might happen 5 times / minute with 100 players autoWalking, but in general it is a very smooth walking

ps: you just need to replace those functions but the rest should be direct difference between the original commit and your source, after that use those functions to solve the errors, (don't change std::list keep it vector because the new functions are customized to support vector dir list)
When you loop, you can loop from the end. So no reverse needed then - in
ProtocolGame::parseNewWalking.


C++:
std::vector<Direction> path;
    path.resize(numdirs, DIRECTION_NORTH);
    for (size_t i = numdirs; --i < numdirs;) {
        uint8_t rawdir = msg.getByte();
        switch (rawdir) {
            case 1: path[i] = DIRECTION_EAST; break;
            case 2: path[i] = DIRECTION_NORTHEAST; break;
            case 3: path[i] = DIRECTION_NORTH; break;
            case 4: path[i] = DIRECTION_NORTHWEST; break;
            case 5: path[i] = DIRECTION_WEST; break;
            case 6: path[i] = DIRECTION_SOUTHWEST; break;
            case 7: path[i] = DIRECTION_SOUTH; break;
            case 8: path[i] = DIRECTION_SOUTHEAST; break;
            default: break;
        }
    }
enjoy. ;)
 
When you loop, you can loop from the end. So no reverse needed then - in
ProtocolGame::parseNewWalking.


C++:
std::vector<Direction> path;
    path.resize(numdirs, DIRECTION_NORTH);
    for (size_t i = numdirs; --i < numdirs;) {
        uint8_t rawdir = msg.getByte();
        switch (rawdir) {
            case 1: path[i] = DIRECTION_EAST; break;
            case 2: path[i] = DIRECTION_NORTHEAST; break;
            case 3: path[i] = DIRECTION_NORTH; break;
            case 4: path[i] = DIRECTION_NORTHWEST; break;
            case 5: path[i] = DIRECTION_WEST; break;
            case 6: path[i] = DIRECTION_SOUTHWEST; break;
            case 7: path[i] = DIRECTION_SOUTH; break;
            case 8: path[i] = DIRECTION_SOUTHEAST; break;
            default: break;
        }
    }
enjoy. ;)
Did it work correctly with map clicks? i remember when i did that in june 2024 it took inverse directions for map click walking..


ah i see now, that is why you used index of i, i can't remember what i used but it was either pop/push_back i didn't even trace the new walking code before.
 
Back
Top