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

Lua A way to change storage when a player loses connection.

OTcreator

Well-Known Member
Joined
Feb 14, 2022
Messages
482
Solutions
1
Reaction score
53
Hi,
Is there any other way that will put less load on the server to change the player's storage when he loses connection (when he has IP=0) ?

I created such a script in globalevents, but it has to be called then every second.
(The idea is to protect against xlog, because if I want to add this to the onLogin function, the player who gives xlog is still logged in, so the storage will not change).

LUA:
for _, cid in ipairs(getPlayersOnline()) do
    local playerIP = getPlayerIP(cid)
    
    if (playerIP == "0.0.0.0") then
        if (getPlayerStorageValue(cid, 45971) == 1) then
            setPlayerStorageValue(cid, 45971, -1)
        end
    end
end
 
Last edited:
Can u explain how you want it to work?
Hi,
Is there any other way that will put less load on the server to change the player's storage when he loses connection (when he has IP=0) ?

I created such a script in globalevents, but it has to be called then every second.
(The idea is to protect against xlog, because if I want to add this to the onLogin function, the player who gives xlog is still logged in, so the storage will not change).

LUA:
for _, cid in ipairs(getPlayersOnline()) do
    local playerIP = getPlayerIP(cid)
  
    if (playerIP == "0.0.0.0") then
        if (getPlayerStorageValue(cid, 45971) == 1) then
            setPlayerStorageValue(cid, 45971, -1)
        end
    end
end
You could check last ping on death to give players protection, or just give protection for all players when server is under DDoS attack instead.
 
Last edited:
Can u explain how you want it to work?

You could check last ping on death to give players protection, or just give protection for all players when server is under DDoS attack instead.

Hi, the idea is , to prevent the possibility of giving xlog (because then the function onLogin, or onLogout are not called because the player remains still in the game).

How would this work?
If a player has IP=0 or simply loses connection, his storagevalue is supposed to change.
 
actually i just checked
C++:
void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem)
{
    // OTCv8 features and extended opcodes
    if (otclientV8 || operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
        if (otclientV8)
            sendFeatures();
    }

    //dispatcher thread
    Player* foundPlayer = g_game.getPlayerByName(name);
    if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
        player = new Player(getThis());
        player->setName(name);

        player->incrementReferenceCounter();
        player->setID();

        if (!IOLoginData::preloadPlayer(player, name)) {
            disconnectClient("Your character could not be loaded.");
            return;
        }

        if (IOBan::isPlayerNamelocked(player->getGUID())) {
            disconnectClient("Your character has been namelocked.");
            return;
        }

        if (g_game.getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) {
            disconnectClient("The game is just going down.\nPlease try again later.");
            return;
        }

        if (g_game.getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) {
            disconnectClient("Server is currently closed.\nPlease try again later.");
            return;
        }

        if (g_config.getBoolean(ConfigManager::ONE_PLAYER_ON_ACCOUNT) && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && g_game.getPlayerByAccount(player->getAccount())) {
            disconnectClient("You may only login with one character\nof your account at the same time.");
            return;
        }

        if (!player->hasFlag(PlayerFlag_CannotBeBanned)) {
            BanInfo banInfo;
            if (IOBan::isAccountBanned(accountId, banInfo)) {
                if (banInfo.reason.empty()) {
                    banInfo.reason = "(none)";
                }

                if (banInfo.expiresAt > 0) {
                    disconnectClient(fmt::format("Your account has been banned until {:s} by {:s}.\n\nReason specified:\n{:s}", formatDateShort(banInfo.expiresAt), banInfo.bannedBy, banInfo.reason));
                }
                else {
                    disconnectClient(fmt::format("Your account has been permanently banned by {:s}.\n\nReason specified:\n{:s}", banInfo.bannedBy, banInfo.reason));
                }
                return;
            }
        }

        if (std::size_t currentSlot = clientLogin(*player)) {
            uint8_t retryTime = getWaitTime(currentSlot);
            auto output = OutputMessagePool::getOutputMessage();
            output->addByte(0x16);
            output->addString(fmt::format("Too many players online.\nYou are at place {:d} on the waiting list.", currentSlot));
            output->addByte(retryTime);
            send(output);
            disconnect();
            return;
        }

        if (!IOLoginData::loadPlayerById(player, player->getGUID())) {
            disconnectClient("Your character could not be loaded.");
            return;
        }

        player->setOperatingSystem(operatingSystem);

        if (!g_game.placeCreature(player, player->getLoginPosition())) {
            if (!g_game.placeCreature(player, player->getTemplePosition(), false, true)) {
                disconnectClient("Temple position is wrong. Contact the administrator.");
                return;
            }
        }

        if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
            player->registerCreatureEvent("ExtendedOpcode");
        }

        player->lastIP = player->getIP();
        player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
        acceptPackets = true;
    }
    else {
        if (eventConnect != 0 || !g_config.getBoolean(ConfigManager::REPLACE_KICK_ON_LOGIN)) {
            //Already trying to connect
            disconnectClient("You are already logged in.");
            return;
        }

        if (foundPlayer->client) {
            foundPlayer->disconnect();
            foundPlayer->isConnecting = true;

            eventConnect = g_scheduler.addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getID(), operatingSystem)));
        }
        else {
            connect(foundPlayer->getID(), operatingSystem);
        }
    }
    OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this());
}
t
his is only time player.h function calls for disconnect so you need to overwrite the protocol and connection ones along with including player.h and referencing the player object within the protocol/connection disconnect() to then change its storage instead.
 
Last edited:
player.h has disconnect functionality that disconnects client and its connection and once connection closes the IP is not necessary anymore.
C++:
void disconnect() {
    if (client) {
        this->addStorageValue(45971, 1, false);
        client->disconnect();
    }
}
perhaps?
since it calls
C++:
void disconnect() const {
    if (auto connection = getConnection()) {
        connection->close();
    }
}
the only thing is that it will set that storage when logging out aswell I guess but if its death related you can then set the storage back again on login?

That's exactly the point.
If it logs out, also can change the memory to zero, even better.
The same thing if the connection is lost.

I was looking, but in protocolgame and was supposed to do something like this, I thought it would help, but I did not test.

C++:
void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem)
{
    eventConnect = 0;

    Player* foundPlayer = g_game.getPlayerByID(playerId);
    if (!foundPlayer || foundPlayer->client) {
        disconnectClient("You are already logged in.");
        return;
    }

    if (isConnectionExpired()) {
        // ProtocolGame::release() has been called at this point and the Connection object
        // no longer exists, so we return to prevent leakage of the Player.
        return;
    }

    player = foundPlayer;
    player->incrementReferenceCounter();

    g_chat->removeUserFromAllChannels(*player);
    player->clearModalWindows();
    player->setOperatingSystem(operatingSystem);
    player->isConnecting = false;

    // Now we set the storage value for the player -- I ADD THIS LINE
    player->setStorageValue(YourStorageKey, YourStorageValue); 

    player->client = getThis();
    sendAddCreature(player, player->getPosition(), 0);
    player->lastIP = player->getIP();
    player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
    player->resetIdleTime();
    acceptPackets = true;
}
 
you would need to implement a function to check for dropped connections or something (other way) to track the players or overload the protocol/connection fucntions to detect drop then but I think your original onThink shouldnt be an issue since its only gonna check all ip addresses of all online players every second it does not seem like a big strain when its only referencing the IP of all players at one time and nothing else from the player object.
 
you would need to implement a function to check for dropped connections or something (other way) to track the players or overload the protocol/connection fucntions to detect drop then but I think your original onThink shouldnt be an issue since its only gonna check all ip addresses of all online players every second it does not seem like a big strain when its only referencing the IP of all players at one time and nothing else from the player object.
I understand, but wouldn't your option be better, it's all about changes in player.h?
 
I understand, but wouldn't your option be better, it's all about changes in player.h?
no the player.h is only refered to when player actually disconnects the Disconnect (e.g when player connects with second client as it has to "disconnect" him then "reconnect" him) function from protocol and connection is the one that refers to actual socket disconnection when players IP is 0.0.0.0 that you refer to.
you could technically make a function inside of player to detect the ip loss aswell but it would be a better option to sort it when the socket is disconnected.

C++:
if (isConnectionExpired()) {
        // ProtocolGame::release() has been called at this point and the Connection object
        // no longer exists, so we return to prevent leakage of the Player.
        return;
    }

ProtocolGame::release()
so probably in here somewhere you would need to reference player object last time before its released

try this:
C++:
void ProtocolGame::release()
{
    //dispatcher thread
    if (player && player->client == shared_from_this()) {
        player->addStorageValue(45971, -1, false);
        player->client.reset();
        player->decrementReferenceCounter();
        player = nullptr;
    }

    OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
    Protocol::release();
}
then onLogin add check to check the storage value and see if it properly set to -1 before you set it to 1 aka logged in if thats what you have in login.lua
this should effectively set the storage before releasing the player pointer from the game.
just to point out even if player logs out the storage will set to -1 but it shouldnt be an issue since its a logout and not death right? you're implementing some form of death protection or something?
just make sure that line is before client reset, the decrease of referencecounter and removing the pointer to player object. if you put it after the engine will either crash or the storage wont set because of wrong player object being referenced after being removed.
just imagine you have a Paper that has all player information you want to add to that paper your storage before you put it back in file cabinet. if it does not work try changin the boolean to true I have not checked how the boolean operates but its something with player being inside of the game and not inside of game I just assumed false since player is technically not in the game but character might still be there if its non logout tile etc.
also in that case you have conflict if player does it on non logout zone he will be in game permanently with full death loss its something to think about you can add additional logic checks for that since player is referenced in protocol.

just had look at isLogin seems like its executed when player logs in? I see multiple references to that when sending magic effect so i assume thats the case here.
 
Last edited:
no the player.h is only refered to when player actually disconnects the Disconnect (e.g when player connects with second client as it has to "disconnect" him then "reconnect" him) function from protocol and connection is the one that refers to actual socket disconnection when players IP is 0.0.0.0 that you refer to.
you could technically make a function inside of player to detect the ip loss aswell but it would be a better option to sort it when the socket is disconnected.

C++:
if (isConnectionExpired()) {
        // ProtocolGame::release() has been called at this point and the Connection object
        // no longer exists, so we return to prevent leakage of the Player.
        return;
    }

ProtocolGame::release()
so probably in here somewhere you would need to reference player object last time before its released

try this:
C++:
void ProtocolGame::release()
{
    //dispatcher thread
    if (player && player->client == shared_from_this()) {
        player->addStorageValue(45971, -1, false);
        player->client.reset();
        player->decrementReferenceCounter();
        player = nullptr;
    }

    OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
    Protocol::release();
}
then onLogin add check to check the storage value and see if it properly set to -1 before you set it to 1 aka logged in if thats what you have in login.lua
this should effectively set the storage before releasing the player pointer from the game.
just to point out even if player logs out the storage will set to -1 but it shouldnt be an issue since its a logout and not death right? you're implementing some form of death protection or something?
just make sure that line is before client reset, the decrease of referencecounter and removing the pointer to player object. if you put it after the engine will either crash or the storage wont set because of wrong player object being referenced after being removed.
just imagine you have a Paper that has all player information you want to add to that paper your storage before you put it back in file cabinet. if it does not work try changin the boolean to true I have not checked how the boolean operates but its something with player being inside of the game and not inside of game I just assumed false since player is technically not in the game but character might still be there if its non logout tile etc.
also in that case you have conflict if player does it on non logout zone he will be in game permanently with full death loss its something to think about you can add additional logic checks for that since player is referenced in protocol.

just had look at isLogin seems like its executed when player logs in? I see multiple references to that when sending magic effect so i assume thats the case here.

Just all the time the point is that the player who gives xlog and quickly returns on another client to the game, all the time is logged in and then does not call the function onLogin once again.

If it won't actually cause major delays with 50 or 100 players (globalevent) maybe it's a shame to bother?
 
Just all the time the point is that the player who gives xlog and quickly returns on another client to the game, all the time is logged in and then does not call the function onLogin once again.

If it won't actually cause major delays with 50 or 100 players (globalevent) maybe it's a shame to bother?
void ProtocolGame::release()
this function is called when the connection is released. e.g xlog/logout.

isLogin is the boolean in addStorage i said try false and true in case it does not work with false but should work with false.

in the end you have 1 execution when it happens instead playercount every second.
 
Back
Top