diff --git a/src/client/client.cpp b/src/client/client.cpp index e78b9bb8a..4c5485325 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -557,8 +557,8 @@ void Client::step(float dtime) if (count_after != count_before) { // Do this every seconds after TOCLIENT_INVENTORY // Reset the locally changed inventory to the authoritative inventory - m_env.getLocalPlayer()->inventory = *m_inventory_from_server; - m_inventory_updated = true; + player->inventory = *m_inventory_from_server; + m_update_wielded_item = true; } } @@ -1331,28 +1331,30 @@ void Client::setPlayerControl(PlayerControl &control) void Client::setPlayerItem(u16 item) { m_env.getLocalPlayer()->setWieldIndex(item); - m_inventory_updated = true; + m_update_wielded_item = true; NetworkPacket pkt(TOSERVER_PLAYERITEM, 2); pkt << item; Send(&pkt); } -// Returns true if the inventory of the local player has been -// updated from the server. If it is true, it is set to false. -bool Client::getLocalInventoryUpdated() +// Returns true once after the inventory of the local player +// has been updated from the server. +bool Client::updateWieldedItem() { - bool updated = m_inventory_updated; - m_inventory_updated = false; - return updated; -} + if (!m_update_wielded_item) + return false; + + m_update_wielded_item = false; -// Copies the inventory of the local player to parameter -void Client::getLocalInventory(Inventory &dst) -{ LocalPlayer *player = m_env.getLocalPlayer(); 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) diff --git a/src/client/client.h b/src/client/client.h index 3bfb1631e..85456fe4d 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -274,9 +274,7 @@ public: // Returns true if the inventory of the local player has been // updated from the server. If it is true, it is set to false. - bool getLocalInventoryUpdated(); - // Copies the inventory of the local player to parameter - void getLocalInventory(Inventory &dst); + bool updateWieldedItem(); /* InventoryManager interface */ Inventory* getInventory(const InventoryLocation &loc) override; @@ -504,7 +502,7 @@ private: // If 0, server init hasn't been received yet. u16 m_proto_ver = 0; - bool m_inventory_updated = false; + bool m_update_wielded_item = false; Inventory *m_inventory_from_server = nullptr; float m_inventory_from_server_age = 0.0f; PacketCounter m_packetcounter; diff --git a/src/client/game.cpp b/src/client/game.cpp index 4d5fbfb97..5bf41bcd6 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -599,7 +599,6 @@ struct GameRunData { bool dig_instantly; bool digging_blocked; bool left_punch; - bool update_wielded_item_trigger; bool reset_jump_timer; float nodig_delay_timer; float dig_time; @@ -1018,7 +1017,6 @@ bool Game::startup(bool *kill, // Reinit runData runData = GameRunData(); runData.time_from_last_punch = 10.0; - runData.update_wielded_item_trigger = true; m_game_ui->initFlags(); @@ -3736,19 +3734,11 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, if (player->getWieldIndex() != runData.new_playeritem) client->setPlayerItem(runData.new_playeritem); - // Update local inventory if it has changed - if (client->getLocalInventoryUpdated()) { - //infostream<<"Updating local inventory"<updateWieldedItem()) { // Update wielded tool ItemStack selected_item, hand_item; ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); camera->wield(tool_item); - - runData.update_wielded_item_trigger = false; } /* diff --git a/src/inventory.cpp b/src/inventory.cpp index f2cc2ede3..375f0f147 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventory.h" #include "serialization.h" #include "debug.h" +#include #include #include "log.h" #include "itemdef.h" @@ -382,27 +383,32 @@ void InventoryList::clearItems() m_items.emplace_back(); } - //setDirty(true); + setModified(); } void InventoryList::setSize(u32 newsize) { - if(newsize != m_items.size()) - m_items.resize(newsize); + if (newsize == m_items.size()) + return; + + m_items.resize(newsize); m_size = newsize; + setModified(); } void InventoryList::setWidth(u32 newwidth) { m_width = newwidth; + setModified(); } void InventoryList::setName(const std::string &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")); @@ -415,6 +421,9 @@ void InventoryList::serialize(std::ostream &os) const os<<"Item "; item.serialize(os); } + // TODO: Implement this: + // if (!incremental || item.checkModified()) + // os << "Keep"; os<<"\n"; } @@ -424,8 +433,8 @@ void InventoryList::serialize(std::ostream &os) const void InventoryList::deSerialize(std::istream &is) { //is.imbue(std::locale("C")); + setModified(); - clearItems(); u32 item_i = 0; m_width = 0; @@ -439,12 +448,12 @@ void InventoryList::deSerialize(std::istream &is) std::string name; std::getline(iss, name, ' '); - if (name == "EndInventoryList") - return; - - // This is a temporary backwards compatibility fix - if (name == "end") + if (name == "EndInventoryList" || name == "end") { + // If partial incremental: Clear leftover items (should not happen!) + for (size_t i = item_i; i < m_items.size(); ++i) + m_items[i].clear(); return; + } if (name == "Width") { iss >> m_width; @@ -464,6 +473,8 @@ void InventoryList::deSerialize(std::istream &is) if(item_i > getSize() - 1) throw SerializationError("too many items"); 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]; m_items[i] = newitem; - //setDirty(true); + setModified(); return olditem; } @@ -565,6 +576,7 @@ void InventoryList::deleteItem(u32 i) { assert(i < m_items.size()); // Pre-condition m_items[i].clear(); + setModified(); } ItemStack InventoryList::addItem(const ItemStack &newitem_) @@ -612,8 +624,8 @@ ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem) return newitem; ItemStack leftover = m_items[i].addItem(newitem, m_itemdef); - //if(leftover != newitem) - // setDirty(true); + if (leftover != newitem) + setModified(); return leftover; } @@ -682,8 +694,8 @@ ItemStack InventoryList::takeItem(u32 i, u32 takecount) return ItemStack(); ItemStack taken = m_items[i].takeItem(takecount); - //if(!taken.empty()) - // setDirty(true); + if (!taken.empty()) + setModified(); return taken; } @@ -788,16 +800,6 @@ void Inventory::clear() m_lists.clear(); } -void Inventory::clearContents() -{ - m_dirty = true; - for (InventoryList *list : m_lists) { - for (u32 j=0; jgetSize(); j++) { - list->deleteItem(j); - } - } -} - Inventory::Inventory(IItemDefManager *itemdef) { m_dirty = false; @@ -807,7 +809,6 @@ Inventory::Inventory(IItemDefManager *itemdef) Inventory::Inventory(const Inventory &other) { *this = other; - m_dirty = false; } Inventory & Inventory::operator = (const Inventory &other) @@ -838,11 +839,15 @@ bool Inventory::operator == (const Inventory &other) const return true; } -void Inventory::serialize(std::ostream &os) const +void Inventory::serialize(std::ostream &os, bool incremental) const { - for (InventoryList *list : m_lists) { - os<<"List "<getName()<<" "<getSize()<<"\n"; - list->serialize(os); + for (const InventoryList *list : m_lists) { + if (!incremental || list->checkModified()) { + os << "List " << list->getName() << " " << list->getSize() << "\n"; + list->serialize(os, incremental); + } else { + os << "KeepList " << list->getName() << "\n"; + } } os<<"EndInventory\n"; @@ -850,7 +855,8 @@ void Inventory::serialize(std::ostream &os) const void Inventory::deSerialize(std::istream &is) { - clear(); + std::vector new_lists; + new_lists.reserve(m_lists.size()); while (is.good()) { std::string line; @@ -861,12 +867,20 @@ void Inventory::deSerialize(std::istream &is) std::string name; std::getline(iss, name, ' '); - if (name == "EndInventory") - return; + if (name == "EndInventory" || name == "end") { + // 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 - if (name == "end") + delete list; + list = nullptr; + m_dirty = true; + } + m_lists.erase(std::remove(m_lists.begin(), m_lists.end(), + nullptr), m_lists.end()); return; + } if (name == "List") { std::string listname; @@ -875,15 +889,33 @@ void Inventory::deSerialize(std::istream &is) std::getline(iss, listname, ' '); 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); - m_lists.push_back(list); - } - else - { - throw SerializationError("invalid inventory specifier: " + name); + new_lists.push_back(list); + if (create_new) + 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; + } } + // 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. diff --git a/src/inventory.h b/src/inventory.h index 3f299993a..b7a93553d 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -194,7 +194,7 @@ public: void setSize(u32 newsize); void setWidth(u32 newWidth); 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); InventoryList(const InventoryList &other); @@ -265,12 +265,16 @@ public: // also with optional rollback recording 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: std::vector m_items; std::string m_name; u32 m_size; u32 m_width = 0; IItemDefManager *m_itemdef; + bool m_dirty = true; }; class Inventory @@ -279,7 +283,6 @@ public: ~Inventory(); void clear(); - void clearContents(); Inventory(IItemDefManager *itemdef); Inventory(const Inventory &other); @@ -290,7 +293,8 @@ public: 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); InventoryList * addList(const std::string &name, u32 size); @@ -301,28 +305,35 @@ public: // A shorthand for adding items. Returns leftover item (possibly empty). ItemStack addItem(const std::string &listname, const ItemStack &newitem) { - m_dirty = true; InventoryList *list = getList(listname); if(list == NULL) return 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: // -1 if not found const s32 getListIndex(const std::string &name) const; std::vector m_lists; IItemDefManager *m_itemdef; - bool m_dirty = false; + bool m_dirty = true; }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 520fcfa81..91829474e 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -333,7 +333,7 @@ void Client::handleCommand_Inventory(NetworkPacket* pkt) player->inventory.deSerialize(is); - m_inventory_updated = true; + m_update_wielded_item = true; delete m_inventory_from_server; m_inventory_from_server = new Inventory(player->inventory); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 451518bbf..05737d0a9 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -195,9 +195,12 @@ with this program; if not, write to the Free Software Foundation, Inc., ContentFeatures version 13 Add full Euler rotations instead of just yaw 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) // Server's supported network protocol range diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 5ad60f48a..1965d87aa 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -732,7 +732,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) // Eat the action delete a; - SendInventory(playersao); + SendInventory(playersao, true); } void Server::handleCommand_ChatMessage(NetworkPacket* pkt) @@ -1310,7 +1310,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Apply returned ItemStack if (playersao->setWieldedItem(item)) { - SendInventory(playersao); + SendInventory(playersao, true); } } @@ -1346,7 +1346,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) item, playersao, pointed)) { // Apply returned ItemStack if (playersao->setWieldedItem(item)) { - SendInventory(playersao); + SendInventory(playersao, true); } } @@ -1364,7 +1364,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) if (m_script->item_OnSecondaryUse( item, playersao)) { if( playersao->setWieldedItem(item)) { - SendInventory(playersao); + SendInventory(playersao, true); } } } // action == INTERACT_ACTIVATE diff --git a/src/remoteplayer.h b/src/remoteplayer.h index ea118e604..260504fb4 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -110,12 +110,7 @@ public: bool checkModified() const { return m_dirty || inventory.checkModified(); } - void setModified(const bool x) - { - m_dirty = x; - if (!x) - inventory.setModified(x); - } + inline void setModified(const bool x) { m_dirty = x; } void setLocalAnimations(v2s32 frames[4], float frame_speed) { diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index be2172f1b..68cc1c0a9 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -355,7 +355,7 @@ int ObjectRef::l_set_wielded_item(lua_State *L) ItemStack item = read_item(L, 2, getServer(L)->idef()); bool success = co->setWieldedItem(item); if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - getServer(L)->SendInventory(((PlayerSAO*)co)); + getServer(L)->SendInventory((PlayerSAO *)co, true); } lua_pushboolean(L, success); return 1; diff --git a/src/server.cpp b/src/server.cpp index 871612e60..59bc12581 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1057,7 +1057,7 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) SendPlayerInventoryFormspec(peer_id); // Send inventory - SendInventory(playersao); + SendInventory(playersao, false); // Send HP or death screen if (playersao->isDead()) @@ -1241,19 +1241,22 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend) break; case InventoryLocation::PLAYER: { - if (!playerSend) - return; RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); if (!player) return; + player->setModified(true); + + if (!playerSend) + return; + PlayerSAO *playersao = player->getPlayerSAO(); if(!playersao) return; - SendInventory(playersao); + SendInventory(playersao, true); } break; case InventoryLocation::NODEMETA: @@ -1527,21 +1530,27 @@ void Server::SendNodeDef(session_t peer_id, 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 */ - NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID()); + NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID()); - std::ostringstream os; - playerSAO->getInventory()->serialize(os); - - std::string s = os.str(); + std::ostringstream os(std::ios::binary); + sao->getInventory()->serialize(os, incremental); + sao->getInventory()->setModified(false); + player->setModified(true); + const std::string &s = os.str(); pkt.putRawString(s.c_str(), s.size()); Send(&pkt); } @@ -2595,8 +2604,9 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id) // Serialization & NetworkPacket isn't a love story std::ostringstream os(std::ios_base::binary); 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(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients pkt.putRawString(os_str); } @@ -2822,6 +2832,11 @@ void Server::UpdateCrafting(RemotePlayer *player) if (!clist || clist->getSize() == 0) return; + if (!clist->checkModified()) { + verbosestream << "Skip Server::UpdateCrafting(): list unmodified" << std::endl; + return; + } + // Get a preview for crafting ItemStack preview; InventoryLocation loc; diff --git a/src/server.h b/src/server.h index 06adbad68..d1dcafac3 100644 --- a/src/server.h +++ b/src/server.h @@ -333,7 +333,7 @@ public: void SendPlayerHPOrDie(PlayerSAO *player, const PlayerHPChangeReason &reason); void SendPlayerBreath(PlayerSAO *sao); - void SendInventory(PlayerSAO* playerSAO); + void SendInventory(PlayerSAO *playerSAO, bool incremental); void SendMovePlayer(session_t peer_id); void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); diff --git a/src/unittest/test_inventory.cpp b/src/unittest/test_inventory.cpp index 1a783afae..5f71636c4 100644 --- a/src/unittest/test_inventory.cpp +++ b/src/unittest/test_inventory.cpp @@ -33,8 +33,9 @@ public: void testSerializeDeserialize(IItemDefManager *idef); - static const char *serialized_inventory; - static const char *serialized_inventory_2; + static const char *serialized_inventory_in; + static const char *serialized_inventory_out; + static const char *serialized_inventory_inc; }; static TestInventory g_test_instance; @@ -49,7 +50,7 @@ void TestInventory::runTests(IGameDef *gamedef) void TestInventory::testSerializeDeserialize(IItemDefManager *idef) { Inventory inv(idef); - std::istringstream is(serialized_inventory, std::ios::binary); + std::istringstream is(serialized_inventory_in, std::ios::binary); inv.deSerialize(is); UASSERT(inv.getList("0")); @@ -62,82 +63,64 @@ void TestInventory::testSerializeDeserialize(IItemDefManager *idef) inv.getList("main")->setWidth(5); std::ostringstream inv_os(std::ios::binary); - inv.serialize(inv_os); - UASSERTEQ(std::string, inv_os.str(), serialized_inventory_2); + inv.serialize(inv_os, false); + 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 = - "List 0 32\n" +const char *TestInventory::serialized_inventory_in = + "List 0 10\n" "Width 3\n" "Empty\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" "Item default:cobble 61\n" "Empty\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" "Item default:dirt 71\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" "Item default:dirt 99\n" "Item default:cobble 38\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" + "EndInventoryList\n" + "List abc 1\n" + "Item default:stick 3\n" + "Width 0\n" "EndInventoryList\n" "EndInventory\n"; -const char *TestInventory::serialized_inventory_2 = - "List main 32\n" +const char *TestInventory::serialized_inventory_out = + "List main 10\n" "Width 5\n" "Empty\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" "Item default:cobble 61\n" "Empty\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" "Item default:dirt 71\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" "Item default:dirt 99\n" "Item default:cobble 38\n" "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" - "Empty\n" + "EndInventoryList\n" + "List abc 1\n" + "Width 0\n" + "Item default:stick 3\n" "EndInventoryList\n" "EndInventory\n"; + +const char *TestInventory::serialized_inventory_inc = + "KeepList main\n" + "KeepList abc\n" + "EndInventory\n"; diff --git a/src/unittest/test_servermodmanager.cpp b/src/unittest/test_servermodmanager.cpp index 72ac7c6bf..0757323f4 100644 --- a/src/unittest/test_servermodmanager.cpp +++ b/src/unittest/test_servermodmanager.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "test.h" #include #include "server/mods.h" +#include "settings.h" #include "test_config.h" class TestServerModManager : public TestBase @@ -85,6 +86,10 @@ void TestServerModManager::runTests(IGameDef *gamedef) 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); } diff --git a/src/unittest/test_world/do_not_remove.txt b/src/unittest/test_world/do_not_remove.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/unittest/test_world/world.mt b/src/unittest/test_world/world.mt deleted file mode 100644 index ab9b5413a..000000000 --- a/src/unittest/test_world/world.mt +++ /dev/null @@ -1 +0,0 @@ -gameid = minimal