Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asynchronous Inbox + asynchronous house saving #4561

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e96e679
Complete zero-blocking Inbox implementation using separate thread and…
gunzino Nov 8, 2023
e799c78
don't include inbox in market when it's yet been loaded
gunzino Nov 8, 2023
a73866b
fix compiling error
gunzino Nov 8, 2023
b9b5841
fix some of the clang formatting
gunzino Nov 8, 2023
b5f725f
fix more clang formations
gunzino Nov 8, 2023
9eb9667
fix PlayerDBEntry initialization
gunzino Nov 8, 2023
39db25f
naming bugfix for win32 implementation
gunzino Nov 8, 2023
59a8fce
naming bugfix for win32 implementation
gunzino Nov 8, 2023
5b17a28
send to offline player's inbox using IOInbox::pushDeliveryItems
gunzino Nov 8, 2023
ae24883
clear loading set before making flush
gunzino Nov 8, 2023
aa16531
simplify internalAddThing
gunzino Nov 9, 2023
9b7ca7d
corrected receiver GUID in mailbox
gunzino Nov 9, 2023
62a7dc5
applied modifications from ramon-bernardo
gunzino Nov 9, 2023
df3c967
house.cpp format fix
gunzino Nov 9, 2023
ffefcfb
properly unlock mutex
gunzino Nov 9, 2023
58e50cb
use const uint32_t& guid for all IOInbox functions
gunzino Nov 9, 2023
991ebff
corrected misleading info about thread
gunzino Nov 9, 2023
99dc929
use static for IOInbox::createInboxItem
gunzino Nov 9, 2023
25ce4b5
use const in saveInbox
gunzino Nov 9, 2023
8d9b2b6
deliver items to inbox in situation when player logs out during loadi…
gunzino Nov 9, 2023
1bd9b53
rename g_dispatcherInbox to g_asyncTasks, preparation for async house…
gunzino Nov 21, 2023
9de7e6a
asynchronous, zero-blocking house saving
gunzino Nov 21, 2023
f9ebe3a
fix clang formatting
gunzino Nov 21, 2023
b23c27f
return correct value when saving individual house
gunzino Nov 21, 2023
3a7b4be
moved private variables to .cpp namespace
gunzino Dec 2, 2023
8fcdcde
fixed clang formatting
gunzino Dec 2, 2023
40c17e6
Merge branch 'otland:master' into async-inbox
gunzino Dec 4, 2023
93bc130
fixed conflict
gunzino Jan 14, 2024
34ddc13
Merge branch 'master' into async-inbox
gunzino Jan 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/housetile.cpp
${CMAKE_CURRENT_LIST_DIR}/inbox.cpp
${CMAKE_CURRENT_LIST_DIR}/iologindata.cpp
${CMAKE_CURRENT_LIST_DIR}/ioinbox.cpp
${CMAKE_CURRENT_LIST_DIR}/iomap.cpp
${CMAKE_CURRENT_LIST_DIR}/iomapserialize.cpp
${CMAKE_CURRENT_LIST_DIR}/iomarket.cpp
Expand Down Expand Up @@ -111,6 +112,7 @@ set(tfs_HDR
${CMAKE_CURRENT_LIST_DIR}/housetile.h
${CMAKE_CURRENT_LIST_DIR}/inbox.h
${CMAKE_CURRENT_LIST_DIR}/iologindata.h
${CMAKE_CURRENT_LIST_DIR}/ioinbox.h
${CMAKE_CURRENT_LIST_DIR}/iomap.h
${CMAKE_CURRENT_LIST_DIR}/iomapserialize.h
${CMAKE_CURRENT_LIST_DIR}/iomarket.h
Expand Down
5 changes: 4 additions & 1 deletion src/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,13 +682,16 @@ void Container::postRemoveNotification(Thing* thing, const Cylinder* newParent,

void Container::internalAddThing(Thing* thing) { internalAddThing(0, thing); }

void Container::internalAddThing(uint32_t, Thing* thing)
void Container::internalAddThing(uint32_t index, Thing* thing)
{
Item* item = thing->getItem();
if (!item) {
return;
}

auto it = itemlist.begin() + index;
itemlist.insert(it, item);

item->setParent(this);
itemlist.push_front(item);
updateItemWeight(item->getWeight());
Expand Down
9 changes: 6 additions & 3 deletions src/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,17 @@ bool DBResult::next()
return row;
}

DBInsert::DBInsert(std::string query) : query(std::move(query)) { this->length = this->query.length(); }
DBInsert::DBInsert(std::string query, Database& db) : query(std::move(query)), db(db)
{
this->length = this->query.length();
}

bool DBInsert::addRow(const std::string& row)
{
// adds new row to buffer
const size_t rowLength = row.length();
length += rowLength;
if (length > Database::getInstance().getMaxPacketSize() && !execute()) {
if (length > db.getMaxPacketSize() && !execute()) {
return false;
}

Expand Down Expand Up @@ -253,7 +256,7 @@ bool DBInsert::execute()
}

// executes buffer
bool res = Database::getInstance().executeQuery(query + values);
bool res = db.executeQuery(query + values);
values.clear();
length = query.length();
return res;
Expand Down
18 changes: 13 additions & 5 deletions src/database.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ class Database
return instance;
}

static Database& getAsyncInstance()
{
static Database asyncInstance;
return asyncInstance;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC getInstance already returns an "async" instance, used for other purposes but should be suitable. Why having another instance?

On that note, singleton sucks, but a "doubleton" is even worse 😆 let's just make it not a global and pass it around

Copy link
Contributor Author

@gunzino gunzino Dec 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean byt getInstance returns "async" instance? Is Database class thread-safe? Can database class execute multiple parallel queries from several threads? The asyncInstance is there only to be used by g_asyncTasks thread for async database IO- in this PR it is now only House and Inbox.


/**
* Connects to the database
*
Expand Down Expand Up @@ -162,26 +168,27 @@ class DBResult
class DBInsert
{
public:
explicit DBInsert(std::string query);
explicit DBInsert(std::string query, Database& db = Database::getInstance());
bool addRow(const std::string& row);
bool addRow(std::ostringstream& row);
bool execute();

private:
std::string query;
Database& db;
std::string values;
size_t length;
};

class DBTransaction
{
public:
constexpr DBTransaction() = default;
constexpr DBTransaction(Database& db = Database::getInstance()) : db(db){};

~DBTransaction()
{
if (state == STATE_START) {
Database::getInstance().rollback();
db.rollback();
}
}

Expand All @@ -192,7 +199,7 @@ class DBTransaction
bool begin()
{
state = STATE_START;
return Database::getInstance().beginTransaction();
return db.beginTransaction();
}

bool commit()
Expand All @@ -202,10 +209,11 @@ class DBTransaction
}

state = STATE_COMMIT;
return Database::getInstance().commit();
return db.commit();
}

private:
Database& db;
enum TransactionStates_t
{
STATE_NO_START,
Expand Down
98 changes: 11 additions & 87 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "globalevent.h"
#include "housetile.h"
#include "inbox.h"
#include "ioinbox.h"
#include "iologindata.h"
#include "iomarket.h"
#include "items.h"
Expand Down Expand Up @@ -127,10 +128,13 @@ void Game::setGameState(GameState_t newState)

saveGameState();

g_asyncTasks.addTask([]() { IOInbox::getInstance().flush(); });

g_dispatcher.addTask([this]() { shutdown(); });

g_scheduler.stop();
g_databaseTasks.stop();
g_asyncTasks.stop();
g_dispatcher.stop();
break;
}
Expand Down Expand Up @@ -5454,34 +5458,7 @@ void Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16
return;
}

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

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

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

IOMarket::moveOfferToHistory(offer.id, OFFERSTATE_CANCELLED);
Expand Down Expand Up @@ -5562,36 +5539,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16

player->bankBalance += totalPrice;

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

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

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

if (buyerPlayer->isOffline()) {
IOLoginData::savePlayer(buyerPlayer);
Expand All @@ -5609,34 +5557,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16
removeMoney(player, debitCash);
player->bankBalance -= debitBank;

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

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

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

Player* sellerPlayer = getPlayerByGUID(offer.playerId);
if (sellerPlayer) {
Expand Down Expand Up @@ -5695,7 +5616,10 @@ void Game::parsePlayerNetworkMessage(uint32_t playerId, uint8_t recvByte, Networ
std::vector<Item*> Game::getMarketItemList(uint16_t wareId, uint16_t sufficientCount, const Player& player)
{
uint16_t count = 0;
std::list<Container*> containers{player.getInbox()};
std::list<Container*> containers;
if (Inbox* inbox = player.getInbox()) {
containers.push_front(inbox);
}

for (const auto& chest : player.depotChests) {
if (!chest.second->empty()) {
Expand Down
30 changes: 26 additions & 4 deletions src/house.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "game.h"
#include "housetile.h"
#include "inbox.h"
#include "ioinbox.h"
#include "iologindata.h"
#include "pugicast.h"

Expand Down Expand Up @@ -230,9 +231,24 @@ bool House::transferToDepot(Player* player) const
}
}

for (Item* item : moveItemList) {
g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, item, item->getItemCount(),
nullptr, FLAG_NOLIMIT);
if (Inbox* inbox = player->getInbox()) {
for (Item* item : moveItemList) {
g_game.internalMoveItem(item->getParent(), inbox, INDEX_WHEREEVER, item, item->getItemCount(), nullptr,
FLAG_NOLIMIT);
}
} else {
ItemBlockList itemList;
for (Item* item : moveItemList) {
Item* cloneItem = item->clone();

ReturnValue ret = g_game.internalRemoveItem(item);
if (ret == RETURNVALUE_NOERROR) {
itemList.emplace_back(0, cloneItem);
} else {
delete cloneItem;
}
}
IOInbox::getInstance().savePlayerItems(player, itemList);
}
return true;
}
Expand Down Expand Up @@ -695,7 +711,13 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const
letter->setText(fmt::format(
"Warning! \nThe {:s} rent of {:d} gold for your house \"{:s}\" is payable. Have it within {:d} days or you will lose this house.",
period, house->getRent(), house->getName(), daysLeft));
g_game.internalAddItem(player.getInbox(), letter, INDEX_WHEREEVER, FLAG_NOLIMIT);
if (!player.getInbox()) {
ItemBlockList itemList;
itemList.emplace_back(0, letter);
IOInbox::getInstance().savePlayerItems(player.getGUID(), itemList);
} else {
g_game.internalAddItem(player.getInbox(), letter, INDEX_WHEREEVER, FLAG_NOLIMIT);
}
house->setPayRentWarnings(house->getPayRentWarnings() + 1);
} else {
house->setOwner(0, true, &player);
Expand Down
Loading