]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
487ce2a98656ab518737bbdf22ecc2a0c72266eb
[minetest.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
22 */
23
24 #include "server.h"
25 #include "utility.h"
26 #include <iostream>
27 #include "clientserver.h"
28 #include "map.h"
29 #include "jmutexautolock.h"
30 #include "main.h"
31 #include "constants.h"
32 #include "voxel.h"
33
34 void * ServerThread::Thread()
35 {
36         ThreadStarted();
37
38         DSTACK(__FUNCTION_NAME);
39
40         while(getRun())
41         {
42                 try{
43                         m_server->AsyncRunStep();
44                 
45                         //dout_server<<"Running m_server->Receive()"<<std::endl;
46                         m_server->Receive();
47                 }
48                 catch(con::NoIncomingDataException &e)
49                 {
50                 }
51 #if CATCH_UNHANDLED_EXCEPTIONS
52                 /*
53                         This is what has to be done in threads to get suitable debug info
54                 */
55                 catch(std::exception &e)
56                 {
57                         dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
58                                         <<e.what()<<std::endl;
59                         assert(0);
60                 }
61 #endif
62         }
63         
64
65         return NULL;
66 }
67
68 void * EmergeThread::Thread()
69 {
70         ThreadStarted();
71
72         DSTACK(__FUNCTION_NAME);
73
74         bool debug=false;
75 #if CATCH_UNHANDLED_EXCEPTIONS
76         try
77         {
78 #endif
79         
80         /*
81                 Get block info from queue, emerge them and send them
82                 to clients.
83
84                 After queue is empty, exit.
85         */
86         while(getRun())
87         {
88                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
89                 if(qptr == NULL)
90                         break;
91                 
92                 SharedPtr<QueuedBlockEmerge> q(qptr);
93
94                 v3s16 &p = q->pos;
95                 
96                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
97
98                 //TimeTaker timer("block emerge", g_device);
99                 
100                 /*
101                         Try to emerge it from somewhere.
102
103                         If it is only wanted as optional, only loading from disk
104                         will be allowed.
105                 */
106                 
107                 /*
108                         Check if any peer wants it as non-optional. In that case it
109                         will be generated.
110
111                         Also decrement the emerge queue count in clients.
112                 */
113
114                 bool optional = true;
115
116                 {
117                         core::map<u16, u8>::Iterator i;
118                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
119                         {
120                                 //u16 peer_id = i.getNode()->getKey();
121
122                                 // Check flags
123                                 u8 flags = i.getNode()->getValue();
124                                 if((flags & TOSERVER_GETBLOCK_FLAG_OPTIONAL) == false)
125                                         optional = false;
126                                 
127                         }
128                 }
129
130                 /*dstream<<"EmergeThread: p="
131                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
132                                 <<"optional="<<optional<<std::endl;*/
133                 
134                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
135                         
136                 core::map<v3s16, MapBlock*> changed_blocks;
137                 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
138
139                 MapBlock *block = NULL;
140                 bool got_block = true;
141                 core::map<v3s16, MapBlock*> modified_blocks;
142                 
143                 {//envlock
144
145                 JMutexAutoLock envlock(m_server->m_env_mutex);
146
147                 //TimeTaker timer("block emerge envlock", g_device);
148                         
149                 try{
150                         bool only_from_disk = false;
151                         
152                         if(optional)
153                                 only_from_disk = true;
154
155                         block = map.emergeBlock(
156                                         p,
157                                         only_from_disk,
158                                         changed_blocks,
159                                         lighting_invalidated_blocks);
160                         
161                         // If it is a dummy, block was not found on disk
162                         if(block->isDummy())
163                         {
164                                 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
165                                 got_block = false;
166                         }
167                 }
168                 catch(InvalidPositionException &e)
169                 {
170                         // Block not found.
171                         // This happens when position is over limit.
172                         got_block = false;
173                 }
174                 
175                 if(got_block)
176                 {
177                         if(debug && changed_blocks.size() > 0)
178                         {
179                                 dout_server<<DTIME<<"Got changed_blocks: ";
180                                 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
181                                                 i.atEnd() == false; i++)
182                                 {
183                                         MapBlock *block = i.getNode()->getValue();
184                                         v3s16 p = block->getPos();
185                                         dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
186                                 }
187                                 dout_server<<std::endl;
188                         }
189
190                         /*
191                                 Update water pressure
192                         */
193
194                         m_server->UpdateBlockWaterPressure(block, modified_blocks);
195
196                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
197                                         i.atEnd() == false; i++)
198                         {
199                                 MapBlock *block = i.getNode()->getValue();
200                                 m_server->UpdateBlockWaterPressure(block, modified_blocks);
201                                 //v3s16 p = i.getNode()->getKey();
202                                 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
203                         }
204
205                         /*
206                                 Collect a list of blocks that have been modified in
207                                 addition to the fetched one.
208                         */
209
210                         // Add all the "changed blocks" to modified_blocks
211                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
212                                         i.atEnd() == false; i++)
213                         {
214                                 MapBlock *block = i.getNode()->getValue();
215                                 modified_blocks.insert(block->getPos(), block);
216                         }
217                         
218                         /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
219                                         <<" blocks"<<std::endl;
220                         TimeTaker timer("** updateLighting", g_device);*/
221                         
222                         // Update lighting without locking the environment mutex,
223                         // add modified blocks to changed blocks
224                         map.updateLighting(lighting_invalidated_blocks, modified_blocks);
225                 }
226                 // If we got no block, there should be no invalidated blocks
227                 else
228                 {
229                         assert(lighting_invalidated_blocks.size() == 0);
230                 }
231
232                 }//envlock
233
234                 /*
235                         Set sent status of modified blocks on clients
236                 */
237         
238                 // NOTE: Server's clients are also behind the connection mutex
239                 JMutexAutoLock lock(m_server->m_con_mutex);
240
241                 /*
242                         Add the originally fetched block to the modified list
243                 */
244                 if(got_block)
245                 {
246                         modified_blocks.insert(p, block);
247                 }
248                 
249                 /*
250                         Set the modified blocks unsent for all the clients
251                 */
252                 
253                 for(core::map<u16, RemoteClient*>::Iterator
254                                 i = m_server->m_clients.getIterator();
255                                 i.atEnd() == false; i++)
256                 {
257                         RemoteClient *client = i.getNode()->getValue();
258                         
259                         if(modified_blocks.size() > 0)
260                         {
261                                 // Remove block from sent history
262                                 client->SetBlocksNotSent(modified_blocks);
263                         }
264                 }
265                 
266         }
267 #if CATCH_UNHANDLED_EXCEPTIONS
268         }//try
269         /*
270                 This is what has to be done in threads to get suitable debug info
271         */
272         catch(std::exception &e)
273         {
274                 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
275                                 <<e.what()<<std::endl;
276                 assert(0);
277         }
278 #endif
279
280         return NULL;
281 }
282
283 void RemoteClient::GetNextBlocks(Server *server, float dtime,
284                 core::array<PrioritySortedBlockTransfer> &dest)
285 {
286         DSTACK(__FUNCTION_NAME);
287         
288         // Won't send anything if already sending
289         {
290                 JMutexAutoLock lock(m_blocks_sending_mutex);
291                 
292                 if(m_blocks_sending.size() >= g_settings.getU16
293                                 ("max_simultaneous_block_sends_per_client"))
294                 {
295                         //dstream<<"Not sending any blocks, Queue full."<<std::endl;
296                         return;
297                 }
298         }
299
300         Player *player = server->m_env.getPlayer(peer_id);
301
302         v3f playerpos = player->getPosition();
303         v3f playerspeed = player->getSpeed();
304
305         v3s16 center_nodepos = floatToInt(playerpos);
306
307         v3s16 center = getNodeBlockPos(center_nodepos);
308
309         /*
310                 Get the starting value of the block finder radius.
311         */
312         s16 last_nearest_unsent_d;
313         s16 d_start;
314         {
315                 JMutexAutoLock lock(m_blocks_sent_mutex);
316                 
317                 if(m_last_center != center)
318                 {
319                         m_nearest_unsent_d = 0;
320                         m_last_center = center;
321                 }
322
323                 static float reset_counter = 0;
324                 reset_counter += dtime;
325                 if(reset_counter > 5.0)
326                 {
327                         reset_counter = 0;
328                         m_nearest_unsent_d = 0;
329                 }
330
331                 last_nearest_unsent_d = m_nearest_unsent_d;
332                 
333                 d_start = m_nearest_unsent_d;
334         }
335
336         u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
337                         ("max_simultaneous_block_sends_per_client");
338         u16 maximum_simultaneous_block_sends = 
339                         maximum_simultaneous_block_sends_setting;
340
341         /*
342                 Check the time from last addNode/removeNode.
343                 
344                 Decrease send rate if player is building stuff.
345         */
346         {
347                 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
348                 m_time_from_building.m_value += dtime;
349                 if(m_time_from_building.m_value
350                                 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)
351                 {
352                         maximum_simultaneous_block_sends
353                                 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
354                 }
355         }
356
357         // Serialization version used
358         //u8 ser_version = serialization_version;
359
360         //bool has_incomplete_blocks = false;
361         
362         s16 d_max = g_settings.getS16("max_block_send_distance");
363         s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
364         
365         //dstream<<"Starting from "<<d_start<<std::endl;
366
367         for(s16 d = d_start; d <= d_max; d++)
368         {
369                 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
370                 
371                 //if(has_incomplete_blocks == false)
372                 {
373                         JMutexAutoLock lock(m_blocks_sent_mutex);
374                         /*
375                                 If m_nearest_unsent_d was changed by the EmergeThread
376                                 (it can change it to 0 through SetBlockNotSent),
377                                 update our d to it.
378                                 Else update m_nearest_unsent_d
379                         */
380                         if(m_nearest_unsent_d != last_nearest_unsent_d)
381                         {
382                                 d = m_nearest_unsent_d;
383                         }
384                         else
385                         {
386                                 m_nearest_unsent_d = d;
387                         }
388                         last_nearest_unsent_d = m_nearest_unsent_d;
389                 }
390
391                 /*
392                         Get the border/face dot coordinates of a "d-radiused"
393                         box
394                 */
395                 core::list<v3s16> list;
396                 getFacePositions(list, d);
397                 
398                 core::list<v3s16>::Iterator li;
399                 for(li=list.begin(); li!=list.end(); li++)
400                 {
401                         v3s16 p = *li + center;
402                         
403                         /*
404                                 Send throttling
405                                 - Don't allow too many simultaneous transfers
406                                 - EXCEPT when the blocks are very close
407
408                                 Also, don't send blocks that are already flying.
409                         */
410                         
411                         u16 maximum_simultaneous_block_sends_now =
412                                         maximum_simultaneous_block_sends;
413                         
414                         if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
415                         {
416                                 maximum_simultaneous_block_sends_now =
417                                                 maximum_simultaneous_block_sends_setting;
418                         }
419
420                         {
421                                 JMutexAutoLock lock(m_blocks_sending_mutex);
422                                 
423                                 // Limit is dynamically lowered when building
424                                 if(m_blocks_sending.size()
425                                                 >= maximum_simultaneous_block_sends_now)
426                                 {
427                                         /*dstream<<"Not sending more blocks. Queue full. "
428                                                         <<m_blocks_sending.size()
429                                                         <<std::endl;*/
430                                         return;
431                                 }
432
433                                 if(m_blocks_sending.find(p) != NULL)
434                                         continue;
435                         }
436                         
437                         /*
438                                 Do not go over-limit
439                         */
440                         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
441                         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
442                         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
443                         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
444                         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
445                         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
446                                 continue;
447
448                         bool generate = d <= d_max_gen;
449                 
450                         // Limit the generating area vertically to half
451                         if(abs(p.Y - center.Y) > d_max_gen / 2)
452                                 generate = false;
453                         
454                         /*
455                                 Don't send already sent blocks
456                         */
457                         {
458                                 JMutexAutoLock lock(m_blocks_sent_mutex);
459                                 
460                                 if(m_blocks_sent.find(p) != NULL)
461                                         continue;
462                         }
463                                         
464                         /*
465                                 Check if map has this block
466                         */
467                         MapBlock *block = NULL;
468                         try
469                         {
470                                 block = server->m_env.getMap().getBlockNoCreate(p);
471                         }
472                         catch(InvalidPositionException &e)
473                         {
474                         }
475                         
476                         bool surely_not_found_on_disk = false;
477                         if(block != NULL)
478                         {
479                                 /*if(block->isIncomplete())
480                                 {
481                                         has_incomplete_blocks = true;
482                                         continue;
483                                 }*/
484
485                                 if(block->isDummy())
486                                 {
487                                         surely_not_found_on_disk = true;
488                                 }
489                         }
490
491                         /*
492                                 If block has been marked to not exist on disk (dummy)
493                                 and generating new ones is not wanted, skip block.
494                         */
495                         if(generate == false && surely_not_found_on_disk == true)
496                         {
497                                 // get next one.
498                                 continue;
499                         }
500
501                         /*
502                                 Add inexistent block to emerge queue.
503                         */
504                         if(block == NULL || surely_not_found_on_disk)
505                         {
506                                 /*SharedPtr<JMutexAutoLock> lock
507                                                 (m_num_blocks_in_emerge_queue.getLock());*/
508                                 
509                                 //TODO: Get value from somewhere
510                                 // Allow only one block in emerge queue
511                                 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
512                                 {
513                                         // Add it to the emerge queue and trigger the thread
514                                         
515                                         u8 flags = 0;
516                                         if(generate == false)
517                                                 flags |= TOSERVER_GETBLOCK_FLAG_OPTIONAL;
518                                         
519                                         server->m_emerge_queue.addBlock(peer_id, p, flags);
520                                         server->m_emergethread.trigger();
521                                 }
522                                 
523                                 // get next one.
524                                 continue;
525                         }
526
527                         /*
528                                 Add block to queue
529                         */
530
531                         PrioritySortedBlockTransfer q((float)d, p, peer_id);
532
533                         dest.push_back(q);
534                 }
535         }
536
537         // Don't add anything here. The loop breaks by returning.
538 }
539
540 void RemoteClient::SendObjectData(
541                 Server *server,
542                 float dtime,
543                 core::map<v3s16, bool> &stepped_blocks
544         )
545 {
546         DSTACK(__FUNCTION_NAME);
547
548         // Can't send anything without knowing version
549         if(serialization_version == SER_FMT_VER_INVALID)
550         {
551                 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
552                                 <<std::endl;
553                 return;
554         }
555
556         /*
557                 Send a TOCLIENT_OBJECTDATA packet.
558                 Sent as unreliable.
559
560                 u16 command
561                 u16 number of player positions
562                 for each player:
563                         v3s32 position*100
564                         v3s32 speed*100
565                         s32 pitch*100
566                         s32 yaw*100
567                 u16 count of blocks
568                 for each block:
569                         block objects
570         */
571
572         std::ostringstream os(std::ios_base::binary);
573         u8 buf[12];
574         
575         // Write command
576         writeU16(buf, TOCLIENT_OBJECTDATA);
577         os.write((char*)buf, 2);
578         
579         /*
580                 Get and write player data
581         */
582
583         core::list<Player*> players = server->m_env.getPlayers();
584
585         // Write player count
586         u16 playercount = players.size();
587         writeU16(buf, playercount);
588         os.write((char*)buf, 2);
589
590         core::list<Player*>::Iterator i;
591         for(i = players.begin();
592                         i != players.end(); i++)
593         {
594                 Player *player = *i;
595
596                 v3f pf = player->getPosition();
597                 v3f sf = player->getSpeed();
598
599                 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
600                 v3s32 speed_i   (sf.X*100, sf.Y*100, sf.Z*100);
601                 s32   pitch_i   (player->getPitch() * 100);
602                 s32   yaw_i     (player->getYaw() * 100);
603                 
604                 writeU16(buf, player->peer_id);
605                 os.write((char*)buf, 2);
606                 writeV3S32(buf, position_i);
607                 os.write((char*)buf, 12);
608                 writeV3S32(buf, speed_i);
609                 os.write((char*)buf, 12);
610                 writeS32(buf, pitch_i);
611                 os.write((char*)buf, 4);
612                 writeS32(buf, yaw_i);
613                 os.write((char*)buf, 4);
614         }
615         
616         /*
617                 Get and write object data
618         */
619
620         /*
621                 Get nearby blocks.
622                 
623                 For making players to be able to build to their nearby
624                 environment (building is not possible on blocks that are not
625                 in memory):
626                 - Set blocks changed
627                 - Add blocks to emerge queue if they are not found
628
629                 SUGGESTION: These could be ignored from the backside of the player
630
631                 TODO: Keep track of total size of packet and stop when it is too big
632         */
633
634         Player *player = server->m_env.getPlayer(peer_id);
635
636         v3f playerpos = player->getPosition();
637         v3f playerspeed = player->getSpeed();
638
639         v3s16 center_nodepos = floatToInt(playerpos);
640         v3s16 center = getNodeBlockPos(center_nodepos);
641
642         //s16 d_max = ACTIVE_OBJECT_D_BLOCKS;
643         s16 d_max = g_settings.getS16("active_object_range");
644         
645         // Number of blocks whose objects were written to bos
646         u16 blockcount = 0;
647
648         //core::map<v3s16, MapBlock*> blocks;
649         std::ostringstream bos(std::ios_base::binary);
650
651         for(s16 d = 0; d <= d_max; d++)
652         {
653                 core::list<v3s16> list;
654                 getFacePositions(list, d);
655                 
656                 core::list<v3s16>::Iterator li;
657                 for(li=list.begin(); li!=list.end(); li++)
658                 {
659                         v3s16 p = *li + center;
660
661                         /*
662                                 Ignore blocks that haven't been sent to the client
663                         */
664                         {
665                                 JMutexAutoLock sentlock(m_blocks_sent_mutex);
666                                 if(m_blocks_sent.find(p) == NULL)
667                                         continue;
668                         }
669                         
670                         // Try stepping block and add it to a send queue
671                         try
672                         {
673
674                         // Get block
675                         MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
676
677                         // Skip block if there are no objects
678                         if(block->getObjectCount() == 0)
679                                 continue;
680                         
681                         // Step block if not in stepped_blocks and add to stepped_blocks
682                         if(stepped_blocks.find(p) == NULL)
683                         {
684                                 block->stepObjects(dtime, true);
685                                 stepped_blocks.insert(p, true);
686                                 block->setChangedFlag();
687                         }
688
689                         /*
690                                 Write objects
691                         */
692
693                         // Write blockpos
694                         writeV3S16(buf, p);
695                         bos.write((char*)buf, 6);
696
697                         // Write objects
698                         block->serializeObjects(bos, serialization_version);
699
700                         blockcount++;
701
702                         /*
703                                 Stop collecting objects if data is already too big
704                         */
705                         // Sum of player and object data sizes
706                         s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
707                         // break out if data too big
708                         if(sum > MAX_OBJECTDATA_SIZE)
709                         {
710                                 goto skip_subsequent;
711                         }
712                         
713                         } //try
714                         catch(InvalidPositionException &e)
715                         {
716                                 // Not in memory
717                                 // Add it to the emerge queue and trigger the thread.
718                                 // Fetch the block only if it is on disk.
719                                 
720                                 // Grab and increment counter
721                                 /*SharedPtr<JMutexAutoLock> lock
722                                                 (m_num_blocks_in_emerge_queue.getLock());
723                                 m_num_blocks_in_emerge_queue.m_value++;*/
724                                 
725                                 // Add to queue as an anonymous fetch from disk
726                                 u8 flags = TOSERVER_GETBLOCK_FLAG_OPTIONAL;
727                                 server->m_emerge_queue.addBlock(0, p, flags);
728                                 server->m_emergethread.trigger();
729                         }
730                 }
731         }
732
733 skip_subsequent:
734
735         // Write block count
736         writeU16(buf, blockcount);
737         os.write((char*)buf, 2);
738
739         // Write block objects
740         os<<bos.str();
741
742         /*
743                 Send data
744         */
745         
746         //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
747
748         // Make data buffer
749         std::string s = os.str();
750         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
751         // Send as unreliable
752         server->m_con.Send(peer_id, 0, data, false);
753 }
754
755 void RemoteClient::GotBlock(v3s16 p)
756 {
757         JMutexAutoLock lock(m_blocks_sending_mutex);
758         JMutexAutoLock lock2(m_blocks_sent_mutex);
759         if(m_blocks_sending.find(p) != NULL)
760                 m_blocks_sending.remove(p);
761         else
762                 dstream<<"RemoteClient::GotBlock(): Didn't find in"
763                                 " m_blocks_sending"<<std::endl;
764         m_blocks_sent.insert(p, true);
765 }
766
767 void RemoteClient::SentBlock(v3s16 p)
768 {
769         JMutexAutoLock lock(m_blocks_sending_mutex);
770         if(m_blocks_sending.size() > 15)
771         {
772                 dstream<<"RemoteClient::SentBlock(): "
773                                 <<"m_blocks_sending.size()="
774                                 <<m_blocks_sending.size()<<std::endl;
775         }
776         if(m_blocks_sending.find(p) == NULL)
777                 m_blocks_sending.insert(p, 0.0);
778         else
779                 dstream<<"RemoteClient::SentBlock(): Sent block"
780                                 " already in m_blocks_sending"<<std::endl;
781 }
782
783 void RemoteClient::SetBlockNotSent(v3s16 p)
784 {
785         JMutexAutoLock sendinglock(m_blocks_sending_mutex);
786         JMutexAutoLock sentlock(m_blocks_sent_mutex);
787
788         m_nearest_unsent_d = 0;
789         
790         if(m_blocks_sending.find(p) != NULL)
791                 m_blocks_sending.remove(p);
792         if(m_blocks_sent.find(p) != NULL)
793                 m_blocks_sent.remove(p);
794 }
795
796 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
797 {
798         JMutexAutoLock sendinglock(m_blocks_sending_mutex);
799         JMutexAutoLock sentlock(m_blocks_sent_mutex);
800
801         m_nearest_unsent_d = 0;
802         
803         for(core::map<v3s16, MapBlock*>::Iterator
804                         i = blocks.getIterator();
805                         i.atEnd()==false; i++)
806         {
807                 v3s16 p = i.getNode()->getKey();
808
809                 if(m_blocks_sending.find(p) != NULL)
810                         m_blocks_sending.remove(p);
811                 if(m_blocks_sent.find(p) != NULL)
812                         m_blocks_sent.remove(p);
813         }
814 }
815
816 /*void RemoteClient::BlockEmerged()
817 {
818         SharedPtr<JMutexAutoLock> lock(m_num_blocks_in_emerge_queue.getLock());
819         assert(m_num_blocks_in_emerge_queue.m_value > 0);
820         m_num_blocks_in_emerge_queue.m_value--;
821 }*/
822
823 /*void RemoteClient::RunSendingTimeouts(float dtime, float timeout)
824 {
825         JMutexAutoLock sendinglock(m_blocks_sending_mutex);
826         
827         core::list<v3s16> remove_queue;
828         for(core::map<v3s16, float>::Iterator
829                         i = m_blocks_sending.getIterator();
830                         i.atEnd()==false; i++)
831         {
832                 v3s16 p = i.getNode()->getKey();
833                 float t = i.getNode()->getValue();
834                 t += dtime;
835                 i.getNode()->setValue(t);
836
837                 if(t > timeout)
838                 {
839                         remove_queue.push_back(p);
840                 }
841         }
842         for(core::list<v3s16>::Iterator
843                         i = remove_queue.begin();
844                         i != remove_queue.end(); i++)
845         {
846                 m_blocks_sending.remove(*i);
847         }
848 }*/
849
850 /*
851         PlayerInfo
852 */
853
854 PlayerInfo::PlayerInfo()
855 {
856         name[0] = 0;
857 }
858
859 void PlayerInfo::PrintLine(std::ostream *s)
860 {
861         (*s)<<id<<": \""<<name<<"\" ("
862                         <<position.X<<","<<position.Y
863                         <<","<<position.Z<<") ";
864         address.print(s);
865         (*s)<<" avg_rtt="<<avg_rtt;
866         (*s)<<std::endl;
867 }
868
869 u32 PIChecksum(core::list<PlayerInfo> &l)
870 {
871         core::list<PlayerInfo>::Iterator i;
872         u32 checksum = 1;
873         u32 a = 10;
874         for(i=l.begin(); i!=l.end(); i++)
875         {
876                 checksum += a * (i->id+1);
877                 checksum ^= 0x435aafcd;
878                 a *= 10;
879         }
880         return checksum;
881 }
882
883 /*
884         Server
885 */
886
887 Server::Server(
888                 std::string mapsavedir,
889                 HMParams hm_params,
890                 MapParams map_params
891         ):
892         m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
893         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
894         m_thread(this),
895         m_emergethread(this)
896 {
897         m_env_mutex.Init();
898         m_con_mutex.Init();
899         m_step_dtime_mutex.Init();
900         m_step_dtime = 0.0;
901 }
902
903 Server::~Server()
904 {
905         // Stop threads
906         stop();
907
908         JMutexAutoLock clientslock(m_con_mutex);
909
910         for(core::map<u16, RemoteClient*>::Iterator
911                 i = m_clients.getIterator();
912                 i.atEnd() == false; i++)
913         {
914                 u16 peer_id = i.getNode()->getKey();
915
916                 // Delete player
917                 {
918                         JMutexAutoLock envlock(m_env_mutex);
919                         m_env.removePlayer(peer_id);
920                 }
921                 
922                 // Delete client
923                 delete i.getNode()->getValue();
924         }
925 }
926
927 void Server::start(unsigned short port)
928 {
929         DSTACK(__FUNCTION_NAME);
930         // Stop thread if already running
931         m_thread.stop();
932         
933         // Initialize connection
934         m_con.setTimeoutMs(30);
935         m_con.Serve(port);
936
937         // Start thread
938         m_thread.setRun(true);
939         m_thread.Start();
940         
941         dout_server<<"Server started on port "<<port<<std::endl;
942 }
943
944 void Server::stop()
945 {
946         DSTACK(__FUNCTION_NAME);
947         // Stop threads (set run=false first so both start stopping)
948         m_thread.setRun(false);
949         m_emergethread.setRun(false);
950         m_thread.stop();
951         m_emergethread.stop();
952         
953         dout_server<<"Server threads stopped"<<std::endl;
954 }
955
956 void Server::step(float dtime)
957 {
958         DSTACK(__FUNCTION_NAME);
959         // Limit a bit
960         if(dtime > 2.0)
961                 dtime = 2.0;
962         {
963                 JMutexAutoLock lock(m_step_dtime_mutex);
964                 m_step_dtime += dtime;
965         }
966 }
967
968 void Server::AsyncRunStep()
969 {
970         DSTACK(__FUNCTION_NAME);
971         
972         float dtime;
973         {
974                 JMutexAutoLock lock1(m_step_dtime_mutex);
975                 dtime = m_step_dtime;
976         }
977         
978         // Send blocks to clients
979         SendBlocks(dtime);
980         
981         if(dtime < 0.001)
982                 return;
983         
984         {
985                 JMutexAutoLock lock1(m_step_dtime_mutex);
986                 m_step_dtime = 0.0;
987         }
988
989         //dstream<<"Server steps "<<dtime<<std::endl;
990         
991         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
992         {
993                 // Has to be locked for peerAdded/Removed
994                 JMutexAutoLock lock1(m_env_mutex);
995                 // Process connection's timeouts
996                 JMutexAutoLock lock2(m_con_mutex);
997                 m_con.RunTimeouts(dtime);
998         }
999         {
1000                 // Step environment
1001                 // This also runs Map's timers
1002                 JMutexAutoLock lock(m_env_mutex);
1003                 m_env.step(dtime);
1004         }
1005         
1006         /*
1007                 Do background stuff
1008         */
1009
1010         /*
1011                 Flow water
1012         */
1013         {
1014                 float interval;
1015                 
1016                 if(g_settings.getBool("endless_water") == false)
1017                         interval = 1.0;
1018                 else
1019                         interval = 0.25;
1020
1021                 static float counter = 0.0;
1022                 counter += dtime;
1023                 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1024                 {
1025                 
1026                 counter = 0.0;
1027
1028                 core::map<v3s16, MapBlock*> modified_blocks;
1029
1030                 {
1031
1032                         JMutexAutoLock envlock(m_env_mutex);
1033                         
1034                         MapVoxelManipulator v(&m_env.getMap());
1035                         v.m_disable_water_climb =
1036                                         g_settings.getBool("disable_water_climb");
1037                         
1038                         if(g_settings.getBool("endless_water") == false)
1039                                 v.flowWater(m_flow_active_nodes, 0, false, 250);
1040                         else
1041                                 v.flowWater(m_flow_active_nodes, 0, false, 50);
1042
1043                         v.blitBack(modified_blocks);
1044
1045                         ServerMap &map = ((ServerMap&)m_env.getMap());
1046                         
1047                         // Update lighting
1048                         core::map<v3s16, MapBlock*> lighting_modified_blocks;
1049                         map.updateLighting(modified_blocks, lighting_modified_blocks);
1050                         
1051                         // Add blocks modified by lighting to modified_blocks
1052                         for(core::map<v3s16, MapBlock*>::Iterator
1053                                         i = lighting_modified_blocks.getIterator();
1054                                         i.atEnd() == false; i++)
1055                         {
1056                                 MapBlock *block = i.getNode()->getValue();
1057                                 modified_blocks.insert(block->getPos(), block);
1058                         }
1059                 } // envlock
1060
1061                 /*
1062                         Set the modified blocks unsent for all the clients
1063                 */
1064                 
1065                 JMutexAutoLock lock2(m_con_mutex);
1066
1067                 for(core::map<u16, RemoteClient*>::Iterator
1068                                 i = m_clients.getIterator();
1069                                 i.atEnd() == false; i++)
1070                 {
1071                         RemoteClient *client = i.getNode()->getValue();
1072                         
1073                         if(modified_blocks.size() > 0)
1074                         {
1075                                 // Remove block from sent history
1076                                 client->SetBlocksNotSent(modified_blocks);
1077                         }
1078                 }
1079
1080                 } // interval counter
1081         }
1082         
1083         // Periodically print some info
1084         {
1085                 static float counter = 0.0;
1086                 counter += dtime;
1087                 if(counter >= 30.0)
1088                 {
1089                         counter = 0.0;
1090
1091                         JMutexAutoLock lock2(m_con_mutex);
1092
1093                         for(core::map<u16, RemoteClient*>::Iterator
1094                                 i = m_clients.getIterator();
1095                                 i.atEnd() == false; i++)
1096                         {
1097                                 //u16 peer_id = i.getNode()->getKey();
1098                                 RemoteClient *client = i.getNode()->getValue();
1099                                 client->PrintInfo(std::cout);
1100                         }
1101                 }
1102         }
1103
1104         // Run time- and client- related stuff
1105         // NOTE: If you intend to add something here, check that it
1106         // doesn't fit in RemoteClient::GetNextBlocks for example.
1107         /*{
1108                 // Clients are behind connection lock
1109                 JMutexAutoLock lock(m_con_mutex);
1110
1111                 for(core::map<u16, RemoteClient*>::Iterator
1112                         i = m_clients.getIterator();
1113                         i.atEnd() == false; i++)
1114                 {
1115                         RemoteClient *client = i.getNode()->getValue();
1116                         //con::Peer *peer = m_con.GetPeer(client->peer_id);
1117                         //client->RunSendingTimeouts(dtime, peer->resend_timeout);
1118                 }
1119         }*/
1120
1121         // Send object positions
1122         {
1123                 static float counter = 0.0;
1124                 counter += dtime;
1125                 if(counter >= g_settings.getFloat("objectdata_interval"))
1126                 {
1127                         JMutexAutoLock lock1(m_env_mutex);
1128                         JMutexAutoLock lock2(m_con_mutex);
1129                         SendObjectData(counter);
1130
1131                         counter = 0.0;
1132                 }
1133         }
1134         
1135         // Trigger emergethread (it gets somehow gets to a
1136         // non-triggered but bysy state sometimes)
1137         {
1138                 static float counter = 0.0;
1139                 counter += dtime;
1140                 if(counter >= 2.0)
1141                 {
1142                         counter = 0.0;
1143                         
1144                         m_emergethread.trigger();
1145                 }
1146         }
1147
1148         // Save map
1149         {
1150                 static float counter = 0.0;
1151                 counter += dtime;
1152                 if(counter >= SERVER_MAP_SAVE_INTERVAL)
1153                 {
1154                         counter = 0.0;
1155
1156                         JMutexAutoLock lock(m_env_mutex);
1157                         // Save only changed parts
1158                         m_env.getMap().save(true);
1159                 }
1160         }
1161 }
1162
1163 void Server::Receive()
1164 {
1165         DSTACK(__FUNCTION_NAME);
1166         u32 data_maxsize = 10000;
1167         Buffer<u8> data(data_maxsize);
1168         u16 peer_id;
1169         u32 datasize;
1170         try{
1171                 {
1172                         JMutexAutoLock lock(m_con_mutex);
1173                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1174                 }
1175                 ProcessData(*data, datasize, peer_id);
1176         }
1177         catch(con::InvalidIncomingDataException &e)
1178         {
1179                 derr_server<<"Server::Receive(): "
1180                                 "InvalidIncomingDataException: what()="
1181                                 <<e.what()<<std::endl;
1182         }
1183         catch(con::PeerNotFoundException &e)
1184         {
1185                 //NOTE: This is not needed anymore
1186                 
1187                 // The peer has been disconnected.
1188                 // Find the associated player and remove it.
1189
1190                 /*JMutexAutoLock envlock(m_env_mutex);
1191
1192                 dout_server<<"ServerThread: peer_id="<<peer_id
1193                                 <<" has apparently closed connection. "
1194                                 <<"Removing player."<<std::endl;
1195
1196                 m_env.removePlayer(peer_id);*/
1197         }
1198 }
1199
1200 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1201 {
1202         DSTACK(__FUNCTION_NAME);
1203         // Environment is locked first.
1204         JMutexAutoLock envlock(m_env_mutex);
1205         JMutexAutoLock conlock(m_con_mutex);
1206         
1207         con::Peer *peer;
1208         try{
1209                 peer = m_con.GetPeer(peer_id);
1210         }
1211         catch(con::PeerNotFoundException &e)
1212         {
1213                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1214                                 <<peer_id<<" not found"<<std::endl;
1215                 return;
1216         }
1217         
1218         //u8 peer_ser_ver = peer->serialization_version;
1219         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1220
1221         try
1222         {
1223
1224         if(datasize < 2)
1225                 return;
1226
1227         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1228         
1229         if(command == TOSERVER_INIT)
1230         {
1231                 // [0] u16 TOSERVER_INIT
1232                 // [2] u8 SER_FMT_VER_HIGHEST
1233                 // [3] u8[20] player_name
1234
1235                 if(datasize < 3)
1236                         return;
1237
1238                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1239                                 <<peer->id<<std::endl;
1240
1241                 // First byte after command is maximum supported
1242                 // serialization version
1243                 u8 client_max = data[2];
1244                 u8 our_max = SER_FMT_VER_HIGHEST;
1245                 // Use the highest version supported by both
1246                 u8 deployed = core::min_(client_max, our_max);
1247                 // If it's lower than the lowest supported, give up.
1248                 if(deployed < SER_FMT_VER_LOWEST)
1249                         deployed = SER_FMT_VER_INVALID;
1250
1251                 //peer->serialization_version = deployed;
1252                 getClient(peer->id)->pending_serialization_version = deployed;
1253
1254                 if(deployed == SER_FMT_VER_INVALID)
1255                 {
1256                         derr_server<<DTIME<<"Server: Cannot negotiate "
1257                                         "serialization version with peer "
1258                                         <<peer_id<<std::endl;
1259                         return;
1260                 }
1261
1262                 /*
1263                         Set up player
1264                 */
1265
1266                 Player *player = m_env.getPlayer(peer_id);
1267
1268                 // Check if player doesn't exist
1269                 if(player == NULL)
1270                         throw con::InvalidIncomingDataException
1271                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1272
1273                 // update name if it was supplied
1274                 if(datasize >= 20+3)
1275                 {
1276                         data[20+3-1] = 0;
1277                         player->updateName((const char*)&data[3]);
1278                 }
1279
1280                 // Now answer with a TOCLIENT_INIT
1281                 
1282                 SharedBuffer<u8> reply(2+1+6);
1283                 writeU16(&reply[0], TOCLIENT_INIT);
1284                 writeU8(&reply[2], deployed);
1285                 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1286                 // Send as reliable
1287                 m_con.Send(peer_id, 0, reply, true);
1288
1289                 return;
1290         }
1291         if(command == TOSERVER_INIT2)
1292         {
1293                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1294                                 <<peer->id<<std::endl;
1295
1296
1297                 getClient(peer->id)->serialization_version
1298                                 = getClient(peer->id)->pending_serialization_version;
1299
1300                 /*
1301                         Send some initialization data
1302                 */
1303                 
1304                 // Send player info to all players
1305                 SendPlayerInfos();
1306
1307                 // Send inventory to player
1308                 SendInventory(peer->id);
1309
1310                 return;
1311         }
1312
1313         if(peer_ser_ver == SER_FMT_VER_INVALID)
1314         {
1315                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1316                                 " serialization format invalid or not initialized."
1317                                 " Skipping incoming command="<<command<<std::endl;
1318                 return;
1319         }
1320         
1321         Player *player = m_env.getPlayer(peer_id);
1322
1323         if(player == NULL){
1324                 derr_server<<"Server::ProcessData(): Cancelling: "
1325                                 "No player for peer_id="<<peer_id
1326                                 <<std::endl;
1327                 return;
1328         }
1329         if(command == TOSERVER_PLAYERPOS)
1330         {
1331                 if(datasize < 2+12+12+4+4)
1332                         return;
1333         
1334                 u32 start = 0;
1335                 v3s32 ps = readV3S32(&data[start+2]);
1336                 v3s32 ss = readV3S32(&data[start+2+12]);
1337                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1338                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1339                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1340                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1341                 pitch = wrapDegrees(pitch);
1342                 yaw = wrapDegrees(yaw);
1343                 player->setPosition(position);
1344                 player->setSpeed(speed);
1345                 player->setPitch(pitch);
1346                 player->setYaw(yaw);
1347                 
1348                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1349                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1350                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1351         }
1352         else if(command == TOSERVER_GOTBLOCKS)
1353         {
1354                 if(datasize < 2+1)
1355                         return;
1356                 
1357                 /*
1358                         [0] u16 command
1359                         [2] u8 count
1360                         [3] v3s16 pos_0
1361                         [3+6] v3s16 pos_1
1362                         ...
1363                 */
1364
1365                 u16 count = data[2];
1366                 for(u16 i=0; i<count; i++)
1367                 {
1368                         if((s16)datasize < 2+1+(i+1)*6)
1369                                 throw con::InvalidIncomingDataException
1370                                         ("GOTBLOCKS length is too short");
1371                         v3s16 p = readV3S16(&data[2+1+i*6]);
1372                         /*dstream<<"Server: GOTBLOCKS ("
1373                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1374                         RemoteClient *client = getClient(peer_id);
1375                         client->GotBlock(p);
1376                 }
1377         }
1378         else if(command == TOSERVER_DELETEDBLOCKS)
1379         {
1380                 if(datasize < 2+1)
1381                         return;
1382                 
1383                 /*
1384                         [0] u16 command
1385                         [2] u8 count
1386                         [3] v3s16 pos_0
1387                         [3+6] v3s16 pos_1
1388                         ...
1389                 */
1390
1391                 u16 count = data[2];
1392                 for(u16 i=0; i<count; i++)
1393                 {
1394                         if((s16)datasize < 2+1+(i+1)*6)
1395                                 throw con::InvalidIncomingDataException
1396                                         ("DELETEDBLOCKS length is too short");
1397                         v3s16 p = readV3S16(&data[2+1+i*6]);
1398                         /*dstream<<"Server: DELETEDBLOCKS ("
1399                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1400                         RemoteClient *client = getClient(peer_id);
1401                         client->SetBlockNotSent(p);
1402                 }
1403         }
1404         else if(command == TOSERVER_CLICK_OBJECT)
1405         {
1406                 if(datasize < 13)
1407                         return;
1408
1409                 /*
1410                         [0] u16 command
1411                         [2] u8 button (0=left, 1=right)
1412                         [3] v3s16 block
1413                         [9] s16 id
1414                         [11] u16 item
1415                 */
1416                 u8 button = readU8(&data[2]);
1417                 v3s16 p;
1418                 p.X = readS16(&data[3]);
1419                 p.Y = readS16(&data[5]);
1420                 p.Z = readS16(&data[7]);
1421                 s16 id = readS16(&data[9]);
1422                 //u16 item_i = readU16(&data[11]);
1423
1424                 MapBlock *block = NULL;
1425                 try
1426                 {
1427                         block = m_env.getMap().getBlockNoCreate(p);
1428                 }
1429                 catch(InvalidPositionException &e)
1430                 {
1431                         derr_server<<"PICK_OBJECT block not found"<<std::endl;
1432                         return;
1433                 }
1434
1435                 MapBlockObject *obj = block->getObject(id);
1436
1437                 if(obj == NULL)
1438                 {
1439                         derr_server<<"PICK_OBJECT object not found"<<std::endl;
1440                         return;
1441                 }
1442
1443                 //TODO: Check that object is reasonably close
1444                 
1445                 // Left click
1446                 if(button == 0)
1447                 {
1448                         if(g_settings.getBool("creative_mode") == false)
1449                         {
1450                         
1451                                 // Skip if inventory has no free space
1452                                 if(player->inventory.getUsedSlots() == player->inventory.getSize())
1453                                 {
1454                                         dout_server<<"Player inventory has no free space"<<std::endl;
1455                                         return;
1456                                 }
1457                         
1458                                 // Add to inventory and send inventory
1459                                 InventoryItem *item = new MapBlockObjectItem
1460                                                 (obj->getInventoryString());
1461                                 player->inventory.addItem(item);
1462                                 SendInventory(player->peer_id);
1463                         }
1464
1465                         // Remove from block
1466                         block->removeObject(id);
1467                 }
1468         }
1469         else if(command == TOSERVER_CLICK_GROUND)
1470         {
1471                 if(datasize < 17)
1472                         return;
1473                 /*
1474                         length: 17
1475                         [0] u16 command
1476                         [2] u8 button (0=left, 1=right)
1477                         [3] v3s16 nodepos_undersurface
1478                         [9] v3s16 nodepos_abovesurface
1479                         [15] u16 item
1480                 */
1481                 u8 button = readU8(&data[2]);
1482                 v3s16 p_under;
1483                 p_under.X = readS16(&data[3]);
1484                 p_under.Y = readS16(&data[5]);
1485                 p_under.Z = readS16(&data[7]);
1486                 v3s16 p_over;
1487                 p_over.X = readS16(&data[9]);
1488                 p_over.Y = readS16(&data[11]);
1489                 p_over.Z = readS16(&data[13]);
1490                 u16 item_i = readU16(&data[15]);
1491
1492                 //TODO: Check that target is reasonably close
1493                 
1494                 /*
1495                         Left button digs ground
1496                 */
1497                 if(button == 0)
1498                 {
1499
1500                         core::map<v3s16, MapBlock*> modified_blocks;
1501
1502                         u8 material;
1503
1504                         try
1505                         {
1506                                 // Get material at position
1507                                 material = m_env.getMap().getNode(p_under).d;
1508                                 // If it's not diggable, do nothing
1509                                 if(content_diggable(material) == false)
1510                                 {
1511                                         return;
1512                                 }
1513                         }
1514                         catch(InvalidPositionException &e)
1515                         {
1516                                 derr_server<<"Server: Ignoring REMOVENODE: Node not found"
1517                                                 <<std::endl;
1518                                 return;
1519                         }
1520                         
1521                         // Reset build time counter
1522                         getClient(peer->id)->m_time_from_building.set(0.0);
1523                         
1524                         // Create packet
1525                         u32 replysize = 8;
1526                         SharedBuffer<u8> reply(replysize);
1527                         writeU16(&reply[0], TOCLIENT_REMOVENODE);
1528                         writeS16(&reply[2], p_under.X);
1529                         writeS16(&reply[4], p_under.Y);
1530                         writeS16(&reply[6], p_under.Z);
1531                         // Send as reliable
1532                         m_con.SendToAll(0, reply, true);
1533                         
1534                         if(g_settings.getBool("creative_mode") == false)
1535                         {
1536                                 // Add to inventory and send inventory
1537                                 InventoryItem *item = new MaterialItem(material, 1);
1538                                 player->inventory.addItem(item);
1539                                 SendInventory(player->peer_id);
1540                         }
1541
1542                         /*
1543                                 Remove the node
1544                                 (this takes some time so it is done after the quick stuff)
1545                         */
1546                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1547                         
1548                         /*
1549                                 Update water
1550                         */
1551                         
1552                         // Update water pressure around modification
1553                         // This also adds it to m_flow_active_nodes if appropriate
1554
1555                         MapVoxelManipulator v(&m_env.getMap());
1556                         v.m_disable_water_climb =
1557                                         g_settings.getBool("disable_water_climb");
1558                         
1559                         VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1560
1561                         try
1562                         {
1563                                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1564                         }
1565                         catch(ProcessingLimitException &e)
1566                         {
1567                                 dstream<<"Processing limit reached (1)"<<std::endl;
1568                         }
1569                         
1570                         v.blitBack(modified_blocks);
1571                         
1572                         // Add the node to m_flow_active_nodes.
1573                         //m_flow_active_nodes[p_under] = 1;
1574
1575                 } // button == 0
1576                 /*
1577                         Right button places blocks and stuff
1578                 */
1579                 else if(button == 1)
1580                 {
1581
1582                         // Get item
1583                         InventoryItem *item = player->inventory.getItem(item_i);
1584                         
1585                         // If there is no item, it is not possible to add it anywhere
1586                         if(item == NULL)
1587                                 return;
1588                         
1589                         /*
1590                                 Handle material items
1591                         */
1592                         if(std::string("MaterialItem") == item->getName())
1593                         {
1594                                 try{
1595                                         // Don't add a node if this is not a free space
1596                                         MapNode n2 = m_env.getMap().getNode(p_over);
1597                                         if(content_buildable_to(n2.d) == false)
1598                                                 return;
1599                                 }
1600                                 catch(InvalidPositionException &e)
1601                                 {
1602                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
1603                                                         <<std::endl;
1604                                         return;
1605                                 }
1606
1607                                 // Reset build time counter
1608                                 getClient(peer->id)->m_time_from_building.set(0.0);
1609                                 
1610                                 // Create node data
1611                                 MaterialItem *mitem = (MaterialItem*)item;
1612                                 MapNode n;
1613                                 n.d = mitem->getMaterial();
1614                                 if(content_directional(n.d))
1615                                         n.dir = packDir(p_under - p_over);
1616
1617 #if 1
1618                                 // Create packet
1619                                 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
1620                                 SharedBuffer<u8> reply(replysize);
1621                                 writeU16(&reply[0], TOCLIENT_ADDNODE);
1622                                 writeS16(&reply[2], p_over.X);
1623                                 writeS16(&reply[4], p_over.Y);
1624                                 writeS16(&reply[6], p_over.Z);
1625                                 n.serialize(&reply[8], peer_ser_ver);
1626                                 // Send as reliable
1627                                 m_con.SendToAll(0, reply, true);
1628                                 
1629                                 /*
1630                                         Handle inventory
1631                                 */
1632                                 if(g_settings.getBool("creative_mode") == false)
1633                                 {
1634                                         // Remove from inventory and send inventory
1635                                         if(mitem->getCount() == 1)
1636                                                 player->inventory.deleteItem(item_i);
1637                                         else
1638                                                 mitem->remove(1);
1639                                         // Send inventory
1640                                         SendInventory(peer_id);
1641                                 }
1642                                 
1643                                 /*
1644                                         Add node.
1645
1646                                         This takes some time so it is done after the quick stuff
1647                                 */
1648                                 core::map<v3s16, MapBlock*> modified_blocks;
1649                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
1650 #endif
1651 #if 0
1652                                 /*
1653                                         Handle inventory
1654                                 */
1655                                 if(g_settings.getBool("creative_mode") == false)
1656                                 {
1657                                         // Remove from inventory and send inventory
1658                                         if(mitem->getCount() == 1)
1659                                                 player->inventory.deleteItem(item_i);
1660                                         else
1661                                                 mitem->remove(1);
1662                                         // Send inventory
1663                                         SendInventory(peer_id);
1664                                 }
1665
1666                                 /*
1667                                         Add node.
1668
1669                                         This takes some time so it is done after the quick stuff
1670                                 */
1671                                 core::map<v3s16, MapBlock*> modified_blocks;
1672                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
1673
1674                                 /*
1675                                         Set the modified blocks unsent for all the clients
1676                                 */
1677                                 
1678                                 //JMutexAutoLock lock2(m_con_mutex);
1679
1680                                 for(core::map<u16, RemoteClient*>::Iterator
1681                                                 i = m_clients.getIterator();
1682                                                 i.atEnd() == false; i++)
1683                                 {
1684                                         RemoteClient *client = i.getNode()->getValue();
1685                                         
1686                                         if(modified_blocks.size() > 0)
1687                                         {
1688                                                 // Remove block from sent history
1689                                                 client->SetBlocksNotSent(modified_blocks);
1690                                         }
1691                                 }
1692 #endif
1693                                 
1694                                 /*
1695                                         Update water
1696                                 */
1697                                 
1698                                 // Update water pressure around modification
1699                                 // This also adds it to m_flow_active_nodes if appropriate
1700
1701                                 MapVoxelManipulator v(&m_env.getMap());
1702                                 v.m_disable_water_climb =
1703                                                 g_settings.getBool("disable_water_climb");
1704                                 
1705                                 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
1706
1707                                 try
1708                                 {
1709                                         v.updateAreaWaterPressure(area, m_flow_active_nodes);
1710                                 }
1711                                 catch(ProcessingLimitException &e)
1712                                 {
1713                                         dstream<<"Processing limit reached (1)"<<std::endl;
1714                                 }
1715                                 
1716                                 v.blitBack(modified_blocks);
1717                         }
1718                         /*
1719                                 Handle block object items
1720                         */
1721                         else if(std::string("MBOItem") == item->getName())
1722                         {
1723                                 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
1724
1725                                 /*dout_server<<"Trying to place a MapBlockObjectItem: "
1726                                                 "inventorystring=\""
1727                                                 <<oitem->getInventoryString()
1728                                                 <<"\""<<std::endl;*/
1729
1730                                 v3s16 blockpos = getNodeBlockPos(p_over);
1731
1732                                 MapBlock *block = NULL;
1733                                 try
1734                                 {
1735                                         block = m_env.getMap().getBlockNoCreate(blockpos);
1736                                 }
1737                                 catch(InvalidPositionException &e)
1738                                 {
1739                                         derr_server<<"Error while placing object: "
1740                                                         "block not found"<<std::endl;
1741                                         return;
1742                                 }
1743
1744                                 v3s16 block_pos_i_on_map = block->getPosRelative();
1745                                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1746
1747                                 v3f pos = intToFloat(p_over);
1748                                 pos -= block_pos_f_on_map;
1749                                 
1750                                 /*dout_server<<"pos="
1751                                                 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1752                                                 <<std::endl;*/
1753
1754
1755                                 MapBlockObject *obj = oitem->createObject
1756                                                 (pos, player->getYaw(), player->getPitch());
1757
1758                                 if(obj == NULL)
1759                                         derr_server<<"WARNING: oitem created NULL object"
1760                                                         <<std::endl;
1761
1762                                 block->addObject(obj);
1763
1764                                 //dout_server<<"Placed object"<<std::endl;
1765
1766                                 if(g_settings.getBool("creative_mode") == false)
1767                                 {
1768                                         // Remove from inventory and send inventory
1769                                         player->inventory.deleteItem(item_i);
1770                                         // Send inventory
1771                                         SendInventory(peer_id);
1772                                 }
1773                         }
1774
1775                 } // button == 1
1776                 /*
1777                         Catch invalid buttons
1778                 */
1779                 else
1780                 {
1781                         derr_server<<"WARNING: Server: Invalid button "
1782                                         <<button<<std::endl;
1783                 }
1784         }
1785         else if(command == TOSERVER_RELEASE)
1786         {
1787                 if(datasize < 3)
1788                         return;
1789                 /*
1790                         length: 3
1791                         [0] u16 command
1792                         [2] u8 button
1793                 */
1794                 //TODO
1795         }
1796         else if(command == TOSERVER_SIGNTEXT)
1797         {
1798                 /*
1799                         u16 command
1800                         v3s16 blockpos
1801                         s16 id
1802                         u16 textlen
1803                         textdata
1804                 */
1805                 std::string datastring((char*)&data[2], datasize-2);
1806                 std::istringstream is(datastring, std::ios_base::binary);
1807                 u8 buf[6];
1808                 // Read stuff
1809                 is.read((char*)buf, 6);
1810                 v3s16 blockpos = readV3S16(buf);
1811                 is.read((char*)buf, 2);
1812                 s16 id = readS16(buf);
1813                 is.read((char*)buf, 2);
1814                 u16 textlen = readU16(buf);
1815                 std::string text;
1816                 for(u16 i=0; i<textlen; i++)
1817                 {
1818                         is.read((char*)buf, 1);
1819                         text += (char)buf[0];
1820                 }
1821
1822                 MapBlock *block = NULL;
1823                 try
1824                 {
1825                         block = m_env.getMap().getBlockNoCreate(blockpos);
1826                 }
1827                 catch(InvalidPositionException &e)
1828                 {
1829                         derr_server<<"Error while setting sign text: "
1830                                         "block not found"<<std::endl;
1831                         return;
1832                 }
1833
1834                 MapBlockObject *obj = block->getObject(id);
1835                 if(obj == NULL)
1836                 {
1837                         derr_server<<"Error while setting sign text: "
1838                                         "object not found"<<std::endl;
1839                         return;
1840                 }
1841                 
1842                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
1843                 {
1844                         derr_server<<"Error while setting sign text: "
1845                                         "object is not a sign"<<std::endl;
1846                         return;
1847                 }
1848
1849                 ((SignObject*)obj)->setText(text);
1850
1851                 obj->getBlock()->setChangedFlag();
1852         }
1853         else
1854         {
1855                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
1856                                 "unknown command "<<command<<std::endl;
1857         }
1858         
1859         } //try
1860         catch(SendFailedException &e)
1861         {
1862                 derr_server<<"Server::ProcessData(): SendFailedException: "
1863                                 <<"what="<<e.what()
1864                                 <<std::endl;
1865         }
1866 }
1867
1868 /*void Server::Send(u16 peer_id, u16 channelnum,
1869                 SharedBuffer<u8> data, bool reliable)
1870 {
1871         JMutexAutoLock lock(m_con_mutex);
1872         m_con.Send(peer_id, channelnum, data, reliable);
1873 }*/
1874
1875 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
1876 {
1877         DSTACK(__FUNCTION_NAME);
1878         /*
1879                 Create a packet with the block in the right format
1880         */
1881         
1882         std::ostringstream os(std::ios_base::binary);
1883         block->serialize(os, ver);
1884         std::string s = os.str();
1885         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
1886
1887         u32 replysize = 8 + blockdata.getSize();
1888         SharedBuffer<u8> reply(replysize);
1889         v3s16 p = block->getPos();
1890         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
1891         writeS16(&reply[2], p.X);
1892         writeS16(&reply[4], p.Y);
1893         writeS16(&reply[6], p.Z);
1894         memcpy(&reply[8], *blockdata, blockdata.getSize());
1895
1896         /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1897                         <<":  \tpacket size: "<<replysize<<std::endl;*/
1898         
1899         /*
1900                 Send packet
1901         */
1902         m_con.Send(peer_id, 1, reply, true);
1903 }
1904
1905 core::list<PlayerInfo> Server::getPlayerInfo()
1906 {
1907         DSTACK(__FUNCTION_NAME);
1908         JMutexAutoLock envlock(m_env_mutex);
1909         JMutexAutoLock conlock(m_con_mutex);
1910         
1911         core::list<PlayerInfo> list;
1912
1913         core::list<Player*> players = m_env.getPlayers();
1914         
1915         core::list<Player*>::Iterator i;
1916         for(i = players.begin();
1917                         i != players.end(); i++)
1918         {
1919                 PlayerInfo info;
1920
1921                 Player *player = *i;
1922                 try{
1923                         con::Peer *peer = m_con.GetPeer(player->peer_id);
1924                         info.id = peer->id;
1925                         info.address = peer->address;
1926                         info.avg_rtt = peer->avg_rtt;
1927                 }
1928                 catch(con::PeerNotFoundException &e)
1929                 {
1930                         // Outdated peer info
1931                         info.id = 0;
1932                         info.address = Address(0,0,0,0,0);
1933                         info.avg_rtt = 0.0;
1934                 }
1935
1936                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
1937                 info.position = player->getPosition();
1938
1939                 list.push_back(info);
1940         }
1941
1942         return list;
1943 }
1944
1945 void Server::peerAdded(con::Peer *peer)
1946 {
1947         DSTACK(__FUNCTION_NAME);
1948         dout_server<<"Server::peerAdded(): peer->id="
1949                         <<peer->id<<std::endl;
1950         
1951         // Connection is already locked when this is called.
1952         //JMutexAutoLock lock(m_con_mutex);
1953         
1954         // Error check
1955         core::map<u16, RemoteClient*>::Node *n;
1956         n = m_clients.find(peer->id);
1957         // The client shouldn't already exist
1958         assert(n == NULL);
1959
1960         // Create client
1961         RemoteClient *client = new RemoteClient();
1962         client->peer_id = peer->id;
1963         m_clients.insert(client->peer_id, client);
1964
1965         // Create player
1966         {
1967                 // Already locked when called
1968                 //JMutexAutoLock envlock(m_env_mutex);
1969                 
1970                 Player *player = m_env.getPlayer(peer->id);
1971                 
1972                 // The player shouldn't already exist
1973                 assert(player == NULL);
1974
1975                 player = new RemotePlayer();
1976                 player->peer_id = peer->id;
1977
1978                 /*
1979                         Set player position
1980                 */
1981
1982                 // Get zero sector (it could have been unloaded to disk)
1983                 m_env.getMap().emergeSector(v2s16(0,0));
1984                 // Get ground height at origin
1985                 f32 groundheight = m_env.getMap().getGroundHeight(v2s16(0,0), true);
1986                 // The zero sector should have been generated
1987                 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
1988                 // Don't go underwater
1989                 if(groundheight < WATER_LEVEL)
1990                         groundheight = WATER_LEVEL;
1991
1992                 player->setPosition(intToFloat(v3s16(
1993                                 0,
1994                                 groundheight + 1,
1995                                 0
1996                 )));
1997
1998                 /*
1999                         Add player to environment
2000                 */
2001
2002                 m_env.addPlayer(player);
2003
2004                 /*
2005                         Add stuff to inventory
2006                 */
2007                 
2008                 if(g_settings.getBool("creative_mode"))
2009                 {
2010                         // Give all materials
2011                         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
2012                         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
2013                         {
2014                                 // Skip some materials
2015                                 if(i == CONTENT_OCEAN)
2016                                         continue;
2017
2018                                 InventoryItem *item = new MaterialItem(i, 1);
2019                                 player->inventory.addItem(item);
2020                         }
2021                         // Sign
2022                         {
2023                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
2024                                 bool r = player->inventory.addItem(item);
2025                                 assert(r == true);
2026                         }
2027                         /*// Rat
2028                         {
2029                                 InventoryItem *item = new MapBlockObjectItem("Rat");
2030                                 bool r = player->inventory.addItem(item);
2031                                 assert(r == true);
2032                         }*/
2033                 }
2034                 else
2035                 {
2036                         // Give some lights
2037                         {
2038                                 InventoryItem *item = new MaterialItem(3, 999);
2039                                 bool r = player->inventory.addItem(item);
2040                                 assert(r == true);
2041                         }
2042                         // and some signs
2043                         for(u16 i=0; i<4; i++)
2044                         {
2045                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
2046                                 bool r = player->inventory.addItem(item);
2047                                 assert(r == true);
2048                         }
2049                         /*// and some rats
2050                         for(u16 i=0; i<4; i++)
2051                         {
2052                                 InventoryItem *item = new MapBlockObjectItem("Rat");
2053                                 bool r = player->inventory.addItem(item);
2054                                 assert(r == true);
2055                         }*/
2056                 }
2057         }
2058 }
2059
2060 void Server::deletingPeer(con::Peer *peer, bool timeout)
2061 {
2062         DSTACK(__FUNCTION_NAME);
2063         dout_server<<"Server::deletingPeer(): peer->id="
2064                         <<peer->id<<", timeout="<<timeout<<std::endl;
2065         
2066         // Connection is already locked when this is called.
2067         //JMutexAutoLock lock(m_con_mutex);
2068
2069         // Error check
2070         core::map<u16, RemoteClient*>::Node *n;
2071         n = m_clients.find(peer->id);
2072         // The client should exist
2073         assert(n != NULL);
2074         
2075         // Delete player
2076         {
2077                 // Already locked when called
2078                 //JMutexAutoLock envlock(m_env_mutex);
2079                 m_env.removePlayer(peer->id);
2080         }
2081         
2082         // Delete client
2083         delete m_clients[peer->id];
2084         m_clients.remove(peer->id);
2085
2086         // Send player info to all clients
2087         SendPlayerInfos();
2088 }
2089
2090 void Server::SendObjectData(float dtime)
2091 {
2092         DSTACK(__FUNCTION_NAME);
2093
2094         core::map<v3s16, bool> stepped_blocks;
2095         
2096         for(core::map<u16, RemoteClient*>::Iterator
2097                 i = m_clients.getIterator();
2098                 i.atEnd() == false; i++)
2099         {
2100                 u16 peer_id = i.getNode()->getKey();
2101                 RemoteClient *client = i.getNode()->getValue();
2102                 assert(client->peer_id == peer_id);
2103                 
2104                 if(client->serialization_version == SER_FMT_VER_INVALID)
2105                         continue;
2106                 
2107                 client->SendObjectData(this, dtime, stepped_blocks);
2108         }
2109 }
2110
2111 void Server::SendPlayerInfos()
2112 {
2113         DSTACK(__FUNCTION_NAME);
2114
2115         //JMutexAutoLock envlock(m_env_mutex);
2116         
2117         core::list<Player*> players = m_env.getPlayers();
2118         
2119         u32 player_count = players.getSize();
2120         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2121
2122         SharedBuffer<u8> data(datasize);
2123         writeU16(&data[0], TOCLIENT_PLAYERINFO);
2124         
2125         u32 start = 2;
2126         core::list<Player*>::Iterator i;
2127         for(i = players.begin();
2128                         i != players.end(); i++)
2129         {
2130                 Player *player = *i;
2131
2132                 /*dstream<<"Server sending player info for player with "
2133                                 "peer_id="<<player->peer_id<<std::endl;*/
2134                 
2135                 writeU16(&data[start], player->peer_id);
2136                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2137                 start += 2+PLAYERNAME_SIZE;
2138         }
2139
2140         //JMutexAutoLock conlock(m_con_mutex);
2141
2142         // Send as reliable
2143         m_con.SendToAll(0, data, true);
2144 }
2145
2146 void Server::SendInventory(u16 peer_id)
2147 {
2148         DSTACK(__FUNCTION_NAME);
2149         
2150         //JMutexAutoLock envlock(m_env_mutex);
2151         
2152         Player* player = m_env.getPlayer(peer_id);
2153
2154         std::ostringstream os;
2155         //os.imbue(std::locale("C"));
2156
2157         player->inventory.serialize(os);
2158
2159         std::string s = os.str();
2160         
2161         SharedBuffer<u8> data(s.size()+2);
2162         writeU16(&data[0], TOCLIENT_INVENTORY);
2163         memcpy(&data[2], s.c_str(), s.size());
2164         
2165         //JMutexAutoLock conlock(m_con_mutex);
2166
2167         // Send as reliable
2168         m_con.Send(peer_id, 0, data, true);
2169 }
2170
2171 void Server::SendBlocks(float dtime)
2172 {
2173         DSTACK(__FUNCTION_NAME);
2174
2175         JMutexAutoLock envlock(m_env_mutex);
2176
2177         core::array<PrioritySortedBlockTransfer> queue;
2178
2179         s32 total_sending = 0;
2180
2181         for(core::map<u16, RemoteClient*>::Iterator
2182                 i = m_clients.getIterator();
2183                 i.atEnd() == false; i++)
2184         {
2185                 RemoteClient *client = i.getNode()->getValue();
2186                 assert(client->peer_id == i.getNode()->getKey());
2187
2188                 total_sending += client->SendingCount();
2189                 
2190                 if(client->serialization_version == SER_FMT_VER_INVALID)
2191                         continue;
2192                 
2193                 client->GetNextBlocks(this, dtime, queue);
2194         }
2195
2196         // Sort.
2197         // Lowest priority number comes first.
2198         // Lowest is most important.
2199         queue.sort();
2200
2201         JMutexAutoLock conlock(m_con_mutex);
2202
2203         for(u32 i=0; i<queue.size(); i++)
2204         {
2205                 //TODO: Calculate limit dynamically
2206                 if(total_sending >= g_settings.getS32
2207                                 ("max_simultaneous_block_sends_server_total"))
2208                         break;
2209                 
2210                 PrioritySortedBlockTransfer q = queue[i];
2211
2212                 MapBlock *block = NULL;
2213                 try
2214                 {
2215                         block = m_env.getMap().getBlockNoCreate(q.pos);
2216                 }
2217                 catch(InvalidPositionException &e)
2218                 {
2219                         continue;
2220                 }
2221
2222                 RemoteClient *client = getClient(q.peer_id);
2223
2224                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
2225
2226                 client->SentBlock(q.pos);
2227
2228                 total_sending++;
2229         }
2230 }
2231
2232
2233 RemoteClient* Server::getClient(u16 peer_id)
2234 {
2235         DSTACK(__FUNCTION_NAME);
2236         //JMutexAutoLock lock(m_con_mutex);
2237         core::map<u16, RemoteClient*>::Node *n;
2238         n = m_clients.find(peer_id);
2239         // A client should exist for all peers
2240         assert(n != NULL);
2241         return n->getValue();
2242 }
2243
2244 void Server::UpdateBlockWaterPressure(MapBlock *block,
2245                         core::map<v3s16, MapBlock*> &modified_blocks)
2246 {
2247         MapVoxelManipulator v(&m_env.getMap());
2248         v.m_disable_water_climb =
2249                         g_settings.getBool("disable_water_climb");
2250         
2251         VoxelArea area(block->getPosRelative(),
2252                         block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
2253
2254         try
2255         {
2256                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2257         }
2258         catch(ProcessingLimitException &e)
2259         {
2260                 dstream<<"Processing limit reached (1)"<<std::endl;
2261         }
2262         
2263         v.blitBack(modified_blocks);
2264 }
2265         
2266
2267