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

C++ OTHire Auto-looting function gone after compiling from source, but code is there

swashed

Member
Joined
Jul 9, 2023
Messages
82
Reaction score
8
I have a pre-compiled server that has an "auto-stacking" function that works flawlessly. When I compile the sources, this feature is no longer working. I think the author made some last minute changes before compiling the release, and now I need it back in and working. Can somebody take a look at this code and tell me if they see anything that may be causing it to not work?

C++:
bool autoStack = g_config.getBoolean(ConfigManager::CONTAINER_ITEMS_AUTO_STACK);
    if(autoStack){
        if(item->isStackable()){
            if(item->getParent() != this){
                //try find a suitable item to stack with
                uint32_t n = 0;
                for(ItemList::iterator cit = itemlist.begin(); cit != itemlist.end(); ++cit){
                    if((*cit) != item && (*cit)->getID() == item->getID() && (*cit)->getItemCount() < 100){
                        *destItem = (*cit);
                        index = n;
                        return this;
                    }

                    ++n;
                }
            }
        }
    }
Post automatically merged:

full function: Cylinder* Container::__queryDestination

C++:
Cylinder* Container::__queryDestination(int32_t& index, const Thing* thing, Item** destItem,
    uint32_t& flags)
{
    if(index == 254 /*move up*/){
        index = INDEX_WHEREEVER;
        *destItem = NULL;

        Container* parentContainer = dynamic_cast<Container*>(getParent());
        if(parentContainer)
            return parentContainer;

        return this;
    }

    if(index == 255 /*add wherever*/){
        index = INDEX_WHEREEVER;
        *destItem = NULL;
    }
    else if(index >= (int32_t)capacity()){
        /*
        if you have a container, maximize it to show all 20 slots
        then you open a bag that is inside the container you will have a bag with 8 slots
        and a "grey" area where the other 12 slots where from the container
        if you drop the item on that grey area
        the client calculates the slot position as if the bag has 20 slots
        */

        index = INDEX_WHEREEVER;
        *destItem = NULL;
    }

    const Item* item = thing->getItem();
    if(item == NULL){
        return this;
    }

    bool autoStack = g_config.getBoolean(ConfigManager::CONTAINER_ITEMS_AUTO_STACK);
    if(autoStack){
        if(item->isStackable()){
            if(item->getParent() != this){
                //try find a suitable item to stack with
                uint32_t n = 0;
                for(ItemList::iterator cit = itemlist.begin(); cit != itemlist.end(); ++cit){
                    if((*cit) != item && (*cit)->getID() == item->getID() && (*cit)->getItemCount() < 100){
                        *destItem = (*cit);
                        index = n;
                        return this;
                    }

                    ++n;
                }
            }
        }
    }

    if(index != INDEX_WHEREEVER){
        Thing* destThing = __getThing(index);
        if(destThing)
            *destItem = destThing->getItem();

        Cylinder* subCylinder = dynamic_cast<Cylinder*>(*destItem);

        if(subCylinder){
            index = INDEX_WHEREEVER;
            *destItem = NULL;
            return subCylinder;
        }
    }

    return this;
}
Post automatically merged:

It works when I move items from one container to another.


autostack1.gif
Post automatically merged:

Doesn't work when I loot gold from a monster

autostack2.gif
Post automatically merged:

Does not work when I conjure ammo/food

autostack3.gif
Post automatically merged:

This seems to be exclusive to the AddItem function, which I found 2 of

C++:
ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index /*= INDEX_WHEREEVER*/,
    uint32_t flags /*= 0*/, bool test /*= false*/)
{
    uint32_t remainderCount = 0;
    return internalAddItem(toCylinder, item, index, flags, test, remainderCount);
}
Post automatically merged:

C++:
ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index,
    uint32_t flags, bool test, uint32_t& remainderCount)
{
    remainderCount = 0;
    if(toCylinder == NULL || item == NULL){
        return RET_NOTPOSSIBLE;
    }

    Cylinder* origToCylinder = toCylinder;

    Item* toItem = NULL;
    toCylinder = toCylinder->__queryDestination(index, item, &toItem, flags);

    //check if we can add this item
    ReturnValue ret = toCylinder->__queryAdd(index, item, item->getItemCount(), flags);
    if(ret != RET_NOERROR){
        return ret;
    }

    /*
    Check if we can move add the whole amount, we do this by checking against the original cylinder,
    since the queryDestination can return a cylinder that might only hold a part of the full amount.
    */
    uint32_t maxQueryCount = 0;
    ret = origToCylinder->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), maxQueryCount, flags);

    if(ret != RET_NOERROR){
        return ret;
    }

    if(!test){
        if(item->isStackable() && toItem){
            uint32_t n = 0;
            uint32_t m = std::min((uint32_t)item->getItemCount(), maxQueryCount);

            if(toItem->getID() == item->getID()){
                n = std::min((uint32_t)100 - toItem->getItemCount(), m);
                toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
            }

            if(m - n > 0){
                if(m - n != item->getItemCount()){
                    Item* remainderItem = Item::CreateItem(item->getID(), m - n);
                    if(internalAddItem(origToCylinder, remainderItem, INDEX_WHEREEVER, flags, false) != RET_NOERROR){
                        FreeThing(remainderItem);
                        remainderCount = m - n;
                    }
                }
                else{
                    toCylinder->__addThing(index, item);

                    int32_t itemIndex = toCylinder->__getIndexOfThing(item);
                    if(itemIndex != -1){
                        toCylinder->postAddNotification(item, NULL, itemIndex);
                    }
                }
            }
            else{
                //fully merged with toItem, item will be destroyed
                item->onRemoved();
                FreeThing(item);
            }
        }
        else{
            toCylinder->__addThing(index, item);

            int32_t itemIndex = toCylinder->__getIndexOfThing(item);
            if(itemIndex != -1){
                toCylinder->postAddNotification(item, NULL, itemIndex);
            }
        }
    }

    return RET_NOERROR;
}
Post automatically merged:

It looks like the meat of this method is locked behind a !test bool, which is possibly whats preventing it from completing?
Post automatically merged:

There is also this container AddItem function that doesn't seem to make use of __queryDestination

C++:
void Container::addItem(Item* item)
{
    itemlist.push_back(item);
    item->setParent(this);
}
 
Last edited:
Bro I just looked over everything. It's pretty obvious the reason it works sometimes and not others is not because it's broken but because of which method you are using to call it. You said it doesn't work with "addItem". That's right, it doesn't, it only is ever called from one function internalAddItem. But more specifically the correct version, as method's can have different signatures, that means they can be used and called differently with the same name. You want it to stack for you in the instances it doesn't, find those locations in the source that call the "other" methods, and replace it with the one that contains your "autostacking" logic.

PS none of the logic you need access to is locked behind the bool you were concerned about.
 
Just wanna make a correction that this is about auto-stacking even though the title was accidentally auto-looting.

Game.cpp file has function internalAddItem, and this is the check for auto-stacking:

C++:
if(!test){
        if(item->isStackable() && toItem){
            uint32_t n = 0;
            uint32_t m = std::min((uint32_t)item->getItemCount(), maxQueryCount);

            if(toItem->getID() == item->getID()){
                n = std::min((uint32_t)100 - toItem->getItemCount(), m);
                toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
            }

            if(m - n > 0){
                if(m - n != item->getItemCount()){
                    Item* remainderItem = Item::CreateItem(item->getID(), m - n);
                    if(internalAddItem(origToCylinder, remainderItem, INDEX_WHEREEVER, flags, false) != RET_NOERROR){
                        FreeThing(remainderItem);
                        remainderCount = m - n;
                    }
                }
                else{
                    toCylinder->__addThing(index, item);

                    int32_t itemIndex = toCylinder->__getIndexOfThing(item);
                    if(itemIndex != -1){
                        toCylinder->postAddNotification(item, NULL, itemIndex);
                    }
                }
            }
            else{
                //fully merged with toItem, item will be destroyed
                item->onRemoved();
                FreeThing(item);
            }
        }
        else{
            toCylinder->__addThing(index, item);

            int32_t itemIndex = toCylinder->__getIndexOfThing(item);
            if(itemIndex != -1){
                toCylinder->postAddNotification(item, NULL, itemIndex);
            }
        }
    }

If it finds an item with a matching item ID, it merges (auto-stacking)
If it doesn't find a matching item ID, it will just create the item in the first available slot (not auto stacking).
Post automatically merged:

Right now it doesn't seem to be detecting matching item IDs
 
Just wanna make a correction that this is about auto-stacking even though the title was accidentally auto-looting.

Game.cpp file has function internalAddItem, and this is the check for auto-stacking:

C++:
if(!test){
        if(item->isStackable() && toItem){
            uint32_t n = 0;
            uint32_t m = std::min((uint32_t)item->getItemCount(), maxQueryCount);

            if(toItem->getID() == item->getID()){
                n = std::min((uint32_t)100 - toItem->getItemCount(), m);
                toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
            }

            if(m - n > 0){
                if(m - n != item->getItemCount()){
                    Item* remainderItem = Item::CreateItem(item->getID(), m - n);
                    if(internalAddItem(origToCylinder, remainderItem, INDEX_WHEREEVER, flags, false) != RET_NOERROR){
                        FreeThing(remainderItem);
                        remainderCount = m - n;
                    }
                }
                else{
                    toCylinder->__addThing(index, item);

                    int32_t itemIndex = toCylinder->__getIndexOfThing(item);
                    if(itemIndex != -1){
                        toCylinder->postAddNotification(item, NULL, itemIndex);
                    }
                }
            }
            else{
                //fully merged with toItem, item will be destroyed
                item->onRemoved();
                FreeThing(item);
            }
        }
        else{
            toCylinder->__addThing(index, item);

            int32_t itemIndex = toCylinder->__getIndexOfThing(item);
            if(itemIndex != -1){
                toCylinder->postAddNotification(item, NULL, itemIndex);
            }
        }
    }

If it finds an item with a matching item ID, it merges (auto-stacking)
If it doesn't find a matching item ID, it will just create the item in the first available slot (not auto stacking).
Post automatically merged:

Right now it doesn't seem to be detecting matching item IDs
Your code you say you need help with, doesn't get executed until its called. It is called inside the function "__queryDestination". Therefore, this code that you are obsessing over and keep posting about, has nothing to do with the actual piece of code written that you say you need help with. Not once in this code above is "__queryDestination" called. (maybe that is the problem?)

If you do know and understand C++, then surely you can fix this issue. If however you don't, you shouldn't assume you are right in your train of thought and one who does, who is spending their time to aid you, is wrong.

I don't have the time to read every single line and think through all the logic in all of those functions, clearly you do and you know how to, so please, share here with others the solution once you have worked it out. Thanks.
 
Your code you say you need help with, doesn't get executed until its called. It is called inside the function "__queryDestination". Therefore, this code that you are obsessing over and keep posting about, has nothing to do with the actual piece of code written that you say you need help with. Not once in this code above is "__queryDestination" called. (maybe that is the problem?)

If you do know and understand C++, then surely you can fix this issue. If however you don't, you shouldn't assume you are right in your train of thought and one who does, who is spending their time to aid you, is wrong.

I don't have the time to read every single line and think through all the logic in all of those functions, clearly you do and you know how to, so please, share here with others the solution once you have worked it out. Thanks.
I don't know or undestand C++, so I know I'm not right...but I also have a hard time following any advice. I'm just trying to understand it enough to change it to have the behavior I need it to have.

__queryDestination is called just before this.

Here is the full function:


And the author has a more up-to-date distro with similar source and this is how the function looks on his newer distro:


I'm thinking to just straight up copy and replace from the newer one and see what happens...
Post automatically merged:

Took code from Nostalrius and adapted it to OTHire. Let's hope this works:

C++:
ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index,
                                  uint32_t flags, bool test, uint32_t& remainderCount)
{
    if (toCylinder == nullptr || item == nullptr) {
        return RET_NOTPOSSIBLE;
    }

    Cylinder* destCylinder = toCylinder;
    Item* toItem = nullptr;
    toCylinder = toCylinder->__queryDestination(index, item, &toItem, flags);

    //check if we can add this item
    ReturnValue ret = toCylinder->__queryAdd(index, item, item->getItemCount(), flags);
    if (ret != RET_NOERROR) {
        return ret;
    }

    /*
    Check if we can move add the whole amount, we do this by checking against the original cylinder,
    since the queryDestination can return a cylinder that might only hold a part of the full amount.
    */
    uint32_t maxQueryCount = 0;
    ret = destCylinder->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), maxQueryCount, flags);

    if (ret != RET_NOERROR) {
        return ret;
    }

    if (test) {
        return RET_NOERROR;
    }

    if (item->isStackable() && item->getID() == toItem->getID()) {
        uint32_t m = std::min<uint32_t>(item->getItemCount(), maxQueryCount);
        uint32_t n = std::min<uint32_t>(100 - toItem->getItemCount(), m);

        toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);

        int32_t count = m - n;
        if (count > 0) {
            if (item->getItemCount() != count) {
                Item* remainderItem = item->clone();
                remainderItem->setItemCount(count);
                if (internalAddItem(destCylinder, remainderItem, INDEX_WHEREEVER, flags, false) != RET_NOERROR) {
                    FreeThing(remainderItem);
                    remainderCount = count;
                }
            } else {
                toCylinder->__addThing(index, item);

                int32_t itemIndex = toCylinder->__getIndexOfThing(item);
                if (itemIndex != -1) {
                    toCylinder->postAddNotification(item, nullptr, itemIndex);
                }
            }
        } else {
            //fully merged with toItem, item will be destroyed
            item->onRemoved();
            FreeThing(item);

            int32_t itemIndex = toCylinder->__getIndexOfThing(item);
            if (itemIndex != -1) {
                toCylinder->postAddNotification(toItem, nullptr, itemIndex);
            }
        }
    } else {
        toCylinder->__addThing(index, item);

        int32_t itemIndex = toCylinder->__getIndexOfThing(item);
        if (itemIndex != -1) {
            toCylinder->postAddNotification(item, nullptr, itemIndex);
        }
    }

    return RET_NOERROR;
}
 
Last edited:
Looks like he just moved the rest of the code outside that "bool test" check. If that is the only real change, and it works for you, then very well done, and I admit I was mistaken.
 
No I think you’re right in that the bool check made no difference. I think the major difference is the calculation right after isStackable, as it’s not at all the same
Post automatically merged:

This didn't work. The changes I made to the internalAddItem method are now just crashing the server with an unhandled exception. It's either completely broken or I just missed an exception
 
Last edited:
I really think you need to call the query inside there and it will work as you want, the exact location, I am unsure, also, will add a lot more over head too if you do, but that is probably how the original worked. I am thinking they replaced the MaxCount variable with a call to the queryMax function or something. Sorry if what I am saying makes no sense, I'm on my phone at work 😅
 
Back
Top