]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/server.cpp
builtin.lua
[dragonfireclient.git] / src / server.cpp
index 14c019d52fc8e6e944756e11d672d20e9e4daed9..6d34a0fac1dd89f323f5775463714385f11a661f 100644 (file)
@@ -39,6 +39,11 @@ 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"
+#include "craftdef.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -185,7 +190,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 +255,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 +359,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 +376,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 +414,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 +463,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 +552,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 +577,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 +590,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 +602,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        {
                                continue;
                        }
-                       
+#endif
                        /*
                                Don't send already sent blocks
                        */
@@ -614,7 +616,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 +645,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 +660,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 +678,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 +687,12 @@ 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)
+                               u32 max_emerge = 25;
+                               // Make it more responsive when needing to generate stuff
+                               if(surely_not_found_on_disk)
+                                       max_emerge = 5;
+                               if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
                                {
                                        //infostream<<"Adding block to emerge queue"<<std::endl;
                                        
@@ -709,55 +704,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 +811,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 +949,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 +986,14 @@ 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_nodedef(createNodeDefManager()),
+       m_craftdef(createCraftDefManager()),
        m_thread(this),
        m_emergethread(this),
        m_time_counter(0),
@@ -979,20 +1015,71 @@ 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_nodedef="<<m_nodedef<<std::endl;
+       
+       // Initialize default node definitions
+       content_mapnode_init(m_nodedef);
+       
+       // Path to builtin.lua
+       std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
+       // 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 builtin.lua
+       infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
+                       <<"\""<<std::endl;
+       bool success = script_load(m_lua, builtinpath.c_str());
+       if(!success){
+               errorstream<<"Server: Failed to load and run "
+                               <<builtinpath<<std::endl;
+               assert(0);
+       }
+       // Load and run "mod" 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 +1122,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 +1151,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_nodedef;
+       
+       // Deinitialize scripting
+       infostream<<"Server: Deinitializing scripting"<<std::endl;
+       script_deinit(m_lua);
 }
 
 void Server::start(unsigned short port)
@@ -1080,7 +1177,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 +1276,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 +1297,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,16 +1312,16 @@ 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;
+       const float map_timer_and_unload_dtime = 2.92;
        if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
        {
                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 +1342,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 +1398,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 +1429,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 +1442,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 +1467,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 +1489,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 +1555,7 @@ void Server::AsyncRunStep()
                        }
                }
                
-               m_env.setKnownActiveObjects(whatever);
+               m_env->setKnownActiveObjects(whatever);
 #endif
 
        }
@@ -1470,7 +1567,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 +1576,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 +1765,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 +1807,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 +1852,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 +1869,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 +1880,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 +1914,7 @@ void Server::Receive()
                                <<" has apparently closed connection. "
                                <<"Removing player."<<std::endl;
 
-               m_env.removePlayer(peer_id);*/
+               m_env->removePlayer(peer_id);*/
        }
 }
 
@@ -1829,9 +1925,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 +1945,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 +1966,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 +1979,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 +2001,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 +2129,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 +2146,36 @@ 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 tool definitions
+               SendToolDef(m_con, peer_id, m_toolmgr);
+               
+               // Send node definitions
+               SendNodeDef(m_con, peer_id, m_nodedef);
+               
+               // Send textures
+               SendTextures(peer_id);
                
                // 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 +2183,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 +2193,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 +2205,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 +2229,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 +2251,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 +2359,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 +2373,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 +2458,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 +2466,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)
        {
@@ -2400,6 +2516,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                NOTE: This can be used in the future to check if
                                somebody is cheating, by checking the timing.
                        */
+                       bool cannot_punch_node = false;
+
+                       MapNode n(CONTENT_IGNORE);
+
+                       try
+                       {
+                               n = m_env->getMap().getNode(p_under);
+                       }
+                       catch(InvalidPositionException &e)
+                       {
+                               infostream<<"Server: Not punching: Node not found."
+                                               <<" Adding block to emerge queue."
+                                               <<std::endl;
+                               m_emerge_queue.addBlock(peer_id,
+                                               getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
+                               cannot_punch_node = true;
+                       }
+
+                       if(cannot_punch_node)
+                               return;
+
+                       /*
+                               Run script hook
+                       */
+                       ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+                       scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
+
                } // action == 0
 
                /*
@@ -2408,7 +2551,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
@@ -2426,19 +2569,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        u8 mineral = MINERAL_NONE;
 
                        bool cannot_remove_node = false;
-
+                       
+                       MapNode n(CONTENT_IGNORE);
                        try
                        {
-                               MapNode n = m_env.getMap().getNode(p_under);
+                               n = m_env->getMap().getNode(p_under);
                                // Get mineral
-                               mineral = n.getMineral();
+                               mineral = n.getMineral(m_nodedef);
                                // 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_nodedef->get(material).diggable == false)
                                        {
                                                infostream<<"Server: Not finishing digging: "
                                                                <<"Node not diggable"
@@ -2450,7 +2594,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 +2675,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_nodedef);
 
                                                if(prop.diggable == false)
                                                {
@@ -2557,16 +2703,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_nodedef->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 +2729,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_nodedef->get(material).extra_dug_item;
+                                       s32 extra_rarity = m_nodedef->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 +2762,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
@@ -2631,6 +2777,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        continue;
                                client->SetBlocksNotSent(modified_blocks);
                        }
+
+                       /*
+                               Run script hook
+                       */
+                       ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+                       scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
                }
                
                /*
@@ -2657,7 +2809,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 +2817,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_nodedef->get(n2).buildable_to == false
                                                || no_enough_privs)
                                        {
                                                // Client probably has wrong data.
@@ -2691,7 +2843,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 +2855,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_nodedef->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_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
                                {
                                        v3f playerpos = player->getPosition();
                                        v3f blockpos = intToFloat(p_over, BS) - playerpos;
@@ -2758,7 +2910,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
@@ -2774,11 +2926,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        client->SetBlocksNotSent(modified_blocks);
                                }
 
+                               /*
+                                       Run script hook
+                               */
+                               ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+                               scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
+
                                /*
                                        Calculate special events
                                */
                                
-                               /*if(n.d == CONTENT_MESE)
+                               /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
                                {
                                        u32 count = 0;
                                        for(s16 z=-1; z<=1; z++)
@@ -2800,7 +2958,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 +2981,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 +3003,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,10 +3090,10 @@ 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)
+               if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
                        return;
                SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
                signmeta->setText(text);
@@ -2944,19 +3102,14 @@ 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();
+                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                       "sign node text");
                }
 
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd()==false; i++)
-               {
-                       RemoteClient *client = i.getNode()->getValue();
-                       client->SetBlockNotSent(blockpos);
-               }
+               setBlockNotSent(blockpos);
        }
        else if(command == TOSERVER_INVENTORY_ACTION)
        {
@@ -3060,8 +3213,8 @@ 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);
-                                               if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
+                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
+                                               if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
                                                        LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
                                                        if (lcm->getOwner() != player->getName())
                                                                return;
@@ -3078,8 +3231,8 @@ 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);
-                                               if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
+                                               NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
+                                               if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
                                                        LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
                                                        if (lcm->getOwner() != player->getName())
                                                                return;
@@ -3161,7 +3314,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 +3517,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,17 +3550,15 @@ 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();
-
-               for(core::map<u16, RemoteClient*>::Iterator
-                       i = m_clients.getIterator();
-                       i.atEnd()==false; i++)
-               {
-                       RemoteClient *client = i.getNode()->getValue();
-                       client->SetBlockNotSent(blockpos);
-               }
+               
+               MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
+               if(block)
+                       block->raiseModified(MOD_STATE_WRITE_NEEDED);
+               
+               setBlockNotSent(blockpos);
 
                return;
        }
@@ -3423,7 +3574,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 +3585,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 +3686,56 @@ 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();
+       infostream<<"Server::SendToolDef(): Sending tool definitions: size="
+                       <<s.size()<<std::endl;
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       con.Send(peer_id, 0, data, true);
+}
+
+void Server::SendNodeDef(con::Connection &con, u16 peer_id,
+               INodeDefManager *nodedef)
+{
+       DSTACK(__FUNCTION_NAME);
+       std::ostringstream os(std::ios_base::binary);
+
+       /*
+               u16 command
+               u32 length of the next item
+               serialized NodeDefManager
+       */
+       writeU16(os, TOCLIENT_NODEDEF);
+       std::ostringstream tmp_os(std::ios::binary);
+       nodedef->serialize(tmp_os);
+       os<<serializeLongString(tmp_os.str());
+
+       // Make data buffer
+       std::string s = os.str();
+       infostream<<"Server::SendNodeDef(): Sending node definitions: size="
+                       <<s.size()<<std::endl;
+       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 +3768,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 +3802,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 +3859,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 +3988,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 +4029,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 +4169,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 +4186,129 @@ 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 */
+       
+       // Put 5kB in one bunch (this is not accurate)
+       u32 bytes_per_bunch = 5000;
+       
+       core::array< core::list<SendableTexture> > texture_bunches;
+       texture_bunches.push_back(core::list<SendableTexture>());
+       
+       u32 texture_size_bunch_total = 0;
+       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);
+                               texture_size_bunch_total += len;
+                               if(fis.eof())
+                                       break;
+                               if(!fis.good()){
+                                       bad = true;
+                                       break;
+                               }
+                       }
+                       if(bad){
+                               errorstream<<"Server::SendTextures(): Failed to read \""
+                                               <<tname<<"\""<<std::endl;
+                               continue;
+                       }
+                       /*infostream<<"Server::SendTextures(): Loaded \""
+                                       <<tname<<"\""<<std::endl;*/
+                       // Put in list
+                       texture_bunches[texture_bunches.size()-1].push_back(
+                                       SendableTexture(tname, tpath, tmp_os.str()));
+                       
+                       // Start next bunch if got enough data
+                       if(texture_size_bunch_total >= bytes_per_bunch){
+                               texture_bunches.push_back(core::list<SendableTexture>());
+                               texture_size_bunch_total = 0;
+                       }
+               }
+       }
+
+       /* Create and send packets */
+       
+       u32 num_bunches = texture_bunches.size();
+       for(u32 i=0; i<num_bunches; i++)
+       {
+               /*
+                       u16 command
+                       u16 total number of texture bunches
+                       u16 index of this bunch
+                       u32 number of textures in this bunch
+                       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);
+               writeU16(os, num_bunches);
+               writeU16(os, i);
+               writeU32(os, texture_bunches[i].size());
+               
+               for(core::list<SendableTexture>::Iterator
+                               j = texture_bunches[i].begin();
+                               j != texture_bunches[i].end(); j++){
+                       os<<serializeString(j->name);
+                       os<<serializeLongString(j->data);
+               }
+               
+               // Make data buffer
+               std::string s = os.str();
+               infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
+                               <<" textures="<<texture_bunches[i].size()
+                               <<" 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 +4351,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 +4362,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);
 
        /*
@@ -4059,14 +4382,19 @@ void Server::UpdateCrafting(u16 peer_id)
                }
                if(clist && rlist && player->craftresult_is_preview)
                {
-                       InventoryItem *items[9];
-                       for(u16 i=0; i<9; i++)
-                       {
-                               items[i] = clist->getItem(i);
+                       // Get result of crafting grid
+                       
+                       std::vector<InventoryItem*> items;
+                       for(u16 i=0; i<9; i++){
+                               if(clist->getItem(i) == NULL)
+                                       items.push_back(NULL);
+                               else
+                                       items.push_back(clist->getItem(i)->clone());
                        }
+                       CraftPointerInput cpi(3, items);
                        
-                       // Get result of crafting grid
-                       InventoryItem *result = craft_get_result(items);
+                       InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
+                       //InventoryItem *result = craft_get_result(items, this);
                        if(result)
                                rlist->addItem(result);
                }
@@ -4105,7 +4433,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 +4442,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 +4458,58 @@ 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_nodedef;
+}
+ICraftDefManager* Server::getCraftDefManager()
+{
+       return m_craftdef;
+}
+ITextureSource* Server::getTextureSource()
+{
+       return NULL;
+}
+u16 Server::allocateUnknownNodeId(const std::string &name)
+{
+       return m_nodedef->allocateDummy(name);
+}
+
+IWritableToolDefManager* Server::getWritableToolDefManager()
+{
+       return m_toolmgr;
+}
+IWritableNodeDefManager* Server::getWritableNodeDefManager()
+{
+       return m_nodedef;
+}
+IWritableCraftDefManager* Server::getWritableCraftDefManager()
+{
+       return m_craftdef;
+}
+
 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 +4522,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 +4539,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 +4573,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 +4594,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 +4603,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 +4614,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 +4625,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 +4648,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 +4706,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 +4715,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 +4729,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 +4753,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