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

Solved Make house save tiles [TFS 1.2]

Lakkir

Active Member
Joined
Jan 6, 2009
Messages
29
Reaction score
25
Location
Chile
First, Happy new year to all!
Well, this is hard to explain in a couple of words, basically I made a system that allows the players change their house tiles, the problem it's when the server restarts, the tiles obviously back to normal, then i guess if there is a way to make the house also saves the floor in the database.

So, i'll post some things that I found that could be usefull to slove this problem...
All the sources are at the end!

In IOMapSerialize.cpp

The most important first and yes, it saves non-movable items

Code:
void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile)
{
    const TileItemVector* tileItems = tile->getItemList();
    if (!tileItems) {
        return;
    }

    std::forward_list<Item*> items;
    uint16_t count = 0;
    for (Item* item : *tileItems) {
        const ItemType& it = Item::items[item->getID()];
        //Note that these are NEGATED, ie. these are the items that will be saved.
        if (!(!item->isMoveable() || it.moveable || item->getDoor() || (item->getContainer() && !item->getContainer()->empty()) || it.canWriteText || item->getBed()) {
            continue;
        }

        items.push_front(item);
        ++count;
    }

    if (!items.empty()) {
        const Position& tilePosition = tile->getPosition();
        stream.write<uint16_t>(tilePosition.x);
        stream.write<uint16_t>(tilePosition.y);
        stream.write<uint8_t>(tilePosition.z);

        stream.write<uint32_t>(count);
        for (const Item* item : items) {
            saveItem(stream, item);
        }
    }
}

Code:
void IOMapSerialize::loadHouseItems(Map* map)
{
    int64_t start = OTSYS_TIME();

    DBResult_ptr result = Database::getInstance()->storeQuery("SELECT `data` FROM `tile_store`");
    if (!result) {
        return;
    }

    do {
        unsigned long attrSize;
        const char* attr = result->getStream("data", attrSize);

        PropStream propStream;
        propStream.init(attr, attrSize);

        uint16_t x, y;
        uint8_t z;
        if (!propStream.read<uint16_t>(x) || !propStream.read<uint16_t>(y) || !propStream.read<uint8_t>(z)) {
            continue;
        }

        Tile* tile = map->getTile(x, y, z);
        if (!tile) {
            continue;
        }

        uint32_t item_count;
        if (!propStream.read<uint32_t>(item_count)) {
            continue;
        }

        while (item_count--) {
            loadItem(propStream, tile);
        }
    } while (result->next());
    std::cout << "> Loaded house items in: " << (OTSYS_TIME() - start) / (1000.) << " s" << std::endl;
}

Code:
bool IOMapSerialize::saveHouseItems()
{
    int64_t start = OTSYS_TIME();
    Database* db = Database::getInstance();
    std::ostringstream query;

    //Start the transaction
    DBTransaction transaction;
    if (!transaction.begin()) {
        return false;
    }

    //clear old tile data
    if (!db->executeQuery("DELETE FROM `tile_store`")) {
        return false;
    }

    DBInsert stmt("INSERT INTO `tile_store` (`house_id`, `data`) VALUES ");

    PropWriteStream stream;
    for (const auto& it : g_game.map.houses.getHouses()) {
        //save house items
        House* house = it.second;
        for (HouseTile* tile : house->getTiles()) {
            saveTile(stream, tile);
            size_t attributesSize;
            const char* attributes = stream.getStream(attributesSize);
            if (attributesSize > 0) {
                query << house->getId() << ',' << db->escapeBlob(attributes, attributesSize);
                if (!stmt.addRow(query)) {
                    return false;
                }
                stream.clear();
            }
        }
    }

    if (!stmt.execute()) {
        return false;
    }

    //End the transaction
    bool success = transaction.commit();
    std::cout << "> Saved house items in: " <<
              (OTSYS_TIME() - start) / (1000.) << " s" << std::endl;
    return success;
}

Sources:
src.rar


Edit----
I did it myself with lua
Thanks whitevo for the idea

Code:
function onSay(player, words, param)
    local minimo ={x=100,y=100}
    local maximo ={x=200,y=200}
    db.query("TRUNCATE TABLE `tile_ground_store`")
    for i=minimo.x,maximo.x do
        for j=minimo.y,maximo.y do
            local piso = Tile({x=i,y=j,z=7})
            if piso then
                local ground = piso:getGround()
                local groundid = ground.itemid
                if groundid == 405 then
                    db.asyncQuery("INSERT INTO `tile_ground_store` (`groundid`, `posx`, `posy`, `posz`) VALUES (" .. groundid .. "," .. i .. "," .. j .. ",7);")
                end
            end
        end
    end
end

This in startup.lua

Code:
    --Load wooden floor
    local resultfloor = db.storeQuery("SELECT * FROM `tile_ground_store`")
    repeat
        local posx = result.getDataInt(resultfloor , "posx")
        local posy = result.getDataInt(resultfloor , "posy")
        doCreateItem(405, 1, {x=posx,y=posy,z=7})
    until not result.next(resultfloor)
 
Last edited:
Interesting.
This made me think about how to avoid item duping on server crash.
Because I have planned Sims like house building system for my server, however haven't given much thought yet how I make it and how to avoid house item duping

I wonder how much pressure it is for game if every item what is moved in game is recorded in database.
Each time server starts, the map is loaded first, but then changed according to database.

Currently items are recorded only on players and with character save. Which allow them to dupe items.
 
Actually I did not think in that kind of duping... I think the best way to avoid that is using a more stable server, saving the server often as posible and making a server restart once a day. You should be ok with that I think.
I'm making a survival tibia, like rust, like minecraft, etc...
I use a giant house to make the system simpler, it saves all the movable and non-movable items but it does not save the floor, which is important to build nice houses.

If you want a script to build houses you can check mine, I post it today
https://otland.net/threads/extreme-simple-house-building-script-tfs-1-2.239331/#post-2314685
 
Last edited:
Yes but if you server crashes once a month is veery unlikely that one player it's ready to dupe plus if you save the server constantly it's too damn hard to someone to dupe somthing valuable by mistake.
Also, i think there is a couple of anti dupe systems published, you could check them, maybe those scripts can help you.
 
Yes but if you server crashes once a month is veery unlikely that one player it's ready to dupe plus if you save the server constantly it's too damn hard to someone to dupe somthing valuable by mistake.
Also, i think there is a couple of anti dupe systems published, you could check them, maybe those scripts can help you.
Didn't find any good and again you just said "its less likely items will be duped, but it leaves the option". I just want to remove that issue all together xD

Anyway I just placed the taught out here, I'm not going to make fix for that any time soon. Got lot of other more important things to do first.
 
Just make a new table in database.
in each row write the position and the itemID's what should be created and the order.

on startup (there is even function for that in globalevents) just create the items to these positions.
and you get the position and item from database ofc.

Very simple to do this in LUA.
 
I did not think that, I'll give it a try and tell you what happend
It works, thanks for the idea, i'll put the script in the first post
 
Last edited:
Back
Top