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

[Modules] Rekzais OTClient Modules

The client now communicates with a separate web socket server and needs no communication to your otserver at all.
When its completely stupid logic - lets make a microservice out of it (in a different language ofc) and lets not validate anything anymore!
Zoomers are hella smart :cool:
 
I mean good for you guys if you do a serverside solution. Otclient already has this feature implemented for the bot so why not make use of it.

This is the easiest solution without taxing the server and without making any source edits.

Everything is open source and free to use. If you have a better solution, post it or open a pull request for it.

I want to make the modules as easy as possible to implement for server owners, that's why I chose a way that requires no source edits.
 
I mean good for you guys if you do a serverside solution. Otclient already has this feature implemented for the bot so why not make use of it.

This is the easiest solution without taxing the server and without making any source edits.

Everything is open source and free to use. If you have a better solution, post it or open a pull request for it.

I want to make the modules as easy as possible to implement for server owners, that's why I chose a way that requires no source edits.
I am just giving you a hint that your solution is a bad workaround. People can spoof their position and you have no way of validating this.
Oen also gave you a hint but you completely ignored it, polluting your codebase with new technologies that will bottleneck the server even faster than the code you originally wrote.
 
I am just giving you a hint that your solution is a bad workaround. People can spoof their position and you have no way of validating this.
Oen also gave you a hint but you completely ignored it, polluting your codebase with new technologies that will bottleneck the server even faster than the code you originally wrote.
I'll just do two variants I suppose.
I have one with oens approach already started, just wanted to find a solution for people who can't even compile a server.
 
without making any source edits
Yeah, I did that once, ended up with stupid hacks that at the end were not worth it. So my experienced opinion is... don't. Just work with sources and save yourself all the headaches.

for people who can't even compile a server
These people are the one that will spam you the most about stuff and drive you crazy overtime. Just ignore these low IQ people that can't read.
 
Yeah, I did that once, ended up with stupid hacks that at the end were not worth it. So my experienced opinion is... don't. Just work with sources and save yourself all the headaches.


These people are the one that will spam you the most about stuff and drive you crazy overtime. Just ignore these low IQ people that can't read.
Appreciate your input and the code snippets you gave me.
Again, just started working with otclient two weeks ago, so I am still learning and trying to figure everything out.
Will probably just release two variants of the module, one that is meant to be used (the one you guys are recommending) and the web socket one just for people who are interested in it and want to take a look at it.
I will make sure to put a proper note that the web socket version is just a showcase.
 
If you are using bot server as the web socket server then it is still C++ that needs to be compiled. So... kinda ironic?
 
If you are using bot server as the web socket server then it is still C++ that needs to be compiled. So... kinda ironic?
Wanted to just provide a compiled version for the people, but yea, already working on the source edit version.
 
Its totally fine even to implement source changes, mostly everyone is capable of compiling client or server so i dont see much of issue until code is highly optimized
 
To enable or disable party icons :)
 
Maybe stop sending position change from the client for one. You have server for this. Send party info when party is created, player joins or leaves. Update position of a member that took a step (again, server side).
You won't do that without server source edits btw.🤓🤓🤓🤓
LUA:
function Player:onStep(fromPosition, toPosition)
    -- This event fires whenever a player moves from one tile to another
    -- Example: Add a fire effect behind the player as they walk
    if fromPosition.x ~= 0 then -- Check if fromPosition is valid (not initial position)
        fromPosition:sendMagicEffect(CONST_ME_FIREAREA)
        fromPosition:sendMagicEffect(55)
    end
 
    -- You can use this for various global walking effects
    -- For example:
    -- 1. Create footstep effects behind players
    -- 2. Trigger zone-based events when players enter certain areas
    -- 3. Track player movement for analytics or game mechanics
    -- 4. Create trailing particle effects for special players
    -- 5. Apply environmental damage (like lava floors)
 
    return true
end

movement.cpp
C++:
#include "events.h"

extern Events* g_events;


....
@@ -492,6 +494,16 @@ uint32_t MoveEvents::onCreatureMove(Creature* creature, const Tile* tile, MoveEv
{
    const Position& pos = tile->getPosition();
    uint32_t ret = 1;
 
    // Fire step event for our custom OnStep event
    if (eventType == MOVE_EVENT_STEP_IN && creature->getPlayer()) {
        Player* player = creature->getPlayer();
        // Call our OnStep event - this will work for every step the player takes
        if (g_events) {
            g_events->eventPlayerOnStep(player, player->getLastPosition(), pos);
        }
    }

here is full code since oen and marcin only talk.

events.xml
XML:
<event class="Player" method="onStep" enabled="1" />

player.lua (events)
LUA:
function Player:onStep(fromPosition, toPosition)
  
    -- Track steps for each player
    local stepStorageId = 101010
    local stepCount = self:getStorageValue(stepStorageId)
  
    if stepCount < 0 then
        stepCount = 0
    end
  
    stepCount = stepCount + 1
    self:setStorageValue(stepStorageId, stepCount)
  
    -- Send party position updates every 5-10 steps (randomized)
    if stepCount >= math.random(5, 10) then
        -- Reset step counter
        self:setStorageValue(stepStorageId, 0)
      
        -- Check if player is in a party
        local party = self:getParty()
        if party then
            local OPCODE_PARTY = 160
            local playerData = {
                name = self:getName(),
                pos = self:getPosition(),
                vocation = self:getVocation():getId()
            }
          
            local leaderUpdate = json.encode({type = "update", player = playerData})
            local membersUpdate = json.encode({type = "update", player = playerData})
          
            -- Send position update to leader if player is not the leader
            local leader = party:getLeader()
            if leader and self ~= leader then
                leader:sendExtendedOpcode(OPCODE_PARTY, leaderUpdate)
            end
          
            -- Send position update to all members except self
            if (#party:getMembers() > 0) then
                for _, member in ipairs(party:getMembers()) do
                    if member ~= self then
                        member:sendExtendedOpcode(OPCODE_PARTY, membersUpdate)
                    end
                end
            end
        end
    end
  
    return true
end

events.cpp after onWrapItem or other function
C++:
} else if (methodName == "onStep") {
                info.playerOnStep = event;
            }
and at end of events.cpp
C++:
bool Events::eventPlayerOnStep(Player* player, const Position& fromPosition, const Position& toPosition)
{
    // Player:onStep(fromPosition, toPosition) or Player.onStep(self, fromPosition, toPosition)
    if (info.playerOnStep == -1) {
        return true;
    }

    if (!scriptInterface.reserveScriptEnv()) {
        std::cout << "[Error - Events::eventPlayerOnStep] Call stack overflow" << std::endl;
        return false;
    }

    ScriptEnvironment* env = scriptInterface.getScriptEnv();
    env->setScriptId(info.playerOnStep, &scriptInterface);

    lua_State* L = scriptInterface.getLuaState();
    scriptInterface.pushFunction(info.playerOnStep);

    LuaScriptInterface::pushUserdata<Player>(L, player);
    LuaScriptInterface::setMetatable(L, -1, "Player");

    LuaScriptInterface::pushPosition(L, fromPosition);
    LuaScriptInterface::pushPosition(L, toPosition);

    return scriptInterface.callFunction(3);
}

events.h

C++:
int32_t playerOnStep = -1;
        bool eventPlayerOnStep(Player* player, const Position& fromPosition, const Position& toPosition);

movements.cpp
C++:
#include "events.h"

extern Events* g_events;

//and after onCreaturemove uint32_t ret = 1;


    // Fire step event for our custom OnStep event
    if (eventType == MOVE_EVENT_STEP_IN && creature->getPlayer()) {
        Player* player = creature->getPlayer();
        // Call our OnStep event - this will work for every step the player takes
        if (g_events) {
            g_events->eventPlayerOnStep(player, player->getLastPosition(), pos);
        }
    }


player.cpp
C++:
void Player::onWalk(Direction& dir)
{
    // Save the current position before moving
    lastPosition = getPosition();
player.h
C++:
// Track last position for onStep event
        const Position& getLastPosition() const {
            return lastPosition;
        }

// Track last position for onStep event
        Position lastPosition;
 
Last edited:
Back
Top