]> git.lizzy.rs Git - minetest.git/blobdiff - src/server.cpp
added dedicated server build without irrlicht
[minetest.git] / src / server.cpp
index a4035284718defead7336bea5cd9fd84e0c9455c..a2dfc8269790cbbd8b43aba9114453a73a2bd937 100644 (file)
@@ -1,3 +1,22 @@
+/*
+Minetest-c55
+Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
 /*
 (c) 2010 Perttu Ahola <celeron55@gmail.com>
 */
@@ -10,6 +29,7 @@
 #include "jmutexautolock.h"
 #include "main.h"
 #include "constants.h"
+#include "voxel.h"
 
 void * ServerThread::Thread()
 {
@@ -74,6 +94,8 @@ void * EmergeThread::Thread()
                v3s16 &p = q->pos;
                
                //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
+
+               //TimeTaker timer("block emerge", g_device);
                
                /*
                        Try to emerge it from somewhere.
@@ -165,12 +187,27 @@ void * EmergeThread::Thread()
                                dout_server<<std::endl;
                        }
 
+                       /*
+                               Update water pressure
+                       */
+
+                       m_server->UpdateBlockWaterPressure(block, modified_blocks);
+
+                       for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
+                                       i.atEnd() == false; i++)
+                       {
+                               MapBlock *block = i.getNode()->getValue();
+                               m_server->UpdateBlockWaterPressure(block, modified_blocks);
+                               //v3s16 p = i.getNode()->getKey();
+                               //m_server->UpdateBlockWaterPressure(p, modified_blocks);
+                       }
+
                        /*
                                Collect a list of blocks that have been modified in
                                addition to the fetched one.
                        */
 
-                       // Add all the "changed blocks"
+                       // Add all the "changed blocks" to modified_blocks
                        for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
                                        i.atEnd() == false; i++)
                        {
@@ -224,12 +261,6 @@ void * EmergeThread::Thread()
                                // Remove block from sent history
                                client->SetBlocksNotSent(modified_blocks);
                        }
-                       
-                       /*if(q->peer_ids.find(client->peer_id) != NULL)
-                       {
-                               // Decrement emerge queue count of client
-                               client->BlockEmerged();
-                       }*/
                }
                
        }
@@ -254,6 +285,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 {
        DSTACK(__FUNCTION_NAME);
        
+       // Increment timers
+       {
+               JMutexAutoLock lock(m_blocks_sent_mutex);
+               m_nearest_unsent_reset_timer += dtime;
+       }
+
        // Won't send anything if already sending
        {
                JMutexAutoLock lock(m_blocks_sending_mutex);
@@ -289,12 +326,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        m_last_center = center;
                }
 
-               static float reset_counter = 0;
-               reset_counter += dtime;
-               if(reset_counter > 5.0)
+               /*dstream<<"m_nearest_unsent_reset_timer="
+                               <<m_nearest_unsent_reset_timer<<std::endl;*/
+               if(m_nearest_unsent_reset_timer > 5.0)
                {
-                       reset_counter = 0;
+                       m_nearest_unsent_reset_timer = 0;
                        m_nearest_unsent_d = 0;
+                       //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
                }
 
                last_nearest_unsent_d = m_nearest_unsent_d;
@@ -302,8 +340,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                d_start = m_nearest_unsent_d;
        }
 
-       u16 maximum_simultaneous_block_sends = g_settings.getU16
+       u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
                        ("max_simultaneous_block_sends_per_client");
+       u16 maximum_simultaneous_block_sends = 
+                       maximum_simultaneous_block_sends_setting;
 
        /*
                Check the time from last addNode/removeNode.
@@ -320,20 +360,29 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
                }
        }
+       
+       u32 num_blocks_selected;
+       {
+               JMutexAutoLock lock(m_blocks_sending_mutex);
+               num_blocks_selected = m_blocks_sending.size();
+       }
+       
+       /*
+               next time d will be continued from the d from which the nearest
+               unsent block was found this time.
+
+               This is because not necessarily any of the blocks found this
+               time are actually sent.
+       */
+       s32 new_nearest_unsent_d = -1;
 
        // Serialization version used
        //u8 ser_version = serialization_version;
 
        //bool has_incomplete_blocks = false;
        
-       /*
-               TODO: Get this from somewhere
-       */
-       //s16 d_max = 7;
-       s16 d_max = 8;
-
-       //TODO: Get this from somewhere (probably a bigger value)
-       s16 d_max_gen = 5;
+       s16 d_max = g_settings.getS16("max_block_send_distance");
+       s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
        
        //dstream<<"Starting from "<<d_start<<std::endl;
 
@@ -353,12 +402,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        if(m_nearest_unsent_d != last_nearest_unsent_d)
                        {
                                d = m_nearest_unsent_d;
+                               last_nearest_unsent_d = m_nearest_unsent_d;
                        }
-                       else
-                       {
-                               m_nearest_unsent_d = d;
-                       }
-                       last_nearest_unsent_d = m_nearest_unsent_d;
                }
 
                /*
@@ -380,18 +425,27 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
                                Also, don't send blocks that are already flying.
                        */
-                       if(d > BLOCK_SEND_DISABLE_LIMITS_MAX_D)
+                       
+                       u16 maximum_simultaneous_block_sends_now =
+                                       maximum_simultaneous_block_sends;
+                       
+                       if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
+                       {
+                               maximum_simultaneous_block_sends_now =
+                                               maximum_simultaneous_block_sends_setting;
+                       }
+
                        {
                                JMutexAutoLock lock(m_blocks_sending_mutex);
                                
                                // Limit is dynamically lowered when building
-                               if(m_blocks_sending.size()
-                                               >= maximum_simultaneous_block_sends)
+                               if(num_blocks_selected
+                                               >= maximum_simultaneous_block_sends_now)
                                {
                                        /*dstream<<"Not sending more blocks. Queue full. "
                                                        <<m_blocks_sending.size()
                                                        <<std::endl;*/
-                                       return;
+                                       goto queue_full;
                                }
 
                                if(m_blocks_sending.find(p) != NULL)
@@ -424,7 +478,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                if(m_blocks_sent.find(p) != NULL)
                                        continue;
                        }
-                                       
+
                        /*
                                Check if map has this block
                        */
@@ -462,6 +516,15 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                                continue;
                        }
 
+                       /*
+                               Record the lowest d from which a a block has been
+                               found being not sent and possibly to exist
+                       */
+                       if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
+                       {
+                               new_nearest_unsent_d = d;
+                       }
+                                       
                        /*
                                Add inexistent block to emerge queue.
                        */
@@ -495,10 +558,17 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        PrioritySortedBlockTransfer q((float)d, p, peer_id);
 
                        dest.push_back(q);
+
+                       num_blocks_selected += 1;
                }
        }
+queue_full:
 
-       // Don't add anything here. The loop breaks by returning.
+       if(new_nearest_unsent_d != -1)
+       {
+               JMutexAutoLock lock(m_blocks_sent_mutex);
+               m_nearest_unsent_d = new_nearest_unsent_d;
+       }
 }
 
 void RemoteClient::SendObjectData(
@@ -858,6 +928,12 @@ Server::Server(
        m_thread(this),
        m_emergethread(this)
 {
+       m_flowwater_timer = 0.0;
+       m_print_info_timer = 0.0;
+       m_objectdata_timer = 0.0;
+       m_emergethread_trigger_timer = 0.0;
+       m_savemap_timer = 0.0;
+       
        m_env_mutex.Init();
        m_con_mutex.Init();
        m_step_dtime_mutex.Init();
@@ -937,11 +1013,19 @@ void Server::AsyncRunStep()
        {
                JMutexAutoLock lock1(m_step_dtime_mutex);
                dtime = m_step_dtime;
-               if(dtime < 0.001)
-                       return;
-               m_step_dtime = 0.0;
        }
        
+       // Send blocks to clients
+       SendBlocks(dtime);
+       
+       if(dtime < 0.001)
+               return;
+       
+       {
+               JMutexAutoLock lock1(m_step_dtime_mutex);
+               m_step_dtime -= dtime;
+       }
+
        //dstream<<"Server steps "<<dtime<<std::endl;
        
        //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
@@ -962,10 +1046,83 @@ void Server::AsyncRunStep()
        /*
                Do background stuff
        */
+
+       /*
+               Flow water
+       */
+       {
+               float interval;
+               
+               if(g_settings.getBool("endless_water") == false)
+                       interval = 1.0;
+               else
+                       interval = 0.25;
+
+               float &counter = m_flowwater_timer;
+               counter += dtime;
+               if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
+               {
+               
+               counter = 0.0;
+
+               core::map<v3s16, MapBlock*> modified_blocks;
+
+               {
+
+                       JMutexAutoLock envlock(m_env_mutex);
+                       
+                       MapVoxelManipulator v(&m_env.getMap());
+                       v.m_disable_water_climb =
+                                       g_settings.getBool("disable_water_climb");
+                       
+                       if(g_settings.getBool("endless_water") == false)
+                               v.flowWater(m_flow_active_nodes, 0, false, 250);
+                       else
+                               v.flowWater(m_flow_active_nodes, 0, false, 50);
+
+                       v.blitBack(modified_blocks);
+
+                       ServerMap &map = ((ServerMap&)m_env.getMap());
+                       
+                       // Update lighting
+                       core::map<v3s16, MapBlock*> lighting_modified_blocks;
+                       map.updateLighting(modified_blocks, lighting_modified_blocks);
+                       
+                       // Add blocks modified by lighting to modified_blocks
+                       for(core::map<v3s16, MapBlock*>::Iterator
+                                       i = lighting_modified_blocks.getIterator();
+                                       i.atEnd() == false; i++)
+                       {
+                               MapBlock *block = i.getNode()->getValue();
+                               modified_blocks.insert(block->getPos(), block);
+                       }
+               } // envlock
+
+               /*
+                       Set the modified blocks unsent for all the clients
+               */
+               
+               JMutexAutoLock lock2(m_con_mutex);
+
+               for(core::map<u16, RemoteClient*>::Iterator
+                               i = m_clients.getIterator();
+                               i.atEnd() == false; i++)
+               {
+                       RemoteClient *client = i.getNode()->getValue();
+                       
+                       if(modified_blocks.size() > 0)
+                       {
+                               // Remove block from sent history
+                               client->SetBlocksNotSent(modified_blocks);
+                       }
+               }
+
+               } // interval counter
+       }
        
        // Periodically print some info
        {
-               static float counter = 0.0;
+               float &counter = m_print_info_timer;
                counter += dtime;
                if(counter >= 30.0)
                {
@@ -984,29 +1141,114 @@ void Server::AsyncRunStep()
                }
        }
 
-       // Run time- and client- related stuff
-       // NOTE: If you intend to add something here, check that it
-       // doesn't fit in RemoteClient::GetNextBlocks for example.
-       /*{
-               // Clients are behind connection lock
-               JMutexAutoLock lock(m_con_mutex);
+       /*
+               Update digging
+
+               NOTE: Some of this could be moved to RemoteClient
+       */
+
+       {
+               JMutexAutoLock envlock(m_env_mutex);
+               JMutexAutoLock conlock(m_con_mutex);
 
                for(core::map<u16, RemoteClient*>::Iterator
                        i = m_clients.getIterator();
                        i.atEnd() == false; i++)
                {
                        RemoteClient *client = i.getNode()->getValue();
-                       //con::Peer *peer = m_con.GetPeer(client->peer_id);
-                       //client->RunSendingTimeouts(dtime, peer->resend_timeout);
+                       Player *player = m_env.getPlayer(client->peer_id);
+
+                       JMutexAutoLock digmutex(client->m_dig_mutex);
+
+                       if(client->m_dig_tool_item == -1)
+                               continue;
+
+                       client->m_dig_time_remaining -= dtime;
+
+                       if(client->m_dig_time_remaining > 0)
+                               continue;
+
+                       v3s16 p_under = client->m_dig_position;
+                       
+                       // Mandatory parameter; actually used for nothing
+                       core::map<v3s16, MapBlock*> modified_blocks;
+
+                       u8 material;
+
+                       try
+                       {
+                               // Get material at position
+                               material = m_env.getMap().getNode(p_under).d;
+                               // If it's not diggable, do nothing
+                               if(content_diggable(material) == false)
+                               {
+                                       derr_server<<"Server: Not finishing digging: Node not diggable"
+                                                       <<std::endl;
+                                       client->m_dig_tool_item = -1;
+                                       break;
+                               }
+                       }
+                       catch(InvalidPositionException &e)
+                       {
+                               derr_server<<"Server: Not finishing digging: Node not found"
+                                               <<std::endl;
+                               client->m_dig_tool_item = -1;
+                               break;
+                       }
+                       
+                       // Create packet
+                       u32 replysize = 8;
+                       SharedBuffer<u8> reply(replysize);
+                       writeU16(&reply[0], TOCLIENT_REMOVENODE);
+                       writeS16(&reply[2], p_under.X);
+                       writeS16(&reply[4], p_under.Y);
+                       writeS16(&reply[6], p_under.Z);
+                       // Send as reliable
+                       m_con.SendToAll(0, reply, true);
+                       
+                       if(g_settings.getBool("creative_mode") == false)
+                       {
+                               // Add to inventory and send inventory
+                               InventoryItem *item = new MaterialItem(material, 1);
+                               player->inventory.addItem(item);
+                               SendInventory(player->peer_id);
+                       }
+
+                       /*
+                               Remove the node
+                               (this takes some time so it is done after the quick stuff)
+                       */
+                       m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
+                       
+                       /*
+                               Update water
+                       */
+                       
+                       // Update water pressure around modification
+                       // This also adds it to m_flow_active_nodes if appropriate
+
+                       MapVoxelManipulator v(&m_env.getMap());
+                       v.m_disable_water_climb =
+                                       g_settings.getBool("disable_water_climb");
+                       
+                       VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
+
+                       try
+                       {
+                               v.updateAreaWaterPressure(area, m_flow_active_nodes);
+                       }
+                       catch(ProcessingLimitException &e)
+                       {
+                               dstream<<"Processing limit reached (1)"<<std::endl;
+                       }
+                       
+                       v.blitBack(modified_blocks);
                }
-       }*/
+       }
 
-       // Send blocks to clients
-       SendBlocks(dtime);
-       
        // Send object positions
        {
-               static float counter = 0.0;
+               float &counter = m_objectdata_timer;
                counter += dtime;
                if(counter >= g_settings.getFloat("objectdata_interval"))
                {
@@ -1021,7 +1263,7 @@ void Server::AsyncRunStep()
        // Trigger emergethread (it gets somehow gets to a
        // non-triggered but bysy state sometimes)
        {
-               static float counter = 0.0;
+               float &counter = m_emergethread_trigger_timer;
                counter += dtime;
                if(counter >= 2.0)
                {
@@ -1033,7 +1275,7 @@ void Server::AsyncRunStep()
 
        // Save map
        {
-               static float counter = 0.0;
+               float &counter = m_savemap_timer;
                counter += dtime;
                if(counter >= SERVER_MAP_SAVE_INTERVAL)
                {
@@ -1352,19 +1594,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        block->removeObject(id);
                }
        }
-       else if(command == TOSERVER_CLICK_GROUND)
+       else if(command == TOSERVER_GROUND_ACTION)
        {
                if(datasize < 17)
                        return;
                /*
                        length: 17
                        [0] u16 command
-                       [2] u8 button (0=left, 1=right)
+                       [2] u8 action
                        [3] v3s16 nodepos_undersurface
                        [9] v3s16 nodepos_abovesurface
                        [15] u16 item
+                       actions:
+                       0: start digging
+                       1: place block
+                       2: stop digging (all parameters ignored)
                */
-               u8 button = readU8(&data[2]);
+               u8 action = readU8(&data[2]);
                v3s16 p_under;
                p_under.X = readS16(&data[3]);
                p_under.Y = readS16(&data[5]);
@@ -1378,64 +1624,67 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                //TODO: Check that target is reasonably close
                
                /*
-                       Left button digs ground
+                       0: start digging
                */
-               if(button == 0)
+               if(action == 0)
                {
 
-                       core::map<v3s16, MapBlock*> modified_blocks;
-
-                       u8 material;
+                       u8 content;
 
                        try
                        {
-                               // Get material at position
-                               material = m_env.getMap().getNode(p_under).d;
-                               // If it's air, do nothing
-                               if(material == MATERIAL_AIR)
+                               // Get content at position
+                               content = m_env.getMap().getNode(p_under).d;
+                               // If it's not diggable, do nothing
+                               if(content_diggable(content) == false)
                                {
                                        return;
                                }
                        }
                        catch(InvalidPositionException &e)
                        {
-                               derr_server<<"Server: Ignoring REMOVENODE: Node not found"
+                               derr_server<<"Server: Not starting digging: Node not found"
                                                <<std::endl;
                                return;
                        }
                        
+                       /*
+                               Set stuff in RemoteClient
+                       */
+                       RemoteClient *client = getClient(peer->id);
+                       JMutexAutoLock(client->m_dig_mutex);
+                       client->m_dig_tool_item = 0;
+                       client->m_dig_position = p_under;
+                       float dig_time = 0.5;
+                       if(content == CONTENT_STONE)
+                       {
+                               dig_time = 1.5;
+                       }
+                       else if(content == CONTENT_TORCH)
+                       {
+                               dig_time = 0.0;
+                       }
+                       client->m_dig_time_remaining = dig_time;
+                       
                        // Reset build time counter
                        getClient(peer->id)->m_time_from_building.set(0.0);
                        
-                       // Create packet
-                       u32 replysize = 8;
-                       SharedBuffer<u8> reply(replysize);
-                       writeU16(&reply[0], TOCLIENT_REMOVENODE);
-                       writeS16(&reply[2], p_under.X);
-                       writeS16(&reply[4], p_under.Y);
-                       writeS16(&reply[6], p_under.Z);
-                       // Send as reliable
-                       m_con.SendToAll(0, reply, true);
-                       
-                       if(g_settings.getBool("creative_mode") == false)
-                       {
-                               // Add to inventory and send inventory
-                               InventoryItem *item = new MaterialItem(material, 1);
-                               player->inventory.addItem(item);
-                               SendInventory(player->peer_id);
-                       }
+               } // action == 0
 
-                       /*
-                               Remove the node
-                               (this takes some time so it is done after the quick stuff)
-                       */
-                       m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
+               /*
+                       2: stop digging
+               */
+               else if(action == 2)
+               {
+                       RemoteClient *client = getClient(peer->id);
+                       JMutexAutoLock digmutex(client->m_dig_mutex);
+                       client->m_dig_tool_item = -1;
+               }
 
-               } // button == 0
                /*
-                       Right button places blocks and stuff
+                       1: place block
                */
-               else if(button == 1)
+               else if(action == 1)
                {
 
                        // Get item
@@ -1450,15 +1699,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        */
                        if(std::string("MaterialItem") == item->getName())
                        {
-                               MaterialItem *mitem = (MaterialItem*)item;
-                               
-                               MapNode n;
-                               n.d = mitem->getMaterial();
-
                                try{
-                                       // Don't add a node if there isn't air
+                                       // Don't add a node if this is not a free space
                                        MapNode n2 = m_env.getMap().getNode(p_over);
-                                       if(n2.d != MATERIAL_AIR)
+                                       if(content_buildable_to(n2.d) == false)
                                                return;
                                }
                                catch(InvalidPositionException &e)
@@ -1471,17 +1715,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Reset build time counter
                                getClient(peer->id)->m_time_from_building.set(0.0);
                                
-                               if(g_settings.getBool("creative_mode") == false)
-                               {
-                                       // Remove from inventory and send inventory
-                                       if(mitem->getCount() == 1)
-                                               player->inventory.deleteItem(item_i);
-                                       else
-                                               mitem->remove(1);
-                                       // Send inventory
-                                       SendInventory(peer_id);
-                               }
-                               
+                               // Create node data
+                               MaterialItem *mitem = (MaterialItem*)item;
+                               MapNode n;
+                               n.d = mitem->getMaterial();
+                               if(content_directional(n.d))
+                                       n.dir = packDir(p_under - p_over);
+
+#if 1
                                // Create packet
                                u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
                                SharedBuffer<u8> reply(replysize);
@@ -1493,6 +1734,43 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                // Send as reliable
                                m_con.SendToAll(0, reply, true);
                                
+                               /*
+                                       Handle inventory
+                               */
+                               if(g_settings.getBool("creative_mode") == false)
+                               {
+                                       // Remove from inventory and send inventory
+                                       if(mitem->getCount() == 1)
+                                               player->inventory.deleteItem(item_i);
+                                       else
+                                               mitem->remove(1);
+                                       // Send inventory
+                                       SendInventory(peer_id);
+                               }
+                               
+                               /*
+                                       Add node.
+
+                                       This takes some time so it is done after the quick stuff
+                               */
+                               core::map<v3s16, MapBlock*> modified_blocks;
+                               m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
+#endif
+#if 0
+                               /*
+                                       Handle inventory
+                               */
+                               if(g_settings.getBool("creative_mode") == false)
+                               {
+                                       // Remove from inventory and send inventory
+                                       if(mitem->getCount() == 1)
+                                               player->inventory.deleteItem(item_i);
+                                       else
+                                               mitem->remove(1);
+                                       // Send inventory
+                                       SendInventory(peer_id);
+                               }
+
                                /*
                                        Add node.
 
@@ -1500,6 +1778,50 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                */
                                core::map<v3s16, MapBlock*> modified_blocks;
                                m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
+
+                               /*
+                                       Set the modified blocks unsent for all the clients
+                               */
+                               
+                               //JMutexAutoLock lock2(m_con_mutex);
+
+                               for(core::map<u16, RemoteClient*>::Iterator
+                                               i = m_clients.getIterator();
+                                               i.atEnd() == false; i++)
+                               {
+                                       RemoteClient *client = i.getNode()->getValue();
+                                       
+                                       if(modified_blocks.size() > 0)
+                                       {
+                                               // Remove block from sent history
+                                               client->SetBlocksNotSent(modified_blocks);
+                                       }
+                               }
+#endif
+                               
+                               /*
+                                       Update water
+                               */
+                               
+                               // Update water pressure around modification
+                               // This also adds it to m_flow_active_nodes if appropriate
+
+                               MapVoxelManipulator v(&m_env.getMap());
+                               v.m_disable_water_climb =
+                                               g_settings.getBool("disable_water_climb");
+                               
+                               VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
+
+                               try
+                               {
+                                       v.updateAreaWaterPressure(area, m_flow_active_nodes);
+                               }
+                               catch(ProcessingLimitException &e)
+                               {
+                                       dstream<<"Processing limit reached (1)"<<std::endl;
+                               }
+                               
+                               v.blitBack(modified_blocks);
                        }
                        /*
                                Handle block object items
@@ -1558,16 +1880,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                }
                        }
 
-               } // button == 1
+               } // action == 1
                /*
-                       Catch invalid buttons
+                       Catch invalid actions
                */
                else
                {
-                       derr_server<<"WARNING: Server: Invalid button "
-                                       <<button<<std::endl;
+                       derr_server<<"WARNING: Server: Invalid action "
+                                       <<action<<std::endl;
                }
        }
+#if 0
        else if(command == TOSERVER_RELEASE)
        {
                if(datasize < 3)
@@ -1577,8 +1900,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        [0] u16 command
                        [2] u8 button
                */
-               //TODO
+               dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
        }
+#endif
        else if(command == TOSERVER_SIGNTEXT)
        {
                /*
@@ -1678,6 +2002,9 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
        writeS16(&reply[4], p.Y);
        writeS16(&reply[6], p.Z);
        memcpy(&reply[8], *blockdata, blockdata.getSize());
+
+       /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                       <<":  \tpacket size: "<<replysize<<std::endl;*/
        
        /*
                Send packet
@@ -1685,107 +2012,6 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
        m_con.Send(peer_id, 1, reply, true);
 }
 
-/*void Server::SendBlock(u16 peer_id, MapBlock *block, u8 ver)
-{
-       JMutexAutoLock conlock(m_con_mutex);
-       
-       SendBlockNoLock(peer_id, block, ver);
-}*/
-
-#if 0
-void Server::SendSectorMeta(u16 peer_id, core::list<v2s16> ps, u8 ver)
-{
-       DSTACK(__FUNCTION_NAME);
-       dstream<<"Server sending sector meta of "
-                       <<ps.getSize()<<" sectors"<<std::endl;
-
-       core::list<v2s16>::Iterator i = ps.begin();
-       core::list<v2s16> sendlist;
-       for(;;)
-       {
-               if(sendlist.size() == 255 || i == ps.end())
-               {
-                       if(sendlist.size() == 0)
-                               break;
-                       /*
-                               [0] u16 command
-                               [2] u8 sector count
-                               [3...] v2s16 pos + sector metadata
-                       */
-                       std::ostringstream os(std::ios_base::binary);
-                       u8 buf[4];
-
-                       writeU16(buf, TOCLIENT_SECTORMETA);
-                       os.write((char*)buf, 2);
-
-                       writeU8(buf, sendlist.size());
-                       os.write((char*)buf, 1);
-
-                       for(core::list<v2s16>::Iterator
-                                       j = sendlist.begin();
-                                       j != sendlist.end(); j++)
-                       {
-                               // Write position
-                               writeV2S16(buf, *j);
-                               os.write((char*)buf, 4);
-                               
-                               /*
-                                       Write ClientMapSector metadata
-                               */
-
-                               /*
-                                       [0] u8 serialization version
-                                       [1] s16 corners[0]
-                                       [3] s16 corners[1]
-                                       [5] s16 corners[2]
-                                       [7] s16 corners[3]
-                                       size = 9
-                                       
-                                       In which corners are in these positions
-                                       v2s16(0,0),
-                                       v2s16(1,0),
-                                       v2s16(1,1),
-                                       v2s16(0,1),
-                               */
-
-                               // Write version
-                               writeU8(buf, ver);
-                               os.write((char*)buf, 1);
-
-                               // Write corners
-                               // TODO: Get real values
-                               s16 corners[4];
-                               ((ServerMap&)m_env.getMap()).getSectorCorners(*j, corners);
-
-                               writeS16(buf, corners[0]);
-                               os.write((char*)buf, 2);
-                               writeS16(buf, corners[1]);
-                               os.write((char*)buf, 2);
-                               writeS16(buf, corners[2]);
-                               os.write((char*)buf, 2);
-                               writeS16(buf, corners[3]);
-                               os.write((char*)buf, 2);
-                       }
-
-                       SharedBuffer<u8> data((u8*)os.str().c_str(), os.str().size());
-
-                       /*dstream<<"Server::SendSectorMeta(): sending packet"
-                                       " with "<<sendlist.size()<<" sectors"<<std::endl;*/
-
-                       m_con.Send(peer_id, 1, data, true);
-
-                       if(i == ps.end())
-                               break;
-
-                       sendlist.clear();
-               }
-
-               sendlist.push_back(*i);
-               i++;
-       }
-}
-#endif
-
 core::list<PlayerInfo> Server::getPlayerInfo()
 {
        DSTACK(__FUNCTION_NAME);
@@ -1856,27 +2082,32 @@ void Server::peerAdded(con::Peer *peer)
                // The player shouldn't already exist
                assert(player == NULL);
 
-               player = new RemotePlayer();
+               player = new ServerRemotePlayer();
                player->peer_id = peer->id;
 
                /*
                        Set player position
                */
-
+               
+               // We're going to throw the player to this position
+               //v2s16 nodepos(29990,29990);
+               //v2s16 nodepos(9990,9990);
+               v2s16 nodepos(0,0);
+               v2s16 sectorpos = getNodeSectorPos(nodepos);
                // Get zero sector (it could have been unloaded to disk)
-               m_env.getMap().emergeSector(v2s16(0,0));
+               m_env.getMap().emergeSector(sectorpos);
                // Get ground height at origin
-               f32 groundheight = m_env.getMap().getGroundHeight(v2s16(0,0), true);
-               // The zero sector should have been generated
+               f32 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
+               // The sector should have been generated -> groundheight exists
                assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
                // Don't go underwater
                if(groundheight < WATER_LEVEL)
                        groundheight = WATER_LEVEL;
 
                player->setPosition(intToFloat(v3s16(
-                               0,
+                               nodepos.X,
                                groundheight + 1,
-                               0
+                               nodepos.Y
                )));
 
                /*
@@ -1892,9 +2123,13 @@ void Server::peerAdded(con::Peer *peer)
                if(g_settings.getBool("creative_mode"))
                {
                        // Give all materials
-                       assert(USEFUL_MATERIAL_COUNT <= PLAYER_INVENTORY_SIZE);
-                       for(u16 i=0; i<USEFUL_MATERIAL_COUNT; i++)
+                       assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
+                       for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
                        {
+                               // Skip some materials
+                               if(i == CONTENT_OCEAN)
+                                       continue;
+
                                InventoryItem *item = new MaterialItem(i, 1);
                                player->inventory.addItem(item);
                        }
@@ -2121,4 +2356,27 @@ RemoteClient* Server::getClient(u16 peer_id)
        return n->getValue();
 }
 
+void Server::UpdateBlockWaterPressure(MapBlock *block,
+                       core::map<v3s16, MapBlock*> &modified_blocks)
+{
+       MapVoxelManipulator v(&m_env.getMap());
+       v.m_disable_water_climb =
+                       g_settings.getBool("disable_water_climb");
+       
+       VoxelArea area(block->getPosRelative(),
+                       block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
+
+       try
+       {
+               v.updateAreaWaterPressure(area, m_flow_active_nodes);
+       }
+       catch(ProcessingLimitException &e)
+       {
+               dstream<<"Processing limit reached (1)"<<std::endl;
+       }
+       
+       v.blitBack(modified_blocks);
+}
+       
+