dissector branch: added wireshark dissector minetest.lua
[oweals/minetest.git] / src / server.cpp
index b5cb48a36d15ef21a035b61966273ebda4a9879d..a8eb68c55a95cc5d1fb9d1dd4f1424404d9cc32c 100644 (file)
@@ -30,9 +30,40 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mineral.h"
 #include "config.h"
 #include "servercommand.h"
+#include "filesys.h"
+#include "content_mapnode.h"
+#include "content_craft.h"
+#include "content_nodemeta.h"
+#include "mapblock.h"
+#include "serverobject.h"
 
 #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();
@@ -74,10 +105,10 @@ void * EmergeThread::Thread()
 
        DSTACK(__FUNCTION_NAME);
 
-       //bool debug=false;
-       
        BEGIN_DEBUG_EXCEPTION_HANDLER
 
+       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+       
        /*
                Get block info from queue, emerge them and send them
                to clients.
@@ -124,7 +155,7 @@ void * EmergeThread::Thread()
                        Also decrement the emerge queue count in clients.
                */
 
-               bool optional = true;
+               bool only_from_disk = true;
 
                {
                        core::map<u16, u8>::Iterator i;
@@ -135,55 +166,25 @@ void * EmergeThread::Thread()
                                // Check flags
                                u8 flags = i.getNode()->getValue();
                                if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
-                                       optional = false;
+                                       only_from_disk = false;
                                
                        }
                }
-
-               /*dstream<<"EmergeThread: p="
-                               <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
-                               <<"optional="<<optional<<std::endl;*/
+               
+               if(enable_mapgen_debug_info)
+                       dstream<<"EmergeThread: p="
+                                       <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
+                                       <<"only_from_disk="<<only_from_disk<<std::endl;
                
                ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
                        
-               core::map<v3s16, MapBlock*> changed_blocks;
-               core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
+               //core::map<v3s16, MapBlock*> changed_blocks;
+               //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
 
                MapBlock *block = NULL;
                bool got_block = true;
                core::map<v3s16, MapBlock*> modified_blocks;
                
-               bool only_from_disk = false;
-               
-               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
                */
@@ -192,36 +193,66 @@ 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)
+                               if(enable_mapgen_debug_info)
+                                       dstream<<"EmergeThread: not in memory, loading"<<std::endl;
+
+                               // 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(only_from_disk == false)
+                               {
+                                       if(block == NULL || block->isGenerated() == false)
+                                       {
+                                               if(enable_mapgen_debug_info)
+                                                       dstream<<"EmergeThread: generating"<<std::endl;
+                                               block = map.generateBlock(p, modified_blocks);
+                                       }
+                               }
+
+                               if(enable_mapgen_debug_info)
+                                       dstream<<"EmergeThread: ended up with: "
+                                                       <<analyze_block(block)<<std::endl;
+
+                               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
@@ -233,7 +264,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 "<<lighting_invalidated_blocks.size()
@@ -254,11 +286,12 @@ void * EmergeThread::Thread()
                                MapBlock *block = i.getNode()->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
@@ -311,11 +344,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
@@ -325,24 +361,33 @@ 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);
        
        // Camera position and direction
-       v3f camera_pos =
-                       playerpos + v3f(0, BS+BS/2, 0);
+       v3f camera_pos = player->getEyePosition();
        v3f camera_dir = v3f(0,0,1);
        camera_dir.rotateYZBy(player->getPitch());
        camera_dir.rotateXZBy(player->getYaw());
 
+       /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
+                       <<camera_dir.Z<<")"<<std::endl;*/
+
        /*
                Get the starting value of the block finder radius.
        */
@@ -355,11 +400,18 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
        /*dstream<<"m_nearest_unsent_reset_timer="
                        <<m_nearest_unsent_reset_timer<<std::endl;*/
-       if(m_nearest_unsent_reset_timer > 5.0)
+                       
+       // This has to be incremented only when the nothing to send pause
+       // is not active
+       m_nearest_unsent_reset_timer += dtime;
+       
+       // Reset periodically to avoid possible bugs or other mishaps
+       if(m_nearest_unsent_reset_timer > 10.0)
        {
                m_nearest_unsent_reset_timer = 0;
                m_nearest_unsent_d = 0;
-               //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
+               /*dstream<<"Resetting m_nearest_unsent_d for "
+                               <<server->getPlayerName(peer_id)<<std::endl;*/
        }
 
        //s16 last_nearest_unsent_d = m_nearest_unsent_d;
@@ -401,11 +453,22 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        s16 d_max = g_settings.getS16("max_block_send_distance");
        s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
        
+       // Don't loop very much at a time
+       if(d_max > d_start+1)
+               d_max = d_start+1;
+       /*if(d_max_gen > d_start+2)
+               d_max_gen = d_start+2;*/
+       
        //dstream<<"Starting from "<<d_start<<std::endl;
 
        bool sending_something = false;
 
-       for(s16 d = d_start; d <= d_max; d++)
+       bool no_blocks_found_for_sending = true;
+
+       bool queue_is_full = false;
+       
+       s16 d;
+       for(d = d_start; d <= d_max; d++)
        {
                //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
                
@@ -450,7 +513,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
                        // Don't select too many blocks for sending
                        if(num_blocks_selected >= max_simul_dynamic)
-                               goto queue_full;
+                       {
+                               queue_is_full = true;
+                               goto queue_full_break;
+                       }
                        
                        // Don't send blocks that are currently being transferred
                        if(m_blocks_sending.find(p) != NULL)
@@ -512,6 +578,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        }
 #endif
 
+                       //dstream<<"d="<<d<<std::endl;
+                       
                        /*
                                Don't generate or send if not in sight
                        */
@@ -526,7 +594,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        */
                        {
                                if(m_blocks_sent.find(p) != NULL)
+                               {
                                        continue;
+                               }
                        }
 
                        /*
@@ -538,11 +608,17 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        bool block_is_invalid = false;
                        if(block != NULL)
                        {
+                               // Reset usage timer, this block will be of use in the future.
+                               block->resetUsageTimer();
+
+                               // Block is dummy if data doesn't exist.
+                               // It means it has been not found from disk and not generated
                                if(block->isDummy())
                                {
                                        surely_not_found_on_disk = true;
                                }
-
+                               
+                               // Block is valid if lighting is up-to-date and data exists
                                if(block->isValid() == false)
                                {
                                        block_is_invalid = true;
@@ -552,19 +628,23 @@ 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
                                        ground level.
 
-                                       Block is not near ground level if night-time mesh
-                                       doesn't differ from day-time mesh.
+                                       Block is near ground level if night-time mesh
+                                       differs from day-time mesh.
                                */
                                if(d > 3)
                                {
@@ -585,14 +665,16 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        }
 
                        /*
-                               Record the lowest d from which a block has been
+                               Record the lowest d from which a block has been
                                found being not sent and possibly to exist
                        */
-                       if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
+                       if(no_blocks_found_for_sending)
                        {
                                if(generate == true)
                                        new_nearest_unsent_d = d;
                        }
+
+                       no_blocks_found_for_sending = false;
                                        
                        /*
                                Add inexistent block to emerge queue.
@@ -602,6 +684,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"<<std::endl;
@@ -632,20 +715,30 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        sending_something = true;
                }
        }
-queue_full:
+queue_full_break:
 
-       if(new_nearest_unsent_d != -1)
+       //dstream<<"Stopped at "<<d<<std::endl;
+       
+       if(no_blocks_found_for_sending)
        {
-               m_nearest_unsent_d = new_nearest_unsent_d;
+               if(queue_is_full == false)
+                       new_nearest_unsent_d = d;
        }
 
+       if(new_nearest_unsent_d != -1)
+               m_nearest_unsent_d = new_nearest_unsent_d;
+
        if(sending_something == false)
        {
                m_nothing_to_send_counter++;
-               if(m_nothing_to_send_counter >= 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 "
+                                       <<server->getPlayerName(peer_id)
+                                       <<" (d="<<d<<")"<<std::endl;*/
                }
        }
        else
@@ -681,6 +774,7 @@ void RemoteClient::SendObjectData(
                u16 command
                u16 number of player positions
                for each player:
+                       u16 peer_id
                        v3s32 position*100
                        v3s32 speed*100
                        s32 pitch*100
@@ -798,7 +892,7 @@ void RemoteClient::SendObjectData(
                        */
                        if(stepped_blocks.find(p) == NULL)
                        {
-                               block->stepObjects(dtime, true, server->getDayNightRatio());
+                               block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
                                stepped_blocks.insert(p, true);
                                block->setChangedFlag();
                        }
@@ -816,7 +910,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++;
 
@@ -962,17 +1058,20 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
 */
 
 Server::Server(
-               std::string mapsavedir
+               std::string mapsavedir,
+               std::string configpath
        ):
        m_env(new ServerMap(mapsavedir), this),
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
+       m_authmanager(mapsavedir+"/auth.txt"),
+       m_banmanager(mapsavedir+"/ipban.txt"),
        m_thread(this),
        m_emergethread(this),
-       m_time_of_day(9000),
        m_time_counter(0),
        m_time_of_day_send_timer(0),
        m_uptime(0),
        m_mapsavedir(mapsavedir),
+       m_configpath(configpath),
        m_shutdown_requested(false),
        m_ignore_map_edit_events(false),
        m_ignore_map_edit_events_peer_id(0)
@@ -987,10 +1086,19 @@ Server::Server(
        m_con_mutex.Init();
        m_step_dtime_mutex.Init();
        m_step_dtime = 0.0;
-
+       
+       // Register us to receive map edit events
        m_env.getMap().addEventReceiver(this);
 
+       // If file exists, load environment metadata
+       if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
+       {
+               dstream<<"Server: Loading environment metadata"<<std::endl;
+               m_env.loadMeta(m_mapsavedir);
+       }
+
        // Load players
+       dstream<<"Server: Loading players"<<std::endl;
        m_env.deSerializePlayers(m_mapsavedir);
 }
 
@@ -1032,6 +1140,12 @@ Server::~Server()
        */
        dstream<<"Server: Saving players"<<std::endl;
        m_env.serializePlayers(m_mapsavedir);
+
+       /*
+               Save environment metadata
+       */
+       dstream<<"Server: Saving environment metadata"<<std::endl;
+       m_env.saveMeta(m_mapsavedir);
        
        /*
                Stop threads
@@ -1114,12 +1228,16 @@ void Server::AsyncRunStep()
                dtime = m_step_dtime;
        }
        
-       // Send blocks to clients
-       SendBlocks(dtime);
+       {
+               ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
+                               "blocks to clients");
+               // Send blocks to clients
+               SendBlocks(dtime);
+       }
        
        if(dtime < 0.001)
                return;
-       
+
        //dstream<<"Server steps "<<dtime<<std::endl;
        //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
        
@@ -1135,15 +1253,32 @@ void Server::AsyncRunStep()
                m_uptime.set(m_uptime.get() + dtime);
        }
        
+       {
+               // Process connection's timeouts
+               JMutexAutoLock lock2(m_con_mutex);
+               ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
+               m_con.RunTimeouts(dtime);
+       }
+       
+       {
+               // This has to be called so that the client list gets synced
+               // with the peer list of the connection
+               ScopeProfiler sp(&g_profiler, "Server: peer change handling");
+               handlePeerChanges();
+       }
+
        /*
-               Update m_time_of_day
+               Update m_time_of_day and overall game time
        */
        {
+               JMutexAutoLock envlock(m_env_mutex);
+
                m_time_counter += dtime;
                f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
                u32 units = (u32)(m_time_counter*speed);
                m_time_counter -= (f32)units / speed;
-               m_time_of_day.set((m_time_of_day.get() + units) % 24000);
+               
+               m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
                
                //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
 
@@ -1167,7 +1302,7 @@ void Server::AsyncRunStep()
                                //Player *player = m_env.getPlayer(client->peer_id);
                                
                                SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-                                               m_time_of_day.get());
+                                               m_env.getTimeOfDay());
                                // Send as reliable
                                m_con.Send(client->peer_id, 0, data, true);
                        }
@@ -1175,22 +1310,20 @@ void Server::AsyncRunStep()
        }
 
        {
-               // Process connection's timeouts
-               JMutexAutoLock lock2(m_con_mutex);
-               m_con.RunTimeouts(dtime);
-       }
-       
-       {
-               // This has to be called so that the client list gets synced
-               // with the peer list of the connection
-               handlePeerChanges();
+               JMutexAutoLock lock(m_env_mutex);
+               // Step environment
+               ScopeProfiler sp(&g_profiler, "Server: environment step");
+               m_env.step(dtime);
        }
-
+               
+       const float map_timer_and_unload_dtime = 5.15;
+       if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
        {
-               // Step environment
-               // This also runs Map's timers
                JMutexAutoLock lock(m_env_mutex);
-               m_env.step(dtime);
+               // Run Map's timers and unload unused data
+               ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
+               m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
+                               g_settings.getFloat("server_unload_unused_data_timeout"));
        }
        
        /*
@@ -1206,7 +1339,9 @@ void Server::AsyncRunStep()
                m_liquid_transform_timer -= 1.00;
                
                JMutexAutoLock lock(m_env_mutex);
-               
+
+               ScopeProfiler sp(&g_profiler, "Server: liquid transform");
+
                core::map<v3s16, MapBlock*> modified_blocks;
                m_env.getMap().transformLiquids(modified_blocks);
 #if 0          
@@ -1279,10 +1414,11 @@ void Server::AsyncRunStep()
        */
        {
                //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
-
                JMutexAutoLock envlock(m_env_mutex);
                JMutexAutoLock conlock(m_con_mutex);
-               
+
+               ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
+
                // Radius inside which objects are active
                s16 radius = 32;
 
@@ -1294,8 +1430,10 @@ void Server::AsyncRunStep()
                        Player *player = m_env.getPlayer(client->peer_id);
                        if(player==NULL)
                        {
-                               dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
-                                               <<" has no associated player"<<std::endl;
+                               // This can happen if the client timeouts somehow
+                               /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
+                                               <<client->peer_id
+                                               <<" has no associated player"<<std::endl;*/
                                continue;
                        }
                        v3s16 pos = floatToInt(player->getPosition(), BS);
@@ -1427,6 +1565,8 @@ void Server::AsyncRunStep()
                JMutexAutoLock envlock(m_env_mutex);
                JMutexAutoLock conlock(m_con_mutex);
 
+               ScopeProfiler sp(&g_profiler, "Server: sending object messages");
+
                // Key = object id
                // Value = data sent by object
                core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
@@ -1539,33 +1679,119 @@ void Server::AsyncRunStep()
                Send queued-for-sending map edit events.
        */
        {
+               // Don't send too many at a time
+               //u32 count = 0;
+
+               // Single change sending is disabled if queue size is not small
+               bool disable_single_change_sending = false;
+               if(m_unsent_map_edit_queue.size() >= 4)
+                       disable_single_change_sending = true;
+
+               bool got_any_events = false;
+
+               // We'll log the amount of each
+               Profiler prof;
+
                while(m_unsent_map_edit_queue.size() != 0)
                {
+                       got_any_events = true;
+
                        MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
+                       
+                       // Players far away from the change are stored here.
+                       // Instead of sending the changes, MapBlocks are set not sent
+                       // for them.
+                       core::list<u16> far_players;
 
                        if(event->type == MEET_ADDNODE)
                        {
-                               dstream<<"Server: MEET_ADDNODE"<<std::endl;
-                               sendAddNode(event->p, event->n, event->already_known_by_peer);
+                               //dstream<<"Server: MEET_ADDNODE"<<std::endl;
+                               prof.add("MEET_ADDNODE", 1);
+                               if(disable_single_change_sending)
+                                       sendAddNode(event->p, event->n, event->already_known_by_peer,
+                                                       &far_players, 5);
+                               else
+                                       sendAddNode(event->p, event->n, event->already_known_by_peer,
+                                                       &far_players, 30);
                        }
                        else if(event->type == MEET_REMOVENODE)
                        {
-                               dstream<<"Server: MEET_REMOVENODE"<<std::endl;
-                               sendRemoveNode(event->p, event->already_known_by_peer);
+                               //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
+                               prof.add("MEET_REMOVENODE", 1);
+                               if(disable_single_change_sending)
+                                       sendRemoveNode(event->p, event->already_known_by_peer,
+                                                       &far_players, 5);
+                               else
+                                       sendRemoveNode(event->p, event->already_known_by_peer,
+                                                       &far_players, 30);
+                       }
+                       else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
+                       {
+                               dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
+                               prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
+                               setBlockNotSent(event->p);
                        }
                        else if(event->type == MEET_OTHER)
                        {
-                               dstream<<"WARNING: Server: MEET_OTHER not implemented"
-                                               <<std::endl;
+                               dstream<<"Server: MEET_OTHER"<<std::endl;
+                               prof.add("MEET_OTHER", 1);
+                               for(core::map<v3s16, bool>::Iterator
+                                               i = event->modified_blocks.getIterator();
+                                               i.atEnd()==false; i++)
+                               {
+                                       v3s16 p = i.getNode()->getKey();
+                                       setBlockNotSent(p);
+                               }
                        }
                        else
                        {
+                               prof.add("unknown", 1);
                                dstream<<"WARNING: Server: Unknown MapEditEvent "
                                                <<((u32)event->type)<<std::endl;
                        }
+                       
+                       /*
+                               Set blocks not sent to far players
+                       */
+                       if(far_players.size() > 0)
+                       {
+                               // Convert list format to that wanted by SetBlocksNotSent
+                               core::map<v3s16, MapBlock*> modified_blocks2;
+                               for(core::map<v3s16, bool>::Iterator
+                                               i = event->modified_blocks.getIterator();
+                                               i.atEnd()==false; i++)
+                               {
+                                       v3s16 p = i.getNode()->getKey();
+                                       modified_blocks2.insert(p,
+                                                       m_env.getMap().getBlockNoCreateNoEx(p));
+                               }
+                               // Set blocks not sent
+                               for(core::list<u16>::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 >= 1 && m_unsent_map_edit_queue.size() < 100)
+                               break;*/
+               }
+
+               if(got_any_events)
+               {
+                       dstream<<"Server: MapEditEvents:"<<std::endl;
+                       prof.print(dstream);
                }
+               
        }
 
        /*
@@ -1579,40 +1805,15 @@ 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;
                }
        }
        
-       /*
-               Step node metadata
-       */
-       {
-               //TimeTaker timer("Step node metadata");
-
-               JMutexAutoLock envlock(m_env_mutex);
-               JMutexAutoLock conlock(m_con_mutex);
-               
-               core::map<v3s16, MapBlock*> changed_blocks;
-               m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
-
-               for(core::map<v3s16, MapBlock*>::Iterator
-                               i = changed_blocks.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       MapBlock *block = i.getNode()->getValue();
-
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd()==false; i++)
-                       {
-                               RemoteClient *client = i.getNode()->getValue();
-                               client->SetBlockNotSent(block->getPos());
-                       }
-               }
-       }
-               
        /*
                Trigger emergethread (it somehow gets to a non-triggered but
                bysy state sometimes)
@@ -1628,7 +1829,7 @@ void Server::AsyncRunStep()
                }
        }
 
-       // Save map
+       // Save map, players and auth stuff
        {
                float &counter = m_savemap_timer;
                counter += dtime;
@@ -1636,25 +1837,41 @@ void Server::AsyncRunStep()
                {
                        counter = 0.0;
 
+                       ScopeProfiler sp(&g_profiler, "Server: saving stuff");
+
+                       // Auth stuff
+                       if(m_authmanager.isModified())
+                               m_authmanager.save();
+
+                       //Bann stuff
+                       if(m_banmanager.isModified())
+                               m_banmanager.save();
+                       
+                       // Map
                        JMutexAutoLock lock(m_env_mutex);
 
-                       if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
-                       {
-                               // Save only changed parts
-                               m_env.getMap().save(true);
+                       /*// Unload unused data (delete from memory)
+                       m_env.getMap().unloadUnusedData(
+                                       g_settings.getFloat("server_unload_unused_sectors_timeout"));
+                                       */
+                       /*u32 deleted_count = m_env.getMap().unloadUnusedData(
+                                       g_settings.getFloat("server_unload_unused_sectors_timeout"));
+                                       */
 
-                               // Delete unused sectors
-                               u32 deleted_count = m_env.getMap().deleteUnusedSectors(
-                                               g_settings.getFloat("server_unload_unused_sectors_timeout"));
-                               if(deleted_count > 0)
-                               {
-                                       dout_server<<"Server: Unloaded "<<deleted_count
-                                                       <<" sectors from memory"<<std::endl;
-                               }
+                       // Save only changed parts
+                       m_env.getMap().save(true);
 
-                               // Save players
-                               m_env.serializePlayers(m_mapsavedir);
-                       }
+                       /*if(deleted_count > 0)
+                       {
+                               dout_server<<"Server: Unloaded "<<deleted_count
+                                               <<" blocks from memory"<<std::endl;
+                       }*/
+
+                       // Save players
+                       m_env.serializePlayers(m_mapsavedir);
+                       
+                       // Save environment metadata
+                       m_env.saveMeta(m_mapsavedir);
                }
        }
 }
@@ -1718,6 +1935,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                <<peer_id<<" not found"<<std::endl;
                return;
        }
+
+       // drop player if is ip is banned
+       if(m_banmanager.isIpBanned(peer->address.serializeString())){
+               SendAccessDenied(m_con, peer_id,
+                               L"Your ip is banned. Banned name was "
+                               +narrow_to_wide(m_banmanager.getBanName(
+                                       peer->address.serializeString())));
+               m_con.deletePeer(peer_id, false);
+               return;
+       }
        
        u8 peer_ser_ver = getClient(peer->id)->serialization_version;
 
@@ -1754,12 +1981,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                //peer->serialization_version = deployed;
                getClient(peer->id)->pending_serialization_version = deployed;
-
+               
                if(deployed == SER_FMT_VER_INVALID)
                {
                        derr_server<<DTIME<<"Server: Cannot negotiate "
                                        "serialization version with peer "
                                        <<peer_id<<std::endl;
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Your client is too old (map format)");
+                       return;
+               }
+               
+               /*
+                       Read and check network protocol version
+               */
+
+               u16 net_proto_version = 0;
+               if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
+               {
+                       net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
+               }
+
+               getClient(peer->id)->net_proto_version = net_proto_version;
+
+               if(net_proto_version == 0)
+               {
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Your client is too old. Please upgrade.");
                        return;
                }
 
@@ -1774,10 +2022,26 @@ 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<<DTIME<<"Server: Player has empty name"<<std::endl;
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Empty name");
+                       return;
+               }
+
+               if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
+               {
+                       derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Name contains unallowed characters");
+                       return;
+               }
+
                // Get password
                char password[PASSWORD_SIZE];
-               if(datasize == 2+1+PLAYERNAME_SIZE)
+               if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
                {
                        // old version - assume blank password
                        password[0] = 0;
@@ -1790,15 +2054,40 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                }
                                password[PASSWORD_SIZE-1] = 0;
                }
-               Player *checkplayer = m_env.getPlayer(playername);
-               if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
+               
+               std::string checkpwd;
+               if(m_authmanager.exists(playername))
+               {
+                       checkpwd = m_authmanager.getPassword(playername);
+               }
+               else
+               {
+                       checkpwd = g_settings.get("default_password");
+               }
+               
+               /*dstream<<"Server: Client gave password '"<<password
+                               <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
+               
+               if(password != checkpwd && m_authmanager.exists(playername))
                {
                        derr_server<<DTIME<<"Server: peer_id="<<peer_id
                                        <<": supplied invalid password for "
                                        <<playername<<std::endl;
-                       SendAccessDenied(m_con, peer_id);
+                       SendAccessDenied(m_con, peer_id, L"Invalid password");
                        return;
                }
+               
+               // Add player to auth manager
+               if(m_authmanager.exists(playername) == false)
+               {
+                       derr_server<<DTIME<<"Server: adding player "<<playername
+                                       <<" to auth manager"<<std::endl;
+                       m_authmanager.add(playername);
+                       m_authmanager.setPassword(playername, checkpwd);
+                       m_authmanager.setPrivs(playername,
+                                       stringToPrivs(g_settings.get("default_privs")));
+                       m_authmanager.save();
+               }
 
                // Get player
                Player *player = emergePlayer(playername, password, peer_id);
@@ -1856,8 +2145,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);
@@ -1891,6 +2179,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                UpdateCrafting(peer->id);
                SendInventory(peer->id);
 
+               // Send player items to all players
+               SendPlayerItems();
+
                // Send HP
                {
                        Player *player = m_env.getPlayer(peer_id);
@@ -1900,7 +2191,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Send time of day
                {
                        SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-                                       m_time_of_day.get());
+                                       m_env.getTimeOfDay());
                        m_con.Send(peer->id, 0, data, true);
                }
                
@@ -1920,6 +2211,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        message += L" joined game";
                        BroadcastChatMessage(message);
                }
+               
+               // Warnings about protocol version can be issued here
+               /*if(getClient(peer->id)->net_proto_version == 0)
+               {
+                       SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
+               }*/
 
                return;
        }
@@ -2020,7 +2317,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;
 
                /*
@@ -2104,7 +2401,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;
 
                /*
@@ -2127,71 +2424,92 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
                }
 
+               // Skip if object has been removed
+               if(obj->m_removed)
+                       return;
+               
                //TODO: Check that object is reasonably close
                
                // Left click, pick object up (usually)
                if(button == 0)
                {
-                       InventoryList *ilist = player->inventory.getList("main");
-                       if(g_settings.getBool("creative_mode") == false && ilist != NULL)
-                       {
+                       /*
+                               Try creating inventory item
+                       */
+                       InventoryItem *item = obj->createPickedUpItem();
                        
-                               // Skip if inventory has no free space
-                               if(ilist->getUsedSlots() == ilist->getSize())
+                       if(item)
+                       {
+                               InventoryList *ilist = player->inventory.getList("main");
+                               if(ilist != NULL)
                                {
-                                       dout_server<<"Player inventory has no free space"<<std::endl;
-                                       return;
-                               }
+                                       if(g_settings.getBool("creative_mode") == false)
+                                       {
+                                               // Skip if inventory has no free space
+                                               if(ilist->getUsedSlots() == ilist->getSize())
+                                               {
+                                                       dout_server<<"Player inventory has no free space"<<std::endl;
+                                                       return;
+                                               }
 
-                               // Skip if object has been removed
-                               if(obj->m_removed)
-                                       return;
-                               
-                               /*
-                                       Create the inventory item
-                               */
-                               InventoryItem *item = obj->createPickedUpItem();
-                               
-                               if(item)
-                               {
-                                       // Add to inventory and send inventory
-                                       ilist->addItem(item);
-                                       UpdateCrafting(player->peer_id);
-                                       SendInventory(player->peer_id);
+                                               // Add to inventory and send inventory
+                                               ilist->addItem(item);
+                                               UpdateCrafting(player->peer_id);
+                                               SendInventory(player->peer_id);
+                                       }
 
                                        // Remove object from environment
                                        obj->m_removed = true;
                                }
-                               else
-                               {
-                                       /*
-                                               Item cannot be picked up. Punch it instead.
-                                       */
+                       }
+                       else
+                       {
+                               /*
+                                       Item cannot be picked up. Punch it instead.
+                               */
 
-                                       ToolItem *titem = NULL;
-                                       std::string toolname = "";
+                               ToolItem *titem = NULL;
+                               std::string toolname = "";
 
-                                       InventoryList *mlist = player->inventory.getList("main");
-                                       if(mlist != NULL)
-                                       {
-                                               InventoryItem *item = mlist->getItem(item_i);
-                                               if(item && (std::string)item->getName() == "ToolItem")
-                                               {
-                                                       titem = (ToolItem*)item;
-                                                       toolname = titem->getToolName();
-                                               }
-                                       }
-                                       
-                                       u16 wear = obj->punch(toolname);
-                                       
-                                       if(titem)
+                               InventoryList *mlist = player->inventory.getList("main");
+                               if(mlist != NULL)
+                               {
+                                       InventoryItem *item = mlist->getItem(item_i);
+                                       if(item && (std::string)item->getName() == "ToolItem")
                                        {
-                                               bool weared_out = titem->addWear(wear);
-                                               if(weared_out)
-                                                       mlist->deleteItem(item_i);
-                                               SendInventory(player->peer_id);
+                                               titem = (ToolItem*)item;
+                                               toolname = titem->getToolName();
                                        }
                                }
+
+                               v3f playerpos = player->getPosition();
+                               v3f objpos = obj->getBasePosition();
+                               v3f dir = (objpos - playerpos).normalize();
+                               
+                               u16 wear = obj->punch(toolname, dir);
+                               
+                               if(titem)
+                               {
+                                       bool weared_out = titem->addWear(wear);
+                                       if(weared_out)
+                                               mlist->deleteItem(item_i);
+                                       SendInventory(player->peer_id);
+                               }
+                       }
+               }
+               // Right click, do something with object
+               if(button == 1)
+               {
+                       // Track hp changes super-crappily
+                       u16 oldhp = player->hp;
+                       
+                       // Do stuff
+                       obj->rightClick(player);
+                       
+                       // Send back stuff
+                       if(player->hp != oldhp)
+                       {
+                               SendPlayerHP(player);
                        }
                }
        }
@@ -2256,7 +2574,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        // Mandatory parameter; actually used for nothing
                        core::map<v3s16, MapBlock*> modified_blocks;
 
-                       u8 material = CONTENT_IGNORE;
+                       content_t material = CONTENT_IGNORE;
                        u8 mineral = MINERAL_NONE;
 
                        bool cannot_remove_node = false;
@@ -2267,7 +2585,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Get mineral
                                mineral = n.getMineral();
                                // Get material at position
-                               material = n.d;
+                               material = n.getContent();
                                // If not yet cancelled
                                if(cannot_remove_node == false)
                                {
@@ -2305,8 +2623,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 "<<player->getName()<<" cannot remove node"
+                                               <<" because privileges are "<<getPlayerPrivs(player)
+                                               <<std::endl;
                                cannot_remove_node = true;
+                       }
 
                        /*
                                If node can't be removed, set block to be re-sent to
@@ -2330,12 +2653,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<u16> far_players;
-                       sendRemoveNode(p_under, peer_id, &far_players, 100);
+                       sendRemoveNode(p_under, peer_id, &far_players, 30);
                        
                        /*
                                Update and send inventory
@@ -2410,10 +2733,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
                        */
@@ -2454,8 +2778,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                try{
                                        // Don't add a node if this is not a free space
                                        MapNode n2 = m_env.getMap().getNode(p_over);
-                                       if(content_buildable_to(n2.d) == false
-                                               || (player->privs & PRIV_BUILD) ==0)
+                                       bool no_enough_privs =
+                                                       ((getPlayerPrivs(player) & PRIV_BUILD)==0);
+                                       if(no_enough_privs)
+                                               dstream<<"Player "<<player->getName()<<" cannot add node"
+                                                       <<" because privileges are "<<getPlayerPrivs(player)
+                                                       <<std::endl;
+
+                                       if(content_features(n2).buildable_to == false
+                                               || no_enough_privs)
                                        {
                                                // Client probably has wrong data.
                                                // Set block not sent, so that client will get
@@ -2485,15 +2816,37 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Create node data
                                MaterialItem *mitem = (MaterialItem*)item;
                                MapNode n;
-                               n.d = mitem->getMaterial();
-                               if(content_features(n.d).wall_mounted)
-                                       n.dir = packDir(p_under - p_over);
-                               
+                               n.setContent(mitem->getMaterial());
+
+                               // Calculate direction for wall mounted stuff
+                               if(content_features(n).wall_mounted)
+                                       n.param2 = packDir(p_under - p_over);
+
+                               // Calculate the direction for furnaces and chests and stuff
+                               if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
+                               {
+                                       v3f playerpos = player->getPosition();
+                                       v3f blockpos = intToFloat(p_over, BS) - playerpos;
+                                       blockpos = blockpos.normalize();
+                                       n.param1 = 0;
+                                       if (fabs(blockpos.X) > fabs(blockpos.Z)) {
+                                               if (blockpos.X < 0)
+                                                       n.param1 = 3;
+                                               else
+                                                       n.param1 = 1;
+                                       } else {
+                                               if (blockpos.Z < 0)
+                                                       n.param1 = 2;
+                                               else
+                                                       n.param1 = 0;
+                                       }
+                               }
+
                                /*
-                                       Send to all players
+                                       Send to all close-by players
                                */
                                core::list<u16> far_players;
-                               sendAddNode(p_over, n, 0, &far_players, 100);
+                               sendAddNode(p_over, n, 0, &far_players, 30);
                                
                                /*
                                        Handle inventory
@@ -2517,10 +2870,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<v3s16, MapBlock*> 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
                                */
@@ -2569,6 +2923,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        return;
                                }
 
+                               /*
+                                       If in creative mode, item dropping is disabled unless
+                                       player has build privileges
+                               */
+                               if(g_settings.getBool("creative_mode") &&
+                                       (getPlayerPrivs(player) & PRIV_BUILD) == 0)
+                               {
+                                       derr_server<<"Not allowing player to drop item: "
+                                                       "creative mode and no build privs"<<std::endl;
+                                       return;
+                               }
+
                                dout_server<<"Placing a miscellaneous item on map"
                                                <<std::endl;
                                
@@ -2652,7 +3018,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
@@ -2711,7 +3077,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
@@ -2887,42 +3253,57 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Whether to send to other players
                bool send_to_others = false;
                
+               // Local player gets all privileges regardless of
+               // what's set on their account.
+               u64 privs = getPlayerPrivs(player);
+
                // Parse commands
-               std::wstring commandprefix = L"/#";
-               if(message.substr(0, commandprefix.size()) == commandprefix)
+               if(message[0] == L'/')
                {
-                       line += L"Server: ";
-
-                       message = message.substr(commandprefix.size());
+                       size_t strip_size = 1;
+                       if (message[1] == L'#') // support old-style commans
+                               ++strip_size;
+                       message = message.substr(strip_size);
 
-                       // 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;
+                       WStrfnd f1(message);
+                       f1.next(L" "); // Skip over /#whatever
+                       std::wstring paramstring = f1.next(L"");
 
                        ServerCommandContext *ctx = new ServerCommandContext(
                                str_split(message, L' '),
+                               paramstring,
                                this,
                                &m_env,
                                player,
                                privs);
 
-                       line += processServerCommand(ctx);
-                       send_to_sender = ctx->flags & 1;
-                       send_to_others = ctx->flags & 2;
+                       std::wstring reply(processServerCommand(ctx));
+                       send_to_sender = ctx->flags & SEND_TO_SENDER;
+                       send_to_others = ctx->flags & SEND_TO_OTHERS;
+
+                       if (ctx->flags & SEND_NO_PREFIX)
+                               line += reply;
+                       else
+                               line += L"Server: " + reply;
+
                        delete ctx;
 
                }
                else
                {
-                       line += L"<";
-                       /*if(is_operator)
-                               line += L"@";*/
-                       line += name;
-                       line += L"> ";
-                       line += message;
-                       send_to_others = true;
+                       if(privs & PRIV_SHOUT)
+                       {
+                               line += L"<";
+                               line += name;
+                               line += L"> ";
+                               line += message;
+                               send_to_others = true;
+                       }
+                       else
+                       {
+                               line += L"Server: You are not allowed to shout";
+                               send_to_sender = true;
+                       }
                }
                
                if(line != L"")
@@ -2983,6 +3364,75 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                SendPlayerHP(player);
        }
+       else if(command == TOSERVER_PASSWORD)
+       {
+               /*
+                       [0] u16 TOSERVER_PASSWORD
+                       [2] u8[28] old password
+                       [30] u8[28] new password
+               */
+
+               if(datasize != 2+PASSWORD_SIZE*2)
+                       return;
+               /*char password[PASSWORD_SIZE];
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+                       password[i] = data[2+i];
+               password[PASSWORD_SIZE-1] = 0;*/
+               std::string oldpwd;
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+               {
+                       char c = data[2+i];
+                       if(c == 0)
+                               break;
+                       oldpwd += c;
+               }
+               std::string newpwd;
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+               {
+                       char c = data[2+PASSWORD_SIZE+i];
+                       if(c == 0)
+                               break;
+                       newpwd += c;
+               }
+
+               dstream<<"Server: Client requests a password change from "
+                               <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+
+               std::string playername = player->getName();
+
+               if(m_authmanager.exists(playername) == false)
+               {
+                       dstream<<"Server: playername not found in authmanager"<<std::endl;
+                       // Wrong old password supplied!!
+                       SendChatMessage(peer_id, L"playername not found in authmanager");
+                       return;
+               }
+
+               std::string checkpwd = m_authmanager.getPassword(playername);
+
+               if(oldpwd != checkpwd)
+               {
+                       dstream<<"Server: invalid old password"<<std::endl;
+                       // Wrong old password supplied!!
+                       SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
+                       return;
+               }
+
+               m_authmanager.setPassword(playername, newpwd);
+               
+               dstream<<"Server: password change successful for "<<playername
+                               <<std::endl;
+               SendChatMessage(peer_id, L"Password change successful");
+       }
+       else if (command == TOSERVER_PLAYERITEM)
+       {
+               if (datasize < 2+2)
+                       return;
+
+               u16 item = readU16(&data[2]);
+               player->wieldItem(item);
+               SendWieldedItem(player);
+       }
        else
        {
                derr_server<<"WARNING: Server::ProcessData(): Ignoring "
@@ -3000,7 +3450,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
 void Server::onMapEditEvent(MapEditEvent *event)
 {
-       dstream<<"Server::onMapEditEvent()"<<std::endl;
+       //dstream<<"Server::onMapEditEvent()"<<std::endl;
        if(m_ignore_map_edit_events)
                return;
        MapEditEvent *e = event->clone();
@@ -3163,12 +3613,14 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
        con.Send(peer_id, 0, data, true);
 }
 
-void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
+void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
+               const std::wstring &reason)
 {
        DSTACK(__FUNCTION_NAME);
        std::ostringstream os(std::ios_base::binary);
 
        writeU16(os, TOCLIENT_ACCESS_DENIED);
+       os<<serializeWideString(reason);
 
        // Make data buffer
        std::string s = os.str();
@@ -3265,6 +3717,60 @@ void Server::SendInventory(u16 peer_id)
        m_con.Send(peer_id, 0, data, true);
 }
 
+std::string getWieldedItemString(const Player *player)
+{
+       const InventoryItem *item = player->getWieldItem();
+       if (item == NULL)
+               return std::string("");
+       std::ostringstream os(std::ios_base::binary);
+       item->serialize(os);
+       return os.str();
+}
+
+void Server::SendWieldedItem(const Player* player)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       assert(player);
+
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOCLIENT_PLAYERITEM);
+       writeU16(os, 1);
+       writeU16(os, player->peer_id);
+       os<<serializeString(getWieldedItemString(player));
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+
+       m_con.SendToAll(0, data, true);
+}
+
+void Server::SendPlayerItems()
+{
+       DSTACK(__FUNCTION_NAME);
+
+       std::ostringstream os(std::ios_base::binary);
+       core::list<Player *> players = m_env.getPlayers(true);
+
+       writeU16(os, TOCLIENT_PLAYERITEM);
+       writeU16(os, players.size());
+       core::list<Player *>::Iterator i;
+       for(i = players.begin(); i != players.end(); ++i)
+       {
+               Player *p = *i;
+               writeU16(os, p->peer_id);
+               os<<serializeString(getWieldedItemString(p));
+       }
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+
+       m_con.SendToAll(0, data, true);
+}
+
 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
 {
        DSTACK(__FUNCTION_NAME);
@@ -3443,6 +3949,17 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
        }
 }
 
+void Server::setBlockNotSent(v3s16 p)
+{
+       for(core::map<u16, RemoteClient*>::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);
@@ -3508,20 +4025,24 @@ void Server::SendBlocks(float dtime)
        core::array<PrioritySortedBlockTransfer> queue;
 
        s32 total_sending = 0;
-
-       for(core::map<u16, RemoteClient*>::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<u16, RemoteClient*>::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.
@@ -3592,317 +4113,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;
-                               }
-                       }
-
-                       // 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
@@ -3949,107 +4163,25 @@ std::wstring Server::getStatusString()
        }
        os<<L"}";
        if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
-               os<<" WARNING: Map saving is disabled."<<std::endl;
+               os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
+       if(g_settings.get("motd") != "")
+               os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
        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_MESE,
-               CONTENT_WATERSOURCE,
-               CONTENT_CLOUD,
-               CONTENT_CHEST,
-               CONTENT_FURNACE,
-               CONTENT_SIGN_WALL,
-               CONTENT_IGNORE
-       };
-       
-       u8 *mip = material_items;
-       for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
-       {
-               if(*mip == CONTENT_IGNORE)
-                       break;
-
-               InventoryItem *item = new MaterialItem(*mip, 1);
-               player->inventory.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; i<USEFUL_CONTENT_COUNT; i++)
-       {
-               // Skip some materials
-               if(i == CONTENT_WATER || i == CONTENT_TORCH
-                       || i == CONTENT_COALSTONE)
-                       continue;
-
-               InventoryItem *item = new MaterialItem(i, 1);
-               player->inventory.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;
-       
+
        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++)
        {
@@ -4079,6 +4211,7 @@ v3f findSpawnPos(ServerMap &map)
                //dstream<<"Searched through "<<i<<" places."<<std::endl;
                break;
        }
+#endif
        
        // If no suitable place was not found, go above water at least.
        if(groundheight < WATER_LEVEL)
@@ -4086,7 +4219,7 @@ v3f findSpawnPos(ServerMap &map)
 
        return intToFloat(v3s16(
                        nodepos.X,
-                       groundheight + 2,
+                       groundheight + 3,
                        nodepos.Y
                        ), BS);
 }
@@ -4112,7 +4245,12 @@ 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);
+                       // Warning: double code below
+                       // Backup actual inventory
+                       player->inventory_backup = new Inventory();
+                       *(player->inventory_backup) = player->inventory;
+                       // Set creative inventory
+                       craft_set_creative_inventory(player);
                }
 
                return player;
@@ -4137,7 +4275,10 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                //player->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")));
 
                /*
                        Set player position
@@ -4162,78 +4303,16 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                
                if(g_settings.getBool("creative_mode"))
                {
-                       setCreativeInventory(player);
+                       // Warning: double code above
+                       // Backup actual inventory
+                       player->inventory_backup = new Inventory();
+                       *(player->inventory_backup) = player->inventory;
+                       // Set creative inventory
+                       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;
@@ -4351,29 +4430,69 @@ 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);
        
-       std::cout<<DTIME<<std::endl;
-       std::cout<<"========================"<<std::endl;
-       std::cout<<"Running dedicated server"<<std::endl;
-       std::cout<<"========================"<<std::endl;
-       std::cout<<std::endl;
+       dstream<<DTIME<<std::endl;
+       dstream<<"========================"<<std::endl;
+       dstream<<"Running dedicated server"<<std::endl;
+       dstream<<"========================"<<std::endl;
+       dstream<<std::endl;
+
+       IntervalLimiter m_profiler_interval;
 
        for(;;)
        {
                // This is kind of a hack but can be done like this
                // because server.step() is very light
-               sleep_ms(30);
+               {
+                       ScopeProfiler sp(&g_profiler, "dedicated server sleep");
+                       sleep_ms(30);
+               }
                server.step(0.030);
 
                if(server.getShutdownRequested() || kill)
                {
-                       std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
+                       dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
                        break;
                }
 
+               /*
+                       Profiler
+               */
+               float profiler_print_interval =
+                               g_settings.getFloat("profiler_print_interval");
+               if(profiler_print_interval != 0)
+               {
+                       if(m_profiler_interval.step(0.030, profiler_print_interval))
+                       {
+                               dstream<<"Profiler:"<<std::endl;
+                               g_profiler.print(dstream);
+                               g_profiler.clear();
+                       }
+               }
+               
+               /*
+                       Player info
+               */
                static int counter = 0;
                counter--;
                if(counter <= 0)
@@ -4386,10 +4505,10 @@ void dedicated_server_loop(Server &server, bool &kill)
                        u32 sum = PIChecksum(list);
                        if(sum != sum_old)
                        {
-                               std::cout<<DTIME<<"Player info:"<<std::endl;
+                               dstream<<DTIME<<"Player info:"<<std::endl;
                                for(i=list.begin(); i!=list.end(); i++)
                                {
-                                       i->PrintLine(&std::cout);
+                                       i->PrintLine(&dstream);
                                }
                        }
                        sum_old = sum;