From 63611932ebae93620386b26cfa82f7c4552b22ff Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 29 May 2011 21:11:16 +0300 Subject: player passwords and privileges in world/auth.txt --HG-- extra : rebase_source : 7260636295d9068fbeeddf4143c89f2b8a91446c --- src/server.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 14 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 9248e6298..4569d028e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -967,6 +967,7 @@ Server::Server( ): m_env(new ServerMap(mapsavedir), this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), + m_authmanager(mapsavedir+"/auth.txt"), m_thread(this), m_emergethread(this), m_time_counter(0), @@ -1646,7 +1647,7 @@ void Server::AsyncRunStep() } } - // Save map + // Save map, players and auth stuff { float &counter = m_savemap_timer; counter += dtime; @@ -1654,8 +1655,11 @@ void Server::AsyncRunStep() { counter = 0.0; + // Auth stuff + m_authmanager.save(); + + // Map JMutexAutoLock lock(m_env_mutex); - if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true) { // Save only changed parts @@ -1795,7 +1799,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) playername[i] = data[3+i]; } playername[PLAYERNAME_SIZE-1] = 0; - + + if(playername[0]=='\0') + { + derr_server<getPassword(),password)) + + std::string checkpwd; + if(m_authmanager.exists(playername)) + { + checkpwd = m_authmanager.getPassword(playername); + } + else + { + checkpwd = g_settings.get("default_password"); + } + + if(password != checkpwd) { derr_server<getPassword(),password)) + password[PASSWORD_SIZE-1] = 0;*/ + std::string oldpwd; + for(u32 i=0; igetName(); + + if(m_authmanager.exists(playername) == false) { + dstream<<"Server: playername not found in authmanager"<updatePassword(password); + + m_authmanager.setPassword(playername, newpwd); + + dstream<<"Server: password change successful for "<peer_id = PEER_ID_INEXISTENT; player->peer_id = peer_id; player->updateName(name); - player->updatePassword(password); + m_authmanager.add(name); + m_authmanager.setPassword(name, password); + m_authmanager.setPrivs(name, + stringToPrivs(g_settings.get("default_privs"))); if(g_settings.exists("default_privs")) player->privs = g_settings.getU64("default_privs"); -- cgit v1.2.3 From e81919c818c6040de7401a037e3fdfac88b28eea Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 29 May 2011 22:34:04 +0300 Subject: hopefully fixed the privilege problems --HG-- extra : rebase_source : 9826d20176134a53ff232816a10407465d8c0f50 --- src/server.cpp | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 4569d028e..56874c46c 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2083,7 +2083,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 13) return; - if((player->privs & PRIV_BUILD) == 0) + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) return; /* @@ -2167,7 +2167,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 7) return; - if((player->privs & PRIV_BUILD) == 0) + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) return; /* @@ -2368,8 +2368,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Make sure the player is allowed to do it - if((player->privs & PRIV_BUILD) == 0) + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + { + dstream<<"Player "<getName()<<" cannot remove node" + <<" because privileges are "<getName()<<" cannot add node" + <<" because privileges are "<privs & PRIV_BUILD) ==0) + || no_enough_privs) { // Client probably has wrong data. // Set block not sent, so that client will get @@ -2715,7 +2727,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) #endif else if(command == TOSERVER_SIGNTEXT) { - if((player->privs & PRIV_BUILD) == 0) + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) return; /* u16 command @@ -2774,7 +2786,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else if(command == TOSERVER_SIGNNODETEXT) { - if((player->privs & PRIV_BUILD) == 0) + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) return; /* u16 command @@ -2952,9 +2964,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Local player gets all privileges regardless of // what's set on their account. - u64 privs = player->privs; - if(g_settings.get("name") == player->getName()) - privs = PRIV_ALL; + u64 privs = getPlayerPrivs(player); // Parse commands std::wstring commandprefix = L"/#"; @@ -4288,9 +4298,6 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id m_authmanager.setPrivs(name, stringToPrivs(g_settings.get("default_privs"))); - if(g_settings.exists("default_privs")) - player->privs = g_settings.getU64("default_privs"); - /* Set player position */ @@ -4503,6 +4510,23 @@ void Server::handlePeerChanges() } } +u64 Server::getPlayerPrivs(Player *player) +{ + if(player==NULL) + return 0; + std::string playername = player->getName(); + // Local player gets all privileges regardless of + // what's set on their account. + if(g_settings.get("name") == playername) + { + return PRIV_ALL; + } + else + { + return getPlayerAuthPrivs(playername); + } +} + void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); -- cgit v1.2.3 From 223b3793485a76f87599d39364b1003c2ca7c49c Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 31 May 2011 00:15:43 +0300 Subject: Reduced the CPU usage of the sent block selector algorithm --- src/server.cpp | 185 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 142 insertions(+), 43 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 56874c46c..6f1f1e6e8 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -312,11 +312,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/ // Increment timers - m_nearest_unsent_reset_timer += dtime; m_nothing_to_send_pause_timer -= dtime; if(m_nothing_to_send_pause_timer >= 0) + { + // Keep this reset + m_nearest_unsent_reset_timer = 0; return; + } // Won't send anything if already sending if(m_blocks_sending.size() >= g_settings.getU16 @@ -326,14 +329,21 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, return; } + //TimeTaker timer("RemoteClient::GetNextBlocks"); + Player *player = server->m_env.getPlayer(peer_id); assert(player != NULL); v3f playerpos = player->getPosition(); v3f playerspeed = player->getSpeed(); + v3f playerspeeddir(0,0,0); + if(playerspeed.getLength() > 1.0*BS) + playerspeeddir = playerspeed / playerspeed.getLength(); + // Predict to next block + v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; - v3s16 center_nodepos = floatToInt(playerpos, BS); + v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); v3s16 center = getNodeBlockPos(center_nodepos); @@ -344,6 +354,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, camera_dir.rotateYZBy(player->getPitch()); camera_dir.rotateXZBy(player->getYaw()); + /*dstream<<"camera_dir=("<getPlayerName(peer_id)< d_start+1) + d_max = d_start+1; + /*if(d_max_gen > d_start+2) + d_max_gen = d_start+2;*/ + //dstream<<"Starting from "<= 3) + if((s16)m_nothing_to_send_counter >= + g_settings.getS16("max_block_send_distance")) { // Pause time in seconds - m_nothing_to_send_pause_timer = 2.0; + m_nothing_to_send_pause_timer = 1.0; + dstream<<"nothing to send to " + <getPlayerName(peer_id) + <<" (d="< modified_blocks; m_env.getMap().transformLiquids(modified_blocks); #if 0 @@ -1298,10 +1360,11 @@ void Server::AsyncRunStep() */ { //dstream<<"Server: Checking added and deleted active objects"<* > buffered_messages; @@ -1598,6 +1663,9 @@ void Server::AsyncRunStep() { JMutexAutoLock lock1(m_env_mutex); JMutexAutoLock lock2(m_con_mutex); + + ScopeProfiler sp(&g_profiler, "Server: sending mbo positions"); + SendObjectData(counter); counter = 0.0; @@ -1612,7 +1680,9 @@ void Server::AsyncRunStep() JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - + + ScopeProfiler sp(&g_profiler, "Server: stepping node metadata"); + core::map changed_blocks; m_env.getMap().nodeMetadataStep(dtime, changed_blocks); @@ -1655,6 +1725,8 @@ void Server::AsyncRunStep() { counter = 0.0; + ScopeProfiler sp(&g_profiler, "Server: saving stuff"); + // Auth stuff m_authmanager.save(); @@ -3646,20 +3718,24 @@ void Server::SendBlocks(float dtime) core::array queue; s32 total_sending = 0; - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending"); - total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + + total_sending += client->SendingCount(); + + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + client->GetNextBlocks(this, dtime, queue); + } } // Sort. @@ -4531,25 +4607,48 @@ void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); - std::cout<PrintLine(&std::cout); + i->PrintLine(&dstream); } } sum_old = sum; -- cgit v1.2.3 From 41f07328c89e04d00a541334c557f802aaa2f659 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 31 May 2011 00:16:58 +0300 Subject: removed some debug prints ...and added support for simple time usage profiling in the last one. --- src/server.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 6f1f1e6e8..9bcac5a64 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -379,8 +379,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, { m_nearest_unsent_reset_timer = 0; m_nearest_unsent_d = 0; - dstream<<"Resetting m_nearest_unsent_d for " - <getPlayerName(peer_id)<getPlayerName(peer_id)<getPlayerName(peer_id) - <<" (d="< Date: Tue, 31 May 2011 00:23:39 +0300 Subject: auth stuff is now saved only when modified --- src/server.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 9bcac5a64..e668db0c3 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1728,7 +1728,8 @@ void Server::AsyncRunStep() ScopeProfiler sp(&g_profiler, "Server: saving stuff"); // Auth stuff - m_authmanager.save(); + if(m_authmanager.isModified()) + m_authmanager.save(); // Map JMutexAutoLock lock(m_env_mutex); -- cgit v1.2.3 From 774042508550c11f0e4fc39256226a4d9d919d77 Mon Sep 17 00:00:00 2001 From: celeron55 Date: Tue, 31 May 2011 11:59:51 +0300 Subject: Updated licenses of CiaranG's contributions to be in line with the new contribution policy and added a TODO note to server.cpp --- src/server.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index e668db0c3..1b471fd09 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1192,7 +1192,7 @@ void Server::AsyncRunStep() if(dtime < 0.001) return; - + //dstream<<"Server steps "< Date: Tue, 31 May 2011 20:02:55 +0300 Subject: Reduced server CPU usage on NodeMetadata step()s. Also furnace now cooks while no players are near it. --- src/server.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 1b471fd09..acfc7446f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1637,6 +1637,11 @@ void Server::AsyncRunStep() dstream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer); } + else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) + { + dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<p); + } else if(event->type == MEET_OTHER) { dstream<<"WARNING: Server: MEET_OTHER not implemented" @@ -1676,7 +1681,7 @@ void Server::AsyncRunStep() Step node metadata TODO: Move to ServerEnvironment and utilize active block stuff */ - { + /*{ //TimeTaker timer("Step node metadata"); JMutexAutoLock envlock(m_env_mutex); @@ -1686,6 +1691,8 @@ void Server::AsyncRunStep() core::map changed_blocks; m_env.getMap().nodeMetadataStep(dtime, changed_blocks); + + // Use setBlockNotSent for(core::map::Iterator i = changed_blocks.getIterator(); @@ -1701,7 +1708,7 @@ void Server::AsyncRunStep() client->SetBlockNotSent(block->getPos()); } } - } + }*/ /* Trigger emergethread (it somehow gets to a non-triggered but @@ -3655,6 +3662,17 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, } } +void Server::setBlockNotSent(v3s16 p) +{ + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd()==false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + client->SetBlockNotSent(p); + } +} + void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) { DSTACK(__FUNCTION_NAME); -- cgit v1.2.3 From 51cf464f7450ee31e3a986b521f90cc6195bb2fb Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Thu, 2 Jun 2011 00:01:11 +0300 Subject: Fixed the password crash on Windows --- src/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index acfc7446f..96fcc0d07 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1923,7 +1923,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) checkpwd = g_settings.get("default_password"); } - if(password != checkpwd) + if(password != checkpwd && checkpwd != "") { derr_server< Date: Thu, 2 Jun 2011 20:09:30 +0300 Subject: removed a debug print that would flood a lot in some kind of a timeout --- src/server.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 96fcc0d07..2a1246496 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1376,8 +1376,10 @@ void Server::AsyncRunStep() Player *player = m_env.getPlayer(client->peer_id); if(player==NULL) { - dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<peer_id - <<" has no associated player"<peer_id + <<" has no associated player"<getPosition(), BS); -- cgit v1.2.3 From c68f21214dfea8184810f5dd91800b61910551fd Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 5 Jun 2011 21:06:54 +0300 Subject: made server to send map seed for testing --- src/server.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 2a1246496..16ad80c00 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2002,8 +2002,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) writeU16(&reply[0], TOCLIENT_INIT); writeU8(&reply[2], deployed); writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS)); - //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed()); - writeU64(&reply[2+1+6], 0); // no seed + writeU64(&reply[2+1+6], m_env.getServerMap().getSeed()); // Send as reliable m_con.Send(peer_id, 0, reply, true); -- cgit v1.2.3 From dc5319b6c9f2e39d93f2fa881403f36fc47ffaac Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Fri, 17 Jun 2011 22:20:15 +0300 Subject: Moved some mapnode content stuff from mapnode.{h,cpp} and digging property stuff from material.cpp to content_mapnode.{h,cpp} --- src/server.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 16ad80c00..70448fbf6 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" #include "servercommand.h" #include "filesys.h" +#include "content_mapnode.h" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) -- cgit v1.2.3 From da692355e84f8d1e5210c3c89daf775cf23ec38b Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 18 Jun 2011 00:46:50 +0300 Subject: Created and moved stuff to content_nodemeta.{h,cpp} --- src/server.cpp | 498 ++------------------------------------------------------- 1 file changed, 9 insertions(+), 489 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 70448fbf6..9fa957c2a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "servercommand.h" #include "filesys.h" #include "content_mapnode.h" +#include "content_craft.h" +#include "content_nodemeta.h" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) @@ -3828,334 +3830,10 @@ void Server::UpdateCrafting(u16 peer_id) items[i] = clist->getItem(i); } - bool found = false; - - // Wood - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_WOOD, 4)); - found = true; - } - } - - // Stick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new CraftItem("Stick", 4)); - found = true; - } - } - - // Fence - if(!found) - { - ItemSpec specs[9]; - specs[3] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[5] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[6] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[8] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_FENCE, 2)); - found = true; - } - } - - // Sign - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - //rlist->addItem(new MapBlockObjectItem("Sign")); - rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1)); - found = true; - } - } - - // Torch - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal"); - specs[3] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_TORCH, 4)); - found = true; - } - } - - // Wooden pick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("WPick", 0)); - found = true; - } - } - - // Stone pick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("STPick", 0)); - found = true; - } - } - - // Steel pick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("SteelPick", 0)); - found = true; - } - } - - // Mese pick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("MesePick", 0)); - found = true; - } - } - - // Wooden shovel - if(!found) - { - ItemSpec specs[9]; - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("WShovel", 0)); - found = true; - } - } - - // Stone shovel - if(!found) - { - ItemSpec specs[9]; - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("STShovel", 0)); - found = true; - } - } - - // Steel shovel - if(!found) - { - ItemSpec specs[9]; - specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("SteelShovel", 0)); - found = true; - } - } - - // Wooden axe - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("WAxe", 0)); - found = true; - } - } - - // Stone axe - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("STAxe", 0)); - found = true; - } - } - - // Steel axe - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("SteelAxe", 0)); - found = true; - } - } - - // Wooden sword - if(!found) - { - ItemSpec specs[9]; - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("WSword", 0)); - found = true; - } - } - - // Stone sword - if(!found) - { - ItemSpec specs[9]; - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("STSword", 0)); - found = true; - } - } - - // Steel sword - if(!found) - { - ItemSpec specs[9]; - specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("SteelSword", 0)); - found = true; - } - } - - // Chest - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_CHEST, 1)); - found = true; - } - } - - // Furnace - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1)); - found = true; - } - } - - // Steel block - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_STEEL, 1)); - found = true; - } - } + // Get result of crafting grid + InventoryItem *result = craft_get_result(items); + if(result) + rlist->addItem(result); } } // if creative_mode == false @@ -4206,97 +3884,6 @@ std::wstring Server::getStatusString() return os.str(); } - -void setCreativeInventory(Player *player) -{ - player->resetInventory(); - - // Give some good tools - { - InventoryItem *item = new ToolItem("MesePick", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("SteelPick", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("SteelAxe", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("SteelShovel", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - - /* - Give materials - */ - - // CONTENT_IGNORE-terminated list - u8 material_items[] = { - CONTENT_TORCH, - CONTENT_COBBLE, - CONTENT_MUD, - CONTENT_STONE, - CONTENT_SAND, - CONTENT_TREE, - CONTENT_LEAVES, - CONTENT_GLASS, - CONTENT_FENCE, - CONTENT_MESE, - CONTENT_WATERSOURCE, - CONTENT_CLOUD, - CONTENT_CHEST, - CONTENT_FURNACE, - CONTENT_SIGN_WALL, - CONTENT_IGNORE - }; - - u8 *mip = material_items; - for(u16 i=0; iinventory.addItem("main", item); - - mip++; - } - -#if 0 - assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE); - - // add torch first - InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1); - player->inventory.addItem("main", item); - - // Then others - for(u16 i=0; iinventory.addItem("main", item); - } -#endif - - /*// Sign - { - InventoryItem *item = new MapBlockObjectItem("Sign Example text"); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - }*/ -} - v3f findSpawnPos(ServerMap &map) { //return v3f(50,50,50)*BS; @@ -4366,7 +3953,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id // Reset inventory to creative if in creative mode if(g_settings.getBool("creative_mode")) { - setCreativeInventory(player); + craft_set_creative_inventory(player); } return player; @@ -4419,78 +4006,11 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id if(g_settings.getBool("creative_mode")) { - setCreativeInventory(player); + craft_set_creative_inventory(player); } else if(g_settings.getBool("give_initial_stuff")) { - { - InventoryItem *item = new ToolItem("SteelPick", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("SteelAxe", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("SteelShovel", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - /*{ - InventoryItem *item = new MaterialItem(CONTENT_MESE, 6); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new CraftItem("Stick", 4); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("WPick", 32000); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("STPick", 32000); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - }*/ - /*// and some signs - for(u16 i=0; i<4; i++) - { - InventoryItem *item = new MapBlockObjectItem("Sign Example text"); - bool r = player->inventory.addItem("main", item); - assert(r == true); - }*/ - /*// Give some other stuff - { - InventoryItem *item = new MaterialItem(CONTENT_TREE, 999); - bool r = player->inventory.addItem("main", item); - assert(r == true); - }*/ + craft_give_initial_stuff(player); } return player; -- cgit v1.2.3 From 5ac900ddd9d574440b499b15516747a374fe3a5a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 18 Jun 2011 15:16:35 +0300 Subject: Changed MapBlockObjects to be never written anymore. Incremented version number. --- src/server.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 9fa957c2a..d3627a93f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -873,7 +873,9 @@ void RemoteClient::SendObjectData( bos.write((char*)buf, 6); // Write objects - block->serializeObjects(bos, serialization_version); + //block->serializeObjects(bos, serialization_version); // DEPRECATED + // count=0 + writeU16(bos, 0); blockcount++; -- cgit v1.2.3 From 7538b4c6201675c566c98b21c8ecddb798a14943 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 25 Jun 2011 04:25:14 +0300 Subject: New map generator added (and SQLite, messed up the commits at that time...) (import from temporary git repo) --- src/server.cpp | 186 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 128 insertions(+), 58 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index d3627a93f..3036aef26 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -37,6 +37,31 @@ with this program; if not, write to the Free Software Foundation, Inc., #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) +class MapEditEventIgnorer +{ +public: + MapEditEventIgnorer(bool *flag): + m_flag(flag) + { + if(*m_flag == false) + *m_flag = true; + else + m_flag = NULL; + } + + ~MapEditEventIgnorer() + { + if(m_flag) + { + assert(*m_flag); + *m_flag = false; + } + } + +private: + bool *m_flag; +}; + void * ServerThread::Thread() { ThreadStarted(); @@ -150,8 +175,8 @@ void * EmergeThread::Thread() ServerMap &map = ((ServerMap&)m_server->m_env.getMap()); - core::map changed_blocks; - core::map lighting_invalidated_blocks; + //core::map changed_blocks; + //core::map lighting_invalidated_blocks; MapBlock *block = NULL; bool got_block = true; @@ -162,32 +187,6 @@ void * EmergeThread::Thread() if(optional) only_from_disk = true; - v2s16 chunkpos = map.sector_to_chunk(p2d); - - bool generate_chunk = false; - if(only_from_disk == false) - { - JMutexAutoLock envlock(m_server->m_env_mutex); - if(map.chunkNonVolatile(chunkpos) == false) - generate_chunk = true; - } - if(generate_chunk) - { - ChunkMakeData data; - - { - JMutexAutoLock envlock(m_server->m_env_mutex); - map.initChunkMake(data, chunkpos); - } - - makeChunk(&data); - - { - JMutexAutoLock envlock(m_server->m_env_mutex); - map.finishChunkMake(data, changed_blocks); - } - } - /* Fetch block from map or generate a single block */ @@ -196,36 +195,55 @@ void * EmergeThread::Thread() // Load sector if it isn't loaded if(map.getSectorNoGenerateNoEx(p2d) == NULL) - map.loadSectorFull(p2d); + //map.loadSectorFull(p2d); + map.loadSectorMeta(p2d); block = map.getBlockNoCreateNoEx(p); - if(!block || block->isDummy()) + if(!block || block->isDummy() || !block->isGenerated()) { - if(only_from_disk) + // Get, load or create sector + /*ServerMapSector *sector = + (ServerMapSector*)map.createSector(p2d);*/ + + // Load/generate block + + /*block = map.emergeBlock(p, sector, changed_blocks, + lighting_invalidated_blocks);*/ + + block = map.loadBlock(p); + + if(block == NULL && only_from_disk == false) + block = map.generateBlock(p, modified_blocks); + //block = map.generateBlock(p, changed_blocks); + /*block = map.generateBlock(p, block, sector, changed_blocks, + lighting_invalidated_blocks);*/ + + if(block == NULL) { got_block = false; } else { - // Get, load or create sector - ServerMapSector *sector = - (ServerMapSector*)map.createSector(p2d); - // Generate block - block = map.generateBlock(p, block, sector, changed_blocks, - lighting_invalidated_blocks); - if(block == NULL) - got_block = false; + /* + Ignore map edit events, they will not need to be + sent to anybody because the block hasn't been sent + to anybody + */ + MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events); + + // Activate objects and stuff + m_server->m_env.activateBlock(block, 3600); } } else { - if(block->getLightingExpired()){ + /*if(block->getLightingExpired()){ lighting_invalidated_blocks[block->getPos()] = block; - } + }*/ } // TODO: Some additional checking and lighting updating, - // see emergeBlock + // see emergeBlock } {//envlock @@ -237,7 +255,8 @@ void * EmergeThread::Thread() Collect a list of blocks that have been modified in addition to the fetched one. */ - + +#if 0 if(lighting_invalidated_blocks.size() > 0) { /*dstream<<"lighting "<getValue(); modified_blocks.insert(block->getPos(), block); } +#endif } // If we got no block, there should be no invalidated blocks else { - assert(lighting_invalidated_blocks.size() == 0); + //assert(lighting_invalidated_blocks.size() == 0); } }//envlock @@ -597,12 +617,16 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, { block_is_invalid = true; }*/ - + +#if 0 v2s16 p2d(p.X, p.Z); ServerMap *map = (ServerMap*)(&server->m_env.getMap()); v2s16 chunkpos = map->sector_to_chunk(p2d); if(map->chunkNonVolatile(chunkpos) == false) block_is_invalid = true; +#endif + if(block->isGenerated() == false) + block_is_invalid = true; #if 1 /* If block is not close, don't send it unless it is near @@ -649,6 +673,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, //TODO: Get value from somewhere // Allow only one block in emerge queue //if(server->m_emerge_queue.peerItemCount(peer_id) < 1) + // Allow two blocks in queue per client if(server->m_emerge_queue.peerItemCount(peer_id) < 2) { //dstream<<"Adding block to emerge queue"< far_players; if(event->type == MEET_ADDNODE) { dstream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer); + sendAddNode(event->p, event->n, event->already_known_by_peer, + &far_players, 30); } else if(event->type == MEET_REMOVENODE) { dstream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer); + sendRemoveNode(event->p, event->already_known_by_peer, + &far_players, 30); } else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) { @@ -1659,8 +1693,35 @@ void Server::AsyncRunStep() dstream<<"WARNING: Server: Unknown MapEditEvent " <<((u32)event->type)< modified_blocks2; + for(core::map::Iterator + i = event->modified_blocks.getIterator(); + i.atEnd()==false; i++) + { + v3s16 p = i.getNode()->getKey(); + modified_blocks2.insert(p, m_env.getMap().getBlockNoCreateNoEx(p)); + } + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) + { + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks2); + } delete event; + + // Don't send too many at a time + count++; + if(count >= 2 && m_unsent_map_edit_queue.size() < 50) + break; } } @@ -1754,7 +1815,7 @@ void Server::AsyncRunStep() m_env.getMap().save(true); // Delete unused sectors - u32 deleted_count = m_env.getMap().deleteUnusedSectors( + u32 deleted_count = m_env.getMap().unloadUnusedData( g_settings.getFloat("server_unload_unused_sectors_timeout")); if(deleted_count > 0) { @@ -2565,10 +2626,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) Remove the node (this takes some time so it is done after the quick stuff) */ - m_ignore_map_edit_events = true; - m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); - m_ignore_map_edit_events = false; - + { + MapEditEventIgnorer ign(&m_ignore_map_edit_events); + + m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); + } /* Set blocks not sent to far players */ @@ -2679,10 +2741,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) This takes some time so it is done after the quick stuff */ core::map modified_blocks; - m_ignore_map_edit_events = true; - m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); - m_ignore_map_edit_events = false; - + { + MapEditEventIgnorer ign(&m_ignore_map_edit_events); + + m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); + } /* Set blocks not sent to far players */ @@ -3889,10 +3952,16 @@ std::wstring Server::getStatusString() v3f findSpawnPos(ServerMap &map) { //return v3f(50,50,50)*BS; - + v2s16 nodepos; s16 groundheight = 0; +#if 0 + nodepos = v2s16(0,0); + groundheight = 20; +#endif + +#if 1 // Try to find a good place a few times for(s32 i=0; i<1000; i++) { @@ -3922,6 +3991,7 @@ v3f findSpawnPos(ServerMap &map) //dstream<<"Searched through "< Date: Sat, 25 Jun 2011 16:32:09 +0300 Subject: mapgen stuff --- src/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 3036aef26..49eb15731 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1720,7 +1720,7 @@ void Server::AsyncRunStep() // Don't send too many at a time count++; - if(count >= 2 && m_unsent_map_edit_queue.size() < 50) + if(count >= 1 && m_unsent_map_edit_queue.size() < 100) break; } } -- cgit v1.2.3 From f2c26e20147cf586b5b4ffa761303b670689ab40 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 25 Jun 2011 18:12:41 +0300 Subject: moved map generator to separate source files --- src/server.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 49eb15731..cf8b57773 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2546,12 +2546,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } /* - Send the removal to all other clients. + Send the removal to all close-by players. - If other player is close, send REMOVENODE - Otherwise set blocks not sent */ core::list far_players; - sendRemoveNode(p_under, peer_id, &far_players, 100); + sendRemoveNode(p_under, peer_id, &far_players, 30); /* Update and send inventory @@ -2714,10 +2714,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) n.dir = packDir(p_under - p_over); /* - Send to all players + Send to all close-by players */ core::list far_players; - sendAddNode(p_over, n, 0, &far_players, 100); + sendAddNode(p_over, n, 0, &far_players, 30); /* Handle inventory -- cgit v1.2.3