• 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!

TFS 1.X+ Help, protocolstatus.cpp

Piifafa

Member
Joined
Apr 16, 2023
Messages
67
Reaction score
16
I really need to organize my server for correct player counts, because I'm an honest man, I don't admit passing wrong counts to lists, However, it counts all mc and I know it shouldn't, I've already limited the number of mc with scripts.
Lua:
/*
 * YurOTS, a free game server emulator
 * Official Repository on Github <https://github.com/rodolfoaugusto/yurOTS-server>
 * Copyright (C) 2020 - Rodi <https://github.com/rodolfoaugusto>
 * A fork of The Forgotten Server(Mark Samman) branch 1.2 and part of Nostalrius(Alejandro Mujica) repositories.
 *
 * The MIT License (MIT). Copyright © 2020 <YurOTS>
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

#include "otpch.h"

#include "protocolstatus.h"
#include "configmanager.h"
#include "game.h"
#include "outputmessage.h"

extern ConfigManager g_config;
extern Game g_game;

std::map<uint32_t, int64_t> ProtocolStatus::ipConnectMap;
const uint64_t ProtocolStatus::start = OTSYS_TIME();
int32_t extraOn = g_config.getNumber(ConfigManager::EXTRA_ONLINE);

enum RequestedInfo_t : uint16_t {
    REQUEST_BASIC_SERVER_INFO = 1 << 0,
    REQUEST_OWNER_SERVER_INFO = 1 << 1,
    REQUEST_MISC_SERVER_INFO = 1 << 2,
    REQUEST_PLAYERS_INFO = 1 << 3,
    REQUEST_MAP_INFO = 1 << 4,
    REQUEST_EXT_PLAYERS_INFO = 1 << 5,
    REQUEST_PLAYER_STATUS_INFO = 1 << 6,
    REQUEST_SERVER_SOFTWARE_INFO = 1 << 7,
};

void ProtocolStatus::onRecvFirstMessage(NetworkMessage& msg)
{
    uint32_t ip = getIP();
    if (ip != 0x0100007F) {
        std::string ipStr = convertIPToString(ip);
        if (ipStr != g_config.getString(ConfigManager::IP)) {
            std::map<uint32_t, int64_t>::const_iterator it = ipConnectMap.find(ip);
            if (it != ipConnectMap.end() && (OTSYS_TIME() < (it->second + g_config.getNumber(ConfigManager::STATUSQUERY_TIMEOUT)))) {
                disconnect();
                return;
            }
        }
    }

    ipConnectMap[ip] = OTSYS_TIME();

    switch (msg.getByte()) {
        //XML info protocol
        case 0xFF: {
            if (msg.getString(4) == "info") {
                g_dispatcher.addTask(createTask(std::bind(&ProtocolStatus::sendStatusString,
                                      std::static_pointer_cast<ProtocolStatus>(shared_from_this()))));
                return;
            }
            break;
        }

        //Another ServerInfo protocol
        case 0x01: {
            uint16_t requestedInfo = msg.get<uint16_t>(); // only a Byte is necessary, though we could add new info here
            std::string characterName;
            if (requestedInfo & REQUEST_PLAYER_STATUS_INFO) {
                characterName = msg.getString();
            }
            g_dispatcher.addTask(createTask(std::bind(&ProtocolStatus::sendInfo, std::static_pointer_cast<ProtocolStatus>(shared_from_this()),
                                  requestedInfo, characterName)));
            return;
        }

        default:
            break;
    }
    disconnect();
}

void ProtocolStatus::sendStatusString()
{
    auto output = OutputMessagePool::getOutputMessage();

    setRawMessages(true);

    pugi::xml_document doc;

    pugi::xml_node decl = doc.prepend_child(pugi::node_declaration);
    decl.append_attribute("version") = "1.0";

    pugi::xml_node tsqp = doc.append_child("tsqp");
    tsqp.append_attribute("version") = "1.0";

    pugi::xml_node serverinfo = tsqp.append_child("serverinfo");
    uint64_t uptime = (OTSYS_TIME() - ProtocolStatus::start) / 1000;
    serverinfo.append_attribute("uptime") = std::to_string(uptime).c_str();
    serverinfo.append_attribute("ip") = g_config.getString(ConfigManager::IP).c_str();
    serverinfo.append_attribute("servername") = g_config.getString(ConfigManager::SERVER_NAME).c_str();
    serverinfo.append_attribute("port") = std::to_string(g_config.getNumber(ConfigManager::LOGIN_PORT)).c_str();
    serverinfo.append_attribute("location") = g_config.getString(ConfigManager::LOCATION).c_str();
    serverinfo.append_attribute("url") = g_config.getString(ConfigManager::URL).c_str();
    serverinfo.append_attribute("server") = STATUS_SERVER_NAME;
    serverinfo.append_attribute("version") = STATUS_SERVER_VERSION;
    serverinfo.append_attribute("client") = CLIENT_VERSION_STR;

    pugi::xml_node owner = tsqp.append_child("owner");
    owner.append_attribute("name") = g_config.getString(ConfigManager::OWNER_NAME).c_str();
    owner.append_attribute("email") = g_config.getString(ConfigManager::OWNER_EMAIL).c_str();

    pugi::xml_node players = tsqp.append_child("players");

    uint32_t real = g_game.getPlayersOnline() + extraOn;

    /*
    std::map<uint32_t, uint32_t> listIP;

    for (const auto& it : g_game.getPlayers()) {
        if (it.second->getIP() != 0) {
            auto ip = listIP.find(it.second->getIP());
            if (ip != listIP.end()) {
                listIP[it.second->getIP()]++;
                if (listIP[it.second->getIP()] < 5) {
                    real++;
                }
            } else {
                listIP[it.second->getIP()] = 1;
                real++;
            }
        }
    }
    */
    players.append_attribute("online") = (std::to_string(real).c_str());

    players.append_attribute("max") = std::to_string(g_config.getNumber(ConfigManager::MAX_PLAYERS)).c_str();
    players.append_attribute("peak") = std::to_string(g_game.getPlayersRecord()).c_str();

    pugi::xml_node monsters = tsqp.append_child("monsters");
    monsters.append_attribute("total") = std::to_string(g_game.getMonstersOnline()).c_str();

    pugi::xml_node npcs = tsqp.append_child("npcs");
    npcs.append_attribute("total") = std::to_string(g_game.getNpcsOnline()).c_str();

    pugi::xml_node rates = tsqp.append_child("rates");

    rates.append_attribute("experience") = std::to_string(g_config.getNumber(ConfigManager::RATE_EXPERIENCE)).c_str();
    rates.append_attribute("skill") = std::to_string(g_config.getNumber(ConfigManager::RATE_SKILL)).c_str();
    rates.append_attribute("magic") = std::to_string(g_config.getNumber(ConfigManager::RATE_MAGIC)).c_str();
   
    rates.append_attribute("loot") = std::to_string(g_config.getNumber(ConfigManager::RATE_LOOT)).c_str();
    rates.append_attribute("spawn") = std::to_string(g_config.getNumber(ConfigManager::RATE_SPAWN)).c_str();

    pugi::xml_node map = tsqp.append_child("map");
    map.append_attribute("name") = g_config.getString(ConfigManager::MAP_NAME).c_str();
    map.append_attribute("author") = g_config.getString(ConfigManager::MAP_AUTHOR).c_str();

    uint32_t mapWidth, mapHeight;
    g_game.getMapDimensions(mapWidth, mapHeight);
    map.append_attribute("width") = std::to_string(mapWidth).c_str();
    map.append_attribute("height") = std::to_string(mapHeight).c_str();

    pugi::xml_node motd = tsqp.append_child("motd");
    motd.text() = g_config.getString(ConfigManager::MOTD).c_str();

    std::ostringstream ss;
    doc.save(ss, "", pugi::format_raw);

    std::string data = ss.str();
    output->addBytes(data.c_str(), data.size());
    send(output);
    disconnect();
}

void ProtocolStatus::sendInfo(uint16_t requestedInfo, const std::string& characterName)
{
    auto output = OutputMessagePool::getOutputMessage();

    if (requestedInfo & REQUEST_BASIC_SERVER_INFO) {
        output->addByte(0x10);
        output->addString(g_config.getString(ConfigManager::SERVER_NAME));
        output->addString(g_config.getString(ConfigManager::IP));
        output->addString(std::to_string(g_config.getNumber(ConfigManager::LOGIN_PORT)));
    }

    if (requestedInfo & REQUEST_OWNER_SERVER_INFO) {
        output->addByte(0x11);
        output->addString(g_config.getString(ConfigManager::OWNER_NAME));
        output->addString(g_config.getString(ConfigManager::OWNER_EMAIL));
    }

    if (requestedInfo & REQUEST_MISC_SERVER_INFO) {
        output->addByte(0x12);
        output->addString(g_config.getString(ConfigManager::MOTD));
        output->addString(g_config.getString(ConfigManager::LOCATION));
        output->addString(g_config.getString(ConfigManager::URL));
        output->add<uint64_t>((OTSYS_TIME() - ProtocolStatus::start) / 1000);
    }

    if (requestedInfo & REQUEST_PLAYERS_INFO) {
        output->addByte(0x20);
        output->add<uint32_t>((g_game.getPlayersOnline() + extraOn));
        output->add<uint32_t>(g_config.getNumber(ConfigManager::MAX_PLAYERS));
        output->add<uint32_t>(g_game.getPlayersRecord());
    }

    if (requestedInfo & REQUEST_MAP_INFO) {
        output->addByte(0x30);
        output->addString(g_config.getString(ConfigManager::MAP_NAME));
        output->addString(g_config.getString(ConfigManager::MAP_AUTHOR));
        uint32_t mapWidth, mapHeight;
        g_game.getMapDimensions(mapWidth, mapHeight);
        output->add<uint16_t>(mapWidth);
        output->add<uint16_t>(mapHeight);
    }

    if (requestedInfo & REQUEST_EXT_PLAYERS_INFO) {
        output->addByte(0x21); // players info - online players list

        const auto& players = g_game.getPlayers();
        output->add<uint32_t>(players.size());
        for (const auto& it : players) {
            output->addString(it.second->getName());
            output->add<uint32_t>(it.second->getLevel());
        }
    }

    if (requestedInfo & REQUEST_PLAYER_STATUS_INFO) {
        output->addByte(0x22); // players info - online status info of a player
        if (g_game.getPlayerByName(characterName) != nullptr) {
            output->addByte(0x01);
        } else {
            output->addByte(0x00);
        }
    }

    if (requestedInfo & REQUEST_SERVER_SOFTWARE_INFO) {
        output->addByte(0x23); // server software info
        output->addString(STATUS_SERVER_NAME);
        output->addString(STATUS_SERVER_VERSION);
        output->addString(CLIENT_VERSION_STR);
    }
    send(output);
    disconnect();
}


I turned this on and doubled the number of players, if there were 40 I counted 80 and so on.
Lua:
    /*
    std::map<uint32_t, uint32_t> listIP;

    for (const auto& it : g_game.getPlayers()) {
        if (it.second->getIP() != 0) {
            auto ip = listIP.find(it.second->getIP());
            if (ip != listIP.end()) {
                listIP[it.second->getIP()]++;
                if (listIP[it.second->getIP()] < 5) {
                    real++;
                }
            } else {
                listIP[it.second->getIP()] = 1;
                real++;
            }
        }
    }
    */

So I tried to make the fix using this. But a very strange error occurs.


Lua:
uint32_t real = 0;

std::map<uint32_t, uint32_t> listIP;

for (const auto& it : g_game.getPlayers()) {
    if (it.second->getIdleTime() < 960000 && it.second->getIP() != 0) {
        auto ip = listIP.find(it.second->getIP());
        if (ip != listIP.end()) {
            listIP[it.second->getIP()]++;
            if (listIP[it.second->getIP()] < 5) {
                real++;
            }
        }
        else {
            listIP[it.second->getIP()] = 1;
            real++;
        }
    }
}
players.append_attribute("online") = std::to_string(real).c_str();

Base using: yurOTS-server/src at master · rodolfoaugusto/yurOTS-server (https://github.com/rodolfoaugusto/yurOTS-server/tree/master/src)
Post automatically merged:

I ended up doing this, but now I don't know the owner of the list couldn't find the part he needed.
1696542986870.png
Lua:
    // Compliance to otservlist.org regulations
    // Dont count players with idletime over 15 minutes, count max 4 players per IP, count unique IPs
    // https://otland.net/threads/very-important-rules-change-on-otservlist-org.247531/
    // https://otland.net/threads/very-important-rules-change-on-otservlist-org.247531/post-2492577
    // No official statement for unique IP count ???
    uint32_t real = 0;
    uint32_t ips = 0;
    std::map<uint32_t, uint32_t> listIP;
    for (const auto& it : g_game.getPlayers()) {
            if (it.second->getIP() != 0) {
                    auto ip = listIP.find(it.second->getIP());
                    if (ip != listIP.end()) {
                            listIP[it.second->getIP()]++;
                            if (listIP[it.second->getIP()] < 5) {
                                    real++;
                            }
                    }
                    else {
                            listIP[it.second->getIP()] = 1;
                            real++;
                            ips++;
                    }
            }
    }
 

Attachments

Last edited:
Solution
As Znote commented in another thread. Here's the solution.
In your file protocolstatus.cpp.
Change this
C++:
uint32_t real = 0;

std::map<uint32_t, uint32_t> listIP;

for (const auto& it : g_game.getPlayers()) {
    if (it.second->getIdleTime() < 960000 && it.second->getIP() != 0) {
        auto ip = listIP.find(it.second->getIP());
        if (ip != listIP.end()) {
            listIP[it.second->getIP()]++;
            if (listIP[it.second->getIP()] < 5) {
                real++;
            }
        }
        else {
            listIP[it.second->getIP()] = 1;
            real++;
        }
    }
}
players.append_attribute("online") = std::to_string(real).c_str();

To this
C++:
    uint32_t real = 0;
    uint32_t ips = 0...
As Znote commented in another thread. Here's the solution.
In your file protocolstatus.cpp.
Change this
C++:
uint32_t real = 0;

std::map<uint32_t, uint32_t> listIP;

for (const auto& it : g_game.getPlayers()) {
    if (it.second->getIdleTime() < 960000 && it.second->getIP() != 0) {
        auto ip = listIP.find(it.second->getIP());
        if (ip != listIP.end()) {
            listIP[it.second->getIP()]++;
            if (listIP[it.second->getIP()] < 5) {
                real++;
            }
        }
        else {
            listIP[it.second->getIP()] = 1;
            real++;
        }
    }
}
players.append_attribute("online") = std::to_string(real).c_str();

To this
C++:
    uint32_t real = 0;
    uint32_t ips = 0;
    std::map<uint32_t, uint32_t> listIP;
    for (const auto& it : g_game.getPlayers()) {
            if (it.second->idleTime <= 900000 && it.second->getIP() != 0) {
                    auto ip = listIP.find(it.second->getIP());
                    if (ip != listIP.end()) {
                            listIP[it.second->getIP()]++;
                            if (listIP[it.second->getIP()] < 5) {
                                    real++;
                            }
                    }
                    else {
                            listIP[it.second->getIP()] = 1;
                            real++;
                            ips++;
                    }
            }
    }
    players.append_attribute("online") = std::to_string(real).c_str();
    players.append_attribute("unique") = std::to_string(ips).c_str();

And your file, player.h change the idleTime variable to be public instead of private.
Change this
C++:
    protected:
        int32_t idleTime = 0;

To this
C++:
  public:
    int32_t idleTime = 0;


Note: Just change the location from where the iddleTime variable is being set.
 
Solution
As Znote commented in another thread. Here's the solution.
In your file protocolstatus.cpp.
Change this
C++:
uint32_t real = 0;

std::map<uint32_t, uint32_t> listIP;

for (const auto& it : g_game.getPlayers()) {
    if (it.second->getIdleTime() < 960000 && it.second->getIP() != 0) {
        auto ip = listIP.find(it.second->getIP());
        if (ip != listIP.end()) {
            listIP[it.second->getIP()]++;
            if (listIP[it.second->getIP()] < 5) {
                real++;
            }
        }
        else {
            listIP[it.second->getIP()] = 1;
            real++;
        }
    }
}
players.append_attribute("online") = std::to_string(real).c_str();

To this
C++:
    uint32_t real = 0;
    uint32_t ips = 0;
    std::map<uint32_t, uint32_t> listIP;
    for (const auto& it : g_game.getPlayers()) {
            if (it.second->idleTime <= 900000 && it.second->getIP() != 0) {
                    auto ip = listIP.find(it.second->getIP());
                    if (ip != listIP.end()) {
                            listIP[it.second->getIP()]++;
                            if (listIP[it.second->getIP()] < 5) {
                                    real++;
                            }
                    }
                    else {
                            listIP[it.second->getIP()] = 1;
                            real++;
                            ips++;
                    }
            }
    }
    players.append_attribute("online") = std::to_string(real).c_str();
    players.append_attribute("unique") = std::to_string(ips).c_str();

And your file, player.h change the idleTime variable to be public instead of private.
Change this
C++:
    protected:
        int32_t idleTime = 0;

To this
C++:
  public:
    int32_t idleTime = 0;


Note: Just change the location from where the iddleTime variable is being set.
Thank you, it was the best solution I saw, now I'm waiting for the owner of the list but everything is ok, believe me!
 
Back
Top