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

TFS 1.X+ [TFS1.2] [10.98] Problem with the market

Haskys

Member
Joined
Jul 19, 2019
Messages
97
Reaction score
8
Location
Poland
Hello,

I have a problem with the market.
The offer cannot be added to the market:

1. Supplementing the required options:
2.JPG

2. Clicking "Create" button turns gray but nothing happens.
3.JPG

3. Lack of item in offers
4.JPG

On another form you can not see the offer.

Database empty:

5.JPG


6.JPG

config.lua:

Lua:
-- Market
marketOfferDuration = 30 * 24 * 60 * 60
premiumToCreateMarketOffer = false
checkExpiredMarketOffersEachMinutes = 60
maxMarketOffersAtATimePerPlayer = 100

iomarket.cpp

C++:
/**
[LIST]
[*]The Forgotten Server - a free and open-source MMORPG server emulator
[*]Copyright (C) 2016  Mark Samman <[email protected]>
[/LIST]
*
[LIST]
[*]This program is free software; you can redistribute it and/or modify
[*]it under the terms of the GNU General Public License as published by
[*]the Free Software Foundation; either version 2 of the License, or
[*](at your option) any later version.
[/LIST]
*
[LIST]
[*]This program is distributed in the hope that it will be useful,
[*]but WITHOUT ANY WARRANTY; without even the implied warranty of
[*]MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
[*]GNU General Public License for more details.
[/LIST]
*
[LIST]
[*]You should have received a copy of the GNU General Public License along
[*]with this program; if not, write to the Free Software Foundation, Inc.,
[*]51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
[/LIST]
*/

#include "otpch.h"

#include "iomarket.h"

#include "configmanager.h"
#include "databasetasks.h"
#include "iologindata.h"
#include "game.h"
#include "scheduler.h"

extern ConfigManager g_config;
extern Game g_game;

MarketOfferList IOMarket::getActiveOffers(MarketAction_t action, uint16_t itemId)
{
    MarketOfferList offerList;

    std::ostringstream query;
    query << "SELECT [ICODE]id[/ICODE], [ICODE]amount[/ICODE], [ICODE]price[/ICODE], [ICODE]created[/ICODE], [ICODE]anonymous[/ICODE], (SELECT [ICODE]name[/ICODE] FROM [ICODE]players[/ICODE] WHERE [ICODE]id[/ICODE] = [ICODE]player_id[/ICODE]) AS [ICODE]player_name[/ICODE] FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]sale[/ICODE] = " << action << " AND [ICODE]itemtype[/ICODE] = " << itemId;

    DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
    if (!result) {
        return offerList;
    }

    const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);

    do {
        MarketOffer offer;
        offer.amount = result->getNumber<uint16_t>("amount");
        offer.price = result->getNumber<uint32_t>("price");
        offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration;
        offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
        if (result->getNumber<uint16_t>("anonymous") == 0) {
            offer.playerName = result->getString("player_name");
        } else {
            offer.playerName = "Anonymous";
        }
        offerList.push_back(offer);
    } while (result->next());
    return offerList;
}

MarketOfferList IOMarket::getOwnOffers(MarketAction_t action, uint32_t playerId)
{
    MarketOfferList offerList;

    const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);

    std::ostringstream query;
    query << "SELECT [ICODE]id[/ICODE], [ICODE]amount[/ICODE], [ICODE]price[/ICODE], [ICODE]created[/ICODE], [ICODE]itemtype[/ICODE] FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]player_id[/ICODE] = " << playerId << " AND [ICODE]sale[/ICODE] = " << action;

    DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
    if (!result) {
        return offerList;
    }

    do {
        MarketOffer offer;
        offer.amount = result->getNumber<uint16_t>("amount");
        offer.price = result->getNumber<uint32_t>("price");
        offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration;
        offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
        offer.itemId = result->getNumber<uint16_t>("itemtype");
        offerList.push_back(offer);
    } while (result->next());
    return offerList;
}

HistoryMarketOfferList IOMarket::getOwnHistory(MarketAction_t action, uint32_t playerId)
{
    HistoryMarketOfferList offerList;

    std::ostringstream query;
    query << "SELECT [ICODE]itemtype[/ICODE], [ICODE]amount[/ICODE], [ICODE]price[/ICODE], [ICODE]expires_at[/ICODE], [ICODE]state[/ICODE] FROM [ICODE]market_history[/ICODE] WHERE [ICODE]player_id[/ICODE] = " << playerId << " AND [ICODE]sale[/ICODE] = " << action;

    DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
    if (!result) {
        return offerList;
    }

    do {
        HistoryMarketOffer offer;
        offer.itemId = result->getNumber<uint16_t>("itemtype");
        offer.amount = result->getNumber<uint16_t>("amount");
        offer.price = result->getNumber<uint32_t>("price");
        offer.timestamp = result->getNumber<uint32_t>("expires_at");

        MarketOfferState_t offerState = static_cast<MarketOfferState_t>(result->getNumber<uint16_t>("state"));
        if (offerState == OFFERSTATE_ACCEPTEDEX) {
            offerState = OFFERSTATE_ACCEPTED;
        }

        offer.state = offerState;

        offerList.push_back(offer);
    } while (result->next());
    return offerList;
}

void IOMarket::processExpiredOffers(DBResult_ptr result, bool)
{
    if (!result) {
        return;
    }

    do {
        if (!IOMarket::moveOfferToHistory(result->getNumber<uint32_t>("id"), OFFERSTATE_EXPIRED)) {
            continue;
        }

        const uint32_t playerId = result->getNumber<uint32_t>("player_id");
        const uint16_t amount = result->getNumber<uint16_t>("amount");
        if (result->getNumber<uint16_t>("sale") == 1) {
            const ItemType& itemType = Item::items[result->getNumber<uint16_t>("itemtype")];
            if (itemType.id == 0) {
                continue;
            }

            Player* player = g_game.getPlayerByGUID(playerId);
            if (!player) {
                player = new Player(nullptr);
                if (!IOLoginData::loadPlayerById(player, playerId)) {
                    delete player;
                    continue;
                }
            }

            if (itemType.stackable) {
                uint16_t tmpAmount = amount;
                while (tmpAmount > 0) {
                    uint16_t stackCount = std::min<uint16_t>(100, tmpAmount);
                    Item* item = Item::CreateItem(itemType.id, stackCount);
                    if (g_game.internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) {
                        delete item;
                        break;
                    }

                    tmpAmount -= stackCount;
                }
            } else {
                int32_t subType;
                if (itemType.charges != 0) {
                    subType = itemType.charges;
                } else {
                    subType = -1;
                }

                for (uint16_t i = 0; i < amount; ++i) {
                    Item* item = Item::CreateItem(itemType.id, subType);
                    if (g_game.internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) {
                        delete item;
                        break;
                    }
                }
            }

            if (player->isOffline()) {
                IOLoginData::savePlayer(player);
                delete player;
            }
        } else {
            uint64_t totalPrice = result->getNumber<uint64_t>("price") * amount;

            Player* player = g_game.getPlayerByGUID(playerId);
            if (player) {
                player->setBankBalance(player->getBankBalance() + totalPrice);
            } else {
                IOLoginData::increaseBankBalance(playerId, totalPrice);
            }
        }
    } while (result->next());
}

void IOMarket::checkExpiredOffers()
{
    const time_t lastExpireDate = time(nullptr) - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);

    std::ostringstream query;
    query << "SELECT [ICODE]id[/ICODE], [ICODE]amount[/ICODE], [ICODE]price[/ICODE], [ICODE]itemtype[/ICODE], [ICODE]player_id[/ICODE], [ICODE]sale[/ICODE] FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]created[/ICODE] <= " << lastExpireDate;
    g_databaseTasks.addTask(query.str(), IOMarket::processExpiredOffers, true);

    int32_t checkExpiredMarketOffersEachMinutes = g_config.getNumber(ConfigManager::CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES);
    if (checkExpiredMarketOffersEachMinutes <= 0) {
        return;
    }

    g_scheduler.addEvent(createSchedulerTask(checkExpiredMarketOffersEachMinutes * 60 * 1000, IOMarket::checkExpiredOffers));
}

uint32_t IOMarket::getPlayerOfferCount(uint32_t playerId)
{
    std::ostringstream query;
    query << "SELECT COUNT(*) AS [ICODE]count[/ICODE] FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]player_id[/ICODE] = " << playerId;

    DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
    if (!result) {
        return 0;
    }
    return result->getNumber<int32_t>("count");
}

MarketOfferEx IOMarket::getOfferByCounter(uint32_t timestamp, uint16_t counter)
{
    MarketOfferEx offer;

    const int32_t created = timestamp - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);

    std::ostringstream query;
    query << "SELECT [ICODE]id[/ICODE], [ICODE]sale[/ICODE], [ICODE]itemtype[/ICODE], [ICODE]amount[/ICODE], [ICODE]created[/ICODE], [ICODE]price[/ICODE], [ICODE]player_id[/ICODE], [ICODE]anonymous[/ICODE], (SELECT [ICODE]name[/ICODE] FROM [ICODE]players[/ICODE] WHERE [ICODE]id[/ICODE] = [ICODE]player_id[/ICODE]) AS [ICODE]player_name[/ICODE] FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]created[/ICODE] = " << created << " AND ([ICODE]id[/ICODE] & 65535) = " << counter << " LIMIT 1";

    DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
    if (!result) {
        offer.id = 0;
        return offer;
    }

    offer.id = result->getNumber<uint32_t>("id");
    offer.type = static_cast<MarketAction_t>(result->getNumber<uint16_t>("sale"));
    offer.amount = result->getNumber<uint16_t>("amount");
    offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
    offer.timestamp = result->getNumber<uint32_t>("created");
    offer.price = result->getNumber<uint32_t>("price");
    offer.itemId = result->getNumber<uint16_t>("itemtype");
    offer.playerId = result->getNumber<uint32_t>("player_id");
    if (result->getNumber<uint16_t>("anonymous") == 0) {
        offer.playerName = result->getString("player_name");
    } else {
        offer.playerName = "Anonymous";
    }
    return offer;
}

void IOMarket::createOffer(uint32_t playerId, MarketAction_t action, uint32_t itemId, uint16_t amount, uint32_t price, bool anonymous)
{
    std::ostringstream query;
    query << "INSERT INTO [ICODE]market_offers[/ICODE] ([ICODE]player_id[/ICODE], [ICODE]sale[/ICODE], [ICODE]itemtype[/ICODE], [ICODE]amount[/ICODE], [ICODE]price[/ICODE], [ICODE]created[/ICODE], [ICODE]anonymous[/ICODE]) VALUES (" << playerId << ',' << action << ',' << itemId << ',' << amount << ',' << price << ',' << time(nullptr) << ',' << anonymous << ')';
    Database::getInstance()->executeQuery(query.str());
}

void IOMarket::acceptOffer(uint32_t offerId, uint16_t amount)
{
    std::ostringstream query;
    query << "UPDATE [ICODE]market_offers[/ICODE] SET [ICODE]amount[/ICODE] = [ICODE]amount[/ICODE] - " << amount << " WHERE [ICODE]id[/ICODE] = " << offerId;
    Database::getInstance()->executeQuery(query.str());
}

void IOMarket::deleteOffer(uint32_t offerId)
{
    std::ostringstream query;
    query << "DELETE FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]id[/ICODE] = " << offerId;
    Database::getInstance()->executeQuery(query.str());
}

void IOMarket::appendHistory(uint32_t playerId, MarketAction_t type, uint16_t itemId, uint16_t amount, uint32_t price, time_t timestamp, MarketOfferState_t state)
{
    std::ostringstream query;
    query << "INSERT INTO [ICODE]market_history[/ICODE] ([ICODE]player_id[/ICODE], [ICODE]sale[/ICODE], [ICODE]itemtype[/ICODE], [ICODE]amount[/ICODE], [ICODE]price[/ICODE], [ICODE]expires_at[/ICODE], [ICODE]inserted[/ICODE], [ICODE]state[/ICODE]) VALUES ("
        << playerId << ',' << type << ',' << itemId << ',' << amount << ',' << price << ','
        << timestamp << ',' << time(nullptr) << ',' << state << ')';
    g_databaseTasks.addTask(query.str());
}

bool IOMarket::moveOfferToHistory(uint32_t offerId, MarketOfferState_t state)
{
    const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);

    Database* db = Database::getInstance();

    std::ostringstream query;
    query << "SELECT [ICODE]player_id[/ICODE], [ICODE]sale[/ICODE], [ICODE]itemtype[/ICODE], [ICODE]amount[/ICODE], [ICODE]price[/ICODE], [ICODE]created[/ICODE] FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]id[/ICODE] = " << offerId;

    DBResult_ptr result = db->storeQuery(query.str());
    if (!result) {
        return false;
    }

    query.str(std::string());
    query << "DELETE FROM [ICODE]market_offers[/ICODE] WHERE [ICODE]id[/ICODE] = " << offerId;
    if (!db->executeQuery(query.str())) {
        return false;
    }

    appendHistory(result->getNumber<uint32_t>("player_id"), static_cast<MarketAction_t>(result->getNumber<uint16_t>("sale")), result->getNumber<uint16_t>("itemtype"), result->getNumber<uint16_t>("amount"), result->getNumber<uint32_t>("price"), result->getNumber<uint32_t>("created") + marketOfferDuration, state);
    return true;
}

void IOMarket::updateStatistics()
{
    std::ostringstream query;
    query << "SELECT [ICODE]sale[/ICODE] AS [ICODE]sale[/ICODE], [ICODE]itemtype[/ICODE] AS [ICODE]itemtype[/ICODE], COUNT([ICODE]price[/ICODE]) AS [ICODE]num[/ICODE], MIN([ICODE]price[/ICODE]) AS [ICODE]min[/ICODE], MAX([ICODE]price[/ICODE]) AS [ICODE]max[/ICODE], SUM([ICODE]price[/ICODE]) AS [ICODE]sum[/ICODE] FROM [ICODE]market_history[/ICODE] WHERE [ICODE]state[/ICODE] = " << OFFERSTATE_ACCEPTED << " GROUP BY [ICODE]itemtype[/ICODE], [ICODE]sale[/ICODE]";
    DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
    if (!result) {
        return;
    }

    do {
        MarketStatistics* statistics;
        if (result->getNumber<uint16_t>("sale") == MARKETACTION_BUY) {
            statistics = &purchaseStatistics[result->getNumber<uint16_t>("itemtype")];
        } else {
            statistics = &saleStatistics[result->getNumber<uint16_t>("itemtype")];
        }

        statistics->numTransactions = result->getNumber<uint32_t>("num");
        statistics->lowestPrice = result->getNumber<uint32_t>("min");
        statistics->totalPrice = result->getNumber<uint64_t>("sum");
        statistics->highestPrice = result->getNumber<uint32_t>("max");
    } while (result->next());
}

MarketStatistics* IOMarket::getPurchaseStatistics(uint16_t itemId)
{
    auto it = purchaseStatistics.find(itemId);
    if (it == purchaseStatistics.end()) {
        return nullptr;
    }
    return &it->second;
}

MarketStatistics* IOMarket::getSaleStatistics(uint16_t itemId)
{
    auto it = saleStatistics.find(itemId);
    if (it == saleStatistics.end()) {
        return nullptr;
    }
    return &it->second;
}

Does anyone know what could be the reason?

Do you need any additional information?

Haskys,
 
Last edited:
Back
Top