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

[otcv8] how to send uid to client?

Vagnerking

Active Member
Joined
Jan 20, 2023
Messages
40
Reaction score
37
GitHub
Vagnerking
I'm trying to create a market, and I need the item's uid to inform which exact item I'm selling (because it has different attributes)

I'm using tfs 0.4
 
I'm trying to create a market, and I need the item's uid to inform which exact item I'm selling (because it has different attributes)

I'm using tfs 0.4
Only items with UIDs defined in map editor (numbers up to 65535) have real UIDs (unique IDs). All Lua UIDs like 70001, 70002 are temporary UIDs in Lua. They may stay in Lua until server restart, but they may be also removed earlier.
You cannot use 'UID' to implement some OTClient market with custom items.

If you want to create OTC module that sells custom items on market - player can click on item which he wants to sell -, you have to use C++ function Game::internalGetThing to read 'position' on OTS - which may be on floor like X,Y,Z,stackpos, but also inside BP using special x,y,z,stackpos values - to read what player clicked in OTC.
Passing that C++ function to Lua - to process 'extended opcode' from OTC - was discussed in this PR on TFS 1.6+:
It's not available in Lua on TFS 0.4/1.x, so you would need to add it yourself.
 
Last edited:
Only items with UIDs defined in map editor (numbers up to 65535) have real UIDs (unique IDs). All Lua UIDs like 70001, 70002 are temporary UIDs in Lua. They may stay in Lua until server restart, but they may be also removed earlier.
You cannot use 'UID' to implement some OTClient market with custom items.
Perfect! Let me see if I understand. In OTCv8, I have functions in the item like this:

item:getId() -- (spriteId, I think)
item:getPosition()
item:getStackPos()

By sending an opcode to the server with the item's position, stack pos, and spriteId, I can simply call:

Thing *Game::internalGetThing(Player *player, const Position &pos, int32_t index,
uint32_t spriteId /* = 0*/, stackposType_t type /* = STACKPOS_NORMAL*/)

(I know I need to create Luascript because it's not standard in TFS 0.4)

and get the item instance with its UID, right?!

What would be the best way to save this item in a database?

My question is... should it be saved as it is in the player_items table? Where only the item ID is saved, and the attributes would be saved as a blob in the "attributes" column.

And yes, I'm creating a new module xD.
 
Perfect! Let me see if I understand. In OTCv8, I have functions in the item like this:

item:getId() -- (spriteId, I think)
item:getPosition()
item:getStackPos()
It's opposite. You can 'use' any Item in OTC and it will send 'position' to OTS.
That OTC 'position' Game.getInternalThing(player, position, index, clientId, type) on map/in BP can be used in OTS C++ Game::internalGetThing to get item that player sees in OTC.
My question is... should it be saved as it is in the player_items table? Where only the item ID is saved, and the attributes would be saved as a blob in the "attributes" column.
These are separate things. First you have to detect given "Item" in OTC-OTS communication.
Then, you should use same 'attributes' code in Lua, as C++ uses in iologindata.cpp to save Item attributes.
 
Extending from what Gesior explained:
Create your own database table, similar to player_items. When a player lists an item, and the server knows which specific item it is (unique item with its own attributes/custom attris etc), you create a new row, and destroy the item ingame. And on purchase, you remove the row and recreate the item using the information from the database row (id, count, attr blob etc)
 
Got it!

My idea was to drag the item to a market module, and when I did so, I would send an opcode, where I would send the item's position, stackpos, and ID (information I already have), and the server would be able to get the exact item (uid) based on this pos, player, stackpos, ID...
 


For anyone having the same problem retrieving the item by client position, here's the code I developed for that.It should work well in OTX 2 or TFS 0.4.

This function calls an external function that adds a UID to the item if it doesn't already have one. The UID of the external function starts at 70000 each time the server starts.

This was created for recreational purposes and for some testing, please be aware of any security issues or problems with UIDs.

Feel free to improve the code or give feedback.

LUA:
int32_t LuaInterface::luaGetItemByPos(lua_State *L)
{
    // getItemByPos(player, pos)
    PositionEx pos;
    popPosition(L, pos);
    ScriptEnviroment *env = getEnv();
    uint32_t playerUid = popNumber(L);

    Player *player = env->getPlayerByUID(playerUid);

    if (!player)
    {
        errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND));
        lua_pushboolean(L, false);
        return 1;
    }

    Item *realItem = nullptr;

    if (pos.x == 0xFFFF)
    {
        if (pos.y & 0x40)
        {
            uint8_t cid = static_cast<uint8_t>(pos.y & 0x0F);
            if (Container *container = player->getContainer(cid))
                realItem = container->getItem(pos.z);
        }
        else
            realItem = player->getInventoryItem((slots_t)pos.y);
    }

    if (!realItem)
    {
        errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND));
        lua_pushboolean(L, false);
        return 1;
    }

    if (realItem->getUniqueId() == 0 && !realItem->isStackable())
        realItem->setUniqueId(env->addThing(realItem));

    lua_newtable(L);

    // Here you can implement it however you like.
    setField(L, "itemId", realItem->getID());
    setField(L, "itemClientId", realItem->getClientID());
    setField(L, "itemName", realItem->getName());
    setField(L, "itemDescription", realItem->getDescription(0));
    setField(L, "itemMaxCount", realItem->getItemCount());
    setField(L, "itemUid", realItem->getUniqueId());

    // I use this to prevent the user from selling container-type items with other items inside.
    bool isContainer = realItem->isContainer();
    setField(L, "itemIsContainer", isContainer);
    setField(L, "itemCountInContainer", isContainer ? realItem->getContainer()->size() : 0);

    return 1;
}


EDIT 1: Adjusted the player variable point you mentioned, thank you @Tofame !

EDIT 2: I took the opportunity to make changes to the uid, where I removed the external class and only added the actual addThing from the server, because the removeItem function, which receives the uid as an param, wasn't finding the item.

EDIT 3 : It does not generate a UID when the item is stackable (it's not necessary).
 
Last edited:
Since you are able to get item by position, you no longer need to set/get unique uid for it.

fetch item by position,
add to DB,
delete in-game

Also, instead of:
C++:
const Player* playerConst ... and the casting later ...
you just do:
C++:
Player* player = env->getPlayerByUID(playerUid);
 
Since you are able to get item by position, you no longer need to set/get unique uid for it.

fetch item by position,
add to DB,
delete in-game

Also, instead of:
C++:
const Player* playerConst ... and the casting later ...
you just do:
C++:
Player* player = env->getPlayerByUID(playerUid);
The UID is important because while the player is in the market choosing their item to sell, they can change the position of the items in their backpack or slot.
 
Last edited:
The UID is important because while the player is in the market choosing their item to sell, they can change the position of the items in their backpack or slot.
Client side, you send the position of the item.
Server side, you store the item pointer, preferably related to Player.
Any further actions in the client, the server be able to point to that item.
 
Last edited:

That's great! I didn't know about this window:lock, thanks for the tip.

The player can receive an item during the lock.
That's true, I think the most suitable/safe solution is to store/create the item's UID (only if it's not a stackable item).
Then, during the actual sale confirmation, we check if the player has the item with that UID or ID (if it's a stackable item).



---
It's always good to be able to share things like this, because it brings a flood of ideas about possible problems and better solutions. Thank you to everyone who is contributing.
 
The player can receive an item during the lock.
How? Unless there is some special system on the server where you can give players items directly to their bp. Otherwise you can't pickup anything, use hotkeys, use other modules like store or npc trade or player trade. And if they do it on purpose then nothing really breaks, they just put wrong item with wrong price on the market, knowingly.
 
How? Unless there is some special system on the server where you can give players items directly to their bp. Otherwise you can't pickup anything, use hotkeys, use other modules like store or npc trade or player trade. And if they do it on purpose then nothing really breaks, they just put wrong item with wrong price on the market, knowingly.
Very common mechanic on OTS, people get eg. coins for eg. staying online.
Selling the wrong item at the incorrect price results in people quitting your server.

You can store each movable item reference in an unordered map. It will use some memory, but it sounds like the only real solution, if you dont want to remove the item on interaction.
 
Last edited:
What about onItemChange event? If the item in the slot of the initial item that the player tries to sell changes, you can simply close the "Selling" window and leave info box that "Item changed, try again.".
 
This discussion is interesting xD

I think we all agree that, in programming, there are several ways to achieve the same result, and yes, each solution has its pros and cons.

I believe there isn't one exact answer, nor the most correct one. There are several ways to do it, so it's up to the programmer to find the best path to follow :D

For example, I did it using UID because I didn't know about window:lock, but I also know that the player can receive an item from the store (website) in their backpack at the time of sale (the shop script checks if there is space in the backpack and capacity if there isn't, it sends the item to the depot), which could result in a different item or, in the case of the OEN solution, handle the error and restart the sale attempt.

Thank you all, I hope our solutions contribute to the community.
 
Back
Top