]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/server.cpp
Fix doc and forceloading crash.
[dragonfireclient.git] / src / server.cpp
index a7767842d7127c1e86977ea5fd3cf217f80fd13a..b257448a1c4e2de8b1b7091ffd74e103352e4c2a 100644 (file)
@@ -1,6 +1,6 @@
 /*
-Minetest-c55
-Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
@@ -20,25 +20,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "server.h"
 #include <iostream>
 #include <queue>
+#include <algorithm>
 #include "clientserver.h"
+#include "ban.h"
+#include "environment.h"
 #include "map.h"
-#include "jmutexautolock.h"
+#include "jthread/jmutexautolock.h"
 #include "main.h"
 #include "constants.h"
 #include "voxel.h"
 #include "config.h"
+#include "version.h"
 #include "filesys.h"
 #include "mapblock.h"
 #include "serverobject.h"
+#include "genericobject.h"
 #include "settings.h"
 #include "profiler.h"
 #include "log.h"
-#include "script.h"
-#include "scriptapi.h"
+#include "scripting_game.h"
 #include "nodedef.h"
 #include "itemdef.h"
 #include "craftdef.h"
+#include "emerge.h"
 #include "mapgen.h"
+#include "biome.h"
 #include "content_mapnode.h"
 #include "content_nodemeta.h"
 #include "content_abm.h"
@@ -50,77 +56,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "sound.h" // dummySoundManager
 #include "event_manager.h"
 #include "hex.h"
+#include "serverlist.h"
 #include "util/string.h"
 #include "util/pointedthing.h"
 #include "util/mathconstants.h"
 #include "rollback.h"
 #include "util/serialize.h"
+#include "util/thread.h"
+#include "defaultsettings.h"
 
-#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
-
-#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
-
-class MapEditEventIgnorer
+class ClientNotFoundException : public BaseException
 {
 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;
+       ClientNotFoundException(const char *s):
+               BaseException(s)
+       {}
 };
 
-class MapEditEventAreaIgnorer
+class ServerThread : public JThread
 {
+       Server *m_server;
+
 public:
-       MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
-               m_ignorevariable(ignorevariable)
-       {
-               if(m_ignorevariable->getVolume() == 0)
-                       *m_ignorevariable = a;
-               else
-                       m_ignorevariable = NULL;
-       }
 
-       ~MapEditEventAreaIgnorer()
+       ServerThread(Server *server):
+               JThread(),
+               m_server(server)
        {
-               if(m_ignorevariable)
-               {
-                       assert(m_ignorevariable->getVolume() != 0);
-                       *m_ignorevariable = VoxelArea();
-               }
        }
-       
-private:
-       VoxelArea *m_ignorevariable;
+
+       void * Thread();
 };
 
 void * ServerThread::Thread()
 {
-       ThreadStarted();
-
        log_register_thread("ServerThread");
 
        DSTACK(__FUNCTION_NAME);
-
        BEGIN_DEBUG_EXCEPTION_HANDLER
 
-       while(getRun())
+       m_server->AsyncRunStep(true);
+
+       ThreadStarted();
+
+       while(!StopRequested())
        {
                try{
                        //TimeTaker timer("AsyncRunStep() + Receive()");
@@ -129,7 +108,7 @@ void * ServerThread::Thread()
                                //TimeTaker timer("AsyncRunStep()");
                                m_server->AsyncRunStep();
                        }
-               
+
                        //infostream<<"Running m_server->Receive()"<<std::endl;
                        m_server->Receive();
                }
@@ -140,6 +119,9 @@ void * ServerThread::Thread()
                {
                        infostream<<"Server: PeerNotFoundException"<<std::endl;
                }
+               catch(ClientNotFoundException &e)
+               {
+               }
                catch(con::ConnectionBindFailed &e)
                {
                        m_server->setAsyncFatalError(e.what());
@@ -149,263 +131,9 @@ void * ServerThread::Thread()
                        m_server->setAsyncFatalError(e.what());
                }
        }
-       
-       END_DEBUG_EXCEPTION_HANDLER(errorstream)
-
-       return NULL;
-}
-
-void * EmergeThread::Thread()
-{
-       ThreadStarted();
-
-       log_register_thread("EmergeThread");
-
-       DSTACK(__FUNCTION_NAME);
-
-       BEGIN_DEBUG_EXCEPTION_HANDLER
-
-       bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
-
-       v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
-       
-       /*
-               Get block info from queue, emerge them and send them
-               to clients.
-
-               After queue is empty, exit.
-       */
-       while(getRun())
-       try{
-               QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
-               if(qptr == NULL)
-                       break;
-               
-               SharedPtr<QueuedBlockEmerge> q(qptr);
-
-               v3s16 &p = q->pos;
-               v2s16 p2d(p.X,p.Z);
-               
-               last_tried_pos = p;
-
-               /*
-                       Do not generate over-limit
-               */
-               if(blockpos_over_limit(p))
-                       continue;
-                       
-               //infostream<<"EmergeThread::Thread(): running"<<std::endl;
-
-               //TimeTaker timer("block emerge");
-               
-               /*
-                       Try to emerge it from somewhere.
-
-                       If it is only wanted as optional, only loading from disk
-                       will be allowed.
-               */
-               
-               /*
-                       Check if any peer wants it as non-optional. In that case it
-                       will be generated.
-
-                       Also decrement the emerge queue count in clients.
-               */
-
-               bool only_from_disk = true;
-
-               {
-                       core::map<u16, u8>::Iterator i;
-                       for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
-                       {
-                               //u16 peer_id = i.getNode()->getKey();
-
-                               // Check flags
-                               u8 flags = i.getNode()->getValue();
-                               if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
-                                       only_from_disk = false;
-                               
-                       }
-               }
-               
-               if(enable_mapgen_debug_info)
-                       infostream<<"EmergeThread: p="
-                                       <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
-                                       <<"only_from_disk="<<only_from_disk<<std::endl;
-               
-               ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
-                       
-               MapBlock *block = NULL;
-               bool got_block = true;
-               core::map<v3s16, MapBlock*> modified_blocks;
-
-               /*
-                       Try to fetch block from memory or disk.
-                       If not found and asked to generate, initialize generator.
-               */
-               
-               bool started_generate = false;
-               mapgen::BlockMakeData data;
-
-               {
-                       JMutexAutoLock envlock(m_server->m_env_mutex);
-                       
-                       // Load sector if it isn't loaded
-                       if(map.getSectorNoGenerateNoEx(p2d) == NULL)
-                               map.loadSectorMeta(p2d);
-                       
-                       // Attempt to load block
-                       block = map.getBlockNoCreateNoEx(p);
-                       if(!block || block->isDummy() || !block->isGenerated())
-                       {
-                               if(enable_mapgen_debug_info)
-                                       infostream<<"EmergeThread: not in memory, "
-                                                       <<"attempting to load from disk"<<std::endl;
-
-                               block = map.loadBlock(p);
-                       }
-                       
-                       // If could not load and allowed to generate, start generation
-                       // inside this same envlock
-                       if(only_from_disk == false &&
-                                       (block == NULL || block->isGenerated() == false)){
-                               if(enable_mapgen_debug_info)
-                                       infostream<<"EmergeThread: generating"<<std::endl;
-                               started_generate = true;
-
-                               map.initBlockMake(&data, p);
-                       }
-               }
-
-               /*
-                       If generator was initialized, generate now when envlock is free.
-               */
-               if(started_generate)
-               {
-                       {
-                               ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
-                                               SPT_AVG);
-                               TimeTaker t("mapgen::make_block()");
-
-                               mapgen::make_block(&data);
-
-                               if(enable_mapgen_debug_info == false)
-                                       t.stop(true); // Hide output
-                       }
-                       
-                       do{ // enable break
-                               // Lock environment again to access the map
-                               JMutexAutoLock envlock(m_server->m_env_mutex);
-                               
-                               ScopeProfiler sp(g_profiler, "EmergeThread: after "
-                                               "mapgen::make_block (envlock)", SPT_AVG);
-
-                               // Blit data back on map, update lighting, add mobs and
-                               // whatever this does
-                               map.finishBlockMake(&data, modified_blocks);
-
-                               // Get central block
-                               block = map.getBlockNoCreateNoEx(p);
-                               
-                               // If block doesn't exist, don't try doing anything with it
-                               // This happens if the block is not in generation boundaries
-                               if(!block)
-                                       break;
-
-                               /*
-                                       Do some post-generate stuff
-                               */
-
-                               v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
-                               v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
-                                               v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
-                               
-                               /*
-                                       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);
-                               MapEditEventAreaIgnorer ign(
-                                               &m_server->m_ignore_map_edit_events_area,
-                                               VoxelArea(minp, maxp));
-                               {
-                                       TimeTaker timer("on_generated");
-                                       scriptapi_environment_on_generated(m_server->m_lua,
-                                                       minp, maxp, mapgen::get_blockseed(data.seed, minp));
-                                       /*int t = timer.stop(true);
-                                       dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
-                               }
-                               
-                               if(enable_mapgen_debug_info)
-                                       infostream<<"EmergeThread: ended up with: "
-                                                       <<analyze_block(block)<<std::endl;
-
-                               // Activate objects and stuff
-                               m_server->m_env->activateBlock(block, 0);
-                       }while(false);
-               }
-
-               if(block == NULL)
-                       got_block = false;
-                       
-               /*
-                       Set sent status of modified blocks on clients
-               */
-       
-               // NOTE: Server's clients are also behind the connection mutex
-               JMutexAutoLock lock(m_server->m_con_mutex);
-
-               /*
-                       Add the originally fetched block to the modified list
-               */
-               if(got_block)
-               {
-                       modified_blocks.insert(p, block);
-               }
-               
-               /*
-                       Set the modified blocks unsent for all the clients
-               */
-               
-               for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_server->m_clients.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       RemoteClient *client = i.getNode()->getValue();
-                       
-                       if(modified_blocks.size() > 0)
-                       {
-                               // Remove block from sent history
-                               client->SetBlocksNotSent(modified_blocks);
-                       }
-               }
-       }
-       catch(VersionMismatchException &e)
-       {
-               std::ostringstream err;
-               err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
-               err<<"----"<<std::endl;
-               err<<"\""<<e.what()<<"\""<<std::endl;
-               err<<"See debug.txt."<<std::endl;
-               err<<"World probably saved by a newer version of Minetest."<<std::endl;
-               m_server->setAsyncFatalError(err.str());
-       }
-       catch(SerializationError &e)
-       {
-               std::ostringstream err;
-               err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
-               err<<"----"<<std::endl;
-               err<<"\""<<e.what()<<"\""<<std::endl;
-               err<<"See debug.txt."<<std::endl;
-               err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
-               m_server->setAsyncFatalError(err.str());
-       }
 
        END_DEBUG_EXCEPTION_HANDLER(errorstream)
 
-       log_deregister_thread();
-
        return NULL;
 }
 
@@ -431,17 +159,17 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
 }
 
 void RemoteClient::GetNextBlocks(Server *server, float dtime,
-               core::array<PrioritySortedBlockTransfer> &dest)
+               std::vector<PrioritySortedBlockTransfer> &dest)
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        /*u32 timer_result;
        TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
-       
+
        // Increment timers
        m_nothing_to_send_pause_timer -= dtime;
        m_nearest_unsent_reset_timer += dtime;
-       
+
        if(m_nothing_to_send_pause_timer >= 0)
                return;
 
@@ -459,7 +187,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        }
 
        //TimeTaker timer("RemoteClient::GetNextBlocks");
-       
+
        v3f playerpos = player->getPosition();
        v3f playerspeed = player->getSpeed();
        v3f playerspeeddir(0,0,0);
@@ -471,7 +199,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
 
        v3s16 center = getNodeBlockPos(center_nodepos);
-       
+
        // Camera position and direction
        v3f camera_pos = player->getEyePosition();
        v3f camera_dir = v3f(0,0,1);
@@ -484,7 +212,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        /*
                Get the starting value of the block finder radius.
        */
-               
+
        if(m_last_center != center)
        {
                m_nearest_unsent_d = 0;
@@ -493,7 +221,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
        /*infostream<<"m_nearest_unsent_reset_timer="
                        <<m_nearest_unsent_reset_timer<<std::endl;*/
-                       
+
        // Reset periodically to workaround for some bugs or stuff
        if(m_nearest_unsent_reset_timer > 20.0)
        {
@@ -514,7 +242,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
        /*
                Check the time from last addNode/removeNode.
-               
+
                Decrease send rate if player is building stuff.
        */
        m_time_from_building += dtime;
@@ -524,12 +252,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                max_simul_sends_usually
                        = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
        }
-       
+
        /*
                Number of blocks sending + number of blocks selected for sending
        */
        u32 num_blocks_selected = m_blocks_sending.size();
-       
+
        /*
                next time d will be continued from the d from which the nearest
                unsent block was found this time.
@@ -541,28 +269,28 @@ 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
        s16 max_d_increment_at_time = 2;
        if(d_max > d_start + max_d_increment_at_time)
                d_max = d_start + max_d_increment_at_time;
        /*if(d_max_gen > d_start+2)
                d_max_gen = d_start+2;*/
-       
+
        //infostream<<"Starting from "<<d_start<<std::endl;
 
        s32 nearest_emerged_d = -1;
        s32 nearest_emergefull_d = -1;
        s32 nearest_sent_d = -1;
        bool queue_is_full = false;
-       
+
        s16 d;
        for(d = d_start; d <= d_max; d++)
        {
                /*errorstream<<"checking d="<<d<<" for "
                                <<server->getPlayerName(peer_id)<<std::endl;*/
                //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
-               
+
                /*
                        If m_nearest_unsent_d was changed by the EmergeThread
                        (it can change it to 0 through SetBlockNotSent),
@@ -579,14 +307,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        Get the border/face dot coordinates of a "d-radiused"
                        box
                */
-               core::list<v3s16> list;
+               std::list<v3s16> list;
                getFacePositions(list, d);
-               
-               core::list<v3s16>::Iterator li;
-               for(li=list.begin(); li!=list.end(); li++)
+
+               std::list<v3s16>::iterator li;
+               for(li=list.begin(); li!=list.end(); ++li)
                {
                        v3s16 p = *li + center;
-                       
+
                        /*
                                Send throttling
                                - Don't allow too many simultaneous transfers
@@ -594,10 +322,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
                                Also, don't send blocks that are already flying.
                        */
-                       
+
                        // Start with the usual maximum
                        u16 max_simul_dynamic = max_simul_sends_usually;
-                       
+
                        // If block is very close, allow full maximum
                        if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
                                max_simul_dynamic = max_simul_sends_setting;
@@ -608,11 +336,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                queue_is_full = true;
                                goto queue_full_break;
                        }
-                       
+
                        // Don't send blocks that are currently being transferred
-                       if(m_blocks_sending.find(p) != NULL)
+                       if(m_blocks_sending.find(p) != m_blocks_sending.end())
                                continue;
-               
+
                        /*
                                Do not go over-limit
                        */
@@ -623,10 +351,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
                        || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
                                continue;
-               
+
                        // If this is true, inexistent block will be made from scratch
                        bool generate = d <= d_max_gen;
-                       
+
                        {
                                /*// Limit the generating area vertically to 2/3
                                if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
@@ -655,7 +383,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                v2s16 p2d_nodes_center(
                                        MAP_BLOCKSIZE*p.X,
                                        MAP_BLOCKSIZE*p.Z);
-                               
+
                                // Get ground height in nodes
                                s16 gh = server->m_env->getServerMap().findGroundLevel(
                                                p2d_nodes_center);
@@ -687,7 +415,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                Don't send already sent blocks
                        */
                        {
-                               if(m_blocks_sent.find(p) != NULL)
+                               if(m_blocks_sent.find(p) != m_blocks_sent.end())
                                {
                                        continue;
                                }
@@ -697,7 +425,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                Check if map has this block
                        */
                        MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
-                       
+
                        bool surely_not_found_on_disk = false;
                        bool block_is_invalid = false;
                        if(block != NULL)
@@ -711,13 +439,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                {
                                        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;
                                }
-                               
+
                                /*if(block->isFullyGenerated() == false)
                                {
                                        block_is_invalid = true;
@@ -763,7 +491,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        */
                        if(block == NULL || surely_not_found_on_disk || block_is_invalid)
                        {
-                               //TODO: Get value from somewhere
+                       /*      //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
@@ -775,13 +503,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
                                {
                                        //infostream<<"Adding block to emerge queue"<<std::endl;
-                                       
+
                                        // Add it to the emerge queue and trigger the thread
-                                       
+
                                        u8 flags = 0;
                                        if(generate == false)
                                                flags |= BLOCK_EMERGE_FLAG_FROMDISK;
-                                       
+
                                        server->m_emerge_queue.addBlock(peer_id, p, flags);
                                        server->m_emergethread.trigger();
 
@@ -790,6 +518,17 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                } else {
                                        if(nearest_emergefull_d == -1)
                                                nearest_emergefull_d = d;
+                                       goto queue_full_break;
+                               }
+                       */
+
+                               if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) {
+                                       if (nearest_emerged_d == -1)
+                                               nearest_emerged_d = d;
+                               } else {
+                                       if (nearest_emergefull_d == -1)
+                                               nearest_emergefull_d = d;
+                                       goto queue_full_break;
                                }
                                
                                // get next one.
@@ -816,7 +555,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 queue_full_break:
 
        //infostream<<"Stopped at "<<d<<std::endl;
-       
+
        // If nothing was found for sending and nothing was queued for
        // emerging, continue next time browsing from here
        if(nearest_emerged_d != -1){
@@ -848,21 +587,21 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
 void RemoteClient::GotBlock(v3s16 p)
 {
-       if(m_blocks_sending.find(p) != NULL)
-               m_blocks_sending.remove(p);
+       if(m_blocks_sending.find(p) != m_blocks_sending.end())
+               m_blocks_sending.erase(p);
        else
        {
                /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
                                " m_blocks_sending"<<std::endl;*/
                m_excess_gotblocks++;
        }
-       m_blocks_sent.insert(p, true);
+       m_blocks_sent.insert(p);
 }
 
 void RemoteClient::SentBlock(v3s16 p)
 {
-       if(m_blocks_sending.find(p) == NULL)
-               m_blocks_sending.insert(p, 0.0);
+       if(m_blocks_sending.find(p) == m_blocks_sending.end())
+               m_blocks_sending[p] = 0.0;
        else
                infostream<<"RemoteClient::SentBlock(): Sent block"
                                " already in m_blocks_sending"<<std::endl;
@@ -871,79 +610,57 @@ void RemoteClient::SentBlock(v3s16 p)
 void RemoteClient::SetBlockNotSent(v3s16 p)
 {
        m_nearest_unsent_d = 0;
-       
-       if(m_blocks_sending.find(p) != NULL)
-               m_blocks_sending.remove(p);
-       if(m_blocks_sent.find(p) != NULL)
-               m_blocks_sent.remove(p);
+
+       if(m_blocks_sending.find(p) != m_blocks_sending.end())
+               m_blocks_sending.erase(p);
+       if(m_blocks_sent.find(p) != m_blocks_sent.end())
+               m_blocks_sent.erase(p);
 }
 
-void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
+void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
 {
        m_nearest_unsent_d = 0;
-       
-       for(core::map<v3s16, MapBlock*>::Iterator
-                       i = blocks.getIterator();
-                       i.atEnd()==false; i++)
+
+       for(std::map<v3s16, MapBlock*>::iterator
+                       i = blocks.begin();
+                       i != blocks.end(); ++i)
        {
-               v3s16 p = i.getNode()->getKey();
+               v3s16 p = i->first;
 
-               if(m_blocks_sending.find(p) != NULL)
-                       m_blocks_sending.remove(p);
-               if(m_blocks_sent.find(p) != NULL)
-                       m_blocks_sent.remove(p);
+               if(m_blocks_sending.find(p) != m_blocks_sending.end())
+                       m_blocks_sending.erase(p);
+               if(m_blocks_sent.find(p) != m_blocks_sent.end())
+                       m_blocks_sent.erase(p);
        }
 }
 
-/*
-       PlayerInfo
-*/
-
-PlayerInfo::PlayerInfo()
-{
-       name[0] = 0;
-       avg_rtt = 0;
-}
-
-void PlayerInfo::PrintLine(std::ostream *s)
-{
-       (*s)<<id<<": ";
-       (*s)<<"\""<<name<<"\" ("
-                       <<(position.X/10)<<","<<(position.Y/10)
-                       <<","<<(position.Z/10)<<") ";
-       address.print(s);
-       (*s)<<" avg_rtt="<<avg_rtt;
-       (*s)<<std::endl;
-}
-
 /*
        Server
 */
 
 Server::Server(
                const std::string &path_world,
-               const std::string &path_config,
                const SubgameSpec &gamespec,
                bool simple_singleplayer_mode
        ):
        m_path_world(path_world),
-       m_path_config(path_config),
        m_gamespec(gamespec),
        m_simple_singleplayer_mode(simple_singleplayer_mode),
        m_async_fatal_error(""),
        m_env(NULL),
-       m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
-       m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
+       m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT,
+             g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), this),
+       m_banmanager(NULL),
        m_rollback(NULL),
        m_rollback_sink_enabled(true),
        m_enable_rollback_recording(false),
-       m_lua(NULL),
+       m_emerge(NULL),
+       m_script(NULL),
        m_itemdef(createItemDefManager()),
        m_nodedef(createNodeDefManager()),
        m_craftdef(createCraftDefManager()),
        m_event(new EventManager()),
-       m_thread(this),
-       m_emergethread(this),
+       m_thread(NULL),
        m_time_of_day_send_timer(0),
        m_uptime(0),
        m_shutdown_requested(false),
@@ -951,82 +668,98 @@ Server::Server(
        m_ignore_map_edit_events_peer_id(0)
 {
        m_liquid_transform_timer = 0.0;
+       m_liquid_transform_every = 1.0;
        m_print_info_timer = 0.0;
+       m_masterserver_timer = 0.0;
        m_objectdata_timer = 0.0;
        m_emergethread_trigger_timer = 0.0;
        m_savemap_timer = 0.0;
-       
-       m_env_mutex.Init();
-       m_con_mutex.Init();
-       m_step_dtime_mutex.Init();
+
        m_step_dtime = 0.0;
+       m_lag = g_settings->getFloat("dedicated_server_step");
 
        if(path_world == "")
                throw ServerError("Supplied empty world path");
-       
+
        if(!gamespec.isValid())
                throw ServerError("Supplied invalid gamespec");
-       
+
        infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
        if(m_simple_singleplayer_mode)
                infostream<<" in simple singleplayer mode"<<std::endl;
        else
                infostream<<std::endl;
        infostream<<"- world:  "<<m_path_world<<std::endl;
-       infostream<<"- config: "<<m_path_config<<std::endl;
        infostream<<"- game:   "<<m_gamespec.path<<std::endl;
 
-       // Create rollback manager
-       std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
-       m_rollback = createRollbackManager(rollback_path, this);
+       // Initialize default settings and override defaults with those provided
+       // by the game
+       set_default_settings(g_settings);
+       Settings gamedefaults;
+       getGameMinetestConfig(gamespec.path, gamedefaults);
+       override_default_settings(g_settings, &gamedefaults);
+
+       // Create server thread
+       m_thread = new ServerThread(this);
+
+       // Create emerge manager
+       m_emerge = new EmergeManager(this);
 
        // Create world if it doesn't exist
        if(!initializeWorld(m_path_world, m_gamespec.id))
                throw ServerError("Failed to initialize world");
 
+       // Create ban manager
+       std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
+       m_banmanager = new BanManager(ban_path);
+
+       // Create rollback manager
+       std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
+       m_rollback = createRollbackManager(rollback_path, this);
+
        ModConfiguration modconf(m_path_world);
        m_mods = modconf.getMods();
+       std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
        // complain about mods with unsatisfied dependencies
-       if(!modconf.isConsistent())     
+       if(!modconf.isConsistent())
        {
-               errorstream << "The following mods have unsatisfied dependencies: ";
-               std::list<ModSpec> modlist = modconf.getUnsatisfiedMods();
-               for(std::list<ModSpec>::iterator it = modlist.begin();
-                       it != modlist.end(); ++it)
+               for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
+                       it != unsatisfied_mods.end(); ++it)
                {
-                       errorstream << (*it).name << " ";
+                       ModSpec mod = *it;
+                       errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
+                       for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
+                               dep_it != mod.unsatisfied_depends.end(); ++dep_it)
+                               errorstream << " \"" << *dep_it << "\"";
+                       errorstream << std::endl;
                }
-               errorstream << std::endl;
        }
 
        Settings worldmt_settings;
        std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
        worldmt_settings.readConfigFile(worldmt.c_str());
        std::vector<std::string> names = worldmt_settings.getNames();
-       std::set<std::string> exclude_mod_names;
        std::set<std::string> load_mod_names;
-       for(std::vector<std::string>::iterator it = names.begin(); 
+       for(std::vector<std::string>::iterator it = names.begin();
                it != names.end(); ++it)
-       {       
-               std::string name = *it;  
-               if (name.compare(0,9,"load_mod_")==0)
-               {
-                       if(worldmt_settings.getBool(name))
-                               load_mod_names.insert(name.substr(9));
-                       else                    
-                               exclude_mod_names.insert(name.substr(9));
-               }
+       {
+               std::string name = *it;
+               if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
+                       load_mod_names.insert(name.substr(9));
        }
        // complain about mods declared to be loaded, but not found
        for(std::vector<ModSpec>::iterator it = m_mods.begin();
-               it != m_mods.end(); ++it)
+                       it != m_mods.end(); ++it)
+               load_mod_names.erase((*it).name);
+       for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
+                       it != unsatisfied_mods.end(); ++it)
                load_mod_names.erase((*it).name);
        if(!load_mod_names.empty())
-       {               
-               errorstream << "The following mods could not be found: ";
+       {
+               errorstream << "The following mods could not be found:";
                for(std::set<std::string>::iterator it = load_mod_names.begin();
                        it != load_mod_names.end(); ++it)
-                       errorstream << (*it) << " ";
+                       errorstream << " \"" << (*it) << "\"";
                errorstream << std::endl;
        }
 
@@ -1040,14 +773,14 @@ Server::Server(
        // Initialize scripting
 
        infostream<<"Server: Initializing Lua"<<std::endl;
-       m_lua = script_init();
-       assert(m_lua);
-       // Export API
-       scriptapi_export(m_lua, this);
+
+       m_script = new GameScripting(this);
+
+
        // Load and run builtin.lua
        infostream<<"Server: Loading builtin.lua [\""
                        <<builtinpath<<"\"]"<<std::endl;
-       bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
+       bool success = m_script->loadMod(builtinpath, "__builtin");
        if(!success){
                errorstream<<"Server: Failed to load and run "
                                <<builtinpath<<std::endl;
@@ -1068,14 +801,14 @@ Server::Server(
                std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
                infostream<<"  ["<<padStringRight(mod.name, 12)<<"] [\""
                                <<scriptpath<<"\"]"<<std::endl;
-               bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
+               bool success = m_script->loadMod(scriptpath, mod.name);
                if(!success){
                        errorstream<<"Server: Failed to load and run "
                                        <<scriptpath<<std::endl;
                        throw ModError("Failed to load and run "+scriptpath);
                }
        }
-       
+
        // Read Textures and calculate sha1 sums
        fillMediaCache();
 
@@ -1083,15 +816,22 @@ Server::Server(
        m_nodedef->updateAliases(m_itemdef);
 
        // Initialize Environment
+       ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
+       m_env = new ServerEnvironment(servermap, m_script, this, m_emerge);
        
-       m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
-                       this, this);
+       // Run some callbacks after the MG params have been set up but before activation
+       MapgenParams *mgparams = servermap->getMapgenParams();
+       m_script->environment_OnMapgenInit(mgparams);
        
+       // Initialize mapgens
+       m_emerge->initMapgens(mgparams);
+       servermap->setMapgenParams(m_emerge->params);
+
        // Give environment reference to scripting api
-       scriptapi_add_environment(m_lua, m_env);
-       
+       m_script->initializeEnvironment(m_env);
+
        // Register us to receive map edit events
-       m_env->getMap().addEventReceiver(this);
+       servermap->addEventReceiver(this);
 
        // If file exists, load environment metadata
        if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
@@ -1108,6 +848,8 @@ Server::Server(
                Add some test ActiveBlockModifiers to environment
        */
        add_legacy_abms(m_env, m_nodedef);
+
+       m_liquid_transform_every = g_settings->getFloat("liquid_update");
 }
 
 Server::~Server()
@@ -1119,19 +861,19 @@ Server::~Server()
        */
        {
                JMutexAutoLock conlock(m_con_mutex);
-               
+
                std::wstring line = L"*** Server shutting down";
 
                /*
                        Send the message to clients
                */
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd() == false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                       i = m_clients.begin();
+                       i != m_clients.end(); ++i)
                {
                        // Get client and check that it is valid
-                       RemoteClient *client = i.getNode()->getValue();
-                       assert(client->peer_id == i.getNode()->getKey());
+                       RemoteClient *client = i->second;
+                       assert(client->peer_id == i->first);
                        if(client->serialization_version == SER_FMT_VER_INVALID)
                                continue;
 
@@ -1150,7 +892,7 @@ Server::~Server()
                /*
                        Execute script shutdown hooks
                */
-               scriptapi_on_shutdown(m_lua);
+               m_script->on_shutdown();
        }
 
        {
@@ -1168,38 +910,44 @@ Server::~Server()
                infostream<<"Server: Saving environment metadata"<<std::endl;
                m_env->saveMeta(m_path_world);
        }
-               
+
        /*
                Stop threads
        */
        stop();
-       
+       delete m_thread;
+
+       //shutdown all emerge threads first!
+       delete m_emerge;
+
        /*
                Delete clients
        */
        {
                JMutexAutoLock clientslock(m_con_mutex);
 
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd() == false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                       i = m_clients.begin();
+                       i != m_clients.end(); ++i)
                {
+
                        // Delete client
-                       delete i.getNode()->getValue();
+                       delete i->second;
                }
        }
-       
+
        // Delete things in the reverse order of creation
        delete m_env;
        delete m_rollback;
+       delete m_banmanager;
        delete m_event;
        delete m_itemdef;
        delete m_nodedef;
        delete m_craftdef;
-       
+
        // Deinitialize scripting
        infostream<<"Server: Deinitializing scripting"<<std::endl;
-       script_deinit(m_lua);
+       delete m_script;
 
        // Delete detached inventories
        {
@@ -1217,16 +965,15 @@ void Server::start(unsigned short port)
        infostream<<"Starting server on port "<<port<<"..."<<std::endl;
 
        // Stop thread if already running
-       m_thread.stop();
-       
+       m_thread->Stop();
+
        // Initialize connection
        m_con.SetTimeoutMs(30);
        m_con.Serve(port);
 
        // Start thread
-       m_thread.setRun(true);
-       m_thread.Start();
-       
+       m_thread->Start();
+
        // ASCII art for the win!
        actionstream
        <<"        .__               __                   __   "<<std::endl
@@ -1243,15 +990,15 @@ void Server::start(unsigned short port)
 void Server::stop()
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        infostream<<"Server: Stopping and waiting threads"<<std::endl;
 
        // Stop threads (set run=false first so both start stopping)
-       m_thread.setRun(false);
-       m_emergethread.setRun(false);
-       m_thread.stop();
-       m_emergethread.stop();
-       
+       m_thread->Stop();
+       //m_emergethread.setRun(false);
+       m_thread->Wait();
+       //m_emergethread.stop();
+
        infostream<<"Server: Threads stopped"<<std::endl;
 }
 
@@ -1272,31 +1019,31 @@ void Server::step(float dtime)
        }
 }
 
-void Server::AsyncRunStep()
+void Server::AsyncRunStep(bool initial_step)
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        g_profiler->add("Server::AsyncRunStep (num)", 1);
-       
+
        float dtime;
        {
                JMutexAutoLock lock1(m_step_dtime_mutex);
                dtime = m_step_dtime;
        }
-       
+
        {
                // Send blocks to clients
                SendBlocks(dtime);
        }
-       
-       if(dtime < 0.001)
+
+       if((dtime < 0.001) && (initial_step == false))
                return;
-       
+
        g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
 
        //infostream<<"Server steps "<<dtime<<std::endl;
        //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
-       
+
        {
                JMutexAutoLock lock1(m_step_dtime_mutex);
                m_step_dtime -= dtime;
@@ -1308,14 +1055,14 @@ 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
@@ -1342,27 +1089,37 @@ void Server::AsyncRunStep()
                        //JMutexAutoLock envlock(m_env_mutex);
                        JMutexAutoLock conlock(m_con_mutex);
 
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++)
+                       u16 time = m_env->getTimeOfDay();
+                       float time_speed = g_settings->getFloat("time_speed");
+
+                       for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin();
+                               i != m_clients.end(); ++i)
                        {
-                               RemoteClient *client = i.getNode()->getValue();
-                               SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-                                               m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
-                               // Send as reliable
-                               m_con.Send(client->peer_id, 0, data, true);
+                               RemoteClient *client = i->second;
+                               SendTimeOfDay(client->peer_id, time, time_speed);
                        }
                }
        }
 
        {
                JMutexAutoLock lock(m_env_mutex);
+               // Figure out and report maximum lag to environment
+               float max_lag = m_env->getMaxLagEstimate();
+               max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
+               if(dtime > max_lag){
+                       if(dtime > 0.1 && dtime > max_lag * 2.0)
+                               infostream<<"Server: Maximum lag peaked to "<<dtime
+                                               <<" s"<<std::endl;
+                       max_lag = dtime;
+               }
+               m_env->reportMaxLagEstimate(max_lag);
                // Step environment
                ScopeProfiler sp(g_profiler, "SEnv step");
                ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
                m_env->step(dtime);
        }
-               
+
        const float map_timer_and_unload_dtime = 2.92;
        if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
        {
@@ -1372,7 +1129,7 @@ void Server::AsyncRunStep()
                m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
                                g_settings->getFloat("server_unload_unused_data_timeout"));
        }
-       
+
        /*
                Do background stuff
        */
@@ -1386,11 +1143,11 @@ void Server::AsyncRunStep()
 
                ScopeProfiler sp(g_profiler, "Server: handle players");
 
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd() == false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                       i = m_clients.begin();
+                       i != m_clients.end(); ++i)
                {
-                       RemoteClient *client = i.getNode()->getValue();
+                       RemoteClient *client = i->second;
                        PlayerSAO *playersao = getPlayerSAO(client->peer_id);
                        if(playersao == NULL)
                                continue;
@@ -1406,6 +1163,13 @@ void Server::AsyncRunStep()
                                        SendPlayerHP(client->peer_id);
                        }
 
+                       /*
+                               Send player breath if changed
+                       */
+                       if(playersao->m_breath_not_sent){
+                               SendPlayerBreath(client->peer_id);
+                       }
+
                        /*
                                Send player inventories if necessary
                        */
@@ -1419,27 +1183,27 @@ void Server::AsyncRunStep()
                        }
                }
        }
-       
+
        /* Transform liquids */
        m_liquid_transform_timer += dtime;
-       if(m_liquid_transform_timer >= 1.00)
+       if(m_liquid_transform_timer >= m_liquid_transform_every)
        {
-               m_liquid_transform_timer -= 1.00;
-               
+               m_liquid_transform_timer -= m_liquid_transform_every;
+
                JMutexAutoLock lock(m_env_mutex);
 
                ScopeProfiler sp(g_profiler, "Server: liquid transform");
 
-               core::map<v3s16, MapBlock*> modified_blocks;
+               std::map<v3s16, MapBlock*> modified_blocks;
                m_env->getMap().transformLiquids(modified_blocks);
-#if 0          
+#if 0
                /*
                        Update lighting
                */
                core::map<v3s16, MapBlock*> lighting_modified_blocks;
                ServerMap &map = ((ServerMap&)m_env->getMap());
                map.updateLighting(modified_blocks, lighting_modified_blocks);
-               
+
                // Add blocks modified by lighting to modified_blocks
                for(core::map<v3s16, MapBlock*>::Iterator
                                i = lighting_modified_blocks.getIterator();
@@ -1452,15 +1216,15 @@ void Server::AsyncRunStep()
                /*
                        Set the modified blocks unsent for all the clients
                */
-               
+
                JMutexAutoLock lock2(m_con_mutex);
 
-               for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin();
+                               i != m_clients.end(); ++i)
                {
-                       RemoteClient *client = i.getNode()->getValue();
-                       
+                       RemoteClient *client = i->second;
+
                        if(modified_blocks.size() > 0)
                        {
                                // Remove block from sent history
@@ -1478,24 +1242,40 @@ void Server::AsyncRunStep()
                        counter = 0.0;
 
                        JMutexAutoLock lock2(m_con_mutex);
-                       
+                       m_clients_names.clear();
                        if(m_clients.size() != 0)
                                infostream<<"Players:"<<std::endl;
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++)
+                       for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin();
+                               i != m_clients.end(); ++i)
                        {
                                //u16 peer_id = i.getNode()->getKey();
-                               RemoteClient *client = i.getNode()->getValue();
+                               RemoteClient *client = i->second;
                                Player *player = m_env->getPlayer(client->peer_id);
                                if(player==NULL)
                                        continue;
                                infostream<<"* "<<player->getName()<<"\t";
                                client->PrintInfo(infostream);
+                               m_clients_names.push_back(player->getName());
                        }
                }
        }
 
+
+       m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
+#if USE_CURL
+       // send masterserver announce
+       {
+               float &counter = m_masterserver_timer;
+               if(!isSingleplayer() && (!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
+               {
+                       ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_names, m_uptime.get(), m_env->getGameTime(), m_lag, m_gamespec.id, m_mods);
+                       counter = 0.01;
+               }
+               counter += dtime;
+       }
+#endif
+
        //if(g_settings->getBool("enable_experimental"))
        {
 
@@ -1513,11 +1293,11 @@ void Server::AsyncRunStep()
                s16 radius = g_settings->getS16("active_object_send_range_blocks");
                radius *= MAP_BLOCKSIZE;
 
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd() == false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                       i = m_clients.begin();
+                       i != m_clients.end(); ++i)
                {
-                       RemoteClient *client = i.getNode()->getValue();
+                       RemoteClient *client = i->second;
 
                        // If definitions and textures have not been sent, don't
                        // send objects either
@@ -1535,41 +1315,41 @@ void Server::AsyncRunStep()
                        }
                        v3s16 pos = floatToInt(player->getPosition(), BS);
 
-                       core::map<u16, bool> removed_objects;
-                       core::map<u16, bool> added_objects;
+                       std::set<u16> removed_objects;
+                       std::set<u16> added_objects;
                        m_env->getRemovedActiveObjects(pos, radius,
                                        client->m_known_objects, removed_objects);
                        m_env->getAddedActiveObjects(pos, radius,
                                        client->m_known_objects, added_objects);
-                       
+
                        // Ignore if nothing happened
                        if(removed_objects.size() == 0 && added_objects.size() == 0)
                        {
                                //infostream<<"active objects: none changed"<<std::endl;
                                continue;
                        }
-                       
+
                        std::string data_buffer;
 
                        char buf[4];
-                       
+
                        // Handle removed objects
                        writeU16((u8*)buf, removed_objects.size());
                        data_buffer.append(buf, 2);
-                       for(core::map<u16, bool>::Iterator
-                                       i = removed_objects.getIterator();
-                                       i.atEnd()==false; i++)
+                       for(std::set<u16>::iterator
+                                       i = removed_objects.begin();
+                                       i != removed_objects.end(); ++i)
                        {
                                // Get object
-                               u16 id = i.getNode()->getKey();
+                               u16 id = *i;
                                ServerActiveObject* obj = m_env->getActiveObject(id);
 
                                // Add to data buffer for sending
-                               writeU16((u8*)buf, i.getNode()->getKey());
+                               writeU16((u8*)buf, id);
                                data_buffer.append(buf, 2);
-                               
+
                                // Remove from known objects
-                               client->m_known_objects.remove(i.getNode()->getKey());
+                               client->m_known_objects.erase(id);
 
                                if(obj && obj->m_known_by_count > 0)
                                        obj->m_known_by_count--;
@@ -1578,14 +1358,14 @@ void Server::AsyncRunStep()
                        // Handle added objects
                        writeU16((u8*)buf, added_objects.size());
                        data_buffer.append(buf, 2);
-                       for(core::map<u16, bool>::Iterator
-                                       i = added_objects.getIterator();
-                                       i.atEnd()==false; i++)
+                       for(std::set<u16>::iterator
+                                       i = added_objects.begin();
+                                       i != added_objects.end(); ++i)
                        {
                                // Get object
-                               u16 id = i.getNode()->getKey();
+                               u16 id = *i;
                                ServerActiveObject* obj = m_env->getActiveObject(id);
-                               
+
                                // Get object type
                                u8 type = ACTIVEOBJECT_TYPE_INVALID;
                                if(obj == NULL)
@@ -1599,7 +1379,7 @@ void Server::AsyncRunStep()
                                data_buffer.append(buf, 2);
                                writeU8((u8*)buf, type);
                                data_buffer.append(buf, 1);
-                               
+
                                if(obj)
                                        data_buffer.append(serializeLongString(
                                                        obj->getClientInitializationData(client->net_proto_version)));
@@ -1607,7 +1387,7 @@ void Server::AsyncRunStep()
                                        data_buffer.append(serializeLongString(""));
 
                                // Add to known objects
-                               client->m_known_objects.insert(i.getNode()->getKey(), false);
+                               client->m_known_objects.insert(id);
 
                                if(obj)
                                        obj->m_known_by_count++;
@@ -1649,7 +1429,7 @@ void Server::AsyncRunStep()
                                all_known_objects[id] = true;
                        }
                }
-               
+
                m_env->setKnownActiveObjects(whatever);
 #endif
 
@@ -1666,7 +1446,7 @@ void Server::AsyncRunStep()
 
                // Key = object id
                // Value = data sent by object
-               core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
+               std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
 
                // Get active object messages from environment
                for(;;)
@@ -1674,44 +1454,44 @@ void Server::AsyncRunStep()
                        ActiveObjectMessage aom = m_env->getActiveObjectMessage();
                        if(aom.id == 0)
                                break;
-                       
-                       core::list<ActiveObjectMessage>* message_list = NULL;
-                       core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
+
+                       std::list<ActiveObjectMessage>* message_list = NULL;
+                       std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
                        n = buffered_messages.find(aom.id);
-                       if(n == NULL)
+                       if(n == buffered_messages.end())
                        {
-                               message_list = new core::list<ActiveObjectMessage>;
-                               buffered_messages.insert(aom.id, message_list);
+                               message_list = new std::list<ActiveObjectMessage>;
+                               buffered_messages[aom.id] = message_list;
                        }
                        else
                        {
-                               message_list = n->getValue();
+                               message_list = n->second;
                        }
                        message_list->push_back(aom);
                }
-               
+
                // Route data to every client
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd()==false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                       i = m_clients.begin();
+                       i != m_clients.end(); ++i)
                {
-                       RemoteClient *client = i.getNode()->getValue();
+                       RemoteClient *client = i->second;
                        std::string reliable_data;
                        std::string unreliable_data;
                        // Go through all objects in message buffer
-                       for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
-                                       j = buffered_messages.getIterator();
-                                       j.atEnd()==false; j++)
+                       for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
+                                       j = buffered_messages.begin();
+                                       j != buffered_messages.end(); ++j)
                        {
                                // If object is not known by client, skip it
-                               u16 id = j.getNode()->getKey();
-                               if(client->m_known_objects.find(id) == NULL)
+                               u16 id = j->first;
+                               if(client->m_known_objects.find(id) == client->m_known_objects.end())
                                        continue;
                                // Get message list of object
-                               core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
+                               std::list<ActiveObjectMessage>* list = j->second;
                                // Go through every message
-                               for(core::list<ActiveObjectMessage>::Iterator
-                                               k = list->begin(); k != list->end(); k++)
+                               for(std::list<ActiveObjectMessage>::iterator
+                                               k = list->begin(); k != list->end(); ++k)
                                {
                                        // Compose the full new data with header
                                        ActiveObjectMessage aom = *k;
@@ -1749,7 +1529,7 @@ void Server::AsyncRunStep()
                                memcpy((char*)&reply[2], unreliable_data.c_str(),
                                                unreliable_data.size());
                                // Send as unreliable
-                               m_con.Send(client->peer_id, 0, reply, false);
+                               m_con.Send(client->peer_id, 1, reply, false);
                        }
 
                        /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
@@ -1762,11 +1542,11 @@ void Server::AsyncRunStep()
                }
 
                // Clear buffered_messages
-               for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
-                               i = buffered_messages.getIterator();
-                               i.atEnd()==false; i++)
+               for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
+                               i = buffered_messages.begin();
+                               i != buffered_messages.end(); ++i)
                {
-                       delete i.getNode()->getValue();
+                       delete i->second;
                }
        }
 
@@ -1796,22 +1576,22 @@ void Server::AsyncRunStep()
                while(m_unsent_map_edit_queue.size() != 0)
                {
                        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;
+                       std::list<u16> far_players;
 
-                       if(event->type == MEET_ADDNODE)
+                       if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
                        {
                                //infostream<<"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);
+                                                       &far_players, 5, event->type == MEET_ADDNODE);
                                else
                                        sendAddNode(event->p, event->n, event->already_known_by_peer,
-                                                       &far_players, 30);
+                                                       &far_players, 30, event->type == MEET_ADDNODE);
                        }
                        else if(event->type == MEET_REMOVENODE)
                        {
@@ -1834,12 +1614,11 @@ void Server::AsyncRunStep()
                        {
                                infostream<<"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++)
+                               for(std::set<v3s16>::iterator
+                                               i = event->modified_blocks.begin();
+                                               i != event->modified_blocks.end(); ++i)
                                {
-                                       v3s16 p = i.getNode()->getKey();
-                                       setBlockNotSent(p);
+                                       setBlockNotSent(*i);
                                }
                        }
                        else
@@ -1848,26 +1627,25 @@ void Server::AsyncRunStep()
                                infostream<<"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++)
+                               std::map<v3s16, MapBlock*> modified_blocks2;
+                               for(std::set<v3s16>::iterator
+                                               i = event->modified_blocks.begin();
+                                               i != event->modified_blocks.end(); ++i)
                                {
-                                       v3s16 p = i.getNode()->getKey();
-                                       modified_blocks2.insert(p,
-                                                       m_env->getMap().getBlockNoCreateNoEx(p));
+                                       modified_blocks2[*i] =
+                                                       m_env->getMap().getBlockNoCreateNoEx(*i);
                                }
                                // Set blocks not sent
-                               for(core::list<u16>::Iterator
+                               for(std::list<u16>::iterator
                                                i = far_players.begin();
-                                               i != far_players.end(); i++)
+                                               i != far_players.end(); ++i)
                                {
                                        u16 peer_id = *i;
                                        RemoteClient *client = getClient(peer_id);
@@ -1892,7 +1670,7 @@ void Server::AsyncRunStep()
                        verbosestream<<"Server: MapEditEvents:"<<std::endl;
                        prof.print(verbosestream);
                }
-               
+
        }
 
        /*
@@ -1905,8 +1683,8 @@ void Server::AsyncRunStep()
                if(counter >= 2.0)
                {
                        counter = 0.0;
-                       
-                       m_emergethread.trigger();
+
+                       m_emerge->startAllThreads();
 
                        // Update m_enable_rollback_recording here too
                        m_enable_rollback_recording =
@@ -1926,15 +1704,15 @@ void Server::AsyncRunStep()
                        ScopeProfiler sp(g_profiler, "Server: saving stuff");
 
                        //Ban stuff
-                       if(m_banmanager.isModified())
-                               m_banmanager.save();
-                       
+                       if(m_banmanager->isModified())
+                               m_banmanager->save();
+
                        // Save changed parts of map
                        m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
 
                        // Save players
                        m_env->serializePlayers(m_path_world);
-                       
+
                        // Save environment metadata
                        m_env->saveMeta(m_path_world);
                }
@@ -1968,7 +1746,7 @@ void Server::Receive()
        catch(con::PeerNotFoundException &e)
        {
                //NOTE: This is not needed anymore
-               
+
                // The peer has been disconnected.
                // Find the associated player and remove it.
 
@@ -1988,22 +1766,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        // Environment is locked first.
        JMutexAutoLock envlock(m_env_mutex);
        JMutexAutoLock conlock(m_con_mutex);
-       
+
        ScopeProfiler sp(g_profiler, "Server::ProcessData");
-       
+
+       std::string addr_s;
        try{
                Address address = m_con.GetPeerAddress(peer_id);
-               std::string addr_s = address.serializeString();
+               addr_s = address.serializeString();
 
                // drop player if is ip is banned
-               if(m_banmanager.isIpBanned(addr_s)){
+               if(m_banmanager->isIpBanned(addr_s)){
+                       std::string ban_name = m_banmanager->getBanName(addr_s);
                        infostream<<"Server: A banned client tried to connect from "
                                        <<addr_s<<"; banned name was "
-                                       <<m_banmanager.getBanName(addr_s)<<std::endl;
+                                       <<ban_name<<std::endl;
                        // This actually doesn't seem to transfer to the client
-                       SendAccessDenied(m_con, peer_id,
-                                       L"Your ip is banned. Banned name was "
-                                       +narrow_to_wide(m_banmanager.getBanName(addr_s)));
+                       DenyAccess(peer_id, L"Your ip is banned. Banned name was "
+                                       +narrow_to_wide(ban_name));
                        m_con.DeletePeer(peer_id);
                        return;
                }
@@ -2014,8 +1793,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                <<peer_id<<" not found"<<std::endl;
                return;
        }
-       
-       std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
 
        u8 peer_ser_ver = getClient(peer_id)->serialization_version;
 
@@ -2026,48 +1803,63 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                return;
 
        ToServerCommand command = (ToServerCommand)readU16(&data[0]);
-       
+
        if(command == TOSERVER_INIT)
        {
                // [0] u16 TOSERVER_INIT
-               // [2] u8 SER_FMT_VER_HIGHEST
+               // [2] u8 SER_FMT_VER_HIGHEST_READ
                // [3] u8[20] player_name
                // [23] u8[28] password <--- can be sent without this, from old versions
 
                if(datasize < 2+1+PLAYERNAME_SIZE)
                        return;
 
-               verbosestream<<"Server: Got TOSERVER_INIT from "
-                               <<peer_id<<std::endl;
+               // If net_proto_version is set, this client has already been handled
+               if(getClient(peer_id)->net_proto_version != 0){
+                       verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
+                                       <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
+                       return;
+               }
+
+               verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
+                               <<peer_id<<")"<<std::endl;
+
+               // Do not allow multiple players in simple singleplayer mode.
+               // This isn't a perfect way to do it, but will suffice for now.
+               if(m_simple_singleplayer_mode && m_clients.size() > 1){
+                       infostream<<"Server: Not allowing another client ("<<addr_s
+                                       <<") to connect in simple singleplayer mode"<<std::endl;
+                       DenyAccess(peer_id, L"Running in simple singleplayer mode.");
+                       return;
+               }
 
                // First byte after command is maximum supported
                // serialization version
                u8 client_max = data[2];
-               u8 our_max = SER_FMT_VER_HIGHEST;
+               u8 our_max = SER_FMT_VER_HIGHEST_READ;
                // Use the highest version supported by both
-               u8 deployed = core::min_(client_max, our_max);
+               u8 deployed = std::min(client_max, our_max);
                // If it's lower than the lowest supported, give up.
                if(deployed < SER_FMT_VER_LOWEST)
                        deployed = SER_FMT_VER_INVALID;
 
                //peer->serialization_version = deployed;
                getClient(peer_id)->pending_serialization_version = deployed;
-               
+
                if(deployed == SER_FMT_VER_INVALID)
                {
                        actionstream<<"Server: A mismatched client tried to connect from "
                                        <<addr_s<<std::endl;
-                       infostream<<"Server: Cannot negotiate "
-                                       "serialization version with peer "
-                                       <<peer_id<<std::endl;
-                       SendAccessDenied(m_con, peer_id, std::wstring(
+                       infostream<<"Server: Cannot negotiate serialization version with "
+                                       <<addr_s<<std::endl;
+                       DenyAccess(peer_id, std::wstring(
                                        L"Your client's version is not supported.\n"
                                        L"Server version is ")
-                                       + narrow_to_wide(VERSION_STRING) + L"."
+                                       + narrow_to_wide(minetest_version_simple) + L"."
                        );
                        return;
                }
-               
+
                /*
                        Read and check network protocol version
                */
@@ -2097,7 +1889,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                net_proto_version = max_net_proto_version;
                }
 
-               verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
+               verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
                                <<min_net_proto_version<<", max: "<<max_net_proto_version
                                <<", chosen: "<<net_proto_version<<std::endl;
 
@@ -2106,12 +1898,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
                                net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
                {
-                       actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
-                                       <<std::endl;
-                       SendAccessDenied(m_con, peer_id, std::wstring(
+                       actionstream<<"Server: A mismatched client tried to connect from "
+                                       <<addr_s<<std::endl;
+                       DenyAccess(peer_id, std::wstring(
                                        L"Your client's version is not supported.\n"
                                        L"Server version is ")
-                                       + narrow_to_wide(VERSION_STRING) + L",\n"
+                                       + narrow_to_wide(minetest_version_simple) + L",\n"
                                        + L"server's PROTOCOL_VERSION is "
                                        + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
                                        + L"..."
@@ -2123,17 +1915,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        );
                        return;
                }
-               
+
                if(g_settings->getBool("strict_protocol_version_checking"))
                {
                        if(net_proto_version != LATEST_PROTOCOL_VERSION)
                        {
                                actionstream<<"Server: A mismatched (strict) client tried to "
                                                <<"connect from "<<addr_s<<std::endl;
-                               SendAccessDenied(m_con, peer_id, std::wstring(
+                               DenyAccess(peer_id, std::wstring(
                                                L"Your client's version is not supported.\n"
                                                L"Server version is ")
-                                               + narrow_to_wide(VERSION_STRING) + L",\n"
+                                               + narrow_to_wide(minetest_version_simple) + L",\n"
                                                + L"server's PROTOCOL_VERSION (strict) is "
                                                + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
                                                + L", client's PROTOCOL_VERSION is "
@@ -2148,7 +1940,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                /*
                        Set up player
                */
-               
+
                // Get player name
                char playername[PLAYERNAME_SIZE];
                for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
@@ -2156,13 +1948,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        playername[i] = data[3+i];
                }
                playername[PLAYERNAME_SIZE-1] = 0;
-               
+
                if(playername[0]=='\0')
                {
                        actionstream<<"Server: Player with an empty name "
                                        <<"tried to connect from "<<addr_s<<std::endl;
-                       SendAccessDenied(m_con, peer_id,
-                                       L"Empty name");
+                       DenyAccess(peer_id, L"Empty name");
                        return;
                }
 
@@ -2170,13 +1961,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                {
                        actionstream<<"Server: Player with an invalid name "
                                        <<"tried to connect from "<<addr_s<<std::endl;
-                       SendAccessDenied(m_con, peer_id,
-                                       L"Name contains unallowed characters");
+                       DenyAccess(peer_id, L"Name contains unallowed characters");
+                       return;
+               }
+
+               if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
+               {
+                       actionstream<<"Server: Player with the name \"singleplayer\" "
+                                       <<"tried to connect from "<<addr_s<<std::endl;
+                       DenyAccess(peer_id, L"Name is not allowed");
                        return;
                }
 
+               {
+                       std::string reason;
+                       if(m_script->on_prejoinplayer(playername, addr_s, reason))
+                       {
+                               actionstream<<"Server: Player with the name \""<<playername<<"\" "
+                                               <<"tried to connect from "<<addr_s<<" "
+                                               <<"but it was disallowed for the following reason: "
+                                               <<reason<<std::endl;
+                               DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
+                               return;
+                       }
+               }
+
                infostream<<"Server: New connection: \""<<playername<<"\" from "
-                               <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
+                               <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
 
                // Get password
                char given_password[PASSWORD_SIZE];
@@ -2195,21 +2006,39 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                }
 
                if(!base64_is_valid(given_password)){
-                       infostream<<"Server: "<<playername
+                       actionstream<<"Server: "<<playername
                                        <<" supplied invalid password hash"<<std::endl;
-                       SendAccessDenied(m_con, peer_id, L"Invalid password hash");
+                       DenyAccess(peer_id, L"Invalid password hash");
                        return;
                }
-               
+
+               // Enforce user limit.
+               // Don't enforce for users that have some admin right
+               if(m_clients.size() >= g_settings->getU16("max_users") &&
+                               !checkPriv(playername, "server") &&
+                               !checkPriv(playername, "ban") &&
+                               !checkPriv(playername, "privs") &&
+                               !checkPriv(playername, "password") &&
+                               playername != g_settings->get("name"))
+               {
+                       actionstream<<"Server: "<<playername<<" tried to join, but there"
+                                       <<" are already max_users="
+                                       <<g_settings->getU16("max_users")<<" players."<<std::endl;
+                       DenyAccess(peer_id, L"Too many users.");
+                       return;
+               }
+
                std::string checkpwd; // Password hash to check against
-               bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
-               
+               bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
                // If no authentication info exists for user, create it
                if(!has_auth){
                        if(!isSingleplayer() &&
                                        g_settings->getBool("disallow_empty_password") &&
                                        std::string(given_password) == ""){
-                               SendAccessDenied(m_con, peer_id, L"Empty passwords are "
+                               actionstream<<"Server: "<<playername
+                                               <<" supplied empty password"<<std::endl;
+                               DenyAccess(peer_id, L"Empty passwords are "
                                                L"disallowed. Set a password and try again.");
                                return;
                        }
@@ -2222,47 +2051,22 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        if (raw_default_password.length() == 0)
                                initial_password = given_password;
 
-                       scriptapi_create_auth(m_lua, playername, initial_password);
+                       m_script->createAuth(playername, initial_password);
                }
-               
-               has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
+
+               has_auth = m_script->getAuth(playername, &checkpwd, NULL);
 
                if(!has_auth){
-                       SendAccessDenied(m_con, peer_id, L"Not allowed to login");
+                       actionstream<<"Server: "<<playername<<" cannot be authenticated"
+                                       <<" (auth handler does not work?)"<<std::endl;
+                       DenyAccess(peer_id, L"Not allowed to login");
                        return;
                }
 
                if(given_password != checkpwd){
-                       infostream<<"Server: peer_id="<<peer_id
-                                       <<": supplied invalid password for "
-                                       <<playername<<std::endl;
-                       SendAccessDenied(m_con, peer_id, L"Invalid password");
-                       return;
-               }
-
-               // Do not allow multiple players in simple singleplayer mode.
-               // This isn't a perfect way to do it, but will suffice for now.
-               if(m_simple_singleplayer_mode && m_clients.size() > 1){
-                       infostream<<"Server: Not allowing another client to connect in"
-                                       <<" simple singleplayer mode"<<std::endl;
-                       SendAccessDenied(m_con, peer_id,
-                                       L"Running in simple singleplayer mode.");
-                       return;
-               }
-               
-               // Enforce user limit.
-               // Don't enforce for users that have some admin right
-               if(m_clients.size() >= g_settings->getU16("max_users") &&
-                               !checkPriv(playername, "server") &&
-                               !checkPriv(playername, "ban") &&
-                               !checkPriv(playername, "privs") &&
-                               !checkPriv(playername, "password") &&
-                               playername != g_settings->get("name"))
-               {
-                       actionstream<<"Server: "<<playername<<" tried to join, but there"
-                                       <<" are already max_users="
-                                       <<g_settings->getU16("max_users")<<" players."<<std::endl;
-                       SendAccessDenied(m_con, peer_id, L"Too many users.");
+                       actionstream<<"Server: "<<playername<<" supplied wrong password"
+                                       <<std::endl;
+                       DenyAccess(peer_id, L"Wrong password");
                        return;
                }
 
@@ -2272,8 +2076,19 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // If failed, cancel
                if(playersao == NULL)
                {
-                       errorstream<<"Server: peer_id="<<peer_id
-                                       <<": failed to emerge player"<<std::endl;
+                       RemotePlayer *player =
+                                       static_cast<RemotePlayer*>(m_env->getPlayer(playername));
+                       if(player && player->peer_id != 0){
+                               errorstream<<"Server: "<<playername<<": Failed to emerge player"
+                                               <<" (player allocated to an another client)"<<std::endl;
+                               DenyAccess(peer_id, L"Another client is connected with this "
+                                               L"name. If your client closed unexpectedly, try again in "
+                                               L"a minute.");
+                       } else {
+                               errorstream<<"Server: "<<playername<<": Failed to emerge player"
+                                               <<std::endl;
+                               DenyAccess(peer_id, L"Could not allocate player.");
+                       }
                        return;
                }
 
@@ -2287,7 +2102,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
                        writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
                        writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
-                       
+
                        // Send as reliable
                        m_con.Send(peer_id, 0, reply, true);
                }
@@ -2323,18 +2138,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                infostream<<"Server: Sending content to "
                                <<getPlayerName(peer_id)<<std::endl;
 
+               // Send player movement settings
+               SendMovement(m_con, peer_id);
+
                // Send item definitions
-               SendItemDef(m_con, peer_id, m_itemdef);
-               
+               SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
+
                // Send node definitions
                SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
-               
+
                // Send media announcement
                sendMediaAnnouncement(peer_id);
-               
+
                // Send privileges
                SendPlayerPrivileges(peer_id);
-               
+
                // Send inventory formspec
                SendPlayerInventoryFormspec(peer_id);
 
@@ -2345,34 +2163,37 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Send HP
                if(g_settings->getBool("enable_damage"))
                        SendPlayerHP(peer_id);
-               
+
+               // Send Breath
+               SendPlayerBreath(peer_id);
+
                // Send detached inventories
                sendDetachedInventories(peer_id);
-               
+
                // Show death screen if necessary
                if(player->hp == 0)
                        SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
 
                // Send time of day
                {
-                       SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-                                       m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
-                       m_con.Send(peer_id, 0, data, true);
+                       u16 time = m_env->getTimeOfDay();
+                       float time_speed = g_settings->getFloat("time_speed");
+                       SendTimeOfDay(peer_id, time, time_speed);
                }
-               
+
                // Note things in chat if not in simple singleplayer mode
                if(!m_simple_singleplayer_mode)
                {
                        // Send information about server to player in chat
                        SendChatMessage(peer_id, getStatusString());
-                       
+
                        // Send information about joining in chat
                        {
                                std::wstring name = L"unknown";
                                Player *player = m_env->getPlayer(peer_id);
                                if(player != NULL)
                                        name = narrow_to_wide(player->getName());
-                               
+
                                std::wstring message;
                                message += L"*** ";
                                message += name;
@@ -2380,7 +2201,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                BroadcastChatMessage(message);
                        }
                }
-               
+
                // Warnings about protocol version can be issued here
                if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
                {
@@ -2393,12 +2214,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                */
                {
                        std::ostringstream os(std::ios_base::binary);
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++)
+                       for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin();
+                               i != m_clients.end(); ++i)
                        {
-                               RemoteClient *client = i.getNode()->getValue();
-                               assert(client->peer_id == i.getNode()->getKey());
+                               RemoteClient *client = i->second;
+                               assert(client->peer_id == i->first);
                                if(client->serialization_version == SER_FMT_VER_INVALID)
                                        continue;
                                // Get player
@@ -2409,7 +2230,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                os<<player->getName()<<" ";
                        }
 
-                       actionstream<<player->getName()<<" joins game. List of players: "
+                       actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. List of players: "
                                        <<os.str()<<std::endl;
                }
 
@@ -2423,7 +2244,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                " Skipping incoming command="<<command<<std::endl;
                return;
        }
-       
+
        Player *player = m_env->getPlayer(peer_id);
        if(player == NULL){
                infostream<<"Server::ProcessData(): Cancelling: "
@@ -2444,7 +2265,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        {
                if(datasize < 2+12+12+4+4)
                        return;
-       
+
                u32 start = 0;
                v3s32 ps = readV3S32(&data[start+2]);
                v3s32 ss = readV3S32(&data[start+2+12]);
@@ -2472,7 +2293,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                player->control.sneak = (bool)(keyPressed&64);
                player->control.LMB = (bool)(keyPressed&128);
                player->control.RMB = (bool)(keyPressed&256);
-               
+
+               bool cheated = playersao->checkMovementCheat();
+               if(cheated){
+                       // Call callbacks
+                       m_script->on_cheat(playersao, "moved_too_fast");
+               }
+
                /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
                                <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
                                <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
@@ -2481,7 +2308,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        {
                if(datasize < 2+1)
                        return;
-               
+
                /*
                        [0] u16 command
                        [2] u8 count
@@ -2507,7 +2334,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        {
                if(datasize < 2+1)
                        return;
-               
+
                /*
                        [0] u16 command
                        [2] u8 count
@@ -2657,6 +2484,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                        setInventoryModified(da->from_inv);
 
+                       /*
+                               Disable dropping items out of craftpreview
+                       */
+                       if(da->from_list == "craftpreview")
+                       {
+                               infostream<<"Ignoring IDropAction from "
+                                               <<(da->from_inv.dump())<<":"<<da->from_list
+                                               <<" because src is "<<da->from_list<<std::endl;
+                               delete a;
+                               return;
+                       }
+
                        // Disallow dropping items if not allowed to interact
                        if(!checkPriv(player->getName(), "interact"))
                        {
@@ -2688,7 +2527,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                return;
                        }
                }
-               
+
                // Do the action
                a->apply(this, playersao, this);
                // Eat the action
@@ -2704,11 +2543,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                u8 buf[6];
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
-               
+
                // Read stuff
                is.read((char*)buf, 2);
                u16 len = readU16(buf);
-               
+
                std::wstring message;
                for(u16 i=0; i<len; i++)
                {
@@ -2722,21 +2561,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                // Get player name of this client
                std::wstring name = narrow_to_wide(player->getName());
-               
+
                // Run script hook
-               bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
+               bool ate = m_script->on_chat_message(player->getName(),
                                wide_to_narrow(message));
                // If script ate the message, don't proceed
                if(ate)
                        return;
-               
+
                // Line to send to players
                std::wstring line;
                // Whether to send to the player that sent the line
                bool send_to_sender = false;
                // Whether to send to other players
                bool send_to_others = false;
-               
+
                // Commands are implemented in Lua, so only catch invalid
                // commands that were not "eaten" and send an error back
                if(message[0] == L'/')
@@ -2761,7 +2600,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                send_to_sender = true;
                        }
                }
-               
+
                if(line != L"")
                {
                        if(send_to_others)
@@ -2770,13 +2609,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        /*
                                Send the message to clients
                        */
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++)
+                       for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin();
+                               i != m_clients.end(); ++i)
                        {
                                // Get client and check that it is valid
-                               RemoteClient *client = i.getNode()->getValue();
-                               assert(client->peer_id == i.getNode()->getKey());
+                               RemoteClient *client = i->second;
+                               assert(client->peer_id == i->first);
                                if(client->serialization_version == SER_FMT_VER_INVALID)
                                        continue;
 
@@ -2812,6 +2651,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                SendPlayerHP(peer_id);
                }
        }
+       else if(command == TOSERVER_BREATH)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+               u16 breath = readU16(is);
+               playersao->setBreath(breath);
+       }
        else if(command == TOSERVER_PASSWORD)
        {
                /*
@@ -2856,7 +2702,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                std::string playername = player->getName();
 
                std::string checkpwd;
-               scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
+               m_script->getAuth(playername, &checkpwd, NULL);
 
                if(oldpwd != checkpwd)
                {
@@ -2866,7 +2712,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
                }
 
-               bool success = scriptapi_set_password(m_lua, playername, newpwd);
+               bool success = m_script->setPassword(playername, newpwd);
                if(success){
                        actionstream<<player->getName()<<" changes password"<<std::endl;
                        SendChatMessage(peer_id, L"Password change successful.");
@@ -2888,9 +2734,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        {
                if(player->hp != 0 || !g_settings->getBool("enable_damage"))
                        return;
-               
+
                RespawnPlayer(peer_id);
-               
+
                actionstream<<player->getName()<<" respawns at "
                                <<PP(player->getPosition()/BS)<<std::endl;
 
@@ -2900,8 +2746,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        else if(command == TOSERVER_REQUEST_MEDIA) {
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
-               
-               core::list<MediaRequest> tosend;
+
+               std::list<std::string> tosend;
                u16 numfiles = readU16(is);
 
                infostream<<"Sending "<<numfiles<<" files to "
@@ -2910,7 +2756,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                for(int i = 0; i < numfiles; i++) {
                        std::string name = deSerializeString(is);
-                       tosend.push_back(MediaRequest(name));
+                       tosend.push_back(name);
                        verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
                                        <<name<<std::endl;
                }
@@ -3012,6 +2858,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                RemoteClient *client = getClient(peer_id);
                                v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
                                client->SetBlockNotSent(blockpos);
+                               // Call callbacks
+                               m_script->on_cheat(playersao, "interacted_too_far");
                                // Do nothing else
                                return;
                        }
@@ -3067,11 +2915,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        infostream<<"Server: Not punching: Node not found."
                                                        <<" Adding block to emerge queue."
                                                        <<std::endl;
-                                       m_emerge_queue.addBlock(peer_id,
-                                                       getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+                                       m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
                                }
                                if(n.getContent() != CONTENT_IGNORE)
-                                       scriptapi_node_on_punch(m_lua, p_under, n, playersao);
+                                       m_script->node_on_punch(p_under, n, playersao);
                                // Cheat prevention
                                playersao->noCheatDigStart(p_under);
                        }
@@ -3124,8 +2971,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        infostream<<"Server: Not finishing digging: Node not found."
                                                        <<" Adding block to emerge queue."
                                                        <<std::endl;
-                                       m_emerge_queue.addBlock(peer_id,
-                                                       getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+                                       m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
                                }
 
                                /* Cheat prevention */
@@ -3142,6 +2988,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                                <<PP(nocheat_p)<<" and completed digging "
                                                                <<PP(p_under)<<"; not digging."<<std::endl;
                                                is_valid_dig = false;
+                                               // Call callbacks
+                                               m_script->on_cheat(playersao, "finished_unknown_dig");
                                        }
                                        // Get player's wielded item
                                        ItemStack playeritem;
@@ -3167,22 +3015,40 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                                <<", which is not diggable with tool. not digging."
                                                                <<std::endl;
                                                is_valid_dig = false;
+                                               // Call callbacks
+                                               m_script->on_cheat(playersao, "dug_unbreakable");
+                                       }
+                                       // Check digging time
+                                       // If already invalidated, we don't have to
+                                       if(!is_valid_dig){
+                                               // Well not our problem then
                                        }
-                                       // If time is considerably too short, ignore dig
-                                       // Check time only for medium and slow timed digs
-                                       if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
+                                       // Clean and long dig
+                                       else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
+                                               // All is good, but grab time from pool; don't care if
+                                               // it's actually available
+                                               playersao->getDigPool().grab(params.time);
+                                       }
+                                       // Short or laggy dig
+                                       // Try getting the time from pool
+                                       else if(playersao->getDigPool().grab(params.time)){
+                                               // All is good
+                                       }
+                                       // Dig not possible
+                                       else{
                                                infostream<<"Server: NoCheat: "<<player->getName()
-                                                               <<" completed digging "
-                                                               <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
-                                                               <<params.time<<"s; not digging."<<std::endl;
+                                                               <<" completed digging "<<PP(p_under)
+                                                               <<"too fast; not digging."<<std::endl;
                                                is_valid_dig = false;
+                                               // Call callbacks
+                                               m_script->on_cheat(playersao, "dug_too_fast");
                                        }
                                }
 
                                /* Actually dig node */
 
                                if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
-                                       scriptapi_node_on_dig(m_lua, p_under, n, playersao);
+                                       m_script->node_on_dig(p_under, n, playersao);
 
                                // Send unusual result (that is, node not being removed)
                                if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
@@ -3194,7 +3060,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                }
                        }
                } // action == 2
-               
+
                /*
                        3: place block or right-click object
                */
@@ -3222,7 +3088,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Do stuff
                                pointed_object->rightClick(playersao);
                        }
-                       else if(scriptapi_item_on_place(m_lua,
+                       else if(m_script->item_OnPlace(
                                        item, playersao, pointed))
                        {
                                // Placement was handled in lua
@@ -3230,13 +3096,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Apply returned ItemStack
                                playersao->setWieldedItem(item);
                        }
-                       
-                       // If item has node placement prediction, always send the above
-                       // node to make sure the client knows what exactly happened
+
+                       // If item has node placement prediction, always send the
+                       // blocks to make sure the client knows what exactly happened
                        if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
                                RemoteClient *client = getClient(peer_id);
                                v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
                                client->SetBlockNotSent(blockpos);
+                               v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+                               if(blockpos2 != blockpos){
+                                       client->SetBlockNotSent(blockpos2);
+                               }
                        }
                } // action == 3
 
@@ -3250,7 +3120,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        actionstream<<player->getName()<<" uses "<<item.name
                                        <<", pointing at "<<pointed.dump()<<std::endl;
 
-                       if(scriptapi_item_on_use(m_lua,
+                       if(m_script->item_OnUse(
                                        item, playersao, pointed))
                        {
                                // Apply returned ItemStack
@@ -3291,7 +3161,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        {
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
-               
+
                v3s16 p = readV3S16(is);
                std::string formname = deSerializeString(is);
                int num = readU16(is);
@@ -3309,8 +3179,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Check the target node for rollback data; leave others unnoticed
                RollbackNode rn_old(&m_env->getMap(), p, this);
 
-               scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
-                               playersao);
+               m_script->node_on_receive_fields(p, formname, fields,playersao);
 
                // Report rollback data
                RollbackNode rn_new(&m_env->getMap(), p, this);
@@ -3324,7 +3193,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        {
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
-               
+
                std::string formname = deSerializeString(is);
                int num = readU16(is);
                std::map<std::string, std::string> fields;
@@ -3334,14 +3203,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        fields[fieldname] = fieldvalue;
                }
 
-               scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
+               m_script->on_playerReceiveFields(playersao, formname, fields);
        }
        else
        {
                infostream<<"Server::ProcessData(): Ignoring "
                                "unknown command "<<command<<std::endl;
        }
-       
+
        } //try
        catch(SendFailedException &e)
        {
@@ -3351,6 +3220,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        }
 }
 
+void Server::setTimeOfDay(u32 time)
+{
+       m_env->setTimeOfDay(time);
+       m_time_of_day_send_timer = 0;
+}
+
 void Server::onMapEditEvent(MapEditEvent *event)
 {
        //infostream<<"Server::onMapEditEvent()"<<std::endl;
@@ -3427,7 +3302,7 @@ void Server::setInventoryModified(const InventoryLocation &loc)
                MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
                if(block)
                        block->raiseModified(MOD_STATE_WRITE_NEEDED);
-               
+
                setBlockNotSent(blockpos);
        }
        break;
@@ -3441,54 +3316,12 @@ void Server::setInventoryModified(const InventoryLocation &loc)
        }
 }
 
-core::list<PlayerInfo> Server::getPlayerInfo()
-{
-       DSTACK(__FUNCTION_NAME);
-       JMutexAutoLock envlock(m_env_mutex);
-       JMutexAutoLock conlock(m_con_mutex);
-       
-       core::list<PlayerInfo> list;
-
-       core::list<Player*> players = m_env->getPlayers();
-       
-       core::list<Player*>::Iterator i;
-       for(i = players.begin();
-                       i != players.end(); i++)
-       {
-               PlayerInfo info;
-
-               Player *player = *i;
-
-               try{
-                       // Copy info from connection to info struct
-                       info.id = player->peer_id;
-                       info.address = m_con.GetPeerAddress(player->peer_id);
-                       info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
-               }
-               catch(con::PeerNotFoundException &e)
-               {
-                       // Set dummy peer info
-                       info.id = 0;
-                       info.address = Address(0,0,0,0,0);
-                       info.avg_rtt = 0.0;
-               }
-
-               snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
-               info.position = player->getPosition();
-
-               list.push_back(info);
-       }
-
-       return list;
-}
-
-
 void Server::peerAdded(con::Peer *peer)
 {
        DSTACK(__FUNCTION_NAME);
        verbosestream<<"Server::peerAdded(): peer->id="
                        <<peer->id<<std::endl;
-       
+
        PeerChange c;
        c.type = PEER_ADDED;
        c.peer_id = peer->id;
@@ -3501,7 +3334,7 @@ void Server::deletingPeer(con::Peer *peer, bool timeout)
        DSTACK(__FUNCTION_NAME);
        verbosestream<<"Server::deletingPeer(): peer->id="
                        <<peer->id<<", timeout="<<timeout<<std::endl;
-       
+
        PeerChange c;
        c.type = PEER_REMOVED;
        c.peer_id = peer->id;
@@ -3513,6 +3346,32 @@ void Server::deletingPeer(con::Peer *peer, bool timeout)
        Static send methods
 */
 
+void Server::SendMovement(con::Connection &con, u16 peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOCLIENT_MOVEMENT);
+       writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
+       writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
+       writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
+       writeF1000(os, g_settings->getFloat("movement_speed_walk"));
+       writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
+       writeF1000(os, g_settings->getFloat("movement_speed_fast"));
+       writeF1000(os, g_settings->getFloat("movement_speed_climb"));
+       writeF1000(os, g_settings->getFloat("movement_speed_jump"));
+       writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
+       writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
+       writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
+       writeF1000(os, g_settings->getFloat("movement_gravity"));
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       con.Send(peer_id, 0, data, true);
+}
+
 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
 {
        DSTACK(__FUNCTION_NAME);
@@ -3528,6 +3387,21 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
        con.Send(peer_id, 0, data, true);
 }
 
+void Server::SendBreath(con::Connection &con, u16 peer_id, u16 breath)
+{
+       DSTACK(__FUNCTION_NAME);
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOCLIENT_BREATH);
+       writeU16(os, breath);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       con.Send(peer_id, 0, data, true);
+}
+
 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
                const std::wstring &reason)
 {
@@ -3562,7 +3436,7 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
 }
 
 void Server::SendItemDef(con::Connection &con, u16 peer_id,
-               IItemDefManager *itemdef)
+               IItemDefManager *itemdef, u16 protocol_version)
 {
        DSTACK(__FUNCTION_NAME);
        std::ostringstream os(std::ios_base::binary);
@@ -3574,7 +3448,7 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id,
        */
        writeU16(os, TOCLIENT_ITEMDEF);
        std::ostringstream tmp_os(std::ios::binary);
-       itemdef->serialize(tmp_os);
+       itemdef->serialize(tmp_os, protocol_version);
        std::ostringstream tmp_os2(std::ios::binary);
        compressZlib(tmp_os.str(), tmp_os2);
        os<<serializeLongString(tmp_os2.str());
@@ -3622,7 +3496,7 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id,
 void Server::SendInventory(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        assert(playersao);
 
@@ -3636,11 +3510,11 @@ void Server::SendInventory(u16 peer_id)
        playersao->getInventory()->serialize(os);
 
        std::string s = os.str();
-       
+
        SharedBuffer<u8> data(s.size()+2);
        writeU16(&data[0], TOCLIENT_INVENTORY);
        memcpy(&data[2], s.c_str(), s.size());
-       
+
        // Send as reliable
        m_con.Send(peer_id, 0, data, true);
 }
@@ -3648,18 +3522,18 @@ void Server::SendInventory(u16 peer_id)
 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        std::ostringstream os(std::ios_base::binary);
        u8 buf[12];
-       
+
        // Write command
        writeU16(buf, TOCLIENT_CHAT_MESSAGE);
        os.write((char*)buf, 2);
-       
+
        // Write length
        writeU16(buf, message.size());
        os.write((char*)buf, 2);
-       
+
        // Write string
        for(u32 i=0; i<message.size(); i++)
        {
@@ -3667,14 +3541,16 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
                writeU16(buf, w);
                os.write((char*)buf, 2);
        }
-       
+
        // Make data buffer
        std::string s = os.str();
        SharedBuffer<u8> data((u8*)s.c_str(), s.size());
        // Send as reliable
        m_con.Send(peer_id, 0, data, true);
 }
-void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, const std::string formname)
+
+void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
+                                       const std::string formname)
 {
        DSTACK(__FUNCTION_NAME);
 
@@ -3694,15 +3570,252 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, co
        m_con.Send(peer_id, 0, data, true);
 }
 
+// Spawns a particle on peer with peer_id
+void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
+                               float expirationtime, float size, bool collisiondetection,
+                               std::string texture)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       std::ostringstream os(std::ios_base::binary);
+       writeU16(os, TOCLIENT_SPAWN_PARTICLE);
+       writeV3F1000(os, pos);
+       writeV3F1000(os, velocity);
+       writeV3F1000(os, acceleration);
+       writeF1000(os, expirationtime);
+       writeF1000(os, size);
+       writeU8(os,  collisiondetection);
+       os<<serializeLongString(texture);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
+// Spawns a particle on all peers
+void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
+                               float expirationtime, float size, bool collisiondetection,
+                               std::string texture)
+{
+       for(std::map<u16, RemoteClient*>::iterator
+               i = m_clients.begin();
+               i != m_clients.end(); i++)
+       {
+               // Get client and check that it is valid
+               RemoteClient *client = i->second;
+               assert(client->peer_id == i->first);
+               if(client->serialization_version == SER_FMT_VER_INVALID)
+                       continue;
+
+               SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
+                       expirationtime, size, collisiondetection, texture);
+       }
+}
+
+// Adds a ParticleSpawner on peer with peer_id
+void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
+       v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
+       float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       std::ostringstream os(std::ios_base::binary);
+       writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
+
+       writeU16(os, amount);
+       writeF1000(os, spawntime);
+       writeV3F1000(os, minpos);
+       writeV3F1000(os, maxpos);
+       writeV3F1000(os, minvel);
+       writeV3F1000(os, maxvel);
+       writeV3F1000(os, minacc);
+       writeV3F1000(os, maxacc);
+       writeF1000(os, minexptime);
+       writeF1000(os, maxexptime);
+       writeF1000(os, minsize);
+       writeF1000(os, maxsize);
+       writeU8(os,  collisiondetection);
+       os<<serializeLongString(texture);
+       writeU32(os, id);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
+// Adds a ParticleSpawner on all peers
+void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
+       v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
+       float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
+{
+       for(std::map<u16, RemoteClient*>::iterator
+               i = m_clients.begin();
+               i != m_clients.end(); i++)
+       {
+               // Get client and check that it is valid
+               RemoteClient *client = i->second;
+               assert(client->peer_id == i->first);
+               if(client->serialization_version == SER_FMT_VER_INVALID)
+                       continue;
+
+               SendAddParticleSpawner(client->peer_id, amount, spawntime,
+                       minpos, maxpos, minvel, maxvel, minacc, maxacc,
+                       minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
+       }
+}
+
+void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       std::ostringstream os(std::ios_base::binary);
+       writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
+
+       writeU16(os, id);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
+void Server::SendDeleteParticleSpawnerAll(u32 id)
+{
+       for(std::map<u16, RemoteClient*>::iterator
+               i = m_clients.begin();
+               i != m_clients.end(); i++)
+       {
+               // Get client and check that it is valid
+               RemoteClient *client = i->second;
+               assert(client->peer_id == i->first);
+               if(client->serialization_version == SER_FMT_VER_INVALID)
+                       continue;
+
+               SendDeleteParticleSpawner(client->peer_id, id);
+       }
+}
+
+void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
+{
+       std::ostringstream os(std::ios_base::binary);
+
+       // Write command
+       writeU16(os, TOCLIENT_HUDADD);
+       writeU32(os, id);
+       writeU8(os, (u8)form->type);
+       writeV2F1000(os, form->pos);
+       os << serializeString(form->name);
+       writeV2F1000(os, form->scale);
+       os << serializeString(form->text);
+       writeU32(os, form->number);
+       writeU32(os, form->item);
+       writeU32(os, form->dir);
+       writeV2F1000(os, form->align);
+       writeV2F1000(os, form->offset);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 1, data, true);
+}
+
+void Server::SendHUDRemove(u16 peer_id, u32 id)
+{
+       std::ostringstream os(std::ios_base::binary);
+
+       // Write command
+       writeU16(os, TOCLIENT_HUDRM);
+       writeU32(os, id);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+
+       m_con.Send(peer_id, 1, data, true);
+}
+
+void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
+{
+       std::ostringstream os(std::ios_base::binary);
+
+       // Write command
+       writeU16(os, TOCLIENT_HUDCHANGE);
+       writeU32(os, id);
+       writeU8(os, (u8)stat);
+       switch (stat) {
+               case HUD_STAT_POS:
+               case HUD_STAT_SCALE:
+               case HUD_STAT_ALIGN:
+               case HUD_STAT_OFFSET:
+                       writeV2F1000(os, *(v2f *)value);
+                       break;
+               case HUD_STAT_NAME:
+               case HUD_STAT_TEXT:
+                       os << serializeString(*(std::string *)value);
+                       break;
+               case HUD_STAT_NUMBER:
+               case HUD_STAT_ITEM:
+               case HUD_STAT_DIR:
+               default:
+                       writeU32(os, *(u32 *)value);
+                       break;
+       }
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
+void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
+{
+       std::ostringstream os(std::ios_base::binary);
+
+       // Write command
+       writeU16(os, TOCLIENT_HUD_SET_FLAGS);
+       writeU32(os, flags);
+       writeU32(os, mask);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
+void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
+{
+       std::ostringstream os(std::ios_base::binary);
+
+       // Write command
+       writeU16(os, TOCLIENT_HUD_SET_PARAM);
+       writeU16(os, param);
+       os<<serializeString(value);
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
 void Server::BroadcastChatMessage(const std::wstring &message)
 {
-       for(core::map<u16, RemoteClient*>::Iterator
-               i = m_clients.getIterator();
-               i.atEnd() == false; i++)
+       for(std::map<u16, RemoteClient*>::iterator
+               i = m_clients.begin();
+               i != m_clients.end(); ++i)
        {
                // Get client and check that it is valid
-               RemoteClient *client = i.getNode()->getValue();
-               assert(client->peer_id == i.getNode()->getKey());
+               RemoteClient *client = i->second;
+               assert(client->peer_id == i->first);
                if(client->serialization_version == SER_FMT_VER_INVALID)
                        continue;
 
@@ -3710,6 +3823,20 @@ void Server::BroadcastChatMessage(const std::wstring &message)
        }
 }
 
+void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       // Make packet
+       SharedBuffer<u8> data(2+2+4);
+       writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
+       writeU16(&data[2], time);
+       writeF1000(&data[4], time_speed);
+
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
 void Server::SendPlayerHP(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
@@ -3717,6 +3844,20 @@ void Server::SendPlayerHP(u16 peer_id)
        assert(playersao);
        playersao->m_hp_not_sent = false;
        SendHP(m_con, peer_id, playersao->getHP());
+
+       // Send to other clients
+       std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
+       ActiveObjectMessage aom(playersao->getId(), true, str);
+       playersao->m_messages_out.push_back(aom);
+}
+
+void Server::SendPlayerBreath(u16 peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+       PlayerSAO *playersao = getPlayerSAO(peer_id);
+       assert(playersao);
+       playersao->m_breath_not_sent = false;
+       SendBreath(m_con, peer_id, playersao->getBreath());
 }
 
 void Server::SendMovePlayer(u16 peer_id)
@@ -3730,7 +3871,7 @@ void Server::SendMovePlayer(u16 peer_id)
        writeV3F1000(os, player->getPosition());
        writeF1000(os, player->getPitch());
        writeF1000(os, player->getYaw());
-       
+
        {
                v3f pos = player->getPosition();
                f32 pitch = player->getPitch();
@@ -3757,8 +3898,8 @@ void Server::SendPlayerPrivileges(u16 peer_id)
                return;
 
        std::set<std::string> privs;
-       scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
-       
+       m_script->getAuth(player->getName(), NULL, &privs);
+
        std::ostringstream os(std::ios_base::binary);
        writeU16(os, TOCLIENT_PRIVILEGES);
        writeU16(os, privs.size());
@@ -3821,10 +3962,10 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
        }
        else
        {
-               for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator(); i.atEnd() == false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin(); i != m_clients.end(); ++i)
                {
-                       RemoteClient *client = i.getNode()->getValue();
+                       RemoteClient *client = i->second;
                        Player *player = m_env->getPlayer(client->peer_id);
                        if(!player)
                                continue;
@@ -3894,7 +4035,7 @@ void Server::stopSound(s32 handle)
 }
 
 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
-       core::list<u16> *far_players, float far_d_nodes)
+       std::list<u16> *far_players, float far_d_nodes)
 {
        float maxd = far_d_nodes*BS;
        v3f p_f = intToFloat(p, BS);
@@ -3907,20 +4048,20 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
        writeS16(&reply[4], p.Y);
        writeS16(&reply[6], p.Z);
 
-       for(core::map<u16, RemoteClient*>::Iterator
-               i = m_clients.getIterator();
-               i.atEnd() == false; i++)
+       for(std::map<u16, RemoteClient*>::iterator
+               i = m_clients.begin();
+               i != m_clients.end(); ++i)
        {
                // Get client and check that it is valid
-               RemoteClient *client = i.getNode()->getValue();
-               assert(client->peer_id == i.getNode()->getKey());
+               RemoteClient *client = i->second;
+               assert(client->peer_id == i->first);
                if(client->serialization_version == SER_FMT_VER_INVALID)
                        continue;
 
                // Don't send if it's the same one
                if(client->peer_id == ignore_id)
                        continue;
-               
+
                if(far_players)
                {
                        // Get player
@@ -3943,18 +4084,19 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
 }
 
 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
-               core::list<u16> *far_players, float far_d_nodes)
+               std::list<u16> *far_players, float far_d_nodes,
+               bool remove_metadata)
 {
        float maxd = far_d_nodes*BS;
        v3f p_f = intToFloat(p, BS);
 
-       for(core::map<u16, RemoteClient*>::Iterator
-               i = m_clients.getIterator();
-               i.atEnd() == false; i++)
+       for(std::map<u16, RemoteClient*>::iterator
+               i = m_clients.begin();
+               i != m_clients.end(); ++i)
        {
                // Get client and check that it is valid
-               RemoteClient *client = i.getNode()->getValue();
-               assert(client->peer_id == i.getNode()->getKey());
+               RemoteClient *client = i->second;
+               assert(client->peer_id == i->first);
                if(client->serialization_version == SER_FMT_VER_INVALID)
                        continue;
 
@@ -3979,13 +4121,23 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
                }
 
                // Create packet
-               u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
+               u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
                SharedBuffer<u8> reply(replysize);
                writeU16(&reply[0], TOCLIENT_ADDNODE);
                writeS16(&reply[2], p.X);
                writeS16(&reply[4], p.Y);
                writeS16(&reply[6], p.Z);
                n.serialize(&reply[8], client->serialization_version);
+               u32 index = 8 + MapNode::serializedLength(client->serialization_version);
+               writeU8(&reply[index], remove_metadata ? 0 : 1);
+               
+               if (!remove_metadata) {
+                       if (client->net_proto_version <= 21) {
+                               // Old clients always clear metadata; fix it
+                               // by sending the full block again.
+                               client->SetBlockNotSent(p);
+                       }
+               }
 
                // Send as reliable
                m_con.Send(client->peer_id, 0, reply, true);
@@ -3994,21 +4146,21 @@ 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++)
+       for(std::map<u16, RemoteClient*>::iterator
+               i = m_clients.begin();
+               i != m_clients.end(); ++i)
        {
-               RemoteClient *client = i.getNode()->getValue();
+               RemoteClient *client = i->second;
                client->SetBlockNotSent(p);
        }
 }
 
-void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
+void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
 {
        DSTACK(__FUNCTION_NAME);
 
        v3s16 p = block->getPos();
-       
+
 #if 0
        // Analyze it a bit
        bool completely_air = true;
@@ -4033,9 +4185,10 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
        /*
                Create a packet with the block in the right format
        */
-       
+
        std::ostringstream os(std::ios_base::binary);
        block->serialize(os, ver, false);
+       block->serializeNetworkSpecific(os, net_proto_version);
        std::string s = os.str();
        SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
 
@@ -4049,11 +4202,11 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
 
        /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
                        <<":  \tpacket size: "<<replysize<<std::endl;*/
-       
+
        /*
                Send packet
        */
-       m_con.Send(peer_id, 1, reply, true);
+       m_con.Send(peer_id, 2, reply, true);
 }
 
 void Server::SendBlocks(float dtime)
@@ -4065,19 +4218,19 @@ void Server::SendBlocks(float dtime)
 
        ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
 
-       core::array<PrioritySortedBlockTransfer> queue;
+       std::vector<PrioritySortedBlockTransfer> queue;
 
        s32 total_sending = 0;
-       
+
        {
                ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
 
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd() == false; i++)
+               for(std::map<u16, RemoteClient*>::iterator
+                       i = m_clients.begin();
+                       i != m_clients.end(); ++i)
                {
-                       RemoteClient *client = i.getNode()->getValue();
-                       assert(client->peer_id == i.getNode()->getKey());
+                       RemoteClient *client = i->second;
+                       assert(client->peer_id == i->first);
 
                        // If definitions and textures have not been sent, don't
                        // send MapBlocks either
@@ -4085,10 +4238,10 @@ void Server::SendBlocks(float dtime)
                                continue;
 
                        total_sending += client->SendingCount();
-                       
+
                        if(client->serialization_version == SER_FMT_VER_INVALID)
                                continue;
-                       
+
                        client->GetNextBlocks(this, dtime, queue);
                }
        }
@@ -4096,7 +4249,7 @@ void Server::SendBlocks(float dtime)
        // Sort.
        // Lowest priority number comes first.
        // Lowest is most important.
-       queue.sort();
+       std::sort(queue.begin(), queue.end());
 
        for(u32 i=0; i<queue.size(); i++)
        {
@@ -4104,7 +4257,7 @@ void Server::SendBlocks(float dtime)
                if(total_sending >= g_settings->getS32
                                ("max_simultaneous_block_sends_server_total"))
                        break;
-               
+
                PrioritySortedBlockTransfer q = queue[i];
 
                MapBlock *block = NULL;
@@ -4117,9 +4270,13 @@ void Server::SendBlocks(float dtime)
                        continue;
                }
 
-               RemoteClient *client = getClient(q.peer_id);
+               RemoteClient *client = getClientNoEx(q.peer_id);
+               if(!client)
+                       continue;
+               if(client->denied)
+                       continue;
 
-               SendBlockNoLock(q.peer_id, block, client->serialization_version);
+               SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
 
                client->SentBlock(q.pos);
 
@@ -4132,7 +4289,7 @@ void Server::fillMediaCache()
        DSTACK(__FUNCTION_NAME);
 
        infostream<<"Server: Calculating media file checksums"<<std::endl;
-       
+
        // Collect all media file paths
        std::list<std::string> paths;
        for(std::vector<ModSpec>::iterator i = m_mods.begin();
@@ -4143,9 +4300,8 @@ void Server::fillMediaCache()
                paths.push_back(mod.path + DIR_DELIM + "media");
                paths.push_back(mod.path + DIR_DELIM + "models");
        }
-       std::string path_all = "textures";
-       paths.push_back(path_all + DIR_DELIM + "all");
-       
+       paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
+
        // Collect media file information from paths into cache
        for(std::list<std::string>::iterator i = paths.begin();
                        i != paths.end(); i++)
@@ -4243,7 +4399,7 @@ void Server::sendMediaAnnouncement(u16 peer_id)
        verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
                        <<std::endl;
 
-       core::list<SendableMediaAnnouncement> file_announcements;
+       std::list<SendableMediaAnnouncement> file_announcements;
 
        for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
                        i != m_media.end(); i++){
@@ -4265,13 +4421,13 @@ void Server::sendMediaAnnouncement(u16 peer_id)
                        string sha1_digest
                }
        */
-       
+
        writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
        writeU16(os, file_announcements.size());
 
-       for(core::list<SendableMediaAnnouncement>::Iterator
+       for(std::list<SendableMediaAnnouncement>::iterator
                        j = file_announcements.begin();
-                       j != file_announcements.end(); j++){
+                       j != file_announcements.end(); ++j){
                os<<serializeString(j->name);
                os<<serializeString(j->sha1_digest);
        }
@@ -4300,7 +4456,7 @@ struct SendableMedia
 };
 
 void Server::sendRequestedMedia(u16 peer_id,
-               const core::list<MediaRequest> &tosend)
+               const std::list<std::string> &tosend)
 {
        DSTACK(__FUNCTION_NAME);
 
@@ -4312,22 +4468,24 @@ void Server::sendRequestedMedia(u16 peer_id,
        // Put 5kB in one bunch (this is not accurate)
        u32 bytes_per_bunch = 5000;
 
-       core::array< core::list<SendableMedia> > file_bunches;
-       file_bunches.push_back(core::list<SendableMedia>());
+       std::vector< std::list<SendableMedia> > file_bunches;
+       file_bunches.push_back(std::list<SendableMedia>());
 
        u32 file_size_bunch_total = 0;
 
-       for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
-                       i != tosend.end(); i++)
+       for(std::list<std::string>::const_iterator i = tosend.begin();
+                       i != tosend.end(); ++i)
        {
-               if(m_media.find(i->name) == m_media.end()){
+               const std::string &name = *i;
+
+               if(m_media.find(name) == m_media.end()){
                        errorstream<<"Server::sendRequestedMedia(): Client asked for "
-                                       <<"unknown file \""<<(i->name)<<"\""<<std::endl;
+                                       <<"unknown file \""<<(name)<<"\""<<std::endl;
                        continue;
                }
 
                //TODO get path + name
-               std::string tpath = m_media[(*i).name].path;
+               std::string tpath = m_media[name].path;
 
                // Read data
                std::ifstream fis(tpath.c_str(), std::ios_base::binary);
@@ -4353,18 +4511,18 @@ void Server::sendRequestedMedia(u16 peer_id,
                }
                if(bad){
                        errorstream<<"Server::sendRequestedMedia(): Failed to read \""
-                                       <<(*i).name<<"\""<<std::endl;
+                                       <<name<<"\""<<std::endl;
                        continue;
                }
                /*infostream<<"Server::sendRequestedMedia(): Loaded \""
                                <<tname<<"\""<<std::endl;*/
                // Put in list
                file_bunches[file_bunches.size()-1].push_back(
-                               SendableMedia((*i).name, tpath, tmp_os.str()));
+                               SendableMedia(name, tpath, tmp_os.str()));
 
                // Start next bunch if got enough data
                if(file_size_bunch_total >= bytes_per_bunch){
-                       file_bunches.push_back(core::list<SendableMedia>());
+                       file_bunches.push_back(std::list<SendableMedia>());
                        file_size_bunch_total = 0;
                }
 
@@ -4395,9 +4553,9 @@ void Server::sendRequestedMedia(u16 peer_id,
                writeU16(os, i);
                writeU32(os, file_bunches[i].size());
 
-               for(core::list<SendableMedia>::Iterator
+               for(std::list<SendableMedia>::iterator
                                j = file_bunches[i].begin();
-                               j != file_bunches[i].end(); j++){
+                               j != file_bunches[i].end(); ++j){
                        os<<serializeString(j->name);
                        os<<serializeLongString(j->data);
                }
@@ -4410,7 +4568,7 @@ void Server::sendRequestedMedia(u16 peer_id,
                                <<" size=" <<s.size()<<std::endl;
                SharedBuffer<u8> data((u8*)s.c_str(), s.size());
                // Send as reliable
-               m_con.Send(peer_id, 0, data, true);
+               m_con.Send(peer_id, 2, data, true);
        }
 }
 
@@ -4438,10 +4596,10 @@ void Server::sendDetachedInventoryToAll(const std::string &name)
 {
        DSTACK(__FUNCTION_NAME);
 
-       for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd() == false; i++){
-               RemoteClient *client = i.getNode()->getValue();
+       for(std::map<u16, RemoteClient*>::iterator
+                       i = m_clients.begin();
+                       i != m_clients.end(); ++i){
+               RemoteClient *client = i->second;
                sendDetachedInventory(name, client->peer_id);
        }
 }
@@ -4466,7 +4624,7 @@ void Server::sendDetachedInventories(u16 peer_id)
 void Server::DiePlayer(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        assert(playersao);
 
@@ -4477,7 +4635,7 @@ void Server::DiePlayer(u16 peer_id)
        playersao->setHP(0);
 
        // Trigger scripted stuff
-       scriptapi_on_dieplayer(m_lua, playersao);
+       m_script->on_dieplayer(playersao);
 
        SendPlayerHP(peer_id);
        SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
@@ -4496,23 +4654,158 @@ void Server::RespawnPlayer(u16 peer_id)
 
        playersao->setHP(PLAYER_MAX_HP);
 
-       bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
+       bool repositioned = m_script->on_respawnplayer(playersao);
        if(!repositioned){
                v3f pos = findSpawnPos(m_env->getServerMap());
                playersao->setPos(pos);
        }
 }
 
+void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       SendAccessDenied(m_con, peer_id, reason);
+
+       RemoteClient *client = getClientNoEx(peer_id);
+       if(client)
+               client->denied = true;
+
+       // If there are way too many clients, get rid of denied new ones immediately
+       if((int)m_clients.size() > 2 * g_settings->getU16("max_users")){
+               verbosestream<<"Server: DenyAccess: Too many clients; getting rid of "
+                               <<"peer_id="<<peer_id<<" immediately"<<std::endl;
+               // Delete peer to stop sending it data
+               m_con.DeletePeer(peer_id);
+               // Delete client also to stop block sends and other stuff
+               DeleteClient(peer_id, CDR_DENY);
+       }
+}
+
+void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       // Error check
+       std::map<u16, RemoteClient*>::iterator n;
+       n = m_clients.find(peer_id);
+       // The client may not exist; clients are immediately removed if their
+       // access is denied, and this event occurs later then.
+       if(n == m_clients.end())
+               return;
+
+       /*
+               Mark objects to be not known by the client
+       */
+       RemoteClient *client = n->second;
+       // Handle objects
+       for(std::set<u16>::iterator
+                       i = client->m_known_objects.begin();
+                       i != client->m_known_objects.end(); ++i)
+       {
+               // Get object
+               u16 id = *i;
+               ServerActiveObject* obj = m_env->getActiveObject(id);
+
+               if(obj && obj->m_known_by_count > 0)
+                       obj->m_known_by_count--;
+       }
+
+       /*
+               Clear references to playing sounds
+       */
+       for(std::map<s32, ServerPlayingSound>::iterator
+                       i = m_playing_sounds.begin();
+                       i != m_playing_sounds.end();)
+       {
+               ServerPlayingSound &psound = i->second;
+               psound.clients.erase(peer_id);
+               if(psound.clients.size() == 0)
+                       m_playing_sounds.erase(i++);
+               else
+                       i++;
+       }
+
+       Player *player = m_env->getPlayer(peer_id);
+
+       // Collect information about leaving in chat
+       std::wstring message;
+       {
+               if(player != NULL && reason != CDR_DENY)
+               {
+                       std::wstring name = narrow_to_wide(player->getName());
+                       message += L"*** ";
+                       message += name;
+                       message += L" left the game.";
+                       if(reason == CDR_TIMEOUT)
+                               message += L" (timed out)";
+               }
+       }
+
+       /* Run scripts and remove from environment */
+       {
+               if(player != NULL)
+               {
+                       PlayerSAO *playersao = player->getPlayerSAO();
+                       assert(playersao);
+
+                       m_script->on_leaveplayer(playersao);
+
+                       playersao->disconnected();
+               }
+       }
+
+       /*
+               Print out action
+       */
+       {
+               if(player != NULL && reason != CDR_DENY)
+               {
+                       std::ostringstream os(std::ios_base::binary);
+                       for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin();
+                               i != m_clients.end(); ++i)
+                       {
+                               RemoteClient *client = i->second;
+                               assert(client->peer_id == i->first);
+                               if(client->serialization_version == SER_FMT_VER_INVALID)
+                                       continue;
+                               // Get player
+                               Player *player = m_env->getPlayer(client->peer_id);
+                               if(!player)
+                                       continue;
+                               // Get name of player
+                               os<<player->getName()<<" ";
+                       }
+
+                       actionstream<<player->getName()<<" "
+                                       <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
+                                       <<" List of players: "<<os.str()<<std::endl;
+               }
+       }
+
+       // Delete client
+       delete m_clients[peer_id];
+       m_clients.erase(peer_id);
+
+       // Send leave chat message to all remaining clients
+       if(message.length() != 0)
+               BroadcastChatMessage(message);
+}
+
 void Server::UpdateCrafting(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        Player* player = m_env->getPlayer(peer_id);
        assert(player);
 
        // Get a preview for crafting
        ItemStack preview;
+       InventoryLocation loc;
+       loc.setPlayer(player->getName());
        getCraftingResult(&player->inventory, preview, false, this);
+       m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
 
        // Put the new preview in
        InventoryList *plist = player->inventory.getList("craftpreview");
@@ -4523,13 +4816,36 @@ void Server::UpdateCrafting(u16 peer_id)
 
 RemoteClient* Server::getClient(u16 peer_id)
 {
-       DSTACK(__FUNCTION_NAME);
-       //JMutexAutoLock lock(m_con_mutex);
-       core::map<u16, RemoteClient*>::Node *n;
+       RemoteClient *client = getClientNoEx(peer_id);
+       if(!client)
+               throw ClientNotFoundException("Client not found");
+       return client;
+}
+RemoteClient* Server::getClientNoEx(u16 peer_id)
+{
+       std::map<u16, RemoteClient*>::iterator n;
        n = m_clients.find(peer_id);
-       // A client should exist for all peers
-       assert(n != NULL);
-       return n->getValue();
+       // The client may not exist; clients are immediately removed if their
+       // access is denied, and this event occurs later then.
+       if(n == m_clients.end())
+               return NULL;
+       return n->second;
+}
+
+std::string Server::getPlayerName(u16 peer_id)
+{
+       Player *player = m_env->getPlayer(peer_id);
+       if(player == NULL)
+               return "[id="+itos(peer_id)+"]";
+       return player->getName();
+}
+
+PlayerSAO* Server::getPlayerSAO(u16 peer_id)
+{
+       Player *player = m_env->getPlayer(peer_id);
+       if(player == NULL)
+               return NULL;
+       return player->getPlayerSAO();
 }
 
 std::wstring Server::getStatusString()
@@ -4537,19 +4853,21 @@ std::wstring Server::getStatusString()
        std::wostringstream os(std::ios_base::binary);
        os<<L"# Server: ";
        // Version
-       os<<L"version="<<narrow_to_wide(VERSION_STRING);
+       os<<L"version="<<narrow_to_wide(minetest_version_simple);
        // Uptime
        os<<L", uptime="<<m_uptime.get();
+       // Max lag estimate
+       os<<L", max_lag="<<m_env->getMaxLagEstimate();
        // Information about clients
-       core::map<u16, RemoteClient*>::Iterator i;
+       std::map<u16, RemoteClient*>::iterator i;
        bool first;
        os<<L", clients={";
-       for(i = m_clients.getIterator(), first = true;
-               i.atEnd() == false; i++)
+       for(i = m_clients.begin(), first = true;
+               i != m_clients.end(); ++i)
        {
                // Get client and check that it is valid
-               RemoteClient *client = i.getNode()->getValue();
-               assert(client->peer_id == i.getNode()->getKey());
+               RemoteClient *client = i->second;
+               assert(client->peer_id == i->first);
                if(client->serialization_version == SER_FMT_VER_INVALID)
                        continue;
                // Get player
@@ -4576,7 +4894,7 @@ std::wstring Server::getStatusString()
 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
 {
        std::set<std::string> privs;
-       scriptapi_get_auth(m_lua, name, NULL, &privs);
+       m_script->getAuth(name, NULL, &privs);
        return privs;
 }
 
@@ -4589,10 +4907,10 @@ bool Server::checkPriv(const std::string &name, const std::string &priv)
 void Server::reportPrivsModified(const std::string &name)
 {
        if(name == ""){
-               for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd() == false; i++){
-                       RemoteClient *client = i.getNode()->getValue();
+               for(std::map<u16, RemoteClient*>::iterator
+                               i = m_clients.begin();
+                               i != m_clients.end(); ++i){
+                       RemoteClient *client = i->second;
                        Player *player = m_env->getPlayer(client->peer_id);
                        reportPrivsModified(player->getName());
                }
@@ -4618,19 +4936,30 @@ void Server::reportInventoryFormspecModified(const std::string &name)
        SendPlayerInventoryFormspec(player->peer_id);
 }
 
-// Saves g_settings to configpath given at initialization
-void Server::saveConfig()
+void Server::setIpBanned(const std::string &ip, const std::string &name)
+{
+       m_banmanager->add(ip, name);
+}
+
+void Server::unsetIpBanned(const std::string &ip_or_name)
+{
+       m_banmanager->remove(ip_or_name);
+}
+
+std::string Server::getBanDescription(const std::string &ip_or_name)
 {
-       if(m_path_config != "")
-               g_settings->updateConfigFile(m_path_config.c_str());
+       return m_banmanager->getBanDescription(ip_or_name);
 }
 
-void Server::notifyPlayer(const char *name, const std::wstring msg)
+void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
 {
        Player *player = m_env->getPlayer(name);
        if(!player)
                return;
-       SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
+       if (prepend)
+               SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
+       else
+               SendChatMessage(player->peer_id, msg);
 }
 
 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
@@ -4647,17 +4976,181 @@ bool Server::showFormspec(const char *playername, const std::string &formspec, c
        return true;
 }
 
+u32 Server::hudAdd(Player *player, HudElement *form) {
+       if (!player)
+               return -1;
+
+       u32 id = player->getFreeHudID();
+       if (id < player->hud.size())
+               player->hud[id] = form;
+       else
+               player->hud.push_back(form);
+       
+       SendHUDAdd(player->peer_id, id, form);
+       return id;
+}
+
+bool Server::hudRemove(Player *player, u32 id) {
+       if (!player || id >= player->hud.size() || !player->hud[id])
+               return false;
+
+       delete player->hud[id];
+       player->hud[id] = NULL;
+       
+       SendHUDRemove(player->peer_id, id);
+       return true;
+}
+
+bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
+       if (!player)
+               return false;
+
+       SendHUDChange(player->peer_id, id, stat, data);
+       return true;
+}
+
+bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
+       if (!player)
+               return false;
+
+       SendHUDSetFlags(player->peer_id, flags, mask);
+       return true;
+}
+
+bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
+       if (!player)
+               return false;
+       if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
+               return false;
+
+       std::ostringstream os(std::ios::binary);
+       writeS32(os, hotbar_itemcount);
+       SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
+       return true;
+}
+
+void Server::hudSetHotbarImage(Player *player, std::string name) {
+       if (!player)
+               return;
+
+       SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
+}
+
+void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
+       if (!player)
+               return;
+
+       SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
+}
+
 void Server::notifyPlayers(const std::wstring msg)
 {
        BroadcastChatMessage(msg);
 }
 
-void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
+void Server::spawnParticle(const char *playername, v3f pos,
+               v3f velocity, v3f acceleration,
+               float expirationtime, float size, bool
+               collisiondetection, std::string texture)
+{
+       Player *player = m_env->getPlayer(playername);
+       if(!player)
+               return;
+       SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
+                       expirationtime, size, collisiondetection, texture);
+}
+
+void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
+               float expirationtime, float size,
+               bool collisiondetection, std::string texture)
+{
+       SendSpawnParticleAll(pos, velocity, acceleration,
+                       expirationtime, size, collisiondetection, texture);
+}
+
+u32 Server::addParticleSpawner(const char *playername,
+               u16 amount, float spawntime,
+               v3f minpos, v3f maxpos,
+               v3f minvel, v3f maxvel,
+               v3f minacc, v3f maxacc,
+               float minexptime, float maxexptime,
+               float minsize, float maxsize,
+               bool collisiondetection, std::string texture)
+{
+       Player *player = m_env->getPlayer(playername);
+       if(!player)
+               return -1;
+
+       u32 id = 0;
+       for(;;) // look for unused particlespawner id
+       {
+               id++;
+               if (std::find(m_particlespawner_ids.begin(),
+                               m_particlespawner_ids.end(), id)
+                               == m_particlespawner_ids.end())
+               {
+                       m_particlespawner_ids.push_back(id);
+                       break;
+               }
+       }
+
+       SendAddParticleSpawner(player->peer_id, amount, spawntime,
+               minpos, maxpos, minvel, maxvel, minacc, maxacc,
+               minexptime, maxexptime, minsize, maxsize,
+               collisiondetection, texture, id);
+
+       return id;
+}
+
+u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
+               v3f minpos, v3f maxpos,
+               v3f minvel, v3f maxvel,
+               v3f minacc, v3f maxacc,
+               float minexptime, float maxexptime,
+               float minsize, float maxsize,
+               bool collisiondetection, std::string texture)
+{
+       u32 id = 0;
+       for(;;) // look for unused particlespawner id
+       {
+               id++;
+               if (std::find(m_particlespawner_ids.begin(),
+                               m_particlespawner_ids.end(), id)
+                               == m_particlespawner_ids.end())
+               {
+                       m_particlespawner_ids.push_back(id);
+                       break;
+               }
+       }
+
+       SendAddParticleSpawnerAll(amount, spawntime,
+               minpos, maxpos, minvel, maxvel, minacc, maxacc,
+               minexptime, maxexptime, minsize, maxsize,
+               collisiondetection, texture, id);
+
+       return id;
+}
+
+void Server::deleteParticleSpawner(const char *playername, u32 id)
+{
+       Player *player = m_env->getPlayer(playername);
+       if(!player)
+               return;
+
+       m_particlespawner_ids.erase(
+                       std::remove(m_particlespawner_ids.begin(),
+                       m_particlespawner_ids.end(), id),
+                       m_particlespawner_ids.end());
+       SendDeleteParticleSpawner(player->peer_id, id);
+}
+
+void Server::deleteParticleSpawnerAll(u32 id)
 {
-       u8 flags = 0;
-       if(!allow_generate)
-               flags |= BLOCK_EMERGE_FLAG_FROMDISK;
-       m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
+       m_particlespawner_ids.erase(
+                       std::remove(m_particlespawner_ids.begin(),
+                       m_particlespawner_ids.end(), id),
+                       m_particlespawner_ids.end());
+       SendDeleteParticleSpawnerAll(id);
 }
 
 Inventory* Server::createDetachedInventory(const std::string &name)
@@ -4702,7 +5195,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
        ServerMap *map = (ServerMap*)(&m_env->getMap());
        // Disable rollback report sink while reverting
        BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
-       
+
        // Fail if no actions to handle
        if(actions.empty()){
                log->push_back("Nothing to do.");
@@ -4711,7 +5204,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
 
        int num_tried = 0;
        int num_failed = 0;
-       
+
        for(std::list<RollbackAction>::const_iterator
                        i = actions.begin();
                        i != actions.end(); i++)
@@ -4734,7 +5227,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
                                log->push_back(os.str());
                }
        }
-       
+
        infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
                        <<" failed"<<std::endl;
 
@@ -4808,11 +5301,11 @@ const ModSpec* Server::getModSpec(const std::string &modname)
        }
        return NULL;
 }
-void Server::getModNames(core::list<std::string> &modlist)
+void Server::getModNames(std::list<std::string> &modlist)
 {
        for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
        {
-               modlist.push_back((*i).name);
+               modlist.push_back(i->name);
        }
 }
 std::string Server::getBuiltinLuaPath()
@@ -4825,50 +5318,42 @@ v3f findSpawnPos(ServerMap &map)
        //return v3f(50,50,50)*BS;
 
        v3s16 nodepos;
-       
+
 #if 0
        nodepos = v2s16(0,0);
        groundheight = 20;
 #endif
 
 #if 1
-       s16 water_level = g_settings->getS16("water_level");
+       s16 water_level = map.m_mgparams->water_level;
 
        // Try to find a good place a few times
        for(s32 i=0; i<1000; i++)
        {
                s32 range = 1 + i;
                // We're going to try to throw the player to this position
-               v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
-                               -range + (myrand()%(range*2)));
-               //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
-               // Get ground height at point (fallbacks to heightmap function)
+               v2s16 nodepos2d = v2s16(
+                               -range + (myrand() % (range * 2)),
+                               -range + (myrand() % (range * 2)));
+
+               // Get ground height at point
                s16 groundheight = map.findGroundLevel(nodepos2d);
-               // Don't go underwater
-               if(groundheight <= water_level)
-               {
-                       //infostream<<"-> Underwater"<<std::endl;
+               if (groundheight <= water_level) // Don't go underwater
                        continue;
-               }
-               // Don't go to high places
-               if(groundheight > water_level + 6)
-               {
-                       //infostream<<"-> Underwater"<<std::endl;
+               if (groundheight > water_level + 6) // Don't go to high places
                        continue;
-               }
-               
-               nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
+
+               nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
                bool is_good = false;
                s32 air_count = 0;
-               for(s32 i=0; i<10; i++){
+               for (s32 i = 0; i < 10; i++) {
                        v3s16 blockpos = getNodeBlockPos(nodepos);
                        map.emergeBlock(blockpos, true);
-                       MapNode n = map.getNodeNoEx(nodepos);
-                       if(n.getContent() == CONTENT_AIR){
+                       content_t c = map.getNodeNoEx(nodepos).getContent();
+                       if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
                                air_count++;
-                               if(air_count >= 2){
+                               if (air_count >= 2){
                                        is_good = true;
-                                       nodepos.Y -= 1;
                                        break;
                                }
                        }
@@ -4881,7 +5366,7 @@ v3f findSpawnPos(ServerMap &map)
                }
        }
 #endif
-       
+
        return intToFloat(nodepos, BS);
 }
 
@@ -4938,14 +5423,17 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
                        getPlayerEffectivePrivs(player->getName()),
                        isSingleplayer());
 
+       /* Clean up old HUD elements from previous sessions */
+       player->hud.clear();
+
        /* Add object to environment */
        m_env->addActiveObject(playersao);
 
        /* Run scripts */
        if(newplayer)
-               scriptapi_on_newplayer(m_lua, playersao);
+               m_script->on_newplayer(playersao);
 
-       scriptapi_on_joinplayer(m_lua, playersao);
+       m_script->on_joinplayer(playersao);
 
        return playersao;
 }
@@ -4954,7 +5442,7 @@ void Server::handlePeerChange(PeerChange &c)
 {
        JMutexAutoLock envlock(m_env_mutex);
        JMutexAutoLock conlock(m_con_mutex);
-       
+
        if(c.type == PEER_ADDED)
        {
                /*
@@ -4962,15 +5450,15 @@ void Server::handlePeerChange(PeerChange &c)
                */
 
                // Error check
-               core::map<u16, RemoteClient*>::Node *n;
+               std::map<u16, RemoteClient*>::iterator n;
                n = m_clients.find(c.peer_id);
                // The client shouldn't already exist
-               assert(n == NULL);
+               assert(n == m_clients.end());
 
                // Create client
                RemoteClient *client = new RemoteClient();
                client->peer_id = c.peer_id;
-               m_clients.insert(client->peer_id, client);
+               m_clients[client->peer_id] = client;
 
        } // PEER_ADDED
        else if(c.type == PEER_REMOVED)
@@ -4979,114 +5467,8 @@ void Server::handlePeerChange(PeerChange &c)
                        Delete
                */
 
-               // Error check
-               core::map<u16, RemoteClient*>::Node *n;
-               n = m_clients.find(c.peer_id);
-               // The client should exist
-               assert(n != NULL);
-               
-               /*
-                       Mark objects to be not known by the client
-               */
-               RemoteClient *client = n->getValue();
-               // Handle objects
-               for(core::map<u16, bool>::Iterator
-                               i = client->m_known_objects.getIterator();
-                               i.atEnd()==false; i++)
-               {
-                       // Get object
-                       u16 id = i.getNode()->getKey();
-                       ServerActiveObject* obj = m_env->getActiveObject(id);
-                       
-                       if(obj && obj->m_known_by_count > 0)
-                               obj->m_known_by_count--;
-               }
-
-               /*
-                       Clear references to playing sounds
-               */
-               for(std::map<s32, ServerPlayingSound>::iterator
-                               i = m_playing_sounds.begin();
-                               i != m_playing_sounds.end();)
-               {
-                       ServerPlayingSound &psound = i->second;
-                       psound.clients.erase(c.peer_id);
-                       if(psound.clients.size() == 0)
-                               m_playing_sounds.erase(i++);
-                       else
-                               i++;
-               }
-
-               Player *player = m_env->getPlayer(c.peer_id);
-
-               // Collect information about leaving in chat
-               std::wstring message;
-               {
-                       if(player != NULL)
-                       {
-                               std::wstring name = narrow_to_wide(player->getName());
-                               message += L"*** ";
-                               message += name;
-                               message += L" left the game.";
-                               if(c.timeout)
-                                       message += L" (timed out)";
-                       }
-               }
-               
-               /* Run scripts and remove from environment */
-               {
-                       if(player != NULL)
-                       {
-                               PlayerSAO *playersao = player->getPlayerSAO();
-                               assert(playersao);
-
-                               scriptapi_on_leaveplayer(m_lua, playersao);
-
-                               playersao->disconnected();
-                       }
-               }
-
-               /*
-                       Print out action
-               */
-               {
-                       if(player != NULL)
-                       {
-                               std::ostringstream os(std::ios_base::binary);
-                               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());
-                                       if(client->serialization_version == SER_FMT_VER_INVALID)
-                                               continue;
-                                       // Get player
-                                       Player *player = m_env->getPlayer(client->peer_id);
-                                       if(!player)
-                                               continue;
-                                       // Get name of player
-                                       os<<player->getName()<<" ";
-                               }
-
-                               actionstream<<player->getName()<<" "
-                                               <<(c.timeout?"times out.":"leaves game.")
-                                               <<" List of players: "
-                                               <<os.str()<<std::endl;
-                       }
-               }
-               
-               // Delete client
-               delete m_clients[c.peer_id];
-               m_clients.remove(c.peer_id);
+               DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
 
-               // Send player info to all remaining clients
-               //SendPlayerInfos();
-               
-               // Send leave chat message to all remaining clients
-               if(message.length() != 0)
-                       BroadcastChatMessage(message);
-               
        } // PEER_REMOVED
        else
        {
@@ -5111,7 +5493,7 @@ void Server::handlePeerChanges()
 void dedicated_server_loop(Server &server, bool &kill)
 {
        DSTACK(__FUNCTION_NAME);
-       
+
        verbosestream<<"dedicated_server_loop()"<<std::endl;
 
        IntervalLimiter m_profiler_interval;
@@ -5130,6 +5512,10 @@ void dedicated_server_loop(Server &server, bool &kill)
                if(server.getShutdownRequested() || kill)
                {
                        infostream<<"Dedicated server quitting"<<std::endl;
+#if USE_CURL
+                       if(g_settings->getBool("server_announce") == true)
+                               ServerList::sendAnnounce("delete");
+#endif
                        break;
                }