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

Login doesn't trigger bed revert

Dip Set

Veteran OT User
Joined
Dec 27, 2007
Messages
405
Solutions
2
Reaction score
369
When a player uses a bed, it converts from playernotsleeping to playersleeping.
onLook ->Dip Set is sleeping.
when a player logs in, it stays visually playersleeping
onLook -> nobody is sleeping.
when a player uses the bed, it reverts to nobody in bed visually but
onLook -> Dip Set is sleeping

So basically when you "use" the bed, it works properly, and transform nobody in bed -> player sleeping in bed
when player is sleeping in bed, it shows properly which character is in the bed
the only problem is on login, it's not triggering bed conversion

TFS 1.3, does anyone have any guidance/direction? I'm not looking for exact answer here, just some direction.

Thank you

my player.cpp wakeup reference

C++:
        BedItem *bed = g_game.getBedBySleeper(guid);
        if (bed)
        {
            bed->wakeUp(this);
        }


game.cpp
C++:
BedItem* Game::getBedBySleeper(uint32_t guid) const
{
    auto it = bedSleepersMap.find(guid);
    if (it == bedSleepersMap.end()) {
        return nullptr;
    }
    return it->second;
}

void Game::setBedSleeper(BedItem* bed, uint32_t guid)
{
    bedSleepersMap[guid] = bed;
}

void Game::removeBedSleeper(uint32_t guid)
{
    auto it = bedSleepersMap.find(guid);
    if (it != bedSleepersMap.end()) {
        bedSleepersMap.erase(it);
    }
}

my bed.cpp (sorry for the length)
C++:
BedItem::BedItem(uint16_t id) : Item(id)
{
    internalRemoveSleeper();
}

Attr_ReadValue BedItem::readAttr(AttrTypes_t attr, PropStream& propStream)
{
    switch (attr) {
        case ATTR_SLEEPERGUID: {
            uint32_t guid;
            if (!propStream.read<uint32_t>(guid)) {
                return ATTR_READ_ERROR;
            }

            if (guid != 0) {
                std::string name = IOLoginData::getNameByGuid(guid);
                if (!name.empty()) {
                    setSpecialDescription(name + " is sleeping there.");
                    g_game.setBedSleeper(this, guid);
                    sleeperGUID = guid;
                }
            }
            return ATTR_READ_CONTINUE;
        }

        case ATTR_SLEEPSTART: {
            uint32_t sleep_start;
            if (!propStream.read<uint32_t>(sleep_start)) {
                return ATTR_READ_ERROR;
            }

            sleepStart = static_cast<uint64_t>(sleep_start);
            return ATTR_READ_CONTINUE;
        }

        default:
            break;
    }
    return Item::readAttr(attr, propStream);
}

void BedItem::serializeAttr(PropWriteStream& propWriteStream) const
{
    if (sleeperGUID != 0) {
        propWriteStream.write<uint8_t>(ATTR_SLEEPERGUID);
        propWriteStream.write<uint32_t>(sleeperGUID);
    }

    if (sleepStart != 0) {
        propWriteStream.write<uint8_t>(ATTR_SLEEPSTART);
        // FIXME: should be stored as 64-bit, but we need to retain backwards compatibility
        propWriteStream.write<uint32_t>(static_cast<uint32_t>(sleepStart));
    }
}

BedItem* BedItem::getNextBedItem() const
{
    Direction dir = Item::items[id].bedPartnerDir;
    Position targetPos = getNextPosition(dir, getPosition());

    Tile* tile = g_game.map.getTile(targetPos);
    if (tile == nullptr) {
        return nullptr;
    }
    return tile->getBedItem();
}

bool BedItem::canUse(Player* player)
{
    if ((player == nullptr) || (house == nullptr) || !player->isPremium()) {
        return false;
    }

    if (sleeperGUID == 0) {
        return true;
    }

    if (house->getHouseAccessLevel(player) == HOUSE_OWNER) {
        return true;
    }

    Player sleeper(nullptr);
    if (!IOLoginData::loadPlayerById(&sleeper, sleeperGUID)) {
        return false;
    }

    if (house->getHouseAccessLevel(&sleeper) > house->getHouseAccessLevel(player)) {
        return false;
    }
    return true;
}

bool BedItem::trySleep(Player* player)
{
    if (!house || player->isRemoved()) {
        return false;
    }

    if (sleeperGUID != 0) {
        if (Item::items[id].transformToFree != 0 && house->getOwner() == player->getGUID()) {
            wakeUp(nullptr);
        }

        g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
        return false;
    }
    return true;
}

bool BedItem::sleep(Player* player)
{
    if (house == nullptr) {
        return false;
    }

    if (sleeperGUID != 0) {
        return false;
    }

    BedItem* nextBedItem = getNextBedItem();

    internalSetSleeper(player);

    if (nextBedItem != nullptr) {
        nextBedItem->internalSetSleeper(player);
    }

    // update the bedSleepersMap
    g_game.setBedSleeper(this, player->getGUID());

    // make the player walk onto the bed
    g_game.map.moveCreature(*player, *getTile());

    // display 'Zzzz'/sleep effect
    g_game.addMagicEffect(player->getPosition(), CONST_ME_SLEEP);

    // kick player after he sees himself walk onto the bed and it change id
    uint32_t playerId = player->getID();
    g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&Game::kickPlayer, &g_game, playerId, false)));

    // change self and partner's appearance
    updateAppearance(player);

    if (nextBedItem != nullptr) {
        nextBedItem->updateAppearance(player);
    }

    return true;
}

void BedItem::wakeUp(Player* player)
{
    if (house == nullptr) {
        return;
    }

    if (sleeperGUID != 0) {
        if (player == nullptr) {
            Player regenPlayer(nullptr);
            if (IOLoginData::loadPlayerById(&regenPlayer, sleeperGUID)) {
                regeneratePlayer(&regenPlayer);
                IOLoginData::savePlayer(&regenPlayer);
            }
        } else {
            regeneratePlayer(player);
            g_game.addCreatureHealth(player);
        }
    }

    // update the bedSleepersMap
    g_game.removeBedSleeper(sleeperGUID);

    BedItem* nextBedItem = getNextBedItem();

    // unset sleep info
    internalRemoveSleeper();

    if (nextBedItem != nullptr) {
        nextBedItem->internalRemoveSleeper();
    }

    // change self and partner's appearance
    updateAppearance(nullptr);

    if (nextBedItem != nullptr) {
        nextBedItem->updateAppearance(nullptr);
    }
}

void BedItem::regeneratePlayer(Player* player) const
{
    const uint32_t sleptTime = time(nullptr) - sleepStart;

    Condition* condition = player->getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
    if (condition != nullptr) {
        uint32_t regen;
        if (condition->getTicks() != -1) {
            regen = std::min<int32_t>((condition->getTicks() / 1000), sleptTime) / 30;
            const int32_t newRegenTicks = condition->getTicks() - (regen * 30000);
            if (newRegenTicks <= 0) {
                player->removeCondition(condition);
            } else {
                condition->setTicks(newRegenTicks);
            }
        } else {
            regen = sleptTime / 30;
        }

        player->changeHealth(regen, false);
        player->changeMana(regen);
    }

    const int32_t soulRegen = sleptTime / (60 * 15);
    player->changeSoul(soulRegen);
}

void BedItem::updateAppearance(const Player* player)
{
    const ItemType& it = Item::items[id];
    if (it.type == ITEM_TYPE_BED) {
        if ((player != nullptr) && it.transformToOnUse[player->getSex()] != 0) {
            const ItemType& newType = Item::items[it.transformToOnUse[player->getSex()]];
            if (newType.type == ITEM_TYPE_BED) {
                g_game.transformItem(this, it.transformToOnUse[player->getSex()]);
            }
        } else if (it.transformToFree != 0) {
            const ItemType& newType = Item::items[it.transformToFree];
            if (newType.type == ITEM_TYPE_BED) {
                g_game.transformItem(this, it.transformToFree);
            }
        }
    }
}

void BedItem::internalSetSleeper(const Player* player)
{
    std::string desc_str = player->getName() + " is sleeping there.";

    sleeperGUID = player->getGUID();
    sleepStart = time(nullptr);
    setSpecialDescription(desc_str);
}

void BedItem::internalRemoveSleeper()
{
    sleeperGUID = 0;
    sleepStart = 0;
    setSpecialDescription("Nobody is sleeping there.");
}
 
Last edited:
I am thinking maybe the issue is with pulling guid?
1. start with fresh bed, player uses it
1615129446910.png
2. player is sleeping, shows player is sleeping, bed looks proper
1615129514111.png
3. player logs in, shows no player is sleeping, but bed looks improper -- BUG IS HERE
1615129554763.png
4. player uses bed, shows player is sleeping there, but bed looks improper
1615129590309.png
5. player logs in, shows no player is sleeping there, but bed reverts
1615129625861.png

so on step 2, when player FIRST appears after sleep, bed revert is not being triggered.
 
C++:
void BedItem::updateAppearance(const Player* player)
{
    const ItemType& it = Item::items[id];
    if (it.type == ITEM_TYPE_BED) {
        if ((player != nullptr) && it.transformToOnUse[player->getSex()] != 0) {
            const ItemType& newType = Item::items[it.transformToOnUse[player->getSex()]];
            if (newType.type == ITEM_TYPE_BED) {
                std::cout << "Bed updateAppearance1 triggered!";
                g_game.transformItem(this, it.transformToOnUse[player->getSex()]);
            }
        } else if (it.transformToFree != 0) {
            const ItemType& newType = Item::items[it.transformToFree];
            if (newType.type == ITEM_TYPE_BED) {
                std::cout << "Bed updateAppearance2 triggered!";
                g_game.transformItem(this, it.transformToFree);
            }
        }
    }
}

console messages
When using a bed that is already "bugged"
C++:
Bed updateAppearance1 triggered!Bed updateAppearance1 triggered!God Dip Set has logged out.
Bed updateAppearance2 triggered!Bed updateAppearance2 triggered!God Dip Set has logged in.

but INTERESTING!
when using "new" bed

C++:
Bed updateAppearance1 triggered!Bed updateAppearance1 triggered!God Dip Set has logged out.
God Dip Set has logged in.
.

so this section is not triggering with "new" beds

C++:
  } else if (it.transformToFree != 0) {
            const ItemType& newType = Item::items[it.transformToFree];
            if (newType.type == ITEM_TYPE_BED) {
                std::cout << "Bed updateAppearance2 triggered!";
                g_game.transformItem(this, it.transformToFree);
            }
 
I'm wondering if bed-wakeup is ever executed on login 🤔 .
Add a print before internalRemoveSleeper(); in wakeUp method.
Will do! I’m not home at the moment but when I get home I’ll try this and let you know.
Post automatically merged:

you should attach debugger and set breakpoint & step through it
Do you have any links to tutorials about how to attach a debugger and/or set breakpoints? If i understand correctly, essentially this would save me from having to make change -> compile -> test -> make change -> compile -> test, right?
Post automatically merged:

I'm wondering if bed-wakeup is ever executed on login 🤔 .
Add a print before internalRemoveSleeper(); in wakeUp method.
C++:
Bed updateAppearance1 triggered! ---> God Dip Set has logged out.



unsetsleepinfo triggered!
nextBedItem triggered!
Bed updateAppearance2 triggered! --->God Dip Set has logged in.
I'm really lost when it comes to c++

(unsetsleepinfo is printed with internalRemoveSleeper())
 
Last edited:
Back
Top