Update inventory texture too
[oweals/minetest.git] / src / server.cpp
index 14c019d52fc8e6e944756e11d672d20e9e4daed9..44c66447c3eda6a35bde5f5e283759ca59bbe10d 100644 (file)
@@ -39,6 +39,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "profiler.h"
 #include "log.h"
+#include "script.h"
+#include "scriptapi.h"
+#include "nodedef.h"
+#include "tooldef.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -185,7 +189,7 @@ void * EmergeThread::Thread()
                                        <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
                                        <<"only_from_disk="<<only_from_disk<<std::endl;
                
-               ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
+               ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
                        
                //core::map<v3s16, MapBlock*> changed_blocks;
                //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
@@ -250,7 +254,7 @@ void * EmergeThread::Thread()
                                        MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
                                        
                                        // Activate objects and stuff
-                                       m_server->m_env.activateBlock(block, 3600);
+                                       m_server->m_env->activateBlock(block, 3600);
                                }
                        }
                        else
@@ -354,11 +358,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        
        // Increment timers
        m_nothing_to_send_pause_timer -= dtime;
+       m_nearest_unsent_reset_timer += dtime;
        
        if(m_nothing_to_send_pause_timer >= 0)
        {
-               // Keep this reset
-               m_nearest_unsent_reset_timer = 0;
                return;
        }
 
@@ -372,7 +375,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
        //TimeTaker timer("RemoteClient::GetNextBlocks");
        
-       Player *player = server->m_env.getPlayer(peer_id);
+       Player *player = server->m_env->getPlayer(peer_id);
 
        assert(player != NULL);
 
@@ -410,17 +413,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        /*infostream<<"m_nearest_unsent_reset_timer="
                        <<m_nearest_unsent_reset_timer<<std::endl;*/
                        
-       // This has to be incremented only when the nothing to send pause
-       // is not active
-       m_nearest_unsent_reset_timer += dtime;
-       
-       // Reset periodically to avoid possible bugs or other mishaps
-       if(m_nearest_unsent_reset_timer > 10.0)
+       // Reset periodically to workaround for some bugs or stuff
+       if(m_nearest_unsent_reset_timer > 20.0)
        {
                m_nearest_unsent_reset_timer = 0;
                m_nearest_unsent_d = 0;
-               /*infostream<<"Resetting m_nearest_unsent_d for "
-                               <<server->getPlayerName(peer_id)<<std::endl;*/
+               //infostream<<"Resetting m_nearest_unsent_d for "
+               //              <<server->getPlayerName(peer_id)<<std::endl;
        }
 
        //s16 last_nearest_unsent_d = m_nearest_unsent_d;
@@ -463,22 +462,24 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
        s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
        
        // Don't loop very much at a time
-       if(d_max > d_start+1)
-               d_max = d_start+1;
+       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;
 
-       bool sending_something = false;
-
-       bool no_blocks_found_for_sending = true;
-
+       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;
                
                /*
@@ -550,12 +551,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
                                        generate = false;*/
 
-                               // Limit the send area vertically to 2/3
-                               if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
+                               // Limit the send area vertically to 1/2
+                               if(abs(p.Y - center.Y) > d_max / 2)
                                        continue;
                        }
 
-#if 1
+#if 0
                        /*
                                If block is far away, don't generate it unless it is
                                near ground level.
@@ -575,7 +576,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                        MAP_BLOCKSIZE*p.Z);
                                
                                // Get ground height in nodes
-                               s16 gh = server->m_env.getServerMap().findGroundLevel(
+                               s16 gh = server->m_env->getServerMap().findGroundLevel(
                                                p2d_nodes_center);
 
                                // If differs a lot, don't generate
@@ -588,7 +589,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 #endif
 
                        //infostream<<"d="<<d<<std::endl;
-                       
+#if 1
                        /*
                                Don't generate or send if not in sight
                                FIXME This only works if the client uses a small enough
@@ -600,7 +601,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        {
                                continue;
                        }
-                       
+#endif
                        /*
                                Don't send already sent blocks
                        */
@@ -614,7 +615,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        /*
                                Check if map has this block
                        */
-                       MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
+                       MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
                        
                        bool surely_not_found_on_disk = false;
                        bool block_is_invalid = false;
@@ -643,7 +644,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
 #if 0
                                v2s16 p2d(p.X, p.Z);
-                               ServerMap *map = (ServerMap*)(&server->m_env.getMap());
+                               ServerMap *map = (ServerMap*)(&server->m_env->getMap());
                                v2s16 chunkpos = map->sector_to_chunk(p2d);
                                if(map->chunkNonVolatile(chunkpos) == false)
                                        block_is_invalid = true;
@@ -658,7 +659,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                        Block is near ground level if night-time mesh
                                        differs from day-time mesh.
                                */
-                               if(d > 3)
+                               if(d >= 4)
                                {
                                        if(block->dayNightDiffed() == false)
                                                continue;
@@ -676,18 +677,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                continue;
                        }
 
-                       /*
-                               Record the lowest d from which a block has been
-                               found being not sent and possibly to exist
-                       */
-                       if(no_blocks_found_for_sending)
-                       {
-                               if(generate == true)
-                                       new_nearest_unsent_d = d;
-                       }
-
-                       no_blocks_found_for_sending = false;
-                                       
                        /*
                                Add inexistent block to emerge queue.
                        */
@@ -697,7 +686,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                // Allow only one block in emerge queue
                                //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
                                // Allow two blocks in queue per client
-                               if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
+                               //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
+                               if(server->m_emerge_queue.peerItemCount(peer_id) < 25)
                                {
                                        //infostream<<"Adding block to emerge queue"<<std::endl;
                                        
@@ -709,55 +699,63 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                        
                                        server->m_emerge_queue.addBlock(peer_id, p, flags);
                                        server->m_emergethread.trigger();
+
+                                       if(nearest_emerged_d == -1)
+                                               nearest_emerged_d = d;
+                               } else {
+                                       if(nearest_emergefull_d == -1)
+                                               nearest_emergefull_d = d;
                                }
                                
                                // get next one.
                                continue;
                        }
 
+                       if(nearest_sent_d == -1)
+                               nearest_sent_d = d;
+
                        /*
                                Add block to send queue
                        */
 
+                       /*errorstream<<"sending from d="<<d<<" to "
+                                       <<server->getPlayerName(peer_id)<<std::endl;*/
+
                        PrioritySortedBlockTransfer q((float)d, p, peer_id);
 
                        dest.push_back(q);
 
                        num_blocks_selected += 1;
-                       sending_something = true;
                }
        }
 queue_full_break:
 
        //infostream<<"Stopped at "<<d<<std::endl;
        
-       if(no_blocks_found_for_sending)
-       {
-               if(queue_is_full == false)
-                       new_nearest_unsent_d = d;
+       // If nothing was found for sending and nothing was queued for
+       // emerging, continue next time browsing from here
+       if(nearest_emerged_d != -1){
+               new_nearest_unsent_d = nearest_emerged_d;
+       } else if(nearest_emergefull_d != -1){
+               new_nearest_unsent_d = nearest_emergefull_d;
+       } else {
+               if(d > g_settings->getS16("max_block_send_distance")){
+                       new_nearest_unsent_d = 0;
+                       m_nothing_to_send_pause_timer = 2.0;
+                       /*infostream<<"GetNextBlocks(): d wrapped around for "
+                                       <<server->getPlayerName(peer_id)
+                                       <<"; setting to 0 and pausing"<<std::endl;*/
+               } else {
+                       if(nearest_sent_d != -1)
+                               new_nearest_unsent_d = nearest_sent_d;
+                       else
+                               new_nearest_unsent_d = d;
+               }
        }
 
        if(new_nearest_unsent_d != -1)
                m_nearest_unsent_d = new_nearest_unsent_d;
 
-       if(sending_something == false)
-       {
-               m_nothing_to_send_counter++;
-               if((s16)m_nothing_to_send_counter >=
-                               g_settings->getS16("max_block_send_distance"))
-               {
-                       // Pause time in seconds
-                       m_nothing_to_send_pause_timer = 1.0;
-                       /*infostream<<"nothing to send to "
-                                       <<server->getPlayerName(peer_id)
-                                       <<" (d="<<d<<")"<<std::endl;*/
-               }
-       }
-       else
-       {
-               m_nothing_to_send_counter = 0;
-       }
-
        /*timer_result = timer.stop(true);
        if(timer_result != 0)
                infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
@@ -808,7 +806,7 @@ void RemoteClient::SendObjectData(
        */
        
        // Get connected players
-       core::list<Player*> players = server->m_env.getPlayers(true);
+       core::list<Player*> players = server->m_env->getPlayers(true);
 
        // Write player count
        u16 playercount = players.size();
@@ -946,6 +944,35 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
        return checksum;
 }
 
+struct ModSpec
+{
+       std::string name;
+       std::string path;
+
+       ModSpec(const std::string &name_="", const std::string path_=""):
+               name(name_),
+               path(path_)
+       {}
+};
+
+static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
+{
+       core::list<ModSpec> mods;
+       for(core::list<std::string>::Iterator i = modspaths.begin();
+                       i != modspaths.end(); i++){
+               std::string modspath = *i;
+               std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
+               for(u32 j=0; j<dirlist.size(); j++){
+                       if(!dirlist[j].dir)
+                               continue;
+                       std::string modname = dirlist[j].name;
+                       std::string modpath = modspath + DIR_DELIM + modname;
+                       mods.push_back(ModSpec(modname, modpath));
+               }
+       }
+       return mods;
+}
+
 /*
        Server
 */
@@ -954,10 +981,13 @@ Server::Server(
                std::string mapsavedir,
                std::string configpath
        ):
-       m_env(new ServerMap(mapsavedir), this),
+       m_env(NULL),
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
-       m_authmanager(mapsavedir+"/auth.txt"),
-       m_banmanager(mapsavedir+"/ipban.txt"),
+       m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
+       m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
+       m_lua(NULL),
+       m_toolmgr(createToolDefManager()),
+       m_nodemgr(createNodeDefManager(NULL)),
        m_thread(this),
        m_emergethread(this),
        m_time_counter(0),
@@ -979,20 +1009,60 @@ Server::Server(
        m_con_mutex.Init();
        m_step_dtime_mutex.Init();
        m_step_dtime = 0.0;
+
+       JMutexAutoLock envlock(m_env_mutex);
+       JMutexAutoLock conlock(m_con_mutex);
+
+       infostream<<"m_nodemgr="<<m_nodemgr<<std::endl;
+       
+       // Initialize default node definitions
+       content_mapnode_init(NULL, m_nodemgr);
+       
+       // Add default global mod path
+       m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
+
+       // Initialize scripting
+       
+       infostream<<"Server: Initializing scripting"<<std::endl;
+       m_lua = script_init();
+       assert(m_lua);
+       // Export API
+       scriptapi_export(m_lua, this);
+       // Load and run scripts
+       core::list<ModSpec> mods = getMods(m_modspaths);
+       for(core::list<ModSpec>::Iterator i = mods.begin();
+                       i != mods.end(); i++){
+               ModSpec mod = *i;
+               infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
+               std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
+               bool success = script_load(m_lua, scriptpath.c_str());
+               if(!success){
+                       errorstream<<"Server: Failed to load and run "
+                                       <<scriptpath<<std::endl;
+                       assert(0);
+               }
+       }
+       
+       // Initialize Environment
+       
+       m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
+
+       // Give environment reference to scripting api
+       scriptapi_add_environment(m_lua, m_env);
        
        // Register us to receive map edit events
-       m_env.getMap().addEventReceiver(this);
+       m_env->getMap().addEventReceiver(this);
 
        // If file exists, load environment metadata
-       if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
+       if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
        {
                infostream<<"Server: Loading environment metadata"<<std::endl;
-               m_env.loadMeta(m_mapsavedir);
+               m_env->loadMeta(m_mapsavedir);
        }
 
        // Load players
        infostream<<"Server: Loading players"<<std::endl;
-       m_env.deSerializePlayers(m_mapsavedir);
+       m_env->deSerializePlayers(m_mapsavedir);
 }
 
 Server::~Server()
@@ -1035,13 +1105,13 @@ Server::~Server()
                        Save players
                */
                infostream<<"Server: Saving players"<<std::endl;
-               m_env.serializePlayers(m_mapsavedir);
+               m_env->serializePlayers(m_mapsavedir);
 
                /*
                        Save environment metadata
                */
                infostream<<"Server: Saving environment metadata"<<std::endl;
-               m_env.saveMeta(m_mapsavedir);
+               m_env->saveMeta(m_mapsavedir);
        }
                
        /*
@@ -1064,13 +1134,23 @@ Server::~Server()
                        {
                                u16 peer_id = i.getNode()->getKey();
                                JMutexAutoLock envlock(m_env_mutex);
-                               m_env.removePlayer(peer_id);
+                               m_env->removePlayer(peer_id);
                        }*/
                        
                        // Delete client
                        delete i.getNode()->getValue();
                }
        }
+
+       // Delete Environment
+       delete m_env;
+
+       delete m_toolmgr;
+       delete m_nodemgr;
+       
+       // Deinitialize scripting
+       infostream<<"Server: Deinitializing scripting"<<std::endl;
+       script_deinit(m_lua);
 }
 
 void Server::start(unsigned short port)
@@ -1080,7 +1160,7 @@ void Server::start(unsigned short port)
        m_thread.stop();
        
        // Initialize connection
-       m_con.setTimeoutMs(30);
+       m_con.SetTimeoutMs(30);
        m_con.Serve(port);
 
        // Start thread
@@ -1179,7 +1259,7 @@ void Server::AsyncRunStep()
                u32 units = (u32)(m_time_counter*speed);
                m_time_counter -= (f32)units / speed;
                
-               m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
+               m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
                
                //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
 
@@ -1200,10 +1280,10 @@ void Server::AsyncRunStep()
                                i.atEnd() == false; i++)
                        {
                                RemoteClient *client = i.getNode()->getValue();
-                               //Player *player = m_env.getPlayer(client->peer_id);
+                               //Player *player = m_env->getPlayer(client->peer_id);
                                
                                SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-                                               m_env.getTimeOfDay());
+                                               m_env->getTimeOfDay());
                                // Send as reliable
                                m_con.Send(client->peer_id, 0, data, true);
                        }
@@ -1215,7 +1295,7 @@ void Server::AsyncRunStep()
                // Step environment
                ScopeProfiler sp(g_profiler, "SEnv step");
                ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
-               m_env.step(dtime);
+               m_env->step(dtime);
        }
                
        const float map_timer_and_unload_dtime = 5.15;
@@ -1224,7 +1304,7 @@ void Server::AsyncRunStep()
                JMutexAutoLock lock(m_env_mutex);
                // Run Map's timers and unload unused data
                ScopeProfiler sp(g_profiler, "Server: map timer and unload");
-               m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
+               m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
                                g_settings->getFloat("server_unload_unused_data_timeout"));
        }
        
@@ -1245,13 +1325,13 @@ void Server::AsyncRunStep()
                ScopeProfiler sp(g_profiler, "Server: liquid transform");
 
                core::map<v3s16, MapBlock*> modified_blocks;
-               m_env.getMap().transformLiquids(modified_blocks);
+               m_env->getMap().transformLiquids(modified_blocks);
 #if 0          
                /*
                        Update lighting
                */
                core::map<v3s16, MapBlock*> lighting_modified_blocks;
-               ServerMap &map = ((ServerMap&)m_env.getMap());
+               ServerMap &map = ((ServerMap&)m_env->getMap());
                map.updateLighting(modified_blocks, lighting_modified_blocks);
                
                // Add blocks modified by lighting to modified_blocks
@@ -1301,7 +1381,7 @@ void Server::AsyncRunStep()
                        {
                                //u16 peer_id = i.getNode()->getKey();
                                RemoteClient *client = i.getNode()->getValue();
-                               Player *player = m_env.getPlayer(client->peer_id);
+                               Player *player = m_env->getPlayer(client->peer_id);
                                if(player==NULL)
                                        continue;
                                infostream<<"* "<<player->getName()<<"\t";
@@ -1332,7 +1412,7 @@ void Server::AsyncRunStep()
                        i.atEnd() == false; i++)
                {
                        RemoteClient *client = i.getNode()->getValue();
-                       Player *player = m_env.getPlayer(client->peer_id);
+                       Player *player = m_env->getPlayer(client->peer_id);
                        if(player==NULL)
                        {
                                // This can happen if the client timeouts somehow
@@ -1345,9 +1425,9 @@ void Server::AsyncRunStep()
 
                        core::map<u16, bool> removed_objects;
                        core::map<u16, bool> added_objects;
-                       m_env.getRemovedActiveObjects(pos, radius,
+                       m_env->getRemovedActiveObjects(pos, radius,
                                        client->m_known_objects, removed_objects);
-                       m_env.getAddedActiveObjects(pos, radius,
+                       m_env->getAddedActiveObjects(pos, radius,
                                        client->m_known_objects, added_objects);
                        
                        // Ignore if nothing happened
@@ -1370,7 +1450,7 @@ void Server::AsyncRunStep()
                        {
                                // Get object
                                u16 id = i.getNode()->getKey();
-                               ServerActiveObject* obj = m_env.getActiveObject(id);
+                               ServerActiveObject* obj = m_env->getActiveObject(id);
 
                                // Add to data buffer for sending
                                writeU16((u8*)buf, i.getNode()->getKey());
@@ -1392,7 +1472,7 @@ void Server::AsyncRunStep()
                        {
                                // Get object
                                u16 id = i.getNode()->getKey();
-                               ServerActiveObject* obj = m_env.getActiveObject(id);
+                               ServerActiveObject* obj = m_env->getActiveObject(id);
                                
                                // Get object type
                                u8 type = ACTIVEOBJECT_TYPE_INVALID;
@@ -1458,7 +1538,7 @@ void Server::AsyncRunStep()
                        }
                }
                
-               m_env.setKnownActiveObjects(whatever);
+               m_env->setKnownActiveObjects(whatever);
 #endif
 
        }
@@ -1470,7 +1550,7 @@ void Server::AsyncRunStep()
                JMutexAutoLock envlock(m_env_mutex);
                JMutexAutoLock conlock(m_con_mutex);
 
-               ScopeProfiler sp(g_profiler, "Server: sending object messages");
+               //ScopeProfiler sp(g_profiler, "Server: sending object messages");
 
                // Key = object id
                // Value = data sent by object
@@ -1479,7 +1559,7 @@ void Server::AsyncRunStep()
                // Get active object messages from environment
                for(;;)
                {
-                       ActiveObjectMessage aom = m_env.getActiveObjectMessage();
+                       ActiveObjectMessage aom = m_env->getActiveObjectMessage();
                        if(aom.id == 0)
                                break;
                        
@@ -1668,7 +1748,7 @@ void Server::AsyncRunStep()
                                {
                                        v3s16 p = i.getNode()->getKey();
                                        modified_blocks2.insert(p,
-                                                       m_env.getMap().getBlockNoCreateNoEx(p));
+                                                       m_env->getMap().getBlockNoCreateNoEx(p));
                                }
                                // Set blocks not sent
                                for(core::list<u16>::Iterator
@@ -1710,7 +1790,7 @@ void Server::AsyncRunStep()
                        JMutexAutoLock lock1(m_env_mutex);
                        JMutexAutoLock lock2(m_con_mutex);
 
-                       ScopeProfiler sp(g_profiler, "Server: sending player positions");
+                       //ScopeProfiler sp(g_profiler, "Server: sending player positions");
 
                        SendObjectData(counter);
 
@@ -1755,15 +1835,15 @@ void Server::AsyncRunStep()
                        JMutexAutoLock lock(m_env_mutex);
 
                        /*// Unload unused data (delete from memory)
-                       m_env.getMap().unloadUnusedData(
+                       m_env->getMap().unloadUnusedData(
                                        g_settings->getFloat("server_unload_unused_sectors_timeout"));
                                        */
-                       /*u32 deleted_count = m_env.getMap().unloadUnusedData(
+                       /*u32 deleted_count = m_env->getMap().unloadUnusedData(
                                        g_settings->getFloat("server_unload_unused_sectors_timeout"));
                                        */
 
                        // Save only changed parts
-                       m_env.getMap().save(true);
+                       m_env->getMap().save(true);
 
                        /*if(deleted_count > 0)
                        {
@@ -1772,10 +1852,10 @@ void Server::AsyncRunStep()
                        }*/
 
                        // Save players
-                       m_env.serializePlayers(m_mapsavedir);
+                       m_env->serializePlayers(m_mapsavedir);
                        
                        // Save environment metadata
-                       m_env.saveMeta(m_mapsavedir);
+                       m_env->saveMeta(m_mapsavedir);
                }
        }
 }
@@ -1783,14 +1863,13 @@ void Server::AsyncRunStep()
 void Server::Receive()
 {
        DSTACK(__FUNCTION_NAME);
-       u32 data_maxsize = 10000;
-       Buffer<u8> data(data_maxsize);
+       SharedBuffer<u8> data;
        u16 peer_id;
        u32 datasize;
        try{
                {
                        JMutexAutoLock conlock(m_con_mutex);
-                       datasize = m_con.Receive(peer_id, *data, data_maxsize);
+                       datasize = m_con.Receive(peer_id, data);
                }
 
                // This has to be called so that the client list gets synced
@@ -1818,7 +1897,7 @@ void Server::Receive()
                                <<" has apparently closed connection. "
                                <<"Removing player."<<std::endl;
 
-               m_env.removePlayer(peer_id);*/
+               m_env->removePlayer(peer_id);*/
        }
 }
 
@@ -1829,9 +1908,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        JMutexAutoLock envlock(m_env_mutex);
        JMutexAutoLock conlock(m_con_mutex);
        
-       con::Peer *peer;
        try{
-               peer = m_con.GetPeer(peer_id);
+               Address address = m_con.GetPeerAddress(peer_id);
+
+               // drop player if is ip is banned
+               if(m_banmanager.isIpBanned(address.serializeString())){
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Your ip is banned. Banned name was "
+                                       +narrow_to_wide(m_banmanager.getBanName(
+                                               address.serializeString())));
+                       m_con.DeletePeer(peer_id);
+                       return;
+               }
        }
        catch(con::PeerNotFoundException &e)
        {
@@ -1840,17 +1928,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                return;
        }
 
-       // drop player if is ip is banned
-       if(m_banmanager.isIpBanned(peer->address.serializeString())){
-               SendAccessDenied(m_con, peer_id,
-                               L"Your ip is banned. Banned name was "
-                               +narrow_to_wide(m_banmanager.getBanName(
-                                       peer->address.serializeString())));
-               m_con.deletePeer(peer_id, false);
-               return;
-       }
-       
-       u8 peer_ser_ver = getClient(peer->id)->serialization_version;
+       u8 peer_ser_ver = getClient(peer_id)->serialization_version;
 
        try
        {
@@ -1871,7 +1949,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
 
                infostream<<"Server: Got TOSERVER_INIT from "
-                               <<peer->id<<std::endl;
+                               <<peer_id<<std::endl;
 
                // First byte after command is maximum supported
                // serialization version
@@ -1884,7 +1962,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        deployed = SER_FMT_VER_INVALID;
 
                //peer->serialization_version = deployed;
-               getClient(peer->id)->pending_serialization_version = deployed;
+               getClient(peer_id)->pending_serialization_version = deployed;
                
                if(deployed == SER_FMT_VER_INVALID)
                {
@@ -1906,7 +1984,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
                }
 
-               getClient(peer->id)->net_proto_version = net_proto_version;
+               getClient(peer_id)->net_proto_version = net_proto_version;
 
                if(net_proto_version == 0)
                {
@@ -2034,7 +2112,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        writeU16(&reply[0], TOCLIENT_INIT);
                        writeU8(&reply[2], deployed);
                        writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
-                       writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
+                       writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
                        
                        // Send as reliable
                        m_con.Send(peer_id, 0, reply, true);
@@ -2051,27 +2129,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        if(command == TOSERVER_INIT2)
        {
                infostream<<"Server: Got TOSERVER_INIT2 from "
-                               <<peer->id<<std::endl;
+                               <<peer_id<<std::endl;
 
 
-               getClient(peer->id)->serialization_version
-                               = getClient(peer->id)->pending_serialization_version;
+               getClient(peer_id)->serialization_version
+                               = getClient(peer_id)->pending_serialization_version;
 
                /*
                        Send some initialization data
                */
+
+               // Send textures
+               SendTextures(peer_id);
+               
+               // Send tool definitions
+               SendToolDef(m_con, peer_id, m_toolmgr);
                
                // Send player info to all players
                SendPlayerInfos();
 
                // Send inventory to player
-               UpdateCrafting(peer->id);
-               SendInventory(peer->id);
+               UpdateCrafting(peer_id);
+               SendInventory(peer_id);
 
                // Send player items to all players
                SendPlayerItems();
 
-               Player *player = m_env.getPlayer(peer_id);
+               Player *player = m_env->getPlayer(peer_id);
 
                // Send HP
                SendPlayerHP(player);
@@ -2079,8 +2163,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Send time of day
                {
                        SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-                                       m_env.getTimeOfDay());
-                       m_con.Send(peer->id, 0, data, true);
+                                       m_env->getTimeOfDay());
+                       m_con.Send(peer_id, 0, data, true);
                }
                
                // Send information about server to player in chat
@@ -2089,7 +2173,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                // Send information about joining in chat
                {
                        std::wstring name = L"unknown";
-                       Player *player = m_env.getPlayer(peer_id);
+                       Player *player = m_env->getPlayer(peer_id);
                        if(player != NULL)
                                name = narrow_to_wide(player->getName());
                        
@@ -2101,7 +2185,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                }
                
                // Warnings about protocol version can be issued here
-               if(getClient(peer->id)->net_proto_version < PROTOCOL_VERSION)
+               if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
                {
                        SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
                }
@@ -2125,7 +2209,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                if(client->serialization_version == SER_FMT_VER_INVALID)
                                        continue;
                                // Get player
-                               Player *player = m_env.getPlayer(client->peer_id);
+                               Player *player = m_env->getPlayer(client->peer_id);
                                if(!player)
                                        continue;
                                // Get name of player
@@ -2147,7 +2231,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                return;
        }
        
-       Player *player = m_env.getPlayer(peer_id);
+       Player *player = m_env->getPlayer(peer_id);
 
        if(player == NULL){
                infostream<<"Server::ProcessData(): Cancelling: "
@@ -2255,7 +2339,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                u16 id = readS16(&data[3]);
                u16 item_i = readU16(&data[5]);
        
-               ServerActiveObject *obj = m_env.getActiveObject(id);
+               ServerActiveObject *obj = m_env->getActiveObject(id);
 
                if(obj == NULL)
                {
@@ -2269,10 +2353,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
                
                //TODO: Check that object is reasonably close
+       
+               // Get ServerRemotePlayer
+               ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+
+               // Update wielded item
+               srp->wieldItem(item_i);
                
-               // Left click, pick object up (usually)
+               // Left click, pick/punch
                if(button == 0)
                {
+                       actionstream<<player->getName()<<" punches object "
+                                       <<obj->getId()<<std::endl;
+                       
+                       // Do stuff
+                       obj->punch(srp);
+                       
+#if 0
                        /*
                                Try creating inventory item
                        */
@@ -2341,6 +2438,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        SendInventory(player->peer_id);
                                }
                        }
+#endif
                }
                // Right click, do something with object
                if(button == 1)
@@ -2348,18 +2446,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        actionstream<<player->getName()<<" right clicks object "
                                        <<obj->getId()<<std::endl;
 
-                       // Track hp changes super-crappily
-                       u16 oldhp = player->hp;
-                       
                        // Do stuff
-                       obj->rightClick(player);
-                       
-                       // Send back stuff
-                       if(player->hp != oldhp)
-                       {
-                               SendPlayerHP(player);
-                       }
+                       obj->rightClick(srp);
                }
+
+               /*
+                       Update player state to client
+               */
+               SendPlayerHP(player);
+               UpdateCrafting(player->peer_id);
+               SendInventory(player->peer_id);
        }
        else if(command == TOSERVER_GROUND_ACTION)
        {
@@ -2408,7 +2504,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                else if(action == 2)
                {
 #if 0
-                       RemoteClient *client = getClient(peer->id);
+                       RemoteClient *client = getClient(peer_id);
                        JMutexAutoLock digmutex(client->m_dig_mutex);
                        client->m_dig_tool_item = -1;
 #endif
@@ -2429,16 +2525,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                        try
                        {
-                               MapNode n = m_env.getMap().getNode(p_under);
+                               MapNode n = m_env->getMap().getNode(p_under);
                                // Get mineral
-                               mineral = n.getMineral();
+                               mineral = n.getMineral(m_nodemgr);
                                // Get material at position
                                material = n.getContent();
                                // If not yet cancelled
                                if(cannot_remove_node == false)
                                {
                                        // If it's not diggable, do nothing
-                                       if(content_diggable(material) == false)
+                                       if(m_nodemgr->get(material).diggable == false)
                                        {
                                                infostream<<"Server: Not finishing digging: "
                                                                <<"Node not diggable"
@@ -2450,7 +2546,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                if(cannot_remove_node == false)
                                {
                                        // Get node metadata
-                                       NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
+                                       NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
                                        if(meta && meta->nodeRemovalDisabled() == true)
                                        {
                                                infostream<<"Server: Not finishing digging: "
@@ -2531,8 +2627,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                std::string toolname = titem->getToolName();
 
                                                // Get digging properties for material and tool
+                                               ToolDiggingProperties tp =
+                                                               m_toolmgr->getDiggingProperties(toolname);
                                                DiggingProperties prop =
-                                                               getDiggingProperties(material, toolname);
+                                                               getDiggingProperties(material, &tp, m_nodemgr);
 
                                                if(prop.diggable == false)
                                                {
@@ -2557,16 +2655,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                InventoryItem *item = NULL;
 
                                if(mineral != MINERAL_NONE)
-                                       item = getDiggedMineralItem(mineral);
+                                       item = getDiggedMineralItem(mineral, this);
                                
                                // If not mineral
                                if(item == NULL)
                                {
-                                       std::string &dug_s = content_features(material).dug_item;
+                                       const std::string &dug_s = m_nodemgr->get(material).dug_item;
                                        if(dug_s != "")
                                        {
                                                std::istringstream is(dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is);
+                                               item = InventoryItem::deSerialize(is, this);
                                        }
                                }
                                
@@ -2583,25 +2681,25 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                item = NULL;
 
                                if(mineral != MINERAL_NONE)
-                                 item = getDiggedMineralItem(mineral);
+                                 item = getDiggedMineralItem(mineral, this);
                        
                                // If not mineral
                                if(item == NULL)
                                {
-                                       std::string &extra_dug_s = content_features(material).extra_dug_item;
-                                       s32 extra_rarity = content_features(material).extra_dug_item_rarity;
+                                       const std::string &extra_dug_s = m_nodemgr->get(material).extra_dug_item;
+                                       s32 extra_rarity = m_nodemgr->get(material).extra_dug_item_rarity;
                                        if(extra_dug_s != "" && extra_rarity != 0
                                           && myrand() % extra_rarity == 0)
                                        {
-                                               std::istringstream is(extra_dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is);
+                                               std::istringstream is(extra_dug_s, std::ios::binary);
+                                               item = InventoryItem::deSerialize(is, this);
                                        }
                                }
                        
                                if(item != NULL)
                                {
-                                       // Add a item to inventory
-                                       player->inventory.addItem("main", item);
+                                       // Add a item to inventory
+                                       player->inventory.addItem("main", item);
 
                                        // Send inventory
                                        UpdateCrafting(player->peer_id);
@@ -2616,7 +2714,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        {
                                MapEditEventIgnorer ign(&m_ignore_map_edit_events);
 
-                               m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
+                               m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
                        }
                        /*
                                Set blocks not sent to far players
@@ -2657,7 +2755,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        {
                                try{
                                        // Don't add a node if this is not a free space
-                                       MapNode n2 = m_env.getMap().getNode(p_over);
+                                       MapNode n2 = m_env->getMap().getNode(p_over);
                                        bool no_enough_privs =
                                                        ((getPlayerPrivs(player) & PRIV_BUILD)==0);
                                        if(no_enough_privs)
@@ -2665,7 +2763,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                        <<" because privileges are "<<getPlayerPrivs(player)
                                                        <<std::endl;
 
-                                       if(content_features(n2).buildable_to == false
+                                       if(m_nodemgr->get(n2).buildable_to == false
                                                || no_enough_privs)
                                        {
                                                // Client probably has wrong data.
@@ -2691,7 +2789,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                }
 
                                // Reset build time counter
-                               getClient(peer->id)->m_time_from_building = 0.0;
+                               getClient(peer_id)->m_time_from_building = 0.0;
                                
                                // Create node data
                                MaterialItem *mitem = (MaterialItem*)item;
@@ -2703,11 +2801,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                <<" at "<<PP(p_under)<<std::endl;
                        
                                // Calculate direction for wall mounted stuff
-                               if(content_features(n).wall_mounted)
+                               if(m_nodemgr->get(n).wall_mounted)
                                        n.param2 = packDir(p_under - p_over);
 
                                // Calculate the direction for furnaces and chests and stuff
-                               if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
+                               if(m_nodemgr->get(n).param_type == CPT_FACEDIR_SIMPLE)
                                {
                                        v3f playerpos = player->getPosition();
                                        v3f blockpos = intToFloat(p_over, BS) - playerpos;
@@ -2758,7 +2856,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        MapEditEventIgnorer ign(&m_ignore_map_edit_events);
 
                                        std::string p_name = std::string(player->getName());
-                                       m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
+                                       m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
                                }
                                /*
                                        Set blocks not sent to far players
@@ -2800,7 +2898,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        Check that the block is loaded so that the item
                                        can properly be added to the static list too
                                */
-                               MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
+                               MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
                                if(block==NULL)
                                {
                                        infostream<<"Error while placing object: "
@@ -2823,15 +2921,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Calculate a position for it
                                v3f pos = intToFloat(p_over, BS);
                                //pos.Y -= BS*0.45;
-                               pos.Y -= BS*0.25; // let it drop a bit
+                               /*pos.Y -= BS*0.25; // let it drop a bit
                                // Randomize a bit
                                pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-                               pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
+                               pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
 
                                /*
                                        Create the object
                                */
-                               ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
+                               ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
 
                                if(obj == NULL)
                                {
@@ -2845,7 +2943,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                        <<" at "<<PP(p_over)<<std::endl;
                                
                                        // Add the object to the environment
-                                       m_env.addActiveObject(obj);
+                                       m_env->addActiveObject(obj);
                                        
                                        infostream<<"Placed object"<<std::endl;
 
@@ -2932,7 +3030,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        text += (char)buf[0];
                }
 
-               NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
                if(!meta)
                        return;
                if(meta->typeId() != CONTENT_SIGN_WALL)
@@ -2944,7 +3042,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                <<" at "<<PP(p)<<std::endl;
                                
                v3s16 blockpos = getNodeBlockPos(p);
-               MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
+               MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
                if(block)
                {
                        block->setChangedFlag();
@@ -3060,7 +3158,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                p.X = stoi(fn.next(","));
                                                p.Y = stoi(fn.next(","));
                                                p.Z = stoi(fn.next(","));
-                                               NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
                                                if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
                                                        LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
                                                        if (lcm->getOwner() != player->getName())
@@ -3078,7 +3176,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                p.X = stoi(fn.next(","));
                                                p.Y = stoi(fn.next(","));
                                                p.Z = stoi(fn.next(","));
-                                               NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
                                                if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
                                                        LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
                                                        if (lcm->getOwner() != player->getName())
@@ -3161,7 +3259,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                str_split(message, L' '),
                                paramstring,
                                this,
-                               &m_env,
+                               m_env,
                                player,
                                privs);
 
@@ -3364,7 +3462,7 @@ Inventory* Server::getInventory(InventoryContext *c, std::string id)
                p.X = stoi(fn.next(","));
                p.Y = stoi(fn.next(","));
                p.Z = stoi(fn.next(","));
-               NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
                if(meta)
                        return meta->getInventory();
                infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
@@ -3397,7 +3495,7 @@ void Server::inventoryModified(InventoryContext *c, std::string id)
                p.Z = stoi(fn.next(","));
                v3s16 blockpos = getNodeBlockPos(p);
 
-               NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
                if(meta)
                        meta->inventoryModified();
 
@@ -3423,7 +3521,7 @@ core::list<PlayerInfo> Server::getPlayerInfo()
        
        core::list<PlayerInfo> list;
 
-       core::list<Player*> players = m_env.getPlayers();
+       core::list<Player*> players = m_env->getPlayers();
        
        core::list<Player*>::Iterator i;
        for(i = players.begin();
@@ -3434,11 +3532,10 @@ core::list<PlayerInfo> Server::getPlayerInfo()
                Player *player = *i;
 
                try{
-                       con::Peer *peer = m_con.GetPeer(player->peer_id);
-                       // Copy info from peer to info struct
-                       info.id = peer->id;
-                       info.address = peer->address;
-                       info.avg_rtt = peer->avg_rtt;
+                       // 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)
                {
@@ -3536,6 +3633,29 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
        con.Send(peer_id, 0, data, true);
 }
 
+void Server::SendToolDef(con::Connection &con, u16 peer_id,
+               IToolDefManager *tooldef)
+{
+       DSTACK(__FUNCTION_NAME);
+       std::ostringstream os(std::ios_base::binary);
+
+       /*
+               u16 command
+               u32 length of the next item
+               serialized ToolDefManager
+       */
+       writeU16(os, TOCLIENT_TOOLDEF);
+       std::ostringstream tmp_os(std::ios::binary);
+       tooldef->serialize(tmp_os);
+       os<<serializeLongString(tmp_os.str());
+
+       // 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);
+}
+
 /*
        Non-static send methods
 */
@@ -3568,7 +3688,7 @@ void Server::SendPlayerInfos()
        //JMutexAutoLock envlock(m_env_mutex);
        
        // Get connected players
-       core::list<Player*> players = m_env.getPlayers(true);
+       core::list<Player*> players = m_env->getPlayers(true);
        
        u32 player_count = players.getSize();
        u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
@@ -3602,7 +3722,7 @@ void Server::SendInventory(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
        
-       Player* player = m_env.getPlayer(peer_id);
+       Player* player = m_env->getPlayer(peer_id);
        assert(player);
 
        /*
@@ -3659,7 +3779,7 @@ void Server::SendPlayerItems()
        DSTACK(__FUNCTION_NAME);
 
        std::ostringstream os(std::ios_base::binary);
-       core::list<Player *> players = m_env.getPlayers(true);
+       core::list<Player *> players = m_env->getPlayers(true);
 
        writeU16(os, TOCLIENT_PLAYERITEM);
        writeU16(os, players.size());
@@ -3788,7 +3908,7 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
                if(far_players)
                {
                        // Get player
-                       Player *player = m_env.getPlayer(client->peer_id);
+                       Player *player = m_env->getPlayer(client->peer_id);
                        if(player)
                        {
                                // If player is far away, only set modified blocks not sent
@@ -3829,7 +3949,7 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
                if(far_players)
                {
                        // Get player
-                       Player *player = m_env.getPlayer(client->peer_id);
+                       Player *player = m_env->getPlayer(client->peer_id);
                        if(player)
                        {
                                // If player is far away, only set modified blocks not sent
@@ -3969,7 +4089,7 @@ void Server::SendBlocks(float dtime)
                MapBlock *block = NULL;
                try
                {
-                       block = m_env.getMap().getBlockNoCreate(q.pos);
+                       block = m_env->getMap().getBlockNoCreate(q.pos);
                }
                catch(InvalidPositionException &e)
                {
@@ -3986,6 +4106,105 @@ void Server::SendBlocks(float dtime)
        }
 }
 
+struct SendableTexture
+{
+       std::string name;
+       std::string path;
+       std::string data;
+
+       SendableTexture(const std::string &name_="", const std::string path_="",
+                       const std::string &data_=""):
+               name(name_),
+               path(path_),
+               data(data_)
+       {}
+};
+
+void Server::SendTextures(u16 peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
+       
+       /* Read textures */
+       
+       core::list<SendableTexture> textures;
+       core::list<ModSpec> mods = getMods(m_modspaths);
+       for(core::list<ModSpec>::Iterator i = mods.begin();
+                       i != mods.end(); i++){
+               ModSpec mod = *i;
+               std::string texturepath = mod.path + DIR_DELIM + "textures";
+               std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
+               for(u32 j=0; j<dirlist.size(); j++){
+                       if(dirlist[j].dir) // Ignode dirs
+                               continue;
+                       std::string tname = dirlist[j].name;
+                       std::string tpath = texturepath + DIR_DELIM + tname;
+                       // Read data
+                       std::ifstream fis(tpath.c_str(), std::ios_base::binary);
+                       if(fis.good() == false){
+                               errorstream<<"Server::SendTextures(): Could not open \""
+                                               <<tname<<"\" for reading"<<std::endl;
+                               continue;
+                       }
+                       std::ostringstream tmp_os(std::ios_base::binary);
+                       bool bad = false;
+                       for(;;){
+                               char buf[1024];
+                               fis.read(buf, 1024);
+                               std::streamsize len = fis.gcount();
+                               tmp_os.write(buf, len);
+                               if(fis.eof())
+                                       break;
+                               if(!fis.good()){
+                                       bad = true;
+                                       break;
+                               }
+                       }
+                       if(bad){
+                               errorstream<<"Server::SendTextures(): Failed to read \""
+                                               <<tname<<"\""<<std::endl;
+                               continue;
+                       }
+                       errorstream<<"Server::SendTextures(): Loaded \""
+                                       <<tname<<"\""<<std::endl;
+                       // Put in list
+                       textures.push_back(SendableTexture(tname, tpath, tmp_os.str()));
+               }
+       }
+
+       /* Create and send packet */
+
+       /*
+               u16 command
+               u32 number of textures
+               for each texture {
+                       u16 length of name
+                       string name
+                       u32 length of data
+                       data
+               }
+       */
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOCLIENT_TEXTURES);
+       writeU32(os, textures.size());
+       
+       for(core::list<SendableTexture>::Iterator i = textures.begin();
+                       i != textures.end(); i++){
+               os<<serializeString(i->name);
+               os<<serializeLongString(i->data);
+       }
+       
+       // Make data buffer
+       std::string s = os.str();
+       infostream<<"Server::SendTextures(): number of textures: "
+                       <<textures.size()<<", data 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);
+}
+
 /*
        Something random
 */
@@ -4028,7 +4247,7 @@ void Server::HandlePlayerHP(Player *player, s16 damage)
 
 void Server::RespawnPlayer(Player *player)
 {
-       v3f pos = findSpawnPos(m_env.getServerMap());
+       v3f pos = findSpawnPos(m_env->getServerMap());
        player->setPosition(pos);
        player->hp = 20;
        SendMovePlayer(player);
@@ -4039,7 +4258,7 @@ void Server::UpdateCrafting(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
        
-       Player* player = m_env.getPlayer(peer_id);
+       Player* player = m_env->getPlayer(peer_id);
        assert(player);
 
        /*
@@ -4066,7 +4285,7 @@ void Server::UpdateCrafting(u16 peer_id)
                        }
                        
                        // Get result of crafting grid
-                       InventoryItem *result = craft_get_result(items);
+                       InventoryItem *result = craft_get_result(items, this);
                        if(result)
                                rlist->addItem(result);
                }
@@ -4105,7 +4324,7 @@ std::wstring Server::getStatusString()
                if(client->serialization_version == SER_FMT_VER_INVALID)
                        continue;
                // Get player
-               Player *player = m_env.getPlayer(client->peer_id);
+               Player *player = m_env->getPlayer(client->peer_id);
                // Get name of player
                std::wstring name = L"unknown";
                if(player != NULL)
@@ -4114,7 +4333,7 @@ std::wstring Server::getStatusString()
                os<<name<<L",";
        }
        os<<L"}";
-       if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
+       if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
                os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
        if(g_settings->get("motd") != "")
                os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
@@ -4130,18 +4349,46 @@ void Server::saveConfig()
 
 void Server::notifyPlayer(const char *name, const std::wstring msg)
 {
-       Player *player = m_env.getPlayer(name);
+       Player *player = m_env->getPlayer(name);
        if(!player)
                return;
        SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
 }
 
+void Server::notifyPlayers(const std::wstring msg)
+{
+       BroadcastChatMessage(msg);
+}
+
+// IGameDef interface
+// Under envlock
+IToolDefManager* Server::getToolDefManager()
+{
+       return m_toolmgr;
+}
+INodeDefManager* Server::getNodeDefManager()
+{
+       return m_nodemgr;
+}
+ITextureSource* Server::getTextureSource()
+{
+       return NULL;
+}
+
+IWritableToolDefManager* Server::getWritableToolDefManager()
+{
+       return m_toolmgr;
+}
+IWritableNodeDefManager* Server::getWritableNodeDefManager()
+{
+       return m_nodemgr;
+}
+
 v3f findSpawnPos(ServerMap &map)
 {
        //return v3f(50,50,50)*BS;
 
-       v2s16 nodepos;
-       s16 groundheight = 0;
+       v3s16 nodepos;
        
 #if 0
        nodepos = v2s16(0,0);
@@ -4154,13 +4401,11 @@ v3f findSpawnPos(ServerMap &map)
        {
                s32 range = 1 + i;
                // We're going to try to throw the player to this position
-               nodepos = v2s16(-range + (myrand()%(range*2)),
+               v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
                                -range + (myrand()%(range*2)));
-               v2s16 sectorpos = getNodeSectorPos(nodepos);
-               // Get sector (NOTE: Don't get because it's slow)
-               //m_env.getMap().emergeSector(sectorpos);
+               //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
                // Get ground height at point (fallbacks to heightmap function)
-               groundheight = map.findGroundLevel(nodepos);
+               s16 groundheight = map.findGroundLevel(nodepos2d);
                // Don't go underwater
                if(groundheight < WATER_LEVEL)
                {
@@ -4173,22 +4418,33 @@ v3f findSpawnPos(ServerMap &map)
                        //infostream<<"-> Underwater"<<std::endl;
                        continue;
                }
-
-               // Found a good place
-               //infostream<<"Searched through "<<i<<" places."<<std::endl;
-               break;
+               
+               nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
+               bool is_good = false;
+               s32 air_count = 0;
+               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){
+                               air_count++;
+                               if(air_count >= 2){
+                                       is_good = true;
+                                       nodepos.Y -= 1;
+                                       break;
+                               }
+                       }
+                       nodepos.Y++;
+               }
+               if(is_good){
+                       // Found a good place
+                       //infostream<<"Searched through "<<i<<" places."<<std::endl;
+                       break;
+               }
        }
 #endif
        
-       // If no suitable place was not found, go above water at least.
-       if(groundheight < WATER_LEVEL)
-               groundheight = WATER_LEVEL;
-
-       return intToFloat(v3s16(
-                       nodepos.X,
-                       groundheight + 3,
-                       nodepos.Y
-                       ), BS);
+       return intToFloat(nodepos, BS);
 }
 
 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
@@ -4196,7 +4452,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
        /*
                Try to get an existing player
        */
-       Player *player = m_env.getPlayer(name);
+       Player *player = m_env->getPlayer(name);
        if(player != NULL)
        {
                // If player is already connected, cancel
@@ -4217,7 +4473,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                        player->inventory_backup = new Inventory();
                        *(player->inventory_backup) = player->inventory;
                        // Set creative inventory
-                       craft_set_creative_inventory(player);
+                       craft_set_creative_inventory(player, this);
                }
 
                return player;
@@ -4226,7 +4482,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
        /*
                If player with the wanted peer_id already exists, cancel.
        */
-       if(m_env.getPlayer(peer_id) != NULL)
+       if(m_env->getPlayer(peer_id) != NULL)
        {
                infostream<<"emergePlayer(): Player with wrong name but same"
                                " peer_id already exists"<<std::endl;
@@ -4237,11 +4493,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                Create a new player
        */
        {
-               player = new ServerRemotePlayer();
-               //player->peer_id = c.peer_id;
-               //player->peer_id = PEER_ID_INEXISTENT;
-               player->peer_id = peer_id;
-               player->updateName(name);
+               // Add authentication stuff
                m_authmanager.add(name);
                m_authmanager.setPassword(name, password);
                m_authmanager.setPrivs(name,
@@ -4252,17 +4504,17 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                */
                
                infostream<<"Server: Finding spawn place for player \""
-                               <<player->getName()<<"\""<<std::endl;
+                               <<name<<"\""<<std::endl;
 
-               v3f pos = findSpawnPos(m_env.getServerMap());
+               v3f pos = findSpawnPos(m_env->getServerMap());
 
-               player->setPosition(pos);
+               player = new ServerRemotePlayer(m_env, pos, peer_id, name);
 
                /*
                        Add player to environment
                */
 
-               m_env.addPlayer(player);
+               m_env->addPlayer(player);
 
                /*
                        Add stuff to inventory
@@ -4275,11 +4527,11 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                        player->inventory_backup = new Inventory();
                        *(player->inventory_backup) = player->inventory;
                        // Set creative inventory
-                       craft_set_creative_inventory(player);
+                       craft_set_creative_inventory(player, this);
                }
                else if(g_settings->getBool("give_initial_stuff"))
                {
-                       craft_give_initial_stuff(player);
+                       craft_give_initial_stuff(player, this);
                }
 
                return player;
@@ -4333,7 +4585,7 @@ void Server::handlePeerChange(PeerChange &c)
                {
                        // Get object
                        u16 id = i.getNode()->getKey();
-                       ServerActiveObject* obj = m_env.getActiveObject(id);
+                       ServerActiveObject* obj = m_env->getActiveObject(id);
                        
                        if(obj && obj->m_known_by_count > 0)
                                obj->m_known_by_count--;
@@ -4342,7 +4594,7 @@ void Server::handlePeerChange(PeerChange &c)
                // Collect information about leaving in chat
                std::wstring message;
                {
-                       Player *player = m_env.getPlayer(c.peer_id);
+                       Player *player = m_env->getPlayer(c.peer_id);
                        if(player != NULL)
                        {
                                std::wstring name = narrow_to_wide(player->getName());
@@ -4356,12 +4608,12 @@ void Server::handlePeerChange(PeerChange &c)
 
                /*// Delete player
                {
-                       m_env.removePlayer(c.peer_id);
+                       m_env->removePlayer(c.peer_id);
                }*/
 
                // Set player client disconnected
                {
-                       Player *player = m_env.getPlayer(c.peer_id);
+                       Player *player = m_env->getPlayer(c.peer_id);
                        if(player != NULL)
                                player->peer_id = 0;
                        
@@ -4380,7 +4632,7 @@ void Server::handlePeerChange(PeerChange &c)
                                        if(client->serialization_version == SER_FMT_VER_INVALID)
                                                continue;
                                        // Get player
-                                       Player *player = m_env.getPlayer(client->peer_id);
+                                       Player *player = m_env->getPlayer(client->peer_id);
                                        if(!player)
                                                continue;
                                        // Get name of player