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

Exp/Kill/Drop Tracker Module [OTCV8] [TFS 1.4.2]

Klank

Althea ¤ A New World Developer
Joined
Feb 21, 2008
Messages
1,089
Reaction score
657
Location
Norway
Greetings.

I made a drop and kill tracker module together with the public exp analyzer module found here.
Module does not use opcode, but by network messages.

Im still learning modules and programming in general, so if you find flaws or bad coding, please comment this thread, so that others and me can correct it.
  • Copy Kill or Drop session to Clipboard:
  • Start new Hunt Button (opens all trackers)
  • Reset singular trackers (ex Reset kills, resets kill tracker only)
  • Trackers needs to be visible to track.
  • Ctrl+H to open Window
  • Tracks Number of killed creatures and items since the respective tracker was opened(resets on death or logout)
  • Exp Session




Killer Tracker / Exp Tracker / Drop Tracker
1714903450241.png1714903461727.png1714905925397.png
Analyzer Menu:
1714903545639.png
So what do you need to do?

Server-side Code
First of all, you need commit from the official TFS branch : Kill tracking (#3860) · otland/forgottenserver@ba72ce6 (https://github.com/otland/forgottenserver/commit/ba72ce6787668974b2f0b9db24747d4ac1693507)

And in addition, you need to add the item name to the network:
// msg:addString(item:getName())
Lua:
function Player.updateKillTracker(self, monster, corpse)
    local monsterType = monster:getType()
    if not monsterType then
        return false
    end

    local msg = NetworkMessage()
    msg:addByte(0xD1)
    msg:addString(monster:getName())

    local monsterOutfit = monsterType:getOutfit()
    msg:addU16(monsterOutfit.lookType or 19)
    msg:addByte(monsterOutfit.lookHead)
    msg:addByte(monsterOutfit.lookBody)
    msg:addByte(monsterOutfit.lookLegs)
    msg:addByte(monsterOutfit.lookFeet)
    msg:addByte(monsterOutfit.lookAddons)

    local corpseSize = corpse:getSize()
    msg:addByte(corpseSize)
    for index = corpseSize - 1, 0, -1 do
        local item = corpse:getItem(
            index)
        msg:addItem(item,true)
        msg:addString(item:getName()) --- ADD THIS
    end

    local party = self:getParty()
    if party then
        local members = party:getMembers()
        members[#members + 1] = party:getLeader()

        for _, member in ipairs(members) do
            msg:sendToPlayer(member)
        end
    else
        msg:sendToPlayer(self)
    end

    msg:delete()
    return true
end
As you might have noticed, there is a “true” in addItem. If the code works without true for you, you don’t need to add it. But if you need to, add this in luascript.cpp.
(This caused a lot of bugs for me in client, adding this to the additem method, at least solved the issue for me.)
Code:
int LuaScriptInterface::luaNetworkMessageAddItem(lua_State* L)
{
    // networkMessage:addItem(item, false/true)
    Item* item = getUserdata<Item>(L, 2);

    if (!item) {
        reportErrorFunc(L, getErrorDesc(LUA_ERROR_ITEM_NOT_FOUND));
        lua_pushnil(L);
        return 1;
    }


    NetworkMessage* message = getUserdata<NetworkMessage>(L, 1);
    bool description = getBoolean(L, 3, false);
    if (message) {
        message->addItem(item, description);
        pushBoolean(L, true);
    } else {
        lua_pushnil(L);
    }
    return 1;
}

Client side Code
Since we are not using opcodes, we need to edit the protocolgameparse.cpp. In OTCV8, the parsing for kill tracker is already reserved.
Replace this:
Code:
void ProtocolGame::parseKillTracker(const InputMessagePtr& msg)
{
    msg->getString();
    msg->getU16();
    msg->getU8();
    msg->getU8();
    msg->getU8();
    msg->getU8();
    msg->getU8();
    int corpseSize = msg->getU8(); // corpse size
    for (int i = 0; i < corpseSize; i++) {
        getItem(msg); // corpse item 
    }
}

With this:
Code:
void ProtocolGame::parseKillTracker(const InputMessagePtr& msg)
{
   std::string name =  msg->getString(); //Monster Name
   uint16_t lookType = msg->getU16();      //Monster Looktype
   uint8_t lookHead = msg->getU8();       // Monster Head
   uint8_t lookBody = msg->getU8();       // Monster Body
   uint8_t lookLegs = msg->getU8();       // Monster Legs
   uint8_t lookFeet = msg->getU8();       // Monster Feet
   uint8_t addons = msg->getU8();       //Monster Addons
   uint8_t corpseSize = msg->getU8(); // corpse size
   std::vector<std::tuple<std::string, ItemPtr>> items;
    for (int i = 0; i < corpseSize; i++) {
        ItemPtr item = getItem(msg);
        std::string itemName = msg->getString();
        items.push_back(std::make_tuple(itemName, item));
    }

    m_localPlayer->updateKillTracker(name, lookType, lookHead, lookBody, lookLegs, lookFeet, addons, corpseSize, items);
}

In localplayer.h, below ‘void setBlessings(int blessings);’

void updateKillTracker(std::string name, uint16_t lookType, uint8_t lookHead, uint8_t lookBody, uint8_t lookLegs, uint8_t lookFeet, uint8_t addons, uint8_t corpseSize, const   std::vector<std::tuple<std::string, ItemPtr>> items);

in localplayer.h, below ‘void setBlessings(int blessings);’ add:
Code:
void updateKillTracker(std::string name, uint16_t lookType, uint8_t lookHead, uint8_t lookBody, uint8_t lookLegs, uint8_t lookFeet, uint8_t addons, uint8_t corpseSize, const   std::vector<std::tuple<std::string, ItemPtr>> items);

In localplayer.cpp, at end of file, add:
Code:
void LocalPlayer::updateKillTracker(std::string name, uint16_t lookType, uint8_t lookHead, uint8_t lookBody, uint8_t lookLegs, uint8_t lookFeet, uint8_t addons, uint8_t corpseSize, const  std::vector<std::tuple<std::string, ItemPtr>> items)
{
        callLuaField("onUpdateKillTracker", name, lookType, lookHead, lookBody, lookLegs, lookFeet, addons,corpseSize, items);
}

That’s about it. Feel free to improve or add more features to the module.
 

Attachments

  • game_huntanalyzer.rar
    6.2 KB · Views: 50 · VirusTotal
great contribution to the otland society thank you very much viking!
 
Hey! Thanks for the module, unfortunately I'm getting this error when I try to open the game, any advise?
1714934810585.png
 
Greetings.

I made a drop and kill tracker module together with the public exp analyzer module found here.
Module does not use opcode, but by network messages.

Im still learning modules and programming in general, so if you find flaws or bad coding, please comment this thread, so that others and me can correct it.
  • Copy Kill or Drop session to Clipboard:
  • Start new Hunt Button (opens all trackers)
  • Reset singular trackers (ex Reset kills, resets kill tracker only)
  • Trackers needs to be visible to track.
  • Ctrl+H to open Window
  • Tracks Number of killed creatures and items since the respective tracker was opened(resets on death or logout)
  • Exp Session




Killer Tracker / Exp Tracker / Drop Tracker
View attachment 84393View attachment 84394View attachment 84401
Analyzer Menu:
View attachment 84396
So what do you need to do?

Server-side Code
First of all, you need commit from the official TFS branch : Kill tracking (#3860) · otland/forgottenserver@ba72ce6 (https://github.com/otland/forgottenserver/commit/ba72ce6787668974b2f0b9db24747d4ac1693507)

And in addition, you need to add the item name to the network:
// msg:addString(item:getName())
Lua:
function Player.updateKillTracker(self, monster, corpse)
    local monsterType = monster:getType()
    if not monsterType then
        return false
    end

    local msg = NetworkMessage()
    msg:addByte(0xD1)
    msg:addString(monster:getName())

    local monsterOutfit = monsterType:getOutfit()
    msg:addU16(monsterOutfit.lookType or 19)
    msg:addByte(monsterOutfit.lookHead)
    msg:addByte(monsterOutfit.lookBody)
    msg:addByte(monsterOutfit.lookLegs)
    msg:addByte(monsterOutfit.lookFeet)
    msg:addByte(monsterOutfit.lookAddons)

    local corpseSize = corpse:getSize()
    msg:addByte(corpseSize)
    for index = corpseSize - 1, 0, -1 do
        local item = corpse:getItem(
            index)
        msg:addItem(item,true)
        msg:addString(item:getName()) --- ADD THIS
    end

    local party = self:getParty()
    if party then
        local members = party:getMembers()
        members[#members + 1] = party:getLeader()

        for _, member in ipairs(members) do
            msg:sendToPlayer(member)
        end
    else
        msg:sendToPlayer(self)
    end

    msg:delete()
    return true
end
As you might have noticed, there is a “true” in addItem. If the code works without true for you, you don’t need to add it. But if you need to, add this in luascript.cpp.
(This caused a lot of bugs for me in client, adding this to the additem method, at least solved the issue for me.)
Code:
int LuaScriptInterface::luaNetworkMessageAddItem(lua_State* L)
{
    // networkMessage:addItem(item, false/true)
    Item* item = getUserdata<Item>(L, 2);

    if (!item) {
        reportErrorFunc(L, getErrorDesc(LUA_ERROR_ITEM_NOT_FOUND));
        lua_pushnil(L);
        return 1;
    }


    NetworkMessage* message = getUserdata<NetworkMessage>(L, 1);
    bool description = getBoolean(L, 3, false);
    if (message) {
        message->addItem(item, description);
        pushBoolean(L, true);
    } else {
        lua_pushnil(L);
    }
    return 1;
}

Client side Code
Since we are not using opcodes, we need to edit the protocolgameparse.cpp. In OTCV8, the parsing for kill tracker is already reserved.
Replace this:
Code:
void ProtocolGame::parseKillTracker(const InputMessagePtr& msg)
{
    msg->getString();
    msg->getU16();
    msg->getU8();
    msg->getU8();
    msg->getU8();
    msg->getU8();
    msg->getU8();
    int corpseSize = msg->getU8(); // corpse size
    for (int i = 0; i < corpseSize; i++) {
        getItem(msg); // corpse item
    }
}

With this:
Code:
void ProtocolGame::parseKillTracker(const InputMessagePtr& msg)
{
   std::string name =  msg->getString(); //Monster Name
   uint16_t lookType = msg->getU16();      //Monster Looktype
   uint8_t lookHead = msg->getU8();       // Monster Head
   uint8_t lookBody = msg->getU8();       // Monster Body
   uint8_t lookLegs = msg->getU8();       // Monster Legs
   uint8_t lookFeet = msg->getU8();       // Monster Feet
   uint8_t addons = msg->getU8();       //Monster Addons
   uint8_t corpseSize = msg->getU8(); // corpse size
   std::vector<std::tuple<std::string, ItemPtr>> items;
    for (int i = 0; i < corpseSize; i++) {
        ItemPtr item = getItem(msg);
        std::string itemName = msg->getString();
        items.push_back(std::make_tuple(itemName, item));
    }

    m_localPlayer->updateKillTracker(name, lookType, lookHead, lookBody, lookLegs, lookFeet, addons, corpseSize, items);
}

In localplayer.h, below ‘void setBlessings(int blessings);’

void updateKillTracker(std::string name, uint16_t lookType, uint8_t lookHead, uint8_t lookBody, uint8_t lookLegs, uint8_t lookFeet, uint8_t addons, uint8_t corpseSize, const   std::vector<std::tuple<std::string, ItemPtr>> items);

in localplayer.h, below ‘void setBlessings(int blessings);’ add:
Code:
void updateKillTracker(std::string name, uint16_t lookType, uint8_t lookHead, uint8_t lookBody, uint8_t lookLegs, uint8_t lookFeet, uint8_t addons, uint8_t corpseSize, const   std::vector<std::tuple<std::string, ItemPtr>> items);

In localplayer.cpp, at end of file, add:
Code:
void LocalPlayer::updateKillTracker(std::string name, uint16_t lookType, uint8_t lookHead, uint8_t lookBody, uint8_t lookLegs, uint8_t lookFeet, uint8_t addons, uint8_t corpseSize, const  std::vector<std::tuple<std::string, ItemPtr>> items)
{
        callLuaField("onUpdateKillTracker", name, lookType, lookHead, lookBody, lookLegs, lookFeet, addons,corpseSize, items);
}

That’s about it. Feel free to improve or add more features to the module.
Good to see this stuff pop up here.
I wont use it myself but thanks for the contribution to the community
 
the luascript part of the server, it is not clear, when I compile I get errors
Post automatically merged:

The loot of the monsters does not analyze them, I use version 772 of Nekiro, does anyone know why?
 
Last edited:
Not at all, I just drag and dropped what was uploaded. Do I also need the exph module from OT Server List - OTC Exp Analyzer (Exp/h) Module Download (https://otserverlist.me/download/modules/exp-analyzer) ?
No you dont need that module.
Are you using OTCV8 and did you do the src changes?

the luascript part of the server, it is not clear, when I compile I get errors
Post automatically merged:

The loot of the monsters does not analyze them, I use version 772 of Nekiro, does anyone know why?
the luapart is added in the commit i reffered to. I am not sure how 772 nekiro works, does it have network messages through lua? What type of errors are you getting?
 
No you dont need that module.
Are you using OTCV8 and did you do the src changes?


the luapart is added in the commit i reffered to. I am not sure how 772 nekiro works, does it have network messages through lua? What type of errors are you getting?
Lua:
/home/ot7/src/luascript.cpp: In static member function ‘static int LuaScriptInterface::luaNetworkMessageAddItem(lua_State*)’:
/home/ot7/src/luascript.cpp:5903:26: error: invalid conversion from ‘Item*’ to ‘uint16_t’ {aka ‘short unsigned int’} [-fpermissive]
 5903 |         message->addItem(item, description);
      |                          ^~~~
      |                          |
      |                          Item*
In file included from /home/ot7/src/connection.h:25,
                 from /home/ot7/src/protocol.h:23,
                 from /home/ot7/src/protocolgame.h:23,
                 from /home/ot7/src/player.h:29,
                 from /home/ot7/src/luascript.cpp:27:
/home/ot7/src/networkmessage.h:115:39: note:   initializing argument 1 of ‘void NetworkMessage::addItem(uint16_t, uint8_t)’
  115 |                 void addItem(uint16_t id, uint8_t count);
 
Lua:
/home/ot7/src/luascript.cpp: In static member function ‘static int LuaScriptInterface::luaNetworkMessageAddItem(lua_State*)’:
/home/ot7/src/luascript.cpp:5903:26: error: invalid conversion from ‘Item*’ to ‘uint16_t’ {aka ‘short unsigned int’} [-fpermissive]
 5903 |         message->addItem(item, description);
      |                          ^~~~
      |                          |
      |                          Item*
In file included from /home/ot7/src/connection.h:25,
                 from /home/ot7/src/protocol.h:23,
                 from /home/ot7/src/protocolgame.h:23,
                 from /home/ot7/src/player.h:29,
                 from /home/ot7/src/luascript.cpp:27:
/home/ot7/src/networkmessage.h:115:39: note:   initializing argument 1 of ‘void NetworkMessage::addItem(uint16_t, uint8_t)’
  115 |                 void addItem(uint16_t id, uint8_t count);
Nekiro downgrade doesnt have boolean description in addItem. So dont do the source change with regards to this. Try run the code with addItem(item) only.
 
Thank you for your contribution Klank. I would say as a custom module it's quite solid and it's a solid release. Keep it up because i love seeing the custom modules that people come up with.

Here are my thoughts.
Firstly I think the itemSprite for the droptracker should be virtual so players can't right click them and try to use them.
A configuration to reset the kill tracker on death or logout might be a neat option as well.
I think theres some destroys and a couple of unbinds missing in the terminate function. CTRL+H is never unbound for example. Same with the event for onUpdateKillTracker

My main improvement would be for you to know that OTCV8 handles lua opcodes as well just like TFS so there isn't really a reason to make source changes for the client when we can instead parse it in lua. That means we don't have to do the change you did to addItem in networkmessage lua call. The boolean specifies if you should send a description or not, this is mainly used for tooltips and it doesn't seem like is used in your code at all so we can leave that out.
I'm aware that the source already contains the C++ functions to do the parse of kill trackers and that they're bound to certain opcodes. But if you do it in lua you can just change the opcode you're sending. The benefit from that is that we wouldn't have to deal people that have been here since 2010 getting easily solvable compile errors and bogging up your thread with "no work pls fix"
Example below.
Lua:
msg:addByte(0xD1) 209 dec

msg:addByte(0x36) 54 decimal
I also did some minor changes that doesn't involve the "true" call to the addItem but that means we need to modify the code to send the itemId and the count instead of sending the full item.

Lua:
for index = corpseSize - 1, 0, -1 do -- This is also debatable. We could just call luaContainerGetItems
    local item = corpse:getItem(index)
    local itemType = ItemType(item:getId())
    msg:addU16(itemType:getClientId())
    msg:addByte(item:getCount())
    msg:addString(item:getName())
end



Then that means that instead of connecting to event onUpdateKillTracker you can then listen to the Opcode in the huntanalyzer.lua file.
Lua:
ProtocolGame.registerOpcode(GameServerOpcodes.GameServerKillTracker <- DEFIND AS A CONST, parseKillTrackerMessage <-- NEW FUNCTION)

Now you can use your onUpdateKillTracker method at the end of the new parse method like this.
Lua:
function parseKillTrackerMessage(protocol, msg)
    local monsterName = msg:getString()
    local looktype = msg:getU16()
    local lookHead = msg:getU8()
    local lookBody = msg:getU8()
    local lookLegs = msg:getU8()
    local lookFeet = msg:getU8()
    local lookAddons = msg:getU8()
    local itemsCount = msg:getU8()

    local items = {}
    for i = 1, itemsCount do
        local itemId = msg:getU16()
        local count = msg:getU8()
        local itemName = msg:getString()
        table.insert(items, {itemId = itemId, count = count, itemName = itemName})
      end
    onUpdateKillTracker(monsterName, looktype, lookHead, lookBody, lookLegs, lookFeet, lookAddons, items)
end

Now your onUpdateKillTracker can look like this instead.

Lua:
function onUpdateKillTracker(monsterName, lookType, lookHead, lookBody, lookLegs, lookFeet, addons, items)
    --Code redacted for space.
    for _, data in pairs(items) do
        local itemName = data.itemName
        local itemId = data.itemId
        local count = data.count
        -- Check if the item ID exists in the lootedItems table
        if not lootedItems[itemId] then
            -- If the item ID doesn't exist, initialize its amount to 0
            lootedItems[itemId] = { amount = 0, name = itemName }
        end
        -- Increment the amount of the looted item
        lootedItems[itemId].amount = lootedItems[itemId].amount + count
    end
    --Code redacted for space.
end

Other than that it the module UI worked straight away when i drag and dropped it into my folder so not sure what error AokiSama is having.
 
Add this as well then
 
Thank you for your contribution Klank. I would say as a custom module it's quite solid and it's a solid release. Keep it up because i love seeing the custom modules that people come up with.

Here are my thoughts.
Firstly I think the itemSprite for the droptracker should be virtual so players can't right click them and try to use them.
A configuration to reset the kill tracker on death or logout might be a neat option as well.
I think theres some destroys and a couple of unbinds missing in the terminate function. CTRL+H is never unbound for example. Same with the event for onUpdateKillTracker

My main improvement would be for you to know that OTCV8 handles lua opcodes as well just like TFS so there isn't really a reason to make source changes for the client when we can instead parse it in lua. That means we don't have to do the change you did to addItem in networkmessage lua call. The boolean specifies if you should send a description or not, this is mainly used for tooltips and it doesn't seem like is used in your code at all so we can leave that out.
I'm aware that the source already contains the C++ functions to do the parse of kill trackers and that they're bound to certain opcodes. But if you do it in lua you can just change the opcode you're sending. The benefit from that is that we wouldn't have to deal people that have been here since 2010 getting easily solvable compile errors and bogging up your thread with "no work pls fix"
Example below.
Lua:
msg:addByte(0xD1) 209 dec

msg:addByte(0x36) 54 decimal
I also did some minor changes that doesn't involve the "true" call to the addItem but that means we need to modify the code to send the itemId and the count instead of sending the full item.

Lua:
for index = corpseSize - 1, 0, -1 do -- This is also debatable. We could just call luaContainerGetItems
    local item = corpse:getItem(index)
    local itemType = ItemType(item:getId())
    msg:addU16(itemType:getClientId())
    msg:addByte(item:getCount())
    msg:addString(item:getName())
end



Then that means that instead of connecting to event onUpdateKillTracker you can then listen to the Opcode in the huntanalyzer.lua file.
Lua:
ProtocolGame.registerOpcode(GameServerOpcodes.GameServerKillTracker <- DEFIND AS A CONST, parseKillTrackerMessage <-- NEW FUNCTION)

Now you can use your onUpdateKillTracker method at the end of the new parse method like this.
Lua:
function parseKillTrackerMessage(protocol, msg)
    local monsterName = msg:getString()
    local looktype = msg:getU16()
    local lookHead = msg:getU8()
    local lookBody = msg:getU8()
    local lookLegs = msg:getU8()
    local lookFeet = msg:getU8()
    local lookAddons = msg:getU8()
    local itemsCount = msg:getU8()

    local items = {}
    for i = 1, itemsCount do
        local itemId = msg:getU16()
        local count = msg:getU8()
        local itemName = msg:getString()
        table.insert(items, {itemId = itemId, count = count, itemName = itemName})
      end
    onUpdateKillTracker(monsterName, looktype, lookHead, lookBody, lookLegs, lookFeet, lookAddons, items)
end

Now your onUpdateKillTracker can look like this instead.

Lua:
function onUpdateKillTracker(monsterName, lookType, lookHead, lookBody, lookLegs, lookFeet, addons, items)
    --Code redacted for space.
    for _, data in pairs(items) do
        local itemName = data.itemName
        local itemId = data.itemId
        local count = data.count
        -- Check if the item ID exists in the lootedItems table
        if not lootedItems[itemId] then
            -- If the item ID doesn't exist, initialize its amount to 0
            lootedItems[itemId] = { amount = 0, name = itemName }
        end
        -- Increment the amount of the looted item
        lootedItems[itemId].amount = lootedItems[itemId].amount + count
    end
    --Code redacted for space.
end

Other than that it the module UI worked straight away when i drag and dropped it into my folder so not sure what error AokiSama is having.
Thank you for your feedback, i really appriciate your thoughts. I was not aware of the item being usable, so glad you mentioned. I also was not aware that you could parse it through lua, that is a lot easier :D .

I wont spend the time to change the "release" here, but you are free to post your version!
 
Add this as well then
You just forgot to include
Lua:
networkMessage:addString(item:getName())
under
Lua:
networkMessage:addItem(item)

Your modification worked fine here, thanks!

You should update the release with Oen's updateKillTracker function @Klank , this solve the addItem(item,true) part as well
 
Last edited:
You just forgot to include
Lua:
networkMessage:addString(item:getName())
under
Lua:
networkMessage:addItem(item)

Your modification worked fine here, thanks!

You should update the release with Oen's updateKillTracker function @Klank , this solve the addItem(item,true) part as well

Glad to hear :)
Im not able to edit 😅
 
No you dont need that module.
Are you using OTCV8 and did you do the src changes?


the luapart is added in the commit i reffered to. I am not sure how 772 nekiro works, does it have network messages through lua? What type of errors are you getting?
Hey so I was using this OTCV8: GitHub - opentibiabr/otcv8: OTCv8 for OpenTibia Community. (https://github.com/opentibiabr/otcv8)

Apparently they have some change that makes some modules not work, not sure what it is.

I changed to: GitHub - OTAcademy/otclientv8: Mirror of official OTClientV8 (https://github.com/OTAcademy/otclientv8)

Now it works with no problems, thanks!
 
@Klank I'm having a problem only with receiving loot data on 1.5 nekiro 8.6. The rest is working perfectly, great contribution! Congratulations!
 
@Klank I'm having a problem only with receiving loot data on 1.5 nekiro 8.6. The rest is working perfectly, great contribution! Congratulations!

Did you add this commit Oen posted?
Add this as well then

you get any errors? Hard to tell what it can be without any spesific 😅
 
Back
Top