Inventory: Send dirty lists where appropriate (#8742)

This change reduces the amount of sent data towards clients. Inventory lists that are already known to the player are skipped, saving quite some data over time.

Raises protocol version to 38 to ensure correct backwards-compatible code.
This commit is contained in:
SmallJoker 2019-08-24 19:07:38 +02:00 committed by GitHub
parent 008b80fe1c
commit 0b4f424f41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 193 additions and 160 deletions

@ -557,8 +557,8 @@ void Client::step(float dtime)
if (count_after != count_before) { if (count_after != count_before) {
// Do this every <interval> seconds after TOCLIENT_INVENTORY // Do this every <interval> seconds after TOCLIENT_INVENTORY
// Reset the locally changed inventory to the authoritative inventory // Reset the locally changed inventory to the authoritative inventory
m_env.getLocalPlayer()->inventory = *m_inventory_from_server; player->inventory = *m_inventory_from_server;
m_inventory_updated = true; m_update_wielded_item = true;
} }
} }
@ -1331,28 +1331,30 @@ void Client::setPlayerControl(PlayerControl &control)
void Client::setPlayerItem(u16 item) void Client::setPlayerItem(u16 item)
{ {
m_env.getLocalPlayer()->setWieldIndex(item); m_env.getLocalPlayer()->setWieldIndex(item);
m_inventory_updated = true; m_update_wielded_item = true;
NetworkPacket pkt(TOSERVER_PLAYERITEM, 2); NetworkPacket pkt(TOSERVER_PLAYERITEM, 2);
pkt << item; pkt << item;
Send(&pkt); Send(&pkt);
} }
// Returns true if the inventory of the local player has been // Returns true once after the inventory of the local player
// updated from the server. If it is true, it is set to false. // has been updated from the server.
bool Client::getLocalInventoryUpdated() bool Client::updateWieldedItem()
{ {
bool updated = m_inventory_updated; if (!m_update_wielded_item)
m_inventory_updated = false; return false;
return updated;
} m_update_wielded_item = false;
// Copies the inventory of the local player to parameter
void Client::getLocalInventory(Inventory &dst)
{
LocalPlayer *player = m_env.getLocalPlayer(); LocalPlayer *player = m_env.getLocalPlayer();
assert(player); assert(player);
dst = player->inventory; if (auto *list = player->inventory.getList("main"))
list->setModified(false);
if (auto *list = player->inventory.getList("hand"))
list->setModified(false);
return true;
} }
Inventory* Client::getInventory(const InventoryLocation &loc) Inventory* Client::getInventory(const InventoryLocation &loc)

@ -274,9 +274,7 @@ public:
// Returns true if the inventory of the local player has been // Returns true if the inventory of the local player has been
// updated from the server. If it is true, it is set to false. // updated from the server. If it is true, it is set to false.
bool getLocalInventoryUpdated(); bool updateWieldedItem();
// Copies the inventory of the local player to parameter
void getLocalInventory(Inventory &dst);
/* InventoryManager interface */ /* InventoryManager interface */
Inventory* getInventory(const InventoryLocation &loc) override; Inventory* getInventory(const InventoryLocation &loc) override;
@ -504,7 +502,7 @@ private:
// If 0, server init hasn't been received yet. // If 0, server init hasn't been received yet.
u16 m_proto_ver = 0; u16 m_proto_ver = 0;
bool m_inventory_updated = false; bool m_update_wielded_item = false;
Inventory *m_inventory_from_server = nullptr; Inventory *m_inventory_from_server = nullptr;
float m_inventory_from_server_age = 0.0f; float m_inventory_from_server_age = 0.0f;
PacketCounter m_packetcounter; PacketCounter m_packetcounter;

@ -599,7 +599,6 @@ struct GameRunData {
bool dig_instantly; bool dig_instantly;
bool digging_blocked; bool digging_blocked;
bool left_punch; bool left_punch;
bool update_wielded_item_trigger;
bool reset_jump_timer; bool reset_jump_timer;
float nodig_delay_timer; float nodig_delay_timer;
float dig_time; float dig_time;
@ -1018,7 +1017,6 @@ bool Game::startup(bool *kill,
// Reinit runData // Reinit runData
runData = GameRunData(); runData = GameRunData();
runData.time_from_last_punch = 10.0; runData.time_from_last_punch = 10.0;
runData.update_wielded_item_trigger = true;
m_game_ui->initFlags(); m_game_ui->initFlags();
@ -3736,19 +3734,11 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
if (player->getWieldIndex() != runData.new_playeritem) if (player->getWieldIndex() != runData.new_playeritem)
client->setPlayerItem(runData.new_playeritem); client->setPlayerItem(runData.new_playeritem);
// Update local inventory if it has changed if (client->updateWieldedItem()) {
if (client->getLocalInventoryUpdated()) {
//infostream<<"Updating local inventory"<<std::endl;
runData.update_wielded_item_trigger = true;
}
if (runData.update_wielded_item_trigger) {
// Update wielded tool // Update wielded tool
ItemStack selected_item, hand_item; ItemStack selected_item, hand_item;
ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
camera->wield(tool_item); camera->wield(tool_item);
runData.update_wielded_item_trigger = false;
} }
/* /*

@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inventory.h" #include "inventory.h"
#include "serialization.h" #include "serialization.h"
#include "debug.h" #include "debug.h"
#include <algorithm>
#include <sstream> #include <sstream>
#include "log.h" #include "log.h"
#include "itemdef.h" #include "itemdef.h"
@ -382,27 +383,32 @@ void InventoryList::clearItems()
m_items.emplace_back(); m_items.emplace_back();
} }
//setDirty(true); setModified();
} }
void InventoryList::setSize(u32 newsize) void InventoryList::setSize(u32 newsize)
{ {
if(newsize != m_items.size()) if (newsize == m_items.size())
return;
m_items.resize(newsize); m_items.resize(newsize);
m_size = newsize; m_size = newsize;
setModified();
} }
void InventoryList::setWidth(u32 newwidth) void InventoryList::setWidth(u32 newwidth)
{ {
m_width = newwidth; m_width = newwidth;
setModified();
} }
void InventoryList::setName(const std::string &name) void InventoryList::setName(const std::string &name)
{ {
m_name = name; m_name = name;
setModified();
} }
void InventoryList::serialize(std::ostream &os) const void InventoryList::serialize(std::ostream &os, bool incremental) const
{ {
//os.imbue(std::locale("C")); //os.imbue(std::locale("C"));
@ -415,6 +421,9 @@ void InventoryList::serialize(std::ostream &os) const
os<<"Item "; os<<"Item ";
item.serialize(os); item.serialize(os);
} }
// TODO: Implement this:
// if (!incremental || item.checkModified())
// os << "Keep";
os<<"\n"; os<<"\n";
} }
@ -424,8 +433,8 @@ void InventoryList::serialize(std::ostream &os) const
void InventoryList::deSerialize(std::istream &is) void InventoryList::deSerialize(std::istream &is)
{ {
//is.imbue(std::locale("C")); //is.imbue(std::locale("C"));
setModified();
clearItems();
u32 item_i = 0; u32 item_i = 0;
m_width = 0; m_width = 0;
@ -439,12 +448,12 @@ void InventoryList::deSerialize(std::istream &is)
std::string name; std::string name;
std::getline(iss, name, ' '); std::getline(iss, name, ' ');
if (name == "EndInventoryList") if (name == "EndInventoryList" || name == "end") {
return; // If partial incremental: Clear leftover items (should not happen!)
for (size_t i = item_i; i < m_items.size(); ++i)
// This is a temporary backwards compatibility fix m_items[i].clear();
if (name == "end")
return; return;
}
if (name == "Width") { if (name == "Width") {
iss >> m_width; iss >> m_width;
@ -464,6 +473,8 @@ void InventoryList::deSerialize(std::istream &is)
if(item_i > getSize() - 1) if(item_i > getSize() - 1)
throw SerializationError("too many items"); throw SerializationError("too many items");
m_items[item_i++].clear(); m_items[item_i++].clear();
} else if (name == "Keep") {
++item_i; // Unmodified item
} }
} }
@ -557,7 +568,7 @@ ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
ItemStack olditem = m_items[i]; ItemStack olditem = m_items[i];
m_items[i] = newitem; m_items[i] = newitem;
//setDirty(true); setModified();
return olditem; return olditem;
} }
@ -565,6 +576,7 @@ void InventoryList::deleteItem(u32 i)
{ {
assert(i < m_items.size()); // Pre-condition assert(i < m_items.size()); // Pre-condition
m_items[i].clear(); m_items[i].clear();
setModified();
} }
ItemStack InventoryList::addItem(const ItemStack &newitem_) ItemStack InventoryList::addItem(const ItemStack &newitem_)
@ -612,8 +624,8 @@ ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
return newitem; return newitem;
ItemStack leftover = m_items[i].addItem(newitem, m_itemdef); ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
//if(leftover != newitem) if (leftover != newitem)
// setDirty(true); setModified();
return leftover; return leftover;
} }
@ -682,8 +694,8 @@ ItemStack InventoryList::takeItem(u32 i, u32 takecount)
return ItemStack(); return ItemStack();
ItemStack taken = m_items[i].takeItem(takecount); ItemStack taken = m_items[i].takeItem(takecount);
//if(!taken.empty()) if (!taken.empty())
// setDirty(true); setModified();
return taken; return taken;
} }
@ -788,16 +800,6 @@ void Inventory::clear()
m_lists.clear(); m_lists.clear();
} }
void Inventory::clearContents()
{
m_dirty = true;
for (InventoryList *list : m_lists) {
for (u32 j=0; j<list->getSize(); j++) {
list->deleteItem(j);
}
}
}
Inventory::Inventory(IItemDefManager *itemdef) Inventory::Inventory(IItemDefManager *itemdef)
{ {
m_dirty = false; m_dirty = false;
@ -807,7 +809,6 @@ Inventory::Inventory(IItemDefManager *itemdef)
Inventory::Inventory(const Inventory &other) Inventory::Inventory(const Inventory &other)
{ {
*this = other; *this = other;
m_dirty = false;
} }
Inventory & Inventory::operator = (const Inventory &other) Inventory & Inventory::operator = (const Inventory &other)
@ -838,11 +839,15 @@ bool Inventory::operator == (const Inventory &other) const
return true; return true;
} }
void Inventory::serialize(std::ostream &os) const void Inventory::serialize(std::ostream &os, bool incremental) const
{ {
for (InventoryList *list : m_lists) { for (const InventoryList *list : m_lists) {
os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n"; if (!incremental || list->checkModified()) {
list->serialize(os); os << "List " << list->getName() << " " << list->getSize() << "\n";
list->serialize(os, incremental);
} else {
os << "KeepList " << list->getName() << "\n";
}
} }
os<<"EndInventory\n"; os<<"EndInventory\n";
@ -850,7 +855,8 @@ void Inventory::serialize(std::ostream &os) const
void Inventory::deSerialize(std::istream &is) void Inventory::deSerialize(std::istream &is)
{ {
clear(); std::vector<InventoryList *> new_lists;
new_lists.reserve(m_lists.size());
while (is.good()) { while (is.good()) {
std::string line; std::string line;
@ -861,12 +867,20 @@ void Inventory::deSerialize(std::istream &is)
std::string name; std::string name;
std::getline(iss, name, ' '); std::getline(iss, name, ' ');
if (name == "EndInventory") if (name == "EndInventory" || name == "end") {
return; // Remove all lists that were not sent
for (auto &list : m_lists) {
if (std::find(new_lists.begin(), new_lists.end(), list) != new_lists.end())
continue;
// This is a temporary backwards compatibility fix delete list;
if (name == "end") list = nullptr;
m_dirty = true;
}
m_lists.erase(std::remove(m_lists.begin(), m_lists.end(),
nullptr), m_lists.end());
return; return;
}
if (name == "List") { if (name == "List") {
std::string listname; std::string listname;
@ -875,15 +889,33 @@ void Inventory::deSerialize(std::istream &is)
std::getline(iss, listname, ' '); std::getline(iss, listname, ' ');
iss>>listsize; iss>>listsize;
InventoryList *list = new InventoryList(listname, listsize, m_itemdef); InventoryList *list = getList(listname);
bool create_new = !list;
if (create_new)
list = new InventoryList(listname, listsize, m_itemdef);
else
list->setSize(listsize);
list->deSerialize(is); list->deSerialize(is);
new_lists.push_back(list);
if (create_new)
m_lists.push_back(list); m_lists.push_back(list);
} else if (name == "KeepList") {
// Incrementally sent list
std::string listname;
std::getline(iss, listname, ' ');
InventoryList *list = getList(listname);
if (list) {
new_lists.push_back(list);
} else {
errorstream << "Inventory::deSerialize(): Tried to keep list '" <<
listname << "' which is non-existent." << std::endl;
} }
else
{
throw SerializationError("invalid inventory specifier: " + name);
} }
// Any additional fields will throw errors when received by a client
// older than PROTOCOL_VERSION 38
} }
// Contents given to deSerialize() were not terminated properly: throw error. // Contents given to deSerialize() were not terminated properly: throw error.

@ -194,7 +194,7 @@ public:
void setSize(u32 newsize); void setSize(u32 newsize);
void setWidth(u32 newWidth); void setWidth(u32 newWidth);
void setName(const std::string &name); void setName(const std::string &name);
void serialize(std::ostream &os) const; void serialize(std::ostream &os, bool incremental) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
InventoryList(const InventoryList &other); InventoryList(const InventoryList &other);
@ -265,12 +265,16 @@ public:
// also with optional rollback recording // also with optional rollback recording
void moveItemSomewhere(u32 i, InventoryList *dest, u32 count); void moveItemSomewhere(u32 i, InventoryList *dest, u32 count);
inline bool checkModified() const { return m_dirty; }
inline void setModified(bool dirty = true) { m_dirty = dirty; }
private: private:
std::vector<ItemStack> m_items; std::vector<ItemStack> m_items;
std::string m_name; std::string m_name;
u32 m_size; u32 m_size;
u32 m_width = 0; u32 m_width = 0;
IItemDefManager *m_itemdef; IItemDefManager *m_itemdef;
bool m_dirty = true;
}; };
class Inventory class Inventory
@ -279,7 +283,6 @@ public:
~Inventory(); ~Inventory();
void clear(); void clear();
void clearContents();
Inventory(IItemDefManager *itemdef); Inventory(IItemDefManager *itemdef);
Inventory(const Inventory &other); Inventory(const Inventory &other);
@ -290,7 +293,8 @@ public:
return !(*this == other); return !(*this == other);
} }
void serialize(std::ostream &os) const; // Never ever serialize to disk using "incremental"!
void serialize(std::ostream &os, bool incremental = false) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
InventoryList * addList(const std::string &name, u32 size); InventoryList * addList(const std::string &name, u32 size);
@ -301,28 +305,35 @@ public:
// A shorthand for adding items. Returns leftover item (possibly empty). // A shorthand for adding items. Returns leftover item (possibly empty).
ItemStack addItem(const std::string &listname, const ItemStack &newitem) ItemStack addItem(const std::string &listname, const ItemStack &newitem)
{ {
m_dirty = true;
InventoryList *list = getList(listname); InventoryList *list = getList(listname);
if(list == NULL) if(list == NULL)
return newitem; return newitem;
return list->addItem(newitem); return list->addItem(newitem);
} }
bool checkModified() const inline bool checkModified() const
{ {
return m_dirty; if (m_dirty)
return true;
for (const auto &list : m_lists)
if (list->checkModified())
return true;
return false;
} }
void setModified(const bool x) inline void setModified(bool dirty)
{ {
m_dirty = x; m_dirty = dirty;
for (const auto &list : m_lists)
list->setModified(dirty);
} }
private: private:
// -1 if not found // -1 if not found
const s32 getListIndex(const std::string &name) const; const s32 getListIndex(const std::string &name) const;
std::vector<InventoryList*> m_lists; std::vector<InventoryList*> m_lists;
IItemDefManager *m_itemdef; IItemDefManager *m_itemdef;
bool m_dirty = false; bool m_dirty = true;
}; };

@ -333,7 +333,7 @@ void Client::handleCommand_Inventory(NetworkPacket* pkt)
player->inventory.deSerialize(is); player->inventory.deSerialize(is);
m_inventory_updated = true; m_update_wielded_item = true;
delete m_inventory_from_server; delete m_inventory_from_server;
m_inventory_from_server = new Inventory(player->inventory); m_inventory_from_server = new Inventory(player->inventory);

@ -195,9 +195,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
ContentFeatures version 13 ContentFeatures version 13
Add full Euler rotations instead of just yaw Add full Euler rotations instead of just yaw
Add TOCLIENT_PLAYER_SPEED Add TOCLIENT_PLAYER_SPEED
PROTOCOL VERSION 38:
Incremental inventory sending mode
Unknown inventory serialization fields no longer throw an error
*/ */
#define LATEST_PROTOCOL_VERSION 37 #define LATEST_PROTOCOL_VERSION 38
#define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION)
// Server's supported network protocol range // Server's supported network protocol range

@ -732,7 +732,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
// Eat the action // Eat the action
delete a; delete a;
SendInventory(playersao); SendInventory(playersao, true);
} }
void Server::handleCommand_ChatMessage(NetworkPacket* pkt) void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
@ -1310,7 +1310,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Apply returned ItemStack // Apply returned ItemStack
if (playersao->setWieldedItem(item)) { if (playersao->setWieldedItem(item)) {
SendInventory(playersao); SendInventory(playersao, true);
} }
} }
@ -1346,7 +1346,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
item, playersao, pointed)) { item, playersao, pointed)) {
// Apply returned ItemStack // Apply returned ItemStack
if (playersao->setWieldedItem(item)) { if (playersao->setWieldedItem(item)) {
SendInventory(playersao); SendInventory(playersao, true);
} }
} }
@ -1364,7 +1364,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
if (m_script->item_OnSecondaryUse( if (m_script->item_OnSecondaryUse(
item, playersao)) { item, playersao)) {
if( playersao->setWieldedItem(item)) { if( playersao->setWieldedItem(item)) {
SendInventory(playersao); SendInventory(playersao, true);
} }
} }
} // action == INTERACT_ACTIVATE } // action == INTERACT_ACTIVATE

@ -110,12 +110,7 @@ public:
bool checkModified() const { return m_dirty || inventory.checkModified(); } bool checkModified() const { return m_dirty || inventory.checkModified(); }
void setModified(const bool x) inline void setModified(const bool x) { m_dirty = x; }
{
m_dirty = x;
if (!x)
inventory.setModified(x);
}
void setLocalAnimations(v2s32 frames[4], float frame_speed) void setLocalAnimations(v2s32 frames[4], float frame_speed)
{ {

@ -355,7 +355,7 @@ int ObjectRef::l_set_wielded_item(lua_State *L)
ItemStack item = read_item(L, 2, getServer(L)->idef()); ItemStack item = read_item(L, 2, getServer(L)->idef());
bool success = co->setWieldedItem(item); bool success = co->setWieldedItem(item);
if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
getServer(L)->SendInventory(((PlayerSAO*)co)); getServer(L)->SendInventory((PlayerSAO *)co, true);
} }
lua_pushboolean(L, success); lua_pushboolean(L, success);
return 1; return 1;

@ -1057,7 +1057,7 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
SendPlayerInventoryFormspec(peer_id); SendPlayerInventoryFormspec(peer_id);
// Send inventory // Send inventory
SendInventory(playersao); SendInventory(playersao, false);
// Send HP or death screen // Send HP or death screen
if (playersao->isDead()) if (playersao->isDead())
@ -1241,19 +1241,22 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
break; break;
case InventoryLocation::PLAYER: case InventoryLocation::PLAYER:
{ {
if (!playerSend)
return;
RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
if (!player) if (!player)
return; return;
player->setModified(true);
if (!playerSend)
return;
PlayerSAO *playersao = player->getPlayerSAO(); PlayerSAO *playersao = player->getPlayerSAO();
if(!playersao) if(!playersao)
return; return;
SendInventory(playersao); SendInventory(playersao, true);
} }
break; break;
case InventoryLocation::NODEMETA: case InventoryLocation::NODEMETA:
@ -1527,21 +1530,27 @@ void Server::SendNodeDef(session_t peer_id,
Non-static send methods Non-static send methods
*/ */
void Server::SendInventory(PlayerSAO* playerSAO) void Server::SendInventory(PlayerSAO *sao, bool incremental)
{ {
UpdateCrafting(playerSAO->getPlayer()); RemotePlayer *player = sao->getPlayer();
// Do not send new format to old clients
incremental &= player->protocol_version >= 38;
UpdateCrafting(player);
/* /*
Serialize it Serialize it
*/ */
NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID()); NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
std::ostringstream os; std::ostringstream os(std::ios::binary);
playerSAO->getInventory()->serialize(os); sao->getInventory()->serialize(os, incremental);
sao->getInventory()->setModified(false);
std::string s = os.str(); player->setModified(true);
const std::string &s = os.str();
pkt.putRawString(s.c_str(), s.size()); pkt.putRawString(s.c_str(), s.size());
Send(&pkt); Send(&pkt);
} }
@ -2595,8 +2604,9 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
// Serialization & NetworkPacket isn't a love story // Serialization & NetworkPacket isn't a love story
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);
inv_it->second->serialize(os); inv_it->second->serialize(os);
inv_it->second->setModified(false);
std::string os_str = os.str(); const std::string &os_str = os.str();
pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
pkt.putRawString(os_str); pkt.putRawString(os_str);
} }
@ -2822,6 +2832,11 @@ void Server::UpdateCrafting(RemotePlayer *player)
if (!clist || clist->getSize() == 0) if (!clist || clist->getSize() == 0)
return; return;
if (!clist->checkModified()) {
verbosestream << "Skip Server::UpdateCrafting(): list unmodified" << std::endl;
return;
}
// Get a preview for crafting // Get a preview for crafting
ItemStack preview; ItemStack preview;
InventoryLocation loc; InventoryLocation loc;

@ -333,7 +333,7 @@ public:
void SendPlayerHPOrDie(PlayerSAO *player, const PlayerHPChangeReason &reason); void SendPlayerHPOrDie(PlayerSAO *player, const PlayerHPChangeReason &reason);
void SendPlayerBreath(PlayerSAO *sao); void SendPlayerBreath(PlayerSAO *sao);
void SendInventory(PlayerSAO* playerSAO); void SendInventory(PlayerSAO *playerSAO, bool incremental);
void SendMovePlayer(session_t peer_id); void SendMovePlayer(session_t peer_id);
void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); void SendPlayerSpeed(session_t peer_id, const v3f &added_vel);

@ -33,8 +33,9 @@ public:
void testSerializeDeserialize(IItemDefManager *idef); void testSerializeDeserialize(IItemDefManager *idef);
static const char *serialized_inventory; static const char *serialized_inventory_in;
static const char *serialized_inventory_2; static const char *serialized_inventory_out;
static const char *serialized_inventory_inc;
}; };
static TestInventory g_test_instance; static TestInventory g_test_instance;
@ -49,7 +50,7 @@ void TestInventory::runTests(IGameDef *gamedef)
void TestInventory::testSerializeDeserialize(IItemDefManager *idef) void TestInventory::testSerializeDeserialize(IItemDefManager *idef)
{ {
Inventory inv(idef); Inventory inv(idef);
std::istringstream is(serialized_inventory, std::ios::binary); std::istringstream is(serialized_inventory_in, std::ios::binary);
inv.deSerialize(is); inv.deSerialize(is);
UASSERT(inv.getList("0")); UASSERT(inv.getList("0"));
@ -62,82 +63,64 @@ void TestInventory::testSerializeDeserialize(IItemDefManager *idef)
inv.getList("main")->setWidth(5); inv.getList("main")->setWidth(5);
std::ostringstream inv_os(std::ios::binary); std::ostringstream inv_os(std::ios::binary);
inv.serialize(inv_os); inv.serialize(inv_os, false);
UASSERTEQ(std::string, inv_os.str(), serialized_inventory_2); UASSERTEQ(std::string, inv_os.str(), serialized_inventory_out);
inv.setModified(false);
inv_os.str("");
inv_os.clear();
inv.serialize(inv_os, true);
UASSERTEQ(std::string, inv_os.str(), serialized_inventory_inc);
ItemStack leftover = inv.getList("main")->takeItem(7, 99 - 12);
ItemStack wanted = ItemStack("default:dirt", 99 - 12, 0, idef);
UASSERT(leftover == wanted);
leftover = inv.getList("main")->getItem(7);
wanted.count = 12;
UASSERT(leftover == wanted);
} }
const char *TestInventory::serialized_inventory = const char *TestInventory::serialized_inventory_in =
"List 0 32\n" "List 0 10\n"
"Width 3\n" "Width 3\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:cobble 61\n" "Item default:cobble 61\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 71\n" "Item default:dirt 71\n"
"Empty\n" "Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 99\n" "Item default:dirt 99\n"
"Item default:cobble 38\n" "Item default:cobble 38\n"
"Empty\n" "Empty\n"
"Empty\n" "EndInventoryList\n"
"Empty\n" "List abc 1\n"
"Empty\n" "Item default:stick 3\n"
"Empty\n" "Width 0\n"
"Empty\n"
"EndInventoryList\n" "EndInventoryList\n"
"EndInventory\n"; "EndInventory\n";
const char *TestInventory::serialized_inventory_2 = const char *TestInventory::serialized_inventory_out =
"List main 32\n" "List main 10\n"
"Width 5\n" "Width 5\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:cobble 61\n" "Item default:cobble 61\n"
"Empty\n" "Empty\n"
"Empty\n" "Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 71\n" "Item default:dirt 71\n"
"Empty\n" "Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Empty\n"
"Item default:dirt 99\n" "Item default:dirt 99\n"
"Item default:cobble 38\n" "Item default:cobble 38\n"
"Empty\n" "Empty\n"
"Empty\n" "EndInventoryList\n"
"Empty\n" "List abc 1\n"
"Empty\n" "Width 0\n"
"Empty\n" "Item default:stick 3\n"
"Empty\n"
"EndInventoryList\n" "EndInventoryList\n"
"EndInventory\n"; "EndInventory\n";
const char *TestInventory::serialized_inventory_inc =
"KeepList main\n"
"KeepList abc\n"
"EndInventory\n";

@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "test.h" #include "test.h"
#include <algorithm> #include <algorithm>
#include "server/mods.h" #include "server/mods.h"
#include "settings.h"
#include "test_config.h" #include "test_config.h"
class TestServerModManager : public TestBase class TestServerModManager : public TestBase
@ -85,6 +86,10 @@ void TestServerModManager::runTests(IGameDef *gamedef)
void TestServerModManager::testCreation() void TestServerModManager::testCreation()
{ {
std::string path = std::string(TEST_WORLDDIR) + DIR_DELIM + "world.mt";
Settings world_config;
world_config.set("gameid", "minimal");
UASSERTEQ(bool, world_config.updateConfigFile(path.c_str()), true);
ServerModManager sm(TEST_WORLDDIR); ServerModManager sm(TEST_WORLDDIR);
} }

@ -1 +0,0 @@
gameid = minimal